diff --git a/pkg/commands/testdata/testcase-crds/crd/bee.yaml b/pkg/commands/testdata/testcase-crds/crd/bee.yaml new file mode 100644 index 000000000..28cc9bd9e --- /dev/null +++ b/pkg/commands/testdata/testcase-crds/crd/bee.yaml @@ -0,0 +1,6 @@ +apiVersion: v1beta1 +kind: Bee +metadata: + name: bee +spec: + action: fly diff --git a/pkg/commands/testdata/testcase-crds/crd/kustomization.yaml b/pkg/commands/testdata/testcase-crds/crd/kustomization.yaml new file mode 100644 index 000000000..98f226de9 --- /dev/null +++ b/pkg/commands/testdata/testcase-crds/crd/kustomization.yaml @@ -0,0 +1,9 @@ +crds: +- mycrd.json + +resources: +- secret.yaml +- mykind.yaml +- bee.yaml + +namePrefix: test- \ No newline at end of file diff --git a/pkg/commands/testdata/testcase-crds/crd/mycrd.json b/pkg/commands/testdata/testcase-crds/crd/mycrd.json new file mode 100644 index 000000000..afe38882d --- /dev/null +++ b/pkg/commands/testdata/testcase-crds/crd/mycrd.json @@ -0,0 +1,170 @@ +{ + "github.com/example/pkg/apis/jingfang/v1beta1.Bee": { + "Schema": { + "description": "Bee", + "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/api-conventions.md#resources", + "type": "string" + }, + "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/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta" + }, + "spec": { + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.BeeSpec" + }, + "status": { + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.BeeStatus" + } + } + }, + "Dependencies": [ + "github.com/example/pkg/apis/jingfang/v1beta1.BeeSpec", + "github.com/example/pkg/apis/jingfang/v1beta1.BeeStatus", + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta" + ] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.BeeList": { + "Schema": { + "required": [ + "items" + ], + "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/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.Bee" + } + }, + "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/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta" + } + } + }, + "Dependencies": [ + "github.com/example/pkg/apis/jingfang/v1beta1.Bee", + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta" + ] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.BeeObjectReference": { + "Schema": { + "properties": { + "name": { + "type": "string" + } + } + }, + "Dependencies": [] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.BeeSpec": { + "Schema": { + "description": "BeeSpec defines the desired state of Bee" + }, + "Dependencies": [] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.BeeStatus": { + "Schema": { + "description": "BeeStatus defines the observed state of Bee" + }, + "Dependencies": [] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.MyKind": { + "Schema": { + "description": "MyKind", + "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/api-conventions.md#resources", + "type": "string" + }, + "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/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta" + }, + "spec": { + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.MyKindSpec" + }, + "status": { + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.MyKindStatus" + } + } + }, + "Dependencies": [ + "github.com/example/pkg/apis/jingfang/v1beta1.MyKindSpec", + "github.com/example/pkg/apis/jingfang/v1beta1.MyKindStatus", + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta" + ] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.MyKindList": { + "Schema": { + "required": [ + "items" + ], + "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/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.MyKind" + } + }, + "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/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta" + } + } + }, + "Dependencies": [ + "github.com/example/pkg/apis/jingfang/v1beta1.MyKind", + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta" + ] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.MyKindSpec": { + "Schema": { + "description": "MyKindSpec defines the desired state of MyKind", + "properties": { + "beeRef": { + "x-kubernetes-object-ref-api-version": "v1beta1", + "x-kubernetes-object-ref-kind": "Bee", + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.BeeObjectReference" + }, + "secretRef": { + "description": "If defined, we use this secret for configuring the MYSQL_ROOT_PASSWORD If it is not set we generate a secret dynamically", + "x-kubernetes-object-ref-api-version": "v1", + "x-kubernetes-object-ref-kind": "Secret", + "$ref": "k8s.io/api/core/v1.LocalObjectReference" + } + } + }, + "Dependencies": [ + "github.com/example/pkg/apis/jingfang/v1beta1.BeeObjectReference", + "k8s.io/api/core/v1.LocalObjectReference" + ] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.MyKindStatus": { + "Schema": { + "description": "MyKindStatus defines the observed state of MyKind" + }, + "Dependencies": [] + } +} diff --git a/pkg/commands/testdata/testcase-crds/crd/mykind.yaml b/pkg/commands/testdata/testcase-crds/crd/mykind.yaml new file mode 100644 index 000000000..0a644daa6 --- /dev/null +++ b/pkg/commands/testdata/testcase-crds/crd/mykind.yaml @@ -0,0 +1,9 @@ +apiVersion: jingfang.example.com/v1beta1 +kind: MyKind +metadata: + name: mykind +spec: + secretRef: + name: crdsecret + beeRef: + name: bee diff --git a/pkg/commands/testdata/testcase-crds/crd/secret.yaml b/pkg/commands/testdata/testcase-crds/crd/secret.yaml new file mode 100644 index 000000000..9126dd868 --- /dev/null +++ b/pkg/commands/testdata/testcase-crds/crd/secret.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Secret +metadata: + name: crdsecret +data: + PATH: YmJiYmJiYmIK diff --git a/pkg/commands/testdata/testcase-crds/expected.diff b/pkg/commands/testdata/testcase-crds/expected.diff new file mode 100644 index 000000000..d9b86c274 --- /dev/null +++ b/pkg/commands/testdata/testcase-crds/expected.diff @@ -0,0 +1,36 @@ +diff -u -N /tmp/noop/jingfang.example.com_v1beta1_MyKind_mykind.yaml /tmp/transformed/jingfang.example.com_v1beta1_MyKind_mykind.yaml +--- /tmp/noop/jingfang.example.com_v1beta1_MyKind_mykind.yaml YYYY-MM-DD HH:MM:SS ++++ /tmp/transformed/jingfang.example.com_v1beta1_MyKind_mykind.yaml YYYY-MM-DD HH:MM:SS +@@ -1,9 +1,9 @@ + apiVersion: jingfang.example.com/v1beta1 + kind: MyKind + metadata: +- name: mykind ++ name: test-mykind + spec: + beeRef: +- name: bee ++ name: test-bee + secretRef: +- name: crdsecret-m5ht5thcb4 ++ name: test-crdsecret-m48btmkck5 +diff -u -N /tmp/noop/v1beta1_Bee_bee.yaml /tmp/transformed/v1beta1_Bee_bee.yaml +--- /tmp/noop/v1beta1_Bee_bee.yaml YYYY-MM-DD HH:MM:SS ++++ /tmp/transformed/v1beta1_Bee_bee.yaml YYYY-MM-DD HH:MM:SS +@@ -1,6 +1,6 @@ + apiVersion: v1beta1 + kind: Bee + metadata: +- name: bee ++ name: test-bee + spec: + action: fly +diff -u -N /tmp/noop/v1_Secret_crdsecret.yaml /tmp/transformed/v1_Secret_crdsecret.yaml +--- /tmp/noop/v1_Secret_crdsecret.yaml YYYY-MM-DD HH:MM:SS ++++ /tmp/transformed/v1_Secret_crdsecret.yaml YYYY-MM-DD HH:MM:SS +@@ -3,4 +3,4 @@ + PATH: YmJiYmJiYmIK + kind: Secret + metadata: +- name: crdsecret-m5ht5thcb4 ++ name: test-crdsecret-m48btmkck5 diff --git a/pkg/commands/testdata/testcase-crds/expected.yaml b/pkg/commands/testdata/testcase-crds/expected.yaml new file mode 100644 index 000000000..57d48cc85 --- /dev/null +++ b/pkg/commands/testdata/testcase-crds/expected.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +data: + PATH: YmJiYmJiYmIK +kind: Secret +metadata: + name: test-crdsecret-m48btmkck5 +--- +apiVersion: v1beta1 +kind: Bee +metadata: + name: test-bee +spec: + action: fly +--- +apiVersion: jingfang.example.com/v1beta1 +kind: MyKind +metadata: + name: test-mykind +spec: + beeRef: + name: test-bee + secretRef: + name: test-crdsecret-m48btmkck5 diff --git a/pkg/commands/testdata/testcase-crds/test.yaml b/pkg/commands/testdata/testcase-crds/test.yaml new file mode 100644 index 000000000..c85121c60 --- /dev/null +++ b/pkg/commands/testdata/testcase-crds/test.yaml @@ -0,0 +1,5 @@ +description: name reference in CRDs +args: [] +filename: testdata/testcase-crds/crd +expectedStdout: testdata/testcase-crds/expected.yaml +expectedDiff: testdata/testcase-crds/expected.diff diff --git a/pkg/crds/constants.go b/pkg/crds/constants.go new file mode 100644 index 000000000..15369a3c7 --- /dev/null +++ b/pkg/crds/constants.go @@ -0,0 +1,43 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package crds read in files for CRD schemas and parse annotations from it +package crds + +// Annotation is to mark a field as annotations. +// "x-kubernetes-annotation": "" +const Annotation = "x-kubernetes-annotation" + +// LabelSelector is to mark a field as LabelSelector +// "x-kubernetes-label-selector": "" +const LabelSelector = "x-kubernetes-label-selector" + +// Identity is to mark a field as Identity +// "x-kubernetes-identity": "" +const Identity = "x-kubernetes-identity" + +// Version marks the type version of an object ref field +// "x-kubernetes-object-ref-api-version": +const Version = "x-kubernetes-object-ref-api-version" + +// Kind marks the type name of an object ref field +// "x-kubernetes-object-ref-kind": +const Kind = "x-kubernetes-object-ref-kind" + +// NameKey marks the field key that refers to an object of an object ref field +// "x-kubernetes-object-ref-name-key": "name" +// default is "name" +const NameKey = "x-kubernetes-object-ref-name-key" diff --git a/pkg/crds/crds.go b/pkg/crds/crds.go index 847bfad19..67745ba68 100644 --- a/pkg/crds/crds.go +++ b/pkg/crds/crds.go @@ -18,10 +18,192 @@ limitations under the License. package crds import ( + "encoding/json" + "reflect" + "strings" + + "github.com/ghodss/yaml" "github.com/kubernetes-sigs/kustomize/pkg/loader" + "github.com/kubernetes-sigs/kustomize/pkg/transformers" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/kube-openapi/pkg/common" ) +type pathConfigs struct { + labelPathConfig transformers.PathConfig + annotationPathConfig transformers.PathConfig + prefixPathConfig transformers.PathConfig + namereferencePathConfigs []transformers.ReferencePathConfig +} + +func (p *pathConfigs) addLabelPathConfig(config transformers.PathConfig) { + if *(p.labelPathConfig.GroupVersionKind) == *config.GroupVersionKind { + p.labelPathConfig.Path = append(p.labelPathConfig.Path, config.Path...) + } else { + p.labelPathConfig = config + } +} + +func (p *pathConfigs) addAnnotationPathConfig(config transformers.PathConfig) { + if *(p.annotationPathConfig.GroupVersionKind) == *config.GroupVersionKind { + p.annotationPathConfig.Path = append(p.labelPathConfig.Path, config.Path...) + } else { + p.annotationPathConfig = config + } +} + +func (p *pathConfigs) addNamereferencePathConfig(config transformers.ReferencePathConfig) { + p.namereferencePathConfigs = transformers.MergeNameReferencePathConfigs(p.namereferencePathConfigs, config) +} + +func (p *pathConfigs) addPrefixPathConfig(config transformers.PathConfig) { + if *(p.prefixPathConfig.GroupVersionKind) == *config.GroupVersionKind { + p.prefixPathConfig.Path = append(p.prefixPathConfig.Path, config.Path...) + } else { + p.prefixPathConfig = config + } +} + // RegisterCRDs parse CRD schemas from paths and update various pathConfigs func RegisterCRDs(loader loader.Loader, paths []string) error { + pathConfigs := []pathConfigs{} + for _, path := range paths { + pathConfig, err := registerCRD(loader, path) + if err != nil { + return err + } + pathConfigs = append(pathConfigs, pathConfig...) + } + addPathConfigs(pathConfigs) return nil } + +// register CRD from one path +func registerCRD(loader loader.Loader, path string) ([]pathConfigs, error) { + result := []pathConfigs{} + + content, err := loader.Load(path) + if err != nil { + return result, err + } + + var types map[string]common.OpenAPIDefinition + if content[0] == '{' { + err = json.Unmarshal(content, &types) + if err != nil { + return nil, err + } + } else { + err = yaml.Unmarshal(content, &types) + if err != nil { + return nil, err + } + } + + crds := getCRDs(types) + for crd, gvk := range crds { + crdPathConfigs := pathConfigs{} + err = getCRDPathConfig(types, crd, crd, gvk, []string{}, &crdPathConfigs) + if err != nil { + return result, err + } + if !reflect.DeepEqual(crdPathConfigs, pathConfigs{}) { + result = append(result, crdPathConfigs) + } + } + + return result, nil +} + +// getCRDs get all CRD types +func getCRDs(types map[string]common.OpenAPIDefinition) map[string]schema.GroupVersionKind { + crds := map[string]schema.GroupVersionKind{} + + for typename, t := range types { + properties := t.Schema.SchemaProps.Properties + _, foundKind := properties["kind"] + _, foundAPIVersion := properties["apiVersion"] + _, foundMetadata := properties["metadata"] + if foundKind && foundAPIVersion && foundMetadata { + // TODO: Get Group and Version for CRD from the openAPI definition once + // "x-kubernetes-group-version-kind" is available in CRD + kind := strings.Split(typename, ".")[len(strings.Split(typename, "."))-1] + crds[typename] = schema.GroupVersionKind{Kind: kind} + } + } + return crds +} + +// getCRDPathConfig gets pathConfigs for one CRD recursively +func getCRDPathConfig(types map[string]common.OpenAPIDefinition, atype string, crd string, gvk schema.GroupVersionKind, + path []string, configs *pathConfigs) error { + if _, ok := types[crd]; !ok { + return nil + } + + for propname, property := range types[atype].Schema.SchemaProps.Properties { + _, annotate := property.Extensions.GetString(Annotation) + if annotate { + configs.addAnnotationPathConfig( + transformers.PathConfig{ + CreateIfNotPresent: false, + GroupVersionKind: &gvk, + Path: append(path, propname), + }, + ) + } + _, label := property.Extensions.GetString(LabelSelector) + if label { + configs.addLabelPathConfig( + transformers.PathConfig{ + CreateIfNotPresent: false, + GroupVersionKind: &gvk, + Path: append(path, propname), + }, + ) + } + _, identity := property.Extensions.GetString(Identity) + if identity { + configs.addPrefixPathConfig( + transformers.PathConfig{ + CreateIfNotPresent: false, + GroupVersionKind: &gvk, + Path: append(path, propname), + }, + ) + } + version, ok := property.Extensions.GetString(Version) + if ok { + kind, ok := property.Extensions.GetString(Kind) + if ok { + nameKey, ok := property.Extensions.GetString(NameKey) + if !ok { + nameKey = "name" + } + configs.addNamereferencePathConfig(transformers.NewReferencePathConfig( + schema.GroupVersionKind{Kind: kind, Version: version}, + []transformers.PathConfig{ + {CreateIfNotPresent: false, + GroupVersionKind: &gvk, + Path: append(path, propname, nameKey), + }})) + + } + } + + if property.Ref.GetURL() != nil { + getCRDPathConfig(types, property.Ref.String(), crd, gvk, append(path, propname), configs) + } + } + return nil +} + +// addPathConfigs add extra path configs to the default ones +func addPathConfigs(p []pathConfigs) { + for _, pc := range p { + transformers.AddLabelsPathConfigs(pc.labelPathConfig) + transformers.AddAnnotationsPathConfigs(pc.annotationPathConfig) + transformers.AddNameReferencePathConfigs(pc.namereferencePathConfigs) + transformers.AddPrefixPathConfigs(pc.prefixPathConfig) + } +} diff --git a/pkg/crds/crds_test.go b/pkg/crds/crds_test.go new file mode 100644 index 000000000..a3696ef1a --- /dev/null +++ b/pkg/crds/crds_test.go @@ -0,0 +1,187 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package crds + +import ( + "reflect" + "testing" + + "github.com/kubernetes-sigs/kustomize/pkg/internal/loadertest" + "github.com/kubernetes-sigs/kustomize/pkg/loader" + "github.com/kubernetes-sigs/kustomize/pkg/transformers" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +const ( + crdContent = ` +{ + "github.com/example/pkg/apis/jingfang/v1beta1.Bee": { + "Schema": { + "description": "Bee", + "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/api-conventions.md#resources", + "type": "string" + }, + "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/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta" + }, + "spec": { + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.BeeSpec" + }, + "status": { + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.BeeStatus" + } + } + }, + "Dependencies": [ + "github.com/example/pkg/apis/jingfang/v1beta1.BeeSpec", + "github.com/example/pkg/apis/jingfang/v1beta1.BeeStatus", + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta" + ] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.BeeSpec": { + "Schema": { + "description": "BeeSpec defines the desired state of Bee" + }, + "Dependencies": [] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.BeeStatus": { + "Schema": { + "description": "BeeStatus defines the observed state of Bee" + }, + "Dependencies": [] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.MyKind": { + "Schema": { + "description": "MyKind", + "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/api-conventions.md#resources", + "type": "string" + }, + "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/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta" + }, + "spec": { + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.MyKindSpec" + }, + "status": { + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.MyKindStatus" + } + } + }, + "Dependencies": [ + "github.com/example/pkg/apis/jingfang/v1beta1.MyKindSpec", + "github.com/example/pkg/apis/jingfang/v1beta1.MyKindStatus", + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta" + ] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.MyKindSpec": { + "Schema": { + "description": "MyKindSpec defines the desired state of MyKind", + "properties": { + "beeRef": { + "x-kubernetes-object-ref-api-version": "v1beta1", + "x-kubernetes-object-ref-kind": "Bee", + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.Bee" + }, + "secretRef": { + "description": "If defined, we use this secret for configuring the MYSQL_ROOT_PASSWORD +If it is not set we generate a secret dynamically", + "x-kubernetes-object-ref-api-version": "v1", + "x-kubernetes-object-ref-kind": "Secret", + "$ref": "k8s.io/api/core/v1.LocalObjectReference" + } + } + }, + "Dependencies": [ + "github.com/example/pkg/apis/jingfang/v1beta1.Bee", + "k8s.io/api/core/v1.LocalObjectReference" + ] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.MyKindStatus": { + "Schema": { + "description": "MyKindStatus defines the observed state of MyKind" + }, + "Dependencies": [] + } +} +` +) + +func makeLoader(t *testing.T) loader.Loader { + loader := loadertest.NewFakeLoader("/testpath") + err := loader.AddFile("/testpath/crd.json", []byte(crdContent)) + if err != nil { + t.Fatalf("Failed to setup fake loader.") + } + return loader +} + +func TestRegisterCRD(t *testing.T) { + expected := []pathConfigs{ + { + namereferencePathConfigs: []transformers.ReferencePathConfig{ + transformers.NewReferencePathConfig( + schema.GroupVersionKind{Kind: "Bee", Version: "v1beta1"}, + []transformers.PathConfig{ + { + CreateIfNotPresent: false, + GroupVersionKind: &schema.GroupVersionKind{Kind: "MyKind"}, + Path: []string{"spec", "beeRef", "name"}, + }, + }, + ), + transformers.NewReferencePathConfig( + schema.GroupVersionKind{Kind: "Secret", Version: "v1"}, + []transformers.PathConfig{ + { + CreateIfNotPresent: false, + GroupVersionKind: &schema.GroupVersionKind{Kind: "MyKind"}, + Path: []string{"spec", "secretRef", "name"}, + }, + }, + ), + }, + }, + } + + loader := makeLoader(t) + + pathconfig, _ := registerCRD(loader, "/testpath/crd.json") + + if !reflect.DeepEqual(pathconfig, expected) { + t.Fatalf("expected\n %v\n but got\n %v\n", expected, pathconfig) + } +} diff --git a/pkg/transformers/labelsandannotations_test.go b/pkg/transformers/labelsandannotations_test.go index 73b27f100..abe775bd4 100644 --- a/pkg/transformers/labelsandannotations_test.go +++ b/pkg/transformers/labelsandannotations_test.go @@ -322,8 +322,8 @@ func TestAddPathConfigs(t *testing.T) { CreateIfNotPresent: true, }, } - AddLabelsPathConfigs(pathConfigs) - AddAnnotationsPathConfigs(pathConfigs) + AddLabelsPathConfigs(pathConfigs...) + AddAnnotationsPathConfigs(pathConfigs[0]) if len(defaultAnnotationsPathConfigs) != aexpected { t.Fatalf("actual %v doesn't match expected: %v", len(defaultAnnotationsPathConfigs), aexpected) } diff --git a/pkg/transformers/labelsandannotationsconfig.go b/pkg/transformers/labelsandannotationsconfig.go index 8248ffb2d..298e0e6ce 100644 --- a/pkg/transformers/labelsandannotationsconfig.go +++ b/pkg/transformers/labelsandannotationsconfig.go @@ -164,11 +164,11 @@ var defaultAnnotationsPathConfigs = []PathConfig{ } // AddLabelsPathConfigs adds extra path configs to the default one -func AddLabelsPathConfigs(pathConfigs []PathConfig) { +func AddLabelsPathConfigs(pathConfigs ...PathConfig) { defaultLabelsPathConfigs = append(defaultLabelsPathConfigs, pathConfigs...) } // AddAnnotationsPathConfigs adds extra path configs to the default one -func AddAnnotationsPathConfigs(pathConfigs []PathConfig) { +func AddAnnotationsPathConfigs(pathConfigs ...PathConfig) { defaultAnnotationsPathConfigs = append(defaultAnnotationsPathConfigs, pathConfigs...) } diff --git a/pkg/transformers/namereference.go b/pkg/transformers/namereference.go index eb49611d1..008f76fc4 100644 --- a/pkg/transformers/namereference.go +++ b/pkg/transformers/namereference.go @@ -26,7 +26,7 @@ import ( // nameReferenceTransformer contains the referencing info between 2 GroupVersionKinds type nameReferenceTransformer struct { - pathConfigs []referencePathConfig + pathConfigs []ReferencePathConfig } var _ Transformer = &nameReferenceTransformer{} @@ -38,7 +38,7 @@ func NewDefaultingNameReferenceTransformer() (Transformer, error) { } // NewNameReferenceTransformer construct a nameReferenceTransformer. -func NewNameReferenceTransformer(pc []referencePathConfig) (Transformer, error) { +func NewNameReferenceTransformer(pc []ReferencePathConfig) (Transformer, error) { if pc == nil { return nil, errors.New("pathConfigs is not expected to be nil") } diff --git a/pkg/transformers/namereference_test.go b/pkg/transformers/namereference_test.go index 009de8ffe..455246855 100644 --- a/pkg/transformers/namereference_test.go +++ b/pkg/transformers/namereference_test.go @@ -207,7 +207,7 @@ func TestNameReferenceRun(t *testing.T) { func TestAddNameReferencePathConfigs(t *testing.T) { expected := len(defaultNameReferencePathConfigs) + 1 - pathConfigs := []referencePathConfig{ + pathConfigs := []ReferencePathConfig{ { referencedGVK: schema.GroupVersionKind{ Kind: "KindA", diff --git a/pkg/transformers/namereferenceconfig.go b/pkg/transformers/namereferenceconfig.go index dd0f83a03..8494b31ec 100644 --- a/pkg/transformers/namereferenceconfig.go +++ b/pkg/transformers/namereferenceconfig.go @@ -22,7 +22,7 @@ import ( // defaultNameReferencePathConfigs is the default configuration for updating // the fields reference the name of other resources. -var defaultNameReferencePathConfigs = []referencePathConfig{ +var defaultNameReferencePathConfigs = []ReferencePathConfig{ { referencedGVK: schema.GroupVersionKind{ Kind: "Deployment", @@ -735,6 +735,27 @@ var defaultNameReferencePathConfigs = []referencePathConfig{ } // AddNameReferencePathConfigs adds extra reference path configs to the default one -func AddNameReferencePathConfigs(r []referencePathConfig) { - defaultNameReferencePathConfigs = append(defaultNameReferencePathConfigs, r...) +func AddNameReferencePathConfigs(r []ReferencePathConfig) { + for _, p := range r { + defaultNameReferencePathConfigs = MergeNameReferencePathConfigs(defaultNameReferencePathConfigs, p) + } +} + +// MergeNameReferencePathConfigs merges one ReferencePathConfig into a slice of ReferencePathConfig +func MergeNameReferencePathConfigs(configs []ReferencePathConfig, config ReferencePathConfig) []ReferencePathConfig { + result := []ReferencePathConfig{} + + found := false + for _, c := range configs { + if c.referencedGVK == config.referencedGVK { + c.pathConfigs = append(c.pathConfigs, config.pathConfigs...) + found = true + } + result = append(result, c) + } + + if !found { + result = append(result, config) + } + return result } diff --git a/pkg/transformers/pathconfig.go b/pkg/transformers/pathconfig.go index 67a1f6e20..0c8fbf80a 100644 --- a/pkg/transformers/pathconfig.go +++ b/pkg/transformers/pathconfig.go @@ -33,12 +33,12 @@ type PathConfig struct { Path []string } -// referencePathConfig contains the configuration of a field that references +// ReferencePathConfig contains the configuration of a field that references // the name of another resource whose GroupVersionKind is specified in referencedGVK. // e.g. pod.spec.template.volumes.configMap.name references the name of a configmap // Its corresponding referencePathConfig will look like: // -// referencePathConfig{ +// ReferencePathConfig{ // referencedGVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"}, // pathConfigs: []PathConfig{ // { @@ -46,10 +46,18 @@ type PathConfig struct { // Path: []string{"spec", "volumes", "configMap", "name"}, // }, // } -type referencePathConfig struct { +type ReferencePathConfig struct { // referencedGVK is the GroupVersionKind that is referenced by // the PathConfig's gvk in the path of PathConfig.Path. referencedGVK schema.GroupVersionKind // PathConfig is the gvk that is referencing the referencedGVK object's name. pathConfigs []PathConfig } + +// NewReferencePathConfig creates a new ReferencePathConfig object +func NewReferencePathConfig(gvk schema.GroupVersionKind, pathconfigs []PathConfig) ReferencePathConfig { + return ReferencePathConfig{ + referencedGVK: gvk, + pathConfigs: pathconfigs, + } +} diff --git a/pkg/transformers/prefixname.go b/pkg/transformers/prefixname.go index 7632f6396..1eb7d40e0 100644 --- a/pkg/transformers/prefixname.go +++ b/pkg/transformers/prefixname.go @@ -100,3 +100,8 @@ func (o *namePrefixTransformer) addPrefix(in interface{}) (interface{}, error) { } return o.prefix + s, nil } + +// AddPrefixPathConfigs adds extra path configs to the default one +func AddPrefixPathConfigs(pathConfigs ...PathConfig) { + defaultNamePrefixPathConfigs = append(defaultNamePrefixPathConfigs, pathConfigs...) +} diff --git a/pkg/transformers/prefixname_test.go b/pkg/transformers/prefixname_test.go index b0aa9d1ea..de95a246c 100644 --- a/pkg/transformers/prefixname_test.go +++ b/pkg/transformers/prefixname_test.go @@ -22,6 +22,7 @@ import ( "github.com/kubernetes-sigs/kustomize/pkg/resmap" "github.com/kubernetes-sigs/kustomize/pkg/resource" + "k8s.io/apimachinery/pkg/runtime/schema" ) func TestPrefixNameRun(t *testing.T) { @@ -91,3 +92,19 @@ func TestPrefixNameRun(t *testing.T) { t.Fatalf("actual doesn't match expected: %v", err) } } + +func TestAddPrefixPathConfigs(t *testing.T) { + expected := len(defaultNamePrefixPathConfigs) + 1 + + pathConfigs := []PathConfig{ + { + GroupVersionKind: &schema.GroupVersionKind{Group: "GroupA", Kind: "KindB"}, + Path: []string{"path", "to", "a", "field"}, + CreateIfNotPresent: true, + }, + } + AddPrefixPathConfigs(pathConfigs...) + if len(defaultNamePrefixPathConfigs) != expected { + t.Fatalf("actual %v doesn't match expected: %v", len(defaultNamePrefixPathConfigs), expected) + } +}