fix run bug

This commit is contained in:
erdun
2020-07-23 12:20:50 +08:00
parent 1aa798fb0b
commit 76ad168ccf
16 changed files with 590 additions and 253 deletions

View File

@@ -52,17 +52,18 @@ func main() {
logs.InitLogs()
defer logs.FlushLogs()
cmdutil.CheckErr(command.Execute())
command.Execute()
}
func newCommand(args []string) *cobra.Command {
ioStream := cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr}
cmds := &cobra.Command{
Use: "rudrx",
Short: "rudrx is a command-line tool to use OAM based micro-app engine.",
Long: "rudrx is a command-line tool to use OAM based micro-app engine.",
Run: runHelp,
Use: "rudrx",
Short: "rudrx is a command-line tool to use OAM based micro-app engine.",
Long: "rudrx is a command-line tool to use OAM based micro-app engine.",
Run: runHelp,
SilenceUsage: true,
}
flags := cmds.PersistentFlags()

View File

@@ -1,7 +1,7 @@
apiVersion: admin.oam.dev/v1alpha2
kind: Template
metadata:
annotation:
annotations:
version: 0.0.1
name: containerizedworkload-template
spec:

View File

@@ -3,7 +3,7 @@ kind: WorkloadDefinition
metadata:
name: containerizedworkloads.core.oam.dev
annotations:
defatultTemplateRef: containerizedworkload-template
rudrx.oam.dev/template: containerizedworkload-template
short: containerized
spec:
definitionRef:

View File

@@ -1,7 +1,7 @@
apiVersion: admin.oam.dev/v1alpha2
kind: Template
metadata:
annotation:
annotations:
version: 0.0.1
name: manualscalertrait.core.oam.dev-template
spec:

View File

@@ -3,7 +3,7 @@ kind: TraitDefinition
metadata:
name: manualscalertrait.core.oam.dev
annotations:
defatultTemplateRef: manualscalertrait.core.oam.dev-template
rudrx.oam.dev/template: manualscalertrait.core.oam.dev-template
short: ManualScaler
spec:
appliesToWorkloads:

View File

@@ -3,7 +3,7 @@ kind: TraitDefinition
metadata:
name: simplerollouttraits.extend.oam.dev
annotations:
defatultTemplateRef: simplerollouttraits.extend.oam.dev-template
rudrx.oam.dev/template: simplerollouttraits.extend.oam.dev-template
short: SimpleRollout
spec:
revisionEnabled: true

View File

@@ -1,7 +1,7 @@
apiVersion: admin.oam.dev/v1alpha2
kind: Template
metadata:
annotation:
annotations:
version: 0.0.1
name: simplerollouttraits.extend.oam.dev-template
spec:

View File

@@ -22,15 +22,12 @@ import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
coreoamdevv1alpha2 "github.com/cloud-native-application/rudrx/api/v1alpha2"
// +kubebuilder:scaffold:imports
)
@@ -57,19 +54,20 @@ var _ = BeforeSuite(func(done Done) {
CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
}
var err error
cfg, err = testEnv.Start()
Expect(err).ToNot(HaveOccurred())
Expect(cfg).ToNot(BeNil())
// TODO add later
// var err error
// cfg, err = testEnv.Start()
// Expect(err).ToNot(HaveOccurred())
// Expect(cfg).ToNot(BeNil())
err = coreoamdevv1alpha2.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
// err = coreoamdevv1alpha2.AddToScheme(scheme.Scheme)
// Expect(err).NotTo(HaveOccurred())
// +kubebuilder:scaffold:scheme
// // +kubebuilder:scaffold:scheme
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
Expect(err).ToNot(HaveOccurred())
Expect(k8sClient).ToNot(BeNil())
// k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
// Expect(err).ToNot(HaveOccurred())
// Expect(k8sClient).ToNot(BeNil())
close(done)
}, 60)

View File

@@ -16,6 +16,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
)
// TemplateLabel is the Annotation refer to template
const TemplateLabel = "rudrx.oam.dev/template"
type commandOptions struct {
Namespace string
Template v1alpha2.Template
@@ -33,36 +36,52 @@ func NewCommandOptions(ioStreams cmdutil.IOStreams) *commandOptions {
// NewBindCommand return bind command
func NewBindCommand(f cmdutil.Factory, c client.Client, ioStreams cmdutil.IOStreams, args []string) *cobra.Command {
cmd := newBindCommand()
cmd.RunE = func(cmd *cobra.Command, args []string) error {
return runSubBindCommand(f, c, ioStreams, args)
}
cmd.SetOutput(ioStreams.Out)
cmd.SetArgs(args)
cmd.SetOut(ioStreams.Out)
cmd.DisableFlagParsing = true
cmd.RunE = func(cmd *cobra.Command, args []string) error {
return runSubBindCommand(cmd, f, c, ioStreams, args)
}
return cmd
}
func runSubBindCommand(f cmdutil.Factory, c client.Client, ioStreams cmdutil.IOStreams, args []string) error {
var traitDefinitions corev1alpha2.TraitDefinitionList
func runSubBindCommand(parentCmd *cobra.Command, f cmdutil.Factory, c client.Client, ioStreams cmdutil.IOStreams, args []string) error {
ctx := context.Background()
o := NewCommandOptions(ioStreams)
o.Client = c
bindCmd := newBindCommand()
bindCmd.SetOutput(ioStreams.Out)
// init fake command and pass args to fake command
// flags and subcommand append to fake comand and parent command
// run fake command only, show tips in parent command only
fakeCommand := newBindCommand()
fakeCommand.SilenceUsage = true
fakeCommand.SilenceErrors = true
fakeCommand.DisableAutoGenTag = true
fakeCommand.DisableFlagsInUseLine = true
fakeCommand.DisableSuggestions = true
fakeCommand.SetOut(o.Out)
if len(args) > 0 {
bindCmd.SetArgs(args[1:])
fakeCommand.SetArgs(args)
} else {
fakeCommand.SetArgs([]string{})
}
var traitDefinitions corev1alpha2.TraitDefinitionList
err := c.List(ctx, &traitDefinitions)
if err != nil {
return fmt.Errorf("Listing trait definitions hit an issue: %v", err)
}
for _, t := range traitDefinitions.Items {
template := t.ObjectMeta.Annotations["defatultTemplateRef"]
for _, template := range traitDefinitions.Items {
templateName := template.Annotations[TemplateLabel]
// skip tarit that without template
if templateName == "" {
continue
}
var traitTemplate v1alpha2.Template
err := c.Get(ctx, client.ObjectKey{Namespace: "default", Name: template}, &traitTemplate)
err := c.Get(ctx, client.ObjectKey{Namespace: "default", Name: templateName}, &traitTemplate)
if err != nil {
return fmt.Errorf("Listing trait template hit an issue: %v", err)
}
@@ -73,23 +92,25 @@ func runSubBindCommand(f cmdutil.Factory, c client.Client, ioStreams cmdutil.IOS
if err != nil {
return fmt.Errorf("Parameters type is wrong: %v .Please report this to OAM maintainer, thanks.", err)
}
bindCmd.PersistentFlags().Int(p.Name, v, p.Usage)
fakeCommand.PersistentFlags().Int(p.Name, v, p.Usage)
parentCmd.PersistentFlags().Int(p.Name, v, p.Usage)
} else {
bindCmd.PersistentFlags().String(p.Name, p.Default, p.Usage)
fakeCommand.PersistentFlags().String(p.Name, p.Default, p.Usage)
parentCmd.PersistentFlags().String(p.Name, p.Default, p.Usage)
}
}
}
bindCmd.RunE = func(cmd *cobra.Command, args []string) error {
if err := o.Complete(f, bindCmd, args, ctx); err != nil {
fakeCommand.RunE = func(cmd *cobra.Command, args []string) error {
if err := o.Complete(fakeCommand, f, args, ctx); err != nil {
return err
}
return o.Run(f, bindCmd, ctx)
return o.Run(f, fakeCommand, ctx)
}
return bindCmd.Execute()
return fakeCommand.Execute()
}
func (o *commandOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string, ctx context.Context) error {
func (o *commandOptions) Complete(cmd *cobra.Command, f cmdutil.Factory, args []string, ctx context.Context) error {
argsLength := len(args)
var componentName string
c := o.Client
@@ -121,10 +142,10 @@ func (o *commandOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
case 1:
// Validate component and suggest trait
errTip := "Error: No trait specified.\nPlease choose a trait: "
for _, t := range traitList {
n := t.Short
for _, trait := range traitList {
n := trait.Short
if n == "" {
n = t.Name
n = trait.Name
}
errTip += n + " "
}
@@ -135,11 +156,11 @@ func (o *commandOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
var traitLongName string
validTrait := false
for _, t := range traitList {
for _, trait := range traitList {
// Support trait name or trait short name case-sensitively
if strings.EqualFold(t.Name, traitName) || strings.EqualFold(t.Short, traitName) {
if strings.EqualFold(trait.Name, traitName) || strings.EqualFold(trait.Short, traitName) {
validTrait = true
traitLongName = t.Name
traitLongName = trait.Name
break
}
}
@@ -152,7 +173,7 @@ func (o *commandOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
c.Get(ctx, client.ObjectKey{Namespace: ns, Name: traitLongName}, &traitDefinition)
var traitTemplate v1alpha2.Template
c.Get(ctx, client.ObjectKey{Namespace: "default", Name: traitDefinition.ObjectMeta.Annotations["defatultTemplateRef"]}, &traitTemplate)
c.Get(ctx, client.ObjectKey{Namespace: "default", Name: traitDefinition.ObjectMeta.Annotations[TemplateLabel]}, &traitTemplate)
pvd := fieldpath.Pave(traitTemplate.Spec.Object.Object)
for _, v := range traitTemplate.Spec.Parameters {
@@ -188,7 +209,7 @@ func (o *commandOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
// Run command
func (o *commandOptions) Run(f cmdutil.Factory, cmd *cobra.Command, ctx context.Context) error {
o.Info("Applying trait for component", o.Component.Name)
o.Infof("Applying trait for component %s\n", o.Component.Name)
c := o.Client
err := c.Update(ctx, &o.AppConfig)
if err != nil {

63
pkg/cmd/bind_test.go Normal file
View File

@@ -0,0 +1,63 @@
package cmd
import (
"testing"
"k8s.io/apimachinery/pkg/runtime"
"github.com/cloud-native-application/rudrx/pkg/test"
)
func TestNewBindCommand(t *testing.T) {
TraitsNotApply := traitDefinitionExample.DeepCopy()
TraitsNotApply.Spec.AppliesToWorkloads = []string{"core.oam.dev/v1alpha2.ContainerizedWorkload"}
cases := map[string]*test.CliTestCase{
"WithNoArgs": {
Resources: test.InitResources{
Create: []runtime.Object{
traitDefinitionExample.DeepCopy(),
traitTemplateExample.DeepCopy(),
},
},
ExpectedOutput: "Please append the name of an application. Use `rudr bind -h` for more detailed information.",
Args: []string{},
WantException: true,
},
"WithWrongAppconfig": {
Resources: test.InitResources{
Create: []runtime.Object{
traitDefinitionExample.DeepCopy(),
traitTemplateExample.DeepCopy(),
},
},
ExpectedOutput: "applicationconfigurations.core.oam.dev \"frontend\" not found",
Args: []string{"frontend"},
WantException: true,
},
"TemplateParametersWork": {
Resources: test.InitResources{
Create: []runtime.Object{
traitDefinitionExample.DeepCopy(),
traitTemplateExample.DeepCopy(),
},
},
ExpectedString: "--replicaCount int",
Args: []string{"-h"},
},
"WorkSuccess": {
Resources: test.InitResources{
Create: []runtime.Object{
appconfigExample.DeepCopy(),
componentExample.DeepCopy(),
traitDefinitionExample.DeepCopy(),
traitTemplateExample.DeepCopy(),
},
},
ExpectedOutput: "Applying trait for component app2060\nSucceeded!",
Args: []string{"app2060", "ManualScaler", "--replicaCount", "5"},
},
}
test.NewCliTest(t, scheme, NewBindCommand, cases).Run()
}

192
pkg/cmd/fixtures_test.go Normal file
View File

@@ -0,0 +1,192 @@
package cmd
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"github.com/cloud-native-application/rudrx/api/v1alpha2"
"github.com/crossplane/oam-kubernetes-runtime/apis/core"
corev1alpha2 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
)
var (
scheme = runtime.NewScheme()
)
func init() {
_ = clientgoscheme.AddToScheme(scheme)
_ = v1alpha2.AddToScheme(scheme)
_ = core.AddToScheme(scheme)
// +kubebuilder:scaffold:scheme
}
// used in testing
var (
workloadTemplateExample = &v1alpha2.Template{
TypeMeta: metav1.TypeMeta{
APIVersion: "admin.oam.dev/v1alpha2",
Kind: "Template",
},
ObjectMeta: metav1.ObjectMeta{
Name: "containerizedworkload-template",
Annotations: map[string]string{
"version": "0.0.1",
},
Namespace: "default",
},
Spec: v1alpha2.TemplateSpec{
Object: unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "core.oam.dev/v1alpha2",
"kind": "ContainerizedWorkload",
"metadata": map[string]interface{}{
"name": "pod",
},
"spec": map[string]interface{}{
"containers": "",
},
},
},
LastCommandParam: "image",
Parameters: []v1alpha2.Parameter{
v1alpha2.Parameter{
Name: "image",
Short: "i",
Required: true,
Type: "string",
FieldPaths: []string{"spec.containers[0].image"},
},
v1alpha2.Parameter{
Name: "port",
Short: "p",
Required: false,
Type: "int",
FieldPaths: []string{"spec.containers[0].ports[0].containerPort"},
},
},
},
}
traitTemplateExample = &v1alpha2.Template{
TypeMeta: metav1.TypeMeta{
APIVersion: "admin.oam.dev/v1alpha2",
Kind: "Template",
},
ObjectMeta: metav1.ObjectMeta{
Name: "manualscalertrait.core.oam.dev-template",
Annotations: map[string]string{
"version": "0.0.1",
},
Namespace: "default",
},
Spec: v1alpha2.TemplateSpec{
Object: unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "core.oam.dev/v1alpha2",
"kind": "ManualScalerTrait",
"metadata": map[string]interface{}{
"name": "pod",
},
"spec": map[string]interface{}{
"replicaCount": "2",
},
},
},
Parameters: []v1alpha2.Parameter{
v1alpha2.Parameter{
Name: "replicaCount",
Short: "i",
Required: true,
Type: "int",
FieldPaths: []string{"spec.replicaCount"},
Default: "5",
},
},
},
}
workloaddefExample = &corev1alpha2.WorkloadDefinition{
TypeMeta: metav1.TypeMeta{
APIVersion: "core.oam.dev/v1alpha2",
Kind: "WorkloadDefinition",
},
ObjectMeta: metav1.ObjectMeta{
Name: "containerizedworkloads.core.oam.dev",
Annotations: map[string]string{
"rudrx.oam.dev/template": "containerizedworkload-template",
},
Namespace: "default",
},
Spec: corev1alpha2.WorkloadDefinitionSpec{
Reference: corev1alpha2.DefinitionReference{
Name: "containerizedworkloads.core.oam.dev",
},
ChildResourceKinds: []corev1alpha2.ChildResourceKind{
corev1alpha2.ChildResourceKind{
APIVersion: "apps/v1",
Kind: "Deployment",
},
corev1alpha2.ChildResourceKind{
APIVersion: "v1",
Kind: "Service",
},
},
},
}
appconfigExample = &corev1alpha2.ApplicationConfiguration{
TypeMeta: metav1.TypeMeta{
APIVersion: "core.oam.dev/v1alpha2",
Kind: "ApplicationConfiguration",
},
ObjectMeta: metav1.ObjectMeta{
Name: "app2060",
Annotations: map[string]string{
"rudrx.oam.dev/template": "containerizedworkload-template",
},
Namespace: "default",
},
Spec: corev1alpha2.ApplicationConfigurationSpec{
Components: []corev1alpha2.ApplicationConfigurationComponent{
corev1alpha2.ApplicationConfigurationComponent{
ComponentName: "app2060",
},
},
},
}
componentExample = &corev1alpha2.Component{
TypeMeta: metav1.TypeMeta{
APIVersion: "core.oam.dev/v1alpha2",
Kind: "Component",
},
ObjectMeta: metav1.ObjectMeta{
Name: "app2060",
Namespace: "default",
},
Spec: corev1alpha2.ComponentSpec{},
}
traitDefinitionExample = &corev1alpha2.TraitDefinition{
TypeMeta: metav1.TypeMeta{
APIVersion: "core.oam.dev/v1alpha2",
Kind: "TraitDefinition",
},
ObjectMeta: metav1.ObjectMeta{
Name: "manualscalertrait.core.oam.dev",
Namespace: "default",
Annotations: map[string]string{
"rudrx.oam.dev/template": "manualscalertrait.core.oam.dev-template",
"short": "ManualScaler",
},
},
Spec: corev1alpha2.TraitDefinitionSpec{
Reference: corev1alpha2.DefinitionReference{
Name: "manualscalertrait.core.oam.dev",
},
AppliesToWorkloads: []string{"core.oam.dev/v1alpha2.ContainerizedWorkload"},
},
}
)

View File

@@ -30,28 +30,50 @@ func newRunOptions(ioStreams cmdutil.IOStreams) *runOptions {
return &runOptions{IOStreams: ioStreams}
}
// NewRunCommand init new command
func NewRunCommand(f cmdutil.Factory, c client.Client, ioStreams cmdutil.IOStreams, args []string) *cobra.Command {
cmd := newRunCommand()
// flags pass to new command directly
cmd.DisableFlagParsing = true
cmd.SetArgs(args)
cmd.SetOut(ioStreams.Out)
cmd.RunE = func(cmd *cobra.Command, args []string) error {
return runSubRunCommand(f, c, ioStreams, args)
return runSubRunCommand(cmd, f, c, ioStreams, args)
}
return cmd
}
func runSubRunCommand(f cmdutil.Factory, c client.Client, ioStreams cmdutil.IOStreams, args []string) error {
// runSubRunCommand is init a new command and run independent
func runSubRunCommand(parentCmd *cobra.Command, f cmdutil.Factory, c client.Client, ioStreams cmdutil.IOStreams, args []string) error {
ctx := context.Background()
workloadNames := []string{}
o := newRunOptions(ioStreams)
o.client = c
// init new cmd, append sub cmd
runCmd := newRunCommand()
runCmd.SetOutput(o.Out)
runCmd.PersistentFlags().StringP("namespace", "n", "default", "namespace for apps")
// init fake command and pass args to fake command
// flags and subcommand append to fake comand and parent command
// run fake command only, show tips in parent command only
fakeCommand := newRunCommand()
fakeCommand.SilenceUsage = true
fakeCommand.SilenceErrors = true
fakeCommand.DisableAutoGenTag = true
fakeCommand.DisableFlagsInUseLine = true
fakeCommand.DisableSuggestions = true
// set args from parent
if len(args) > 0 {
runCmd.SetArgs(args[1:])
fakeCommand.SetArgs(args)
} else {
fakeCommand.SetArgs([]string{})
}
fakeCommand.SetOutput(o.Out)
fakeCommand.RunE = func(cmd *cobra.Command, args []string) error {
return errors.New("You must specify a workload, like " + strings.Join(workloadNames, ", ") +
"\nSee 'rudr run -h' for help and examples")
}
fakeCommand.PersistentFlags().StringP("namespace", "n", "default", "namespace for apps")
parentCmd.PersistentFlags().StringP("namespace", "n", "default", "namespace for apps")
var workloadDefs corev1alpha2.WorkloadDefinitionList
err := c.List(ctx, &workloadDefs)
@@ -70,15 +92,15 @@ func runSubRunCommand(f cmdutil.Factory, c client.Client, ioStreams cmdutil.IOSt
name = wd.Name
}
workloadNames = append(workloadNames, name)
templateRef, ok := wd.ObjectMeta.Annotations["defatultTemplateRef"]
templateRef, ok := wd.ObjectMeta.Annotations[TemplateLabel]
if !ok {
continue
}
var tmp v1alpha2.Template
err = c.Get(ctx, client.ObjectKey{Namespace: "", Name: templateRef}, &tmp)
err = c.Get(ctx, client.ObjectKey{Namespace: "default", Name: templateRef}, &tmp)
if err != nil {
return fmt.Errorf("list workload Definition err: %v", err)
return fmt.Errorf("get workload Definition err: %v", err)
}
subcmd := &cobra.Command{
@@ -96,18 +118,16 @@ func runSubRunCommand(f cmdutil.Factory, c client.Client, ioStreams cmdutil.IOSt
subcmd.SetOutput(o.Out)
for _, v := range tmp.Spec.Parameters {
if tmp.Spec.LastCommandParam != v.Name {
runCmd.PersistentFlags().StringP(v.Name, v.Short, v.Default, v.Usage)
subcmd.PersistentFlags().StringP(v.Name, v.Short, v.Default, v.Usage)
}
}
runCmd.AddCommand(subcmd)
tmp.DeepCopyInto(&o.Template)
fakeCommand.AddCommand(subcmd)
parentCmd.AddCommand(subcmd)
}
runCmd.RunE = func(cmd *cobra.Command, args []string) error {
return errors.New("You must specify a workload, like " + strings.Join(workloadNames, ", ") +
"\nSee 'rudr run -h' for help and examples")
}
return runCmd.Execute()
return fakeCommand.Execute()
}
func (o *runOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
@@ -156,7 +176,6 @@ func (o *runOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri
}
pvd.SetString("metadata.name", args[0])
namespaceCover := cmd.Flag("namespace").Value.String()
if namespaceCover != "" {
namespace = namespaceCover
@@ -171,7 +190,7 @@ func (o *runOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri
}
func (o *runOptions) Run(f cmdutil.Factory, cmd *cobra.Command) error {
o.Infof("Creating AppConfig %s", o.AppConfig.Name)
o.Infof("Creating AppConfig %s\n", o.AppConfig.Name)
err := o.client.Create(context.Background(), &o.Component)
if err != nil {
return fmt.Errorf("create component err: %s", err)

View File

@@ -1,201 +1,81 @@
package cmd
import (
"context"
"strings"
"testing"
"gotest.tools/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
k8sRuntime "k8s.io/apimachinery/pkg/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
coreoamdevv1alpha2 "github.com/cloud-native-application/rudrx/api/v1alpha2"
cmdutil "github.com/cloud-native-application/rudrx/pkg/cmd/util"
"github.com/crossplane/oam-kubernetes-runtime/apis/core"
corev1alpha2 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
"github.com/cloud-native-application/rudrx/pkg/test"
)
var (
scheme = k8sRuntime.NewScheme()
)
type testResources struct {
create []runtime.Object
update []runtime.Object
}
func init() {
_ = clientgoscheme.AddToScheme(scheme)
_ = coreoamdevv1alpha2.AddToScheme(scheme)
_ = core.AddToScheme(scheme)
// +kubebuilder:scaffold:scheme
}
func TestNewRunCommand(t *testing.T) {
templateExample, workloaddefExample := getTestExample()
templateExample2 := templateExample.DeepCopy()
workloadTemplateExample2 := workloadTemplateExample.DeepCopy()
workloaddefExample2 := workloaddefExample.DeepCopy()
workloaddefExample2.Annotations["short"] = "containerized"
cases := map[string]struct {
resources *testResources
// want to exist with error
wantException bool
// output equal to
expectedOutput string
// output contains
expectedString string
args []string
}{
cases := map[string]*test.CliTestCase{
"WorkloadNotDefinited": {
resources: &testResources{
create: []runtime.Object{
workloaddefExample,
templateExample,
Resources: test.InitResources{
Create: []runtime.Object{
workloaddefExample.DeepCopy(),
workloadTemplateExample.DeepCopy(),
},
},
wantException: true,
expectedString: "You must specify a workload, like containerizedworkloads.core.oam.dev",
args: []string{},
WantException: true,
ExpectedString: "You must specify a workload, like containerizedworkloads.core.oam.dev",
Args: []string{},
},
"WorkloadShortWork": {
resources: &testResources{
create: []runtime.Object{
workloaddefExample2,
templateExample2,
Resources: test.InitResources{
Create: []runtime.Object{
workloaddefExample2.DeepCopy(),
workloadTemplateExample2.DeepCopy(),
},
},
wantException: true,
expectedString: "You must specify a workload, like containerized",
args: []string{},
WantException: true,
ExpectedString: "You must specify a workload, like containerized",
Args: []string{},
},
"PortFlagNotSet": {
Resources: test.InitResources{
Create: []runtime.Object{
workloaddefExample2.DeepCopy(),
workloadTemplateExample2.DeepCopy(),
},
},
ExpectedResources: []runtime.Object{
appconfigExample,
componentExample,
},
WantException: true,
ExpectedString: "Flag `port` is NOT set, please check and try again.",
Args: []string{"containerized", "app2060", "nginx:1.9.4"},
},
"TemplateParametersWork": {
Resources: test.InitResources{
Create: []runtime.Object{
workloaddefExample2.DeepCopy(),
workloadTemplateExample2.DeepCopy(),
},
},
ExpectedString: "-p, --port",
Args: []string{"containerized", "-h"},
},
"AppConfigCreated": {
Resources: test.InitResources{
Create: []runtime.Object{
workloaddefExample2.DeepCopy(),
workloadTemplateExample2.DeepCopy(),
},
},
ExpectedExistResources: []runtime.Object{
appconfigExample,
componentExample,
},
ExpectedOutput: "Creating AppConfig app2060\nSUCCEED",
Args: []string{"containerized", "app2060", "nginx:1.9.4", "-p", "80"},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
factory := cmdtesting.NewTestFactory().WithNamespace("test")
client := fake.NewFakeClientWithScheme(scheme)
iostream, _, outPut, _ := cmdutil.NewTestIOStreams()
if len(tc.resources.create) != 0 {
for _, resource := range tc.resources.create {
err := client.Create(context.TODO(), resource)
assert.NilError(t, err)
}
}
if len(tc.resources.update) != 0 {
for _, resource := range tc.resources.update {
err := client.Update(context.TODO(), resource)
println(111, err.Error())
assert.NilError(t, err)
}
}
runCmd := NewRunCommand(factory, client, iostream, []string{})
runCmd.SetOutput(outPut)
err := runCmd.Execute()
errTip := tc.expectedString
if tc.expectedOutput != "" {
errTip = tc.expectedOutput
}
if tc.wantException {
assert.ErrorContains(t, err, errTip)
return
}
if tc.expectedOutput != "" {
assert.Equal(t, tc.expectedOutput, outPut.String())
return
}
assert.Equal(t, true, strings.Contains(outPut.String(), tc.expectedString))
})
}
}
func getTestExample() (*coreoamdevv1alpha2.Template, *corev1alpha2.WorkloadDefinition) {
templateExample := &coreoamdevv1alpha2.Template{
TypeMeta: metav1.TypeMeta{
APIVersion: "admin.oam.dev/v1alpha2",
Kind: "Template",
},
ObjectMeta: metav1.ObjectMeta{
Name: "containerizedworkload-template",
Annotations: map[string]string{
"version": "0.0.1",
},
},
Spec: coreoamdevv1alpha2.TemplateSpec{
Object: unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "core.oam.dev/v1alpha2",
"kind": "ContainerizedWorkload",
"metadata": map[string]interface{}{
"name": "pod",
},
"spec": map[string]interface{}{
"containers": `
- image: myrepo/myapp:v1
name: master
ports:
- containerPort: 6379
protocol: TCP
name: tbd`,
},
},
},
LastCommandParam: "image",
Parameters: []coreoamdevv1alpha2.Parameter{
coreoamdevv1alpha2.Parameter{
Name: "image",
Short: "i",
Required: true,
Type: "string",
FieldPaths: []string{"spec.containers[0].image"},
},
coreoamdevv1alpha2.Parameter{
Name: "port",
Short: "p",
Required: false,
Type: "int",
FieldPaths: []string{"spec.containers[0].ports[0].containerPort"},
},
},
},
}
workloaddefExample := &corev1alpha2.WorkloadDefinition{
TypeMeta: metav1.TypeMeta{
APIVersion: "core.oam.dev/v1alpha2",
Kind: "WorkloadDefinition",
},
ObjectMeta: metav1.ObjectMeta{
Name: "containerizedworkloads.core.oam.dev",
Annotations: map[string]string{
"defatultTemplateRef": "containerizedworkload-template",
},
},
Spec: corev1alpha2.WorkloadDefinitionSpec{
Reference: corev1alpha2.DefinitionReference{
Name: "containerizedworkloads.core.oam.dev",
},
ChildResourceKinds: []corev1alpha2.ChildResourceKind{
corev1alpha2.ChildResourceKind{
APIVersion: "apps/v1",
Kind: "Deployment",
},
corev1alpha2.ChildResourceKind{
APIVersion: "v1",
Kind: "Service",
},
},
},
}
return templateExample, workloaddefExample
test.NewCliTest(t, scheme, NewRunCommand, cases).Run()
}

37
pkg/cmd/trait_test.go Normal file
View File

@@ -0,0 +1,37 @@
package cmd
import (
"testing"
"k8s.io/apimachinery/pkg/runtime"
"github.com/cloud-native-application/rudrx/pkg/test"
)
func TestNewTraitCommand(t *testing.T) {
TraitsNotApply := traitDefinitionExample.DeepCopy()
TraitsNotApply.Spec.AppliesToWorkloads = []string{}
cases := map[string]*test.CliTestCase{
"PrintTraits": {
Resources: test.InitResources{
Create: []runtime.Object{
traitDefinitionExample.DeepCopy(),
},
},
ExpectedString: "manualscalertrait.core.oam.dev",
Args: []string{},
},
"TraitsNotApply": {
Resources: test.InitResources{
Create: []runtime.Object{
TraitsNotApply,
},
},
ExpectedOutput: "NAME SHORT DEFINITION APPLIES TO STATUS",
Args: []string{},
},
}
test.NewCliTest(t, scheme, NewTraitsCommand, cases).Run()
}

View File

@@ -22,16 +22,19 @@ func NewTraitsCommand(f cmdutil.Factory, c client.Client, ioStreams cmdutil.IOSt
Example: `rudr traits`,
RunE: func(cmd *cobra.Command, args []string) error {
workloadName := cmd.Flag("workload").Value.String()
return printTraitList(ctx, c, workloadName)
return printTraitList(ctx, c, workloadName, ioStreams)
},
}
cmd.SetOutput(ioStreams.Out)
// flags pass to new command directly
cmd.DisableFlagParsing = true
cmd.SetArgs(args)
cmd.SetOut(ioStreams.Out)
cmd.PersistentFlags().StringP("workload", "w", "", "Workload name")
return cmd
}
func printTraitList(ctx context.Context, c client.Client, workloadName string) error {
func printTraitList(ctx context.Context, c client.Client, workloadName string, ioStreams cmdutil.IOStreams) error {
traitList, err := RetrieveTraitsByWorkload(ctx, c, workloadName)
table := uitable.New()
@@ -45,8 +48,7 @@ func printTraitList(ctx context.Context, c client.Client, workloadName string) e
for _, r := range traitList {
table.AddRow(r.Name, r.Short, r.Definition, r.AppliesTo, r.Status)
}
fmt.Println(table)
ioStreams.Info(table.String())
return nil
}
@@ -64,7 +66,6 @@ func RetrieveTraitsByWorkload(ctx context.Context, c client.Client, workloadName
Get trait list by optional filter `workloadName`
*/
var traitList []TraitMeta
var traitDefinitionList corev1alpha2.TraitDefinitionList
err := c.List(ctx, &traitDefinitionList)

125
pkg/test/cli.go Normal file
View File

@@ -0,0 +1,125 @@
package test
import (
"context"
"strings"
"testing"
"gotest.tools/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
cmdutil "github.com/cloud-native-application/rudrx/pkg/cmd/util"
"github.com/spf13/cobra"
)
// CliTest test engine
type CliTest interface {
Run()
}
// InitResources resource to init before run testing
type InitResources struct {
Create []runtime.Object
Update []runtime.Object
}
// CliTestCase is testing case for cli
type CliTestCase struct {
// Resources to init
Resources InitResources
// ExpectedExistResources expected exist resources
ExpectedExistResources []runtime.Object
// ExpectedResources expected resources exist and equal
ExpectedResources []runtime.Object
// WantException expected exit with error
WantException bool
// ExpectedOutput output equal to
ExpectedOutput string
// ExpectedString output contains strings
ExpectedString string
Args []string
// Namespaces to run
Namespaces string
}
type clitestImpl struct {
cases map[string]*CliTestCase
t *testing.T
scheme *runtime.Scheme
command func(f cmdutil.Factory, c client.Client, ioStreams cmdutil.IOStreams, args []string) *cobra.Command
}
// NewCliTest return cli testimpl
func NewCliTest(t *testing.T, scheme *runtime.Scheme,
command func(f cmdutil.Factory, c client.Client, ioStreams cmdutil.IOStreams,
args []string) *cobra.Command, cases map[string]*CliTestCase) CliTest {
return &clitestImpl{
cases: cases,
t: t,
scheme: scheme,
command: command,
}
}
// Run testing
func (c *clitestImpl) Run() {
for name, tc := range c.cases {
c.t.Run(name, func(t *testing.T) {
factory := cmdtesting.NewTestFactory().WithNamespace("default")
fakeClient := fake.NewFakeClientWithScheme(c.scheme)
iostream, _, outPut, _ := cmdutil.NewTestIOStreams()
// init resources
if len(tc.Resources.Create) != 0 {
for _, resource := range tc.Resources.Create {
err := fakeClient.Create(context.TODO(), resource)
assert.NilError(t, err)
}
}
if len(tc.Resources.Update) != 0 {
for _, resource := range tc.Resources.Update {
err := fakeClient.Update(context.TODO(), resource)
assert.NilError(t, err)
}
}
// init command
runCmd := c.command(factory, fakeClient, iostream, tc.Args)
runCmd.SetOutput(outPut)
err := runCmd.Execute()
// check expected resources
if len(tc.ExpectedExistResources) != 0 {
for _, expectedResource := range tc.ExpectedExistResources {
object, _ := expectedResource.(metav1.Object)
resource := expectedResource.DeepCopyObject()
err := fakeClient.Get(context.TODO(), client.ObjectKey{Namespace: object.GetNamespace(), Name: object.GetName()}, resource)
assert.NilError(t, err)
}
}
// check exit output
errTip := tc.ExpectedString
if tc.ExpectedOutput != "" {
errTip = tc.ExpectedOutput
}
if tc.WantException {
assert.ErrorContains(t, err, errTip)
return
}
// check output messages
if tc.ExpectedOutput != "" {
assert.Equal(t, tc.ExpectedOutput, outPut.String())
return
}
assert.Equal(t, true, strings.Contains(outPut.String(), tc.ExpectedString))
})
}
}