mirror of
https://github.com/open-cluster-management-io/ocm.git
synced 2026-08-19 04:06:35 +00:00
✨ feat: add NetworkPolicies for open-cluster-management-hub namespace (#1634)
Signed-off-by: Aman Gupta <amangta.gupta@gmail.com>
This commit is contained in:
@@ -81,6 +81,10 @@ rules:
|
||||
- apiGroups: ["admissionregistration.k8s.io"]
|
||||
resources: ["validatingwebhookconfigurations", "mutatingwebhookconfigurations"]
|
||||
verbs: ["create", "get", "list", "update", "watch", "patch", "delete"]
|
||||
# Allow the registration-operator to manage NetworkPolicies
|
||||
- apiGroups: ["networking.k8s.io"]
|
||||
resources: ["networkpolicies"]
|
||||
verbs: ["create", "get", "list", "update", "watch", "patch", "delete"]
|
||||
# Allow the nuclues to manage clustermanager apis.
|
||||
- apiGroups: ["operator.open-cluster-management.io"]
|
||||
resources: ["clustermanagers"]
|
||||
|
||||
@@ -83,6 +83,10 @@ rules:
|
||||
- apiGroups: ["admissionregistration.k8s.io"]
|
||||
resources: ["validatingwebhookconfigurations", "mutatingwebhookconfigurations"]
|
||||
verbs: ["create", "get", "list", "update", "watch", "patch", "delete"]
|
||||
# Allow the registration-operator to manage NetworkPolicies
|
||||
- apiGroups: ["networking.k8s.io"]
|
||||
resources: ["networkpolicies"]
|
||||
verbs: ["create", "get", "list", "update", "watch", "patch", "delete"]
|
||||
# Allow the nuclues to manage clustermanager apis.
|
||||
- apiGroups: ["operator.open-cluster-management.io"]
|
||||
resources: ["clustermanagers"]
|
||||
|
||||
+13
-1
@@ -59,7 +59,7 @@ metadata:
|
||||
categories: Integration & Delivery,OpenShift Optional
|
||||
certified: "false"
|
||||
containerImage: quay.io/open-cluster-management/registration-operator:latest
|
||||
createdAt: "2026-06-09T16:57:09Z"
|
||||
createdAt: "2026-07-29T16:39:49Z"
|
||||
description: Manages the installation and upgrade of the ClusterManager.
|
||||
operators.operatorframework.io/builder: operator-sdk-v1.32.0
|
||||
operators.operatorframework.io/project_layout: go.kubebuilder.io/v3
|
||||
@@ -309,6 +309,18 @@ spec:
|
||||
- watch
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- networking.k8s.io
|
||||
resources:
|
||||
- networkpolicies
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
- list
|
||||
- update
|
||||
- watch
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- operator.open-cluster-management.io
|
||||
resources:
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ metadata:
|
||||
categories: Integration & Delivery,OpenShift Optional
|
||||
certified: "false"
|
||||
containerImage: quay.io/open-cluster-management/registration-operator:latest
|
||||
createdAt: "2026-06-09T16:57:09Z"
|
||||
createdAt: "2026-07-29T16:39:49Z"
|
||||
description: Manages the installation and upgrade of the Klusterlet.
|
||||
operators.operatorframework.io/builder: operator-sdk-v1.32.0
|
||||
operators.operatorframework.io/project_layout: go.kubebuilder.io/v3
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: default-deny-all
|
||||
namespace: {{ .ClusterManagerNamespace }}
|
||||
spec:
|
||||
podSelector:
|
||||
matchExpressions:
|
||||
- key: app
|
||||
operator: In
|
||||
values:
|
||||
- clustermanager-registration-controller
|
||||
- "{{ .ClusterManagerName }}-registration-webhook"
|
||||
- "{{ .ClusterManagerName }}-work-controller"
|
||||
- "{{ .ClusterManagerName }}-work-webhook"
|
||||
- clustermanager-placement-controller
|
||||
- clustermanager-addon-manager-controller
|
||||
- "{{ .ClusterManagerName }}-addon-webhook"
|
||||
- "{{ .ClusterManagerName }}-grpc-server"
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
@@ -0,0 +1,45 @@
|
||||
# Combined egress policy for all hub controller and webhook pods.
|
||||
# Rule 1: DNS resolution (CoreDNS port 53, node-local-dns/openshift-dns port 5353)
|
||||
# Rule 2: kube-apiserver egress (ports 443, 6443)
|
||||
#
|
||||
# No "to" selector is specified on either rule — egress is restricted by port only.
|
||||
# DNS and apiserver endpoints vary by platform (kube-system vs openshift-dns,
|
||||
# ClusterIP vs static pod vs external control plane), and locking "to" would
|
||||
# require platform-specific namespace or IP configuration that OCM intentionally
|
||||
# avoids. The port restriction limits the blast radius to DNS and HTTPS traffic.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-egress
|
||||
namespace: {{ .ClusterManagerNamespace }}
|
||||
spec:
|
||||
podSelector:
|
||||
matchExpressions:
|
||||
- key: app
|
||||
operator: In
|
||||
values:
|
||||
- clustermanager-registration-controller
|
||||
- "{{ .ClusterManagerName }}-registration-webhook"
|
||||
- "{{ .ClusterManagerName }}-work-controller"
|
||||
- "{{ .ClusterManagerName }}-work-webhook"
|
||||
- clustermanager-placement-controller
|
||||
- clustermanager-addon-manager-controller
|
||||
- "{{ .ClusterManagerName }}-addon-webhook"
|
||||
- "{{ .ClusterManagerName }}-grpc-server"
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
- ports:
|
||||
- port: 53
|
||||
protocol: UDP
|
||||
- port: 53
|
||||
protocol: TCP
|
||||
- port: 5353
|
||||
protocol: UDP
|
||||
- port: 5353
|
||||
protocol: TCP
|
||||
- ports:
|
||||
- port: 443
|
||||
protocol: TCP
|
||||
- port: 6443
|
||||
protocol: TCP
|
||||
@@ -0,0 +1,32 @@
|
||||
# Intra-namespace pod-to-pod communication.
|
||||
# Allows pods within open-cluster-management-hub to reach each other
|
||||
# (e.g. placement-controller debug server, inter-controller calls).
|
||||
# API-server webhook ingress is handled separately by 04-hub-ns-webhook-ingress.yaml.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-intra-namespace
|
||||
namespace: {{ .ClusterManagerNamespace }}
|
||||
spec:
|
||||
podSelector: &ocmPods
|
||||
matchExpressions:
|
||||
- key: app
|
||||
operator: In
|
||||
values:
|
||||
- clustermanager-registration-controller
|
||||
- "{{ .ClusterManagerName }}-registration-webhook"
|
||||
- "{{ .ClusterManagerName }}-work-controller"
|
||||
- "{{ .ClusterManagerName }}-work-webhook"
|
||||
- clustermanager-placement-controller
|
||||
- clustermanager-addon-manager-controller
|
||||
- "{{ .ClusterManagerName }}-addon-webhook"
|
||||
- "{{ .ClusterManagerName }}-grpc-server"
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress:
|
||||
- from:
|
||||
- podSelector: *ocmPods
|
||||
egress:
|
||||
- to:
|
||||
- podSelector: *ocmPods
|
||||
@@ -0,0 +1,42 @@
|
||||
# Ingress policy for all externally-reachable hub pods.
|
||||
# Always applied when NetworkPolicies feature gate is enabled — restricted by pod label and port only.
|
||||
# Intentionally allows ingress from all sources: webhook sources vary by platform (kube-system,
|
||||
# openshift-kube-apiserver, external control planes), and OCM cannot assume a platform-specific
|
||||
# source namespace. Port restriction limits the blast radius to webhook, gRPC, and debug ports.
|
||||
#
|
||||
# Covers:
|
||||
# - registration/work/addon webhooks (apiserver admission calls)
|
||||
# - grpc-server (spoke cluster connections, ports 8090/443)
|
||||
# - placement debug server (port 9443)
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-hub-ingress
|
||||
namespace: {{ .ClusterManagerNamespace }}
|
||||
spec:
|
||||
podSelector:
|
||||
matchExpressions:
|
||||
- key: app
|
||||
operator: In
|
||||
values:
|
||||
- "{{ .ClusterManagerName }}-registration-webhook"
|
||||
- "{{ .ClusterManagerName }}-work-webhook"
|
||||
- "{{ .ClusterManagerName }}-addon-webhook"
|
||||
- "{{ .ClusterManagerName }}-grpc-server"
|
||||
- clustermanager-placement-controller
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- ports:
|
||||
- port: {{ .RegistrationWebhook.Port }}
|
||||
protocol: TCP
|
||||
- port: {{ .WorkWebhook.Port }}
|
||||
protocol: TCP
|
||||
- port: {{ .AddonWebhook.Port }}
|
||||
protocol: TCP
|
||||
- port: 8090
|
||||
protocol: TCP
|
||||
- port: 443
|
||||
protocol: TCP
|
||||
- port: 9443
|
||||
protocol: TCP
|
||||
@@ -0,0 +1,29 @@
|
||||
# Prometheus scrape ingress for all hub controller and webhook pods.
|
||||
# Allows ingress on metrics ports from any source within the cluster.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-prometheus-ingress
|
||||
namespace: {{ .ClusterManagerNamespace }}
|
||||
spec:
|
||||
podSelector:
|
||||
matchExpressions:
|
||||
- key: app
|
||||
operator: In
|
||||
values:
|
||||
- clustermanager-registration-controller
|
||||
- "{{ .ClusterManagerName }}-registration-webhook"
|
||||
- "{{ .ClusterManagerName }}-work-controller"
|
||||
- "{{ .ClusterManagerName }}-work-webhook"
|
||||
- clustermanager-placement-controller
|
||||
- clustermanager-addon-manager-controller
|
||||
- "{{ .ClusterManagerName }}-addon-webhook"
|
||||
- "{{ .ClusterManagerName }}-grpc-server"
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- ports:
|
||||
- port: 8080
|
||||
protocol: TCP
|
||||
- port: 8443
|
||||
protocol: TCP
|
||||
@@ -55,6 +55,8 @@ type HubConfig struct {
|
||||
// TLS configuration injected into all managed hub component deployments
|
||||
TLSMinVersion string
|
||||
TLSCipherSuites string
|
||||
|
||||
NetworkPoliciesEnabled bool
|
||||
}
|
||||
|
||||
type Webhook struct {
|
||||
|
||||
@@ -14,18 +14,10 @@ spec:
|
||||
- from:
|
||||
- podSelector: {}
|
||||
egress:
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: default
|
||||
ports:
|
||||
- ports:
|
||||
- protocol: TCP
|
||||
port: 443
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: open-cluster-management-hub
|
||||
ports:
|
||||
- ports:
|
||||
- protocol: TCP
|
||||
port: 9443
|
||||
- to:
|
||||
|
||||
@@ -8,23 +8,12 @@ spec:
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: openshift-dns
|
||||
ports:
|
||||
- ports:
|
||||
- protocol: UDP
|
||||
port: 5353
|
||||
- protocol: TCP
|
||||
port: 5353
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
k8s-app: kube-dns
|
||||
ports:
|
||||
- ports:
|
||||
- protocol: UDP
|
||||
port: 53
|
||||
- protocol: TCP
|
||||
|
||||
@@ -16,17 +16,6 @@ spec:
|
||||
egress:
|
||||
- to:
|
||||
- podSelector: {}
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
addon.open-cluster-management.io/namespace: "true"
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 443
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: default
|
||||
ports:
|
||||
- ports:
|
||||
- protocol: TCP
|
||||
port: 443
|
||||
|
||||
@@ -805,6 +805,28 @@ func FeatureGateEnabled(features []operatorapiv1.FeatureGate,
|
||||
return defaultFeature.Default
|
||||
}
|
||||
|
||||
// NetworkPolicies is an operator-internal feature gate that controls whether NetworkPolicy
|
||||
// manifests are applied to the hub/agent namespace. Not passed through to agent binaries.
|
||||
const NetworkPolicies featuregate.Feature = "NetworkPolicies"
|
||||
|
||||
// operatorOnlyFeatureGates are feature gates consumed by the operator itself,
|
||||
// not passed through to agent binaries as CLI flags.
|
||||
var operatorOnlyFeatureGates = map[featuregate.Feature]bool{
|
||||
NetworkPolicies: true,
|
||||
}
|
||||
|
||||
// FilterOperatorFeatureGates removes operator-internal feature gates from the list
|
||||
// so they are not forwarded as CLI flags to hub/agent binaries.
|
||||
func FilterOperatorFeatureGates(features []operatorapiv1.FeatureGate) []operatorapiv1.FeatureGate {
|
||||
var filtered []operatorapiv1.FeatureGate
|
||||
for _, f := range features {
|
||||
if !operatorOnlyFeatureGates[featuregate.Feature(f.Feature)] {
|
||||
filtered = append(filtered, f)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// IsSingleton returns if agent is deployed in singleton mode either hosted or not
|
||||
func IsSingleton(mode operatorapiv1.InstallMode) bool {
|
||||
return mode == operatorapiv1.InstallModeSingleton || mode == operatorapiv1.InstallModeSingletonHosted
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
admissionv1 "k8s.io/api/admissionregistration/v1"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
fakeapiextensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake"
|
||||
"k8s.io/apimachinery/pkg/api/equality"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -2442,6 +2443,195 @@ func TestNormalizeImagePullSecretName(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// networkPolicyPort is a comparable, protocol+port pair used to assert on the
|
||||
// exact ports of a specific NetworkPolicy rule (avoids ambiguous substring
|
||||
// matches on the rendered YAML, e.g. "port: 53" matching inside "port: 5353").
|
||||
type networkPolicyPort struct {
|
||||
protocol corev1.Protocol
|
||||
port int32
|
||||
}
|
||||
|
||||
func portsOf(ports []networkingv1.NetworkPolicyPort) []networkPolicyPort {
|
||||
got := make([]networkPolicyPort, 0, len(ports))
|
||||
for _, p := range ports {
|
||||
var proto corev1.Protocol
|
||||
if p.Protocol != nil {
|
||||
proto = *p.Protocol
|
||||
}
|
||||
var port int32
|
||||
if p.Port != nil {
|
||||
port = p.Port.IntVal
|
||||
}
|
||||
got = append(got, networkPolicyPort{protocol: proto, port: port})
|
||||
}
|
||||
return got
|
||||
}
|
||||
|
||||
func TestNetworkPolicyTemplateRendering(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
config manifests.HubConfig
|
||||
manifestFile string
|
||||
expectContains []string
|
||||
expectAbsent []string
|
||||
expectedPolicyTypes []networkingv1.PolicyType
|
||||
// expectedIngressPorts/expectedEgressPorts, when non-nil, must match the
|
||||
// ports of each ingress/egress rule, in order, exactly.
|
||||
expectedIngressPorts [][]networkPolicyPort
|
||||
expectedEgressPorts [][]networkPolicyPort
|
||||
}{
|
||||
{
|
||||
name: "default deny scoped to OCM pods",
|
||||
config: manifests.HubConfig{
|
||||
ClusterManagerNamespace: "open-cluster-management-hub",
|
||||
ClusterManagerName: "cluster-manager",
|
||||
},
|
||||
manifestFile: "cluster-manager/hub/networkpolicies/01-hub-ns-default-deny.yaml",
|
||||
expectContains: []string{
|
||||
"namespace: open-cluster-management-hub",
|
||||
"clustermanager-registration-controller",
|
||||
"cluster-manager-work-controller",
|
||||
"clustermanager-placement-controller",
|
||||
"clustermanager-addon-manager-controller",
|
||||
"cluster-manager-grpc-server",
|
||||
},
|
||||
expectedPolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress, networkingv1.PolicyTypeEgress},
|
||||
},
|
||||
{
|
||||
name: "egress policy scoped to OCM pods",
|
||||
config: manifests.HubConfig{
|
||||
ClusterManagerNamespace: "open-cluster-management-hub",
|
||||
ClusterManagerName: "cluster-manager",
|
||||
},
|
||||
manifestFile: "cluster-manager/hub/networkpolicies/02-hub-ns-egress.yaml",
|
||||
expectContains: []string{
|
||||
"allow-egress",
|
||||
"clustermanager-registration-controller",
|
||||
"cluster-manager-grpc-server",
|
||||
},
|
||||
expectedPolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeEgress},
|
||||
// Rule 0: DNS (port 53 distinct from port 5353). Rule 1: kube-apiserver.
|
||||
expectedEgressPorts: [][]networkPolicyPort{
|
||||
{
|
||||
{protocol: corev1.ProtocolUDP, port: 53},
|
||||
{protocol: corev1.ProtocolTCP, port: 53},
|
||||
{protocol: corev1.ProtocolUDP, port: 5353},
|
||||
{protocol: corev1.ProtocolTCP, port: 5353},
|
||||
},
|
||||
{
|
||||
{protocol: corev1.ProtocolTCP, port: 443},
|
||||
{protocol: corev1.ProtocolTCP, port: 6443},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hub ingress covers webhooks, grpc-server, and placement debug — all sources intentionally allowed",
|
||||
config: manifests.HubConfig{
|
||||
ClusterManagerNamespace: "open-cluster-management-hub",
|
||||
ClusterManagerName: "cluster-manager",
|
||||
RegistrationWebhook: manifests.Webhook{Port: 9443},
|
||||
WorkWebhook: manifests.Webhook{Port: 9443},
|
||||
AddonWebhook: manifests.Webhook{Port: 9443},
|
||||
},
|
||||
manifestFile: "cluster-manager/hub/networkpolicies/04-hub-ns-webhook-ingress.yaml",
|
||||
expectContains: []string{
|
||||
"allow-hub-ingress",
|
||||
"cluster-manager-registration-webhook",
|
||||
"cluster-manager-work-webhook",
|
||||
"cluster-manager-addon-webhook",
|
||||
"cluster-manager-grpc-server",
|
||||
"clustermanager-placement-controller",
|
||||
},
|
||||
expectAbsent: []string{"namespaceSelector"},
|
||||
expectedPolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
|
||||
expectedIngressPorts: [][]networkPolicyPort{
|
||||
{
|
||||
{protocol: corev1.ProtocolTCP, port: 9443}, // RegistrationWebhook.Port
|
||||
{protocol: corev1.ProtocolTCP, port: 9443}, // WorkWebhook.Port
|
||||
{protocol: corev1.ProtocolTCP, port: 9443}, // AddonWebhook.Port
|
||||
{protocol: corev1.ProtocolTCP, port: 8090}, // grpc-server
|
||||
{protocol: corev1.ProtocolTCP, port: 443}, // grpc-server
|
||||
{protocol: corev1.ProtocolTCP, port: 9443}, // placement debug
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prometheus NP uses ClusterManagerName vars",
|
||||
config: manifests.HubConfig{
|
||||
ClusterManagerNamespace: "open-cluster-management-hub",
|
||||
ClusterManagerName: "cluster-manager",
|
||||
},
|
||||
manifestFile: "cluster-manager/hub/networkpolicies/05-hub-ns-prometheus.yaml",
|
||||
expectContains: []string{
|
||||
"clustermanager-registration-controller",
|
||||
"cluster-manager-work-controller",
|
||||
"clustermanager-placement-controller",
|
||||
"cluster-manager-grpc-server",
|
||||
},
|
||||
expectAbsent: []string{"namespaceSelector"},
|
||||
expectedPolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
|
||||
expectedIngressPorts: [][]networkPolicyPort{
|
||||
{
|
||||
{protocol: corev1.ProtocolTCP, port: 8080},
|
||||
{protocol: corev1.ProtocolTCP, port: 8443},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
template, err := manifests.ClusterManagerManifestFiles.ReadFile(c.manifestFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read manifest %s: %v", c.manifestFile, err)
|
||||
}
|
||||
rendered := assets.MustCreateAssetFromTemplate(c.manifestFile, template, c.config).Data
|
||||
|
||||
for _, want := range c.expectContains {
|
||||
if !strings.Contains(string(rendered), want) {
|
||||
t.Errorf("expected rendered manifest to contain %q\ngot:\n%s", want, rendered)
|
||||
}
|
||||
}
|
||||
for _, absent := range c.expectAbsent {
|
||||
if strings.Contains(string(rendered), absent) {
|
||||
t.Errorf("expected rendered manifest NOT to contain %q\ngot:\n%s", absent, rendered)
|
||||
}
|
||||
}
|
||||
|
||||
var np networkingv1.NetworkPolicy
|
||||
if err := yaml.Unmarshal(rendered, &np); err != nil {
|
||||
t.Fatalf("failed to unmarshal rendered manifest into NetworkPolicy: %v\ngot:\n%s", err, rendered)
|
||||
}
|
||||
|
||||
if c.expectedPolicyTypes != nil && !reflect.DeepEqual(np.Spec.PolicyTypes, c.expectedPolicyTypes) {
|
||||
t.Errorf("expected policyTypes %v, got %v", c.expectedPolicyTypes, np.Spec.PolicyTypes)
|
||||
}
|
||||
|
||||
if c.expectedIngressPorts != nil {
|
||||
if len(np.Spec.Ingress) != len(c.expectedIngressPorts) {
|
||||
t.Fatalf("expected %d ingress rule(s), got %d: %+v", len(c.expectedIngressPorts), len(np.Spec.Ingress), np.Spec.Ingress)
|
||||
}
|
||||
for i, rule := range np.Spec.Ingress {
|
||||
if got, want := portsOf(rule.Ports), c.expectedIngressPorts[i]; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("ingress rule %d: expected ports %+v, got %+v", i, want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if c.expectedEgressPorts != nil {
|
||||
if len(np.Spec.Egress) != len(c.expectedEgressPorts) {
|
||||
t.Fatalf("expected %d egress rule(s), got %d: %+v", len(c.expectedEgressPorts), len(np.Spec.Egress), np.Spec.Egress)
|
||||
}
|
||||
for i, rule := range np.Spec.Egress {
|
||||
if got, want := portsOf(rule.Ports), c.expectedEgressPorts[i]; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("egress rule %d: expected ports %+v, got %+v", i, want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegistrationClusterRoleImagePullSecretName(t *testing.T) {
|
||||
template, err := manifests.ClusterManagerManifestFiles.ReadFile("cluster-manager/hub/registration/clusterrole.yaml")
|
||||
if err != nil {
|
||||
|
||||
+2
-1
@@ -195,8 +195,9 @@ func (n *clusterManagerController) sync(ctx context.Context, controllerContext f
|
||||
registrationFeatureGates = clusterManager.Spec.RegistrationConfiguration.FeatureGates
|
||||
config.AutoApproveUsers = strings.Join(clusterManager.Spec.RegistrationConfiguration.AutoApproveUsers, ",")
|
||||
}
|
||||
config.NetworkPoliciesEnabled = helpers.FeatureGateEnabled(registrationFeatureGates, ocmfeature.DefaultHubRegistrationFeatureGates, helpers.NetworkPolicies)
|
||||
config.RegistrationFeatureGates, registrationFeatureMsgs = helpers.ConvertToFeatureGateFlags("Registration",
|
||||
registrationFeatureGates, ocmfeature.DefaultHubRegistrationFeatureGates)
|
||||
helpers.FilterOperatorFeatureGates(registrationFeatureGates), ocmfeature.DefaultHubRegistrationFeatureGates)
|
||||
config.ClusterProfileEnabled = helpers.FeatureGateEnabled(registrationFeatureGates, ocmfeature.DefaultHubRegistrationFeatureGates, ocmfeature.ClusterProfile)
|
||||
// setting for cluster importer.
|
||||
// TODO(qiujian16) since this is disabled by feature gate, the image is obtained from cluster manager's env var. Need a more elegant approach.
|
||||
|
||||
+74
@@ -1709,3 +1709,77 @@ func containsArg(args []string, prefix string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TestNetworkPolicyFeatureGate(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
featureGates []operatorapiv1.FeatureGate
|
||||
expectBaseNPs bool
|
||||
expectPrometheusNP bool
|
||||
}{
|
||||
{
|
||||
name: "feature gate disabled — no NPs",
|
||||
featureGates: nil,
|
||||
expectBaseNPs: false,
|
||||
expectPrometheusNP: false,
|
||||
},
|
||||
{
|
||||
name: "feature gate enabled — all NPs applied",
|
||||
featureGates: []operatorapiv1.FeatureGate{
|
||||
{Feature: "NetworkPolicies", Mode: operatorapiv1.FeatureGateModeTypeEnable},
|
||||
},
|
||||
expectBaseNPs: true,
|
||||
expectPrometheusNP: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
clusterManager := newClusterManager("testhub")
|
||||
if c.featureGates != nil {
|
||||
if clusterManager.Spec.RegistrationConfiguration == nil {
|
||||
clusterManager.Spec.RegistrationConfiguration = &operatorapiv1.RegistrationHubConfiguration{}
|
||||
}
|
||||
clusterManager.Spec.RegistrationConfiguration.FeatureGates = c.featureGates
|
||||
}
|
||||
|
||||
tc := newTestController(t, clusterManager)
|
||||
clusterManagerNamespace := helpers.ClusterManagerNamespace(clusterManager.Name, clusterManager.Spec.DeployOption.Mode)
|
||||
setup(t, tc, setDeployment(clusterManager.Name, clusterManagerNamespace))
|
||||
|
||||
syncContext := testingcommon.NewFakeSyncContext(t, "testhub")
|
||||
if err := tc.clusterManagerController.sync(ctx, syncContext, "testhub"); err != nil {
|
||||
t.Fatalf("unexpected sync error: %v", err)
|
||||
}
|
||||
|
||||
baseNPNames := []string{"default-deny-all", "allow-egress", "allow-intra-namespace", "allow-hub-ingress"}
|
||||
createdNPs := map[string]bool{}
|
||||
for _, action := range tc.hubKubeClient.Actions() {
|
||||
if action.GetVerb() != createVerb {
|
||||
continue
|
||||
}
|
||||
obj := action.(clienttesting.CreateActionImpl).Object
|
||||
if np, ok := obj.(interface{ GetName() string }); ok {
|
||||
createdNPs[np.GetName()] = true
|
||||
}
|
||||
}
|
||||
|
||||
if c.expectBaseNPs {
|
||||
for _, name := range baseNPNames {
|
||||
if !createdNPs[name] {
|
||||
t.Errorf("expectBaseNPs=true but NetworkPolicy %q was not created", name)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, name := range baseNPNames {
|
||||
if createdNPs[name] {
|
||||
t.Errorf("expectBaseNPs=false but NetworkPolicy %q was created", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if hasPrometheus := createdNPs["allow-prometheus-ingress"]; hasPrometheus != c.expectPrometheusNP {
|
||||
t.Errorf("expectPrometheusNP=%v but hasPrometheusNP=%v", c.expectPrometheusNP, hasPrometheus)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+16
@@ -87,6 +87,17 @@ var (
|
||||
"cluster-manager/hub/grpc-server/serviceaccount.yaml",
|
||||
"cluster-manager/hub/grpc-server/service.yaml",
|
||||
}
|
||||
|
||||
// hubNetworkPolicyBaseFiles are always-on NetworkPolicy resources for open-cluster-management-hub.
|
||||
// Applied as part of ClusterManager CR reconciliation.
|
||||
hubNetworkPolicyBaseFiles = []string{
|
||||
"cluster-manager/hub/networkpolicies/01-hub-ns-default-deny.yaml",
|
||||
"cluster-manager/hub/networkpolicies/02-hub-ns-egress.yaml",
|
||||
"cluster-manager/hub/networkpolicies/03-hub-ns-intra-namespace.yaml",
|
||||
"cluster-manager/hub/networkpolicies/04-hub-ns-webhook-ingress.yaml",
|
||||
}
|
||||
|
||||
hubPrometheusNPFile = "cluster-manager/hub/networkpolicies/05-hub-ns-prometheus.yaml"
|
||||
)
|
||||
|
||||
type hubReconcile struct {
|
||||
@@ -195,5 +206,10 @@ func getHubResources(mode operatorapiv1.InstallMode, config manifests.HubConfig)
|
||||
hubResources = append(hubResources, hubDefaultWebhookServiceFiles...)
|
||||
}
|
||||
|
||||
if config.NetworkPoliciesEnabled {
|
||||
hubResources = append(hubResources, hubNetworkPolicyBaseFiles...)
|
||||
hubResources = append(hubResources, hubPrometheusNPFile)
|
||||
}
|
||||
|
||||
return hubResources
|
||||
}
|
||||
|
||||
+2
-24
@@ -19,7 +19,6 @@ import (
|
||||
coreinformer "k8s.io/client-go/informers/core/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/component-base/featuregate"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
operatorv1client "open-cluster-management.io/api/client/operator/clientset/versioned/typed/operator/v1"
|
||||
@@ -46,19 +45,8 @@ const (
|
||||
klusterletFinalizer = "operator.open-cluster-management.io/klusterlet-cleanup"
|
||||
managedResourcesEvictionTimestampAnno = "operator.open-cluster-management.io/managed-resources-eviction-timestamp"
|
||||
klusterletNamespaceLabelKey = "operator.open-cluster-management.io/klusterlet"
|
||||
|
||||
// NetworkPolicies is an operator-internal feature gate that controls whether NetworkPolicy
|
||||
// manifests are applied to the agent namespace. Disabled by default; enable via the Klusterlet
|
||||
// CR's registrationConfiguration.featureGates.
|
||||
NetworkPolicies featuregate.Feature = "NetworkPolicies"
|
||||
)
|
||||
|
||||
// operatorOnlyFeatureGates are feature gates consumed by the operator itself,
|
||||
// not passed through to agent binaries as CLI flags.
|
||||
var operatorOnlyFeatureGates = map[featuregate.Feature]bool{
|
||||
NetworkPolicies: true,
|
||||
}
|
||||
|
||||
// buildClusterAnnotationsString renders the ClusterAnnotations map into the
|
||||
// comma-separated "key1=value1,key2=value2" form consumed by the registration
|
||||
// agent's --cluster-annotations flag. Keys are sorted deterministically.
|
||||
@@ -72,16 +60,6 @@ func buildClusterAnnotationsString(annotations map[string]string) string {
|
||||
return strings.Join(arr, ",")
|
||||
}
|
||||
|
||||
func filterOperatorFeatureGates(features []operatorapiv1.FeatureGate) []operatorapiv1.FeatureGate {
|
||||
var filtered []operatorapiv1.FeatureGate
|
||||
for _, f := range features {
|
||||
if !operatorOnlyFeatureGates[featuregate.Feature(f.Feature)] {
|
||||
filtered = append(filtered, f)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
type klusterletController struct {
|
||||
patcher patcher.Patcher[*operatorapiv1.Klusterlet, operatorapiv1.KlusterletSpec, operatorapiv1.KlusterletStatus]
|
||||
klusterletLister operatorlister.KlusterletLister
|
||||
@@ -455,9 +433,9 @@ func (n *klusterletController) sync(ctx context.Context, controllerContext facto
|
||||
config.AboutAPIEnabled = helpers.FeatureGateEnabled(
|
||||
registrationFeatureGates, ocmfeature.DefaultSpokeRegistrationFeatureGates, ocmfeature.ClusterProperty)
|
||||
config.NetworkPoliciesEnabled = helpers.FeatureGateEnabled(
|
||||
registrationFeatureGates, ocmfeature.DefaultSpokeRegistrationFeatureGates, NetworkPolicies)
|
||||
registrationFeatureGates, ocmfeature.DefaultSpokeRegistrationFeatureGates, helpers.NetworkPolicies)
|
||||
config.RegistrationFeatureGates, registrationFeatureMsgs = helpers.ConvertToFeatureGateFlags("Registration",
|
||||
filterOperatorFeatureGates(registrationFeatureGates), ocmfeature.DefaultSpokeRegistrationFeatureGates)
|
||||
helpers.FilterOperatorFeatureGates(registrationFeatureGates), ocmfeature.DefaultSpokeRegistrationFeatureGates)
|
||||
|
||||
var workFeatureGates []operatorapiv1.FeatureGate
|
||||
if klusterlet.Spec.WorkConfiguration != nil {
|
||||
|
||||
+1
-1
@@ -668,7 +668,7 @@ func TestSyncDeployWithNetworkPolicies(t *testing.T) {
|
||||
klusterlet := newKlusterlet("klusterlet", "testns", "cluster1")
|
||||
klusterlet.Spec.RegistrationConfiguration.FeatureGates = append(
|
||||
klusterlet.Spec.RegistrationConfiguration.FeatureGates,
|
||||
operatorapiv1.FeatureGate{Feature: string(NetworkPolicies), Mode: operatorapiv1.FeatureGateModeTypeEnable},
|
||||
operatorapiv1.FeatureGate{Feature: string(helpers.NetworkPolicies), Mode: operatorapiv1.FeatureGateModeTypeEnable},
|
||||
)
|
||||
bootStrapSecret := newSecret(helpers.BootstrapHubKubeConfig, "testns")
|
||||
hubKubeConfigSecret := newSecret(helpers.HubKubeConfig, "testns")
|
||||
|
||||
@@ -170,6 +170,11 @@ var _ = BeforeSuite(func() {
|
||||
return hub.EnableHubRegistrationFeature("ClusterImporter")
|
||||
}).Should(Succeed())
|
||||
|
||||
By("Enable NetworkPolicies Feature")
|
||||
Eventually(func() error {
|
||||
return hub.EnableHubRegistrationFeature("NetworkPolicies")
|
||||
}).Should(Succeed())
|
||||
|
||||
By("Enable CleanUpCompletedManifestWork feature gate")
|
||||
Eventually(func() error {
|
||||
return hub.EnableHubWorkFeature("CleanUpCompletedManifestWork")
|
||||
|
||||
@@ -44,19 +44,19 @@ var _ = ginkgo.Describe("ClusterManager Default Mode with aws registration", fun
|
||||
|
||||
if clusterManager.Spec.RegistrationConfiguration == nil {
|
||||
clusterManager.Spec.RegistrationConfiguration = &operatorapiv1.RegistrationHubConfiguration{}
|
||||
clusterManager.Spec.RegistrationConfiguration.RegistrationDrivers = []operatorapiv1.RegistrationDriverHub{
|
||||
{
|
||||
AuthType: operatorapiv1.AwsIrsaAuthType,
|
||||
AwsIrsa: &operatorapiv1.AwsIrsaConfig{
|
||||
HubClusterArn: hubClusterArn,
|
||||
Tags: []string{
|
||||
"product:v1:tenant:app-name=My-App",
|
||||
"product:v1:tenant:created-by=Team-1",
|
||||
},
|
||||
AutoApprovedIdentities: []string{"arn:aws:eks:us-west-2:123456789013:cluster/.*", "arn:aws:eks:us-west-2:123456789012:cluster/.*"},
|
||||
}
|
||||
clusterManager.Spec.RegistrationConfiguration.RegistrationDrivers = []operatorapiv1.RegistrationDriverHub{
|
||||
{
|
||||
AuthType: operatorapiv1.AwsIrsaAuthType,
|
||||
AwsIrsa: &operatorapiv1.AwsIrsaConfig{
|
||||
HubClusterArn: hubClusterArn,
|
||||
Tags: []string{
|
||||
"product:v1:tenant:app-name=My-App",
|
||||
"product:v1:tenant:created-by=Team-1",
|
||||
},
|
||||
AutoApprovedIdentities: []string{"arn:aws:eks:us-west-2:123456789013:cluster/.*", "arn:aws:eks:us-west-2:123456789012:cluster/.*"},
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
_, err = operatorClient.OperatorV1().ClusterManagers().Update(context.Background(),
|
||||
clusterManager, metav1.UpdateOptions{})
|
||||
@@ -70,7 +70,17 @@ var _ = ginkgo.Describe("ClusterManager Default Mode with aws registration", fun
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clusterManager.Spec.RegistrationConfiguration = nil
|
||||
var featureGates []operatorapiv1.FeatureGate
|
||||
if clusterManager.Spec.RegistrationConfiguration != nil {
|
||||
featureGates = clusterManager.Spec.RegistrationConfiguration.FeatureGates
|
||||
}
|
||||
if len(featureGates) == 0 {
|
||||
clusterManager.Spec.RegistrationConfiguration = nil
|
||||
} else {
|
||||
clusterManager.Spec.RegistrationConfiguration = &operatorapiv1.RegistrationHubConfiguration{
|
||||
FeatureGates: featureGates,
|
||||
}
|
||||
}
|
||||
_, err = operatorClient.OperatorV1().ClusterManagers().Update(context.Background(),
|
||||
clusterManager, metav1.UpdateOptions{})
|
||||
return err
|
||||
@@ -141,14 +151,14 @@ var _ = ginkgo.Describe("ClusterManager Default Mode with aws registration", fun
|
||||
|
||||
if clusterManager.Spec.RegistrationConfiguration == nil {
|
||||
clusterManager.Spec.RegistrationConfiguration = &operatorapiv1.RegistrationHubConfiguration{}
|
||||
clusterManager.Spec.RegistrationConfiguration.RegistrationDrivers = []operatorapiv1.RegistrationDriverHub{
|
||||
{
|
||||
AuthType: operatorapiv1.AwsIrsaAuthType,
|
||||
AwsIrsa: &operatorapiv1.AwsIrsaConfig{
|
||||
HubClusterArn: hubClusterArn,
|
||||
},
|
||||
}
|
||||
clusterManager.Spec.RegistrationConfiguration.RegistrationDrivers = []operatorapiv1.RegistrationDriverHub{
|
||||
{
|
||||
AuthType: operatorapiv1.AwsIrsaAuthType,
|
||||
AwsIrsa: &operatorapiv1.AwsIrsaConfig{
|
||||
HubClusterArn: hubClusterArn,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
_, err = operatorClient.OperatorV1().ClusterManagers().Update(context.Background(),
|
||||
clusterManager, metav1.UpdateOptions{})
|
||||
@@ -162,7 +172,17 @@ var _ = ginkgo.Describe("ClusterManager Default Mode with aws registration", fun
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clusterManager.Spec.RegistrationConfiguration = nil
|
||||
var featureGates []operatorapiv1.FeatureGate
|
||||
if clusterManager.Spec.RegistrationConfiguration != nil {
|
||||
featureGates = clusterManager.Spec.RegistrationConfiguration.FeatureGates
|
||||
}
|
||||
if len(featureGates) == 0 {
|
||||
clusterManager.Spec.RegistrationConfiguration = nil
|
||||
} else {
|
||||
clusterManager.Spec.RegistrationConfiguration = &operatorapiv1.RegistrationHubConfiguration{
|
||||
FeatureGates: featureGates,
|
||||
}
|
||||
}
|
||||
_, err = operatorClient.OperatorV1().ClusterManagers().Update(context.Background(),
|
||||
clusterManager, metav1.UpdateOptions{})
|
||||
return err
|
||||
|
||||
@@ -327,31 +327,37 @@ func enableGRPCAuth(operatorClient operatorclient.Interface, clusterManagerName
|
||||
return err
|
||||
}
|
||||
|
||||
clusterManager.Spec.RegistrationConfiguration = &operatorapiv1.RegistrationHubConfiguration{
|
||||
RegistrationDrivers: []operatorapiv1.RegistrationDriverHub{
|
||||
var featureGates []operatorapiv1.FeatureGate
|
||||
if clusterManager.Spec.RegistrationConfiguration != nil {
|
||||
featureGates = clusterManager.Spec.RegistrationConfiguration.FeatureGates
|
||||
}
|
||||
|
||||
registrationDrivers := []operatorapiv1.RegistrationDriverHub{
|
||||
{
|
||||
AuthType: operatorapiv1.CSRAuthType,
|
||||
},
|
||||
{
|
||||
AuthType: operatorapiv1.GRPCAuthType,
|
||||
},
|
||||
}
|
||||
if len(autoApprovedIdentities) != 0 {
|
||||
registrationDrivers = []operatorapiv1.RegistrationDriverHub{
|
||||
{
|
||||
AuthType: operatorapiv1.CSRAuthType,
|
||||
},
|
||||
{
|
||||
AuthType: operatorapiv1.GRPCAuthType,
|
||||
},
|
||||
},
|
||||
}
|
||||
if len(autoApprovedIdentities) != 0 {
|
||||
clusterManager.Spec.RegistrationConfiguration = &operatorapiv1.RegistrationHubConfiguration{
|
||||
RegistrationDrivers: []operatorapiv1.RegistrationDriverHub{
|
||||
{
|
||||
AuthType: operatorapiv1.CSRAuthType,
|
||||
},
|
||||
{
|
||||
AuthType: operatorapiv1.GRPCAuthType,
|
||||
GRPC: &operatorapiv1.GRPCRegistrationConfig{
|
||||
AutoApprovedIdentities: autoApprovedIdentities,
|
||||
},
|
||||
GRPC: &operatorapiv1.GRPCRegistrationConfig{
|
||||
AutoApprovedIdentities: autoApprovedIdentities,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
clusterManager.Spec.RegistrationConfiguration = &operatorapiv1.RegistrationHubConfiguration{
|
||||
RegistrationDrivers: registrationDrivers,
|
||||
FeatureGates: featureGates,
|
||||
}
|
||||
_, err = operatorClient.OperatorV1().ClusterManagers().Update(context.Background(),
|
||||
clusterManager, metav1.UpdateOptions{})
|
||||
return err
|
||||
@@ -364,7 +370,17 @@ func disableGRPCAuth(operatorClient operatorclient.Interface, clusterManagerName
|
||||
return err
|
||||
}
|
||||
|
||||
clusterManager.Spec.RegistrationConfiguration = nil
|
||||
var featureGates []operatorapiv1.FeatureGate
|
||||
if clusterManager.Spec.RegistrationConfiguration != nil {
|
||||
featureGates = clusterManager.Spec.RegistrationConfiguration.FeatureGates
|
||||
}
|
||||
if len(featureGates) == 0 {
|
||||
clusterManager.Spec.RegistrationConfiguration = nil
|
||||
} else {
|
||||
clusterManager.Spec.RegistrationConfiguration = &operatorapiv1.RegistrationHubConfiguration{
|
||||
FeatureGates: featureGates,
|
||||
}
|
||||
}
|
||||
_, err = operatorClient.OperatorV1().ClusterManagers().Update(context.Background(),
|
||||
clusterManager, metav1.UpdateOptions{})
|
||||
return err
|
||||
|
||||
@@ -421,8 +421,8 @@ var _ = ginkgo.Describe("ClusterManager Hosted Mode", func() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(actual.Status.RelatedResources) != 43 {
|
||||
return fmt.Errorf("should get 43 relatedResources, actual got %v, %v",
|
||||
if len(actual.Status.RelatedResources) != 48 {
|
||||
return fmt.Errorf("should get 48 relatedResources, actual got %v, %v",
|
||||
len(actual.Status.RelatedResources), actual.Status.RelatedResources)
|
||||
}
|
||||
return nil
|
||||
@@ -500,8 +500,8 @@ var _ = ginkgo.Describe("ClusterManager Hosted Mode", func() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(actual.Status.RelatedResources) != 47 {
|
||||
return fmt.Errorf("should get 47 relatedResources, actual got %v, %v",
|
||||
if len(actual.Status.RelatedResources) != 52 {
|
||||
return fmt.Errorf("should get 52 relatedResources, actual got %v, %v",
|
||||
len(actual.Status.RelatedResources), actual.Status.RelatedResources)
|
||||
}
|
||||
return nil
|
||||
@@ -714,8 +714,8 @@ var _ = ginkgo.Describe("ClusterManager Hosted Mode", func() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(actual.Status.RelatedResources) != 42 {
|
||||
return fmt.Errorf("should get 42 relatedResources, actual got %v, %v",
|
||||
if len(actual.Status.RelatedResources) != 47 {
|
||||
return fmt.Errorf("should get 47 relatedResources, actual got %v, %v",
|
||||
len(actual.Status.RelatedResources), actual.Status.RelatedResources)
|
||||
}
|
||||
return nil
|
||||
@@ -775,8 +775,8 @@ var _ = ginkgo.Describe("ClusterManager Hosted Mode", func() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(actual.Status.RelatedResources) != 47 {
|
||||
return fmt.Errorf("should get 47 relatedResources, actual got %v, %v", len(actual.Status.RelatedResources), actual.Status.RelatedResources)
|
||||
if len(actual.Status.RelatedResources) != 52 {
|
||||
return fmt.Errorf("should get 52 relatedResources, actual got %v, %v", len(actual.Status.RelatedResources), actual.Status.RelatedResources)
|
||||
}
|
||||
return nil
|
||||
}, eventuallyTimeout, eventuallyInterval).ShouldNot(gomega.HaveOccurred())
|
||||
|
||||
@@ -424,8 +424,8 @@ var _ = ginkgo.Describe("ClusterManager Default Mode", ginkgo.Ordered, func() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(actual.Status.RelatedResources) != 44 {
|
||||
return fmt.Errorf("should get 44 relatedResources, actual got %v, %v",
|
||||
if len(actual.Status.RelatedResources) != 49 {
|
||||
return fmt.Errorf("should get 49 relatedResources, actual got %v, %v",
|
||||
len(actual.Status.RelatedResources), actual.Status.RelatedResources)
|
||||
}
|
||||
return nil
|
||||
@@ -503,8 +503,8 @@ var _ = ginkgo.Describe("ClusterManager Default Mode", ginkgo.Ordered, func() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(actual.Status.RelatedResources) != 48 {
|
||||
return fmt.Errorf("should get 48 relatedResources, actual got %v, %v",
|
||||
if len(actual.Status.RelatedResources) != 53 {
|
||||
return fmt.Errorf("should get 53 relatedResources, actual got %v, %v",
|
||||
len(actual.Status.RelatedResources), actual.Status.RelatedResources)
|
||||
}
|
||||
return nil
|
||||
@@ -715,8 +715,8 @@ var _ = ginkgo.Describe("ClusterManager Default Mode", ginkgo.Ordered, func() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(actual.Status.RelatedResources) != 43 {
|
||||
return fmt.Errorf("should get 43 relatedResources, actual got %v", len(actual.Status.RelatedResources))
|
||||
if len(actual.Status.RelatedResources) != 48 {
|
||||
return fmt.Errorf("should get 48 relatedResources, actual got %v", len(actual.Status.RelatedResources))
|
||||
}
|
||||
return nil
|
||||
}, eventuallyTimeout, eventuallyInterval).ShouldNot(gomega.HaveOccurred())
|
||||
@@ -771,8 +771,8 @@ var _ = ginkgo.Describe("ClusterManager Default Mode", ginkgo.Ordered, func() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(actual.Status.RelatedResources) != 48 {
|
||||
return fmt.Errorf("should get 48 relatedResources, actual got %v", len(actual.Status.RelatedResources))
|
||||
if len(actual.Status.RelatedResources) != 53 {
|
||||
return fmt.Errorf("should get 53 relatedResources, actual got %v", len(actual.Status.RelatedResources))
|
||||
}
|
||||
return nil
|
||||
}, eventuallyTimeout, eventuallyInterval).ShouldNot(gomega.HaveOccurred())
|
||||
@@ -1147,8 +1147,8 @@ var _ = ginkgo.Describe("ClusterManager Default Mode", ginkgo.Ordered, func() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(actual.Status.RelatedResources) != 49 {
|
||||
return fmt.Errorf("should get 49 relatedResources, actual got %v", len(actual.Status.RelatedResources))
|
||||
if len(actual.Status.RelatedResources) != 54 {
|
||||
return fmt.Errorf("should get 54 relatedResources, actual got %v", len(actual.Status.RelatedResources))
|
||||
}
|
||||
return nil
|
||||
}, eventuallyTimeout, eventuallyInterval).ShouldNot(gomega.HaveOccurred())
|
||||
@@ -1659,7 +1659,8 @@ var _ = ginkgo.Describe("ClusterManager TLS Profile", func() {
|
||||
context.TODO(), metav1.DeleteOptions{}, metav1.ListOptions{})
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
|
||||
// Clean up PlacementConfiguration and RegistrationConfiguration to avoid affecting other tests
|
||||
// Clean up PlacementConfiguration and RegistrationConfiguration to avoid affecting other tests,
|
||||
// but keep any existing feature gates (e.g. NetworkPolicies) intact.
|
||||
gomega.Eventually(func() error {
|
||||
clusterManager, err := operatorClient.OperatorV1().ClusterManagers().Get(
|
||||
context.Background(), clusterManagerName, metav1.GetOptions{})
|
||||
@@ -1667,7 +1668,17 @@ var _ = ginkgo.Describe("ClusterManager TLS Profile", func() {
|
||||
return err
|
||||
}
|
||||
clusterManager.Spec.PlacementConfiguration = nil
|
||||
clusterManager.Spec.RegistrationConfiguration = nil
|
||||
var featureGates []operatorapiv1.FeatureGate
|
||||
if clusterManager.Spec.RegistrationConfiguration != nil {
|
||||
featureGates = clusterManager.Spec.RegistrationConfiguration.FeatureGates
|
||||
}
|
||||
if len(featureGates) == 0 {
|
||||
clusterManager.Spec.RegistrationConfiguration = nil
|
||||
} else {
|
||||
clusterManager.Spec.RegistrationConfiguration = &operatorapiv1.RegistrationHubConfiguration{
|
||||
FeatureGates: featureGates,
|
||||
}
|
||||
}
|
||||
_, err = operatorClient.OperatorV1().ClusterManagers().Update(
|
||||
context.Background(), clusterManager, metav1.UpdateOptions{})
|
||||
return err
|
||||
|
||||
@@ -138,6 +138,14 @@ var _ = ginkgo.BeforeSuite(func() {
|
||||
DeployOption: operatorapiv1.ClusterManagerDeployOption{
|
||||
Mode: operatorapiv1.InstallModeDefault,
|
||||
},
|
||||
RegistrationConfiguration: &operatorapiv1.RegistrationHubConfiguration{
|
||||
FeatureGates: []operatorapiv1.FeatureGate{
|
||||
{
|
||||
Feature: "NetworkPolicies",
|
||||
Mode: operatorapiv1.FeatureGateModeTypeEnable,
|
||||
},
|
||||
},
|
||||
},
|
||||
WorkConfiguration: &operatorapiv1.WorkConfiguration{
|
||||
WorkDriver: operatorapiv1.WorkDriverTypeKube,
|
||||
FeatureGates: []operatorapiv1.FeatureGate{
|
||||
@@ -177,6 +185,14 @@ var _ = ginkgo.BeforeSuite(func() {
|
||||
},
|
||||
},
|
||||
},
|
||||
RegistrationConfiguration: &operatorapiv1.RegistrationHubConfiguration{
|
||||
FeatureGates: []operatorapiv1.FeatureGate{
|
||||
{
|
||||
Feature: "NetworkPolicies",
|
||||
Mode: operatorapiv1.FeatureGateModeTypeEnable,
|
||||
},
|
||||
},
|
||||
},
|
||||
WorkConfiguration: &operatorapiv1.WorkConfiguration{
|
||||
WorkDriver: operatorapiv1.WorkDriverTypeKube,
|
||||
FeatureGates: []operatorapiv1.FeatureGate{
|
||||
|
||||
Reference in New Issue
Block a user