From 7ac074fad0fa0aacf0e52f44863e396aa3215c52 Mon Sep 17 00:00:00 2001 From: davis-haba Date: Tue, 31 Jan 2023 18:08:53 -0800 Subject: [PATCH 1/8] autogenerate JSONSchemaProps for Match Signed-off-by: davis-haba --- Makefile | 1 + build/update-match-schema.sh | 39 ++++ .../bases/match.gatekeeper.sh_matchcrd.yaml | 211 ++++++++++++++++++ pkg/mutation/match/match.go | 57 +---- pkg/mutation/match/match_types.go | 77 +++++++ pkg/mutation/match/zz_generated.deepcopy.go | 18 ++ pkg/target/match_schema.go | 152 +++---------- pkg/target/matchcrd_constant.go | 199 +++++++++++++++++ 8 files changed, 579 insertions(+), 175 deletions(-) create mode 100755 build/update-match-schema.sh create mode 100644 config/crd/bases/match.gatekeeper.sh_matchcrd.yaml create mode 100644 pkg/mutation/match/match_types.go create mode 100644 pkg/target/matchcrd_constant.go diff --git a/Makefile b/Makefile index 96d4c5a7e24..b818e986e39 100644 --- a/Makefile +++ b/Makefile @@ -262,6 +262,7 @@ manifests: __controller-gen paths="./apis/..." \ paths="./pkg/..." \ output:crd:artifacts:config=config/crd/bases + ./build/update-match-schema.sh rm -rf manifest_staging mkdir -p manifest_staging/deploy/experimental mkdir -p manifest_staging/charts/gatekeeper diff --git a/build/update-match-schema.sh b/build/update-match-schema.sh new file mode 100755 index 00000000000..e508160f54a --- /dev/null +++ b/build/update-match-schema.sh @@ -0,0 +1,39 @@ +# This script parses the YAML for the Match CRD, found in $CRD_FILE, and outputs +# it as a go constant in $GO_FILE. For controller-gen to generate the CRD, we +# must include the metadata and typemeta fields. Since we don't want these fields +# to exist on the real Match CRD, we embed the Match type in a dummy type that +# has the metadata/typemeta fields. We then parse out these added fields. + +GO_FILE="./pkg/target/matchcrd_constant.go" +SRC_FILE="./pkg/mutation/match/match_types.go" +CRD_FILE="./config/crd/bases/match.gatekeeper.sh_matchcrd.yaml" + +# Prepare file +cat << EOF > ${GO_FILE} +package target + +// This file is generated from $SRC_FILE via "make manifests". +// DO NOT MODIFY THIS FILE DIRECTLY! + +const matchYAML = \` +EOF + +# Delete apiVersion block, adjust indentation to un-embed the match field, escape backticks +start=$(cat ${CRD_FILE} | grep -n "description: MatchDummyCRD" | cut -d: -f1) +end=$(cat ${CRD_FILE} | grep -n "embeddedMatch:" | cut -d: -f1) +cat ${CRD_FILE} | sed "${end},$ s/ //" | sed "${start},${end}d" | sed "s/\`/\`+\"\`\"+\`/g" >> ${GO_FILE} + +# Delete the 'kind:' and 'metadataDummy:' blocks at the end. This assumes the metadataDummy +# block is immediately after the kind block, and the metadataDummy block contains only +# one line (type: object) +start=$(cat ${GO_FILE} | grep -n -E "kind:$" | cut -d: -f1) +end=$(cat ${GO_FILE} | grep -n -E "metadataDummy:$" | cut -d: -f1) +end=$((end+1)) +sed -i "${start},${end}d" ${GO_FILE} + +echo "\`" >> ${GO_FILE} + + + + + diff --git a/config/crd/bases/match.gatekeeper.sh_matchcrd.yaml b/config/crd/bases/match.gatekeeper.sh_matchcrd.yaml new file mode 100644 index 00000000000..bbc913e54d1 --- /dev/null +++ b/config/crd/bases/match.gatekeeper.sh_matchcrd.yaml @@ -0,0 +1,211 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: matchcrd.match.gatekeeper.sh +spec: + group: match.gatekeeper.sh + names: + kind: MatchDummyCRD + listKind: MatchDummyCRDList + plural: matchcrd + singular: matchdummycrd + scope: Namespaced + versions: + - name: match + schema: + openAPIV3Schema: + description: MatchDummyCRD is a "dummy" CRD to hold the Match object, which + we ultimately need to generate JSONSchemaProps. The TypeMeta and ObjectMeta + fields are required for controller-gen to generate the CRD. DO NOT CHANGE + THIS COMMENT. It is used for regex matching to extract the embedded match + CRD. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + embeddedMatch: + description: Match selects objects to apply mutations to. + properties: + excludedNamespaces: + description: 'ExcludedNamespaces is a list of namespace names. If + defined, a constraint only applies to resources not in a listed + namespace. ExcludedNamespaces also supports a prefix or suffix based + glob. For example, `excludedNamespaces: [kube-*]` matches both + `kube-system` and `kube-public`, and `excludedNamespaces: [*-system]` + matches both `kube-system` and `gatekeeper-system`.' + items: + description: 'A string that supports globbing at its front or end. + Ex: "kube-*" will match "kube-system" or "kube-public", "*-system" + will match "kube-system" or "gatekeeper-system". The asterisk + is required for wildcard matching.' + pattern: ^(\*|\*-)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\*|-\*)?$ + type: string + type: array + kinds: + items: + description: Kinds accepts a list of objects with apiGroups and + kinds fields that list the groups/kinds of objects to which the + mutation will apply. If multiple groups/kinds objects are specified, + only one match is needed for the resource to be in scope. + properties: + apiGroups: + description: APIGroups is the API groups the resources belong + to. '*' is all groups. If '*' is present, the length of the + slice must be one. Required. + items: + type: string + type: array + kinds: + items: + type: string + type: array + type: object + type: array + labelSelector: + description: 'LabelSelector is the combination of two optional fields: + `matchLabels` and `matchExpressions`. These two fields provide + different methods of selecting or excluding k8s objects based on + the label keys and values included in object metadata. All selection + expressions from both sections are ANDed to determine if an object + meets the cumulative requirements of the selector.' + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that + contains values, a key, and an operator that relates the key + and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to + a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. + type: object + type: object + name: + description: 'Name is the name of an object. If defined, it will + match against objects with the specified name. Name also supports + a prefix or suffix glob. For example, `name: pod-*` would match + both `pod-a` and `pod-b`, and `name: *-pod` would match both `a-pod` + and `b-pod`.' + pattern: ^(\*|\*-)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\*|-\*)?$ + type: string + namespaceSelector: + description: NamespaceSelector is a label selector against an object's + containing namespace or the object itself, if the object is a namespace. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that + contains values, a key, and an operator that relates the key + and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to + a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. + type: object + type: object + namespaces: + description: 'Namespaces is a list of namespace names. If defined, + a constraint only applies to resources in a listed namespace. Namespaces + also supports a prefix or suffix based glob. For example, `namespaces: + [kube-*]` matches both `kube-system` and `kube-public`, and `namespaces: + [*-system]` matches both `kube-system` and `gatekeeper-system`.' + items: + description: 'A string that supports globbing at its front or end. + Ex: "kube-*" will match "kube-system" or "kube-public", "*-system" + will match "kube-system" or "gatekeeper-system". The asterisk + is required for wildcard matching.' + pattern: ^(\*|\*-)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\*|-\*)?$ + type: string + type: array + scope: + description: Scope determines if cluster-scoped and/or namespaced-scoped + resources are matched. Accepts `*`, `Cluster`, or `Namespaced`. + (defaults to `*`) + type: string + source: + description: Source determines whether generated or original resources + are matched. Accepts `Generated`|`Original`|`All` (defaults to `All`). + A value of `Generated` will only match generated resources, while + `Original` will only match regular resources. + enum: + - All + - Generated + - Original + type: string + type: object + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadataDummy: + type: object + type: object + served: true + storage: true diff --git a/pkg/mutation/match/match.go b/pkg/mutation/match/match.go index a65af53fc18..52b8893b9d2 100644 --- a/pkg/mutation/match/match.go +++ b/pkg/mutation/match/match.go @@ -6,7 +6,6 @@ import ( "reflect" "github.com/open-policy-agent/gatekeeper/pkg/mutation/types" - "github.com/open-policy-agent/gatekeeper/pkg/util" corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -20,62 +19,8 @@ var ErrMatch = errors.New("failed to run Match criteria") // Only for use in Match, not ApplyTo. const Wildcard = "*" -// Match selects objects to apply mutations to. -// +kubebuilder:object:generate=true -type Match struct { - // Source determines whether generated or original resources are matched. - // Accepts `Generated`|`Original`|`All` (defaults to `All`). A value of - // `Generated` will only match generated resources, while `Original` will only - // match regular resources. - // +kubebuilder:validation:Enum=All;Generated;Original - Source string `json:"source,omitempty"` - Kinds []Kinds `json:"kinds,omitempty"` - // Scope determines if cluster-scoped and/or namespaced-scoped resources - // are matched. Accepts `*`, `Cluster`, or `Namespaced`. (defaults to `*`) - Scope apiextensionsv1.ResourceScope `json:"scope,omitempty"` - // Namespaces is a list of namespace names. If defined, a constraint only - // applies to resources in a listed namespace. Namespaces also supports a - // prefix or suffix based glob. For example, `namespaces: [kube-*]` matches both - // `kube-system` and `kube-public`, and `namespaces: [*-system]` matches both - // `kube-system` and `gatekeeper-system`. - Namespaces []util.Wildcard `json:"namespaces,omitempty"` - // ExcludedNamespaces is a list of namespace names. If defined, a - // constraint only applies to resources not in a listed namespace. - // ExcludedNamespaces also supports a prefix or suffix based glob. For example, - // `excludedNamespaces: [kube-*]` matches both `kube-system` and - // `kube-public`, and `excludedNamespaces: [*-system]` matches both `kube-system` and - // `gatekeeper-system`. - ExcludedNamespaces []util.Wildcard `json:"excludedNamespaces,omitempty"` - // LabelSelector is the combination of two optional fields: `matchLabels` - // and `matchExpressions`. These two fields provide different methods of - // selecting or excluding k8s objects based on the label keys and values - // included in object metadata. All selection expressions from both - // sections are ANDed to determine if an object meets the cumulative - // requirements of the selector. - LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"` - // NamespaceSelector is a label selector against an object's containing - // namespace or the object itself, if the object is a namespace. - NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty"` - // Name is the name of an object. If defined, it will match against objects with the specified - // name. Name also supports a prefix or suffix glob. For example, `name: pod-*` would match - // both `pod-a` and `pod-b`, and `name: *-pod` would match both `a-pod` and `b-pod`. - Name util.Wildcard `json:"name,omitempty"` -} - -// Kinds accepts a list of objects with apiGroups and kinds fields -// that list the groups/kinds of objects to which the mutation will apply. -// If multiple groups/kinds objects are specified, -// only one match is needed for the resource to be in scope. -// +kubebuilder:object:generate=true -type Kinds struct { - // APIGroups is the API groups the resources belong to. '*' is all groups. - // If '*' is present, the length of the slice must be one. - // Required. - APIGroups []string `json:"apiGroups,omitempty" protobuf:"bytes,1,rep,name=apiGroups"` - Kinds []string `json:"kinds,omitempty"` -} - // Matchable represent an object to be matched along with its metadata. +// +kubebuilder:object:generate=false type Matchable struct { Object client.Object Namespace *corev1.Namespace diff --git a/pkg/mutation/match/match_types.go b/pkg/mutation/match/match_types.go new file mode 100644 index 00000000000..97968bdc79b --- /dev/null +++ b/pkg/mutation/match/match_types.go @@ -0,0 +1,77 @@ +// +kubebuilder:object:generate=true +// +groupName=match.gatekeeper.sh +package match + +import ( + "github.com/open-policy-agent/gatekeeper/pkg/util" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Match selects objects to apply mutations to. +// +kubebuilder:object:generate=true +type Match struct { + // Source determines whether generated or original resources are matched. + // Accepts `Generated`|`Original`|`All` (defaults to `All`). A value of + // `Generated` will only match generated resources, while `Original` will only + // match regular resources. + // +kubebuilder:validation:Enum=All;Generated;Original + Source string `json:"source,omitempty"` + Kinds []Kinds `json:"kinds,omitempty"` + // Scope determines if cluster-scoped and/or namespaced-scoped resources + // are matched. Accepts `*`, `Cluster`, or `Namespaced`. (defaults to `*`) + Scope apiextensionsv1.ResourceScope `json:"scope,omitempty"` + // Namespaces is a list of namespace names. If defined, a constraint only + // applies to resources in a listed namespace. Namespaces also supports a + // prefix or suffix based glob. For example, `namespaces: [kube-*]` matches both + // `kube-system` and `kube-public`, and `namespaces: [*-system]` matches both + // `kube-system` and `gatekeeper-system`. + Namespaces []util.Wildcard `json:"namespaces,omitempty"` + // ExcludedNamespaces is a list of namespace names. If defined, a + // constraint only applies to resources not in a listed namespace. + // ExcludedNamespaces also supports a prefix or suffix based glob. For example, + // `excludedNamespaces: [kube-*]` matches both `kube-system` and + // `kube-public`, and `excludedNamespaces: [*-system]` matches both `kube-system` and + // `gatekeeper-system`. + ExcludedNamespaces []util.Wildcard `json:"excludedNamespaces,omitempty"` + // LabelSelector is the combination of two optional fields: `matchLabels` + // and `matchExpressions`. These two fields provide different methods of + // selecting or excluding k8s objects based on the label keys and values + // included in object metadata. All selection expressions from both + // sections are ANDed to determine if an object meets the cumulative + // requirements of the selector. + LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"` + // NamespaceSelector is a label selector against an object's containing + // namespace or the object itself, if the object is a namespace. + NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty"` + // Name is the name of an object. If defined, it will match against objects with the specified + // name. Name also supports a prefix or suffix glob. For example, `name: pod-*` would match + // both `pod-a` and `pod-b`, and `name: *-pod` would match both `a-pod` and `b-pod`. + Name util.Wildcard `json:"name,omitempty"` +} + +// Kinds accepts a list of objects with apiGroups and kinds fields +// that list the groups/kinds of objects to which the mutation will apply. +// If multiple groups/kinds objects are specified, +// only one match is needed for the resource to be in scope. +// +kubebuilder:object:generate=true +type Kinds struct { + // APIGroups is the API groups the resources belong to. '*' is all groups. + // If '*' is present, the length of the slice must be one. + // Required. + APIGroups []string `json:"apiGroups,omitempty" protobuf:"bytes,1,rep,name=apiGroups"` + Kinds []string `json:"kinds,omitempty"` +} + +// MatchDummyCRD is a "dummy" CRD to hold the Match object, which we ultimately +// need to generate JSONSchemaProps. The TypeMeta and ObjectMeta fields are +// required for controller-gen to generate the CRD. +// DO NOT CHANGE THE START OF THIS COMMENT. +// It is used for regex matching to extract the embedded match CRD. +// +kubebuilder:resource:path="matchcrd" +type MatchDummyCRD struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadataDummy,omitempty"` + + Match `json:"embeddedMatch,omitempty"` +} diff --git a/pkg/mutation/match/zz_generated.deepcopy.go b/pkg/mutation/match/zz_generated.deepcopy.go index 88cf2894ff7..759b4498abd 100644 --- a/pkg/mutation/match/zz_generated.deepcopy.go +++ b/pkg/mutation/match/zz_generated.deepcopy.go @@ -121,3 +121,21 @@ func (in *Match) DeepCopy() *Match { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MatchDummyCRD) DeepCopyInto(out *MatchDummyCRD) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Match.DeepCopyInto(&out.Match) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MatchDummyCRD. +func (in *MatchDummyCRD) DeepCopy() *MatchDummyCRD { + if in == nil { + return nil + } + out := new(MatchDummyCRD) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/target/match_schema.go b/pkg/target/match_schema.go index 314815e5e76..eddeb0c3b88 100644 --- a/pkg/target/match_schema.go +++ b/pkg/target/match_schema.go @@ -1,130 +1,44 @@ package target -import "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" +import ( + "fmt" -// This pattern is meant to match: -// -// REGULAR NAMESPACES -// - These are defined by this pattern: [a-z0-9]([-a-z0-9]*[a-z0-9])? -// - You'll see that this is the first two-thirds or so of the pattern below -// -// PREFIX OR SUFFIX BASED WILDCARDS -// - A typical namespace must end in an alphanumeric character. A prefixed wildcard -// can end in "*" (like `kube*`) or "-*" (like `kube-*`), and a suffixed wildcard -// can start with "*" (like `*system`) or "*-" (like `*-system`). -// - To implement this, we add either (\*|\*-)? as a prefix or (\*|-\*)? as a suffix. -// Using both prefixed wildcards and suffixed wildcards at once is not supported. Therefore, -// this _does not_ allow the value to start _and_ end in a wildcard (like `*-*`). -// - Crucially, this _does not_ allow the value to start or end in a dash (like `-system` or `kube-`). -// That is not a valid namespace and not a wildcard, so it's disallowed. -// -// Notably, this disallows other uses of the "*" character like: -// - * -// - k*-system -// -// See the following regexr to test this regex: https://regexr.com/6dgdj -const wildcardNSPattern = `^(\*|\*-)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\*|-\*)?$` + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/yaml" +) -func matchSchema() apiextensions.JSONSchemaProps { - // Define some repeatedly used sections - wildcardNSList := apiextensions.JSONSchemaProps{ - Type: "array", - Items: &apiextensions.JSONSchemaPropsOrArray{ - Schema: &apiextensions.JSONSchemaProps{Type: "string", Pattern: wildcardNSPattern}, - }, - } +var matchJSONSchemaProps apiextensions.JSONSchemaProps - nullableStringList := apiextensions.JSONSchemaProps{ - Type: "array", - Items: &apiextensions.JSONSchemaPropsOrArray{ - Schema: &apiextensions.JSONSchemaProps{Type: "string", Nullable: true}, - }, +func init() { + matchCRD := &apiextensionsv1.CustomResourceDefinition{} + if err := yaml.Unmarshal([]byte(matchYAML), matchCRD); err != nil { + panic(fmt.Errorf("failed to unmarshal match yaml: %w", err)) } - trueBool := true - labelSelectorSchema := apiextensions.JSONSchemaProps{ - Type: "object", - Properties: map[string]apiextensions.JSONSchemaProps{ - "matchLabels": { - Type: "object", - Description: "A mapping of label keys to sets of allowed label values for those keys. A selected resource will match all of these expressions.", - AdditionalProperties: &apiextensions.JSONSchemaPropsOrBool{ - Allows: true, - Schema: &apiextensions.JSONSchemaProps{Type: "string"}, - }, - XPreserveUnknownFields: &trueBool, - }, - "matchExpressions": { - Type: "array", - Description: "a list of label selection expressions. A selected resource will match all of these expressions.", - Items: &apiextensions.JSONSchemaPropsOrArray{ - Schema: &apiextensions.JSONSchemaProps{ - Type: "object", - Description: "a selector that specifies a label key, a set of label values, an operator that defines the relationship between the two that will match the selector.", - Properties: map[string]apiextensions.JSONSchemaProps{ - "key": { - Description: "the label key that the selector applies to.", - Type: "string", - }, - "operator": { - Type: "string", - Description: "the relationship between the label and value set that defines a matching selection.", - Enum: []apiextensions.JSON{ - "In", - "NotIn", - "Exists", - "DoesNotExist", - }, - }, - "values": { - Type: "array", - Description: "a set of label values.", - Items: &apiextensions.JSONSchemaPropsOrArray{ - Schema: &apiextensions.JSONSchemaProps{Type: "string"}, - }, - }, - }, - }, - }, - }, - }, + // Sanity checks to ensure the CRD was generated properly + if len(matchCRD.Spec.Versions) == 0 { + panic(fmt.Errorf("generated match CRD does not contain any versions")) + } + if matchCRD.Spec.Versions[0].Schema.OpenAPIV3Schema == nil { + panic(fmt.Errorf("generated match CRD has nil OpenAPIV3Schema")) } - // Make sure to copy description changes into pkg/mutation/match/match.go's `Match` struct. - return apiextensions.JSONSchemaProps{ - Type: "object", - Properties: map[string]apiextensions.JSONSchemaProps{ - "kinds": { - Type: "array", - Items: &apiextensions.JSONSchemaPropsOrArray{ - Schema: &apiextensions.JSONSchemaProps{ - Type: "object", - Description: "The Group and Kind of objects that should be matched. If multiple groups/kinds combinations are specified, an incoming resource need only match one to be in scope.", - Properties: map[string]apiextensions.JSONSchemaProps{ - "apiGroups": nullableStringList, - "kinds": nullableStringList, - }, - }, - }, - }, - "namespaces": *propsWithDescription(&wildcardNSList, "`namespaces` is a list of namespace names. If defined, a constraint only applies to resources in a listed namespace. Namespaces also supports a prefix-based glob. For example, `namespaces: [kube-*]` matches both `kube-system` and `kube-public`."), - "excludedNamespaces": *propsWithDescription(&wildcardNSList, "`excludedNamespaces` is a list of namespace names. If defined, a constraint only applies to resources not in a listed namespace. ExcludedNamespaces also supports a prefix-based glob. For example, `excludedNamespaces: [kube-*]` matches both `kube-system` and `kube-public`."), - "labelSelector": *propsWithDescription(&labelSelectorSchema, "`labelSelector` is the combination of two optional fields: `matchLabels` and `matchExpressions`. These two fields provide different methods of selecting or excluding k8s objects based on the label keys and values included in object metadata. All selection expressions from both sections are ANDed to determine if an object meets the cumulative requirements of the selector."), - "namespaceSelector": *propsWithDescription(&labelSelectorSchema, "`namespaceSelector` is a label selector against an object's containing namespace or the object itself, if the object is a namespace."), - "scope": { - Type: "string", - Description: "`scope` determines if cluster-scoped and/or namespaced-scoped resources are matched. Accepts `*`, `Cluster`, or `Namespaced`. (defaults to `*`)", - Enum: []apiextensions.JSON{ - "*", - "Cluster", - "Namespaced", - }, - }, - "name": { - Type: "string", - Description: "`name` is the name of an object. If defined, it matches against objects with the specified name. Name also supports a prefix-based glob. For example, `name: pod-*` matches both `pod-a` and `pod-b`.", - Pattern: wildcardNSPattern, - }, - }, + // Convert v1 JSONSchemaProps to versionless + rt := runtime.NewScheme() + if err := apiextensions.AddToScheme(rt); err != nil { + panic(fmt.Errorf("could not add apiextensions to scheme: %w", err)) + } + if err := apiextensionsv1.AddToScheme(rt); err != nil { + panic(fmt.Errorf("could not add apiextensionsv1 to scheme: %w", err)) } + matchJSONSchemaProps = apiextensions.JSONSchemaProps{} + if err := rt.Convert(matchCRD.Spec.Versions[0].Schema.OpenAPIV3Schema, &matchJSONSchemaProps, nil); err != nil { + panic(fmt.Errorf("could not convert match JSONSchemaProps from v1 to versionless: %w", err)) + } +} + +func matchSchema() apiextensions.JSONSchemaProps { + return matchJSONSchemaProps } diff --git a/pkg/target/matchcrd_constant.go b/pkg/target/matchcrd_constant.go new file mode 100644 index 00000000000..ad9300a2416 --- /dev/null +++ b/pkg/target/matchcrd_constant.go @@ -0,0 +1,199 @@ +package target + +// This file is generated from ./pkg/mutation/match/match_types.go via "make manifests". +// DO NOT MODIFY THIS FILE DIRECTLY! + +const matchYAML = ` +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: matchcrd.match.gatekeeper.sh +spec: + group: match.gatekeeper.sh + names: + kind: MatchDummyCRD + listKind: MatchDummyCRDList + plural: matchcrd + singular: matchdummycrd + scope: Namespaced + versions: + - name: match + schema: + openAPIV3Schema: + description: Match selects objects to apply mutations to. + properties: + excludedNamespaces: + description: 'ExcludedNamespaces is a list of namespace names. If + defined, a constraint only applies to resources not in a listed + namespace. ExcludedNamespaces also supports a prefix or suffix based + glob. For example, `+"`"+`excludedNamespaces: [kube-*]`+"`"+` matches both + `+"`"+`kube-system`+"`"+` and `+"`"+`kube-public`+"`"+`, and `+"`"+`excludedNamespaces: [*-system]`+"`"+` + matches both `+"`"+`kube-system`+"`"+` and `+"`"+`gatekeeper-system`+"`"+`.' + items: + description: 'A string that supports globbing at its front or end. + Ex: "kube-*" will match "kube-system" or "kube-public", "*-system" + will match "kube-system" or "gatekeeper-system". The asterisk + is required for wildcard matching.' + pattern: ^(\*|\*-)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\*|-\*)?$ + type: string + type: array + kinds: + items: + description: Kinds accepts a list of objects with apiGroups and + kinds fields that list the groups/kinds of objects to which the + mutation will apply. If multiple groups/kinds objects are specified, + only one match is needed for the resource to be in scope. + properties: + apiGroups: + description: APIGroups is the API groups the resources belong + to. '*' is all groups. If '*' is present, the length of the + slice must be one. Required. + items: + type: string + type: array + kinds: + items: + type: string + type: array + type: object + type: array + labelSelector: + description: 'LabelSelector is the combination of two optional fields: + `+"`"+`matchLabels`+"`"+` and `+"`"+`matchExpressions`+"`"+`. These two fields provide + different methods of selecting or excluding k8s objects based on + the label keys and values included in object metadata. All selection + expressions from both sections are ANDed to determine if an object + meets the cumulative requirements of the selector.' + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that + contains values, a key, and an operator that relates the key + and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to + a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. + type: object + type: object + name: + description: 'Name is the name of an object. If defined, it will + match against objects with the specified name. Name also supports + a prefix or suffix glob. For example, `+"`"+`name: pod-*`+"`"+` would match + both `+"`"+`pod-a`+"`"+` and `+"`"+`pod-b`+"`"+`, and `+"`"+`name: *-pod`+"`"+` would match both `+"`"+`a-pod`+"`"+` + and `+"`"+`b-pod`+"`"+`.' + pattern: ^(\*|\*-)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\*|-\*)?$ + type: string + namespaceSelector: + description: NamespaceSelector is a label selector against an object's + containing namespace or the object itself, if the object is a namespace. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that + contains values, a key, and an operator that relates the key + and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to + a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. + type: object + type: object + namespaces: + description: 'Namespaces is a list of namespace names. If defined, + a constraint only applies to resources in a listed namespace. Namespaces + also supports a prefix or suffix based glob. For example, `+"`"+`namespaces: + [kube-*]`+"`"+` matches both `+"`"+`kube-system`+"`"+` and `+"`"+`kube-public`+"`"+`, and `+"`"+`namespaces: + [*-system]`+"`"+` matches both `+"`"+`kube-system`+"`"+` and `+"`"+`gatekeeper-system`+"`"+`.' + items: + description: 'A string that supports globbing at its front or end. + Ex: "kube-*" will match "kube-system" or "kube-public", "*-system" + will match "kube-system" or "gatekeeper-system". The asterisk + is required for wildcard matching.' + pattern: ^(\*|\*-)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\*|-\*)?$ + type: string + type: array + scope: + description: Scope determines if cluster-scoped and/or namespaced-scoped + resources are matched. Accepts `+"`"+`*`+"`"+`, `+"`"+`Cluster`+"`"+`, or `+"`"+`Namespaced`+"`"+`. + (defaults to `+"`"+`*`+"`"+`) + type: string + source: + description: Source determines whether generated or original resources + are matched. Accepts `+"`"+`Generated`+"`"+`|`+"`"+`Original`+"`"+`|`+"`"+`All`+"`"+` (defaults to `+"`"+`All`+"`"+`). + A value of `+"`"+`Generated`+"`"+` will only match generated resources, while + `+"`"+`Original`+"`"+` will only match regular resources. + enum: + - All + - Generated + - Original + type: string + type: object + type: object + served: true + storage: true +` From 1695876de657c579f3695abeb888bb94b620dc4b Mon Sep 17 00:00:00 2001 From: davis-haba Date: Tue, 31 Jan 2023 18:34:08 -0800 Subject: [PATCH 2/8] e2e test for source field in Constraint Signed-off-by: davis-haba --- test/bats/test.bats | 15 +++++++++++++++ .../loadbalancers_must_have_env_source_gen.yaml | 14 ++++++++++++++ website/docs/expansion.md | 2 +- 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 test/expansion/loadbalancers_must_have_env_source_gen.yaml diff --git a/test/bats/test.bats b/test/bats/test.bats index 24bf49a8720..a3fee54b18c 100644 --- a/test/bats/test.bats +++ b/test/bats/test.bats @@ -426,6 +426,20 @@ __expansion_audit_test() { assert_success # with a violating deployment on cluster, test that audit produces expansion violations wait_for_process ${WAIT_TIME} ${SLEEP_TIME} "__expansion_audit_test" + run kubectl delete -f test/expansion/warn_expand_deployments + run kubectl delete -f test/expansion/deployment_no_label.yaml + + # test source field on Constraints + run kubectl apply -f test/expansion/expand_deployments.yaml + run kubectl delete --ignore-not-found -f test/expansion/loadbalancers_must_have_env.yaml + run kubectl apply -f test/expansion/loadbalancers_must_have_env_source_gen.yaml + wait_for_process ${WAIT_TIME} ${SLEEP_TIME} "constraint_enforced k8srequiredlabels loadbalancers-must-have-env-gen" + # a generated pod should be denied + run kubectl apply -f test/expansion/deployment_no_label.yaml + assert_failure + # an original pod should be accepted, as the constraint only matches generated pods + run kubectl run nginx --image=nginx --dry-run=server --output json + assert_success # cleanup run kubectl delete --ignore-not-found namespace loadbalancers @@ -433,6 +447,7 @@ __expansion_audit_test() { run kubectl delete --ignore-not-found -f test/expansion/warn_expand_deployments.yaml run kubectl delete --ignore-not-found -f test/expansion/k8srequiredlabels_ct.yaml run kubectl delete --ignore-not-found -f test/expansion/loadbalancers_must_have_env.yaml + run kubectl delete --ignore-not-found -f test/expansion/loadbalancers_must_have_env_source_gen.yaml run kubectl delete --ignore-not-found -f test/expansion/assignmeta_env.yaml run kubectl delete --ignore-not-found -f test/expansion/deployment_no_label.yaml run kubectl delete --ignore-not-found -f test/expansion/deployment_with_label.yaml diff --git a/test/expansion/loadbalancers_must_have_env_source_gen.yaml b/test/expansion/loadbalancers_must_have_env_source_gen.yaml new file mode 100644 index 00000000000..5d9060b8586 --- /dev/null +++ b/test/expansion/loadbalancers_must_have_env_source_gen.yaml @@ -0,0 +1,14 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sRequiredLabels +metadata: + name: loadbalancers-must-have-env-gen +spec: + match: + scope: "Namespaced" + namespaces: [ "loadbalancers" ] + kinds: + - apiGroups: [ "" ] + kinds: [ "Pod" ] + source: "Generated" + parameters: + labels: [ "env" ] diff --git a/website/docs/expansion.md b/website/docs/expansion.md index 232149ead13..8b35ec06001 100644 --- a/website/docs/expansion.md +++ b/website/docs/expansion.md @@ -120,7 +120,7 @@ specified by the Constraint in violation. #### Match Source The `source` field on the `match` API, present in the Mutation -and `ConstraintTemplate` kinds, specifies if the config should match Generated ( +and `Constraint` kinds, specifies if the config should match Generated ( i.e. expanded) resources, Original resources, or both. The `source` field is an `enum` which accepts the following values: From f6680f0972cd3602029ab1b446e9097f2cb162c9 Mon Sep 17 00:00:00 2001 From: davis-haba Date: Tue, 31 Jan 2023 18:53:54 -0800 Subject: [PATCH 3/8] fix linter Signed-off-by: davis-haba --- build/update-match-schema.sh | 3 +- .../bases/match.gatekeeper.sh_matchcrd.yaml | 4 +-- pkg/target/matchcrd_constant.go | 30 +++++++++---------- pkg/target/target.go | 6 ---- 4 files changed, 19 insertions(+), 24 deletions(-) diff --git a/build/update-match-schema.sh b/build/update-match-schema.sh index e508160f54a..5c16a3ff377 100755 --- a/build/update-match-schema.sh +++ b/build/update-match-schema.sh @@ -12,8 +12,9 @@ CRD_FILE="./config/crd/bases/match.gatekeeper.sh_matchcrd.yaml" cat << EOF > ${GO_FILE} package target -// This file is generated from $SRC_FILE via "make manifests". // DO NOT MODIFY THIS FILE DIRECTLY! +// This file is generated from $SRC_FILE via "make manifests". +// If there are changes, you may have to gofmt this file afterwards. const matchYAML = \` EOF diff --git a/config/crd/bases/match.gatekeeper.sh_matchcrd.yaml b/config/crd/bases/match.gatekeeper.sh_matchcrd.yaml index bbc913e54d1..f1c19529675 100644 --- a/config/crd/bases/match.gatekeeper.sh_matchcrd.yaml +++ b/config/crd/bases/match.gatekeeper.sh_matchcrd.yaml @@ -21,8 +21,8 @@ spec: description: MatchDummyCRD is a "dummy" CRD to hold the Match object, which we ultimately need to generate JSONSchemaProps. The TypeMeta and ObjectMeta fields are required for controller-gen to generate the CRD. DO NOT CHANGE - THIS COMMENT. It is used for regex matching to extract the embedded match - CRD. + THE START OF THIS COMMENT. It is used for regex matching to extract the + embedded match CRD. properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation diff --git a/pkg/target/matchcrd_constant.go b/pkg/target/matchcrd_constant.go index ad9300a2416..a023563106d 100644 --- a/pkg/target/matchcrd_constant.go +++ b/pkg/target/matchcrd_constant.go @@ -30,9 +30,9 @@ spec: description: 'ExcludedNamespaces is a list of namespace names. If defined, a constraint only applies to resources not in a listed namespace. ExcludedNamespaces also supports a prefix or suffix based - glob. For example, `+"`"+`excludedNamespaces: [kube-*]`+"`"+` matches both - `+"`"+`kube-system`+"`"+` and `+"`"+`kube-public`+"`"+`, and `+"`"+`excludedNamespaces: [*-system]`+"`"+` - matches both `+"`"+`kube-system`+"`"+` and `+"`"+`gatekeeper-system`+"`"+`.' + glob. For example, ` + "`" + `excludedNamespaces: [kube-*]` + "`" + ` matches both + ` + "`" + `kube-system` + "`" + ` and ` + "`" + `kube-public` + "`" + `, and ` + "`" + `excludedNamespaces: [*-system]` + "`" + ` + matches both ` + "`" + `kube-system` + "`" + ` and ` + "`" + `gatekeeper-system` + "`" + `.' items: description: 'A string that supports globbing at its front or end. Ex: "kube-*" will match "kube-system" or "kube-public", "*-system" @@ -63,7 +63,7 @@ spec: type: array labelSelector: description: 'LabelSelector is the combination of two optional fields: - `+"`"+`matchLabels`+"`"+` and `+"`"+`matchExpressions`+"`"+`. These two fields provide + ` + "`" + `matchLabels` + "`" + ` and ` + "`" + `matchExpressions` + "`" + `. These two fields provide different methods of selecting or excluding k8s objects based on the label keys and values included in object metadata. All selection expressions from both sections are ANDed to determine if an object @@ -113,9 +113,9 @@ spec: name: description: 'Name is the name of an object. If defined, it will match against objects with the specified name. Name also supports - a prefix or suffix glob. For example, `+"`"+`name: pod-*`+"`"+` would match - both `+"`"+`pod-a`+"`"+` and `+"`"+`pod-b`+"`"+`, and `+"`"+`name: *-pod`+"`"+` would match both `+"`"+`a-pod`+"`"+` - and `+"`"+`b-pod`+"`"+`.' + a prefix or suffix glob. For example, ` + "`" + `name: pod-*` + "`" + ` would match + both ` + "`" + `pod-a` + "`" + ` and ` + "`" + `pod-b` + "`" + `, and ` + "`" + `name: *-pod` + "`" + ` would match both ` + "`" + `a-pod` + "`" + ` + and ` + "`" + `b-pod` + "`" + `.' pattern: ^(\*|\*-)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\*|-\*)?$ type: string namespaceSelector: @@ -166,9 +166,9 @@ spec: namespaces: description: 'Namespaces is a list of namespace names. If defined, a constraint only applies to resources in a listed namespace. Namespaces - also supports a prefix or suffix based glob. For example, `+"`"+`namespaces: - [kube-*]`+"`"+` matches both `+"`"+`kube-system`+"`"+` and `+"`"+`kube-public`+"`"+`, and `+"`"+`namespaces: - [*-system]`+"`"+` matches both `+"`"+`kube-system`+"`"+` and `+"`"+`gatekeeper-system`+"`"+`.' + also supports a prefix or suffix based glob. For example, ` + "`" + `namespaces: + [kube-*]` + "`" + ` matches both ` + "`" + `kube-system` + "`" + ` and ` + "`" + `kube-public` + "`" + `, and ` + "`" + `namespaces: + [*-system]` + "`" + ` matches both ` + "`" + `kube-system` + "`" + ` and ` + "`" + `gatekeeper-system` + "`" + `.' items: description: 'A string that supports globbing at its front or end. Ex: "kube-*" will match "kube-system" or "kube-public", "*-system" @@ -179,14 +179,14 @@ spec: type: array scope: description: Scope determines if cluster-scoped and/or namespaced-scoped - resources are matched. Accepts `+"`"+`*`+"`"+`, `+"`"+`Cluster`+"`"+`, or `+"`"+`Namespaced`+"`"+`. - (defaults to `+"`"+`*`+"`"+`) + resources are matched. Accepts ` + "`" + `*` + "`" + `, ` + "`" + `Cluster` + "`" + `, or ` + "`" + `Namespaced` + "`" + `. + (defaults to ` + "`" + `*` + "`" + `) type: string source: description: Source determines whether generated or original resources - are matched. Accepts `+"`"+`Generated`+"`"+`|`+"`"+`Original`+"`"+`|`+"`"+`All`+"`"+` (defaults to `+"`"+`All`+"`"+`). - A value of `+"`"+`Generated`+"`"+` will only match generated resources, while - `+"`"+`Original`+"`"+` will only match regular resources. + are matched. Accepts ` + "`" + `Generated` + "`" + `|` + "`" + `Original` + "`" + `|` + "`" + `All` + "`" + ` (defaults to ` + "`" + `All` + "`" + `). + A value of ` + "`" + `Generated` + "`" + ` will only match generated resources, while + ` + "`" + `Original` + "`" + ` will only match regular resources. enum: - All - Generated diff --git a/pkg/target/target.go b/pkg/target/target.go index c93c16d5c3e..d037324ed29 100644 --- a/pkg/target/target.go +++ b/pkg/target/target.go @@ -162,12 +162,6 @@ func unstructuredToAdmissionRequest(obj *unstructured.Unstructured) (*gkReview, return &gkReview{AdmissionRequest: req}, nil } -func propsWithDescription(props *apiextensions.JSONSchemaProps, description string) *apiextensions.JSONSchemaProps { - propCopy := props.DeepCopy() - propCopy.Description = description - return propCopy -} - func (h *K8sValidationTarget) MatchSchema() apiextensions.JSONSchemaProps { return matchSchema() } From aaf2df8d55876d0c313047f3f2fa73fe643e5f7d Mon Sep 17 00:00:00 2001 From: davis-haba Date: Tue, 31 Jan 2023 19:29:05 -0800 Subject: [PATCH 4/8] gofmt match schema CRD as part of make manifests Signed-off-by: davis-haba --- build/update-match-schema.sh | 6 ++-- .../bases/match.gatekeeper.sh_matchcrd.yaml | 14 ++++---- pkg/mutation/match/match_types.go | 6 ++-- pkg/mutation/match/zz_generated.deepcopy.go | 36 +++++++++---------- pkg/target/matchcrd_constant.go | 8 ++--- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/build/update-match-schema.sh b/build/update-match-schema.sh index 5c16a3ff377..cc56ec6831c 100755 --- a/build/update-match-schema.sh +++ b/build/update-match-schema.sh @@ -8,19 +8,17 @@ GO_FILE="./pkg/target/matchcrd_constant.go" SRC_FILE="./pkg/mutation/match/match_types.go" CRD_FILE="./config/crd/bases/match.gatekeeper.sh_matchcrd.yaml" -# Prepare file cat << EOF > ${GO_FILE} package target // DO NOT MODIFY THIS FILE DIRECTLY! // This file is generated from $SRC_FILE via "make manifests". -// If there are changes, you may have to gofmt this file afterwards. const matchYAML = \` EOF # Delete apiVersion block, adjust indentation to un-embed the match field, escape backticks -start=$(cat ${CRD_FILE} | grep -n "description: MatchDummyCRD" | cut -d: -f1) +start=$(cat ${CRD_FILE} | grep -n "description: DummyCRD" | cut -d: -f1) end=$(cat ${CRD_FILE} | grep -n "embeddedMatch:" | cut -d: -f1) cat ${CRD_FILE} | sed "${end},$ s/ //" | sed "${start},${end}d" | sed "s/\`/\`+\"\`\"+\`/g" >> ${GO_FILE} @@ -34,6 +32,8 @@ sed -i "${start},${end}d" ${GO_FILE} echo "\`" >> ${GO_FILE} +gofmt -w -l ${GO_FILE} + diff --git a/config/crd/bases/match.gatekeeper.sh_matchcrd.yaml b/config/crd/bases/match.gatekeeper.sh_matchcrd.yaml index f1c19529675..3a4381d0d7e 100644 --- a/config/crd/bases/match.gatekeeper.sh_matchcrd.yaml +++ b/config/crd/bases/match.gatekeeper.sh_matchcrd.yaml @@ -9,20 +9,20 @@ metadata: spec: group: match.gatekeeper.sh names: - kind: MatchDummyCRD - listKind: MatchDummyCRDList + kind: DummyCRD + listKind: DummyCRDList plural: matchcrd - singular: matchdummycrd + singular: dummycrd scope: Namespaced versions: - name: match schema: openAPIV3Schema: - description: MatchDummyCRD is a "dummy" CRD to hold the Match object, which - we ultimately need to generate JSONSchemaProps. The TypeMeta and ObjectMeta + description: DummyCRD is a "dummy" CRD to hold the Match object, which we + ultimately need to generate JSONSchemaProps. The TypeMeta and ObjectMeta fields are required for controller-gen to generate the CRD. DO NOT CHANGE - THE START OF THIS COMMENT. It is used for regex matching to extract the - embedded match CRD. + THE FIRST WORD OF THIS COMMENT. It is used for regex matching to extract + the embedded match CRD. properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation diff --git a/pkg/mutation/match/match_types.go b/pkg/mutation/match/match_types.go index 97968bdc79b..7c4c1b5cc7c 100644 --- a/pkg/mutation/match/match_types.go +++ b/pkg/mutation/match/match_types.go @@ -63,13 +63,13 @@ type Kinds struct { Kinds []string `json:"kinds,omitempty"` } -// MatchDummyCRD is a "dummy" CRD to hold the Match object, which we ultimately +// DummyCRD is a "dummy" CRD to hold the Match object, which we ultimately // need to generate JSONSchemaProps. The TypeMeta and ObjectMeta fields are // required for controller-gen to generate the CRD. -// DO NOT CHANGE THE START OF THIS COMMENT. +// DO NOT CHANGE THE FIRST WORD OF THIS COMMENT. // It is used for regex matching to extract the embedded match CRD. // +kubebuilder:resource:path="matchcrd" -type MatchDummyCRD struct { +type DummyCRD struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadataDummy,omitempty"` diff --git a/pkg/mutation/match/zz_generated.deepcopy.go b/pkg/mutation/match/zz_generated.deepcopy.go index 759b4498abd..abecee261fd 100644 --- a/pkg/mutation/match/zz_generated.deepcopy.go +++ b/pkg/mutation/match/zz_generated.deepcopy.go @@ -55,6 +55,24 @@ func (in *ApplyTo) DeepCopy() *ApplyTo { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DummyCRD) DeepCopyInto(out *DummyCRD) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Match.DeepCopyInto(&out.Match) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DummyCRD. +func (in *DummyCRD) DeepCopy() *DummyCRD { + if in == nil { + return nil + } + out := new(DummyCRD) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Kinds) DeepCopyInto(out *Kinds) { *out = *in @@ -121,21 +139,3 @@ func (in *Match) DeepCopy() *Match { in.DeepCopyInto(out) return out } - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MatchDummyCRD) DeepCopyInto(out *MatchDummyCRD) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Match.DeepCopyInto(&out.Match) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MatchDummyCRD. -func (in *MatchDummyCRD) DeepCopy() *MatchDummyCRD { - if in == nil { - return nil - } - out := new(MatchDummyCRD) - in.DeepCopyInto(out) - return out -} diff --git a/pkg/target/matchcrd_constant.go b/pkg/target/matchcrd_constant.go index a023563106d..b8ad68e2d76 100644 --- a/pkg/target/matchcrd_constant.go +++ b/pkg/target/matchcrd_constant.go @@ -1,7 +1,7 @@ package target -// This file is generated from ./pkg/mutation/match/match_types.go via "make manifests". // DO NOT MODIFY THIS FILE DIRECTLY! +// This file is generated from ./pkg/mutation/match/match_types.go via "make manifests". const matchYAML = ` --- @@ -15,10 +15,10 @@ metadata: spec: group: match.gatekeeper.sh names: - kind: MatchDummyCRD - listKind: MatchDummyCRDList + kind: DummyCRD + listKind: DummyCRDList plural: matchcrd - singular: matchdummycrd + singular: dummycrd scope: Namespaced versions: - name: match From 81cc0f860e67c7ec08a294ca14fce6432fe40661 Mon Sep 17 00:00:00 2001 From: davis-haba Date: Tue, 31 Jan 2023 19:43:54 -0800 Subject: [PATCH 5/8] delete whitespace at end of script Signed-off-by: davis-haba --- build/update-match-schema.sh | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/build/update-match-schema.sh b/build/update-match-schema.sh index cc56ec6831c..990806b0f47 100755 --- a/build/update-match-schema.sh +++ b/build/update-match-schema.sh @@ -1,8 +1,11 @@ -# This script parses the YAML for the Match CRD, found in $CRD_FILE, and outputs -# it as a go constant in $GO_FILE. For controller-gen to generate the CRD, we -# must include the metadata and typemeta fields. Since we don't want these fields -# to exist on the real Match CRD, we embed the Match type in a dummy type that -# has the metadata/typemeta fields. We then parse out these added fields. +# The purpose of the script is to build a golang string constant containing the +# YAML code for the Match CRD. This is needed to auto generate the +# JSONSchemaProps for Match. +# It will parse the YAML for the Match CRD, found in $CRD_FILE, and output to +# $GO_FILE. For controller-gen to generate the CRD, we must include the metadata +# and typemeta fields. Since we don't want these fields to exist on the real +# Match CRD, we embed the Match type in a dummy type that has the +# metadata/typemeta fields, and then parse out these unwanted fields. GO_FILE="./pkg/target/matchcrd_constant.go" SRC_FILE="./pkg/mutation/match/match_types.go" @@ -33,8 +36,3 @@ sed -i "${start},${end}d" ${GO_FILE} echo "\`" >> ${GO_FILE} gofmt -w -l ${GO_FILE} - - - - - From af0cc31ed3669746e3fdc94079f055bbe8f2183a Mon Sep 17 00:00:00 2001 From: davis-haba Date: Tue, 14 Feb 2023 11:42:49 -0800 Subject: [PATCH 6/8] extract embedded match crd in code Signed-off-by: davis-haba --- build/update-match-schema.sh | 27 +- .../bases/match.gatekeeper.sh_matchcrd.yaml | 4 +- pkg/mutation/match/match_types.go | 2 - pkg/target/match_schema.go | 4 +- pkg/target/matchcrd_constant.go | 343 +++++++++--------- pkg/target/target_test.go | 3 + 6 files changed, 192 insertions(+), 191 deletions(-) diff --git a/build/update-match-schema.sh b/build/update-match-schema.sh index 990806b0f47..f6f68f4c433 100755 --- a/build/update-match-schema.sh +++ b/build/update-match-schema.sh @@ -1,11 +1,7 @@ -# The purpose of the script is to build a golang string constant containing the -# YAML code for the Match CRD. This is needed to auto generate the -# JSONSchemaProps for Match. -# It will parse the YAML for the Match CRD, found in $CRD_FILE, and output to -# $GO_FILE. For controller-gen to generate the CRD, we must include the metadata -# and typemeta fields. Since we don't want these fields to exist on the real -# Match CRD, we embed the Match type in a dummy type that has the -# metadata/typemeta fields, and then parse out these unwanted fields. +# This script builds a golang string constant containing the YAML code for the +# Match CRD. This is needed to auto generate the JSONSchemaProps for Match. It +# will parse the YAML for the Match CRD, found in $CRD_FILE, and output to +# $GO_FILE. GO_FILE="./pkg/target/matchcrd_constant.go" SRC_FILE="./pkg/mutation/match/match_types.go" @@ -20,19 +16,8 @@ package target const matchYAML = \` EOF -# Delete apiVersion block, adjust indentation to un-embed the match field, escape backticks -start=$(cat ${CRD_FILE} | grep -n "description: DummyCRD" | cut -d: -f1) -end=$(cat ${CRD_FILE} | grep -n "embeddedMatch:" | cut -d: -f1) -cat ${CRD_FILE} | sed "${end},$ s/ //" | sed "${start},${end}d" | sed "s/\`/\`+\"\`\"+\`/g" >> ${GO_FILE} - -# Delete the 'kind:' and 'metadataDummy:' blocks at the end. This assumes the metadataDummy -# block is immediately after the kind block, and the metadataDummy block contains only -# one line (type: object) -start=$(cat ${GO_FILE} | grep -n -E "kind:$" | cut -d: -f1) -end=$(cat ${GO_FILE} | grep -n -E "metadataDummy:$" | cut -d: -f1) -end=$((end+1)) -sed -i "${start},${end}d" ${GO_FILE} - +# Escape backticks in the yaml, add terminating backtick +cat ${CRD_FILE} | sed "s/\`/\`+\"\`\"+\`/g" >> ${GO_FILE} echo "\`" >> ${GO_FILE} gofmt -w -l ${GO_FILE} diff --git a/config/crd/bases/match.gatekeeper.sh_matchcrd.yaml b/config/crd/bases/match.gatekeeper.sh_matchcrd.yaml index 3a4381d0d7e..def300b7b39 100644 --- a/config/crd/bases/match.gatekeeper.sh_matchcrd.yaml +++ b/config/crd/bases/match.gatekeeper.sh_matchcrd.yaml @@ -20,9 +20,7 @@ spec: openAPIV3Schema: description: DummyCRD is a "dummy" CRD to hold the Match object, which we ultimately need to generate JSONSchemaProps. The TypeMeta and ObjectMeta - fields are required for controller-gen to generate the CRD. DO NOT CHANGE - THE FIRST WORD OF THIS COMMENT. It is used for regex matching to extract - the embedded match CRD. + fields are required for controller-gen to generate the CRD. properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation diff --git a/pkg/mutation/match/match_types.go b/pkg/mutation/match/match_types.go index 7c4c1b5cc7c..ec45a65c7ab 100644 --- a/pkg/mutation/match/match_types.go +++ b/pkg/mutation/match/match_types.go @@ -66,8 +66,6 @@ type Kinds struct { // DummyCRD is a "dummy" CRD to hold the Match object, which we ultimately // need to generate JSONSchemaProps. The TypeMeta and ObjectMeta fields are // required for controller-gen to generate the CRD. -// DO NOT CHANGE THE FIRST WORD OF THIS COMMENT. -// It is used for regex matching to extract the embedded match CRD. // +kubebuilder:resource:path="matchcrd" type DummyCRD struct { metav1.TypeMeta `json:",inline"` diff --git a/pkg/target/match_schema.go b/pkg/target/match_schema.go index eddeb0c3b88..047d5451760 100644 --- a/pkg/target/match_schema.go +++ b/pkg/target/match_schema.go @@ -33,8 +33,8 @@ func init() { if err := apiextensionsv1.AddToScheme(rt); err != nil { panic(fmt.Errorf("could not add apiextensionsv1 to scheme: %w", err)) } - matchJSONSchemaProps = apiextensions.JSONSchemaProps{} - if err := rt.Convert(matchCRD.Spec.Versions[0].Schema.OpenAPIV3Schema, &matchJSONSchemaProps, nil); err != nil { + embedded := matchCRD.Spec.Versions[0].Schema.OpenAPIV3Schema.Properties["embeddedMatch"] + if err := rt.Convert(&embedded, &matchJSONSchemaProps, nil); err != nil { panic(fmt.Errorf("could not convert match JSONSchemaProps from v1 to versionless: %w", err)) } } diff --git a/pkg/target/matchcrd_constant.go b/pkg/target/matchcrd_constant.go index b8ad68e2d76..b38c9be5a04 100644 --- a/pkg/target/matchcrd_constant.go +++ b/pkg/target/matchcrd_constant.go @@ -24,176 +24,193 @@ spec: - name: match schema: openAPIV3Schema: - description: Match selects objects to apply mutations to. - properties: - excludedNamespaces: - description: 'ExcludedNamespaces is a list of namespace names. If - defined, a constraint only applies to resources not in a listed - namespace. ExcludedNamespaces also supports a prefix or suffix based - glob. For example, ` + "`" + `excludedNamespaces: [kube-*]` + "`" + ` matches both - ` + "`" + `kube-system` + "`" + ` and ` + "`" + `kube-public` + "`" + `, and ` + "`" + `excludedNamespaces: [*-system]` + "`" + ` - matches both ` + "`" + `kube-system` + "`" + ` and ` + "`" + `gatekeeper-system` + "`" + `.' - items: - description: 'A string that supports globbing at its front or end. - Ex: "kube-*" will match "kube-system" or "kube-public", "*-system" - will match "kube-system" or "gatekeeper-system". The asterisk - is required for wildcard matching.' - pattern: ^(\*|\*-)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\*|-\*)?$ - type: string - type: array - kinds: - items: - description: Kinds accepts a list of objects with apiGroups and - kinds fields that list the groups/kinds of objects to which the - mutation will apply. If multiple groups/kinds objects are specified, - only one match is needed for the resource to be in scope. + description: DummyCRD is a "dummy" CRD to hold the Match object, which we + ultimately need to generate JSONSchemaProps. The TypeMeta and ObjectMeta + fields are required for controller-gen to generate the CRD. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + embeddedMatch: + description: Match selects objects to apply mutations to. + properties: + excludedNamespaces: + description: 'ExcludedNamespaces is a list of namespace names. If + defined, a constraint only applies to resources not in a listed + namespace. ExcludedNamespaces also supports a prefix or suffix based + glob. For example, ` + "`" + `excludedNamespaces: [kube-*]` + "`" + ` matches both + ` + "`" + `kube-system` + "`" + ` and ` + "`" + `kube-public` + "`" + `, and ` + "`" + `excludedNamespaces: [*-system]` + "`" + ` + matches both ` + "`" + `kube-system` + "`" + ` and ` + "`" + `gatekeeper-system` + "`" + `.' + items: + description: 'A string that supports globbing at its front or end. + Ex: "kube-*" will match "kube-system" or "kube-public", "*-system" + will match "kube-system" or "gatekeeper-system". The asterisk + is required for wildcard matching.' + pattern: ^(\*|\*-)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\*|-\*)?$ + type: string + type: array + kinds: + items: + description: Kinds accepts a list of objects with apiGroups and + kinds fields that list the groups/kinds of objects to which the + mutation will apply. If multiple groups/kinds objects are specified, + only one match is needed for the resource to be in scope. + properties: + apiGroups: + description: APIGroups is the API groups the resources belong + to. '*' is all groups. If '*' is present, the length of the + slice must be one. Required. + items: + type: string + type: array + kinds: + items: + type: string + type: array + type: object + type: array + labelSelector: + description: 'LabelSelector is the combination of two optional fields: + ` + "`" + `matchLabels` + "`" + ` and ` + "`" + `matchExpressions` + "`" + `. These two fields provide + different methods of selecting or excluding k8s objects based on + the label keys and values included in object metadata. All selection + expressions from both sections are ANDed to determine if an object + meets the cumulative requirements of the selector.' properties: - apiGroups: - description: APIGroups is the API groups the resources belong - to. '*' is all groups. If '*' is present, the length of the - slice must be one. Required. + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. items: - type: string + description: A label selector requirement is a selector that + contains values, a key, and an operator that relates the key + and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to + a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object type: array - kinds: - items: + matchLabels: + additionalProperties: type: string - type: array + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. + type: object type: object - type: array - labelSelector: - description: 'LabelSelector is the combination of two optional fields: - ` + "`" + `matchLabels` + "`" + ` and ` + "`" + `matchExpressions` + "`" + `. These two fields provide - different methods of selecting or excluding k8s objects based on - the label keys and values included in object metadata. All selection - expressions from both sections are ANDed to determine if an object - meets the cumulative requirements of the selector.' - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement is a selector that - contains values, a key, and an operator that relates the key - and values. - properties: - key: - description: key is the label key that the selector applies - to. - type: string - operator: - description: operator represents a key's relationship to - a set of values. Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of string values. If the - operator is In or NotIn, the values array must be non-empty. - If the operator is Exists or DoesNotExist, the values - array must be empty. This array is replaced during a strategic - merge patch. - items: + name: + description: 'Name is the name of an object. If defined, it will + match against objects with the specified name. Name also supports + a prefix or suffix glob. For example, ` + "`" + `name: pod-*` + "`" + ` would match + both ` + "`" + `pod-a` + "`" + ` and ` + "`" + `pod-b` + "`" + `, and ` + "`" + `name: *-pod` + "`" + ` would match both ` + "`" + `a-pod` + "`" + ` + and ` + "`" + `b-pod` + "`" + `.' + pattern: ^(\*|\*-)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\*|-\*)?$ + type: string + namespaceSelector: + description: NamespaceSelector is a label selector against an object's + containing namespace or the object itself, if the object is a namespace. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that + contains values, a key, and an operator that relates the key + and values. + properties: + key: + description: key is the label key that the selector applies + to. type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single - {key,value} in the matchLabels map is equivalent to an element - of matchExpressions, whose key field is "key", the operator - is "In", and the values array contains only "value". The requirements - are ANDed. - type: object - type: object - name: - description: 'Name is the name of an object. If defined, it will - match against objects with the specified name. Name also supports - a prefix or suffix glob. For example, ` + "`" + `name: pod-*` + "`" + ` would match - both ` + "`" + `pod-a` + "`" + ` and ` + "`" + `pod-b` + "`" + `, and ` + "`" + `name: *-pod` + "`" + ` would match both ` + "`" + `a-pod` + "`" + ` - and ` + "`" + `b-pod` + "`" + `.' - pattern: ^(\*|\*-)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\*|-\*)?$ - type: string - namespaceSelector: - description: NamespaceSelector is a label selector against an object's - containing namespace or the object itself, if the object is a namespace. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement is a selector that - contains values, a key, and an operator that relates the key - and values. - properties: - key: - description: key is the label key that the selector applies - to. - type: string - operator: - description: operator represents a key's relationship to - a set of values. Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of string values. If the - operator is In or NotIn, the values array must be non-empty. - If the operator is Exists or DoesNotExist, the values - array must be empty. This array is replaced during a strategic - merge patch. - items: + operator: + description: operator represents a key's relationship to + a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. type: string - type: array - required: - - key - - operator + values: + description: values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single - {key,value} in the matchLabels map is equivalent to an element - of matchExpressions, whose key field is "key", the operator - is "In", and the values array contains only "value". The requirements - are ANDed. - type: object - type: object - namespaces: - description: 'Namespaces is a list of namespace names. If defined, - a constraint only applies to resources in a listed namespace. Namespaces - also supports a prefix or suffix based glob. For example, ` + "`" + `namespaces: - [kube-*]` + "`" + ` matches both ` + "`" + `kube-system` + "`" + ` and ` + "`" + `kube-public` + "`" + `, and ` + "`" + `namespaces: - [*-system]` + "`" + ` matches both ` + "`" + `kube-system` + "`" + ` and ` + "`" + `gatekeeper-system` + "`" + `.' - items: - description: 'A string that supports globbing at its front or end. - Ex: "kube-*" will match "kube-system" or "kube-public", "*-system" - will match "kube-system" or "gatekeeper-system". The asterisk - is required for wildcard matching.' - pattern: ^(\*|\*-)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\*|-\*)?$ + type: object + namespaces: + description: 'Namespaces is a list of namespace names. If defined, + a constraint only applies to resources in a listed namespace. Namespaces + also supports a prefix or suffix based glob. For example, ` + "`" + `namespaces: + [kube-*]` + "`" + ` matches both ` + "`" + `kube-system` + "`" + ` and ` + "`" + `kube-public` + "`" + `, and ` + "`" + `namespaces: + [*-system]` + "`" + ` matches both ` + "`" + `kube-system` + "`" + ` and ` + "`" + `gatekeeper-system` + "`" + `.' + items: + description: 'A string that supports globbing at its front or end. + Ex: "kube-*" will match "kube-system" or "kube-public", "*-system" + will match "kube-system" or "gatekeeper-system". The asterisk + is required for wildcard matching.' + pattern: ^(\*|\*-)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\*|-\*)?$ + type: string + type: array + scope: + description: Scope determines if cluster-scoped and/or namespaced-scoped + resources are matched. Accepts ` + "`" + `*` + "`" + `, ` + "`" + `Cluster` + "`" + `, or ` + "`" + `Namespaced` + "`" + `. + (defaults to ` + "`" + `*` + "`" + `) + type: string + source: + description: Source determines whether generated or original resources + are matched. Accepts ` + "`" + `Generated` + "`" + `|` + "`" + `Original` + "`" + `|` + "`" + `All` + "`" + ` (defaults to ` + "`" + `All` + "`" + `). + A value of ` + "`" + `Generated` + "`" + ` will only match generated resources, while + ` + "`" + `Original` + "`" + ` will only match regular resources. + enum: + - All + - Generated + - Original type: string - type: array - scope: - description: Scope determines if cluster-scoped and/or namespaced-scoped - resources are matched. Accepts ` + "`" + `*` + "`" + `, ` + "`" + `Cluster` + "`" + `, or ` + "`" + `Namespaced` + "`" + `. - (defaults to ` + "`" + `*` + "`" + `) - type: string - source: - description: Source determines whether generated or original resources - are matched. Accepts ` + "`" + `Generated` + "`" + `|` + "`" + `Original` + "`" + `|` + "`" + `All` + "`" + ` (defaults to ` + "`" + `All` + "`" + `). - A value of ` + "`" + `Generated` + "`" + ` will only match generated resources, while - ` + "`" + `Original` + "`" + ` will only match regular resources. - enum: - - All - - Generated - - Original - type: string - type: object - type: object - served: true - storage: true + type: object + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadataDummy: + type: object + type: object + served: true + storage: true ` diff --git a/pkg/target/target_test.go b/pkg/target/target_test.go index 9d5c37bb09a..785f090aeb2 100644 --- a/pkg/target/target_test.go +++ b/pkg/target/target_test.go @@ -54,6 +54,7 @@ func TestValidateConstraint(t *testing.T) { }, "spec": { "match": { + "source": "All", "kinds": [ { "apiGroups": [""], @@ -80,6 +81,7 @@ func TestValidateConstraint(t *testing.T) { }, "spec": { "match": { + "source": "Original", "kinds": [ { "apiGroups": [""], @@ -202,6 +204,7 @@ func TestValidateConstraint(t *testing.T) { }, "spec": { "match": { + "source": "Generated", "kinds": [ { "apiGroups": [""], From f94d95f6135d76b10f74ab6b0328a11af1ab878c Mon Sep 17 00:00:00 2001 From: davis-haba Date: Fri, 3 Mar 2023 17:35:11 -0800 Subject: [PATCH 7/8] update match crd desc Signed-off-by: davis-haba --- config/crd/bases/match.gatekeeper.sh_matchcrd.yaml | 2 +- .../bases/mutations.gatekeeper.sh_assignmetadata.yaml | 6 +++--- .../crds/assignmetadata-customresourcedefinition.yaml | 6 +++--- manifest_staging/deploy/gatekeeper.yaml | 6 +++--- pkg/mutation/match/match_types.go | 2 +- pkg/target/match_schema.go | 9 +++++++-- pkg/target/matchcrd_constant.go | 2 +- 7 files changed, 19 insertions(+), 14 deletions(-) diff --git a/config/crd/bases/match.gatekeeper.sh_matchcrd.yaml b/config/crd/bases/match.gatekeeper.sh_matchcrd.yaml index def300b7b39..c8b94c2c601 100644 --- a/config/crd/bases/match.gatekeeper.sh_matchcrd.yaml +++ b/config/crd/bases/match.gatekeeper.sh_matchcrd.yaml @@ -28,7 +28,7 @@ spec: internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string embeddedMatch: - description: Match selects objects to apply mutations to. + description: Match selects which objects are in scope. properties: excludedNamespaces: description: 'ExcludedNamespaces is a list of namespace names. If diff --git a/config/crd/bases/mutations.gatekeeper.sh_assignmetadata.yaml b/config/crd/bases/mutations.gatekeeper.sh_assignmetadata.yaml index 895f3c44790..7fb1ee7e389 100644 --- a/config/crd/bases/mutations.gatekeeper.sh_assignmetadata.yaml +++ b/config/crd/bases/mutations.gatekeeper.sh_assignmetadata.yaml @@ -38,7 +38,7 @@ spec: location: type: string match: - description: Match selects objects to apply mutations to. + description: Match selects which objects are in scope. properties: excludedNamespaces: description: 'ExcludedNamespaces is a list of namespace names. @@ -337,7 +337,7 @@ spec: location: type: string match: - description: Match selects objects to apply mutations to. + description: Match selects which objects are in scope. properties: excludedNamespaces: description: 'ExcludedNamespaces is a list of namespace names. @@ -636,7 +636,7 @@ spec: location: type: string match: - description: Match selects objects to apply mutations to. + description: Match selects which objects are in scope. properties: excludedNamespaces: description: 'ExcludedNamespaces is a list of namespace names. diff --git a/manifest_staging/charts/gatekeeper/crds/assignmetadata-customresourcedefinition.yaml b/manifest_staging/charts/gatekeeper/crds/assignmetadata-customresourcedefinition.yaml index 3a63eef3cb3..468b01fccdb 100644 --- a/manifest_staging/charts/gatekeeper/crds/assignmetadata-customresourcedefinition.yaml +++ b/manifest_staging/charts/gatekeeper/crds/assignmetadata-customresourcedefinition.yaml @@ -39,7 +39,7 @@ spec: location: type: string match: - description: Match selects objects to apply mutations to. + description: Match selects which objects are in scope. properties: excludedNamespaces: description: 'ExcludedNamespaces is a list of namespace names. If defined, a constraint only applies to resources not in a listed namespace. ExcludedNamespaces also supports a prefix or suffix based glob. For example, `excludedNamespaces: [kube-*]` matches both `kube-system` and `kube-public`, and `excludedNamespaces: [*-system]` matches both `kube-system` and `gatekeeper-system`.' @@ -250,7 +250,7 @@ spec: location: type: string match: - description: Match selects objects to apply mutations to. + description: Match selects which objects are in scope. properties: excludedNamespaces: description: 'ExcludedNamespaces is a list of namespace names. If defined, a constraint only applies to resources not in a listed namespace. ExcludedNamespaces also supports a prefix or suffix based glob. For example, `excludedNamespaces: [kube-*]` matches both `kube-system` and `kube-public`, and `excludedNamespaces: [*-system]` matches both `kube-system` and `gatekeeper-system`.' @@ -461,7 +461,7 @@ spec: location: type: string match: - description: Match selects objects to apply mutations to. + description: Match selects which objects are in scope. properties: excludedNamespaces: description: 'ExcludedNamespaces is a list of namespace names. If defined, a constraint only applies to resources not in a listed namespace. ExcludedNamespaces also supports a prefix or suffix based glob. For example, `excludedNamespaces: [kube-*]` matches both `kube-system` and `kube-public`, and `excludedNamespaces: [*-system]` matches both `kube-system` and `gatekeeper-system`.' diff --git a/manifest_staging/deploy/gatekeeper.yaml b/manifest_staging/deploy/gatekeeper.yaml index 5ee0385f92f..20b8e2f9839 100644 --- a/manifest_staging/deploy/gatekeeper.yaml +++ b/manifest_staging/deploy/gatekeeper.yaml @@ -1067,7 +1067,7 @@ spec: location: type: string match: - description: Match selects objects to apply mutations to. + description: Match selects which objects are in scope. properties: excludedNamespaces: description: 'ExcludedNamespaces is a list of namespace names. If defined, a constraint only applies to resources not in a listed namespace. ExcludedNamespaces also supports a prefix or suffix based glob. For example, `excludedNamespaces: [kube-*]` matches both `kube-system` and `kube-public`, and `excludedNamespaces: [*-system]` matches both `kube-system` and `gatekeeper-system`.' @@ -1278,7 +1278,7 @@ spec: location: type: string match: - description: Match selects objects to apply mutations to. + description: Match selects which objects are in scope. properties: excludedNamespaces: description: 'ExcludedNamespaces is a list of namespace names. If defined, a constraint only applies to resources not in a listed namespace. ExcludedNamespaces also supports a prefix or suffix based glob. For example, `excludedNamespaces: [kube-*]` matches both `kube-system` and `kube-public`, and `excludedNamespaces: [*-system]` matches both `kube-system` and `gatekeeper-system`.' @@ -1489,7 +1489,7 @@ spec: location: type: string match: - description: Match selects objects to apply mutations to. + description: Match selects which objects are in scope. properties: excludedNamespaces: description: 'ExcludedNamespaces is a list of namespace names. If defined, a constraint only applies to resources not in a listed namespace. ExcludedNamespaces also supports a prefix or suffix based glob. For example, `excludedNamespaces: [kube-*]` matches both `kube-system` and `kube-public`, and `excludedNamespaces: [*-system]` matches both `kube-system` and `gatekeeper-system`.' diff --git a/pkg/mutation/match/match_types.go b/pkg/mutation/match/match_types.go index ec45a65c7ab..fc7904bda75 100644 --- a/pkg/mutation/match/match_types.go +++ b/pkg/mutation/match/match_types.go @@ -8,7 +8,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// Match selects objects to apply mutations to. +// Match selects which objects are in scope. // +kubebuilder:object:generate=true type Match struct { // Source determines whether generated or original resources are matched. diff --git a/pkg/target/match_schema.go b/pkg/target/match_schema.go index 047d5451760..e3ed2e7b876 100644 --- a/pkg/target/match_schema.go +++ b/pkg/target/match_schema.go @@ -18,7 +18,7 @@ func init() { } // Sanity checks to ensure the CRD was generated properly - if len(matchCRD.Spec.Versions) == 0 { + if len(matchCRD.Spec.Versions) != 1 { panic(fmt.Errorf("generated match CRD does not contain any versions")) } if matchCRD.Spec.Versions[0].Schema.OpenAPIV3Schema == nil { @@ -37,8 +37,13 @@ func init() { if err := rt.Convert(&embedded, &matchJSONSchemaProps, nil); err != nil { panic(fmt.Errorf("could not convert match JSONSchemaProps from v1 to versionless: %w", err)) } + + // Verify conversion worked by checking properties field + if _, exists := matchJSONSchemaProps.Properties["kinds"]; !exists { + panic("converted match schema does not have 'kinds' field") + } } func matchSchema() apiextensions.JSONSchemaProps { - return matchJSONSchemaProps + return *matchJSONSchemaProps.DeepCopy() } diff --git a/pkg/target/matchcrd_constant.go b/pkg/target/matchcrd_constant.go index b38c9be5a04..68269144ffd 100644 --- a/pkg/target/matchcrd_constant.go +++ b/pkg/target/matchcrd_constant.go @@ -34,7 +34,7 @@ spec: internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string embeddedMatch: - description: Match selects objects to apply mutations to. + description: Match selects which objects are in scope. properties: excludedNamespaces: description: 'ExcludedNamespaces is a list of namespace names. If From daaa25749503ffdfb77f2aa51a62fd857eaf930d Mon Sep 17 00:00:00 2001 From: davis-haba Date: Thu, 9 Mar 2023 23:07:20 -0800 Subject: [PATCH 8/8] exclude matchcrd_constant.go from linter Signed-off-by: davis-haba exclude matchcrd_constant.go from linter Signed-off-by: davis-haba --- .golangci.yaml | 2 ++ build/update-match-schema.sh | 2 -- pkg/target/matchcrd_constant.go | 30 +++++++++++++++--------------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 2141ad99f57..f26ecbbe0a1 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -1,5 +1,7 @@ run: timeout: 5m + skip-files: + - pkg/target/matchcrd_constant.go linters-settings: gocritic: diff --git a/build/update-match-schema.sh b/build/update-match-schema.sh index f6f68f4c433..14c55a6577a 100755 --- a/build/update-match-schema.sh +++ b/build/update-match-schema.sh @@ -19,5 +19,3 @@ EOF # Escape backticks in the yaml, add terminating backtick cat ${CRD_FILE} | sed "s/\`/\`+\"\`\"+\`/g" >> ${GO_FILE} echo "\`" >> ${GO_FILE} - -gofmt -w -l ${GO_FILE} diff --git a/pkg/target/matchcrd_constant.go b/pkg/target/matchcrd_constant.go index 68269144ffd..584e32c82b3 100644 --- a/pkg/target/matchcrd_constant.go +++ b/pkg/target/matchcrd_constant.go @@ -40,9 +40,9 @@ spec: description: 'ExcludedNamespaces is a list of namespace names. If defined, a constraint only applies to resources not in a listed namespace. ExcludedNamespaces also supports a prefix or suffix based - glob. For example, ` + "`" + `excludedNamespaces: [kube-*]` + "`" + ` matches both - ` + "`" + `kube-system` + "`" + ` and ` + "`" + `kube-public` + "`" + `, and ` + "`" + `excludedNamespaces: [*-system]` + "`" + ` - matches both ` + "`" + `kube-system` + "`" + ` and ` + "`" + `gatekeeper-system` + "`" + `.' + glob. For example, `+"`"+`excludedNamespaces: [kube-*]`+"`"+` matches both + `+"`"+`kube-system`+"`"+` and `+"`"+`kube-public`+"`"+`, and `+"`"+`excludedNamespaces: [*-system]`+"`"+` + matches both `+"`"+`kube-system`+"`"+` and `+"`"+`gatekeeper-system`+"`"+`.' items: description: 'A string that supports globbing at its front or end. Ex: "kube-*" will match "kube-system" or "kube-public", "*-system" @@ -73,7 +73,7 @@ spec: type: array labelSelector: description: 'LabelSelector is the combination of two optional fields: - ` + "`" + `matchLabels` + "`" + ` and ` + "`" + `matchExpressions` + "`" + `. These two fields provide + `+"`"+`matchLabels`+"`"+` and `+"`"+`matchExpressions`+"`"+`. These two fields provide different methods of selecting or excluding k8s objects based on the label keys and values included in object metadata. All selection expressions from both sections are ANDed to determine if an object @@ -123,9 +123,9 @@ spec: name: description: 'Name is the name of an object. If defined, it will match against objects with the specified name. Name also supports - a prefix or suffix glob. For example, ` + "`" + `name: pod-*` + "`" + ` would match - both ` + "`" + `pod-a` + "`" + ` and ` + "`" + `pod-b` + "`" + `, and ` + "`" + `name: *-pod` + "`" + ` would match both ` + "`" + `a-pod` + "`" + ` - and ` + "`" + `b-pod` + "`" + `.' + a prefix or suffix glob. For example, `+"`"+`name: pod-*`+"`"+` would match + both `+"`"+`pod-a`+"`"+` and `+"`"+`pod-b`+"`"+`, and `+"`"+`name: *-pod`+"`"+` would match both `+"`"+`a-pod`+"`"+` + and `+"`"+`b-pod`+"`"+`.' pattern: ^(\*|\*-)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\*|-\*)?$ type: string namespaceSelector: @@ -176,9 +176,9 @@ spec: namespaces: description: 'Namespaces is a list of namespace names. If defined, a constraint only applies to resources in a listed namespace. Namespaces - also supports a prefix or suffix based glob. For example, ` + "`" + `namespaces: - [kube-*]` + "`" + ` matches both ` + "`" + `kube-system` + "`" + ` and ` + "`" + `kube-public` + "`" + `, and ` + "`" + `namespaces: - [*-system]` + "`" + ` matches both ` + "`" + `kube-system` + "`" + ` and ` + "`" + `gatekeeper-system` + "`" + `.' + also supports a prefix or suffix based glob. For example, `+"`"+`namespaces: + [kube-*]`+"`"+` matches both `+"`"+`kube-system`+"`"+` and `+"`"+`kube-public`+"`"+`, and `+"`"+`namespaces: + [*-system]`+"`"+` matches both `+"`"+`kube-system`+"`"+` and `+"`"+`gatekeeper-system`+"`"+`.' items: description: 'A string that supports globbing at its front or end. Ex: "kube-*" will match "kube-system" or "kube-public", "*-system" @@ -189,14 +189,14 @@ spec: type: array scope: description: Scope determines if cluster-scoped and/or namespaced-scoped - resources are matched. Accepts ` + "`" + `*` + "`" + `, ` + "`" + `Cluster` + "`" + `, or ` + "`" + `Namespaced` + "`" + `. - (defaults to ` + "`" + `*` + "`" + `) + resources are matched. Accepts `+"`"+`*`+"`"+`, `+"`"+`Cluster`+"`"+`, or `+"`"+`Namespaced`+"`"+`. + (defaults to `+"`"+`*`+"`"+`) type: string source: description: Source determines whether generated or original resources - are matched. Accepts ` + "`" + `Generated` + "`" + `|` + "`" + `Original` + "`" + `|` + "`" + `All` + "`" + ` (defaults to ` + "`" + `All` + "`" + `). - A value of ` + "`" + `Generated` + "`" + ` will only match generated resources, while - ` + "`" + `Original` + "`" + ` will only match regular resources. + are matched. Accepts `+"`"+`Generated`+"`"+`|`+"`"+`Original`+"`"+`|`+"`"+`All`+"`"+` (defaults to `+"`"+`All`+"`"+`). + A value of `+"`"+`Generated`+"`"+` will only match generated resources, while + `+"`"+`Original`+"`"+` will only match regular resources. enum: - All - Generated