mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-14 18:10:21 +00:00
refine detach with extended traitdefinition
This commit is contained in:
@@ -6,7 +6,7 @@ spec:
|
||||
appliesToWorkloads:
|
||||
- core.oam.dev/v1alpha2.ContainerizedWorkload
|
||||
definitionRef:
|
||||
name: manualscalertrait.core.oam.dev
|
||||
name: manualscalertraits.core.oam.dev
|
||||
extension:
|
||||
alias: ManualScaler
|
||||
object:
|
||||
|
||||
194
pkg/cmd/bind.go
Normal file
194
pkg/cmd/bind.go
Normal file
@@ -0,0 +1,194 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/cloud-native-application/rudrx/api/types"
|
||||
|
||||
cmdutil "github.com/cloud-native-application/rudrx/pkg/cmd/util"
|
||||
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
|
||||
corev1alpha2 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
type commandOptions struct {
|
||||
Env *EnvMeta
|
||||
Template types.Template
|
||||
Component corev1alpha2.Component
|
||||
AppConfig corev1alpha2.ApplicationConfiguration
|
||||
Client client.Client
|
||||
cmdutil.IOStreams
|
||||
}
|
||||
|
||||
func NewCommandOptions(ioStreams cmdutil.IOStreams) *commandOptions {
|
||||
return &commandOptions{IOStreams: ioStreams}
|
||||
}
|
||||
|
||||
func NewBindCommand(f cmdutil.Factory, c client.Client, ioStreams cmdutil.IOStreams, args []string) *cobra.Command {
|
||||
|
||||
var err error
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
o := NewCommandOptions(ioStreams)
|
||||
o.Env, err = GetEnv()
|
||||
if err != nil {
|
||||
fmt.Printf("Listing trait definitions hit an issue: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
o.Client = c
|
||||
cmd := &cobra.Command{
|
||||
Use: "bind APPLICATION-NAME TRAIT-NAME [FLAG]",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: "Attach a trait to a component",
|
||||
Long: "Attach a trait to a component.",
|
||||
Example: `rudr bind frontend scaler --max=5`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args, ctx))
|
||||
cmdutil.CheckErr(o.Run(f, cmd, ctx))
|
||||
},
|
||||
}
|
||||
cmd.SetArgs(args)
|
||||
var traitDefinitions corev1alpha2.TraitDefinitionList
|
||||
err = c.List(ctx, &traitDefinitions)
|
||||
if err != nil {
|
||||
fmt.Println("Listing trait definitions hit an issue:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for _, t := range traitDefinitions.Items {
|
||||
var traitTemplate types.Template
|
||||
traitTemplate, err := types.ConvertTemplateJson2Object(t.Spec.Extension)
|
||||
if err != nil {
|
||||
fmt.Printf("extract template from traitDefinition %v err: %v, ignore it\n", t.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, p := range traitTemplate.Parameters {
|
||||
if p.Type == "int" {
|
||||
v, err := strconv.Atoi(p.Default)
|
||||
if err != nil {
|
||||
fmt.Println("Parameters type is wrong: ", err, ".Please report this to OAM maintainer, thanks.")
|
||||
os.Exit(1)
|
||||
}
|
||||
cmd.PersistentFlags().Int(p.Name, v, p.Usage)
|
||||
} else {
|
||||
cmd.PersistentFlags().String(p.Name, p.Default, p.Usage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *commandOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string, ctx context.Context) error {
|
||||
argsLength := len(args)
|
||||
var componentName string
|
||||
|
||||
c := o.Client
|
||||
|
||||
namespace := o.Env.Namespace
|
||||
|
||||
if argsLength == 0 {
|
||||
return errors.New("please append the name of an application. Use `rudr bind -h` for more detailed information")
|
||||
} else if argsLength <= 2 {
|
||||
componentName = args[0]
|
||||
err := c.Get(ctx, client.ObjectKey{Namespace: o.Env.Namespace, Name: componentName}, &o.AppConfig)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var component corev1alpha2.Component
|
||||
err = c.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, &component)
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("%s. Please choose an existed component name.", err)
|
||||
cmdutil.PrintErrorMessage(errMsg, 1)
|
||||
}
|
||||
|
||||
// Retrieve all traits which can be used for the following 1) help and 2) validating
|
||||
traitList, _ := RetrieveTraitsByWorkload(ctx, o.Client, namespace, "")
|
||||
//if err != nil {
|
||||
// errMsg := fmt.Sprintf("List available traits hit an issue: %s", err)
|
||||
// cmdutil.PrintErrorMessage(errMsg, 1)
|
||||
//}
|
||||
|
||||
switch argsLength {
|
||||
case 1:
|
||||
// Validate component and suggest trait
|
||||
fmt.Print("Error: No trait specified.\nPlease choose a trait: ")
|
||||
for _, t := range traitList {
|
||||
n := t.Short
|
||||
if n == "" {
|
||||
n = t.Name
|
||||
}
|
||||
fmt.Print(n, " ")
|
||||
}
|
||||
os.Exit(1)
|
||||
|
||||
case 2:
|
||||
// validate trait
|
||||
traitName := args[1]
|
||||
traitLongName, _, _ := cmdutil.GetTraitNameAliasKind(ctx, c, namespace, traitName)
|
||||
|
||||
traitDefinition, err := cmdutil.GetTraitDefinitionByName(ctx, c, namespace, traitLongName)
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("trait name [%s] is not valid, please try again", traitName)
|
||||
cmdutil.PrintErrorMessage(errMsg, 1)
|
||||
}
|
||||
|
||||
traitTemplate, err := types.ConvertTemplateJson2Object(traitDefinition.Spec.Extension)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("attaching the trait hit an issue: %s", err)
|
||||
}
|
||||
|
||||
pvd := fieldpath.Pave(traitTemplate.Object)
|
||||
for _, v := range traitTemplate.Parameters {
|
||||
flagSet := cmd.Flag(v.Name)
|
||||
for _, path := range v.FieldPaths {
|
||||
fValue := flagSet.Value.String()
|
||||
if v.Type == "int" {
|
||||
portValue, _ := strconv.ParseFloat(fValue, 64)
|
||||
pvd.SetNumber(path, portValue)
|
||||
continue
|
||||
}
|
||||
pvd.SetString(path, fValue)
|
||||
}
|
||||
}
|
||||
|
||||
// metadata.name needs to be in lower case.
|
||||
pvd.SetString("metadata.name", strings.ToLower(traitName))
|
||||
|
||||
var t corev1alpha2.ComponentTrait
|
||||
t.Trait.Object = &unstructured.Unstructured{Object: pvd.UnstructuredContent()}
|
||||
o.Component.Name = componentName
|
||||
o.AppConfig.Spec.Components = []corev1alpha2.ApplicationConfigurationComponent{{
|
||||
ComponentName: componentName,
|
||||
Traits: []corev1alpha2.ComponentTrait{t},
|
||||
}}
|
||||
}
|
||||
} else {
|
||||
cmdutil.PrintErrorMessage("Unknown command is specified, please check and try again.", 1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *commandOptions) Run(f cmdutil.Factory, cmd *cobra.Command, ctx context.Context) error {
|
||||
fmt.Println("Applying trait for component", o.Component.Name)
|
||||
c := o.Client
|
||||
err := c.Update(ctx, &o.AppConfig)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("Applying trait hit an issue: %s", err)
|
||||
cmdutil.PrintErrorMessage(msg, 1)
|
||||
}
|
||||
fmt.Println("Succeeded!")
|
||||
return nil
|
||||
}
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/cloud-native-application/rudrx/api/v1alpha2"
|
||||
"github.com/cloud-native-application/rudrx/api/types"
|
||||
|
||||
cmdutil "github.com/cloud-native-application/rudrx/pkg/cmd/util"
|
||||
corev1alpha2 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -15,8 +15,8 @@ import (
|
||||
)
|
||||
|
||||
type detachCommandOptions struct {
|
||||
Namespace string
|
||||
Template v1alpha2.Template
|
||||
Env *EnvMeta
|
||||
Template types.Template
|
||||
Component corev1alpha2.Component
|
||||
AppConfig corev1alpha2.ApplicationConfiguration
|
||||
Client client.Client
|
||||
@@ -29,8 +29,14 @@ func NewDetachCommandOptions(ioStreams cmdutil.IOStreams) *detachCommandOptions
|
||||
|
||||
func NewDetachCommand(f cmdutil.Factory, c client.Client, ioStreams cmdutil.IOStreams) *cobra.Command {
|
||||
ctx := context.Background()
|
||||
|
||||
o := NewDetachCommandOptions(ioStreams)
|
||||
o.Client = c
|
||||
var err error
|
||||
o.Env, err = GetEnv()
|
||||
if err != nil {
|
||||
fmt.Printf("Listing trait definitions hit an issue: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "detach APPLICATION-NAME TRAIT-NAME",
|
||||
@@ -38,48 +44,21 @@ func NewDetachCommand(f cmdutil.Factory, c client.Client, ioStreams cmdutil.IOSt
|
||||
Long: "detach the trait from the application",
|
||||
Example: `rudr detach frontend ManualScaler`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
namespace := cmd.Flag("namespace").Value.String()
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args, ctx, namespace))
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args, ctx))
|
||||
cmdutil.CheckErr(o.Apply(f, cmd, ctx))
|
||||
},
|
||||
}
|
||||
|
||||
var traitDefinitions corev1alpha2.TraitDefinitionList
|
||||
err := c.List(ctx, &traitDefinitions)
|
||||
if err != nil {
|
||||
if err = c.List(ctx, &traitDefinitions); err != nil {
|
||||
fmt.Println("Listing trait definitions hit an issue:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for _, t := range traitDefinitions.Items {
|
||||
template := t.ObjectMeta.Annotations["defatultTemplateRef"]
|
||||
|
||||
var traitTemplate v1alpha2.Template
|
||||
err := c.Get(ctx, client.ObjectKey{Namespace: "default", Name: template}, &traitTemplate)
|
||||
if err != nil {
|
||||
fmt.Println("Listing trait template hit an issue:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
o.Client = c
|
||||
|
||||
for _, p := range traitTemplate.Spec.Parameters {
|
||||
if p.Type == "int" {
|
||||
v, err := strconv.Atoi(p.Default)
|
||||
if err != nil {
|
||||
fmt.Println("Parameters type is wrong: ", err, ".Please report this to OAM maintainer, thanks.")
|
||||
}
|
||||
cmd.PersistentFlags().Int(p.Name, v, p.Usage)
|
||||
} else {
|
||||
cmd.PersistentFlags().String(p.Name, p.Default, p.Usage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *detachCommandOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string, ctx context.Context, namespace string) error {
|
||||
func (o *detachCommandOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string, ctx context.Context) error {
|
||||
namespace := o.Env.Namespace
|
||||
argsLength := len(args)
|
||||
var applicationName string
|
||||
|
||||
@@ -88,14 +67,9 @@ func (o *detachCommandOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, a
|
||||
if argsLength == 0 {
|
||||
cmdutil.PrintErrorMessage("please append an application name", 1)
|
||||
} else if argsLength <= 2 {
|
||||
if namespace == "" {
|
||||
namespace = "default"
|
||||
}
|
||||
|
||||
applicationName = args[0]
|
||||
// Check the validity of the specified application name
|
||||
err := c.Get(ctx, client.ObjectKey{Namespace: namespace, Name: applicationName}, &o.AppConfig)
|
||||
if err != nil {
|
||||
if err := c.Get(ctx, client.ObjectKey{Namespace: namespace, Name: applicationName}, &o.AppConfig); err != nil {
|
||||
fmt.Print("Hint: please choose an existed application.")
|
||||
return err
|
||||
}
|
||||
@@ -117,15 +91,14 @@ func (o *detachCommandOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, a
|
||||
if len(traitNames) != 0 {
|
||||
fmt.Printf(" Please choose the trait you would like to deatch: %s", strings.Join(traitAlias, ","))
|
||||
}
|
||||
return err
|
||||
case 2:
|
||||
// validate trait
|
||||
traitName := args[1]
|
||||
|
||||
_, _, tKind := cmdutil.GetTraitNameAliasKind(ctx, c, namespace, traitName)
|
||||
if tKind == "" {
|
||||
fmt.Printf("Error: trait name `%s` is NOT valid, please try again.", traitName)
|
||||
return nil
|
||||
tName, tAlias, tKind := cmdutil.GetTraitNameAliasKind(ctx, c, namespace, traitName)
|
||||
if tName == "" && tAlias == "" && tKind == "" {
|
||||
errMsg := fmt.Sprintf("Error: trait name `%s` is NOT valid, please try again.", traitName)
|
||||
cmdutil.PrintErrorMessage(errMsg, 1)
|
||||
}
|
||||
|
||||
traits := o.AppConfig.Spec.Components[0].Traits
|
||||
@@ -136,7 +109,6 @@ func (o *detachCommandOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, a
|
||||
i--
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
cmdutil.PrintErrorMessage("Unknown command is specified, please check and try again.", 1)
|
||||
@@ -149,11 +121,9 @@ func (o *detachCommandOptions) Apply(f cmdutil.Factory, cmd *cobra.Command, ctx
|
||||
c := o.Client
|
||||
err := c.Update(ctx, &o.AppConfig)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("Applying trait hit an issue: %s", err)
|
||||
msg := fmt.Sprintf("Detaching the trait hit an issue: %s", err)
|
||||
cmdutil.PrintErrorMessage(msg, 1)
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("Succeeded!")
|
||||
fmt.Println(msg)
|
||||
fmt.Println("Succeeded!")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ import (
|
||||
|
||||
"github.com/cloud-native-application/rudrx/api/types"
|
||||
|
||||
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
|
||||
corev1alpha2 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
@@ -94,8 +92,9 @@ func ListTraitDefinitionsByApplicationConfiguration(app corev1alpha2.Application
|
||||
var traitDefinitionList []corev1alpha2.TraitDefinition
|
||||
for _, t := range app.Spec.Components[0].Traits {
|
||||
var trait corev1alpha2.TraitDefinition
|
||||
json.Unmarshal(t.Trait.Raw, &trait)
|
||||
traitDefinitionList = append(traitDefinitionList, trait)
|
||||
if err := json.Unmarshal(t.Trait.Raw, &trait); err == nil {
|
||||
traitDefinitionList = append(traitDefinitionList, trait)
|
||||
}
|
||||
}
|
||||
return traitDefinitionList
|
||||
}
|
||||
@@ -133,18 +132,19 @@ func RetrieveApplicationsByName(ctx context.Context, c client.Client, applicatio
|
||||
}
|
||||
|
||||
var workload corev1alpha2.WorkloadDefinition
|
||||
json.Unmarshal(component.Spec.Workload.Raw, &workload)
|
||||
workloadName := workload.TypeMeta.Kind
|
||||
if err := json.Unmarshal(component.Spec.Workload.Raw, &workload); err == nil {
|
||||
workloadName := workload.TypeMeta.Kind
|
||||
|
||||
traitNames := GetTraitNamesByApplicationConfiguration(a)
|
||||
traitNames := GetTraitNamesByApplicationConfiguration(a)
|
||||
|
||||
applicationMetaList = append(applicationMetaList, ApplicationMeta{
|
||||
Name: a.Name,
|
||||
Workload: workloadName,
|
||||
Traits: traitNames,
|
||||
Status: string(a.Status.Conditions[0].Status),
|
||||
CreatedTime: a.ObjectMeta.CreationTimestamp.String(),
|
||||
})
|
||||
applicationMetaList = append(applicationMetaList, ApplicationMeta{
|
||||
Name: a.Name,
|
||||
Workload: workloadName,
|
||||
Traits: traitNames,
|
||||
Status: string(a.Status.Conditions[0].Status),
|
||||
CreatedTime: a.ObjectMeta.CreationTimestamp.String(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return applicationMetaList, nil
|
||||
@@ -222,38 +222,23 @@ func GetTraitNameAliasKind(ctx context.Context, c client.Client, namespace strin
|
||||
if err == nil {
|
||||
template, err := types.ConvertTemplateJson2Object(t.Spec.Extension)
|
||||
if err == nil {
|
||||
tName, tAlias = t.Name, template.Alias
|
||||
tName, tAlias = t.Spec.Reference.Name, template.Alias
|
||||
tKind = fmt.Sprintf("%v", template.Object["kind"])
|
||||
}
|
||||
} else {
|
||||
t, err := GetTraitDefinitionByAlias(ctx, c, name)
|
||||
if err == nil {
|
||||
template, err := types.ConvertTemplateJson2Object(t.Spec.Extension)
|
||||
if err == nil {
|
||||
tName, tAlias = t.Name, template.Alias
|
||||
tName, tAlias = t.Spec.Reference.Name, template.Alias
|
||||
tKind = fmt.Sprintf("%v", template.Object["kind"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if tName == "" {
|
||||
tKind = name
|
||||
} else {
|
||||
tKind = GetCRDKind(ctx, c, namespace, tName)
|
||||
}
|
||||
|
||||
return tName, tAlias, tKind
|
||||
}
|
||||
|
||||
func GetCRDByName(ctx context.Context, c client.Client, namespace string, name string) v1.CustomResourceDefinition {
|
||||
var crd v1.CustomResourceDefinition
|
||||
c.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, &crd)
|
||||
return crd
|
||||
}
|
||||
|
||||
func GetCRDKind(ctx context.Context, c client.Client, namespace string, name string) string {
|
||||
crd := GetCRDByName(ctx, c, namespace, name)
|
||||
return crd.Spec.Names.Kind
|
||||
}
|
||||
|
||||
func GetWorkloadNameAliasKind(ctx context.Context, c client.Client, namespace string, workloadName string) (string, string, string) {
|
||||
var name, alias, kind string
|
||||
|
||||
@@ -288,13 +273,12 @@ func GetWorkloadDefinitionByName(ctx context.Context, c client.Client, namespace
|
||||
func GetWorkloadDefinitionByAlias(ctx context.Context, c client.Client, traitAlias string) (corev1alpha2.WorkloadDefinition, error) {
|
||||
var workloadDefinitionList corev1alpha2.WorkloadDefinitionList
|
||||
var workloadDefinition corev1alpha2.WorkloadDefinition
|
||||
// TODO(zzxwill) Need to check return error
|
||||
c.List(ctx, &workloadDefinitionList)
|
||||
|
||||
for _, t := range workloadDefinitionList.Items {
|
||||
if strings.EqualFold(t.ObjectMeta.Annotations["short"], traitAlias) {
|
||||
workloadDefinition = t
|
||||
break
|
||||
if err := c.List(ctx, &workloadDefinitionList); err == nil {
|
||||
for _, t := range workloadDefinitionList.Items {
|
||||
if strings.EqualFold(t.ObjectMeta.Annotations["short"], traitAlias) {
|
||||
workloadDefinition = t
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,10 +46,7 @@ func GetKubeConfig() string {
|
||||
func IsNamespaceExist(c client.Client, namespace string) bool {
|
||||
var ns corev1.Namespace
|
||||
err := c.Get(context.Background(), types.NamespacedName{Name: namespace}, &ns)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func NewNamespace(c client.Client, namespace string) error {
|
||||
|
||||
Reference in New Issue
Block a user