From 22d014d91a3898275805cb0ca641f71e20795a2f Mon Sep 17 00:00:00 2001 From: chival <47812250+chivalryq@users.noreply.github.com> Date: Tue, 29 Jun 2021 15:52:43 +0800 Subject: [PATCH] Align vela cli and kubectl vela client tools (#1827) * allow vela CLI to specify NS * vela up support application yaml * fix * add default cap center to vela CLI * add alias for `vela components`(`vela comp`,or `vela component`) and `vela traits`(`vela trait`) * fix cap ls STATUS fields are always "uninstalled" * fix vela up process * Revert "allow vela CLI to specify NS" This reverts commit 33f27362 will refactor to use Initializer * add --discover for vela CLI * * rfc capcenter to reuse registry type * change default cap center to oss * judge if application file in advance * fix CI * try CI * fix error check --- references/cli/capability.go | 29 +++++--- references/cli/components.go | 14 +++- references/cli/traits.go | 15 +++- references/cli/up.go | 37 +++++++--- references/common/application.go | 22 ++++++ references/common/capability.go | 33 +++++---- references/plugins/capcenter.go | 115 ++++++++++++++++--------------- references/plugins/local.go | 2 +- references/plugins/registry.go | 63 ++++++++++++----- 9 files changed, 220 insertions(+), 110 deletions(-) diff --git a/references/cli/capability.go b/references/cli/capability.go index e05c03ed3..df558fc2e 100644 --- a/references/cli/capability.go +++ b/references/cli/capability.go @@ -203,17 +203,11 @@ func NewCapListCommand(c common2.Args, ioStreams cmdutil.IOStreams) *cobra.Comma if err != nil { return err } - capabilityList, err := common.ListCapabilities(env.Namespace, c, repoName) + + err = printCenterCapabilities(env.Namespace, repoName, c, ioStreams, nil) if err != nil { return err } - table := newUITable() - table.AddRow("NAME", "CENTER", "TYPE", "DEFINITION", "STATUS", "APPLIES-TO") - - for _, c := range capabilityList { - table.AddRow(c.Name, c.Center, c.Type, c.CrdName, c.Status, c.AppliesTo) - } - ioStreams.Info(table.String()) return nil }, } @@ -274,3 +268,22 @@ func removeCapCenter(args []string, ioStreams cmdutil.IOStreams) error { } return err } +func printCenterCapabilities(namespace, repoName string, args common2.Args, ioStreams cmdutil.IOStreams, option *types.CapType) error { + capabilityList, err := common.ListCapabilities(namespace, args, repoName) + if err != nil { + return err + } + table := newUITable() + table.AddRow("NAME", "CENTER", "TYPE", "DEFINITION", "STATUS", "APPLIES-TO") + + for _, c := range capabilityList { + if option == nil { + table.AddRow(c.Name, c.Center, c.Type, c.CrdName, c.Status, c.AppliesTo) + } + if option != nil && c.Type == *option { + table.AddRow(c.Name, c.Center, c.Type, c.CrdName, c.Status, c.AppliesTo) + } + } + ioStreams.Info(table.String()) + return nil +} diff --git a/references/cli/components.go b/references/cli/components.go index fd32e3656..7b3dfee3b 100644 --- a/references/cli/components.go +++ b/references/cli/components.go @@ -40,6 +40,7 @@ import ( func NewComponentsCommand(c common2.Args, ioStreams cmdutil.IOStreams) *cobra.Command { cmd := &cobra.Command{ Use: "components", + Aliases: []string{"comp", "component"}, DisableFlagsInUseLine: true, Short: "List components", Long: "List components", @@ -48,16 +49,27 @@ func NewComponentsCommand(c common2.Args, ioStreams cmdutil.IOStreams) *cobra.Co return c.SetConfig() }, RunE: func(cmd *cobra.Command, args []string) error { + isDiscover, _ := cmd.Flags().GetBool("discover") env, err := GetEnv(cmd) if err != nil { return err } - return printComponentList(env.Namespace, c, ioStreams) + if !isDiscover { + return printComponentList(env.Namespace, c, ioStreams) + } + option := types.TypeComponentDefinition + err = printCenterCapabilities(env.Namespace, "", c, ioStreams, &option) + if err != nil { + return err + } + + return nil }, Annotations: map[string]string{ types.TagCommandType: types.TypeCap, }, } + cmd.Flags().Bool("discover", false, "discover traits in capability centers") cmd.SetOut(ioStreams.Out) return cmd } diff --git a/references/cli/traits.go b/references/cli/traits.go index 594e662ec..a29a895e7 100644 --- a/references/cli/traits.go +++ b/references/cli/traits.go @@ -40,6 +40,7 @@ import ( func NewTraitsCommand(c common2.Args, ioStreams cmdutil.IOStreams) *cobra.Command { cmd := &cobra.Command{ Use: "traits", + Aliases: []string{"trait"}, DisableFlagsInUseLine: true, Short: "List traits", Long: "List traits", @@ -48,17 +49,27 @@ func NewTraitsCommand(c common2.Args, ioStreams cmdutil.IOStreams) *cobra.Comman return c.SetConfig() }, RunE: func(cmd *cobra.Command, args []string) error { + isDiscover, _ := cmd.Flags().GetBool("discover") env, err := GetEnv(cmd) if err != nil { return err } - return printTraitList(env.Namespace, c, ioStreams) + if !isDiscover { + return printTraitList(env.Namespace, c, ioStreams) + } + option := types.TypeTrait + err = printCenterCapabilities(env.Namespace, "", c, ioStreams, &option) + if err != nil { + return err + } + + return nil }, Annotations: map[string]string{ types.TagCommandType: types.TypeCap, }, } - + cmd.Flags().Bool("discover", false, "discover traits in capability centers") cmd.SetOut(ioStreams.Out) return cmd } diff --git a/references/cli/up.go b/references/cli/up.go index ad96c0791..02d81a7fa 100644 --- a/references/cli/up.go +++ b/references/cli/up.go @@ -17,8 +17,14 @@ limitations under the License. package cli import ( + "io/ioutil" + "path/filepath" + + "github.com/ghodss/yaml" + "github.com/pkg/errors" "github.com/spf13/cobra" + corev1beta1 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" "github.com/oam-dev/kubevela/apis/types" common2 "github.com/oam-dev/kubevela/pkg/utils/common" cmdutil "github.com/oam-dev/kubevela/pkg/utils/util" @@ -51,21 +57,36 @@ func NewUpCommand(c common2.Args, ioStream cmdutil.IOStreams) *cobra.Command { if err != nil { return err } - - o := &common.AppfileOptions{ - Kubecli: kubecli, - IO: ioStream, - Env: velaEnv, - } filePath, err := cmd.Flags().GetString(appFilePath) if err != nil { return err } - return o.Run(filePath, velaEnv.Namespace, c) + fileContent, err := ioutil.ReadFile(filepath.Clean(filePath)) + if err != nil { + return err + } + var app corev1beta1.Application + err = yaml.Unmarshal(fileContent, &app) + if err != nil { + return errors.Wrap(err, "File format is illegal") + } + if app.APIVersion != "" && app.Kind != "" { + err = common.ApplyApplication(app, ioStream, kubecli) + if err != nil { + return err + } + } else { + o := &common.AppfileOptions{ + Kubecli: kubecli, + IO: ioStream, + Env: velaEnv, + } + return o.Run(filePath, velaEnv.Namespace, c) + } + return nil }, } cmd.SetOut(ioStream.Out) - cmd.Flags().StringP(appFilePath, "f", "", "specify file path for appfile") return cmd } diff --git a/references/common/application.go b/references/common/application.go index 9a7642e2a..d8760ff22 100644 --- a/references/common/application.go +++ b/references/common/application.go @@ -42,6 +42,7 @@ import ( "github.com/oam-dev/kubevela/apis/types" "github.com/oam-dev/kubevela/pkg/oam" oamutil "github.com/oam-dev/kubevela/pkg/oam/util" + "github.com/oam-dev/kubevela/pkg/utils/apply" "github.com/oam-dev/kubevela/pkg/utils/common" cmdutil "github.com/oam-dev/kubevela/pkg/utils/util" "github.com/oam-dev/kubevela/references/apiserver/apis" @@ -520,3 +521,24 @@ func (o *AppfileOptions) Info(app *corev1beta1.Application) string { } return appUpMessage } + +// ApplyApplication will apply an application file in K8s GVK format +func ApplyApplication(app corev1beta1.Application, ioStream cmdutil.IOStreams, clt client.Client) error { + if app.Namespace == "" { + app.Namespace = types.DefaultAppNamespace + } + _, err := ioStream.Out.Write([]byte("Applying an application in K8S format...\n")) + if err != nil { + return err + } + applicator := apply.NewAPIApplicator(clt) + err = applicator.Apply(context.Background(), &app) + if err != nil { + return err + } + _, err = ioStream.Out.Write([]byte("Successfully apply application")) + if err != nil { + return err + } + return nil +} diff --git a/references/common/capability.go b/references/common/capability.go index 0e125aa85..9d536d7ca 100644 --- a/references/common/capability.go +++ b/references/common/capability.go @@ -170,7 +170,7 @@ func InstallComponentDefinition(client client.Client, workloadData []byte, ioStr } // InstallTraitDefinition will add a trait into K8s cluster and install it's controller -func InstallTraitDefinition(client client.Client, mapper discoverymapper.DiscoveryMapper, traitdata []byte, ioStreams cmdutil.IOStreams, tp *types.Capability) error { +func InstallTraitDefinition(client client.Client, mapper discoverymapper.DiscoveryMapper, traitdata []byte, ioStreams cmdutil.IOStreams, cap *types.Capability) error { var td v1beta1.TraitDefinition var err error if err = yaml.Unmarshal(traitdata, &td); err != nil { @@ -178,24 +178,24 @@ func InstallTraitDefinition(client client.Client, mapper discoverymapper.Discove } td.Namespace = types.DefaultKubeVelaNS ioStreams.Info("Installing trait capability " + td.Name) - if tp.Install != nil { - tp.Source.ChartName = tp.Install.Helm.Name - if err = helm.InstallHelmChart(ioStreams, tp.Install.Helm); err != nil { + if cap.Install != nil { + cap.Source.ChartName = cap.Install.Helm.Name + if err = helm.InstallHelmChart(ioStreams, cap.Install.Helm); err != nil { return err } - err = addSourceIntoExtension(td.Spec.Extension, tp.Source) + err = addSourceIntoExtension(td.Spec.Extension, cap.Source) if err != nil { return err } } - if err = HackForStandardTrait(*tp, client); err != nil { + if err = HackForStandardTrait(*cap, client); err != nil { return err } gvk, err := util.GetGVKFromDefinition(mapper, td.Spec.Reference) if err != nil { return err } - tp.CrdInfo = &types.CRDInfo{ + cap.CrdInfo = &types.CRDInfo{ APIVersion: gvk.GroupVersion().String(), Kind: gvk.Kind, } @@ -400,7 +400,6 @@ func ListCapabilities(userNamespace string, c common.Args, capabilityCenterName } return capabilityList, nil } - func listCenterCapabilities(userNamespace string, c common.Args, repoDir string) ([]types.Capability, error) { dm, err := c.GetDiscoveryMapper() if err != nil { @@ -414,10 +413,10 @@ func listCenterCapabilities(userNamespace string, c common.Args, repoDir string) return templates, nil } baseDir := filepath.Base(repoDir) - workloads := gatherComponents(userNamespace, c, templates) + components := gatherComponents(userNamespace, c, templates) for i, p := range templates { - status := checkInstallStatus(userNamespace, c, baseDir, p) - convertedApplyTo := ConvertApplyTo(p.AppliesTo, workloads) + status := checkInstallStatus(userNamespace, c, p) + convertedApplyTo := ConvertApplyTo(p.AppliesTo, components) templates[i].Center = baseDir templates[i].Status = status templates[i].AppliesTo = convertedApplyTo @@ -460,23 +459,23 @@ func RemoveCapabilityCenter(centerName string) (string, error) { } func gatherComponents(userNamespace string, c common.Args, templates []types.Capability) []types.Capability { - workloads, err := plugins.LoadInstalledCapabilityWithType(userNamespace, c, types.TypeComponentDefinition) + components, err := plugins.LoadInstalledCapabilityWithType(userNamespace, c, types.TypeComponentDefinition) if err != nil { - workloads = make([]types.Capability, 0) + components = make([]types.Capability, 0) } for _, t := range templates { if t.Type == types.TypeComponentDefinition { - workloads = append(workloads, t) + components = append(components, t) } } - return workloads + return components } -func checkInstallStatus(userNamespace string, c common.Args, repoName string, tmp types.Capability) string { +func checkInstallStatus(userNamespace string, c common.Args, tmp types.Capability) string { var status = "uninstalled" installed, _ := plugins.LoadInstalledCapabilityWithType(userNamespace, c, tmp.Type) for _, i := range installed { - if i.Source != nil && i.Source.RepoName == repoName && i.Name == tmp.Name && i.CrdName == tmp.CrdName { + if i.Name == tmp.Name && i.CrdName == tmp.CrdName { return "installed" } } diff --git a/references/plugins/capcenter.go b/references/plugins/capcenter.go index 455ed6776..de0585d75 100644 --- a/references/plugins/capcenter.go +++ b/references/plugins/capcenter.go @@ -18,7 +18,6 @@ package plugins import ( "context" - "encoding/base64" "errors" "fmt" "io/ioutil" @@ -40,7 +39,7 @@ import ( "github.com/oam-dev/kubevela/pkg/utils/system" ) -// Content contains different type of content needed when building Registry or GithubCenter +// Content contains different type of content needed when building Registry type Content struct { OssContent GithubContent @@ -86,6 +85,8 @@ func NewCenterClient(ctx context.Context, name, address, token string) (CenterCl switch Type { case TypeGithub: return NewGithubCenter(ctx, token, name, &cfg.GithubContent) + case TypeOss: + return NewOssCenter(fmt.Sprintf("https://%s/", cfg.BucketURL), name), nil default: } return nil, errors.New("we only support github as repository now") @@ -178,22 +179,12 @@ func Parse(addr string) (string, *Content, error) { return TypeUnknown, nil, nil } -// RemoteCapability defines the capability discovered from remote cap center -type RemoteCapability struct { - // Name MUST be xxx.yaml - Name string `json:"name"` - URL string `json:"downloadUrl"` - Sha string `json:"sha"` - // Type MUST be file - Type string `json:"type"` -} - -// RemoteCapabilities is slice of cap center -type RemoteCapabilities []RemoteCapability - // LoadRepos will load all cap center repos -// TODO(wonderflow): we can make default(built-in) repo configurable, then we should make default inside the answer func LoadRepos() ([]CapCenterConfig, error) { + defaultRepo := CapCenterConfig{ + Name: "default-cap-center", + Address: "oss://registry.kubevela.net/", + } config, err := system.GetRepoConfig() if err != nil { return nil, err @@ -201,7 +192,7 @@ func LoadRepos() ([]CapCenterConfig, error) { data, err := ioutil.ReadFile(filepath.Clean(config)) if err != nil { if os.IsNotExist(err) { - return []CapCenterConfig{}, nil + return []CapCenterConfig{defaultRepo}, nil } return nil, err } @@ -209,6 +200,16 @@ func LoadRepos() ([]CapCenterConfig, error) { if err = yaml.Unmarshal(data, &repos); err != nil { return nil, err } + haveDefault := false + for _, repo := range repos { + if repo.Address == defaultRepo.Address { + haveDefault = true + break + } + } + if !haveDefault { + repos = append(repos, defaultRepo) + } return repos, nil } @@ -226,8 +227,8 @@ func StoreRepos(repos []CapCenterConfig) error { return ioutil.WriteFile(config, data, 0644) } -// ParseAndSyncCapability will convert config from remote center to capability -func ParseAndSyncCapability(mapper discoverymapper.DiscoveryMapper, data []byte) (types.Capability, error) { +// ParseCapability will convert config from remote center to capability +func ParseCapability(mapper discoverymapper.DiscoveryMapper, data []byte) (types.Capability, error) { var obj = unstructured.Unstructured{Object: make(map[string]interface{})} err := yaml.Unmarshal(data, &obj.Object) if err != nil { @@ -258,18 +259,8 @@ func ParseAndSyncCapability(mapper discoverymapper.DiscoveryMapper, data []byte) return types.Capability{}, fmt.Errorf("unknown definition Type %s", obj.GetKind()) } -// GithubCenter implementation of cap center -type GithubCenter struct { - client *github.Client - cfg *GithubContent - centerName string - ctx context.Context -} - -var _ CenterClient = &GithubCenter{} - // NewGithubCenter will create client by github center implementation -func NewGithubCenter(ctx context.Context, token, centerName string, r *GithubContent) (*GithubCenter, error) { +func NewGithubCenter(ctx context.Context, token, centerName string, r *GithubContent) (*GithubRegistry, error) { var tc *http.Client if token != "" { ts := oauth2.StaticTokenSource( @@ -277,17 +268,18 @@ func NewGithubCenter(ctx context.Context, token, centerName string, r *GithubCon ) tc = oauth2.NewClient(ctx, ts) } - return &GithubCenter{client: github.NewClient(tc), cfg: r, centerName: centerName, ctx: ctx}, nil + return &GithubRegistry{client: github.NewClient(tc), cfg: r, centerName: centerName, ctx: ctx}, nil } -// SyncCapabilityFromCenter will sync capability from github cap center +// SyncCapabilityFromCenter will sync capability from github registry // TODO(wonderflow): currently we only sync by create, we also need to delete which not exist remotely. -func (g *GithubCenter) SyncCapabilityFromCenter() error { +func (g *GithubRegistry) SyncCapabilityFromCenter() error { dir, err := system.GetCapCenterDir() if err != nil { return err } repoDir := filepath.Join(dir, g.centerName) + _, _ = system.CreateIfNotExist(repoDir) var success int items, err := g.getRepoFile() if err != nil { @@ -296,7 +288,8 @@ func (g *GithubCenter) SyncCapabilityFromCenter() error { for _, item := range items { addon, err := item.toAddon() if err != nil { - return err + fmt.Printf("[INFO] CRD for %s not found\n", item.name) + continue } //nolint:gosec err = ioutil.WriteFile(filepath.Join(repoDir, addon.Name+".yaml"), item.data, 0644) @@ -310,33 +303,43 @@ func (g *GithubCenter) SyncCapabilityFromCenter() error { return nil } -func (g *GithubCenter) getRepoFile() ([]RegistryFile, error) { - var items []RegistryFile - _, dirs, _, err := g.client.Repositories.GetContents(g.ctx, g.cfg.Owner, g.cfg.Repo, g.cfg.Path, &github.RepositoryContentGetOptions{Ref: g.cfg.Ref}) - if err != nil { - return []RegistryFile{}, err +// NewOssCenter will create OSS center implementation +func NewOssCenter(bucketURL string, centerName string) *OssRegistry { + var tc http.Client + return &OssRegistry{ + Client: &tc, + bucketURL: bucketURL, + centerName: centerName, } - for _, repoItem := range dirs { - if *repoItem.Type != "file" { +} + +// SyncCapabilityFromCenter will sync capability from oss registry +func (o *OssRegistry) SyncCapabilityFromCenter() error { + dir, err := system.GetCapCenterDir() + if err != nil { + return err + } + repoDir := filepath.Join(dir, o.centerName) + _, _ = system.CreateIfNotExist(repoDir) + var success int + items, err := o.getRegFiles() + if err != nil { + return err + } + for _, item := range items { + addon, err := item.toAddon() + if err != nil { + fmt.Printf("[INFO] CRD for %s not found\n", item.name) continue } - fileContent, _, _, err := g.client.Repositories.GetContents(g.ctx, g.cfg.Owner, g.cfg.Repo, *repoItem.Path, &github.RepositoryContentGetOptions{Ref: g.cfg.Ref}) + //nolint:gosec + err = ioutil.WriteFile(filepath.Join(repoDir, addon.Name+".yaml"), item.data, 0644) if err != nil { - fmt.Printf("Getting content URL %s error: %s\n", repoItem.GetURL(), err) + fmt.Printf("write definition %s to %s err %v\n", addon.Name+".yaml", repoDir, err) continue - } - var data []byte - if *fileContent.Encoding == "base64" { - data, err = base64.StdEncoding.DecodeString(*fileContent.Content) - if err != nil { - fmt.Printf("decode github content %s err %s\n", fileContent.GetPath(), err) - continue - } } - items = append(items, RegistryFile{ - data: data, - name: *fileContent.Name, - }) + success++ } - return items, nil + fmt.Printf("successfully sync %d from %s remote center\n", success, o.centerName) + return nil } diff --git a/references/plugins/local.go b/references/plugins/local.go index b9ef1dba8..3e075c1c1 100644 --- a/references/plugins/local.go +++ b/references/plugins/local.go @@ -259,7 +259,7 @@ func LoadCapabilityFromSyncedCenter(mapper discoverymapper.DiscoveryMapper, dir fmt.Printf("read file %s err %v\n", f.Name(), err) continue } - tmp, err := ParseAndSyncCapability(mapper, data) + tmp, err := ParseCapability(mapper, data) if err != nil { fmt.Printf("get definition of %s err %v\n", f.Name(), err) continue diff --git a/references/plugins/registry.go b/references/plugins/registry.go index 0b159473c..2847b5f8a 100644 --- a/references/plugins/registry.go +++ b/references/plugins/registry.go @@ -26,9 +26,9 @@ import ( "os" "path" "path/filepath" - "strings" "github.com/google/go-github/v32/github" + "github.com/pkg/errors" "golang.org/x/oauth2" "github.com/oam-dev/kubevela/apis/types" @@ -43,10 +43,10 @@ type Registry interface { // GithubRegistry is Registry's implementation treat github url as resource type GithubRegistry struct { - client *github.Client - cfg *GithubContent - ctx context.Context - name string // to be used to cache registry + client *github.Client + cfg *GithubContent + ctx context.Context + centerName string // to be used to cache registry } // NewRegistry will create a registry implementation @@ -64,7 +64,7 @@ func NewRegistry(ctx context.Context, token, registryName string, regURL string) ) tc = oauth2.NewClient(ctx, ts) } - return GithubRegistry{client: github.NewClient(tc), cfg: &cfg.GithubContent, ctx: ctx, name: registryName}, nil + return GithubRegistry{client: github.NewClient(tc), cfg: &cfg.GithubContent, ctx: ctx, centerName: registryName}, nil case TypeOss: var tc http.Client return OssRegistry{ @@ -161,7 +161,8 @@ func (g *GithubRegistry) getRepoFile() ([]RegistryFile, error) { // OssRegistry is Registry's implementation treat OSS url as resource type OssRegistry struct { *http.Client - bucketURL string + bucketURL string + centerName string } // GetCap return capability object and raw data specified by cap name @@ -196,6 +197,22 @@ func (o OssRegistry) GetCap(addonName string) (types.Capability, []byte, error) // ListCaps list all capabilities of registry func (o OssRegistry) ListCaps() ([]types.Capability, error) { + rfs, err := o.getRegFiles() + if err != nil { + return []types.Capability{}, errors.Wrap(err, "Get raw files fail") + } + capas := make([]types.Capability, 0) + + for _, rf := range rfs { + capa, err := rf.toAddon() + if err != nil { + fmt.Printf("[WARN] Parse file %s fail\n", rf.name) + } + capas = append(capas, capa) + } + return capas, nil +} +func (o OssRegistry) getRegFiles() ([]RegistryFile, error) { req, _ := http.NewRequestWithContext( context.Background(), http.MethodGet, @@ -204,30 +221,42 @@ func (o OssRegistry) ListCaps() ([]types.Capability, error) { ) resp, err := o.Client.Do(req) if err != nil { - return []types.Capability{}, err + return []RegistryFile{}, err } data, err := ioutil.ReadAll(resp.Body) _ = resp.Body.Close() if err != nil { - return []types.Capability{}, err + return []RegistryFile{}, err } list := &ListBucketResult{} err = xml.Unmarshal(data, list) if err != nil { - return []types.Capability{}, err + return []RegistryFile{}, err } - capas := make([]types.Capability, 0) + rfs := make([]RegistryFile, 0) for _, fileName := range list.File { - addonName := strings.Split(fileName, ".")[0] - capa, _, err := o.GetCap(addonName) + req, _ := http.NewRequestWithContext( + context.Background(), + http.MethodGet, + o.bucketURL+fileName, + nil, + ) + resp, err := o.Client.Do(req) if err != nil { - fmt.Printf("Get %s err: %s\n", fileName, err) + fmt.Printf("[WARN] %s download fail\n", fileName) continue } - capas = append(capas, capa) + data, _ := ioutil.ReadAll(resp.Body) + _ = resp.Body.Close() + rf := RegistryFile{ + data: data, + name: fileName, + } + rfs = append(rfs, rf) + } - return capas, nil + return rfs, nil } // LocalRegistry is Registry's implementation treat local url as resource @@ -282,7 +311,7 @@ func (item RegistryFile) toAddon() (types.Capability, error) { if err != nil { return types.Capability{}, err } - capability, err := ParseAndSyncCapability(dm, item.data) + capability, err := ParseCapability(dm, item.data) if err != nil { return types.Capability{}, err }