diff --git a/artifacts/example/clusteroverridepolicy.yaml b/artifacts/example/clusteroverridepolicy.yaml index 856449697..4dfce9d86 100644 --- a/artifacts/example/clusteroverridepolicy.yaml +++ b/artifacts/example/clusteroverridepolicy.yaml @@ -8,11 +8,12 @@ spec: kind: Deployment name: nginx namespace: default - targetCluster: - clusterNames: - - member1 - overriders: - plaintext: - - operator: replace - path: /spec/replicas - value: 1 + overrideRules: + - targetCluster: + clusterNames: + - member1 + overriders: + plaintext: + - operator: replace + path: /spec/replicas + value: 1 diff --git a/artifacts/example/example-override.yaml b/artifacts/example/example-override.yaml index 465fce3ba..82dea2164 100644 --- a/artifacts/example/example-override.yaml +++ b/artifacts/example/example-override.yaml @@ -13,22 +13,23 @@ spec: matchLabels: image: nginx # this override policy will only apply to resources propagated to the matching clusters - targetCluster: - clusterNames: # user can either select cluster by names or by labelselector - - dc-1-cluster-1 - - dc-1-cluster-2 - labelSelector: - matchLabels: - failuredomain.kubernetes.io/region: dc1 - # all matching targetClusters would share the same set of overrides below - overriders: - plaintext: - - path: "/spec/template/spec/containers/0/image" - operator: replace - value: "dc-1.registry.io/nginx:1.17.0-alpine" - - path: "/metadata/annotations" - operator: add - value: - foo: bar - - path: "/metadata/annotations/foo" - operator: remove + overrideRules: + - targetCluster: + clusterNames: # user can either select cluster by names or by labelselector + - dc-1-cluster-1 + - dc-1-cluster-2 + labelSelector: + matchLabels: + failuredomain.kubernetes.io/region: dc1 + # all matching targetClusters would share the same set of overrides below + overriders: + plaintext: + - path: "/spec/template/spec/containers/0/image" + operator: replace + value: "dc-1.registry.io/nginx:1.17.0-alpine" + - path: "/metadata/annotations" + operator: add + value: + foo: bar + - path: "/metadata/annotations/foo" + operator: remove diff --git a/artifacts/example/overridepolicy_command.yaml b/artifacts/example/overridepolicy_command.yaml index aa8b0403d..b6c317d62 100644 --- a/artifacts/example/overridepolicy_command.yaml +++ b/artifacts/example/overridepolicy_command.yaml @@ -7,12 +7,13 @@ spec: resourceSelectors: - apiVersion: apps/v1 kind: Deployment - targetCluster: - clusterNames: - - member1 - overriders: - commandOverrider: - - containerName: alpine - operator: add - value: - - test + overrideRules: + - targetCluster: + clusterNames: + - member1 + overriders: + commandOverrider: + - containerName: alpine + operator: add + value: + - test diff --git a/artifacts/example/overridepolicy_image.yaml b/artifacts/example/overridepolicy_image.yaml index a2c878712..7fc266ed9 100644 --- a/artifacts/example/overridepolicy_image.yaml +++ b/artifacts/example/overridepolicy_image.yaml @@ -7,12 +7,22 @@ spec: resourceSelectors: - apiVersion: apps/v1 kind: Deployment - targetCluster: - labelSelector: - matchLabels: - location: us - overriders: - imageOverrider: - - component: Registry - operator: replace - value: fictional.registry.us + overrideRules: + - targetCluster: + labelSelector: + matchLabels: + location: us + overriders: + imageOverrider: + - component: Registry + operator: replace + value: fictional.registry.us + - targetCluster: + labelSelector: + matchLabels: + location: cn + overriders: + imageOverrider: + - component: Registry + operator: replace + value: fictional.registry.cn diff --git a/artifacts/example/overridepolicy_simple.yaml b/artifacts/example/overridepolicy_simple.yaml index 4fb4c951f..411d7005a 100644 --- a/artifacts/example/overridepolicy_simple.yaml +++ b/artifacts/example/overridepolicy_simple.yaml @@ -8,12 +8,13 @@ spec: - apiVersion: apps/v1 kind: Deployment name: nginx - targetCluster: - clusterNames: - - member1 - overriders: - plaintext: - - path: "/metadata/annotations" - operator: add - value: - foo: bar + overrideRules: + - targetCluster: + clusterNames: + - member1 + overriders: + plaintext: + - path: "/metadata/annotations" + operator: add + value: + foo: bar diff --git a/pkg/util/overridemanager/overridemanager.go b/pkg/util/overridemanager/overridemanager.go index 0e51b9f04..71e0f9b34 100644 --- a/pkg/util/overridemanager/overridemanager.go +++ b/pkg/util/overridemanager/overridemanager.go @@ -33,6 +33,12 @@ type overrideOption struct { Value interface{} `json:"value,omitempty"` } +type policyOverriders struct { + name string + namespace string + overriders policyv1alpha1.Overriders +} + type overrideManagerImpl struct { client.Client } @@ -89,20 +95,20 @@ func (o *overrideManagerImpl) applyClusterOverrides(rawObj *unstructured.Unstruc return nil, nil } - matchingPolicies := o.getMatchingClusterOverridePolicies(policyList.Items, rawObj, cluster) - if len(matchingPolicies) == 0 { + matchingPolicyOverriders := o.getOverridersFromClusterOverridePolicies(policyList.Items, rawObj, cluster) + if len(matchingPolicyOverriders) == 0 { klog.V(2).Infof("No cluster override policy for resource: %s/%s", rawObj.GetNamespace(), rawObj.GetName()) return nil, nil } appliedList := &AppliedOverrides{} - for _, p := range matchingPolicies { - if err := applyPolicyOverriders(rawObj, p.Spec.Overriders); err != nil { - klog.Errorf("Failed to apply cluster overrides(%s) for resource(%s/%s), error: %v", p.Name, rawObj.GetNamespace(), rawObj.GetName(), err) + for _, p := range matchingPolicyOverriders { + if err := applyPolicyOverriders(rawObj, p.overriders); err != nil { + klog.Errorf("Failed to apply cluster overrides(%s) for resource(%s/%s), error: %v", p.name, rawObj.GetNamespace(), rawObj.GetName(), err) return nil, err } - klog.V(2).Infof("Applied cluster overrides(%s) for %s/%s", p.Name, rawObj.GetNamespace(), rawObj.GetName()) - appliedList.Add(p.Name, p.Spec.Overriders) + klog.V(2).Infof("Applied cluster overrides(%s) for resource(%s/%s)", p.name, rawObj.GetNamespace(), rawObj.GetName()) + appliedList.Add(p.name, p.overriders) } return appliedList, nil @@ -121,26 +127,26 @@ func (o *overrideManagerImpl) applyNamespacedOverrides(rawObj *unstructured.Unst return nil, nil } - matchingPolicies := o.getMatchingOverridePolicies(policyList.Items, rawObj, cluster) - if len(matchingPolicies) == 0 { + matchingPolicyOverriders := o.getOverridersFromOverridePolicies(policyList.Items, rawObj, cluster) + if len(matchingPolicyOverriders) == 0 { klog.V(2).Infof("No override policy for resource(%s/%s)", rawObj.GetNamespace(), rawObj.GetName()) return nil, nil } appliedList := &AppliedOverrides{} - for _, p := range matchingPolicies { - if err := applyPolicyOverriders(rawObj, p.Spec.Overriders); err != nil { - klog.Errorf("Failed to apply overrides(%s/%s) for resource(%s/%s), error: %v", p.Namespace, p.Name, rawObj.GetNamespace(), rawObj.GetName(), err) + for _, p := range matchingPolicyOverriders { + if err := applyPolicyOverriders(rawObj, p.overriders); err != nil { + klog.Errorf("Failed to apply overrides(%s/%s) for resource(%s/%s), error: %v", p.namespace, p.name, rawObj.GetNamespace(), rawObj.GetName(), err) return nil, err } - klog.V(2).Infof("Applied overrides(%s/%s) for resource(%s/%s)", p.Namespace, p.Name, rawObj.GetNamespace(), rawObj.GetName()) - appliedList.Add(p.Name, p.Spec.Overriders) + klog.V(2).Infof("Applied overrides(%s/%s) for resource(%s/%s)", p.namespace, p.name, rawObj.GetNamespace(), rawObj.GetName()) + appliedList.Add(p.name, p.overriders) } return appliedList, nil } -func (o *overrideManagerImpl) getMatchingClusterOverridePolicies(policies []policyv1alpha1.ClusterOverridePolicy, resource *unstructured.Unstructured, cluster *clusterv1alpha1.Cluster) []policyv1alpha1.ClusterOverridePolicy { +func (o *overrideManagerImpl) getOverridersFromClusterOverridePolicies(policies []policyv1alpha1.ClusterOverridePolicy, resource *unstructured.Unstructured, cluster *clusterv1alpha1.Cluster) []policyOverriders { resourceMatchingPolicies := make([]policyv1alpha1.ClusterOverridePolicy, 0) for _, policy := range policies { if policy.Spec.ResourceSelectors == nil { @@ -153,29 +159,41 @@ func (o *overrideManagerImpl) getMatchingClusterOverridePolicies(policies []poli } } - clusterMatchingPolicies := make([]policyv1alpha1.ClusterOverridePolicy, 0) + clusterMatchingPolicyOverriders := make([]policyOverriders, 0) for _, policy := range resourceMatchingPolicies { - if policy.Spec.TargetCluster == nil { - clusterMatchingPolicies = append(clusterMatchingPolicies, policy) - continue + overrideRules := policy.Spec.OverrideRules + // Since the tuple of '.spec.TargetCluster' and '.spec.Overriders' can not co-exist with '.spec.OverrideRules' + // (guaranteed by webhook), so we only look '.spec.OverrideRules' here. + if len(overrideRules) == 0 { + overrideRules = []policyv1alpha1.RuleWithCluster{ + { + TargetCluster: policy.Spec.TargetCluster, + Overriders: policy.Spec.Overriders, + }, + } } - - if util.ClusterMatches(cluster, *policy.Spec.TargetCluster) { - clusterMatchingPolicies = append(clusterMatchingPolicies, policy) + for _, rule := range overrideRules { + if rule.TargetCluster == nil || (rule.TargetCluster != nil && util.ClusterMatches(cluster, *rule.TargetCluster)) { + clusterMatchingPolicyOverriders = append(clusterMatchingPolicyOverriders, policyOverriders{ + name: policy.Name, + namespace: policy.Namespace, + overriders: rule.Overriders, + }) + } } } // select policy in which at least one PlaintextOverrider matches target resource. // TODO(RainbowMango): check if the overrider instructions can be applied to target resource. - sort.Slice(clusterMatchingPolicies, func(i, j int) bool { - return clusterMatchingPolicies[i].Name < clusterMatchingPolicies[j].Name + sort.Slice(clusterMatchingPolicyOverriders, func(i, j int) bool { + return clusterMatchingPolicyOverriders[i].name < clusterMatchingPolicyOverriders[j].name }) - return clusterMatchingPolicies + return clusterMatchingPolicyOverriders } -func (o *overrideManagerImpl) getMatchingOverridePolicies(policies []policyv1alpha1.OverridePolicy, resource *unstructured.Unstructured, cluster *clusterv1alpha1.Cluster) []policyv1alpha1.OverridePolicy { +func (o *overrideManagerImpl) getOverridersFromOverridePolicies(policies []policyv1alpha1.OverridePolicy, resource *unstructured.Unstructured, cluster *clusterv1alpha1.Cluster) []policyOverriders { resourceMatchingPolicies := make([]policyv1alpha1.OverridePolicy, 0) for _, policy := range policies { if policy.Spec.ResourceSelectors == nil { @@ -188,26 +206,38 @@ func (o *overrideManagerImpl) getMatchingOverridePolicies(policies []policyv1alp } } - clusterMatchingPolicies := make([]policyv1alpha1.OverridePolicy, 0) + clusterMatchingPolicyOverriders := make([]policyOverriders, 0) for _, policy := range resourceMatchingPolicies { - if policy.Spec.TargetCluster == nil { - clusterMatchingPolicies = append(clusterMatchingPolicies, policy) - continue + overrideRules := policy.Spec.OverrideRules + // Since the tuple of '.spec.TargetCluster' and '.spec.Overriders' can not co-exist with '.spec.OverrideRules' + // (guaranteed by webhook), so we only look '.spec.OverrideRules' here. + if len(overrideRules) == 0 { + overrideRules = []policyv1alpha1.RuleWithCluster{ + { + TargetCluster: policy.Spec.TargetCluster, + Overriders: policy.Spec.Overriders, + }, + } } - - if util.ClusterMatches(cluster, *policy.Spec.TargetCluster) { - clusterMatchingPolicies = append(clusterMatchingPolicies, policy) + for _, rule := range overrideRules { + if rule.TargetCluster == nil || (rule.TargetCluster != nil && util.ClusterMatches(cluster, *rule.TargetCluster)) { + clusterMatchingPolicyOverriders = append(clusterMatchingPolicyOverriders, policyOverriders{ + name: policy.Name, + namespace: policy.Namespace, + overriders: rule.Overriders, + }) + } } } // select policy in which at least one PlaintextOverrider matches target resource. // TODO(RainbowMango): check if the overrider instructions can be applied to target resource. - sort.Slice(clusterMatchingPolicies, func(i, j int) bool { - return clusterMatchingPolicies[i].Name < clusterMatchingPolicies[j].Name + sort.Slice(clusterMatchingPolicyOverriders, func(i, j int) bool { + return clusterMatchingPolicyOverriders[i].name < clusterMatchingPolicyOverriders[j].name }) - return clusterMatchingPolicies + return clusterMatchingPolicyOverriders } // applyJSONPatch applies the override on to the given unstructured object.