diff --git a/README.md b/README.md index 4e343af7..116887a5 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ For more about Open Cluster Management and its Policy Framework: **NOTE:** This will default to placing the binary in `${HOME}/.config/kustomize/plugin/`. You can change this by exporting `KUSTOMIZE_PLUGIN_HOME` to a different path. -#### Configuration +#### Configuration and use as a Generator 1. Create a `kustomization.yaml` file that points to `PolicyGenerator` manifest(s), with any additional desired patches or customizations (see @@ -80,6 +80,14 @@ For more about Open Cluster Management and its Policy Framework: kustomize build --enable-alpha-plugins ``` +#### Configuration and use as a Transformer + +The plugin can also be used as a transformer, to wrap all incoming `resources` from a +`kustomization.yaml` file into one Policy. This feature is somewhat experimental. + +An example configuration as a transformer (and its output) can be found in the +[`examples/generator/`](./examples/generator/) folder. + ### As a standalone binary In order to bypass Kustomize and run the generator binary directly: diff --git a/examples/transformer/kustomization.yaml b/examples/transformer/kustomization.yaml new file mode 100644 index 00000000..0466cbe4 --- /dev/null +++ b/examples/transformer/kustomization.yaml @@ -0,0 +1,4 @@ +resources: +- github.com/redhat-cop/gitops-catalog/advanced-cluster-management/operator/overlays/release-2.5?ref=main +transformers: +- ./policyTransformer.yaml diff --git a/examples/transformer/output.yaml b/examples/transformer/output.yaml new file mode 100644 index 00000000..f7c9b7f3 --- /dev/null +++ b/examples/transformer/output.yaml @@ -0,0 +1,105 @@ +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: placement-transformed-policy + namespace: default +spec: + clusterSelector: + matchExpressions: [] +--- +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: binding-transformed-policy + namespace: default +placementRef: + apiGroup: apps.open-cluster-management.io + kind: PlacementRule + name: placement-transformed-policy +subjects: +- apiGroup: policy.open-cluster-management.io + kind: Policy + name: transformed-policy +--- +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + annotations: + policy.open-cluster-management.io/categories: CM Configuration Management + policy.open-cluster-management.io/controls: CM-2 Baseline Configuration + policy.open-cluster-management.io/standards: NIST SP 800-53 + name: transformed-policy + namespace: default +spec: + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: transformed-policy + spec: + evaluationInterval: + compliant: 30m + noncompliant: 45s + object-templates: + - complianceType: musthave + objectDefinition: + apiVersion: v1 + kind: Namespace + metadata: + annotations: {} + labels: + openshift.io/cluster-monitoring: "true" + name: open-cluster-management + remediationAction: inform + severity: low + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: transformed-policy2 + spec: + evaluationInterval: + compliant: 30m + noncompliant: 45s + object-templates: + - complianceType: musthave + objectDefinition: + apiVersion: operators.coreos.com/v1 + kind: OperatorGroup + metadata: + annotations: {} + name: open-cluster-management + namespace: open-cluster-management + spec: + targetNamespaces: + - open-cluster-management + remediationAction: inform + severity: low + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: transformed-policy3 + spec: + evaluationInterval: + compliant: 30m + noncompliant: 45s + object-templates: + - complianceType: musthave + objectDefinition: + apiVersion: operators.coreos.com/v1alpha1 + kind: Subscription + metadata: + annotations: {} + name: advanced-cluster-management + namespace: open-cluster-management + spec: + channel: release-2.5 + installPlanApproval: Automatic + name: advanced-cluster-management + source: redhat-operators + sourceNamespace: openshift-marketplace + remediationAction: inform + severity: low diff --git a/examples/transformer/policyTransformer.yaml b/examples/transformer/policyTransformer.yaml new file mode 100644 index 00000000..0ab7ae03 --- /dev/null +++ b/examples/transformer/policyTransformer.yaml @@ -0,0 +1,16 @@ +apiVersion: policy.open-cluster-management.io/v1 +kind: PolicyGenerator +metadata: + name: policy-transformer + +policyDefaults: + namespace: default + consolidateManifests: false + evaluationInterval: + compliant: 30m + noncompliant: 45s + +policies: +- name: transformed-policy + manifests: + - path: stdin diff --git a/internal/plugin.go b/internal/plugin.go index 1e41b74b..eb5ee5ca 100644 --- a/internal/plugin.go +++ b/internal/plugin.go @@ -807,16 +807,18 @@ func (p *Plugin) assertValidConfig() error { ) } - _, err := os.Stat(manifest.Path) - if err != nil { - return fmt.Errorf( - "could not read the manifest path %s in policy %s", manifest.Path, policy.Name, - ) - } + if manifest.Path != "stdin" { + _, err := os.Stat(manifest.Path) + if err != nil { + return fmt.Errorf( + "could not read the manifest path %s in policy %s", manifest.Path, policy.Name, + ) + } - err = verifyManifestPath(p.baseDirectory, manifest.Path) - if err != nil { - return err + err = verifyManifestPath(p.baseDirectory, manifest.Path) + if err != nil { + return err + } } evalInterval := manifest.EvaluationInterval diff --git a/internal/utils.go b/internal/utils.go index 84416be0..0b9b5e19 100644 --- a/internal/utils.go +++ b/internal/utils.go @@ -55,11 +55,13 @@ func getManifests(policyConf *types.PolicyConfig) ([][]map[string]interface{}, e // Handle when a Kustomization directory is specified hasKustomize := false + for _, f := range files { _, filename := path.Split(f.Name()) if filename == "kustomization.yml" || filename == "kustomization.yaml" { hasKustomize = true manifestFiles, err = processKustomizeDir(manifest.Path) + if err != nil { return nil, err } @@ -187,6 +189,27 @@ func getPolicyTemplates(policyConf *types.PolicyConfig) ([]map[string]map[string continue } + // Annotations with these prefixes might be added to resources by kustomize, + // and should be removed when the resource is wrapped in a policy. + prefixesToDelete := []string{ + "config.kubernetes.io/path", + "config.kubernetes.io/index", + "config.k8s.io/id", + "kustomize.config.k8s.io/id", + "internal.config.kubernetes.io", + } + annotations, _, _ := unstructured.NestedStringMap(manifest, "metadata", "annotations") + + for key := range annotations { + for _, prefix := range prefixesToDelete { + if strings.HasPrefix(key, prefix) { + delete(annotations, key) + } + } + } + + _ = unstructured.SetNestedStringMap(manifest, annotations, "metadata", "annotations") + objTemplate := map[string]interface{}{ "complianceType": complianceType, "objectDefinition": manifest,