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:
yangsoon
2021-03-24 21:22:29 +08:00
committed by GitHub
parent 9366c6e0b4
commit fc55b65aad
9 changed files with 235 additions and 19 deletions

View File

@@ -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

View File

@@ -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 {

View File

@@ -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)
}

View File

@@ -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())

View File

@@ -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"
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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{}

View File

@@ -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)