diff --git a/api/types/capability.go b/api/types/capability.go index 9b2aa3949..c8360d82d 100644 --- a/api/types/capability.go +++ b/api/types/capability.go @@ -28,17 +28,18 @@ import ( ) type Source struct { - RepoName string `json:"repoName"` + RepoName string `json:"repoName"` + ChartName string `json:"chartName,omitempty"` } // Capability defines the content of a capability type Capability struct { - Name string `json:"name"` - Type DefinitionType `json:"type"` - CueTemplate string `json:"template,omitempty"` - Parameters []Parameter `json:"parameters,omitempty"` - DefinitionPath string `json:"definition"` - CrdName string `json:"crdName,omitempty"` + Name string `json:"name"` + Type CapType `json:"type"` + CueTemplate 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"` @@ -56,15 +57,15 @@ type Chart struct { } type Installation struct { - Helm []Chart `json:"helm"` + Helm Chart `json:"helm"` } -type DefinitionType string +type CapType string const ( - TypeWorkload DefinitionType = "workload" - TypeTrait DefinitionType = "trait" - TypeScope DefinitionType = "scope" + TypeWorkload CapType = "workload" + TypeTrait CapType = "trait" + TypeScope CapType = "scope" ) type Parameter struct { diff --git a/cmd/vela/main.go b/cmd/vela/main.go index 16265334e..a512aaae8 100644 --- a/cmd/vela/main.go +++ b/cmd/vela/main.go @@ -156,6 +156,7 @@ func PrintHelpByTag(cmd *cobra.Command, all []*cobra.Command, tag string) { } } cmd.Println(table.String()) + cmd.Println(" ") cmd.Println() } diff --git a/config/samples/traits/route.yaml b/config/samples/traits/route.yaml index 85908f066..ecec89522 100644 --- a/config/samples/traits/route.yaml +++ b/config/samples/traits/route.yaml @@ -15,9 +15,10 @@ spec: extension: install: helm: - - repo: stable - name: nginx-ingress - version: 1.41.2 + repo: stable + name: nginx-ingress + url: https://kubernetes-charts.storage.googleapis.com/ + version: 1.41.2 template: | #Template: { apiVersion: "networking.k8s.io/v1beta1" diff --git a/go.mod b/go.mod index efeda6b7e..048c7b2ca 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( cuelang.org/go v0.2.2 github.com/crossplane/crossplane-runtime v0.8.0 github.com/crossplane/oam-kubernetes-runtime v0.0.8 + github.com/gertd/go-pluralize v0.1.7 github.com/ghodss/yaml v1.0.0 github.com/gin-gonic/gin v1.6.3 github.com/google/go-github/v32 v32.1.0 @@ -17,8 +18,8 @@ require ( github.com/spf13/cobra v1.0.0 github.com/stretchr/testify v1.6.1 go.uber.org/zap v1.10.0 - gopkg.in/natefinch/lumberjack.v2 v2.0.0 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 + gopkg.in/natefinch/lumberjack.v2 v2.0.0 gotest.tools v2.2.0+incompatible helm.sh/helm/v3 v3.2.4 k8s.io/api v0.18.6 diff --git a/pkg/cmd/capability.go b/pkg/cmd/capability.go index f6a8a8d21..6764244fa 100644 --- a/pkg/cmd/capability.go +++ b/pkg/cmd/capability.go @@ -5,9 +5,14 @@ import ( "errors" "fmt" "io/ioutil" + "os" "path/filepath" "strings" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "k8s.io/apimachinery/pkg/runtime" + apierrors "k8s.io/apimachinery/pkg/api/errors" "github.com/ghodss/yaml" @@ -33,6 +38,7 @@ func CapabilityCommandGroup(parentCmd *cobra.Command, c types.Args, ioStream cmd NewCapListCommand(ioStream), NewCapCenterSyncCommand(ioStream), NewCapAddCommand(c, ioStream), + NewCapRemoveCommand(c, ioStream), ) } @@ -70,7 +76,13 @@ func NewCapCenterConfigCommand(ioStreams cmdutil.IOStreams) *cobra.Command { if err = plugins.StoreRepos(repos); err != nil { return err } - ioStreams.Info(fmt.Sprintf("Successfully configured capability center: %s, please use 'vela cap:center:sync %s' to sync capabilities", args[0], args[0])) + ioStreams.Info(fmt.Sprintf("Successfully configured capability center: %s, start to sync from remote", args[0])) + client, err := plugins.NewCenterClient(context.Background(), config.Name, config.Address, config.Token) + err = client.SyncCapabilityFromCenter() + if err != nil { + return err + } + ioStreams.Info("sync finished") return nil }, Annotations: map[string]string{ @@ -112,6 +124,38 @@ func NewCapAddCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command return cmd } +func NewCapRemoveCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Use: "cap:remove ", + Short: "Remove capability from cluster", + Long: "Remove capability from cluster", + Example: `vela cap:remove route`, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("you must specify for capability you want to remove") + } + newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema}) + if err != nil { + return err + } + name := args[0] + if strings.Contains(name, "/") { + l := strings.Split(name, "/") + if len(l) > 2 { + return fmt.Errorf("invalid format '%s', you can't contain more than one / in name", name) + } + name = l[1] + } + return RemoveCapability(newClient, name, ioStreams) + }, + Annotations: map[string]string{ + types.TagCommandType: types.TypeOthers, + }, + } + cmd.PersistentFlags().StringP("token", "t", "", "Github Repo token") + return cmd +} + func NewCapCenterSyncCommand(ioStreams cmdutil.IOStreams) *cobra.Command { cmd := &cobra.Command{ Use: "cap:center:sync [centerName]", @@ -151,6 +195,7 @@ func NewCapCenterSyncCommand(ioStreams cmdutil.IOStreams) *cobra.Command { return err } } + ioStreams.Info("sync finished") return nil }, Annotations: map[string]string{ @@ -176,7 +221,7 @@ func NewCapListCommand(ioStreams cmdutil.IOStreams) *cobra.Command { return err } table := uitable.New() - table.AddRow("NAME", "TYPE", "DEFINITION", "STATUS", "APPLIES-TO") + table.AddRow("NAME", "CENTER", "TYPE", "DEFINITION", "STATUS", "APPLIES-TO") if repoName != "" { if err = ListCenterCapabilities(table, filepath.Join(dir, repoName), ioStreams); err != nil { return err @@ -188,7 +233,6 @@ func NewCapListCommand(ioStreams cmdutil.IOStreams) *cobra.Command { if err != nil { return err } - for _, dd := range dirs { if !dd.IsDir() { continue @@ -207,6 +251,53 @@ func NewCapListCommand(ioStreams cmdutil.IOStreams) *cobra.Command { return cmd } +func RemoveCapability(client client.Client, capabilityName string, ioStreams cmdutil.IOStreams) error { + // TODO(wonderflow): make sure no apps is using this capability + caps, err := plugins.LoadAllInstalledCapability() + if err != nil { + return err + } + for _, w := range caps { + if w.Name == capabilityName { + return UninstallCap(client, w, ioStreams) + } + } + return errors.New(capabilityName + " not exist") +} + +func UninstallCap(client client.Client, cap types.Capability, ioStreams cmdutil.IOStreams) error { + // 1. Remove WorkloadDefinition or TraitDefinition + ctx := context.Background() + var obj runtime.Object + switch cap.Type { + case types.TypeTrait: + obj = &v1alpha2.TraitDefinition{ObjectMeta: v1.ObjectMeta{Name: cap.CrdName, Namespace: types.DefaultOAMNS}} + case types.TypeWorkload: + obj = &v1alpha2.WorkloadDefinition{ObjectMeta: v1.ObjectMeta{Name: cap.CrdName, Namespace: types.DefaultOAMNS}} + } + if err := client.Delete(ctx, obj); err != nil { + return err + } + + if cap.Install != nil && cap.Install.Helm.Name != "" { + // 2. Remove Helm chart if there is + if err := HelmUninstall(ioStreams, cap.Install.Helm.Name, cap.Name); err != nil { + return err + } + } + + // 3. Remove local capability file + capdir, _ := system.GetCapabilityDir() + switch cap.Type { + case types.TypeTrait: + return os.Remove(filepath.Join(capdir, "traits", cap.Name)) + case types.TypeWorkload: + return os.Remove(filepath.Join(capdir, "workloads", cap.Name)) + } + ioStreams.Infof("%s removed successfully", cap.Name) + return nil +} + func InstallCapability(client client.Client, centerName, capabilityName string, ioStreams cmdutil.IOStreams) error { dir, _ := system.GetCapCenterDir() repoDir := filepath.Join(dir, centerName) @@ -218,7 +309,6 @@ func InstallCapability(client client.Client, centerName, capabilityName string, defDir, _ := system.GetCapabilityDir() switch tp.Type { case types.TypeWorkload: - defDir = filepath.Join(defDir, "workloads") var wd v1alpha2.WorkloadDefinition workloadData, err := ioutil.ReadFile(filepath.Join(repoDir, tp.CrdName+".yaml")) if err != nil { @@ -230,6 +320,7 @@ func InstallCapability(client client.Client, centerName, capabilityName string, wd.Namespace = types.DefaultOAMNS ioStreams.Info("Installing workload capability " + wd.Name) if tp.Install != nil { + tp.Source.ChartName = tp.Install.Helm.Name if err = InstallHelmChart(ioStreams, tp.Install.Helm); err != nil { return err } @@ -238,7 +329,6 @@ func InstallCapability(client client.Client, centerName, capabilityName string, return err } case types.TypeTrait: - defDir = filepath.Join(defDir, "traits") var td v1alpha2.TraitDefinition traitdata, err := ioutil.ReadFile(filepath.Join(repoDir, tp.CrdName+".yaml")) if err != nil { @@ -250,6 +340,7 @@ func InstallCapability(client client.Client, centerName, capabilityName string, td.Namespace = types.DefaultOAMNS ioStreams.Info("Installing trait capability " + td.Name) if tp.Install != nil { + tp.Source.ChartName = tp.Install.Helm.Name if err = InstallHelmChart(ioStreams, tp.Install.Helm); err != nil { return err } @@ -268,19 +359,14 @@ func InstallCapability(client client.Client, centerName, capabilityName string, return nil } -func InstallHelmChart(ioStreams cmdutil.IOStreams, charts []types.Chart) error { - for _, c := range charts { - if err := HelmInstall(ioStreams, c.Repo, c.URl, c.Name, c.Version, c.Name); err != nil { - return err - } - } - return nil +func InstallHelmChart(ioStreams cmdutil.IOStreams, c types.Chart) error { + return HelmInstall(ioStreams, c.Repo, c.URl, c.Name, c.Version, c.Name) } func GetSyncedCapabilities(repoName, addonName string) (types.Capability, error) { dir, _ := system.GetCapCenterDir() repoDir := filepath.Join(dir, repoName) - templates, err := plugins.LoadCapabilityFromLocal(repoDir) + templates, err := plugins.LoadCapabilityFromSyncedCenter(repoDir) if err != nil { return types.Capability{}, err } @@ -293,7 +379,7 @@ func GetSyncedCapabilities(repoName, addonName string) (types.Capability, error) } func ListCenterCapabilities(table *uitable.Table, repoDir string, ioStreams cmdutil.IOStreams) error { - templates, err := plugins.LoadCapabilityFromLocal(repoDir) + templates, err := plugins.LoadCapabilityFromSyncedCenter(repoDir) if err != nil { return err } @@ -301,23 +387,31 @@ func ListCenterCapabilities(table *uitable.Table, repoDir string, ioStreams cmdu return nil } baseDir := filepath.Base(repoDir) + workloads := GatherWorkloads(templates) for _, p := range templates { status := CheckInstallStatus(baseDir, p) - table.AddRow(baseDir+"/"+p.Name, p.Type, p.Type, status, p.AppliesTo) + convertedApplyTo := ConvertApplyTo(p.AppliesTo, workloads) + table.AddRow(p.Name, baseDir, p.Type, p.CrdName, status, convertedApplyTo) } return nil } +func GatherWorkloads(templates []types.Capability) []types.Capability { + workloads, err := plugins.LoadInstalledCapabilityWithType(types.TypeWorkload) + if err != nil { + workloads = make([]types.Capability, 0) + } + for _, t := range templates { + if t.Type == types.TypeWorkload { + workloads = append(workloads, t) + } + } + return workloads +} + func CheckInstallStatus(repoName string, tmp types.Capability) string { var status = "uninstalled" - dir, _ := system.GetCapabilityDir() - switch tmp.Type { - case types.TypeTrait: - dir = filepath.Join(dir, "traits") - case types.TypeWorkload: - dir = filepath.Join(dir, "workloads") - } - installed, _ := plugins.LoadTempFromLocal(dir) + installed, _ := plugins.LoadInstalledCapabilityWithType(tmp.Type) for _, i := range installed { if i.Source != nil && i.Source.RepoName == repoName && i.Name == tmp.Name && i.CrdName == tmp.CrdName { return "installed" diff --git a/pkg/cmd/refresh.go b/pkg/cmd/refresh.go index 0face6a27..5d47560b2 100644 --- a/pkg/cmd/refresh.go +++ b/pkg/cmd/refresh.go @@ -2,7 +2,6 @@ package cmd import ( "context" - "path/filepath" "github.com/cloud-native-application/rudrx/api/types" cmdutil "github.com/cloud-native-application/rudrx/pkg/cmd/util" @@ -43,10 +42,8 @@ func RefreshDefinitions(ctx context.Context, c client.Client, ioStreams cmdutil. if err != nil { return err } - workloadDir := filepath.Join(dir, "workloads") - system.StatAndCreate(workloadDir) - ioStreams.Infof("get %d workload definitions from cluster, syncing to %s...", len(templates), workloadDir) - successNum := plugins.SinkTemp2Local(templates, workloadDir) + ioStreams.Infof("get %d workload definitions from cluster, syncing...", len(templates)) + successNum := plugins.SinkTemp2Local(templates, dir) ioStreams.Infof("%d workload definitions successfully synced\n", successNum) ioStreams.Info("syncing trait definitions from cluster...") @@ -54,10 +51,8 @@ func RefreshDefinitions(ctx context.Context, c client.Client, ioStreams cmdutil. if err != nil { return err } - traitDir := filepath.Join(dir, "traits") - system.StatAndCreate(traitDir) - ioStreams.Infof("get %d trait definitions from cluster, syncing to %s...", len(templates), traitDir) - successNum = plugins.SinkTemp2Local(templates, traitDir) + ioStreams.Infof("get %d trait definitions from cluster, syncing...", len(templates)) + successNum = plugins.SinkTemp2Local(templates, dir) ioStreams.Infof("%d trait definitions successfully synced\n", successNum) return nil } diff --git a/pkg/cmd/system.go b/pkg/cmd/system.go index 11e0dc84f..bf0ba1d87 100644 --- a/pkg/cmd/system.go +++ b/pkg/cmd/system.go @@ -220,9 +220,24 @@ func HelmInstall(ioStreams cmdutil.IOStreams, repoName, repoUrl, chartName, vers return nil } +func HelmUninstall(ioStreams cmdutil.IOStreams, chartName, releaseName string) error { + if !IsHelmReleaseRunning(releaseName, chartName, ioStreams) { + return nil + } + uninstall, err := NewHelmUninstall() + if err != nil { + return err + } + _, err = uninstall.Run(releaseName) + if err != nil { + return err + } + ioStreams.Infof("Successfully removed %s with release name %s\n", chartName, releaseName) + return nil +} + func NewHelmInstall(version, releaseName string, ioStreams cmdutil.IOStreams) (*action.Install, error) { actionConfig := new(action.Configuration) - if err := actionConfig.Init( kube.GetConfig(cmdutil.GetKubeConfig(), "", types.DefaultOAMNS), types.DefaultOAMNS, @@ -233,12 +248,25 @@ func NewHelmInstall(version, releaseName string, ioStreams cmdutil.IOStreams) (* } client := action.NewInstall(actionConfig) - client.Namespace = types.DefaultOAMNS client.ReleaseName = releaseName client.Version = version return client, nil } +func NewHelmUninstall() (*action.Uninstall, error) { + actionConfig := new(action.Configuration) + + if err := actionConfig.Init( + kube.GetConfig(cmdutil.GetKubeConfig(), "", types.DefaultOAMNS), + types.DefaultOAMNS, + os.Getenv("HELM_DRIVER"), + debug, + ); err != nil { + return nil, err + } + return action.NewUninstall(actionConfig), nil +} + func debug(format string, v ...interface{}) { if settings.Debug { format = fmt.Sprintf("[debug] %s\n", format) diff --git a/pkg/cmd/trait_bind.go b/pkg/cmd/trait_bind.go index 52a0f5a9a..695483487 100644 --- a/pkg/cmd/trait_bind.go +++ b/pkg/cmd/trait_bind.go @@ -5,7 +5,6 @@ import ( "encoding/json" "errors" "fmt" - "path/filepath" "strconv" "strings" @@ -13,8 +12,6 @@ import ( "cuelang.org/go/cue" - "github.com/cloud-native-application/rudrx/pkg/utils/system" - "k8s.io/apimachinery/pkg/runtime" "github.com/cloud-native-application/rudrx/pkg/plugins" @@ -45,8 +42,7 @@ func NewCommandOptions(ioStreams cmdutil.IOStreams) *commandOptions { } func AddTraitCommands(parentCmd *cobra.Command, c types.Args, ioStreams cmdutil.IOStreams) error { - dir, _ := system.GetCapabilityDir() - templates, err := plugins.LoadTempFromLocal(filepath.Join(dir, "traits")) + templates, err := plugins.LoadInstalledCapabilityWithType(types.TypeTrait) if err != nil { return err } @@ -172,8 +168,7 @@ func (o *commandOptions) Complete(cmd *cobra.Command, args []string, ctx context } func AddTraitDetachCommands(parentCmd *cobra.Command, c types.Args, ioStreams cmdutil.IOStreams) error { - dir, _ := system.GetCapabilityDir() - templates, err := plugins.LoadTempFromLocal(filepath.Join(dir, "traits")) + templates, err := plugins.LoadInstalledCapabilityWithType(types.TypeTrait) if err != nil { return err } diff --git a/pkg/cmd/traits.go b/pkg/cmd/traits.go index c6a6da958..42dfdd6b2 100644 --- a/pkg/cmd/traits.go +++ b/pkg/cmd/traits.go @@ -1,15 +1,14 @@ package cmd import ( - "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" + plur "github.com/gertd/go-pluralize" "github.com/gosuri/uitable" "github.com/spf13/cobra" ) @@ -23,12 +22,11 @@ func NewTraitsCommand(ioStreams cmdutil.IOStreams) *cobra.Command { Long: "List traits", Example: `vela traits`, RunE: func(cmd *cobra.Command, args []string) error { - dir, _ := system.GetCapabilityDir() - templates, err := plugins.LoadTempFromLocal(filepath.Join(dir, "traits")) + templates, err := plugins.LoadInstalledCapabilityWithType(types.TypeTrait) if err != nil { return err } - workloads, err := plugins.LoadTempFromLocal(filepath.Join(dir, "workloads")) + workloads, err := plugins.LoadInstalledCapabilityWithType(types.TypeWorkload) if err != nil { return err } @@ -83,9 +81,22 @@ func ConvertApplyTo(applyTo []string, workloads []types.Capability) []string { return converted } -func check(crdname string, workloads []types.Capability) (string, bool) { +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 +} + +func check(applyto string, workloads []types.Capability) (string, bool) { for _, v := range workloads { - if crdname == v.CrdName { + if parse(applyto) == v.CrdName { return v.Name, true } } diff --git a/pkg/cmd/traits_test.go b/pkg/cmd/traits_test.go index 0e93c22b0..215bdacd3 100644 --- a/pkg/cmd/traits_test.go +++ b/pkg/cmd/traits_test.go @@ -15,13 +15,15 @@ import ( func Test_printTraitList(t *testing.T) { traits := []types.Capability{ { - Name: "route", - CrdName: "routes.oam.dev", - AppliesTo: []string{"deployments.apps", "clonsets.alibaba"}, + Name: "route", + CrdName: "routes.oam.dev", + // This format is currently OAM spec standard + AppliesTo: []string{"apps/v1.Deployment", "alibaba/v1.Clonset"}, }, { - Name: "scaler", - CrdName: "scaler.oam.dev", + Name: "scaler", + CrdName: "scaler.oam.dev", + // This format is also reasonable, it's align with oam definition name, so we also support here AppliesTo: []string{"deployments.apps"}, }, } @@ -86,3 +88,8 @@ func Test_printTraitList(t *testing.T) { assert.Equal(t, c.ExpectedString, b.String(), cname) } } + +func TestParse(t *testing.T) { + assert.Equal(t, "containerizedworkloads.core.oam.dev", parse("core.oam.dev/v1alpha2.ContainerizedWorkload")) + assert.Equal(t, "containerizedworkloads.core.oam.dev", parse("containerizedworkloads.core.oam.dev")) +} diff --git a/pkg/cmd/workload_run.go b/pkg/cmd/workload_run.go index 1a6dc8ac2..27765d625 100644 --- a/pkg/cmd/workload_run.go +++ b/pkg/cmd/workload_run.go @@ -48,8 +48,7 @@ func newRunOptions(ioStreams cmdutil.IOStreams) *runOptions { } func AddWorkloadCommands(parentCmd *cobra.Command, c types.Args, ioStreams cmdutil.IOStreams) error { - dir, _ := system.GetCapabilityDir() - templates, err := plugins.LoadTempFromLocal(filepath.Join(dir, "workloads")) + templates, err := plugins.LoadInstalledCapabilityWithType(types.TypeWorkload) if err != nil { return err } diff --git a/pkg/cmd/workloads.go b/pkg/cmd/workloads.go index 059ad787d..e586aa05d 100644 --- a/pkg/cmd/workloads.go +++ b/pkg/cmd/workloads.go @@ -1,12 +1,9 @@ package cmd import ( - "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" "github.com/gosuri/uitable" @@ -21,8 +18,7 @@ func NewWorkloadsCommand(ioStreams cmdutil.IOStreams) *cobra.Command { Long: "List workloads", Example: `vela workloads`, RunE: func(cmd *cobra.Command, args []string) error { - dir, _ := system.GetCapabilityDir() - workloads, err := plugins.LoadTempFromLocal(filepath.Join(dir, "workloads")) + workloads, err := plugins.LoadInstalledCapabilityWithType(types.TypeWorkload) if err != nil { return err } diff --git a/pkg/plugins/cluster.go b/pkg/plugins/cluster.go index e3007b76d..79ce44e90 100644 --- a/pkg/plugins/cluster.go +++ b/pkg/plugins/cluster.go @@ -71,7 +71,7 @@ func GetTraitsFromCluster(ctx context.Context, namespace string, c client.Client return templates, nil } -func HandleDefinition(name, syncDir, crdName string, extention *runtime.RawExtension, tp types.DefinitionType, applyTo []string) (types.Capability, error) { +func HandleDefinition(name, syncDir, crdName string, extention *runtime.RawExtension, tp types.CapType, applyTo []string) (types.Capability, error) { var tmp types.Capability tmp, err := HandleTemplate(extention, name, syncDir) if err != nil { diff --git a/pkg/plugins/local.go b/pkg/plugins/local.go index 2990d0ef8..50b09a18b 100644 --- a/pkg/plugins/local.go +++ b/pkg/plugins/local.go @@ -10,73 +10,34 @@ import ( "strings" "github.com/cloud-native-application/rudrx/api/types" + "github.com/cloud-native-application/rudrx/pkg/utils/system" ) -func GetDefFromLocal(dir string, defType types.DefinitionType) ([]types.Capability, error) { - temps, err := LoadTempFromLocal(dir) +func LoadAllInstalledCapability() ([]types.Capability, error) { + workloads, err := LoadInstalledCapabilityWithType(types.TypeWorkload) if err != nil { return nil, err } - var defs []types.Capability - for _, t := range temps { - if t.Type != defType { - continue - } - defs = append(defs, t) - } - return defs, nil -} - -func SinkTemp2Local(templates []types.Capability, dir string) int { - success := 0 - for _, tmp := range templates { - data, err := json.Marshal(tmp) - if err != nil { - fmt.Printf("sync %s err: %v\n", tmp.Name, err) - continue - } - err = ioutil.WriteFile(filepath.Join(dir, tmp.Name), data, 0644) - if err != nil { - fmt.Printf("sync %s err: %v\n", tmp.Name, err) - continue - } - success++ - } - return success -} - -func LoadCapabilityFromLocal(dir string) ([]types.Capability, error) { - var tmps []types.Capability - files, err := ioutil.ReadDir(dir) + traits, err := LoadInstalledCapabilityWithType(types.TypeTrait) if err != nil { - if os.IsNotExist(err) { - 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 { - fmt.Printf("read file %s err %v\n", f.Name(), err) - continue - } - tmp, err := ParseAndSyncCapability(data, filepath.Join(dir, ".tmp")) - if err != nil { - fmt.Printf("get definition of %s err %v\n", f.Name(), err) - continue - } - tmps = append(tmps, tmp) - } - return tmps, nil + workloads = append(workloads, traits...) + return workloads, nil } -func LoadTempFromLocal(dir string) ([]types.Capability, error) { +func LoadInstalledCapabilityWithType(capT types.CapType) ([]types.Capability, error) { + dir, _ := system.GetCapabilityDir() + return loadInstalledCapabilityWithType(dir, capT) +} + +// leave dir as argument for test convenience +func loadInstalledCapabilityWithType(dir string, capT types.CapType) ([]types.Capability, error) { + dir = GetSubDir(dir, capT) + return loadInstalledCapability(dir) +} + +func loadInstalledCapability(dir string) ([]types.Capability, error) { var tmps []types.Capability files, err := ioutil.ReadDir(dir) if err != nil { @@ -108,3 +69,64 @@ func LoadTempFromLocal(dir string) ([]types.Capability, error) { } return tmps, nil } + +func GetSubDir(dir string, capT types.CapType) string { + switch capT { + case types.TypeWorkload: + return filepath.Join(dir, "workloads") + case types.TypeTrait: + return filepath.Join(dir, "traits") + } + return dir +} + +func SinkTemp2Local(templates []types.Capability, dir string) int { + success := 0 + for _, tmp := range templates { + subDir := GetSubDir(dir, tmp.Type) + system.StatAndCreate(subDir) + data, err := json.Marshal(tmp) + if err != nil { + fmt.Printf("sync %s err: %v\n", tmp.Name, err) + continue + } + err = ioutil.WriteFile(filepath.Join(subDir, tmp.Name), data, 0644) + if err != nil { + fmt.Printf("sync %s err: %v\n", tmp.Name, err) + continue + } + success++ + } + return success +} + +func LoadCapabilityFromSyncedCenter(dir string) ([]types.Capability, error) { + var tmps []types.Capability + files, err := ioutil.ReadDir(dir) + if err != nil { + if os.IsNotExist(err) { + 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 { + fmt.Printf("read file %s err %v\n", f.Name(), err) + continue + } + tmp, err := ParseAndSyncCapability(data, filepath.Join(dir, ".tmp")) + if err != nil { + fmt.Printf("get definition of %s err %v\n", f.Name(), err) + continue + } + tmps = append(tmps, tmp) + } + return tmps, nil +} diff --git a/pkg/plugins/local_test.go b/pkg/plugins/local_test.go index 5c3ae2f00..1dbdb9b5d 100644 --- a/pkg/plugins/local_test.go +++ b/pkg/plugins/local_test.go @@ -47,7 +47,7 @@ func TestLocalSink(t *testing.T) { cases := map[string]struct { dir string tmps []types.Capability - Type types.DefinitionType + Type types.CapType expDef []types.Capability err error }{ @@ -91,21 +91,14 @@ func TestLocalSink(t *testing.T) { } } -func testInDir(t *testing.T, casename, dir string, tmps, defexp []types.Capability, Type types.DefinitionType, err1 error) { +func testInDir(t *testing.T, casename, dir string, tmps, defexp []types.Capability, Type types.CapType, err1 error) { err := os.MkdirAll(dir, 0755) assert.NoError(t, err, casename) defer os.RemoveAll(dir) number := SinkTemp2Local(tmps, dir) assert.Equal(t, len(tmps), number) - gottmps, err := LoadTempFromLocal(dir) - 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) + gotDef, err := loadInstalledCapabilityWithType(dir, Type) assert.NoError(t, err, casename) assert.Equal(t, defexp, gotDef, casename) }