mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-14 18:10:21 +00:00
Compatible with Application containing WorkloadDefinition type helm moudle (#1261)
* add workloaddef ref to helm. * fix package_suit_test * show git diff result.
This commit is contained in:
1
Makefile
1
Makefile
@@ -111,6 +111,7 @@ reviewable: manifests fmt vet lint staticcheck
|
||||
|
||||
# Execute auto-gen code commands and ensure branch is clean.
|
||||
check-diff: reviewable
|
||||
git --no-pager diff
|
||||
git diff --quiet || ($(ERR) please run 'make reviewable' to include all changes && false)
|
||||
@$(OK) branch is clean
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ type ComponentDefinitionStatus struct {
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// ComponentDefinition is the Schema for the componentdefinitions API
|
||||
// +kubebuilder:resource:scope=Namespaced,categories={crossplane,oam}
|
||||
// +kubebuilder:resource:scope=Namespaced,categories={oam}
|
||||
// +kubebuilder:storageversion
|
||||
// +kubebuilder:subresource:status
|
||||
type ComponentDefinition struct {
|
||||
|
||||
@@ -163,8 +163,7 @@ func (p *Parser) GenerateAppFile(ctx context.Context, name string, app *v1alpha2
|
||||
|
||||
func (p *Parser) parseWorkload(ctx context.Context, comp v1alpha2.ApplicationComponent) (*Workload, error) {
|
||||
|
||||
// TODO: pass in p.dm
|
||||
templ, err := util.LoadTemplate(ctx, p.client, comp.WorkloadType, types.TypeComponentDefinition)
|
||||
templ, err := util.LoadTemplate(ctx, p.dm, p.client, comp.WorkloadType, types.TypeComponentDefinition)
|
||||
if err != nil && !kerrors.IsNotFound(err) {
|
||||
return nil, errors.WithMessagef(err, "fetch type of %s", comp.Name)
|
||||
}
|
||||
@@ -212,8 +211,7 @@ func (p *Parser) parseWorkload(ctx context.Context, comp v1alpha2.ApplicationCom
|
||||
}
|
||||
|
||||
func (p *Parser) parseTrait(ctx context.Context, name string, properties map[string]interface{}) (*Trait, error) {
|
||||
// TODO: pass in p.dm
|
||||
templ, err := util.LoadTemplate(ctx, p.client, name, types.TypeTrait)
|
||||
templ, err := util.LoadTemplate(ctx, p.dm, p.client, name, types.TypeTrait)
|
||||
if kerrors.IsNotFound(err) {
|
||||
return nil, errors.Errorf("trait definition of %s not found", name)
|
||||
}
|
||||
|
||||
@@ -261,17 +261,18 @@ parameter: {
|
||||
return err
|
||||
}, time.Second*2, time.Millisecond*300).Should(BeNil())
|
||||
|
||||
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("-", `
|
||||
|
||||
Eventually(func() error {
|
||||
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
|
||||
@@ -280,7 +281,10 @@ output: {
|
||||
status: key: "test2"
|
||||
}
|
||||
`)
|
||||
inst, err = r.Build(bi)
|
||||
inst, err = r.Build(bi)
|
||||
return err
|
||||
}, time.Second*5, time.Millisecond*300).Should(BeNil())
|
||||
|
||||
Expect(err).Should(BeNil())
|
||||
base, err = model.NewBase(inst.Lookup("output"))
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
@@ -168,3 +168,27 @@ func SchemeWith(o ...runtime.Object) *runtime.Scheme {
|
||||
s.AddKnownTypes(GV, o...)
|
||||
return s
|
||||
}
|
||||
|
||||
// NotFoundErr describes NotFound Resource error
|
||||
type NotFoundErr struct {
|
||||
NotFoundStatus metav1.Status
|
||||
}
|
||||
|
||||
// NewMockNotFoundErr return a mock NotFoundErr
|
||||
func NewMockNotFoundErr() NotFoundErr {
|
||||
return NotFoundErr{
|
||||
NotFoundStatus: metav1.Status{
|
||||
Reason: metav1.StatusReasonNotFound,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Status returns the Status Reason
|
||||
func (mock NotFoundErr) Status() metav1.Status {
|
||||
return mock.NotFoundStatus
|
||||
}
|
||||
|
||||
// Error return error info
|
||||
func (mock NotFoundErr) Error() string {
|
||||
return "Not Found Resource"
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ func GetScopeGVK(ctx context.Context, cli client.Reader, dm discoverymapper.Disc
|
||||
}
|
||||
|
||||
// LoadTemplate Get template according to key
|
||||
func LoadTemplate(ctx context.Context, cli client.Reader, key string, kd types.CapType) (*Template, error) {
|
||||
func LoadTemplate(ctx context.Context, dm discoverymapper.DiscoveryMapper, cli client.Reader, key string, kd types.CapType) (*Template, error) {
|
||||
// Application Controller only load template from ComponentDefinition and TraitDefinition
|
||||
switch kd {
|
||||
case types.TypeComponentDefinition:
|
||||
@@ -70,9 +70,14 @@ func LoadTemplate(ctx context.Context, cli client.Reader, key string, kd types.C
|
||||
tmpl.CapabilityCategory = types.TerraformCategory
|
||||
}
|
||||
tmpl.WorkloadDefinition = wd
|
||||
// GetGVKFromDefinition
|
||||
// TODO: need to pass in a discoverMapper in order to get the tmpl reference
|
||||
// from the workloadDefinition reference
|
||||
gvk, err := GetGVKFromDefinition(dm, wd.Spec.Reference)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessagef(err, "Get GVK from workload definition [%s]", key)
|
||||
}
|
||||
tmpl.Reference = common.WorkloadGVK{
|
||||
APIVersion: gvk.GroupVersion().String(),
|
||||
Kind: gvk.Kind,
|
||||
}
|
||||
return tmpl, nil
|
||||
}
|
||||
return nil, errors.WithMessagef(err, "LoadTemplate from ComponentDefinition [%s] ", key)
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/mock"
|
||||
)
|
||||
|
||||
func TestLoadComponentTemplate(t *testing.T) {
|
||||
@@ -91,8 +92,114 @@ spec:
|
||||
return nil
|
||||
},
|
||||
}
|
||||
tdm := mock.NewMockDiscoveryMapper()
|
||||
tdm.MockKindsFor = mock.NewMockKindsFor("Deployment", "v1")
|
||||
temp, err := LoadTemplate(context.TODO(), tdm, &tclient, "worker", types.TypeComponentDefinition)
|
||||
|
||||
temp, err := LoadTemplate(context.TODO(), &tclient, "worker", types.TypeComponentDefinition)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
var r cue.Runtime
|
||||
inst, err := r.Compile("-", temp.TemplateStr)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
instDest, err := r.Compile("-", cueTemplate)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
s1, _ := inst.Value().String()
|
||||
s2, _ := instDest.Value().String()
|
||||
if s1 != s2 {
|
||||
t.Errorf("parsered template is not correct")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadWorkloadTemplate(t *testing.T) {
|
||||
cueTemplate := `
|
||||
context: {
|
||||
name: "test"
|
||||
}
|
||||
output: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
spec: {
|
||||
selector: matchLabels: {
|
||||
"app.oam.dev/component": context.name
|
||||
}
|
||||
|
||||
template: {
|
||||
metadata: labels: {
|
||||
"app.oam.dev/component": context.name
|
||||
}
|
||||
|
||||
spec: {
|
||||
containers: [{
|
||||
name: context.name
|
||||
image: parameter.image
|
||||
|
||||
if parameter["cmd"] != _|_ {
|
||||
command: parameter.cmd
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
selector:
|
||||
matchLabels:
|
||||
"app.oam.dev/component": context.name
|
||||
}
|
||||
}
|
||||
|
||||
parameter: {
|
||||
// +usage=Which image would you like to use for your service
|
||||
// +short=i
|
||||
image: string
|
||||
|
||||
cmd?: [...string]
|
||||
}
|
||||
`
|
||||
|
||||
var workloadDefintion = `
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: worker
|
||||
namespace: default
|
||||
annotations:
|
||||
definition.oam.dev/description: "Long-running scalable backend worker without network endpoint"
|
||||
spec:
|
||||
workload:
|
||||
definition:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
extension:
|
||||
template: |
|
||||
` + cueTemplate
|
||||
|
||||
// Create mock client
|
||||
tclient := test.MockClient{
|
||||
MockGet: func(ctx context.Context, key ktypes.NamespacedName, obj runtime.Object) error {
|
||||
switch o := obj.(type) {
|
||||
case *v1alpha2.WorkloadDefinition:
|
||||
cd, err := UnMarshalStringToWorkloadDefinition(workloadDefintion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*o = *cd
|
||||
case *v1alpha2.ComponentDefinition:
|
||||
err := mock.NewMockNotFoundErr()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
tdm := mock.NewMockDiscoveryMapper()
|
||||
tdm.MockKindsFor = mock.NewMockKindsFor("Deployment", "v1")
|
||||
temp, err := LoadTemplate(context.TODO(), tdm, &tclient, "worker", types.TypeComponentDefinition)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -210,7 +317,9 @@ spec:
|
||||
},
|
||||
}
|
||||
|
||||
temp, err := LoadTemplate(context.TODO(), &tclient, "ingress", types.TypeTrait)
|
||||
tdm := mock.NewMockDiscoveryMapper()
|
||||
tdm.MockKindsFor = mock.NewMockKindsFor("Deployment", "v1")
|
||||
temp, err := LoadTemplate(context.TODO(), tdm, &tclient, "ingress", types.TypeTrait)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
||||
@@ -145,6 +145,19 @@ func UnMarshalStringToComponentDefinition(s string) (*v1alpha2.ComponentDefiniti
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
// UnMarshalStringToWorkloadDefinition parse a string to a workloadDefinition object
|
||||
func UnMarshalStringToWorkloadDefinition(s string) (*v1alpha2.WorkloadDefinition, error) {
|
||||
obj := &v1alpha2.WorkloadDefinition{}
|
||||
_body, err := yaml.YAMLToJSON([]byte(s))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.Unmarshal(_body, obj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
// UnMarshalStringToTraitDefinition parse a string to a traitDefinition object
|
||||
func UnMarshalStringToTraitDefinition(s string) (*v1alpha2.TraitDefinition, error) {
|
||||
obj := &v1alpha2.TraitDefinition{}
|
||||
|
||||
@@ -27,6 +27,7 @@ var _ = Describe("Test application containing helm module", func() {
|
||||
appName = "test-app"
|
||||
compName = "test-comp"
|
||||
cdName = "webapp-chart"
|
||||
wdName = "webapp-chart-wd"
|
||||
tdName = "virtualgroup"
|
||||
)
|
||||
var app v1alpha2.Application
|
||||
@@ -73,7 +74,6 @@ var _ = Describe("Test application containing helm module", func() {
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
Expect(k8sClient.Create(ctx, &cd)).Should(Succeed())
|
||||
|
||||
By("Install a patch trait used to test CUE module")
|
||||
@@ -289,6 +289,68 @@ var _ = Describe("Test application containing helm module", func() {
|
||||
}, 60*time.Second, 10*time.Second).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("Test deploy an application containing helm module defined by workloadDefinition", func() {
|
||||
|
||||
workloaddef := v1alpha2.WorkloadDefinition{}
|
||||
workloaddef.SetName(wdName)
|
||||
workloaddef.SetNamespace(namespace)
|
||||
workloaddef.Spec.Reference = common.DefinitionReference{Name: "deployments.apps", Version: "v1"}
|
||||
workloaddef.Spec.Schematic = &common.Schematic{
|
||||
HELM: &common.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/",
|
||||
}),
|
||||
},
|
||||
}
|
||||
By("register workloadDefinition")
|
||||
Expect(k8sClient.Create(ctx, &workloaddef)).Should(Succeed())
|
||||
|
||||
appTestName := "test-app-refer-to-workloaddef"
|
||||
appTest := v1alpha2.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appTestName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1alpha2.ApplicationSpec{
|
||||
Components: []v1alpha2.ApplicationComponent{
|
||||
{
|
||||
Name: compName,
|
||||
WorkloadType: wdName,
|
||||
Settings: util.Object2RawExtension(map[string]interface{}{
|
||||
"image": map[string]interface{}{
|
||||
"tag": "5.1.2",
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
By("Create application")
|
||||
Expect(k8sClient.Create(ctx, &appTest)).Should(Succeed())
|
||||
|
||||
ac := &v1alpha2.ApplicationContext{}
|
||||
acName := appTestName
|
||||
By("Verify the AppConfig is created successfully")
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: acName, Namespace: namespace}, ac)
|
||||
}, 30*time.Second, time.Second).Should(Succeed())
|
||||
|
||||
By("Verify the workload(deployment) is created successfully by Helm")
|
||||
deploy := &appsv1.Deployment{}
|
||||
deployName := fmt.Sprintf("%s-%s-podinfo", appTestName, compName)
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy)
|
||||
}, 240*time.Second, 5*time.Second).Should(Succeed())
|
||||
})
|
||||
|
||||
It("Test store JSON schema of Helm Chart in ConfigMap", func() {
|
||||
By("Get the ConfigMap")
|
||||
cmName := fmt.Sprintf("schema-%s", cdName)
|
||||
|
||||
Reference in New Issue
Block a user