diff --git a/pkg/controller/utils/capability.go b/pkg/controller/utils/capability.go index eb39617e3..e2a42fd7f 100644 --- a/pkg/controller/utils/capability.go +++ b/pkg/controller/utils/capability.go @@ -329,10 +329,8 @@ func generateOpenAPISchemaFromCapabilityParameter(capability types.Capability, p if err != nil { return nil, err } - pd.ImportBuiltinPackagesFor(bi) - var r cue.Runtime - cueInst, err := r.Build(bi) + cueInst, err := pd.ImportPackagesAndBuildInstance(bi) if err != nil { return nil, err } diff --git a/pkg/dsl/definition/package.go b/pkg/dsl/definition/package.go index 90614654e..4ecc6e438 100644 --- a/pkg/dsl/definition/package.go +++ b/pkg/dsl/definition/package.go @@ -82,6 +82,20 @@ func (pd *PackageDiscover) ImportBuiltinPackagesFor(bi *build.Instance) { bi.Imports = append(bi.Imports, pd.velaBuiltinPackages...) } +// ImportPackagesAndBuildInstance Combine import built-in packages and build cue template together to avoid data race +func (pd *PackageDiscover) ImportPackagesAndBuildInstance(bi *build.Instance) (inst *cue.Instance, err error) { + pd.ImportBuiltinPackagesFor(bi) + + var r cue.Runtime + pd.mutex.Lock() + defer pd.mutex.Unlock() + cueInst, err := r.Build(bi) + if err != nil { + return nil, err + } + return cueInst, err +} + // ListPackageKinds list packages and their kinds func (pd *PackageDiscover) ListPackageKinds() map[string][]VersionKind { pd.mutex.RLock() @@ -132,6 +146,30 @@ func (pd *PackageDiscover) mount(pkg *pkgInstance, pkgKinds []VersionKind) { pd.velaBuiltinPackages = append(pd.velaBuiltinPackages, pkg.Instance) } +func (pd *PackageDiscover) pkgBuild(packages map[string]*pkgInstance, pkgName string, + dGVK domainGroupVersionKind, def string, kubePkg *pkgInstance, groupKinds map[string][]VersionKind) error { + pkg, ok := packages[pkgName] + if !ok { + pkg = newPackage(pkgName) + pkg.Imports = []*build.Instance{kubePkg.Instance} + } + + mykinds := groupKinds[pkgName] + mykinds = append(mykinds, VersionKind{ + APIVersion: dGVK.APIVersion, + Kind: dGVK.Kind, + DefinitionName: "#" + dGVK.Kind, + }) + + if err := pkg.AddFile(dGVK.reverseString(), def); err != nil { + return err + } + + packages[pkgName] = pkg + groupKinds[pkgName] = mykinds + return nil +} + func (pd *PackageDiscover) addKubeCUEPackagesFromCluster(apiSchema string) error { var r cue.Runtime oaInst, err := r.Compile("-", apiSchema) @@ -180,32 +218,10 @@ kind: "%s" apiVersion: "%s", }`, 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 - } - if err := pkgBuild(genStandardPkgName(v)); err != nil { + if err := pd.pkgBuild(packages, genStandardPkgName(v), v, def, kubePkg, groupKinds); err != nil { return err } - if err := pkgBuild(genOpenPkgName(v)); err != nil { + if err := pd.pkgBuild(packages, genOpenPkgName(v), v, def, kubePkg, groupKinds); err != nil { return err } } diff --git a/pkg/dsl/definition/package_suit_test.go b/pkg/dsl/definition/package_suit_test.go index f03de7134..8287848cf 100644 --- a/pkg/dsl/definition/package_suit_test.go +++ b/pkg/dsl/definition/package_suit_test.go @@ -25,7 +25,6 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "cuelang.org/go/cue" "cuelang.org/go/cue/build" "github.com/google/go-cmp/cmp" admissionregistrationv1 "k8s.io/api/admissionregistration/v1" @@ -100,7 +99,6 @@ var _ = Describe("Package discovery resources for definition from K8s APIServer" By("test ingress in kube package") bi := build.NewContext().NewInstance("", nil) - pd.ImportBuiltinPackagesFor(bi) err := bi.AddFile("-", ` import ( kube "kube/networking.k8s.io/v1beta1" @@ -134,8 +132,7 @@ parameter: { } }`) Expect(err).ToNot(HaveOccurred()) - var r cue.Runtime - inst, err := r.Build(bi) + inst, err := pd.ImportPackagesAndBuildInstance(bi) Expect(err).Should(BeNil()) base, err := model.NewBase(inst.Lookup("output")) Expect(err).Should(BeNil()) @@ -161,7 +158,6 @@ parameter: { })).Should(BeEquivalentTo("")) By("test Invalid Import path") bi = build.NewContext().NewInstance("", nil) - pd.ImportBuiltinPackagesFor(bi) bi.AddFile("-", ` import ( kube "kube/networking.k8s.io/v1" @@ -183,7 +179,7 @@ parameter: { name: "myapp" image: "nginx" }`) - inst, err = r.Build(bi) + inst, err = pd.ImportPackagesAndBuildInstance(bi) Expect(err).Should(BeNil()) _, err = model.NewBase(inst.Lookup("output")) Expect(err).ShouldNot(BeNil()) @@ -191,7 +187,6 @@ parameter: { By("test Deployment in kube package") bi = build.NewContext().NewInstance("", nil) - pd.ImportBuiltinPackagesFor(bi) bi.AddFile("-", ` import ( kube "kube/apps/v1" @@ -212,7 +207,7 @@ parameter: { name: "myapp" image: "nginx" }`) - inst, err = r.Build(bi) + inst, err = pd.ImportPackagesAndBuildInstance(bi) Expect(err).Should(BeNil()) base, err = model.NewBase(inst.Lookup("output")) Expect(err).Should(BeNil()) @@ -234,7 +229,6 @@ parameter: { By("test Secret in kube package") bi = build.NewContext().NewInstance("", nil) - pd.ImportBuiltinPackagesFor(bi) bi.AddFile("-", ` import ( kube "kube/v1" @@ -249,7 +243,7 @@ output: { parameter: { name: "myapp" }`) - inst, err = r.Build(bi) + inst, err = pd.ImportPackagesAndBuildInstance(bi) Expect(err).Should(BeNil()) base, err = model.NewBase(inst.Lookup("output")) Expect(err).Should(BeNil()) @@ -263,7 +257,6 @@ parameter: { By("test Service in kube package") bi = build.NewContext().NewInstance("", nil) - pd.ImportBuiltinPackagesFor(bi) bi.AddFile("-", ` import ( kube "kube/v1" @@ -278,7 +271,7 @@ output: { parameter: { name: "myapp" }`) - inst, err = r.Build(bi) + inst, err = pd.ImportPackagesAndBuildInstance(bi) Expect(err).Should(BeNil()) base, err = model.NewBase(inst.Lookup("output")) Expect(err).Should(BeNil()) @@ -367,7 +360,6 @@ parameter: { }, time.Second*30, time.Millisecond*300).Should(BeNil()) bi = build.NewContext().NewInstance("", nil) - pd.ImportBuiltinPackagesFor(bi) err = bi.AddFile("-", ` import ( kv1 "kube/example.com/v1" @@ -379,7 +371,7 @@ output: { } `) Expect(err).Should(BeNil()) - inst, err = r.Build(bi) + inst, err = pd.ImportPackagesAndBuildInstance(bi) Expect(err).Should(BeNil()) base, err = model.NewBase(inst.Lookup("output")) Expect(err).Should(BeNil()) @@ -400,7 +392,6 @@ output: { By("test ingress in kube package") bi := build.NewContext().NewInstance("", nil) - pd.ImportBuiltinPackagesFor(bi) err := bi.AddFile("-", ` import ( network "k8s.io/networking/v1beta1" @@ -432,8 +423,7 @@ parameter: { } }`) Expect(err).ToNot(HaveOccurred()) - var r cue.Runtime - inst, err := r.Build(bi) + inst, err := pd.ImportPackagesAndBuildInstance(bi) Expect(err).Should(BeNil()) base, err := model.NewBase(inst.Lookup("output")) Expect(err).Should(BeNil()) @@ -459,7 +449,6 @@ parameter: { })).Should(BeEquivalentTo("")) By("test Invalid Import path") bi = build.NewContext().NewInstance("", nil) - pd.ImportBuiltinPackagesFor(bi) bi.AddFile("-", ` import ( "k8s.io/networking/v1" @@ -481,7 +470,7 @@ parameter: { name: "myapp" image: "nginx" }`) - inst, err = r.Build(bi) + inst, err = pd.ImportPackagesAndBuildInstance(bi) Expect(err).Should(BeNil()) _, err = model.NewBase(inst.Lookup("output")) Expect(err).ShouldNot(BeNil()) @@ -489,7 +478,6 @@ parameter: { By("test Deployment in kube package") bi = build.NewContext().NewInstance("", nil) - pd.ImportBuiltinPackagesFor(bi) bi.AddFile("-", ` import ( apps "k8s.io/apps/v1" @@ -510,7 +498,7 @@ parameter: { name: "myapp" image: "nginx" }`) - inst, err = r.Build(bi) + inst, err = pd.ImportPackagesAndBuildInstance(bi) Expect(err).Should(BeNil()) base, err = model.NewBase(inst.Lookup("output")) Expect(err).Should(BeNil()) @@ -532,7 +520,6 @@ parameter: { By("test Secret in kube package") bi = build.NewContext().NewInstance("", nil) - pd.ImportBuiltinPackagesFor(bi) bi.AddFile("-", ` import ( "k8s.io/core/v1" @@ -547,7 +534,7 @@ output: { parameter: { name: "myapp" }`) - inst, err = r.Build(bi) + inst, err = pd.ImportPackagesAndBuildInstance(bi) Expect(err).Should(BeNil()) base, err = model.NewBase(inst.Lookup("output")) Expect(err).Should(BeNil()) @@ -561,7 +548,6 @@ parameter: { By("test Service in kube package") bi = build.NewContext().NewInstance("", nil) - pd.ImportBuiltinPackagesFor(bi) bi.AddFile("-", ` import ( "k8s.io/core/v1" @@ -576,7 +562,7 @@ output: { parameter: { name: "myapp" }`) - inst, err = r.Build(bi) + inst, err = pd.ImportPackagesAndBuildInstance(bi) Expect(err).Should(BeNil()) base, err = model.NewBase(inst.Lookup("output")) Expect(err).Should(BeNil()) @@ -665,7 +651,6 @@ parameter: { }, time.Second*30, time.Millisecond*300).Should(BeNil()) bi = build.NewContext().NewInstance("", nil) - pd.ImportBuiltinPackagesFor(bi) err = bi.AddFile("-", ` import ( ev1 "example.com/v1" @@ -677,7 +662,7 @@ output: { } `) Expect(err).Should(BeNil()) - inst, err = r.Build(bi) + inst, err = pd.ImportPackagesAndBuildInstance(bi) Expect(err).Should(BeNil()) base, err = model.NewBase(inst.Lookup("output")) Expect(err).Should(BeNil()) diff --git a/pkg/dsl/definition/package_test.go b/pkg/dsl/definition/package_test.go index b7a595291..8dc008133 100644 --- a/pkg/dsl/definition/package_test.go +++ b/pkg/dsl/definition/package_test.go @@ -407,26 +407,22 @@ func TestPackage(t *testing.T) { }) ` bi := build.NewContext().NewInstance("", nil) - mypd.ImportBuiltinPackagesFor(bi) bi.AddFile("-", ` import "test.io/apps/v1" output: v1.#Bucket `) - var r cue.Runtime - inst, err := r.Build(bi) + inst, err := mypd.ImportPackagesAndBuildInstance(bi) assert.NilError(t, err) base, err := model.NewBase(inst.Value()) assert.NilError(t, err) assert.Equal(t, base.String(), exceptObj) bi = build.NewContext().NewInstance("", nil) - mypd.ImportBuiltinPackagesFor(bi) bi.AddFile("-", ` import "kube/apps.test.io/v1" output: v1.#Bucket `) - - inst, err = r.Build(bi) + inst, err = mypd.ImportPackagesAndBuildInstance(bi) assert.NilError(t, err) base, err = model.NewBase(inst.Value()) assert.NilError(t, err) diff --git a/pkg/dsl/definition/template.go b/pkg/dsl/definition/template.go index 411faa5b6..2e408cad8 100644 --- a/pkg/dsl/definition/template.go +++ b/pkg/dsl/definition/template.go @@ -103,9 +103,8 @@ func (wd *workloadDef) Complete(ctx process.Context, abstractTemplate string, pa if err := bi.AddFile("-", ctx.ExtendedContextFile()); err != nil { return err } - wd.pd.ImportBuiltinPackagesFor(bi) - var r cue.Runtime - inst, err := r.Build(bi) + + inst, err := wd.pd.ImportPackagesAndBuildInstance(bi) if err != nil { return err } @@ -285,52 +284,52 @@ func (td *traitDef) Complete(ctx process.Context, abstractTemplate string, param if err := bi.AddFile("context", ctx.ExtendedContextFile()); err != nil { return errors.WithMessagef(err, "invalid context of trait %s", td.name) } - td.pd.ImportBuiltinPackagesFor(bi) - instances := cue.Build([]*build.Instance{bi}) - for _, inst := range instances { - if err := inst.Value().Err(); err != nil { - return errors.WithMessagef(err, "invalid template of trait %s after merge with parameter and context", td.name) - } - processing := inst.Lookup("processing") - var err error - if processing.Exists() { - if inst, err = task.Process(inst); err != nil { - return errors.WithMessagef(err, "invalid process of trait %s", td.name) - } - } + inst, err := td.pd.ImportPackagesAndBuildInstance(bi) + if err != nil { + return err + } - outputs := inst.Lookup(OutputsFieldName) - if outputs.Exists() { - st, err := outputs.Struct() - if err != nil { - return errors.WithMessagef(err, "invalid outputs of trait %s", td.name) - } - for i := 0; i < st.Len(); i++ { - fieldInfo := st.Field(i) - if fieldInfo.IsDefinition || fieldInfo.IsHidden || fieldInfo.IsOptional { - continue - } - other, err := model.NewOther(fieldInfo.Value) - if err != nil { - return errors.WithMessagef(err, "invalid outputs(resource=%s) of trait %s", fieldInfo.Name, td.name) - } - ctx.AppendAuxiliaries(process.Auxiliary{Ins: other, Type: td.name, Name: fieldInfo.Name}) - } - } - - patcher := inst.Lookup(PatchFieldName) - if patcher.Exists() { - base, _ := ctx.Output() - p, err := model.NewOther(patcher) - if err != nil { - return errors.WithMessagef(err, "invalid patch of trait %s", td.name) - } - if err := base.Unify(p); err != nil { - return errors.WithMessagef(err, "invalid patch trait %s into workload", td.name) - } + if err := inst.Value().Err(); err != nil { + return errors.WithMessagef(err, "invalid template of trait %s after merge with parameter and context", td.name) + } + processing := inst.Lookup("processing") + if processing.Exists() { + if inst, err = task.Process(inst); err != nil { + return errors.WithMessagef(err, "invalid process of trait %s", td.name) } } + outputs := inst.Lookup(OutputsFieldName) + if outputs.Exists() { + st, err := outputs.Struct() + if err != nil { + return errors.WithMessagef(err, "invalid outputs of trait %s", td.name) + } + for i := 0; i < st.Len(); i++ { + fieldInfo := st.Field(i) + if fieldInfo.IsDefinition || fieldInfo.IsHidden || fieldInfo.IsOptional { + continue + } + other, err := model.NewOther(fieldInfo.Value) + if err != nil { + return errors.WithMessagef(err, "invalid outputs(resource=%s) of trait %s", fieldInfo.Name, td.name) + } + ctx.AppendAuxiliaries(process.Auxiliary{Ins: other, Type: td.name, Name: fieldInfo.Name}) + } + } + + patcher := inst.Lookup(PatchFieldName) + if patcher.Exists() { + base, _ := ctx.Output() + p, err := model.NewOther(patcher) + if err != nil { + return errors.WithMessagef(err, "invalid patch of trait %s", td.name) + } + if err := base.Unify(p); err != nil { + return errors.WithMessagef(err, "invalid patch trait %s into workload", td.name) + } + } + return nil }