add refresh and use local cache instead request cluster every time

This commit is contained in:
天元
2020-08-07 19:20:21 +08:00
parent bb8043cc2b
commit 41536fe2cd
20 changed files with 410 additions and 241 deletions

11
api/types/args.go Normal file
View File

@@ -0,0 +1,11 @@
package types
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
)
type Args struct {
Config *rest.Config
Schema *runtime.Scheme
}

View File

@@ -34,6 +34,10 @@ type Template struct {
Template string `json:"template,omitempty"`
Parameters []Parameter `json:"parameters,omitempty"`
DefinitionPath string `json:"definition"`
CrdName string `json:"crdName,omitempty"`
//trait only
AppliesTo []string `json:"appliesTo,omitempty"`
}
type DefinitionType string
@@ -72,13 +76,35 @@ func ConvertTemplateJson2Object(in *runtime.RawExtension) (Template, error) {
func SetFlagBy(cmd *cobra.Command, v Parameter) {
switch v.Type {
case cue.IntKind:
cmd.Flags().Int64P(v.Name, v.Short, v.Default.(int64), v.Usage)
var vv int64
switch val := v.Default.(type) {
case int64:
vv = val
case json.Number:
vv, _ = val.Int64()
case int:
vv = int64(val)
case float64:
vv = int64(val)
}
cmd.Flags().Int64P(v.Name, v.Short, vv, v.Usage)
case cue.StringKind:
cmd.Flags().StringP(v.Name, v.Short, v.Default.(string), v.Usage)
case cue.BoolKind:
cmd.Flags().BoolP(v.Name, v.Short, v.Default.(bool), v.Usage)
case cue.NumberKind, cue.FloatKind:
cmd.Flags().Float64P(v.Name, v.Short, v.Default.(float64), v.Usage)
var vv float64
switch val := v.Default.(type) {
case int64:
vv = float64(val)
case json.Number:
vv, _ = val.Float64()
case int:
vv = float64(val)
case float64:
vv = val
}
cmd.Flags().Float64P(v.Name, v.Short, vv, v.Usage)
}
if v.Required && v.Name != "name" {
cmd.MarkFlagRequired(v.Name)

View File

@@ -1,15 +1,14 @@
package main
import (
"context"
"fmt"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"math/rand"
"os"
"runtime"
"time"
"github.com/cloud-native-application/rudrx/api/types"
"sigs.k8s.io/controller-runtime/pkg/client/config"
"github.com/cloud-native-application/rudrx/pkg/utils/system"
@@ -18,7 +17,6 @@ import (
"github.com/spf13/cobra"
k8sruntime "k8s.io/apimachinery/pkg/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/cloud-native-application/rudrx/pkg/cmd"
cmdutil "github.com/cloud-native-application/rudrx/pkg/cmd/util"
@@ -71,34 +69,17 @@ func newCommand() *cobra.Command {
return nil, cobra.ShellCompDirectiveNoFileComp
},
}
restConf, err := config.GetConfig()
if err != nil {
fmt.Println("get kubeconfig err", err)
os.Exit(1)
}
newClient, err := client.New(restConf, client.Options{Scheme: scheme})
err = cmds.RegisterFlagCompletionFunc("namespace", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
// Choose a long enough timeout that the user notices somethings is not working
// but short enough that the user is not made to wait very long
to := int64(3)
listOpt := &client.ListOptions{
Raw: &metav1.ListOptions{TimeoutSeconds: &to},
}
nsNames := []string{}
namespaces := v1.NamespaceList{}
if err = newClient.List(context.Background(), &namespaces, listOpt); err == nil {
for _, ns := range namespaces.Items {
nsNames = append(nsNames, ns.Name)
}
return nsNames, cobra.ShellCompDirectiveNoFileComp
}
return nil, cobra.ShellCompDirectiveDefault
})
if err != nil {
fmt.Println("create client from kubeconfig err", err)
os.Exit(1)
commandArgs := types.Args{
Config: restConf,
Schema: scheme,
}
if err := system.InitApplicationDir(); err != nil {
fmt.Println("InitApplicationDir err", err)
os.Exit(1)
@@ -109,31 +90,37 @@ func newCommand() *cobra.Command {
}
cmds.AddCommand(
cmd.NewTraitsCommand(newClient, ioStream, []string{}),
cmd.NewWorkloadsCommand(newClient, ioStream, os.Args[1:]),
cmd.NewAdminInitCommand(newClient, ioStream),
cmd.NewAdminInitCommand(commandArgs, ioStream),
cmd.NewAdminInfoCommand(VelaVersion, ioStream),
cmd.NewDeleteCommand(newClient, ioStream, os.Args[1:]),
cmd.NewAppsCommand(newClient, ioStream),
cmd.NewEnvInitCommand(newClient, ioStream),
cmd.NewTraitsCommand(ioStream),
cmd.NewWorkloadsCommand(ioStream),
cmd.NewRefreshCommand(commandArgs, ioStream),
cmd.NewDeleteCommand(commandArgs, ioStream, os.Args[1:]),
cmd.NewAppsCommand(commandArgs, ioStream),
cmd.NewAppStatusCommand(commandArgs, ioStream),
cmd.NewEnvInitCommand(commandArgs, ioStream),
cmd.NewEnvSwitchCommand(ioStream),
cmd.NewEnvDeleteCommand(ioStream),
cmd.NewEnvCommand(ioStream),
NewVersionCommand(),
cmd.NewAppStatusCommand(newClient, ioStream),
cmd.NewAddonConfigCommand(ioStream),
cmd.NewAddonListCommand(newClient, ioStream),
cmd.NewAddonListCommand(commandArgs, ioStream),
cmd.NewCompletionCommand(),
NewVersionCommand(),
)
if err = cmd.AddWorkloadPlugins(cmds, newClient, ioStream); err != nil {
if err = cmd.AddWorkloadPlugins(cmds, commandArgs, ioStream); err != nil {
fmt.Println("Add plugins from workloadDefinition err", err)
os.Exit(1)
}
if err = cmd.AddTraitPlugins(cmds, newClient, ioStream); err != nil {
if err = cmd.AddTraitPlugins(cmds, commandArgs, ioStream); err != nil {
fmt.Println("Add plugins from traitDefinition err", err)
os.Exit(1)
}
if err = cmd.DetachTraitPlugins(cmds, newClient, ioStream); err != nil {
if err = cmd.DetachTraitPlugins(cmds, commandArgs, ioStream); err != nil {
fmt.Println("Add plugins from traitDefinition err", err)
os.Exit(1)
}

View File

@@ -82,24 +82,27 @@ func NewAddonConfigCommand(ioStreams cmdutil.IOStreams) *cobra.Command {
return cmd
}
func NewAddonListCommand(c client.Client, ioStreams cmdutil.IOStreams) *cobra.Command {
func NewAddonListCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command {
ctx := context.Background()
cmd := &cobra.Command{
Use: "addon:ls",
Short: "List addons",
Long: "List addons of workloads and traits",
Example: `vela addon:ls`,
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) error {
env, err := GetEnv()
if err != nil {
ioStreams.Errorf("Failed to get Env information:%s", err)
os.Exit(1)
return err
}
err = retrievePlugins(ctx, c, ioStreams, env.Namespace)
newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema})
if err != nil {
ioStreams.Errorf("Failed to list Addons:%s", err)
os.Exit(1)
return err
}
err = retrievePlugins(ctx, newClient, ioStreams, env.Namespace)
if err != nil {
return err
}
return nil
},
}
return cmd

View File

@@ -37,14 +37,19 @@ func newDeleteCommand() *cobra.Command {
}
// NewDeleteCommand init new command
func NewDeleteCommand(c client.Client, ioStreams cmdutil.IOStreams, args []string) *cobra.Command {
func NewDeleteCommand(c types.Args, ioStreams cmdutil.IOStreams, args []string) *cobra.Command {
cmd := newDeleteCommand()
cmd.SetArgs(args)
cmd.SetOut(ioStreams.Out)
o := newDeleteOptions(ioStreams)
o.client = c
o.Env, _ = GetEnv()
cmd.RunE = func(cmd *cobra.Command, args []string) error {
newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema})
if err != nil {
return err
}
o.client = newClient
if err := o.Complete(cmd, args); err != nil {
return err
}

View File

@@ -36,7 +36,7 @@ func NewEnvCommand(ioStreams cmdutil.IOStreams) *cobra.Command {
cmd.SetOut(ioStreams.Out)
cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
cmdutil.PrintUsageIntroduce(cmd, "Prepare environments for applications")
subcmds := []*cobra.Command{cmd, NewEnvInitCommand(nil, ioStreams), NewEnvSwitchCommand(ioStreams), NewEnvDeleteCommand(ioStreams)}
subcmds := []*cobra.Command{cmd, NewEnvInitCommand(types.Args{}, ioStreams), NewEnvSwitchCommand(ioStreams), NewEnvDeleteCommand(ioStreams)}
cmdutil.PrintUsage(cmd, subcmds)
cmdutil.PrintExample(cmd, subcmds)
cmdutil.PrintFlags(cmd, subcmds)
@@ -44,7 +44,7 @@ func NewEnvCommand(ioStreams cmdutil.IOStreams) *cobra.Command {
return cmd
}
func NewEnvInitCommand(c client.Client, ioStreams cmdutil.IOStreams) *cobra.Command {
func NewEnvInitCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command {
var envArgs types.EnvMeta
ctx := context.Background()
cmd := &cobra.Command{
@@ -54,7 +54,11 @@ func NewEnvInitCommand(c client.Client, ioStreams cmdutil.IOStreams) *cobra.Comm
Long: "Create environment and switch to it",
Example: `vela env:init test --namespace test`,
RunE: func(cmd *cobra.Command, args []string) error {
return CreateOrUpdateEnv(ctx, c, &envArgs, args, ioStreams)
newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema})
if err != nil {
return err
}
return CreateOrUpdateEnv(ctx, newClient, &envArgs, args, ioStreams)
},
}
cmd.SetOut(ioStreams.Out)

View File

@@ -18,7 +18,6 @@ import (
oamv1 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
"github.com/spf13/cobra"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/pkg/errors"
@@ -51,7 +50,6 @@ type initCmd struct {
namespace string
out io.Writer
client client.Client
config *rest.Config
version string
}
@@ -92,7 +90,6 @@ func NewAdminInfoCommand(version string, ioStreams cmdutil.IOStreams) *cobra.Com
return i.run(version, ioStreams)
},
}
return cmd
}
@@ -109,7 +106,7 @@ func (i *infoCmd) run(version string, ioStreams cmdutil.IOStreams) error {
return nil
}
func NewAdminInitCommand(c client.Client, ioStreams cmdutil.IOStreams) *cobra.Command {
func NewAdminInitCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command {
i := &initCmd{out: ioStreams.Out}
@@ -118,7 +115,11 @@ func NewAdminInitCommand(c client.Client, ioStreams cmdutil.IOStreams) *cobra.Co
Short: "Initialize Vela on both client and server",
Long: initDesc,
RunE: func(cmd *cobra.Command, args []string) error {
i.client = c
newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema})
if err != nil {
return err
}
i.client = newClient
i.namespace = types.DefaultOAMNS
return i.run(ioStreams)
},

View File

@@ -3,16 +3,16 @@ package cmd
import (
"context"
"fmt"
"os"
"strings"
"github.com/cloud-native-application/rudrx/api/types"
cmdutil "github.com/cloud-native-application/rudrx/pkg/cmd/util"
"github.com/gosuri/uitable"
"github.com/spf13/cobra"
"sigs.k8s.io/controller-runtime/pkg/client"
)
func NewAppsCommand(c client.Client, ioStreams cmdutil.IOStreams) *cobra.Command {
func NewAppsCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command {
ctx := context.Background()
cmd := &cobra.Command{
Use: "app:ls",
@@ -20,13 +20,17 @@ func NewAppsCommand(c client.Client, ioStreams cmdutil.IOStreams) *cobra.Command
Short: "List applications",
Long: "List applications with workloads, traits, status and created time",
Example: `vela ls`,
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) error {
env, err := GetEnv()
if err != nil {
ioStreams.Errorf("Failed to get Env information:%s", err)
os.Exit(1)
return err
}
printApplicationList(ctx, c, "", env.Namespace)
newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema})
if err != nil {
return err
}
printApplicationList(ctx, newClient, "", env.Namespace)
return nil
},
}

67
pkg/cmd/refresh.go Normal file
View File

@@ -0,0 +1,67 @@
package cmd
import (
"context"
"os"
"path/filepath"
"github.com/cloud-native-application/rudrx/api/types"
cmdutil "github.com/cloud-native-application/rudrx/pkg/cmd/util"
"github.com/cloud-native-application/rudrx/pkg/plugins"
"github.com/cloud-native-application/rudrx/pkg/utils/system"
"github.com/spf13/cobra"
"sigs.k8s.io/controller-runtime/pkg/client"
)
func NewRefreshCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command {
ctx := context.Background()
cmd := &cobra.Command{
Use: "refresh",
DisableFlagsInUseLine: true,
Short: "Sync definition from cluster",
Long: "Refresh and sync definition files from cluster",
Example: `vela refresh`,
RunE: func(cmd *cobra.Command, args []string) error {
newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema})
if err != nil {
return err
}
return RefreshDefinitions(ctx, newClient, ioStreams)
},
}
cmd.SetOut(ioStreams.Out)
return cmd
}
func StatOrCreate(dir string) {
if _, err := os.Stat(dir); os.IsNotExist(err) {
os.MkdirAll(dir, 0755)
}
}
func RefreshDefinitions(ctx context.Context, c client.Client, ioStreams cmdutil.IOStreams) error {
dir, _ := system.GetDefinitionDir()
ioStreams.Info("syncing workload definitions from cluster...")
templates, err := plugins.GetWorkloadsFromCluster(ctx, types.DefaultOAMNS, c, dir, nil)
if err != nil {
return err
}
workloadDir := filepath.Join(dir, "workloads")
StatOrCreate(workloadDir)
ioStreams.Infof("get %d workload definitions from cluster, syncing to %s...", len(templates), workloadDir)
successNum := plugins.SinkTemp2Local(templates, workloadDir)
ioStreams.Infof("%d workload definitions successfully synced\n", successNum)
ioStreams.Info("syncing trait definitions from cluster...")
templates, err = plugins.GetTraitsFromCluster(ctx, types.DefaultOAMNS, c, dir, nil)
if err != nil {
return err
}
traitDir := filepath.Join(dir, "traits")
StatOrCreate(traitDir)
ioStreams.Infof("get %d trait definitions from cluster, syncing to %s...", len(templates), traitDir)
successNum = plugins.SinkTemp2Local(templates, traitDir)
ioStreams.Infof("%d trait definitions successfully synced\n", successNum)
return nil
}

View File

@@ -4,6 +4,8 @@ import (
"context"
"os"
"github.com/cloud-native-application/rudrx/api/types"
cmdutil "github.com/cloud-native-application/rudrx/pkg/cmd/util"
"github.com/ghodss/yaml"
@@ -11,7 +13,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
)
func NewAppStatusCommand(c client.Client, ioStreams cmdutil.IOStreams) *cobra.Command {
func NewAppStatusCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command {
ctx := context.Background()
cmd := &cobra.Command{
Use: "app:status",
@@ -30,9 +32,12 @@ func NewAppStatusCommand(c client.Client, ioStreams cmdutil.IOStreams) *cobra.Co
ioStreams.Errorf("Error: failed to get Env: %s", err)
return err
}
namespace := env.Namespace
return printApplicationStatus(ctx, c, ioStreams, appName, namespace)
newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema})
if err != nil {
return err
}
return printApplicationStatus(ctx, newClient, ioStreams, appName, namespace)
},
}
cmd.SetOut(ioStreams.Out)

View File

@@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"path/filepath"
"strconv"
"strings"
@@ -43,9 +44,9 @@ func NewCommandOptions(ioStreams cmdutil.IOStreams) *commandOptions {
return &commandOptions{IOStreams: ioStreams}
}
func AddTraitPlugins(parentCmd *cobra.Command, c client.Client, ioStreams cmdutil.IOStreams) error {
func AddTraitPlugins(parentCmd *cobra.Command, c types.Args, ioStreams cmdutil.IOStreams) error {
dir, _ := system.GetDefinitionDir()
templates, err := plugins.GetTraitsFromCluster(context.TODO(), types.DefaultOAMNS, c, dir, nil)
templates, err := plugins.LoadTempFromLocal(filepath.Join(dir, "traits"))
if err != nil {
return err
}
@@ -53,7 +54,6 @@ func AddTraitPlugins(parentCmd *cobra.Command, c client.Client, ioStreams cmduti
for _, tmp := range templates {
var name = tmp.Name
o := NewCommandOptions(ioStreams)
o.Client = c
o.Env, _ = GetEnv()
pluginCmd := &cobra.Command{
Use: name + " <appname> [args]",
@@ -62,6 +62,11 @@ func AddTraitPlugins(parentCmd *cobra.Command, c client.Client, ioStreams cmduti
Long: "Attach " + name + " trait to an app",
Example: `vela scale frontend --max=5`,
RunE: func(cmd *cobra.Command, args []string) error {
newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema})
if err != nil {
return err
}
o.Client = newClient
if err := o.Complete(cmd, args, ctx); err != nil {
return err
}
@@ -163,9 +168,9 @@ func (o *commandOptions) Complete(cmd *cobra.Command, args []string, ctx context
return nil
}
func DetachTraitPlugins(parentCmd *cobra.Command, c client.Client, ioStreams cmdutil.IOStreams) error {
func DetachTraitPlugins(parentCmd *cobra.Command, c types.Args, ioStreams cmdutil.IOStreams) error {
dir, _ := system.GetDefinitionDir()
templates, err := plugins.GetTraitsFromCluster(context.TODO(), types.DefaultOAMNS, c, dir, nil)
templates, err := plugins.LoadTempFromLocal(filepath.Join(dir, "traits"))
if err != nil {
return err
}
@@ -173,7 +178,6 @@ func DetachTraitPlugins(parentCmd *cobra.Command, c client.Client, ioStreams cmd
for _, tmp := range templates {
var name = tmp.Name
o := NewCommandOptions(ioStreams)
o.Client = c
o.Env, _ = GetEnv()
pluginCmd := &cobra.Command{
Use: name + ":detach <appname>",
@@ -182,6 +186,11 @@ func DetachTraitPlugins(parentCmd *cobra.Command, c client.Client, ioStreams cmd
Long: "Detach " + name + " trait from an app",
Example: `vela scale:detach frontend`,
RunE: func(cmd *cobra.Command, args []string) error {
newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema})
if err != nil {
return err
}
o.Client = newClient
if err := o.DetachTrait(cmd, args, ctx); err != nil {
return err
}

View File

@@ -1,37 +0,0 @@
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 ALIAS DEFINITION APPLIES TO STATUS\n",
Args: []string{},
},
}
test.NewCliTest(t, scheme, NewTraitsCommand, cases).Run()
}

View File

@@ -1,19 +1,20 @@
package cmd
import (
"context"
"fmt"
"path/filepath"
"strings"
"github.com/cloud-native-application/rudrx/api/types"
"github.com/cloud-native-application/rudrx/pkg/plugins"
"github.com/cloud-native-application/rudrx/pkg/utils/system"
cmdutil "github.com/cloud-native-application/rudrx/pkg/cmd/util"
corev1alpha2 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
"github.com/gosuri/uitable"
"github.com/spf13/cobra"
"sigs.k8s.io/controller-runtime/pkg/client"
)
func NewTraitsCommand(c client.Client, ioStreams cmdutil.IOStreams, args []string) *cobra.Command {
ctx := context.Background()
func NewTraitsCommand(ioStreams cmdutil.IOStreams) *cobra.Command {
var workloadName string
cmd := &cobra.Command{
Use: "traits [--apply-to WORKLOADNAME]",
@@ -22,7 +23,16 @@ func NewTraitsCommand(c client.Client, ioStreams cmdutil.IOStreams, args []strin
Long: "List traits",
Example: `vela traits`,
RunE: func(cmd *cobra.Command, args []string) error {
return printTraitList(ctx, c, &workloadName, ioStreams)
dir, _ := system.GetDefinitionDir()
templates, err := plugins.LoadTempFromLocal(filepath.Join(dir, "traits"))
if err != nil {
return err
}
workloads, err := plugins.LoadTempFromLocal(filepath.Join(dir, "workloads"))
if err != nil {
return err
}
return printTraitList(templates, workloads, &workloadName, ioStreams)
},
}
@@ -31,32 +41,29 @@ func NewTraitsCommand(c client.Client, ioStreams cmdutil.IOStreams, args []strin
return cmd
}
func printTraitList(ctx context.Context, c client.Client, workloadName *string, ioStreams cmdutil.IOStreams) error {
traitList, err := RetrieveTraitsByWorkload(ctx, c, "", *workloadName)
func printTraitList(traits, workloads []types.Template, workloadName *string, ioStreams cmdutil.IOStreams) error {
table := uitable.New()
table.MaxColWidth = 60
if err != nil {
return fmt.Errorf("Listing Trait DefinitionPath hit an issue: %s", err)
}
table.AddRow("NAME", "ALIAS", "DEFINITION", "APPLIES TO", "STATUS")
for _, r := range traitList {
wdList := strings.Split(r.AppliesTo, ",")
if len(wdList) > 1 {
isFirst := true
for _, wd := range wdList {
wd = strings.Trim(wd, " ")
if isFirst {
table.AddRow(r.Name, r.Short, r.Definition, wd, r.Status)
isFirst = false
table.AddRow("NAME", "DEFINITION", "APPLIES TO")
for _, r := range traits {
convertedApplyTo := ConvertApplyTo(r.AppliesTo, workloads)
if *workloadName != "" {
if !In(convertedApplyTo, *workloadName) {
continue
}
convertedApplyTo = []string{*workloadName}
}
if len(convertedApplyTo) > 1 && *workloadName == "" {
for i, wd := range convertedApplyTo {
if i > 0 {
table.AddRow("", "", wd)
} else {
table.AddRow("", "", "", wd, "")
table.AddRow(r.Name, r.CrdName, wd)
}
}
} else {
table.AddRow(r.Name, r.Short, r.Definition, r.AppliesTo, r.Status)
table.AddRow(r.Name, r.CrdName, strings.Join(convertedApplyTo, ""))
}
}
ioStreams.Info(table.String())
@@ -64,53 +71,32 @@ func printTraitList(ctx context.Context, c client.Client, workloadName *string,
return nil
}
type TraitMeta struct {
Name string `json:"name"`
Short string `json:"shot"`
Definition string `json:"definition,omitempty"`
AppliesTo string `json:"appliesTo,omitempty"`
Status string `json:"status,omitempty"`
}
// RetrieveTraitsByWorkload Get trait list by optional filter `workloadName`
func RetrieveTraitsByWorkload(ctx context.Context, c client.Client, namespace string, workloadName string) ([]TraitMeta, error) {
var traitList []TraitMeta
var traitDefinitionList corev1alpha2.TraitDefinitionList
if namespace == "" {
namespace = "default"
}
err := c.List(ctx, &traitDefinitionList, client.InNamespace(namespace))
for _, r := range traitDefinitionList.Items {
var appliesTo string
if workloadName == "" {
appliesTo = strings.Join(r.Spec.AppliesToWorkloads, ", ")
if appliesTo == "" {
continue
}
} else {
flag := false
for _, w := range r.Spec.AppliesToWorkloads {
if workloadName == w {
flag = true
break
}
}
if !flag {
continue
}
appliesTo = workloadName
func ConvertApplyTo(applyTo []string, workloads []types.Template) []string {
var converted []string
for _, v := range applyTo {
newName, exist := check(v, workloads)
if !exist {
continue
}
// TODO(zzxwill) `Status` might not be proper as I'd like to describe where the trait is, in cluster or in registry
traitList = append(traitList, TraitMeta{
Name: r.Name,
Short: r.ObjectMeta.Annotations["short"],
Definition: r.Spec.Reference.Name,
AppliesTo: appliesTo,
Status: "-",
})
converted = append(converted, newName)
}
return traitList, err
return converted
}
func check(crdname string, workloads []types.Template) (string, bool) {
for _, v := range workloads {
if crdname == v.CrdName {
return v.Name, true
}
}
return "", false
}
func In(l []string, v string) bool {
for _, ll := range l {
if ll == v {
return true
}
}
return false
}

88
pkg/cmd/traits_test.go Normal file
View File

@@ -0,0 +1,88 @@
package cmd
import (
"bytes"
"testing"
"github.com/gosuri/uitable"
"gotest.tools/assert"
"github.com/cloud-native-application/rudrx/api/types"
cmdutil "github.com/cloud-native-application/rudrx/pkg/cmd/util"
)
func Test_printTraitList(t *testing.T) {
traits := []types.Template{
{
Name: "route",
CrdName: "routes.oam.dev",
AppliesTo: []string{"deployments.apps", "clonsets.alibaba"},
},
{
Name: "scaler",
CrdName: "scaler.oam.dev",
AppliesTo: []string{"deployments.apps"},
},
}
workloads := []types.Template{
{
Name: "deployment",
CrdName: "deployments.apps",
},
{
Name: "clonset",
CrdName: "clonsets.alibaba",
},
}
newTable := func() *uitable.Table {
table := uitable.New()
table.MaxColWidth = 60
table.AddRow("NAME", "DEFINITION", "APPLIES TO")
return table
}
tb1 := newTable()
tb1.AddRow("route", "routes.oam.dev", "deployment")
tb1.AddRow("", "", "clonset")
tb1.AddRow("scaler", "scaler.oam.dev", "deployment")
tb2 := newTable()
tb2.AddRow("route", "routes.oam.dev", "deployment")
tb2.AddRow("scaler", "scaler.oam.dev", "deployment")
tb3 := newTable()
tb3.AddRow("route", "routes.oam.dev", "clonset")
cases := map[string]struct {
traits []types.Template
workloads []types.Template
workloadName string
iostream cmdutil.IOStreams
ExpectedString string
}{
"All Workloads": {
traits: traits,
workloads: workloads,
ExpectedString: tb1.String() + "\n",
},
"Specify Workload Name deployment": {
traits: traits,
workloads: workloads,
workloadName: "deployment",
ExpectedString: tb2.String() + "\n",
},
"Specify Workload Name clonset": {
traits: traits,
workloads: workloads,
workloadName: "clonset",
ExpectedString: tb3.String() + "\n",
},
}
for cname, c := range cases {
b := bytes.Buffer{}
iostream := cmdutil.IOStreams{Out: &b}
nn := c.workloadName
printTraitList(c.traits, c.workloads, &nn, iostream)
assert.Equal(t, c.ExpectedString, b.String(), cname)
}
}

View File

@@ -47,9 +47,9 @@ func newRunOptions(ioStreams cmdutil.IOStreams) *runOptions {
return &runOptions{IOStreams: ioStreams}
}
func AddWorkloadPlugins(parentCmd *cobra.Command, c client.Client, ioStreams cmdutil.IOStreams) error {
func AddWorkloadPlugins(parentCmd *cobra.Command, c types.Args, ioStreams cmdutil.IOStreams) error {
dir, _ := system.GetDefinitionDir()
templates, err := plugins.GetWorkloadsFromCluster(context.TODO(), types.DefaultOAMNS, c, dir, nil)
templates, err := plugins.LoadTempFromLocal(filepath.Join(dir, "workloads"))
if err != nil {
return err
}
@@ -57,7 +57,6 @@ func AddWorkloadPlugins(parentCmd *cobra.Command, c client.Client, ioStreams cmd
for _, tmp := range templates {
var name = tmp.Name
o := newRunOptions(ioStreams)
o.client = c
o.Env, _ = GetEnv()
pluginCmd := &cobra.Command{
Use: name + ":run <appname> [args]",
@@ -66,6 +65,11 @@ func AddWorkloadPlugins(parentCmd *cobra.Command, c client.Client, ioStreams cmd
Long: "Run " + name + " workloads",
Example: `vela deployment:run frontend -i nginx:latest`,
RunE: func(cmd *cobra.Command, args []string) error {
newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema})
if err != nil {
return err
}
o.client = newClient
if err := o.Complete(cmd, args, context.TODO()); err != nil {
return err
}

View File

@@ -1,18 +1,19 @@
package cmd
import (
"context"
"fmt"
"path/filepath"
"github.com/cloud-native-application/rudrx/api/types"
"github.com/cloud-native-application/rudrx/pkg/plugins"
"github.com/cloud-native-application/rudrx/pkg/utils/system"
cmdutil "github.com/cloud-native-application/rudrx/pkg/cmd/util"
corev1alpha2 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
"github.com/gosuri/uitable"
"github.com/spf13/cobra"
"sigs.k8s.io/controller-runtime/pkg/client"
)
func NewWorkloadsCommand(c client.Client, ioStreams cmdutil.IOStreams, args []string) *cobra.Command {
ctx := context.Background()
func NewWorkloadsCommand(ioStreams cmdutil.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "workloads",
DisableFlagsInUseLine: true,
@@ -20,52 +21,25 @@ func NewWorkloadsCommand(c client.Client, ioStreams cmdutil.IOStreams, args []st
Long: "List workloads",
Example: `vela workloads`,
RunE: func(cmd *cobra.Command, args []string) error {
return printWorkloadList(ctx, c, ioStreams)
dir, _ := system.GetDefinitionDir()
workloads, err := plugins.LoadTempFromLocal(filepath.Join(dir, "workloads"))
if err != nil {
return err
}
return printWorkloadList(workloads, ioStreams)
},
}
cmd.SetOut(ioStreams.Out)
return cmd
}
func printWorkloadList(ctx context.Context, c client.Client, ioStreams cmdutil.IOStreams) error {
workloadList, err := ListWorkloads(ctx, c)
func printWorkloadList(workloadList []types.Template, ioStreams cmdutil.IOStreams) error {
table := uitable.New()
table.MaxColWidth = 60
if err != nil {
return fmt.Errorf("Listing Trait DefinitionPath hit an issue: %s", err)
}
table.AddRow("NAME", "SHORT", "DEFINITION")
table.AddRow("NAME", "DEFINITION")
for _, r := range workloadList {
table.AddRow(r.Name, r.Short, r.Definition)
table.AddRow(r.Name, r.CrdName)
}
ioStreams.Info(table.String())
return nil
}
type WorkloadData struct {
Name string `json:"name"`
Short string `json:"shot"`
Definition string `json:"definition,omitempty"`
}
func ListWorkloads(ctx context.Context, c client.Client) ([]WorkloadData, error) {
var workloadList []WorkloadData
var workloadDefinitionList corev1alpha2.WorkloadDefinitionList
err := c.List(ctx, &workloadDefinitionList)
for _, r := range workloadDefinitionList.Items {
workloadList = append(workloadList, WorkloadData{
Name: r.Name,
Short: r.ObjectMeta.Annotations["short"],
Definition: r.Spec.Reference.Name,
})
}
return workloadList, err
}

View File

@@ -48,6 +48,7 @@ func GetWorkloadsFromCluster(ctx context.Context, namespace string, c client.Cli
continue
}
tmp.Type = types.TypeWorkload
tmp.CrdName = wd.Spec.Reference.Name
templates = append(templates, tmp)
}
return templates, nil
@@ -69,6 +70,8 @@ func GetTraitsFromCluster(ctx context.Context, namespace string, c client.Client
continue
}
tmp.Type = types.TypeTrait
tmp.AppliesTo = td.Spec.AppliesToWorkloads
tmp.CrdName = td.Spec.Reference.Name
templates = append(templates, tmp)
}
return templates, nil

View File

@@ -30,10 +30,12 @@ var _ = Describe("DefinitionFiles", func() {
Type: cue.StringKind,
},
},
CrdName: "routes.test",
}
deployment := types.Template{
Name: "deployment",
Type: types.TypeWorkload,
Name: "deployment",
Type: types.TypeWorkload,
CrdName: "deployments.testapps",
Parameters: []types.Parameter{
{
Name: "name",

View File

@@ -1,9 +1,13 @@
package plugins
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/cloud-native-application/rudrx/api/types"
)
@@ -23,39 +27,57 @@ func GetDefFromLocal(dir string, defType types.DefinitionType) ([]types.Template
return defs, nil
}
func SinkTemp2Local(templates []types.Template, dir string) error {
func SinkTemp2Local(templates []types.Template, dir string) int {
success := 0
for _, tmp := range templates {
data, err := json.Marshal(tmp)
if err != nil {
return err
fmt.Printf("sync %s err: %v\n", tmp.Name, err)
continue
}
err = ioutil.WriteFile(filepath.Join(dir, tmp.Name), data, 0644)
if err != nil {
return err
fmt.Printf("sync %s err: %v\n", tmp.Name, err)
continue
}
success++
}
return nil
return success
}
func LoadTempFromLocal(dir string) ([]types.Template, error) {
var tmps []types.Template
files, err := ioutil.ReadDir(dir)
if err != nil {
if os.IsNotExist(err) {
fmt.Println("\"no definition files found, use 'vela refresh' to sync from cluster\"")
return nil, nil
}
return nil, err
}
for _, f := range files {
if f.IsDir() {
continue
}
if strings.HasSuffix(f.Name(), ".cue") {
continue
}
data, err := ioutil.ReadFile(filepath.Join(dir, f.Name()))
if err != nil {
return nil, err
fmt.Printf("read file %s err %v\n", f.Name(), err)
continue
}
var tmp types.Template
if err = json.Unmarshal(data, &tmp); err != nil {
return nil, err
decoder := json.NewDecoder(bytes.NewBuffer(data))
decoder.UseNumber()
if err = decoder.Decode(&tmp); err != nil {
fmt.Printf("ignore invalid format file: %s\n", f.Name())
continue
}
tmps = append(tmps, tmp)
}
if len(tmps) == 0 {
fmt.Println("\"no definition files found, use 'vela refresh' to sync from cluster\"")
}
return tmps, nil
}

View File

@@ -49,6 +49,7 @@ func TestLocalSink(t *testing.T) {
tmps []types.Template
Type types.DefinitionType
expDef []types.Template
err error
}{
"Test No Templates": {
dir: "vela-test1",
@@ -86,18 +87,22 @@ func TestLocalSink(t *testing.T) {
},
}
for name, c := range cases {
testInDir(t, name, c.dir, c.tmps, c.expDef, c.Type)
testInDir(t, name, c.dir, c.tmps, c.expDef, c.Type, c.err)
}
}
func testInDir(t *testing.T, casename, dir string, tmps, defexp []types.Template, Type types.DefinitionType) {
func testInDir(t *testing.T, casename, dir string, tmps, defexp []types.Template, Type types.DefinitionType, err1 error) {
err := os.MkdirAll(dir, 0755)
assert.NoError(t, err, casename)
defer os.RemoveAll(dir)
err = SinkTemp2Local(tmps, dir)
assert.NoError(t, err, casename)
number := SinkTemp2Local(tmps, dir)
assert.Equal(t, len(tmps), number)
gottmps, err := LoadTempFromLocal(dir)
assert.NoError(t, err, casename)
if err1 != nil {
assert.Equal(t, err1, err)
} else {
assert.NoError(t, err, casename)
}
assert.Equal(t, tmps, gottmps, casename)
if Type != "" {
gotDef, err := GetDefFromLocal(dir, Type)