mirror of
https://github.com/kubevela/kubevela.git
synced 2026-03-05 11:11:28 +00:00
* #884 complete the trait info when it is applied to * * add unit test for trait print which applied to * * code optimization * code optimization to go standard * summarize all 'applies to' content to *
232 lines
6.7 KiB
Go
232 lines
6.7 KiB
Go
package serverlib
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/oam-dev/kubevela/pkg/appfile/api"
|
|
|
|
"github.com/oam-dev/kubevela/pkg/appfile"
|
|
|
|
"cuelang.org/go/cue"
|
|
plur "github.com/gertd/go-pluralize"
|
|
"github.com/spf13/pflag"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
"github.com/oam-dev/kubevela/apis/types"
|
|
cmdutil "github.com/oam-dev/kubevela/pkg/commands/util"
|
|
"github.com/oam-dev/kubevela/pkg/plugins"
|
|
)
|
|
|
|
// ListTraitDefinitions will list all definition include traits and workloads
|
|
func ListTraitDefinitions(workloadName *string) ([]types.Capability, error) {
|
|
var traitList []types.Capability
|
|
traits, err := plugins.LoadInstalledCapabilityWithType(types.TypeTrait)
|
|
if err != nil {
|
|
return traitList, err
|
|
}
|
|
workloads, err := plugins.LoadInstalledCapabilityWithType(types.TypeWorkload)
|
|
if err != nil {
|
|
return traitList, err
|
|
}
|
|
traitList = convertAllAppliyToList(traits, workloads, workloadName)
|
|
return traitList, nil
|
|
}
|
|
|
|
// GetTraitDefinition will get trait capability with applyTo converted
|
|
func GetTraitDefinition(workloadName *string, traitType string) (types.Capability, error) {
|
|
var traitDef types.Capability
|
|
traitCap, err := plugins.GetInstalledCapabilityWithCapName(types.TypeTrait, traitType)
|
|
if err != nil {
|
|
return traitDef, err
|
|
}
|
|
workloadsCap, err := plugins.LoadInstalledCapabilityWithType(types.TypeWorkload)
|
|
if err != nil {
|
|
return traitDef, err
|
|
}
|
|
traitList := convertAllAppliyToList([]types.Capability{traitCap}, workloadsCap, workloadName)
|
|
if len(traitList) != 1 {
|
|
return traitDef, fmt.Errorf("could not get installed capability by %s", traitType)
|
|
}
|
|
traitDef = traitList[0]
|
|
return traitDef, nil
|
|
}
|
|
|
|
func convertAllAppliyToList(traits []types.Capability, workloads []types.Capability, workloadName *string) []types.Capability {
|
|
var traitList []types.Capability
|
|
for _, t := range traits {
|
|
convertedApplyTo := ConvertApplyTo(t.AppliesTo, workloads)
|
|
if *workloadName != "" {
|
|
if !in(convertedApplyTo, *workloadName) {
|
|
continue
|
|
}
|
|
convertedApplyTo = []string{*workloadName}
|
|
}
|
|
t.AppliesTo = convertedApplyTo
|
|
traitList = append(traitList, t)
|
|
}
|
|
return traitList
|
|
}
|
|
|
|
// ConvertApplyTo will convert applyTo slice to workload capability name if CRD matches
|
|
func ConvertApplyTo(applyTo []string, workloads []types.Capability) []string {
|
|
var converted []string
|
|
if in(applyTo, "*") {
|
|
converted = append(converted, "*")
|
|
} else {
|
|
for _, v := range applyTo {
|
|
newName, exist := check(v, workloads)
|
|
if !exist {
|
|
continue
|
|
}
|
|
if !in(converted, newName) {
|
|
converted = append(converted, newName)
|
|
}
|
|
}
|
|
}
|
|
return converted
|
|
}
|
|
|
|
func check(applyto string, workloads []types.Capability) (string, bool) {
|
|
for _, v := range workloads {
|
|
if Parse(applyto) == v.CrdName || Parse(applyto) == v.Name {
|
|
return v.Name, true
|
|
}
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
func in(l []string, v string) bool {
|
|
for _, ll := range l {
|
|
if ll == v {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Parse will parse applyTo(with format apigroup/Version.Kind) to crd name by just calculate the plural of kind word.
|
|
// TODO we should use discoverymapper instead of calculate plural
|
|
func Parse(applyTo string) string {
|
|
l := strings.Split(applyTo, "/")
|
|
if len(l) != 2 {
|
|
return applyTo
|
|
}
|
|
apigroup, versionKind := l[0], l[1]
|
|
l = strings.Split(versionKind, ".")
|
|
if len(l) != 2 {
|
|
return applyTo
|
|
}
|
|
return plur.NewClient().Plural(strings.ToLower(l[1])) + "." + apigroup
|
|
}
|
|
|
|
// ValidateAndMutateForCore was built in validate and mutate function for core workloads and traits
|
|
func ValidateAndMutateForCore(traitType, workloadName string, flags *pflag.FlagSet, env *types.EnvMeta) error {
|
|
switch traitType {
|
|
case "route":
|
|
domain, _ := flags.GetString("domain")
|
|
if domain == "" {
|
|
if env.Domain == "" {
|
|
return fmt.Errorf("--domain is required if not contain in environment")
|
|
}
|
|
if strings.HasPrefix(env.Domain, "https://") {
|
|
env.Domain = strings.TrimPrefix(env.Domain, "https://")
|
|
}
|
|
if strings.HasPrefix(env.Domain, "http://") {
|
|
env.Domain = strings.TrimPrefix(env.Domain, "http://")
|
|
}
|
|
if err := flags.Set("domain", workloadName+"."+env.Domain); err != nil {
|
|
return fmt.Errorf("set flag for vela-core trait('route') err %w, please make sure your template is right", err)
|
|
}
|
|
}
|
|
issuer, _ := flags.GetString("issuer")
|
|
if issuer == "" && env.Issuer != "" {
|
|
if err := flags.Set("issuer", env.Issuer); err != nil {
|
|
return fmt.Errorf("set flag for vela-core trait('route') err %w, please make sure your template is right", err)
|
|
}
|
|
}
|
|
default:
|
|
// extend other trait here in the future
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AddOrUpdateTrait attach trait to workload
|
|
func AddOrUpdateTrait(env *types.EnvMeta, appName string, componentName string, flagSet *pflag.FlagSet, template types.Capability) (*api.Application, error) {
|
|
err := ValidateAndMutateForCore(template.Name, componentName, flagSet, env)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if appName == "" {
|
|
appName = componentName
|
|
}
|
|
app, err := appfile.LoadApplication(env.Name, appName)
|
|
if err != nil {
|
|
return app, err
|
|
}
|
|
traitAlias := template.Name
|
|
traitData, err := appfile.GetTraitsByType(app, componentName, traitAlias)
|
|
if err != nil {
|
|
return app, err
|
|
}
|
|
for _, v := range template.Parameters {
|
|
name := v.Name
|
|
if v.Alias != "" {
|
|
name = v.Alias
|
|
}
|
|
// nolint:exhaustive
|
|
switch v.Type {
|
|
case cue.IntKind:
|
|
traitData[v.Name], err = flagSet.GetInt64(name)
|
|
case cue.StringKind:
|
|
traitData[v.Name], err = flagSet.GetString(name)
|
|
case cue.BoolKind:
|
|
traitData[v.Name], err = flagSet.GetBool(name)
|
|
case cue.NumberKind, cue.FloatKind:
|
|
traitData[v.Name], err = flagSet.GetFloat64(name)
|
|
default:
|
|
// Currently we don't support get value from complex type
|
|
continue
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get flag(s) \"%s\" err %w", name, err)
|
|
}
|
|
}
|
|
if err = appfile.SetTrait(app, componentName, traitAlias, traitData); err != nil {
|
|
return app, err
|
|
}
|
|
return app, appfile.Save(app, env.Name)
|
|
}
|
|
|
|
// TraitOperationRun will check if it's a stage operation before run
|
|
func TraitOperationRun(ctx context.Context, c client.Client, env *types.EnvMeta, appObj *api.Application,
|
|
staging bool, io cmdutil.IOStreams) (string, error) {
|
|
if staging {
|
|
return "Staging saved", nil
|
|
}
|
|
err := appfile.BuildRun(ctx, appObj, c, env, io)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return "Deployed!", nil
|
|
}
|
|
|
|
// PrepareDetachTrait will detach trait in local AppFile
|
|
func PrepareDetachTrait(envName string, traitType string, componentName string, appName string) (*api.Application, error) {
|
|
var appObj *api.Application
|
|
var err error
|
|
if appName == "" {
|
|
appName = componentName
|
|
}
|
|
if appObj, err = appfile.LoadApplication(envName, appName); err != nil {
|
|
return appObj, err
|
|
}
|
|
|
|
if err = appfile.RemoveTrait(appObj, componentName, traitType); err != nil {
|
|
return appObj, err
|
|
}
|
|
return appObj, appfile.Save(appObj, envName)
|
|
}
|