Files
kubevela/references/cli/def_test.go
Eng Zer Jun 426aa7af34 Refactor: move from io/ioutil to io and os package (#2234)
The io/ioutil package has been deprecated as of Go 1.16, see
https://golang.org/doc/go1.16#ioutil. This commit replaces the existing
io/ioutil functions with their new definitions in io and os packages.

Signed-off-by: Eng Zer Jun <zerjun@beatchain.co>
2021-09-06 18:33:42 +08:00

378 lines
11 KiB
Go

/*
Copyright 2021 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cli
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
common3 "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
common2 "github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/references/common"
)
const (
// VelaTestNamespace namespace for hosting objects used during test
VelaTestNamespace = "vela-test-system"
)
func initArgs() common2.Args {
return common2.Args{
Client: fake.NewClientBuilder().WithScheme(common2.Scheme).Build(),
}
}
func initCommand(cmd *cobra.Command) {
cmd.SilenceErrors = true
cmd.SilenceUsage = true
cmd.Flags().StringP("env", "", "", "")
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
}
func createTrait(c common2.Args, t *testing.T) string {
traitName := fmt.Sprintf("my-trait-%d", time.Now().UnixNano())
createNamespacedTrait(c, traitName, VelaTestNamespace, t)
return traitName
}
func createNamespacedTrait(c common2.Args, name string, ns string, t *testing.T) {
traitName := fmt.Sprintf("my-trait-%d", time.Now().UnixNano())
if err := c.Client.Create(context.Background(), &v1beta1.TraitDefinition{
ObjectMeta: v1.ObjectMeta{
Name: name,
Namespace: ns,
Annotations: map[string]string{
common.DefinitionDescriptionKey: "My test-trait " + traitName,
},
},
Spec: v1beta1.TraitDefinitionSpec{
Schematic: &common3.Schematic{CUE: &common3.CUE{Template: "parameter: {}"}},
},
}); err != nil {
t.Fatalf("failed to create trait: %v", err)
}
}
func createLocalTraitAt(traitName string, localPath string, t *testing.T) string {
s := fmt.Sprintf(`// k8s metadata
"%s": {
type: "trait"
description: "My test-trait %s"
attributes: {
appliesToWorkloads: ["webservice", "worker"]
podDisruptive: true
}
}
// template
template: {
patch: {
spec: {
replicas: *1 | int
}
}
parameter: {
// +usage=Specify the number of workloads
replicas: *1 | int
}
}
`, traitName, traitName)
filename := filepath.Join(localPath, traitName+".cue")
if err := os.WriteFile(filename, []byte(s), 0600); err != nil {
t.Fatalf("failed to write temp trait file %s: %v", filename, err)
}
return filename
}
func createLocalTrait(t *testing.T) (string, string) {
traitName := fmt.Sprintf("my-trait-%d", time.Now().UnixNano())
filename := createLocalTraitAt(traitName, os.TempDir(), t)
return traitName, filename
}
func createLocalTraits(t *testing.T) string {
dirname, err := os.MkdirTemp(os.TempDir(), "vela-def-test-*")
if err != nil {
t.Fatalf("failed to create temporary directory: %v", err)
}
for i := 0; i < 3; i++ {
createLocalTraitAt(fmt.Sprintf("trait-%d", i), dirname, t)
}
return dirname
}
func createLocalDeploymentYAML(t *testing.T) string {
s := `apiVersion: apps/v1
kind: Deployment
metadata:
name: "main"
Spec:
image: "busybox"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: "secondary"
Spec:
image: "busybox"
`
filename := filepath.Join(os.TempDir(), fmt.Sprintf("%d-deployments.yaml", time.Now().UnixNano()))
if err := os.WriteFile(filename, []byte(s), 0600); err != nil {
t.Fatalf("failed to create temp deployments file %s: %v", filename, err)
}
return filename
}
func removeFile(filename string, t *testing.T) {
if err := os.Remove(filename); err != nil {
t.Fatalf("failed to remove file %s: %v", filename, err)
}
}
func removeDir(dirname string, t *testing.T) {
if err := os.RemoveAll(dirname); err != nil {
t.Fatalf("failed to remove dir %s: %v", dirname, err)
}
}
func TestNewDefinitionCommandGroup(t *testing.T) {
cmd := DefinitionCommandGroup(common2.Args{})
initCommand(cmd)
cmd.SetArgs([]string{"-h"})
if err := cmd.Execute(); err != nil {
t.Fatalf("failed to execute definition command: %v", err)
}
}
func TestNewDefinitionInitCommand(t *testing.T) {
c := initArgs()
// test normal
cmd := NewDefinitionInitCommand(c)
initCommand(cmd)
cmd.SetArgs([]string{"my-ingress", "-t", "trait", "--desc", "test ingress"})
if err := cmd.Execute(); err != nil {
t.Fatalf("unexpected error when executing init command: %v", err)
}
// test interactive
cmd = NewDefinitionInitCommand(initArgs())
initCommand(cmd)
componentName := "my-webservice"
cmd.SetArgs([]string{componentName, "--interactive"})
templateFilename := createLocalDeploymentYAML(t)
filename := strings.Replace(templateFilename, ".yaml", ".cue", 1)
defer removeFile(templateFilename, t)
defer removeFile(filename, t)
inputs := fmt.Sprintf("comp\ncomponent\nMy webservice component.\n%s\n%s\n", templateFilename, filename)
reader := strings.NewReader(inputs)
cmd.SetIn(reader)
if err := cmd.Execute(); err != nil {
t.Fatalf("unexpeced error when executing init command interactively: %v", err)
}
}
func TestNewDefinitionGetCommand(t *testing.T) {
c := initArgs()
// normal test
cmd := NewDefinitionGetCommand(c)
initCommand(cmd)
traitName := createTrait(c, t)
cmd.SetArgs([]string{traitName})
if err := cmd.Execute(); err != nil {
t.Fatalf("unexpeced error when executing get command: %v", err)
}
// test multi trait
cmd = NewDefinitionGetCommand(c)
initCommand(cmd)
createNamespacedTrait(c, traitName, "default", t)
cmd.SetArgs([]string{traitName})
if err := cmd.Execute(); err == nil {
t.Fatalf("expect found multiple traits error, but not found")
}
// test no trait
cmd = NewDefinitionGetCommand(c)
initCommand(cmd)
cmd.SetArgs([]string{traitName + "s"})
if err := cmd.Execute(); err == nil {
t.Fatalf("expect found no trait error, but not found")
}
}
func TestNewDefinitionListCommand(t *testing.T) {
c := initArgs()
// normal test
cmd := NewDefinitionListCommand(c)
initCommand(cmd)
_ = createTrait(c, t)
cmd.SetArgs([]string{})
if err := cmd.Execute(); err != nil {
t.Fatalf("unexpeced error when executing list command: %v", err)
}
// test no trait
cmd = NewDefinitionListCommand(c)
initCommand(cmd)
cmd.SetArgs([]string{"--namespace", "default"})
if err := cmd.Execute(); err != nil {
t.Fatalf("no trait found should not return error, err: %v", err)
}
}
func TestNewDefinitionEditCommand(t *testing.T) {
c := initArgs()
// normal test
cmd := NewDefinitionEditCommand(c)
initCommand(cmd)
traitName := createTrait(c, t)
if err := os.Setenv("EDITOR", "sed -i -e 's/test-trait/TestTrait/g'"); err != nil {
t.Fatalf("failed to set editor env: %v", err)
}
cmd.SetArgs([]string{traitName})
if err := cmd.Execute(); err != nil {
t.Fatalf("unexpeced error when executing edit command: %v", err)
}
// test no change
cmd = NewDefinitionEditCommand(c)
initCommand(cmd)
createNamespacedTrait(c, traitName, "default", t)
if err := os.Setenv("EDITOR", "sed -i -e 's/test-trait-test/TestTrait/g'"); err != nil {
t.Fatalf("failed to set editor env: %v", err)
}
cmd.SetArgs([]string{traitName, "-n", "default"})
if err := cmd.Execute(); err != nil {
t.Fatalf("unexpeced error when executing edit command: %v", err)
}
}
func TestNewDefinitionRenderCommand(t *testing.T) {
c := initArgs()
// normal test
cmd := NewDefinitionRenderCommand(c)
initCommand(cmd)
_ = os.Setenv(HelmChartFormatEnvName, "true")
_, traitFilename := createLocalTrait(t)
defer removeFile(traitFilename, t)
cmd.SetArgs([]string{traitFilename})
if err := cmd.Execute(); err != nil {
t.Fatalf("unexpeced error when executing redner command: %v", err)
}
// directory read/write test
_ = os.Setenv(HelmChartFormatEnvName, "system")
dirname := createLocalTraits(t)
defer removeDir(dirname, t)
outputDir, err := os.MkdirTemp(os.TempDir(), "vela-def-tests-output-*")
if err != nil {
t.Fatalf("failed to create temporary output dir: %v", err)
}
defer removeDir(outputDir, t)
cmd = NewDefinitionRenderCommand(c)
initCommand(cmd)
cmd.SetArgs([]string{dirname, "-o", outputDir, "--message", "Author: KubeVela"})
if err := cmd.Execute(); err != nil {
t.Fatalf("unexpeced error when executing render command: %v", err)
}
// directory read/print test
_ = os.WriteFile(filepath.Join(dirname, "temp.json"), []byte("hello"), 0600)
_ = os.WriteFile(filepath.Join(dirname, "temp.cue"), []byte("hello"), 0600)
cmd = NewDefinitionRenderCommand(c)
initCommand(cmd)
cmd.SetArgs([]string{dirname})
if err := cmd.Execute(); err != nil {
t.Fatalf("unexpeced error when executing render command: %v", err)
}
}
func TestNewDefinitionApplyCommand(t *testing.T) {
c := initArgs()
// dry-run test
cmd := NewDefinitionApplyCommand(c)
initCommand(cmd)
_, traitFilename := createLocalTrait(t)
defer removeFile(traitFilename, t)
cmd.SetArgs([]string{traitFilename, "--dry-run"})
if err := cmd.Execute(); err != nil {
t.Fatalf("unexpeced error when executing apply command: %v", err)
}
// normal test and reapply
cmd = NewDefinitionApplyCommand(c)
initCommand(cmd)
cmd.SetArgs([]string{traitFilename})
for i := 0; i < 2; i++ {
if err := cmd.Execute(); err != nil {
t.Fatalf("unexpeced error when executing apply command: %v", err)
}
}
}
func TestNewDefinitionDelCommand(t *testing.T) {
c := initArgs()
cmd := NewDefinitionDelCommand(c)
initCommand(cmd)
traitName := createTrait(c, t)
reader := strings.NewReader("yes\n")
cmd.SetIn(reader)
cmd.SetArgs([]string{traitName})
if err := cmd.Execute(); err != nil {
t.Fatalf("unexpeced error when executing del command: %v", err)
}
obj := &v1beta1.TraitDefinition{}
if err := c.Client.Get(context.Background(), types.NamespacedName{
Namespace: VelaTestNamespace,
Name: traitName,
}, obj); !errors.IsNotFound(err) {
t.Fatalf("should not found target definition %s, err: %v", traitName, err)
}
if err := cmd.Execute(); err == nil {
t.Fatalf("should encounter not found error, but no error found")
}
}
func TestNewDefinitionVetCommand(t *testing.T) {
c := initArgs()
cmd := NewDefinitionValidateCommand(c)
initCommand(cmd)
_, traitFilename := createLocalTrait(t)
defer removeFile(traitFilename, t)
cmd.SetArgs([]string{traitFilename})
if err := cmd.Execute(); err != nil {
t.Fatalf("unexpeced error when executing vet command: %v", err)
}
bs, err := os.ReadFile(traitFilename)
if err != nil {
t.Fatalf("failed to read trait file %s: %v", traitFilename, err)
}
bs = []byte(string(bs) + "abc:{xa}")
if err = os.WriteFile(traitFilename, bs, 0600); err != nil {
t.Fatalf("failed to modify trait file %s: %v", traitFilename, err)
}
if err = cmd.Execute(); err == nil {
t.Fatalf("expect validation failed but error not found")
}
}