Files
kubevela/pkg/appfile/api/appfile_test.go
roy wang 93ae8a9099 fix staticcheck issues
add staticcheck CI action

add staticcheck in Makefile

Signed-off-by: roywang <seiwy2010@gmail.com>
2021-01-29 18:42:03 +09:00

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