Unverified Commit acf497b1 authored by Jianbo Sun's avatar Jianbo Sun Committed by GitHub
Browse files

make CUE inner kube package as a standalone package && clean code (#1263)

parent 30f30d9c
Showing with 743 additions and 265 deletions
+743 -265
......@@ -39,7 +39,7 @@ all: build
# Run tests
test: vet lint staticcheck
go test -race -coverprofile=coverage.txt -covermode=atomic ./pkg/... ./cmd/...
go test -race -covermode=atomic ./references/apiserver/... ./references/cli/... ./references/common/...
go test -race -covermode=atomic ./references/apiserver/... ./references/appfile/... ./references/cli/... ./references/common/... ./references/plugins/...
@$(OK) unit-tests pass
# Build manager binary
......
......@@ -31,11 +31,13 @@ import (
oamcore "github.com/oam-dev/kubevela/apis/core.oam.dev"
velacore "github.com/oam-dev/kubevela/apis/standard.oam.dev/v1alpha1"
velacontroller "github.com/oam-dev/kubevela/pkg/controller"
standardcontroller "github.com/oam-dev/kubevela/pkg/controller"
oamcontroller "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev"
oamv1alpha2 "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2"
"github.com/oam-dev/kubevela/pkg/controller/utils"
"github.com/oam-dev/kubevela/pkg/dsl/definition"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
"github.com/oam-dev/kubevela/pkg/utils/system"
oamwebhook "github.com/oam-dev/kubevela/pkg/webhook/core.oam.dev"
velawebhook "github.com/oam-dev/kubevela/pkg/webhook/standard.oam.dev"
......@@ -156,19 +158,6 @@ func main() {
os.Exit(1)
}
if useWebhook {
setupLog.Info("vela webhook enabled, will serving at :" + strconv.Itoa(webhookPort))
if err = oamwebhook.Register(mgr); err != nil {
setupLog.Error(err, "unable to setup oam runtime webhook")
os.Exit(1)
}
velawebhook.Register(mgr, disableCaps)
if err := waitWebhookSecretVolume(certDir, waitSecretTimeout, waitSecretInterval); err != nil {
setupLog.Error(err, "unable to get webhook secret")
os.Exit(1)
}
}
switch strings.ToLower(applyOnceOnly) {
case "", "false", string(oamcontroller.ApplyOnceOnlyOff):
controllerArgs.ApplyMode = oamcontroller.ApplyOnceOnlyOff
......@@ -186,12 +175,35 @@ func main() {
os.Exit(1)
}
dm, err := discoverymapper.New(mgr.GetConfig())
if err != nil {
setupLog.Error(err, "failed to create CRD discovery client")
os.Exit(1)
}
controllerArgs.DiscoveryMapper = dm
pd, err := definition.NewPackageDiscover(mgr.GetConfig())
if err != nil {
setupLog.Error(err, "failed to create CRD discovery for CUE package client")
os.Exit(1)
}
controllerArgs.PackageDiscover = pd
if useWebhook {
setupLog.Info("vela webhook enabled, will serving at :" + strconv.Itoa(webhookPort))
oamwebhook.Register(mgr, controllerArgs)
velawebhook.Register(mgr, disableCaps)
if err := waitWebhookSecretVolume(certDir, waitSecretTimeout, waitSecretInterval); err != nil {
setupLog.Error(err, "unable to get webhook secret")
os.Exit(1)
}
}
if err = oamv1alpha2.Setup(mgr, controllerArgs, logging.NewLogrLogger(setupLog)); err != nil {
setupLog.Error(err, "unable to setup the oam core controller")
os.Exit(1)
}
if err = velacontroller.Setup(mgr, disableCaps); err != nil {
if err = standardcontroller.Setup(mgr, disableCaps); err != nil {
setupLog.Error(err, "unable to setup the vela core controller")
os.Exit(1)
}
......
......@@ -380,20 +380,62 @@ output: {
### Import Kube Package
KubeVela automatically generates a cue package named `kube` as internal packages by reading K8s openapi from the
KubeVela automatically generates all K8s resources as internal packages by reading K8s openapi from the
installed K8s cluster.
You can use package `kube` in CUE Template of KubeVela just like the same way with the CUE internal packages.
You can use these packages with the format `kube/<apiVersion>` in CUE Template of KubeVela just like the same way
with the CUE internal packages.
For example, `Deployment` can be used as:
```cue
import ("kube")
import (
apps "kube/apps/v1"
)
parameter: {
name: string
}
output: kube.#Deployment
output: apps.#Deployment
output: {
metadata: name: parameter.name
}
```
Service can be used as (import package with an alias is not necessary):
```cue
import ("kube/v1")
output: v1.#Service
output: {
metadata: {
"name": parameter.name
}
spec: type: "ClusterIP",
}
parameter: {
name: "myapp"
}
```
Even the installed CRD works:
```
import (
oam "kube/core.oam.dev/v1alpha2"
)
output: oam.#Application
output: {
metadata: {
"name": parameter.name
}
}
parameter: {
name: "myapp"
}
```
\ No newline at end of file
......@@ -3,30 +3,28 @@ kind: TraitDefinition
metadata:
name: myingress
spec:
status:
customStatus: |-
if len(context.outputs.ingress.status.loadBalancer.ingress) > 0 {
message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host + ", IP: " + context.outputs.ingress.status.loadBalancer.ingress[0].ip
}
if len(context.outputs.ingress.status.loadBalancer.ingress) == 0 {
message: "No loadBalancer found, visiting by using 'vela port-forward " + context.appName + " --route'\n"
}
healthPolicy: |
isHealth: len(context.outputs.service.spec.clusterIP) > 0
appliesToWorkloads:
- myworker
- "*"
schematic:
cue:
template: |
import (
kubev1 "kube/v1"
network "kube/networking.k8s.io/v1beta1"
)
parameter: {
domain: string
http: [string]: int
}
outputs: {
service: kubev1.#Service
ingress: network.#Ingress
}
// trait template can have multiple outputs in one trait
outputs: service: {
apiVersion: "v1"
kind: "Service"
metadata:
name: context.name
spec: {
......@@ -42,8 +40,6 @@ spec:
}
outputs: ingress: {
apiVersion: "networking.k8s.io/v1beta1"
kind: "Ingress"
metadata:
name: context.name
spec: {
......
......@@ -8,9 +8,11 @@ spec:
schematic:
cue:
template: |
import (
apps "kube/apps/v1"
)
output: apps.#Deployment
output: {
apiVersion: "apps/v1"
kind: "Deployment"
spec: {
selector: matchLabels: {
"app.oam.dev/component": context.name
......
......@@ -45,6 +45,8 @@ type Workload struct {
DefinitionReference v1alpha2.WorkloadGVK
// TODO: remove all the duplicate fields above as workload now contains the whole template
FullTemplate *util.Template
engine definition.AbstractEngine
}
// GetUserConfigName get user config from AppFile, it will contain config file in it.
......@@ -65,17 +67,17 @@ func (wl *Workload) GetUserConfigName() string {
// EvalContext eval workload template and set result to context
func (wl *Workload) EvalContext(ctx process.Context) error {
return definition.NewWorkloadAbstractEngine(wl.Name).Params(wl.Params).Complete(ctx, wl.Template)
return wl.engine.Complete(ctx, wl.Template, wl.Params)
}
// EvalStatus eval workload status
func (wl *Workload) EvalStatus(ctx process.Context, cli client.Client, ns string) (string, error) {
return definition.NewWorkloadAbstractEngine(wl.Name).Status(ctx, cli, ns, wl.CustomStatusFormat)
return wl.engine.Status(ctx, cli, ns, wl.CustomStatusFormat)
}
// EvalHealth eval workload health check
func (wl *Workload) EvalHealth(ctx process.Context, client client.Client, namespace string) (bool, error) {
return definition.NewWorkloadAbstractEngine(wl.Name).HealthCheck(ctx, client, namespace, wl.HealthCheckPolicy)
return wl.engine.HealthCheck(ctx, client, namespace, wl.HealthCheckPolicy)
}
// Scope defines the scope of workload
......@@ -96,21 +98,22 @@ type Trait struct {
CustomStatusFormat string
FullTemplate *util.Template
engine definition.AbstractEngine
}
// EvalContext eval trait template and set result to context
func (trait *Trait) EvalContext(ctx process.Context) error {
return definition.NewTraitAbstractEngine(trait.Name).Params(trait.Params).Complete(ctx, trait.Template)
return trait.engine.Complete(ctx, trait.Template, trait.Params)
}
// EvalStatus eval trait status
func (trait *Trait) EvalStatus(ctx process.Context, cli client.Client, ns string) (string, error) {
return definition.NewTraitAbstractEngine(trait.Name).Status(ctx, cli, ns, trait.CustomStatusFormat)
return trait.engine.Status(ctx, cli, ns, trait.CustomStatusFormat)
}
// EvalHealth eval trait health check
func (trait *Trait) EvalHealth(ctx process.Context, client client.Client, namespace string) (bool, error) {
return definition.NewTraitAbstractEngine(trait.Name).HealthCheck(ctx, client, namespace, trait.HealthCheckPolicy)
return trait.engine.HealthCheck(ctx, client, namespace, trait.HealthCheckPolicy)
}
// Appfile describes application
......@@ -129,13 +132,15 @@ func (af *Appfile) TemplateValidate() error {
type Parser struct {
client client.Client
dm discoverymapper.DiscoveryMapper
pd *definition.PackageDiscover
}
// NewApplicationParser create appfile parser
func NewApplicationParser(cli client.Client, dm discoverymapper.DiscoveryMapper) *Parser {
func NewApplicationParser(cli client.Client, dm discoverymapper.DiscoveryMapper, pd *definition.PackageDiscover) *Parser {
return &Parser{
client: cli,
dm: dm,
pd: pd,
}
}
......@@ -156,27 +161,30 @@ func (p *Parser) GenerateAppFile(ctx context.Context, name string, app *v1alpha2
}
func (p *Parser) parseWorkload(ctx context.Context, comp v1alpha2.ApplicationComponent) (*Workload, error) {
workload := new(Workload)
workload.Traits = []*Trait{}
workload.Name = comp.Name
workload.Type = comp.WorkloadType
// TODO: pass in p.dm
templ, err := util.LoadTemplate(ctx, p.client, workload.Type, types.TypeComponentDefinition)
templ, err := util.LoadTemplate(ctx, p.client, comp.WorkloadType, types.TypeComponentDefinition)
if err != nil && !kerrors.IsNotFound(err) {
return nil, errors.WithMessagef(err, "fetch type of %s", comp.Name)
}
workload.CapabilityCategory = templ.CapabilityCategory
workload.Template = templ.TemplateStr
workload.HealthCheckPolicy = templ.Health
workload.CustomStatusFormat = templ.CustomStatus
workload.DefinitionReference = templ.Reference
workload.Helm = templ.Helm
workload.FullTemplate = templ
settings, err := util.RawExtension2Map(&comp.Settings)
if err != nil {
return nil, errors.WithMessagef(err, "fail to parse settings for %s", comp.Name)
}
workload.Params = settings
workload := &Workload{
Traits: []*Trait{},
Name: comp.Name,
Type: comp.WorkloadType,
CapabilityCategory: templ.CapabilityCategory,
Template: templ.TemplateStr,
HealthCheckPolicy: templ.Health,
CustomStatusFormat: templ.CustomStatus,
DefinitionReference: templ.Reference,
Helm: templ.Helm,
FullTemplate: templ,
Params: settings,
engine: definition.NewWorkloadAbstractEngine(comp.Name, p.pd),
}
for _, traitValue := range comp.Traits {
properties, err := util.RawExtension2Map(&traitValue.Properties)
if err != nil {
......@@ -211,7 +219,6 @@ func (p *Parser) parseTrait(ctx context.Context, name string, properties map[str
if err != nil {
return nil, err
}
return &Trait{
Name: name,
CapabilityCategory: templ.CapabilityCategory,
......@@ -220,6 +227,7 @@ func (p *Parser) parseTrait(ctx context.Context, name string, properties map[str
HealthCheckPolicy: templ.Health,
CustomStatusFormat: templ.CustomStatus,
FullTemplate: templ,
engine: definition.NewTraitAbstractEngine(name, p.pd),
}, nil
}
......@@ -241,7 +249,6 @@ func (p *Parser) GenerateApplicationConfiguration(app *Appfile, ns string) (*v1a
var comp *v1alpha2.Component
var acComp *v1alpha2.ApplicationConfigurationComponent
var err error
switch wl.CapabilityCategory {
case types.HelmCategory:
comp, acComp, err = generateComponentFromHelmModule(p.client, wl, app.Name, app.RevisionName, ns)
......
......@@ -37,6 +37,7 @@ import (
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
oamtypes "github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/dsl/definition"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/util"
)
......@@ -243,7 +244,7 @@ var _ = Describe("Test application parser", func() {
},
}
appfile, err := NewApplicationParser(&tclient, nil).GenerateAppFile(context.TODO(), "test", &o)
appfile, err := NewApplicationParser(&tclient, dm, pd).GenerateAppFile(context.TODO(), "test", &o)
Expect(err).ShouldNot(HaveOccurred())
Expect(equal(expectedExceptApp, appfile)).Should(BeTrue())
......@@ -279,26 +280,28 @@ func equal(af, dest *Appfile) bool {
}
var _ = Describe("Test appFile parser", func() {
// TestApp is test data
var TestApp = &Appfile{
Name: "test",
Workloads: []*Workload{
{
Name: "myweb",
Type: "worker",
Params: map[string]interface{}{
"image": "busybox",
"cmd": []interface{}{"sleep", "1000"},
"config": "myconfig",
},
Scopes: []Scope{
{Name: "test-scope", GVK: schema.GroupVersionKind{
Group: "core.oam.dev",
Version: "v1alpha2",
Kind: "HealthScope",
}},
},
Template: `
It("application without-trait will only create appfile with workload", func() {
// TestApp is test data
var TestApp = &Appfile{
Name: "test",
Workloads: []*Workload{
{
Name: "myweb",
Type: "worker",
Params: map[string]interface{}{
"image": "busybox",
"cmd": []interface{}{"sleep", "1000"},
"config": "myconfig",
},
Scopes: []Scope{
{Name: "test-scope", GVK: schema.GroupVersionKind{
Group: "core.oam.dev",
Version: "v1alpha2",
Kind: "HealthScope",
}},
},
engine: definition.NewWorkloadAbstractEngine("myweb", pd),
Template: `
output: {
apiVersion: "apps/v1"
kind: "Deployment"
......@@ -340,13 +343,14 @@ var _ = Describe("Test appFile parser", func() {
cmd?: [...string]
}`,
Traits: []*Trait{
{
Name: "scaler",
Params: map[string]interface{}{
"replicas": float64(10),
},
Template: `
Traits: []*Trait{
{
Name: "scaler",
Params: map[string]interface{}{
"replicas": float64(10),
},
engine: definition.NewTraitAbstractEngine("scaler", pd),
Template: `
outputs: scaler: {
apiVersion: "core.oam.dev/v1alpha2"
kind: "ManualScalerTrait"
......@@ -359,19 +363,17 @@ var _ = Describe("Test appFile parser", func() {
replicas: *1 | int
}
`,
},
},
},
},
},
}
cm := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{Name: "kubevela-test-myweb-myconfig", Namespace: "default"},
Data: map[string]string{"c1": "v1", "c2": "v2"},
}
It("application without-trait will only create appfile with workload", func() {
}
cm := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{Name: "kubevela-test-myweb-myconfig", Namespace: "default"},
Data: map[string]string{"c1": "v1", "c2": "v2"},
}
Expect(k8sClient.Create(context.Background(), cm.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
ac, components, err := NewApplicationParser(k8sClient, nil).GenerateApplicationConfiguration(TestApp, "default")
ac, components, err := NewApplicationParser(k8sClient, dm, pd).GenerateApplicationConfiguration(TestApp, "default")
Expect(err).To(BeNil())
manuscaler := util.Object2RawExtension(&unstructured.Unstructured{
Object: map[string]interface{}{
......@@ -498,25 +500,29 @@ var _ = Describe("Test appfile parser to parse helm module", func() {
appName = "test-app"
compName = "test-comp"
)
appFile := &Appfile{
Name: appName,
Workloads: []*Workload{
{
Name: compName,
Type: "webapp-chart",
CapabilityCategory: oamtypes.HelmCategory,
Params: map[string]interface{}{
"image": map[string]interface{}{
"tag": "5.1.2",
},
},
Traits: []*Trait{
{
Name: "scaler",
Params: map[string]interface{}{
"replicas": float64(10),
It("Test application containing helm module", func() {
appFile := &Appfile{
Name: appName,
Workloads: []*Workload{
{
Name: compName,
Type: "webapp-chart",
CapabilityCategory: oamtypes.HelmCategory,
Params: map[string]interface{}{
"image": map[string]interface{}{
"tag": "5.1.2",
},
Template: `
},
engine: definition.NewWorkloadAbstractEngine(compName, pd),
Traits: []*Trait{
{
Name: "scaler",
Params: map[string]interface{}{
"replicas": float64(10),
},
engine: definition.NewTraitAbstractEngine("scaler", pd),
Template: `
outputs: scaler: {
apiVersion: "core.oam.dev/v1alpha2"
kind: "ManualScalerTrait"
......@@ -529,32 +535,30 @@ var _ = Describe("Test appfile parser to parse helm module", func() {
replicas: *1 | int
}
`,
},
},
},
Helm: &v1alpha2.Helm{
Release: util.Object2RawExtension(map[string]interface{}{
"chart": map[string]interface{}{
"spec": map[string]interface{}{
"chart": "podinfo",
"version": "5.1.4",
Helm: &v1alpha2.Helm{
Release: util.Object2RawExtension(map[string]interface{}{
"chart": map[string]interface{}{
"spec": map[string]interface{}{
"chart": "podinfo",
"version": "5.1.4",
},
},
},
}),
Repository: util.Object2RawExtension(map[string]interface{}{
"url": "http://oam.dev/catalog/",
}),
},
DefinitionReference: v1alpha2.WorkloadGVK{
APIVersion: "apps/v1",
Kind: "Deployment",
}),
Repository: util.Object2RawExtension(map[string]interface{}{
"url": "http://oam.dev/catalog/",
}),
},
DefinitionReference: v1alpha2.WorkloadGVK{
APIVersion: "apps/v1",
Kind: "Deployment",
},
},
},
},
}
It("Test application containing helm module", func() {
}
By("Generate ApplicationConfiguration and Components")
ac, components, err := NewApplicationParser(k8sClient, dm).GenerateApplicationConfiguration(appFile, "default")
ac, components, err := NewApplicationParser(k8sClient, dm, pd).GenerateApplicationConfiguration(appFile, "default")
Expect(err).To(BeNil())
manuscaler := util.Object2RawExtension(&unstructured.Unstructured{
......
......@@ -17,6 +17,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log/zap"
corev1alpha2 "github.com/oam-dev/kubevela/apis/core.oam.dev"
"github.com/oam-dev/kubevela/pkg/dsl/definition"
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
// +kubebuilder:scaffold:imports
)
......@@ -26,6 +27,7 @@ var scheme *runtime.Scheme
var k8sClient client.Client
var testEnv *envtest.Environment
var dm discoverymapper.DiscoveryMapper
var pd *definition.PackageDiscover
func TestAppFile(t *testing.T) {
RegisterFailHandler(Fail)
......@@ -58,6 +60,9 @@ var _ = BeforeSuite(func(done Done) {
dm, err = discoverymapper.New(cfg)
Expect(err).ToNot(HaveOccurred())
Expect(dm).ToNot(BeNil())
pd, err = definition.NewPackageDiscover(cfg)
Expect(err).ToNot(HaveOccurred())
Expect(pd).ToNot(BeNil())
close(done)
}, 60)
......
......@@ -16,6 +16,11 @@ limitations under the License.
package core_oam_dev
import (
"github.com/oam-dev/kubevela/pkg/dsl/definition"
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
)
// ApplyOnceOnlyMode enumerates ApplyOnceOnly modes.
type ApplyOnceOnlyMode string
......@@ -47,4 +52,9 @@ type Args struct {
// CustomRevisionHookURL is a webhook which will let oam-runtime to call with AC+Component info
// The webhook server will return a customized component revision for oam-runtime
CustomRevisionHookURL string
// DiscoveryMapper used for CRD discovery in controller, a K8s client is contained in it.
DiscoveryMapper discoverymapper.DiscoveryMapper
// PackageDiscover used for CRD discovery in CUE packages, a K8s client is contained in it.
PackageDiscover *definition.PackageDiscover
}
......@@ -18,7 +18,6 @@ package application
import (
"context"
"fmt"
"time"
"github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
......@@ -48,6 +47,7 @@ const RolloutReconcileWaitTime = time.Second * 3
type Reconciler struct {
client.Client
dm discoverymapper.DiscoveryMapper
pd *definition.PackageDiscover
Log logr.Logger
Scheme *runtime.Scheme
applicator apply.Applicator
......@@ -89,7 +89,7 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
applog.Info("parse template")
// parse template
appParser := appfile.NewApplicationParser(r.Client, r.dm)
appParser := appfile.NewApplicationParser(r.Client, r.dm, r.pd)
ctx = oamutil.SetNamespaceInCtx(ctx, app.Namespace)
generatedAppfile, err := appParser.GenerateAppFile(ctx, app.Name, app)
......@@ -175,19 +175,13 @@ func (r *Reconciler) UpdateStatus(ctx context.Context, app *v1alpha2.Application
}
// Setup adds a controller that reconciles AppRollout.
func Setup(mgr ctrl.Manager, _ core.Args, _ logging.Logger) error {
dm, err := discoverymapper.New(mgr.GetConfig())
if err := definition.AddKubeCUEPackagesFromCluster(mgr.GetConfig()); err != nil {
ctrl.Log.Error(err, "use kubernetes cluster openAPI as rendering package")
}
if err != nil {
return fmt.Errorf("create discovery dm fail %w", err)
}
func Setup(mgr ctrl.Manager, args core.Args, _ logging.Logger) error {
reconciler := Reconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("Application"),
Scheme: mgr.GetScheme(),
dm: dm,
dm: args.DiscoveryMapper,
pd: args.PackageDiscover,
applicator: apply.NewAPIApplicator(mgr.GetClient()),
}
return reconciler.SetupWithManager(mgr)
......
......@@ -30,6 +30,7 @@ import (
. "github.com/onsi/gomega"
"github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
"github.com/ghodss/yaml"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/apps/v1"
......@@ -40,7 +41,6 @@ import (
"k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/yaml"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2/applicationcontext"
......@@ -104,6 +104,12 @@ var _ = Describe("Test Application Controller", func() {
Name: "myweb",
WorkloadType: "worker-import",
Settings: runtime.RawExtension{Raw: []byte("{\"cmd\":[\"sleep\",\"1000\"],\"image\":\"busybox\"}")},
Traits: []v1alpha2.ApplicationTrait{
{
Name: "ingress-import",
Properties: runtime.RawExtension{Raw: []byte("{\"http\":{\"/\":80},\"domain\":\"abc.com\"}")},
},
},
},
},
},
......@@ -189,6 +195,8 @@ var _ = Describe("Test Application Controller", func() {
importWd := &v1alpha2.WorkloadDefinition{}
importWdJson, _ := yaml.YAMLToJSON([]byte(wDImportYaml))
importTd := &v1alpha2.TraitDefinition{}
webserverwd := &v1alpha2.ComponentDefinition{}
webserverwdJson, _ := yaml.YAMLToJSON([]byte(webComponentDefYaml))
......@@ -209,6 +217,10 @@ var _ = Describe("Test Application Controller", func() {
Expect(json.Unmarshal(importWdJson, importWd)).Should(BeNil())
Expect(k8sClient.Create(ctx, importWd.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
importTdJson, err := yaml.YAMLToJSON([]byte(tdImportedYaml))
Expect(err).ShouldNot(HaveOccurred())
Expect(json.Unmarshal(importTdJson, importTd)).Should(BeNil())
Expect(k8sClient.Create(ctx, importTd.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
Expect(json.Unmarshal(tDDefJson, td)).Should(BeNil())
Expect(k8sClient.Create(ctx, td.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
......@@ -1236,7 +1248,7 @@ var _ = Describe("Test Application Controller", func() {
}, appContext)).Should(BeNil())
})
It("app-import-pkg will create workload by import kube package", func() {
It("app-import-pkg will create workload by imported kube package", func() {
expDeployment := getExpDeployment("myweb", appImportPkg.Name)
expDeployment.Labels["workload.oam.dev/type"] = "worker-import"
ns := &corev1.Namespace{
......@@ -1266,6 +1278,10 @@ var _ = Describe("Test Application Controller", func() {
Namespace: curApp.Namespace,
Name: curApp.Status.LatestRevision.Name,
}, appRevision)).Should(BeNil())
appConfig, err := applicationcontext.ConvertRawExtention2AppConfig(appRevision.Spec.ApplicationConfiguration)
Expect(err).ShouldNot(HaveOccurred())
Expect(string(appConfig.Spec.Components[0].Traits[0].Trait.Raw)).Should(BeEquivalentTo("{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"labels\":{\"app.oam.dev/component\":\"myweb\",\"app.oam.dev/name\":\"app-import-pkg\",\"trait.oam.dev/resource\":\"service\",\"trait.oam.dev/type\":\"ingress-import\"},\"name\":\"myweb\"},\"spec\":{\"ports\":[{\"port\":80,\"targetPort\":80}],\"selector\":{\"app.oam.dev/component\":\"myweb\"}}}"))
Expect(string(appConfig.Spec.Components[0].Traits[1].Trait.Raw)).Should(BeEquivalentTo("{\"apiVersion\":\"networking.k8s.io/v1beta1\",\"kind\":\"Ingress\",\"metadata\":{\"labels\":{\"app.oam.dev/component\":\"myweb\",\"app.oam.dev/name\":\"app-import-pkg\",\"trait.oam.dev/resource\":\"ingress\",\"trait.oam.dev/type\":\"ingress-import\"},\"name\":\"myweb\"},\"spec\":{\"rules\":[{\"host\":\"abc.com\",\"http\":{\"paths\":[{\"backend\":{\"serviceName\":\"myweb\",\"servicePort\":80},\"path\":\"/\"}]}}]}}"))
By("Check ApplicationContext created")
appContext := &v1alpha2.ApplicationContext{}
......@@ -1402,8 +1418,8 @@ spec:
name: deployments.apps
extension:
template: |
import "kube"
output: kube.#Deployment & {
import "kube/apps/v1"
output: v1.#Deployment & {
metadata: {
annotations: {
if context["config"] != _|_ {
......@@ -1449,6 +1465,69 @@ spec:
}
`
tdImportedYaml = `apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
name: ingress-import
namespace: vela-system
spec:
appliesToWorkloads:
- "*"
schematic:
cue:
template: |
import (
kubev1 "kube/v1"
network "kube/networking.k8s.io/v1beta1"
)
parameter: {
domain: string
http: [string]: int
}
outputs: {
service: kubev1.#Service
ingress: network.#Ingress
}
// trait template can have multiple outputs in one trait
outputs: service: {
metadata:
name: context.name
spec: {
selector:
"app.oam.dev/component": context.name
ports: [
for k, v in parameter.http {
port: v
targetPort: v
},
]
}
}
outputs: ingress: {
metadata:
name: context.name
spec: {
rules: [{
host: parameter.domain
http: {
paths: [
for k, v in parameter.http {
path: k
backend: {
serviceName: context.name
servicePort: v
}
},
]
}
}]
}
}`
webComponentDefYaml = `apiVersion: core.oam.dev/v1alpha2
kind: ComponentDefinition
metadata:
......
......@@ -202,7 +202,7 @@ var _ = Describe("test generate revision ", func() {
It("Test apply success for none rollout case", func() {
By("Apply the application")
appParser := appfile.NewApplicationParser(reconciler.Client, reconciler.dm)
appParser := appfile.NewApplicationParser(reconciler.Client, reconciler.dm, reconciler.pd)
ctx = util.SetNamespaceInCtx(ctx, app.Namespace)
generatedAppfile, err := appParser.GenerateAppFile(ctx, app.Name, &app)
Expect(err).Should(Succeed())
......
......@@ -115,11 +115,14 @@ var _ = BeforeSuite(func(done Done) {
Expect(k8sClient).ToNot(BeNil())
dm, err := discoverymapper.New(cfg)
Expect(err).To(BeNil())
pd, err := definition.NewPackageDiscover(cfg)
Expect(err).To(BeNil())
reconciler = &Reconciler{
Client: k8sClient,
Log: ctrl.Log.WithName("Application-Test"),
Scheme: testScheme,
dm: dm,
pd: pd,
}
// setup the controller manager since we need the component handler to run in the background
ctlManager, err = ctrl.NewManager(cfg, ctrl.Options{
......@@ -145,7 +148,6 @@ var _ = BeforeSuite(func(done Done) {
Expect(err).NotTo(HaveOccurred())
definitonNs := corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "vela-system"}}
Expect(k8sClient.Create(context.Background(), definitonNs.DeepCopy())).Should(BeNil())
Expect(definition.AddKubeCUEPackagesFromCluster(cfg)).Should(BeNil())
// start the controller in the background so that new componentRevisions are created
go func() {
err = ctlManager.Start(stop)
......
......@@ -84,10 +84,6 @@ const (
// Setup adds a controller that reconciles ApplicationConfigurations.
func Setup(mgr ctrl.Manager, args core.Args, l logging.Logger) error {
dm, err := discoverymapper.New(mgr.GetConfig())
if err != nil {
return fmt.Errorf("create discovery dm fail %w", err)
}
name := "oam/" + strings.ToLower(v1alpha2.ApplicationConfigurationGroupKind)
return ctrl.NewControllerManagedBy(mgr).
......@@ -99,7 +95,7 @@ func Setup(mgr ctrl.Manager, args core.Args, l logging.Logger) error {
RevisionLimit: args.RevisionLimit,
CustomRevisionHookURL: args.CustomRevisionHookURL,
}).
Complete(NewReconciler(mgr, dm,
Complete(NewReconciler(mgr, args.DiscoveryMapper,
l.WithValues("controller", name),
WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name))),
WithApplyOnceOnlyMode(args.ApplyMode)))
......
......@@ -2,7 +2,6 @@ package applicationrollout
import (
"context"
"fmt"
"strconv"
"time"
......@@ -220,14 +219,10 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
}
// Setup adds a controller that reconciles AppRollout.
func Setup(mgr ctrl.Manager, _ controller.Args, _ logging.Logger) error {
dm, err := discoverymapper.New(mgr.GetConfig())
if err != nil {
return fmt.Errorf("create discovery dm fail %w", err)
}
func Setup(mgr ctrl.Manager, args controller.Args, _ logging.Logger) error {
reconciler := Reconciler{
Client: mgr.GetClient(),
dm: dm,
dm: args.DiscoveryMapper,
Scheme: mgr.GetScheme(),
}
return reconciler.SetupWithManager(mgr)
......
......@@ -34,6 +34,7 @@ import (
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
controller "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev"
"github.com/oam-dev/kubevela/pkg/controller/utils"
"github.com/oam-dev/kubevela/pkg/dsl/definition"
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
"github.com/oam-dev/kubevela/pkg/oam/util"
)
......@@ -41,7 +42,9 @@ import (
// Reconciler reconciles a ComponentDefinition object
type Reconciler struct {
client.Client
dm discoverymapper.DiscoveryMapper
dm discoverymapper.DiscoveryMapper
// TODO support package discover refresh in definition
pd *definition.PackageDiscover
Scheme *runtime.Scheme
record event.Recorder
}
......@@ -119,15 +122,12 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
}
// Setup adds a controller that reconciles ComponentDefinition.
func Setup(mgr ctrl.Manager, _ controller.Args, _ logging.Logger) error {
dm, err := discoverymapper.New(mgr.GetConfig())
if err != nil {
return err
}
func Setup(mgr ctrl.Manager, args controller.Args, _ logging.Logger) error {
r := Reconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
dm: dm,
dm: args.DiscoveryMapper,
pd: args.PackageDiscover,
}
return r.SetupWithManager(mgr)
}
......@@ -51,15 +51,11 @@ const (
)
// Setup adds a controller that reconciles ContainerizedWorkload.
func Setup(mgr ctrl.Manager, _ controller.Args, _ logging.Logger) error {
dm, err := discoverymapper.New(mgr.GetConfig())
if err != nil {
return err
}
func Setup(mgr ctrl.Manager, args controller.Args, _ logging.Logger) error {
reconciler := Reconciler{
Client: mgr.GetClient(),
DiscoveryClient: *discovery.NewDiscoveryClientForConfigOrDie(mgr.GetConfig()),
dm: dm,
dm: args.DiscoveryMapper,
log: ctrl.Log.WithName("ManualScalarTrait"),
record: event.NewAPIRecorder(mgr.GetEventRecorderFor("ManualScalarTrait")),
Scheme: mgr.GetScheme(),
......
......@@ -34,6 +34,7 @@ import (
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
controller "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev"
"github.com/oam-dev/kubevela/pkg/controller/utils"
"github.com/oam-dev/kubevela/pkg/dsl/definition"
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
"github.com/oam-dev/kubevela/pkg/oam/util"
)
......@@ -41,7 +42,9 @@ import (
// Reconciler reconciles a TraitDefinition object
type Reconciler struct {
client.Client
dm discoverymapper.DiscoveryMapper
dm discoverymapper.DiscoveryMapper
// TODO support package discover refresh in definition
pd *definition.PackageDiscover
Scheme *runtime.Scheme
record event.Recorder
}
......@@ -95,15 +98,12 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
}
// Setup adds a controller that reconciles TraitDefinition.
func Setup(mgr ctrl.Manager, _ controller.Args, _ logging.Logger) error {
dm, err := discoverymapper.New(mgr.GetConfig())
if err != nil {
return err
}
func Setup(mgr ctrl.Manager, args controller.Args, _ logging.Logger) error {
r := Reconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
dm: dm,
dm: args.DiscoveryMapper,
pd: args.PackageDiscover,
}
return r.SetupWithManager(mgr)
}
......@@ -4,6 +4,8 @@ import (
"context"
"fmt"
"strings"
"sync"
"time"
"cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
......@@ -14,43 +16,179 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
)
var velaBuiltinPkgs []*build.Instance
// PackageDiscover defines the inner CUE packages loaded from K8s cluster
type PackageDiscover struct {
velaBuiltinPackages []*build.Instance
pkgKinds map[string][]string
mutex sync.RWMutex
client *rest.RESTClient
}
// NewPackageDiscover will create a PackageDiscover client with the K8s config file.
func NewPackageDiscover(config *rest.Config) (*PackageDiscover, error) {
client, err := getClusterOpenAPIClient(config)
if err != nil {
return nil, err
}
pd := &PackageDiscover{
client: client,
pkgKinds: make(map[string][]string),
}
if err = pd.RefreshKubePackagesFromCluster(); err != nil {
return nil, err
}
return pd, nil
}
// AddVelaInternalPackagesFor will add KubeVela built-in packages into your CUE instance
func AddVelaInternalPackagesFor(bi *build.Instance) {
bi.Imports = append(bi.Imports, velaBuiltinPkgs...)
// ImportBuiltinPackagesFor will add KubeVela built-in packages into your CUE instance
func (pd *PackageDiscover) ImportBuiltinPackagesFor(bi *build.Instance) {
pd.mutex.RLock()
defer pd.mutex.RUnlock()
bi.Imports = append(bi.Imports, pd.velaBuiltinPackages...)
}
// AddKubeCUEPackagesFromCluster use K8s native API and CRD definition as a reference package in template rendering
func AddKubeCUEPackagesFromCluster(config *rest.Config) error {
copyConfig := *config
apiSchema, err := getClusterOpenAPI(&copyConfig)
// 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()
if err != nil {
return err
}
kubePkg := newPackage("kube")
if err := kubePkg.addOpenAPI(apiSchema); err != nil {
return pd.addKubeCUEPackagesFromCluster(string(body))
}
// Exist checks if the GVK exists in the built-in packages
func (pd *PackageDiscover) Exist(gvk metav1.GroupVersionKind) bool {
// package name equals to importPath
importPath := genPackageName(gvk.Group, gvk.Version)
pd.mutex.RLock()
defer pd.mutex.RUnlock()
pkgKinds := pd.pkgKinds[importPath]
for _, k := range pkgKinds {
if k == gvk.Kind {
return true
}
}
return false
}
// mount will mount the new parsed package into PackageDiscover built-in packages
func (pd *PackageDiscover) mount(pkg *pkgInstance, pkgKinds []string) {
pd.mutex.Lock()
defer pd.mutex.Unlock()
for i, p := range pd.velaBuiltinPackages {
if p.ImportPath == pkg.ImportPath {
pd.velaBuiltinPackages[i] = pkg.Instance
return
}
}
pd.pkgKinds[pkg.ImportPath] = pkgKinds
pd.velaBuiltinPackages = append(pd.velaBuiltinPackages, pkg.Instance)
}
func (pd *PackageDiscover) addKubeCUEPackagesFromCluster(apiSchema string) error {
var r cue.Runtime
oaInst, err := r.Compile("-", apiSchema)
if err != nil {
return err
}
kubePkg.mount()
kinds := map[string]metav1.GroupVersionKind{}
pathValue := oaInst.Value().Lookup("paths")
if pathValue.Exists() {
if st, err := pathValue.Struct(); err == nil {
iter := st.Fields()
for iter.Next() {
gvk := iter.Value().Lookup("post",
"x-kubernetes-group-version-kind")
if gvk.Exists() {
if v, err := getGVK(gvk); err == nil {
kinds["#"+v.Kind] = v
}
}
}
}
}
oaFile, err := jsonschema.Extract(oaInst, &jsonschema.Config{
Root: "#/definitions",
Map: openAPIMapping,
})
if err != nil {
return err
}
packages := make(map[string]*pkgInstance)
groupKinds := make(map[string][]string)
for k, v := range kinds {
apiVersion := v.Version
if v.Group != "" {
apiVersion = v.Group + "/" + apiVersion
}
def := fmt.Sprintf(`%s: {
kind: "%s"
apiVersion: "%s",
}`, k, v.Kind, apiVersion)
pkgName := genPackageName(v.Group, v.Version)
pkg, ok := packages[pkgName]
if !ok {
pkg = newPackage(pkgName)
}
mykinds := groupKinds[pkgName]
mykinds = append(mykinds, v.Kind)
if err := pkg.AddFile(k, def); 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 getClusterOpenAPI(config *rest.Config) (string, error) {
config.NegotiatedSerializer = serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{})
restClient, err := rest.UnversionedRESTClientFor(config)
if err != nil {
return "", err
func genPackageName(group, version string) string {
res := []string{"kube"}
if group != "" {
res = append(res, group)
}
// version should never be empty
res = append(res, version)
return strings.Join(res, "/")
}
body, err := restClient.Get().AbsPath("/openapi/v2").Do(context.Background()).Raw()
if err != nil {
return "", err
func setDiscoveryDefaults(config *rest.Config) {
config.APIPath = ""
config.GroupVersion = nil
if config.Timeout == 0 {
config.Timeout = 32 * time.Second
}
if config.Burst == 0 && config.QPS < 100 {
// discovery is expected to be bursty, increase the default burst
// to accommodate looking up resource info for many API groups.
// matches burst set by ConfigFlags#ToDiscoveryClient().
// see https://issue.k8s.io/86149
config.Burst = 100
}
codec := runtime.NoopEncoder{Decoder: clientgoscheme.Codecs.UniversalDecoder()}
config.NegotiatedSerializer = serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{Serializer: codec})
if len(config.UserAgent) == 0 {
config.UserAgent = rest.DefaultKubernetesUserAgent()
}
return string(body), nil
}
func getClusterOpenAPIClient(config *rest.Config) (*rest.RESTClient, error) {
copyConfig := *config
setDiscoveryDefaults(&copyConfig)
return rest.UnversionedRESTClientFor(&copyConfig)
}
func openAPIMapping(pos token.Pos, a []string) ([]ast.Label, error) {
......@@ -110,66 +248,6 @@ func (pkg *pkgInstance) processOpenAPIFile(f *ast.File) {
}
}
func (pkg *pkgInstance) addOpenAPI(apiSchema string) error {
var r cue.Runtime
oaInst, err := r.Compile("-", apiSchema)
if err != nil {
return err
}
kinds := map[string]metav1.GroupVersionKind{}
pathv := oaInst.Value().Lookup("paths")
if pathv.Exists() {
if st, err := pathv.Struct(); err == nil {
iter := st.Fields()
for iter.Next() {
gvk := iter.Value().Lookup("post",
"x-kubernetes-group-version-kind")
if gvk.Exists() {
if v, err := getGVK(gvk); err == nil {
kinds["#"+v.Kind] = v
}
}
}
}
}
oaFile, err := jsonschema.Extract(oaInst, &jsonschema.Config{
Root: "#/definitions",
Map: openAPIMapping,
})
if err != nil {
return err
}
for k, v := range kinds {
apiversion := v.Version
if v.Group != "" {
apiversion = v.Group + "/" + apiversion
}
def := fmt.Sprintf(`%s: {
kind: "%s"
apiVersion: "%s",
}`, k, v.Kind, apiversion)
if err := pkg.AddFile(k, def); err != nil {
return err
}
}
pkg.processOpenAPIFile(oaFile)
return pkg.AddSyntax(oaFile)
}
func (pkg *pkgInstance) mount() {
for i := range velaBuiltinPkgs {
if velaBuiltinPkgs[i].ImportPath == pkg.ImportPath {
velaBuiltinPkgs[i] = pkg.Instance
return
}
}
velaBuiltinPkgs = append(velaBuiltinPkgs, pkg.Instance)
}
func getGVK(v cue.Value) (metav1.GroupVersionKind, error) {
ret := metav1.GroupVersionKind{}
var err error
......
......@@ -17,14 +17,274 @@ limitations under the License.
package definition
import (
"context"
"time"
"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"
"k8s.io/utils/pointer"
"github.com/oam-dev/kubevela/pkg/oam/util"
"github.com/oam-dev/kubevela/pkg/dsl/model"
)
var _ = Describe("Package discovery resources for definition from K8s APIServer", func() {
It("discovery built-in k8s resource", func() {
Expect(AddKubeCUEPackagesFromCluster(cfg)).Should(BeNil())
By("test ingress in kube package")
bi := build.NewContext().NewInstance("", nil)
pd.ImportBuiltinPackagesFor(bi)
err := bi.AddFile("-", `
import (
network "kube/networking.k8s.io/v1beta1"
)
output: network.#Ingress
output: {
apiVersion: "networking.k8s.io/v1beta1"
kind: "Ingress"
metadata: name: "myapp"
spec: {
rules: [{
host: parameter.domain
http: {
paths: [
for k, v in parameter.http {
path: k
backend: {
serviceName: "myname"
servicePort: v
}
},
]
}
}]
}
}
parameter: {
domain: "abc.com"
http: {
"/": 80
}
}`)
Expect(err).ToNot(HaveOccurred())
var r cue.Runtime
inst, err := r.Build(bi)
Expect(err).Should(BeNil())
base, err := model.NewBase(inst.Lookup("output"))
Expect(err).Should(BeNil())
data, err := base.Unstructured()
Expect(err).Should(BeNil())
Expect(cmp.Diff(data, &unstructured.Unstructured{Object: map[string]interface{}{
"kind": "Ingress",
"apiVersion": "networking.k8s.io/v1beta1",
"metadata": map[string]interface{}{"name": "myapp"},
"spec": map[string]interface{}{
"rules": []interface{}{
map[string]interface{}{
"host": "abc.com",
"http": map[string]interface{}{
"paths": []interface{}{
map[string]interface{}{
"path": "/",
"backend": map[string]interface{}{
"serviceName": "myname",
"servicePort": int64(80),
}}}}}}}},
})).Should(BeEquivalentTo(""))
By("test Deployment in kube package")
bi = build.NewContext().NewInstance("", nil)
pd.ImportBuiltinPackagesFor(bi)
bi.AddFile("-", `
import (
apps "kube/apps/v1"
)
output: apps.#Deployment
output: {
metadata: {
"name": parameter.name
}
spec: template: spec: {
containers: [{
name:"haha",
image: parameter.image
}]
}
}
parameter: {
name: "myapp"
image: "nginx"
}`)
inst, err = r.Build(bi)
Expect(err).Should(BeNil())
base, err = model.NewBase(inst.Lookup("output"))
Expect(err).Should(BeNil())
data, err = base.Unstructured()
Expect(err).Should(BeNil())
Expect(cmp.Diff(data, &unstructured.Unstructured{Object: map[string]interface{}{
"kind": "Deployment",
"apiVersion": "apps/v1",
"metadata": map[string]interface{}{"name": "myapp"},
"spec": map[string]interface{}{
"selector": map[string]interface{}{},
"template": map[string]interface{}{
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "haha",
"image": "nginx"}}}}}},
})).Should(BeEquivalentTo(""))
By("test Secret in kube package")
bi = build.NewContext().NewInstance("", nil)
pd.ImportBuiltinPackagesFor(bi)
bi.AddFile("-", `
import ("kube/v1")
output: v1.#Secret
output: {
metadata: {
"name": parameter.name
}
type:"kubevela"
}
parameter: {
name: "myapp"
}`)
inst, err = r.Build(bi)
Expect(err).Should(BeNil())
base, err = model.NewBase(inst.Lookup("output"))
Expect(err).Should(BeNil())
data, err = base.Unstructured()
Expect(err).Should(BeNil())
Expect(cmp.Diff(data, &unstructured.Unstructured{Object: map[string]interface{}{
"kind": "Secret",
"apiVersion": "v1",
"metadata": map[string]interface{}{"name": "myapp"},
"type": "kubevela"}})).Should(BeEquivalentTo(""))
By("test Service in kube package")
bi = build.NewContext().NewInstance("", nil)
pd.ImportBuiltinPackagesFor(bi)
bi.AddFile("-", `
import ("kube/v1")
output: v1.#Service
output: {
metadata: {
"name": parameter.name
}
spec: type: "ClusterIP",
}
parameter: {
name: "myapp"
}`)
inst, err = r.Build(bi)
Expect(err).Should(BeNil())
base, err = model.NewBase(inst.Lookup("output"))
Expect(err).Should(BeNil())
data, err = base.Unstructured()
Expect(err).Should(BeNil())
Expect(cmp.Diff(data, &unstructured.Unstructured{Object: map[string]interface{}{
"kind": "Service",
"apiVersion": "v1",
"metadata": map[string]interface{}{"name": "myapp"},
"spec": map[string]interface{}{
"type": "ClusterIP"}},
})).Should(BeEquivalentTo(""))
Expect(pd.Exist(metav1.GroupVersionKind{
Group: "",
Version: "v1",
Kind: "Service",
})).Should(Equal(true))
By("Check newly added CRD refreshed and could be used in CUE package")
crd1 := crdv1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "foo.example.com",
},
Spec: crdv1.CustomResourceDefinitionSpec{
Group: "example.com",
Names: crdv1.CustomResourceDefinitionNames{
Kind: "Foo",
ListKind: "FooList",
Plural: "foo",
Singular: "foo",
},
Versions: []crdv1.CustomResourceDefinitionVersion{{
Name: "v1",
Served: true,
Storage: true,
Subresources: &crdv1.CustomResourceSubresources{Status: &crdv1.CustomResourceSubresourceStatus{}},
Schema: &crdv1.CustomResourceValidation{
OpenAPIV3Schema: &crdv1.JSONSchemaProps{
Type: "object",
Properties: map[string]crdv1.JSONSchemaProps{
"spec": {
Type: "object",
XPreserveUnknownFields: pointer.BoolPtr(true),
Properties: map[string]crdv1.JSONSchemaProps{
"key": {Type: "string"},
}},
"status": {
Type: "object",
XPreserveUnknownFields: pointer.BoolPtr(true),
Properties: map[string]crdv1.JSONSchemaProps{
"key": {Type: "string"},
"app-hash": {Type: "string"},
}}}}}},
},
Scope: crdv1.NamespaceScoped,
},
}
Expect(k8sClient.Create(context.Background(), &crd1)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
time.Sleep(2 * time.Second)
Expect(pd.Exist(metav1.GroupVersionKind{
Group: "example.com",
Version: "v1",
Kind: "Foo",
})).Should(Equal(false))
Expect(pd.RefreshKubePackagesFromCluster()).ShouldNot(HaveOccurred())
By("test new added CRD in kube package")
bi = build.NewContext().NewInstance("", nil)
pd.ImportBuiltinPackagesFor(bi)
bi.AddFile("-", `
import ("kube/example.com/v1")
output: v1.#Foo
output: {
spec: key: "test1"
status: key: "test2"
}
`)
inst, err = r.Build(bi)
Expect(err).Should(BeNil())
base, err = model.NewBase(inst.Lookup("output"))
Expect(err).Should(BeNil())
data, err = base.Unstructured()
Expect(err).Should(BeNil())
Expect(cmp.Diff(data, &unstructured.Unstructured{Object: map[string]interface{}{
"kind": "Foo",
"apiVersion": "example.com/v1",
"spec": map[string]interface{}{
"key": "test1"},
"status": map[string]interface{}{
"key": "test2"}},
})).Should(BeEquivalentTo(""))
})
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment