github.com/kubevela/workflow@v0.6.0/pkg/cue/packages/package_test.go (about)

     1  /*
     2  Copyright 2022 The KubeVela Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package packages
    18  
    19  import (
    20  	"fmt"
    21  	"testing"
    22  
    23  	"cuelang.org/go/cue"
    24  	"cuelang.org/go/cue/build"
    25  	"cuelang.org/go/cue/cuecontext"
    26  	"cuelang.org/go/cue/parser"
    27  	"cuelang.org/go/cue/token"
    28  	"github.com/stretchr/testify/require"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  
    31  	"github.com/kubevela/workflow/pkg/cue/model"
    32  )
    33  
    34  func TestPackage(t *testing.T) {
    35  	var openAPISchema = `
    36  {	
    37  	"paths": {
    38  		"paths...": {
    39  			"post":{
    40  				"x-kubernetes-group-version-kind": {
    41                      "group": "apps.test.io",
    42                      "kind": "Bucket",
    43                      "version": "v1"
    44                  }
    45  			}
    46  		}
    47  	},
    48      "definitions":{
    49          "io.test.apps.v1.Bucket":{
    50              "properties":{
    51  				"apiVersion": {"type": "string"}
    52   				"kind": {"type": "string"}
    53                  "acl":{
    54                      "default":"private",
    55                      "enum":[
    56                          "public-read-write",
    57                          "public-read",
    58                          "private"
    59                      ],
    60                      "type":"string"
    61                  },
    62                  "dataRedundancyType":{
    63                      "default":"LRS",
    64                      "enum":[
    65                          "LRS",
    66                          "ZRS"
    67                      ],
    68                      "type":"string"
    69                  },
    70                  "dataSourceRef":{
    71                      "properties":{
    72                          "dsPath":{
    73                              "type":"string"
    74                          }
    75                      },
    76                      "required":[
    77                          "dsPath"
    78                      ],
    79                      "type":"object"
    80                  },
    81                  "importRef":{
    82                      "properties":{
    83                          "importKey":{
    84                              "type":"string"
    85                          }
    86                      },
    87                      "required":[
    88                          "importKey"
    89                      ],
    90                      "type":"object"
    91                  },
    92                  "output":{
    93                      "additionalProperties":{
    94                          "oneOf":[
    95                              {
    96                                  "properties":{
    97                                      "outRef":{
    98                                          "type":"string"
    99                                      }
   100                                  },
   101                                  "required":[
   102                                      "outRef"
   103                                  ]
   104                              },
   105                              {
   106                                  "properties":{
   107                                      "valueRef":{
   108                                          "description":"Example: demoVpc.vpcId",
   109                                          "type":"string"
   110                                      }
   111                                  },
   112                                  "required":[
   113                                      "valueRef"
   114                                  ]
   115                              }
   116                          ],
   117                          "type":"object"
   118                      },
   119                      "properties":{
   120                          "bucketName":{
   121                              "properties":{
   122                                  "outRef":{
   123                                      "enum":[
   124                                          "self.name"
   125                                      ],
   126                                      "type":"string"
   127                                  }
   128                              },
   129                              "required":[
   130                                  "outRef"
   131                              ],
   132                              "type":"object"
   133                          },
   134                          "extranetEndpoint":{
   135                              "properties":{
   136                                  "outRef":{
   137                                      "enum":[
   138                                          "self.state.extranetEndpoint"
   139                                      ],
   140                                      "type":"string"
   141                                  }
   142                              },
   143                              "required":[
   144                                  "outRef"
   145                              ],
   146                              "type":"object"
   147                          },
   148                          "intranetEndpoint":{
   149                              "properties":{
   150                                  "outRef":{
   151                                      "enum":[
   152                                          "self.state.intranetEndpoint"
   153                                      ],
   154                                      "type":"string"
   155                                  }
   156                              },
   157                              "required":[
   158                                  "outRef"
   159                              ],
   160                              "type":"object"
   161                          },
   162                          "masterUserId":{
   163                              "properties":{
   164                                  "outRef":{
   165                                      "enum":[
   166                                          "self.state.masterUserId"
   167                                      ],
   168                                      "type":"string"
   169                                  }
   170                              },
   171                              "required":[
   172                                  "outRef"
   173                              ],
   174                              "type":"object"
   175                          }
   176                      },
   177                      "required":[
   178                          "bucketName",
   179                          "extranetEndpoint",
   180                          "intranetEndpoint",
   181                          "masterUserId"
   182                      ],
   183                      "type":"object"
   184                  },
   185                  "profile":{
   186                      "properties":{
   187                          "baasRepo":{
   188                              "oneOf":[
   189                                  {
   190                                      "type":"string"
   191                                  },
   192                                  {
   193                                      "properties":{
   194                                          "valueRef":{
   195                                              "description":"Example: demoVpc.vpcId",
   196                                              "type":"string"
   197                                          }
   198                                      },
   199                                      "required":[
   200                                          "valueRef"
   201                                      ],
   202                                      "type":"object"
   203                                  }
   204                              ]
   205                          },
   206                          "cloudProduct":{
   207                              "enum":[
   208                                  "AliCloudOSS"
   209                              ],
   210                              "type":"string"
   211                          },
   212                          "endpoint":{
   213                              "oneOf":[
   214                                  {
   215                                      "type":"string"
   216                                  },
   217                                  {
   218                                      "properties":{
   219                                          "valueRef":{
   220                                              "description":"Example: demoVpc.vpcId",
   221                                              "type":"string"
   222                                          }
   223                                      },
   224                                      "required":[
   225                                          "valueRef"
   226                                      ],
   227                                      "type":"object"
   228                                  }
   229                              ]
   230                          },
   231                          "envType":{
   232                              "oneOf":[
   233                                  {
   234                                      "enum":[
   235                                          "testing",
   236                                          "product"
   237                                      ]
   238                                  },
   239                                  {
   240                                      "properties":{
   241                                          "valueRef":{
   242                                              "description":"Example: demoVpc.vpcId",
   243                                              "type":"string"
   244                                          }
   245                                      },
   246                                      "required":[
   247                                          "valueRef"
   248                                      ],
   249                                      "type":"object"
   250                                  }
   251                              ]
   252                          },
   253                          "provider":{
   254                              "enum":[
   255                                  "alicloud"
   256                              ],
   257                              "type":"string"
   258                          },
   259                          "region":{
   260                              "oneOf":[
   261                                  {
   262                                      "type":"string"
   263                                  },
   264                                  {
   265                                      "properties":{
   266                                          "valueRef":{
   267                                              "description":"Example: demoVpc.vpcId",
   268                                              "type":"string"
   269                                          }
   270                                      },
   271                                      "required":[
   272                                          "valueRef"
   273                                      ],
   274                                      "type":"object"
   275                                  }
   276                              ]
   277                          },
   278                          "serviceAccount":{
   279                              "oneOf":[
   280                                  {
   281                                      "type":"string"
   282                                  },
   283                                  {
   284                                      "properties":{
   285                                          "valueRef":{
   286                                              "description":"Example: demoVpc.vpcId",
   287                                              "type":"string"
   288                                          }
   289                                      },
   290                                      "required":[
   291                                          "valueRef"
   292                                      ],
   293                                      "type":"object"
   294                                  }
   295                              ]
   296                          }
   297                      },
   298                      "required":[
   299                          "cloudProduct",
   300                          "provider",
   301                          "baasRepo",
   302                          "region"
   303                      ],
   304                      "type":"object"
   305                  },
   306                  "storageClass":{
   307                      "default":"Standard",
   308                      "enum":[
   309                          "Standard",
   310                          "IA",
   311                          "Archive",
   312                          "ColdArchive"
   313                      ],
   314                      "type":"string"
   315                  },
   316                  "type":{
   317                      "enum":[
   318                          "alicloud_oss_bucket"
   319                      ],
   320                      "type":"string"
   321                  }
   322              },
   323              "required":[
   324                  "type",
   325                  "output",
   326                  "profile",
   327                  "acl"
   328              ],
   329              "type":"object"
   330          }
   331      }
   332  }
   333  `
   334  	r := require.New(t)
   335  	mypd := &PackageDiscover{pkgKinds: make(map[string][]VersionKind)}
   336  	err := mypd.addKubeCUEPackagesFromCluster(openAPISchema)
   337  	r.NoError(err)
   338  	expectPkgKinds := map[string][]VersionKind{
   339  		"test.io/apps/v1": []VersionKind{{
   340  			DefinitionName: "#Bucket",
   341  			APIVersion:     "apps.test.io/v1",
   342  			Kind:           "Bucket",
   343  		}},
   344  		"kube/apps.test.io/v1": []VersionKind{{
   345  			DefinitionName: "#Bucket",
   346  			APIVersion:     "apps.test.io/v1",
   347  			Kind:           "Bucket",
   348  		}},
   349  	}
   350  	r.Equal(mypd.ListPackageKinds(), expectPkgKinds)
   351  
   352  	// TODO: fix losing close struct in cue
   353  	exceptObj := `output: {
   354  	apiVersion:          "apps.test.io/v1"
   355  	kind:                "Bucket"
   356  	acl:                 *"private" | "public-read" | "public-read-write"
   357  	dataRedundancyType?: "LRS" | "ZRS" | *"LRS"
   358  	dataSourceRef?: {
   359  		dsPath: string
   360  	}
   361  	importRef?: {
   362  		importKey: string
   363  	}
   364  	output: {
   365  		bucketName: {
   366  			outRef: "self.name"
   367  		}
   368  		extranetEndpoint: {
   369  			outRef: "self.state.extranetEndpoint"
   370  		}
   371  		intranetEndpoint: {
   372  			outRef: "self.state.intranetEndpoint"
   373  		}
   374  		masterUserId: {
   375  			outRef: "self.state.masterUserId"
   376  		}
   377  	}
   378  	profile: {
   379  		baasRepo: string | {
   380  			// Example: demoVpc.vpcId
   381  			valueRef: string
   382  		}
   383  		cloudProduct: "AliCloudOSS"
   384  		endpoint?:    string | {
   385  			// Example: demoVpc.vpcId
   386  			valueRef: string
   387  		}
   388  		envType?: "testing" | "product" | {
   389  			// Example: demoVpc.vpcId
   390  			valueRef: string
   391  		}
   392  		provider: "alicloud"
   393  		region:   string | {
   394  			// Example: demoVpc.vpcId
   395  			valueRef: string
   396  		}
   397  		serviceAccount?: string | {
   398  			// Example: demoVpc.vpcId
   399  			valueRef: string
   400  		}
   401  	}
   402  	storageClass?: "Standard" | "IA" | "Archive" | "ColdArchive" | *"Standard"
   403  	type:          "alicloud_oss_bucket"
   404  }
   405  `
   406  	bi := build.NewContext().NewInstance("", nil)
   407  	file, err := parser.ParseFile("-", `
   408  import "test.io/apps/v1"
   409  output: v1.#Bucket
   410  `)
   411  	r.NoError(err)
   412  	err = bi.AddSyntax(file)
   413  	r.NoError(err)
   414  	inst, err := mypd.ImportPackagesAndBuildValue(bi)
   415  	r.NoError(err)
   416  	base, err := model.NewBase(inst)
   417  	r.NoError(err)
   418  	s, err := base.String()
   419  	r.NoError(err)
   420  	r.Equal(s, exceptObj)
   421  
   422  	bi = build.NewContext().NewInstance("", nil)
   423  	file, err = parser.ParseFile("-", `
   424  import "kube/apps.test.io/v1"
   425  output: v1.#Bucket
   426  `)
   427  	r.NoError(err)
   428  	err = bi.AddSyntax(file)
   429  	r.NoError(err)
   430  	inst, err = mypd.ImportPackagesAndBuildValue(bi)
   431  	r.NoError(err)
   432  	base, err = model.NewBase(inst)
   433  	r.NoError(err)
   434  	s, err = base.String()
   435  	r.NoError(err)
   436  	r.Equal(s, exceptObj)
   437  }
   438  
   439  func TestProcessFile(t *testing.T) {
   440  	srcTmpl := `
   441  #Definition: {
   442  	kind?: string
   443  	apiVersion?: string
   444  	metadata: {
   445  		name: string
   446  		...
   447  	}
   448  	...
   449  }
   450  `
   451  	r := require.New(t)
   452  	file, err := parser.ParseFile("-", srcTmpl)
   453  	r.NoError(err)
   454  	testPkg := newPackage("foo")
   455  	testPkg.processOpenAPIFile(file)
   456  	cuectx := cuecontext.New()
   457  	inst := cuectx.BuildFile(file)
   458  
   459  	testCasesInst := cuectx.CompileString(`
   460  #Definition: {}
   461  case1: #Definition & {additionalProperty: "test"}
   462  
   463  case2: #Definition & {
   464      metadata: {
   465          additionalProperty: "test"
   466      }
   467  }
   468  `)
   469  	retInst := inst.FillPath(cue.ParsePath(""), testCasesInst.Value())
   470  	r.Error(retInst.LookupPath(cue.ParsePath("case1")).Err(), "case1.additionalProperty: field not allowed")
   471  	r.Error(retInst.LookupPath(cue.ParsePath("case2.metadata")).Err(), "case2.metadata.additionalProperty: field not allowed")
   472  }
   473  
   474  func TestMount(t *testing.T) {
   475  	mypd := &PackageDiscover{pkgKinds: make(map[string][]VersionKind)}
   476  	testPkg := newPackage("foo")
   477  	mypd.mount(testPkg, []VersionKind{})
   478  	r := require.New(t)
   479  	r.Equal(len(mypd.velaBuiltinPackages), 1)
   480  	mypd.mount(testPkg, []VersionKind{})
   481  	r.Equal(len(mypd.velaBuiltinPackages), 1)
   482  	r.Equal(mypd.velaBuiltinPackages[0], testPkg.Instance)
   483  }
   484  
   485  func TestGetDGVK(t *testing.T) {
   486  	srcTmpl := `
   487  {
   488  	"x-kubernetes-group-version-kind": {
   489  		"group": "apps.test.io",
   490  		"kind": "Foo",
   491  		"version": "v1"
   492  	}
   493  }
   494  `
   495  	r := require.New(t)
   496  	file, err := parser.ParseFile("-", srcTmpl)
   497  	r.NoError(err)
   498  	inst := cuecontext.New().BuildFile(file)
   499  	gvk, err := getDGVK(inst.Value().LookupPath(cue.ParsePath("\"x-kubernetes-group-version-kind\"")))
   500  	r.NoError(err)
   501  	r.Equal(gvk, domainGroupVersionKind{
   502  		Domain:     "test.io",
   503  		Group:      "apps",
   504  		Version:    "v1",
   505  		Kind:       "Foo",
   506  		APIVersion: "apps.test.io/v1",
   507  	})
   508  
   509  	srcTmpl = `
   510  {
   511  	"x-kubernetes-group-version-kind": {
   512  		"group": "test.io",
   513  		"kind": "Foo",
   514  		"version": "v1"
   515  	}
   516  }
   517  `
   518  	inst = cuecontext.New().CompileString(srcTmpl)
   519  	gvk, err = getDGVK(inst.LookupPath(cue.ParsePath("\"x-kubernetes-group-version-kind\"")))
   520  	r.NoError(err)
   521  	r.Equal(gvk, domainGroupVersionKind{
   522  		Group:      "test.io",
   523  		Version:    "v1",
   524  		Kind:       "Foo",
   525  		APIVersion: "test.io/v1",
   526  	})
   527  }
   528  
   529  func TestOpenAPIMapping(t *testing.T) {
   530  	testCases := []struct {
   531  		input  []string
   532  		pos    token.Pos
   533  		result string
   534  		errMsg string
   535  	}{
   536  		{
   537  			input:  []string{"definitions", "io.k8s.api.discovery.v1beta1.Endpoint"},
   538  			pos:    token.NoPos,
   539  			result: "[io_k8s_api_discovery_v1beta1_Endpoint]",
   540  		},
   541  		{
   542  			input:  []string{"definitions", "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps"},
   543  			pos:    token.NoPos.Add(1),
   544  			result: "[_]",
   545  		},
   546  		{
   547  			input:  []string{"definitions", "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps"},
   548  			pos:    token.NoPos,
   549  			result: "[io_k8s_apiextensions_apiserver_pkg_apis_apiextensions_v1_JSONSchemaProps]",
   550  		},
   551  		{
   552  			input:  []string{"definitions"},
   553  			pos:    token.NoPos,
   554  			errMsg: "openAPIMapping format invalid",
   555  		},
   556  	}
   557  
   558  	emptyMapper := make(map[string]domainGroupVersionKind)
   559  	for _, tCase := range testCases {
   560  		r := require.New(t)
   561  		labels, err := openAPIMapping(emptyMapper)(tCase.pos, tCase.input)
   562  		if tCase.errMsg != "" {
   563  			r.Error(err, tCase.errMsg)
   564  			continue
   565  		}
   566  		r.NoError(err)
   567  		r.Equal(len(labels), 1)
   568  		r.Equal(tCase.result, fmt.Sprint(labels))
   569  	}
   570  }
   571  
   572  func TestGeneratePkgName(t *testing.T) {
   573  	testCases := []struct {
   574  		dgvk        domainGroupVersionKind
   575  		sdPkgName   string
   576  		openPkgName string
   577  	}{
   578  		{
   579  			dgvk: domainGroupVersionKind{
   580  				Domain:  "k8s.io",
   581  				Group:   "networking",
   582  				Version: "v1",
   583  				Kind:    "Ingress",
   584  			},
   585  			sdPkgName:   "k8s.io/networking/v1",
   586  			openPkgName: "kube/networking.k8s.io",
   587  		},
   588  		{
   589  			dgvk: domainGroupVersionKind{
   590  				Group:   "example.com",
   591  				Version: "v1",
   592  				Kind:    "Sls",
   593  			},
   594  			sdPkgName:   "example.com/v1",
   595  			openPkgName: "kube/example.com/v1",
   596  		},
   597  	}
   598  
   599  	for _, tCase := range testCases {
   600  		r := require.New(t)
   601  		r.Equal(genStandardPkgName(tCase.dgvk), tCase.sdPkgName)
   602  	}
   603  }
   604  
   605  func TestReverseString(t *testing.T) {
   606  	testCases := []struct {
   607  		gvr           metav1.GroupVersionKind
   608  		reverseString string
   609  	}{
   610  		{
   611  			gvr: metav1.GroupVersionKind{
   612  				Group:   "networking.k8s.io",
   613  				Version: "v1",
   614  				Kind:    "NetworkPolicy",
   615  			},
   616  			reverseString: "io_k8s_api_networking_v1_NetworkPolicy",
   617  		},
   618  		{
   619  			gvr: metav1.GroupVersionKind{
   620  				Group:   "example.com",
   621  				Version: "v1",
   622  				Kind:    "Sls",
   623  			},
   624  			reverseString: "com_example_v1_Sls",
   625  		},
   626  		{
   627  			gvr: metav1.GroupVersionKind{
   628  				Version: "v1",
   629  				Kind:    "Pod",
   630  			},
   631  			reverseString: "io_k8s_api_core_v1_Pod",
   632  		},
   633  	}
   634  
   635  	for _, tCase := range testCases {
   636  		r := require.New(t)
   637  		r.Equal(convert2DGVK(tCase.gvr).reverseString(), tCase.reverseString)
   638  	}
   639  }