mirror of
https://github.com/kubevela/kubevela.git
synced 2026-05-03 16:07:12 +00:00
add staticcheck CI action add staticcheck in Makefile Signed-off-by: roywang <seiwy2010@gmail.com>
468 lines
11 KiB
Go
468 lines
11 KiB
Go
package api
|
||
|
||
import (
|
||
"os"
|
||
"testing"
|
||
|
||
"github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
|
||
"github.com/ghodss/yaml"
|
||
"github.com/stretchr/testify/assert"
|
||
v12 "k8s.io/api/core/v1"
|
||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||
"k8s.io/apimachinery/pkg/runtime"
|
||
|
||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
||
"github.com/oam-dev/kubevela/apis/types"
|
||
"github.com/oam-dev/kubevela/pkg/appfile/config"
|
||
"github.com/oam-dev/kubevela/pkg/appfile/template"
|
||
cmdutil "github.com/oam-dev/kubevela/pkg/commands/util"
|
||
"github.com/oam-dev/kubevela/pkg/oam"
|
||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||
)
|
||
|
||
func TestBuildOAMApplication2(t *testing.T) {
|
||
expectNs := "test-ns"
|
||
|
||
tm := template.NewFakeTemplateManager()
|
||
tm.Templates = map[string]*template.Template{
|
||
"containerWorkload": {
|
||
Captype: types.TypeWorkload,
|
||
Raw: `{parameters : {image: string} }`,
|
||
},
|
||
"scaler": {
|
||
Captype: types.TypeTrait,
|
||
Raw: `{parameters : {relicas: int} }`,
|
||
},
|
||
}
|
||
|
||
testCases := []struct {
|
||
appFile *AppFile
|
||
expectApp *v1alpha2.Application
|
||
}{
|
||
{
|
||
appFile: &AppFile{
|
||
Name: "test",
|
||
Services: map[string]Service{
|
||
"webapp": map[string]interface{}{
|
||
"type": "containerWorkload",
|
||
"image": "busybox",
|
||
},
|
||
},
|
||
},
|
||
expectApp: &v1alpha2.Application{
|
||
TypeMeta: metav1.TypeMeta{
|
||
Kind: "Application",
|
||
APIVersion: "core.oam.dev/v1alpha2",
|
||
}, ObjectMeta: metav1.ObjectMeta{
|
||
Name: "test",
|
||
},
|
||
Spec: v1alpha2.ApplicationSpec{
|
||
Components: []v1alpha2.ApplicationComponent{
|
||
{
|
||
Name: "webapp",
|
||
WorkloadType: "containerWorkload",
|
||
Settings: runtime.RawExtension{
|
||
Raw: []byte("{\"image\":\"busybox\"}"),
|
||
},
|
||
Scopes: map[string]string{"healthscopes.core.oam.dev": "test-default-health"},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
{
|
||
appFile: &AppFile{
|
||
Name: "test",
|
||
Services: map[string]Service{
|
||
"webapp": map[string]interface{}{
|
||
"type": "containerWorkload",
|
||
"image": "busybox",
|
||
"scaler": map[string]interface{}{
|
||
"replicas": 10,
|
||
},
|
||
},
|
||
},
|
||
},
|
||
expectApp: &v1alpha2.Application{
|
||
TypeMeta: metav1.TypeMeta{
|
||
Kind: "Application",
|
||
APIVersion: "core.oam.dev/v1alpha2",
|
||
}, ObjectMeta: metav1.ObjectMeta{
|
||
Name: "test",
|
||
},
|
||
Spec: v1alpha2.ApplicationSpec{
|
||
Components: []v1alpha2.ApplicationComponent{
|
||
{
|
||
Name: "webapp",
|
||
WorkloadType: "containerWorkload",
|
||
Settings: runtime.RawExtension{
|
||
Raw: []byte("{\"image\":\"busybox\"}"),
|
||
},
|
||
Scopes: map[string]string{"healthscopes.core.oam.dev": "test-default-health"},
|
||
Traits: []v1alpha2.ApplicationTrait{
|
||
{
|
||
Name: "scaler",
|
||
Properties: runtime.RawExtension{
|
||
Raw: []byte("{\"replicas\":10}"),
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
}
|
||
|
||
for _, tcase := range testCases {
|
||
tcase.expectApp.Namespace = expectNs
|
||
o, _, err := tcase.appFile.BuildOAMApplication(&types.EnvMeta{Namespace: expectNs}, cmdutil.IOStreams{
|
||
In: os.Stdin,
|
||
Out: os.Stdout,
|
||
}, tm, false)
|
||
assert.Equal(t, nil, err)
|
||
assert.Equal(t, tcase.expectApp, o)
|
||
}
|
||
}
|
||
|
||
func TestBuildOAMApplication(t *testing.T) {
|
||
yamlOneService := `name: myapp
|
||
services:
|
||
express-server:
|
||
image: oamdev/testapp:v1
|
||
cmd: ["node", "server.js"]
|
||
route:
|
||
domain: example.com
|
||
http:
|
||
"/": 8080
|
||
`
|
||
yamlTwoServices := yamlOneService + `
|
||
mongodb:
|
||
type: backend
|
||
image: bitnami/mongodb:3.6.20
|
||
cmd: ["mongodb"]
|
||
`
|
||
yamlNoImage := `name: myapp
|
||
services:
|
||
bad-server:
|
||
build:
|
||
docker:
|
||
file: Dockerfile
|
||
cmd: ["node", "server.js"]
|
||
`
|
||
|
||
yamlWithConfig := `name: myapp
|
||
services:
|
||
express-server:
|
||
type: withconfig
|
||
image: oamdev/testapp:v1
|
||
cmd: ["node", "server.js"]
|
||
route:
|
||
domain: example.com
|
||
http:
|
||
"/": 8080
|
||
config: test
|
||
`
|
||
|
||
templateWebservice := `parameter: #webservice
|
||
#webservice: {
|
||
cmd: [...string]
|
||
image: string
|
||
}
|
||
|
||
output: {
|
||
apiVersion: "test.oam.dev/v1"
|
||
kind: "WebService"
|
||
metadata: {
|
||
name: context.name
|
||
}
|
||
spec: {
|
||
image: parameter.image
|
||
command: parameter.cmd
|
||
}
|
||
}
|
||
`
|
||
templateBackend := `parameter: #backend
|
||
#backend: {
|
||
cmd: [...string]
|
||
image: string
|
||
}
|
||
|
||
output: {
|
||
apiVersion: "test.oam.dev/v1"
|
||
kind: "Worker"
|
||
metadata: {
|
||
name: context.name
|
||
}
|
||
spec: {
|
||
image: parameter.image
|
||
command: parameter.cmd
|
||
}
|
||
}`
|
||
templateWithConfig := `parameter: #withconfig
|
||
#withconfig: {
|
||
cmd: [...string]
|
||
image: string
|
||
}
|
||
|
||
output: {
|
||
apiVersion: "test.oam.dev/v1"
|
||
kind: "WebService"
|
||
metadata: {
|
||
name: context.name
|
||
}
|
||
spec: {
|
||
image: parameter.image
|
||
command: parameter.cmd
|
||
env: context.config
|
||
}
|
||
}
|
||
`
|
||
templateRoute := `parameter: #route
|
||
#route: {
|
||
domain: string
|
||
http: [string]: int
|
||
}
|
||
|
||
// trait template can have multiple outputs and they are all traits
|
||
outputs: service: {
|
||
apiVersion: "v1"
|
||
kind: "Service"
|
||
metadata:
|
||
name: context.name
|
||
spec: {
|
||
selector:
|
||
app: context.name
|
||
ports: [
|
||
for k, v in parameter.http {
|
||
port: v
|
||
targetPort: v
|
||
}
|
||
]
|
||
}
|
||
}
|
||
|
||
outputs: ingress: {
|
||
apiVersion: "networking.k8s.io/v1beta1"
|
||
kind: "Ingress"
|
||
spec: {
|
||
rules: [{
|
||
host: parameter.domain
|
||
http: {
|
||
paths: [
|
||
for k, v in parameter.http {
|
||
path: k
|
||
backend: {
|
||
serviceName: context.name
|
||
servicePort: v
|
||
}
|
||
}
|
||
]
|
||
}
|
||
}]
|
||
}
|
||
}
|
||
`
|
||
ac1 := &v1alpha2.Application{
|
||
TypeMeta: metav1.TypeMeta{
|
||
Kind: "Application",
|
||
APIVersion: "core.oam.dev/v1alpha2",
|
||
},
|
||
ObjectMeta: metav1.ObjectMeta{
|
||
Name: "myapp",
|
||
Namespace: "default",
|
||
},
|
||
Spec: v1alpha2.ApplicationSpec{
|
||
Components: []v1alpha2.ApplicationComponent{{
|
||
WorkloadType: "webservice",
|
||
Name: "express-server",
|
||
Scopes: map[string]string{"healthscopes.core.oam.dev": "myapp-default-health"},
|
||
Settings: runtime.RawExtension{
|
||
Raw: []byte(`{"image": "oamdev/testapp:v1", "cmd": ["node", "server.js"]}`),
|
||
},
|
||
Traits: []v1alpha2.ApplicationTrait{{
|
||
Name: "route",
|
||
Properties: runtime.RawExtension{
|
||
Raw: []byte(`{"domain": "example.com", "http":{"/": 8080}}`),
|
||
},
|
||
},
|
||
},
|
||
}},
|
||
},
|
||
}
|
||
ac2 := ac1.DeepCopy()
|
||
ac2.Spec.Components = append(ac2.Spec.Components, v1alpha2.ApplicationComponent{
|
||
Name: "mongodb",
|
||
WorkloadType: "backend",
|
||
Settings: runtime.RawExtension{
|
||
Raw: []byte(`{"image":"bitnami/mongodb:3.6.20","cmd": ["mongodb"]}`),
|
||
},
|
||
Traits: []v1alpha2.ApplicationTrait{},
|
||
Scopes: map[string]string{"healthscopes.core.oam.dev": "myapp-default-health"},
|
||
})
|
||
|
||
ac3 := ac1.DeepCopy()
|
||
ac3.Spec.Components[0].WorkloadType = "withconfig"
|
||
|
||
// TODO application 那边补测试:
|
||
// 2. 1对多的情况,多对1 的情况
|
||
|
||
fakeConfigData2 := []map[string]string{{
|
||
"name": "test",
|
||
"value": "test-value",
|
||
}}
|
||
|
||
ac3cm := &v12.ConfigMap{
|
||
Data: map[string]string{
|
||
"test": "test-value",
|
||
},
|
||
}
|
||
ac3cm.SetName("kubevela-myapp-express-server-test")
|
||
|
||
health := &v1alpha2.HealthScope{
|
||
TypeMeta: metav1.TypeMeta{
|
||
APIVersion: v1alpha2.HealthScopeGroupVersionKind.GroupVersion().String(),
|
||
Kind: v1alpha2.HealthScopeKind,
|
||
},
|
||
}
|
||
health.Name = FormatDefaultHealthScopeName("myapp")
|
||
health.Namespace = "default"
|
||
health.Spec.WorkloadReferences = make([]v1alpha1.TypedReference, 0)
|
||
type args struct {
|
||
appfileData string
|
||
workloadTemplates map[string]string
|
||
traitTemplates map[string]string
|
||
}
|
||
type want struct {
|
||
objs []oam.Object
|
||
app *v1alpha2.Application
|
||
err error
|
||
}
|
||
cases := map[string]struct {
|
||
args args
|
||
want want
|
||
}{
|
||
"one service should generate one component and one appconfig": {
|
||
args: args{
|
||
appfileData: yamlOneService,
|
||
workloadTemplates: map[string]string{
|
||
"webservice": templateWebservice,
|
||
},
|
||
traitTemplates: map[string]string{
|
||
"route": templateRoute,
|
||
},
|
||
},
|
||
want: want{
|
||
app: ac1,
|
||
objs: []oam.Object{health},
|
||
},
|
||
},
|
||
"two services should generate two components and one appconfig": {
|
||
args: args{
|
||
appfileData: yamlTwoServices,
|
||
workloadTemplates: map[string]string{
|
||
"webservice": templateWebservice,
|
||
"backend": templateBackend,
|
||
},
|
||
traitTemplates: map[string]string{
|
||
"route": templateRoute,
|
||
},
|
||
},
|
||
want: want{
|
||
app: ac2,
|
||
objs: []oam.Object{health},
|
||
},
|
||
},
|
||
"no image should fail": {
|
||
args: args{
|
||
appfileData: yamlNoImage,
|
||
},
|
||
want: want{
|
||
err: ErrImageNotDefined,
|
||
},
|
||
},
|
||
"config data should be set, add return configmap objects": {
|
||
args: args{
|
||
appfileData: yamlWithConfig,
|
||
workloadTemplates: map[string]string{
|
||
"withconfig": templateWithConfig,
|
||
},
|
||
traitTemplates: map[string]string{
|
||
"route": templateRoute,
|
||
},
|
||
},
|
||
want: want{
|
||
app: ac3,
|
||
objs: []oam.Object{ac3cm, health},
|
||
},
|
||
},
|
||
}
|
||
|
||
io := cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr}
|
||
for caseName, c := range cases {
|
||
t.Run(caseName, func(t *testing.T) {
|
||
|
||
app := NewAppFile()
|
||
app.configGetter = &config.Fake{
|
||
Data: fakeConfigData2,
|
||
}
|
||
err := yaml.Unmarshal([]byte(c.args.appfileData), app)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
tm := template.NewFakeTemplateManager()
|
||
for k, v := range c.args.traitTemplates {
|
||
tm.Templates[k] = &template.Template{
|
||
Captype: types.TypeTrait,
|
||
Raw: v,
|
||
}
|
||
}
|
||
for k, v := range c.args.workloadTemplates {
|
||
tm.Templates[k] = &template.Template{
|
||
Captype: types.TypeWorkload,
|
||
Raw: v,
|
||
}
|
||
}
|
||
|
||
application, objects, err := app.BuildOAMApplication(&types.EnvMeta{Namespace: "default"}, io, tm, false)
|
||
if c.want.err != nil {
|
||
assert.Equal(t, c.want.err, err)
|
||
return
|
||
}
|
||
assert.Equal(t, c.want.app.ObjectMeta, application.ObjectMeta)
|
||
for _, comp := range application.Spec.Components {
|
||
var found bool
|
||
for idx, expcomp := range c.want.app.Spec.Components {
|
||
if comp.Name != expcomp.Name {
|
||
continue
|
||
}
|
||
found = true
|
||
assert.Equal(t, comp.WorkloadType, c.want.app.Spec.Components[idx].WorkloadType)
|
||
assert.Equal(t, comp.Name, c.want.app.Spec.Components[idx].Name)
|
||
assert.Equal(t, comp.Scopes, c.want.app.Spec.Components[idx].Scopes)
|
||
|
||
got, err := util.RawExtension2Map(&comp.Settings)
|
||
assert.NoError(t, err)
|
||
exp, err := util.RawExtension2Map(&c.want.app.Spec.Components[idx].Settings)
|
||
assert.NoError(t, err)
|
||
assert.Equal(t, exp, got)
|
||
for tidx, tr := range comp.Traits {
|
||
assert.Equal(t, tr.Name, c.want.app.Spec.Components[idx].Traits[tidx].Name)
|
||
|
||
got, err := util.RawExtension2Map(&tr.Properties)
|
||
assert.NoError(t, err)
|
||
exp, err := util.RawExtension2Map(&c.want.app.Spec.Components[idx].Traits[tidx].Properties)
|
||
assert.NoError(t, err)
|
||
assert.Equal(t, exp, got)
|
||
}
|
||
}
|
||
assert.Equal(t, true, found, "no component found for %s", comp.Name)
|
||
}
|
||
for idx, v := range objects {
|
||
assert.Equal(t, c.want.objs[idx], v)
|
||
}
|
||
|
||
})
|
||
}
|
||
}
|