diff --git a/Makefile b/Makefile index efd4e1937..75ac261ae 100644 --- a/Makefile +++ b/Makefile @@ -277,7 +277,7 @@ kustomize: ifeq (, $(shell kustomize version | grep $(KUSTOMIZE_VERSION))) @{ \ set -e ;\ - echo 'installing kustomize-v$(KUSTOMIZE_VERSION)' ;\ + echo 'installing kustomize-v$(KUSTOMIZE_VERSION) into $(GOBIN)' ;\ curl -s https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh | bash -s $(KUSTOMIZE_VERSION) $(GOBIN);\ echo 'Install succeed' ;\ } diff --git a/pkg/controller/common/rollout/rollout_webhook_test.go b/pkg/controller/common/rollout/rollout_webhook_test.go index 5ef81f58a..5deed0a5a 100644 --- a/pkg/controller/common/rollout/rollout_webhook_test.go +++ b/pkg/controller/common/rollout/rollout_webhook_test.go @@ -110,6 +110,8 @@ func TestMakeHTTPRequest(t *testing.T) { }, } for testName, tt := range tests { + // deep copy it before going to goroutine + tt := tt t.Run(testName, func(t *testing.T) { // generate a test server so we can capture and inspect the request testServer := NewMock(tt.httpParameter.method, tt.httpParameter.statusCode, tt.httpParameter.body) diff --git a/pkg/controller/core.oam.dev/v1alpha2/application/application_controller_test.go b/pkg/controller/core.oam.dev/v1alpha2/application/application_controller_test.go index 8fd52520e..7d5efff01 100644 --- a/pkg/controller/core.oam.dev/v1alpha2/application/application_controller_test.go +++ b/pkg/controller/core.oam.dev/v1alpha2/application/application_controller_test.go @@ -1480,8 +1480,11 @@ spec: name: deployments.apps extension: template: | - import "kube/apps/v1" - output: v1.#Deployment & { + import ( + "k8s.io/apps/v1" + appsv1 "kube/apps/v1" + ) + output: v1.#Deployment & appsv1.#Deployment & { metadata: { annotations: { if context["config"] != _|_ { @@ -1539,8 +1542,8 @@ spec: cue: template: | import ( - kubev1 "kube/v1" - network "kube/networking.k8s.io/v1beta1" + kubev1 "k8s.io/core/v1" + network "k8s.io/networking/v1beta1" ) parameter: { diff --git a/pkg/dsl/definition/package.go b/pkg/dsl/definition/package.go index 5d100c9be..794530e6a 100644 --- a/pkg/dsl/definition/package.go +++ b/pkg/dsl/definition/package.go @@ -19,6 +19,7 @@ package definition import ( "context" "fmt" + "path/filepath" "strings" "sync" "time" @@ -36,14 +37,28 @@ import ( "k8s.io/client-go/rest" ) +const ( + // BuiltinPackageDomain Specify the domain of the built-in package + BuiltinPackageDomain = "kube" + // K8sResourcePrefix Indicates that the definition comes from kubernetes + K8sResourcePrefix = "io_k8s_api_" +) + // PackageDiscover defines the inner CUE packages loaded from K8s cluster type PackageDiscover struct { velaBuiltinPackages []*build.Instance - pkgKinds map[string][]string + pkgKinds map[string][]VersionKind mutex sync.RWMutex client *rest.RESTClient } +// VersionKind contains the resource metadata and reference name +type VersionKind struct { + DefinitionName string + APIVersion string + Kind string +} + // NewPackageDiscover will create a PackageDiscover client with the K8s config file. func NewPackageDiscover(config *rest.Config) (*PackageDiscover, error) { client, err := getClusterOpenAPIClient(config) @@ -52,7 +67,7 @@ func NewPackageDiscover(config *rest.Config) (*PackageDiscover, error) { } pd := &PackageDiscover{ client: client, - pkgKinds: make(map[string][]string), + pkgKinds: make(map[string][]VersionKind), } if err = pd.RefreshKubePackagesFromCluster(); err != nil { return nil, err @@ -67,6 +82,13 @@ func (pd *PackageDiscover) ImportBuiltinPackagesFor(bi *build.Instance) { bi.Imports = append(bi.Imports, pd.velaBuiltinPackages...) } +// ListPackageKinds list packages and their kinds +func (pd *PackageDiscover) ListPackageKinds() map[string][]VersionKind { + pd.mutex.RLock() + defer pd.mutex.RUnlock() + return pd.pkgKinds +} + // RefreshKubePackagesFromCluster will use K8s client to load/refresh all K8s open API as a reference kube package using in template func (pd *PackageDiscover) RefreshKubePackagesFromCluster() error { body, err := pd.client.Get().AbsPath("/openapi/v2").Do(context.Background()).Raw() @@ -78,13 +100,14 @@ func (pd *PackageDiscover) RefreshKubePackagesFromCluster() error { // Exist checks if the GVK exists in the built-in packages func (pd *PackageDiscover) Exist(gvk metav1.GroupVersionKind) bool { + dgvk := convert2DGVK(gvk) // package name equals to importPath - importPath := genPackageName(gvk.Group, gvk.Version) + importPath := genStandardPkgName(dgvk) pd.mutex.RLock() defer pd.mutex.RUnlock() pkgKinds := pd.pkgKinds[importPath] - for _, k := range pkgKinds { - if k == gvk.Kind { + for _, v := range pkgKinds { + if v.Kind == dgvk.Kind { return true } } @@ -92,7 +115,7 @@ func (pd *PackageDiscover) Exist(gvk metav1.GroupVersionKind) bool { } // mount will mount the new parsed package into PackageDiscover built-in packages -func (pd *PackageDiscover) mount(pkg *pkgInstance, pkgKinds []string) { +func (pd *PackageDiscover) mount(pkg *pkgInstance, pkgKinds []VersionKind) { pd.mutex.Lock() defer pd.mutex.Unlock() for i, p := range pd.velaBuiltinPackages { @@ -111,7 +134,7 @@ func (pd *PackageDiscover) addKubeCUEPackagesFromCluster(apiSchema string) error if err != nil { return err } - kinds := map[string]metav1.GroupVersionKind{} + dgvkMapper := make(map[string]domainGroupVersionKind) pathValue := oaInst.Value().Lookup("paths") if pathValue.Exists() { if st, err := pathValue.Struct(); err == nil { @@ -120,8 +143,8 @@ func (pd *PackageDiscover) addKubeCUEPackagesFromCluster(apiSchema string) error gvk := iter.Value().Lookup("post", "x-kubernetes-group-version-kind") if gvk.Exists() { - if v, err := getGVK(gvk); err == nil { - kinds["#"+v.Kind] = v + if v, err := getDGVK(gvk); err == nil { + dgvkMapper[v.reverseString()] = v } } } @@ -129,55 +152,75 @@ func (pd *PackageDiscover) addKubeCUEPackagesFromCluster(apiSchema string) error } oaFile, err := jsonschema.Extract(oaInst, &jsonschema.Config{ Root: "#/definitions", - Map: openAPIMapping, + Map: openAPIMapping(dgvkMapper), }) if err != nil { return err } + kubePkg := newPackage("kube") + kubePkg.processOpenAPIFile(oaFile) + if err := kubePkg.AddSyntax(oaFile); err != nil { + return err + } packages := make(map[string]*pkgInstance) - groupKinds := make(map[string][]string) + groupKinds := make(map[string][]VersionKind) - for k, v := range kinds { - apiVersion := v.Version - if v.Group != "" { - apiVersion = v.Group + "/" + apiVersion - } - def := fmt.Sprintf(`%s: { + for k := range dgvkMapper { + v := dgvkMapper[k] + apiVersion := v.APIVersion + def := fmt.Sprintf(` +import "kube" + +#%s: kube.%s & { kind: "%s" apiVersion: "%s", -}`, k, v.Kind, apiVersion) - pkgName := genPackageName(v.Group, v.Version) - pkg, ok := packages[pkgName] - if !ok { - pkg = newPackage(pkgName) +}`, v.Kind, k, v.Kind, apiVersion) + + pkgBuild := func(pkgName string) error { + pkg, ok := packages[pkgName] + if !ok { + pkg = newPackage(pkgName) + pkg.Imports = []*build.Instance{kubePkg.Instance} + } + + mykinds := groupKinds[pkgName] + mykinds = append(mykinds, VersionKind{ + APIVersion: v.APIVersion, + Kind: v.Kind, + DefinitionName: "#" + v.Kind, + }) + + if err := pkg.AddFile(v.reverseString(), def); err != nil { + return err + } + + packages[pkgName] = pkg + groupKinds[pkgName] = mykinds + return nil } - - mykinds := groupKinds[pkgName] - mykinds = append(mykinds, v.Kind) - - if err := pkg.AddFile(k, def); err != nil { + if err := pkgBuild(genStandardPkgName(v)); err != nil { + return err + } + if err := pkgBuild(genOpenPkgName(v)); err != nil { return err } - packages[pkgName] = pkg - groupKinds[pkgName] = mykinds } for name, pkg := range packages { - pkg.processOpenAPIFile(oaFile) - if err = pkg.AddSyntax(oaFile); err != nil { - return err - } pd.mount(pkg, groupKinds[name]) } return nil } -func genPackageName(group, version string) string { - res := []string{"kube"} - if group != "" { - res = append(res, group) +func genOpenPkgName(v domainGroupVersionKind) string { + return BuiltinPackageDomain + "/" + v.APIVersion +} + +func genStandardPkgName(v domainGroupVersionKind) string { + res := []string{v.Group, v.Version} + if v.Domain != "" { + res = []string{v.Domain, v.Group, v.Version} } - // version should never be empty - res = append(res, version) + return strings.Join(res, "/") } @@ -207,18 +250,56 @@ func getClusterOpenAPIClient(config *rest.Config) (*rest.RESTClient, error) { return rest.UnversionedRESTClientFor(©Config) } -func openAPIMapping(pos token.Pos, a []string) ([]ast.Label, error) { - if len(a) < 2 { - return nil, errors.New("openAPIMapping format invalid") +func openAPIMapping(dgvkMapper map[string]domainGroupVersionKind) func(pos token.Pos, a []string) ([]ast.Label, error) { + return func(pos token.Pos, a []string) ([]ast.Label, error) { + if len(a) < 2 { + return nil, errors.New("openAPIMapping format invalid") + } + + name := strings.ReplaceAll(a[1], ".", "_") + + if _, ok := dgvkMapper[name]; !ok && strings.HasPrefix(name, K8sResourcePrefix) { + trimName := strings.TrimPrefix(name, K8sResourcePrefix) + if v, ok := dgvkMapper[trimName]; ok { + v.Domain = "k8s.io" + dgvkMapper[name] = v + delete(dgvkMapper, trimName) + } + } + + if strings.HasSuffix(a[1], ".JSONSchemaProps") && pos != token.NoPos { + return []ast.Label{ast.NewIdent("_")}, nil + } + + return []ast.Label{ast.NewIdent(name)}, nil } - spl := strings.Split(a[1], ".") - name := spl[len(spl)-1] +} - if name == "JSONSchemaProps" && pos != token.NoPos { - return []ast.Label{ast.NewIdent("_")}, nil +type domainGroupVersionKind struct { + Domain string + Group string + Version string + Kind string + APIVersion string +} + +func (dgvk domainGroupVersionKind) reverseString() string { + var s = []string{dgvk.Kind, dgvk.Version} + s = append(s, strings.Split(dgvk.Group, ".")...) + domain := dgvk.Domain + if domain == "k8s.io" { + domain = "api.k8s.io" } - return []ast.Label{ast.NewIdent("#" + name)}, nil + + if domain != "" { + s = append(s, strings.Split(domain, ".")...) + } + + for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return strings.Join(s, "_") } type pkgInstance struct { @@ -228,7 +309,7 @@ type pkgInstance struct { func newPackage(name string) *pkgInstance { return &pkgInstance{ &build.Instance{ - PkgName: name, + PkgName: filepath.Base(name), ImportPath: name, }, } @@ -264,17 +345,45 @@ func (pkg *pkgInstance) processOpenAPIFile(f *ast.File) { } } -func getGVK(v cue.Value) (metav1.GroupVersionKind, error) { - ret := metav1.GroupVersionKind{} - var err error - ret.Group, err = v.Lookup("group").String() +func getDGVK(v cue.Value) (ret domainGroupVersionKind, err error) { + gvk := metav1.GroupVersionKind{} + gvk.Group, err = v.Lookup("group").String() if err != nil { - return ret, err + return } - ret.Version, err = v.Lookup("version").String() + gvk.Version, err = v.Lookup("version").String() if err != nil { - return ret, err + return } - ret.Kind, err = v.Lookup("kind").String() - return ret, err + + gvk.Kind, err = v.Lookup("kind").String() + if err != nil { + return + } + + ret = convert2DGVK(gvk) + return +} + +func convert2DGVK(gvk metav1.GroupVersionKind) domainGroupVersionKind { + ret := domainGroupVersionKind{ + Version: gvk.Version, + Kind: gvk.Kind, + APIVersion: gvk.Version, + } + if gvk.Group == "" { + ret.Group = "core" + ret.Domain = "k8s.io" + } else { + ret.APIVersion = gvk.Group + "/" + ret.APIVersion + sv := strings.Split(gvk.Group, ".") + // Domain must contain dot + if len(sv) > 2 { + ret.Domain = strings.Join(sv[1:], ".") + ret.Group = sv[0] + } else { + ret.Group = gvk.Group + } + } + return ret } diff --git a/pkg/dsl/definition/package_suit_test.go b/pkg/dsl/definition/package_suit_test.go index b547dbde8..acd88a3a4 100644 --- a/pkg/dsl/definition/package_suit_test.go +++ b/pkg/dsl/definition/package_suit_test.go @@ -20,11 +20,12 @@ import ( "context" "time" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "cuelang.org/go/cue" "cuelang.org/go/cue/build" "github.com/google/go-cmp/cmp" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -38,17 +39,17 @@ import ( var _ = Describe("Package discovery resources for definition from K8s APIServer", func() { - PIt("discovery built-in k8s resource", func() { + It("discovery built-in k8s resource", func() { By("test ingress in kube package") bi := build.NewContext().NewInstance("", nil) pd.ImportBuiltinPackagesFor(bi) err := bi.AddFile("-", ` import ( - network "kube/networking.k8s.io/v1beta1" + network "k8s.io/networking/v1beta1" + kube "kube/networking.k8s.io/v1beta1" ) - -output: network.#Ingress +output: network.#Ingress & kube.#Ingress output: { apiVersion: "networking.k8s.io/v1beta1" kind: "Ingress" @@ -102,16 +103,46 @@ parameter: { "servicePort": int64(80), }}}}}}}}, })).Should(BeEquivalentTo("")) + By("test Invalid Import path") + bi = build.NewContext().NewInstance("", nil) + pd.ImportBuiltinPackagesFor(bi) + bi.AddFile("-", ` +import ( + "k8s.io/networking/v1" + kube "kube/networking.k8s.io/v1" +) +output: v1.#Deployment & kube.#Deployment +output: { + metadata: { + "name": parameter.name + } + spec: template: spec: { + containers: [{ + name:"invalid-path", + image: parameter.image + }] + } +} + +parameter: { + name: "myapp" + image: "nginx" +}`) + inst, err = r.Build(bi) + Expect(err).Should(BeNil()) + _, err = model.NewBase(inst.Lookup("output")) + Expect(err).ShouldNot(BeNil()) + Expect(err.Error()).Should(Equal("_|_ // undefined field \"#Deployment\"")) By("test Deployment in kube package") bi = build.NewContext().NewInstance("", nil) pd.ImportBuiltinPackagesFor(bi) bi.AddFile("-", ` import ( - apps "kube/apps/v1" + apps "k8s.io/apps/v1" + kube "kube/apps/v1" ) - -output: apps.#Deployment +output: apps.#Deployment & kube.#Deployment output: { metadata: { "name": parameter.name @@ -123,7 +154,6 @@ output: { }] } } - parameter: { name: "myapp" image: "nginx" @@ -152,16 +182,17 @@ parameter: { bi = build.NewContext().NewInstance("", nil) pd.ImportBuiltinPackagesFor(bi) bi.AddFile("-", ` -import ("kube/v1") - -output: v1.#Secret +import ( + "k8s.io/core/v1" + kube "kube/v1" +) +output: v1.#Secret & kube.#Secret output: { metadata: { "name": parameter.name } type:"kubevela" } - parameter: { name: "myapp" }`) @@ -181,16 +212,17 @@ parameter: { bi = build.NewContext().NewInstance("", nil) pd.ImportBuiltinPackagesFor(bi) bi.AddFile("-", ` -import ("kube/v1") - -output: v1.#Service +import ( + "k8s.io/core/v1" + kube "kube/v1" +) +output: v1.#Service & kube.#Service output: { metadata: { "name": parameter.name } spec: type: "ClusterIP", } - parameter: { name: "myapp" }`) @@ -274,10 +306,12 @@ parameter: { } bi = build.NewContext().NewInstance("", nil) pd.ImportBuiltinPackagesFor(bi) - if err = bi.AddFile("-", ` -import ("kube/example.com/v1") - -output: v1.#Foo + if err := bi.AddFile("-", ` +import ( + ev1 "example.com/v1" + kv1 "kube/example.com/v1" +) +output: ev1.#Foo & kv1.#Foo output: { spec: key: "test1" status: key: "test2" diff --git a/pkg/dsl/definition/package_test.go b/pkg/dsl/definition/package_test.go index cd7740f4c..46f28389c 100644 --- a/pkg/dsl/definition/package_test.go +++ b/pkg/dsl/definition/package_test.go @@ -24,6 +24,7 @@ import ( "cuelang.org/go/cue/build" "cuelang.org/go/cue/parser" "cuelang.org/go/cue/token" + "github.com/google/go-cmp/cmp" "gotest.tools/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -37,7 +38,7 @@ func TestPackage(t *testing.T) { "paths...": { "post":{ "x-kubernetes-group-version-kind": { - "group": "test.io", + "group": "apps.test.io", "kind": "Bucket", "version": "v1" } @@ -45,7 +46,7 @@ func TestPackage(t *testing.T) { } }, "definitions":{ - "Bucket":{ + "io.test.apps.v1.Bucket":{ "properties":{ "apiVersion": {"type": "string"} "kind": {"type": "string"} @@ -330,12 +331,85 @@ func TestPackage(t *testing.T) { } } ` - mypd := &PackageDiscover{pkgKinds: make(map[string][]string)} + mypd := &PackageDiscover{pkgKinds: make(map[string][]VersionKind)} mypd.addKubeCUEPackagesFromCluster(openAPISchema) + expectPkgKinds := map[string][]VersionKind{ + "test.io/apps/v1": []VersionKind{{ + DefinitionName: "#Bucket", + APIVersion: "apps.test.io/v1", + Kind: "Bucket", + }}, + "kube/apps.test.io/v1": []VersionKind{{ + DefinitionName: "#Bucket", + APIVersion: "apps.test.io/v1", + Kind: "Bucket", + }}, + } + assert.Equal(t, cmp.Diff(mypd.ListPackageKinds(), expectPkgKinds), "") + + exceptObj := `output: close({ + kind: "Bucket" + apiVersion: "apps.test.io/v1" + type: "alicloud_oss_bucket" + acl: "public-read-write" | "public-read" | *"private" + dataRedundancyType?: "ZRS" | *"LRS" + dataSourceRef?: { + dsPath: string + } + importRef?: { + importKey: string + } + output: { + {[!~"^(bucketName|extranetEndpoint|intranetEndpoint|masterUserId)$"]: { + outRef: string + } | { + // Example: demoVpc.vpcId + valueRef: string + }} + bucketName: { + outRef: "self.name" + } + extranetEndpoint: { + outRef: "self.state.extranetEndpoint" + } + intranetEndpoint: { + outRef: "self.state.intranetEndpoint" + } + masterUserId: { + outRef: "self.state.masterUserId" + } + } + profile: { + baasRepo: string | { + // Example: demoVpc.vpcId + valueRef: string + } + cloudProduct: "AliCloudOSS" + endpoint?: string | { + // Example: demoVpc.vpcId + valueRef: string + } + envType?: "testing" | "product" | { + // Example: demoVpc.vpcId + valueRef: string + } + provider: "alicloud" + region: string | { + // Example: demoVpc.vpcId + valueRef: string + } + serviceAccount?: string | { + // Example: demoVpc.vpcId + valueRef: string + } + } + storageClass?: "IA" | "Archive" | "ColdArchive" | *"Standard" +}) +` bi := build.NewContext().NewInstance("", nil) mypd.ImportBuiltinPackagesFor(bi) bi.AddFile("-", ` -import "kube/test.io/v1" +import "test.io/apps/v1" output: v1.#Bucket `) var r cue.Runtime @@ -343,66 +417,19 @@ output: v1.#Bucket assert.NilError(t, err) base, err := model.NewBase(inst.Value()) assert.NilError(t, err) + assert.Equal(t, base.String(), exceptObj) - exceptObj := `output: close({ - kind: "Bucket" - apiVersion: "test.io/v1" - type: "alicloud_oss_bucket" - acl: "public-read-write" | "public-read" | *"private" - dataRedundancyType?: "ZRS" | *"LRS" - dataSourceRef?: close({ - dsPath: string - }) - importRef?: close({ - importKey: string - }) - output: close({ - {[!~"^(bucketName|extranetEndpoint|intranetEndpoint|masterUserId)$"]: { - outRef: string - } | { - // Example: demoVpc.vpcId - valueRef: string - }} - bucketName: close({ - outRef: "self.name" - }) - extranetEndpoint: close({ - outRef: "self.state.extranetEndpoint" - }) - intranetEndpoint: close({ - outRef: "self.state.intranetEndpoint" - }) - masterUserId: close({ - outRef: "self.state.masterUserId" - }) - }) - profile: close({ - baasRepo: string | close({ - // Example: demoVpc.vpcId - valueRef: string - }) - cloudProduct: "AliCloudOSS" - endpoint?: string | close({ - // Example: demoVpc.vpcId - valueRef: string - }) - envType?: "testing" | "product" | close({ - // Example: demoVpc.vpcId - valueRef: string - }) - provider: "alicloud" - region: string | close({ - // Example: demoVpc.vpcId - valueRef: string - }) - serviceAccount?: string | close({ - // Example: demoVpc.vpcId - valueRef: string - }) - }) - storageClass?: "IA" | "Archive" | "ColdArchive" | *"Standard" -}) -` + bi = build.NewContext().NewInstance("", nil) + mypd.ImportBuiltinPackagesFor(bi) + bi.AddFile("-", ` +import "kube/apps.test.io/v1" +output: v1.#Bucket +`) + + inst, err = r.Build(bi) + assert.NilError(t, err) + base, err = model.NewBase(inst.Value()) + assert.NilError(t, err) assert.Equal(t, base.String(), exceptObj) } @@ -444,20 +471,20 @@ func TestProcessFile(t *testing.T) { } func TestMount(t *testing.T) { - mypd := &PackageDiscover{pkgKinds: make(map[string][]string)} + mypd := &PackageDiscover{pkgKinds: make(map[string][]VersionKind)} testPkg := newPackage("foo") - mypd.mount(testPkg, []string{"abc"}) + mypd.mount(testPkg, []VersionKind{}) assert.Equal(t, len(mypd.velaBuiltinPackages), 1) - mypd.mount(testPkg, []string{"abc"}) + mypd.mount(testPkg, []VersionKind{}) assert.Equal(t, len(mypd.velaBuiltinPackages), 1) assert.Equal(t, mypd.velaBuiltinPackages[0], testPkg.Instance) } -func TestGetGVK(t *testing.T) { +func TestGetDGVK(t *testing.T) { srcTmpl := ` { "x-kubernetes-group-version-kind": { - "group": "test.io", + "group": "apps.test.io", "kind": "Foo", "version": "v1" } @@ -466,12 +493,34 @@ func TestGetGVK(t *testing.T) { var r cue.Runtime inst, err := r.Compile("-", srcTmpl) assert.NilError(t, err) - gvk, err := getGVK(inst.Value().Lookup("x-kubernetes-group-version-kind")) + gvk, err := getDGVK(inst.Value().Lookup("x-kubernetes-group-version-kind")) assert.NilError(t, err) - assert.Equal(t, gvk, metav1.GroupVersionKind{ - Group: "test.io", - Version: "v1", - Kind: "Foo", + assert.Equal(t, gvk, domainGroupVersionKind{ + Domain: "test.io", + Group: "apps", + Version: "v1", + Kind: "Foo", + APIVersion: "apps.test.io/v1", + }) + + srcTmpl = ` +{ + "x-kubernetes-group-version-kind": { + "group": "test.io", + "kind": "Foo", + "version": "v1" + } +} +` + inst, err = r.Compile("-", srcTmpl) + assert.NilError(t, err) + gvk, err = getDGVK(inst.Value().Lookup("x-kubernetes-group-version-kind")) + assert.NilError(t, err) + assert.Equal(t, gvk, domainGroupVersionKind{ + Group: "test.io", + Version: "v1", + Kind: "Foo", + APIVersion: "test.io/v1", }) } @@ -485,7 +534,7 @@ func TestOpenAPIMapping(t *testing.T) { { input: []string{"definitions", "io.k8s.api.discovery.v1beta1.Endpoint"}, pos: token.NoPos, - result: "[#Endpoint]", + result: "[io_k8s_api_discovery_v1beta1_Endpoint]", }, { input: []string{"definitions", "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps"}, @@ -495,7 +544,7 @@ func TestOpenAPIMapping(t *testing.T) { { input: []string{"definitions", "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps"}, pos: token.NoPos, - result: "[#JSONSchemaProps]", + result: "[io_k8s_apiextensions-apiserver_pkg_apis_apiextensions_v1_JSONSchemaProps]", }, { input: []string{"definitions"}, @@ -504,8 +553,9 @@ func TestOpenAPIMapping(t *testing.T) { }, } + emptyMapper := make(map[string]domainGroupVersionKind) for _, tCase := range testCases { - labels, err := openAPIMapping(tCase.pos, tCase.input) + labels, err := openAPIMapping(emptyMapper)(tCase.pos, tCase.input) if tCase.errMsg != "" { assert.Error(t, err, tCase.errMsg) continue @@ -515,3 +565,70 @@ func TestOpenAPIMapping(t *testing.T) { assert.Equal(t, tCase.result, fmt.Sprint(labels)) } } + +func TestGeneratePkgName(t *testing.T) { + testCases := []struct { + dgvk domainGroupVersionKind + sdPkgName string + openPkgName string + }{ + { + dgvk: domainGroupVersionKind{ + Domain: "k8s.io", + Group: "networking", + Version: "v1", + Kind: "Ingress", + }, + sdPkgName: "k8s.io/networking/v1", + openPkgName: "kube/networking.k8s.io", + }, + { + dgvk: domainGroupVersionKind{ + Group: "example.com", + Version: "v1", + Kind: "Sls", + }, + sdPkgName: "example.com/v1", + openPkgName: "kube/example.com/v1", + }, + } + + for _, tCase := range testCases { + assert.Equal(t, genStandardPkgName(tCase.dgvk), tCase.sdPkgName) + } +} + +func TestReverseString(t *testing.T) { + testCases := []struct { + gvr metav1.GroupVersionKind + reverseString string + }{ + { + gvr: metav1.GroupVersionKind{ + Group: "networking.k8s.io", + Version: "v1", + Kind: "NetworkPolicy", + }, + reverseString: "io_k8s_api_networking_v1_NetworkPolicy", + }, + { + gvr: metav1.GroupVersionKind{ + Group: "example.com", + Version: "v1", + Kind: "Sls", + }, + reverseString: "com_example_v1_Sls", + }, + { + gvr: metav1.GroupVersionKind{ + Version: "v1", + Kind: "Pod", + }, + reverseString: "io_k8s_api_core_v1_Pod", + }, + } + + for _, tCase := range testCases { + assert.Equal(t, convert2DGVK(tCase.gvr).reverseString(), tCase.reverseString) + } +}