Feat: support NOTES.cue in addon for additional info printer (#5195)

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
This commit is contained in:
Jianbo Sun
2022-12-16 18:07:27 +08:00
committed by GitHub
parent c94fa21c28
commit dcb5378ff9
10 changed files with 246 additions and 78 deletions
+105 -16
View File
@@ -35,6 +35,7 @@ import (
"github.com/google/go-github/v32/github"
"github.com/imdario/mergo"
prismclusterv1alpha1 "github.com/kubevela/prism/pkg/apis/cluster/v1alpha1"
"github.com/kubevela/workflow/pkg/cue/model/value"
"github.com/pkg/errors"
"github.com/xanzy/go-gitlab"
"golang.org/x/oauth2"
@@ -88,6 +89,12 @@ const (
// AppTemplateCueFileName is the addon application template.cue file name
AppTemplateCueFileName string = "template.cue"
// NotesCUEFileName is the addon notes print to end users when installed
NotesCUEFileName string = "NOTES.cue"
// KeyWordNotes is the keyword in NOTES.cue which will render the notes message out.
KeyWordNotes string = "notes"
// GlobalParameterFileName is the addon global parameter.cue file name
GlobalParameterFileName string = "parameter.cue"
@@ -111,6 +118,9 @@ const (
// DefaultGiteeURL is the addon repository of gitee api
DefaultGiteeURL string = "https://gitee.com/api/v5/"
// InstallerRuntimeOption inject install runtime info into addon options
InstallerRuntimeOption string = "installerRuntimeOption"
)
// ParameterFileName is the addon resources/parameter.cue file name
@@ -153,10 +163,17 @@ type Pattern struct {
// Patterns is the file pattern that the addon should be in
var Patterns = []Pattern{
// config-templates pattern
{IsDir: true, Value: ConfigTemplateDirName},
// single file reader pattern
{Value: ReadmeFileName}, {Value: MetadataFileName}, {Value: TemplateFileName},
{Value: ParameterFileName}, {IsDir: true, Value: ResourcesDirName}, {IsDir: true, Value: DefinitionsDirName},
{IsDir: true, Value: DefSchemaName}, {IsDir: true, Value: ViewDirName}, {Value: AppTemplateCueFileName}, {Value: GlobalParameterFileName}, {Value: LegacyReadmeFileName}}
// parameter in resource directory
{Value: ParameterFileName},
// directory files
{IsDir: true, Value: ResourcesDirName}, {IsDir: true, Value: DefinitionsDirName}, {IsDir: true, Value: DefSchemaName}, {IsDir: true, Value: ViewDirName},
// CUE app template, parameter and notes
{Value: AppTemplateCueFileName}, {Value: GlobalParameterFileName}, {Value: NotesCUEFileName},
{Value: LegacyReadmeFileName}}
// GetPatternFromItem will check if the file path has a valid pattern, return empty string if it's invalid.
// AsyncReader is needed to calculate relative path
@@ -282,6 +299,7 @@ func GetInstallPackageFromReader(r AsyncReader, meta *SourceMeta, uiData *UIData
DefSchemaName: readDefSchemaFile,
ViewDirName: readViewFile,
AppTemplateCueFileName: readAppCueTemplate,
NotesCUEFileName: readNotesFile,
}
ptItems := ClassifyItemByPattern(meta, r)
@@ -342,6 +360,16 @@ func readParamFile(a *UIData, reader AsyncReader, readPath string) error {
return nil
}
// readNotesFile read single NOTES.cue file
func readNotesFile(a *InstallPackage, reader AsyncReader, readPath string) error {
data, err := reader.ReadFile(readPath)
if err != nil {
return err
}
a.Notes = ElementFile{Data: data, Name: filepath.Base(readPath)}
return nil
}
// readGlobalParamFile read global parameter file.
func readGlobalParamFile(a *UIData, reader AsyncReader, readPath string) error {
b, err := reader.ReadFile(readPath)
@@ -842,11 +870,16 @@ type Installer struct {
dryRun bool
dryRunBuff *bytes.Buffer
installerRuntime map[string]interface{}
registries []Registry
}
// NewAddonInstaller will create an installer for addon
func NewAddonInstaller(ctx context.Context, cli client.Client, discoveryClient *discovery.DiscoveryClient, apply apply.Applicator, config *rest.Config, r *Registry, args map[string]interface{}, cache *Cache, registries []Registry, opts ...InstallOption) Installer {
if args == nil {
args = map[string]interface{}{}
}
i := Installer{
ctx: ctx,
config: config,
@@ -859,13 +892,22 @@ func NewAddonInstaller(ctx context.Context, cli client.Client, discoveryClient *
dryRunBuff: &bytes.Buffer{},
registries: registries,
}
ir := args[InstallerRuntimeOption]
if irr, ok := ir.(map[string]interface{}); ok {
i.installerRuntime = irr
} else {
i.installerRuntime = map[string]interface{}{}
}
// clean injected data from runtime option
delete(args, InstallerRuntimeOption)
for _, opt := range opts {
opt(&i)
}
return i
}
func (h *Installer) enableAddon(addon *InstallPackage) error {
func (h *Installer) enableAddon(addon *InstallPackage) (string, error) {
var err error
h.addon = addon
@@ -873,22 +915,28 @@ func (h *Installer) enableAddon(addon *InstallPackage) error {
err = checkAddonVersionMeetRequired(h.ctx, addon.SystemRequirements, h.cli, h.dc)
if err != nil {
version := h.getAddonVersionMeetSystemRequirement(addon.Name)
return VersionUnMatchError{addonName: addon.Name, err: err, userSelectedAddonVersion: addon.Version, availableVersion: version}
return "", VersionUnMatchError{addonName: addon.Name, err: err, userSelectedAddonVersion: addon.Version, availableVersion: version}
}
}
if err = h.installDependency(addon); err != nil {
return err
return "", err
}
if err = h.dispatchAddonResource(addon); err != nil {
return err
return "", err
}
// we shouldn't put continue func into dispatchAddonResource, because the re-apply app maybe already update app and
// the suspend will set with false automatically
if err := h.continueOrRestartWorkflow(); err != nil {
return err
return "", err
}
return nil
additionalInfo, err := h.renderNotes(addon)
if err != nil {
klog.Warningf("fail to render notes for addon %s: %v\n", addon.Name, err)
// notes don't affect the installation, so just print warn logs instead of abort with errors
return "", nil
}
return additionalInfo, nil
}
func (h *Installer) loadInstallPackage(name, version string) (*InstallPackage, error) {
@@ -960,9 +1008,13 @@ func (h *Installer) installDependency(addon *InstallPackage) error {
// try to install the dependent addon from the same registry with the current addon
depAddon, err = h.loadInstallPackage(dep.Name, dep.Version)
if err == nil {
if err = depHandler.enableAddon(depAddon); err != nil {
additionalInfo, err := depHandler.enableAddon(depAddon)
if err != nil {
return errors.Wrap(err, "fail to dispatch dependent addon resource")
}
if len(additionalInfo) > 0 {
klog.Infof("addon %s installed with additional info: %s\n", addon.Name, additionalInfo)
}
return nil
}
if !errors.Is(err, ErrNotExist) {
@@ -983,9 +1035,13 @@ func (h *Installer) installDependency(addon *InstallPackage) error {
return err
}
if err == nil {
if err = depHandler.enableAddon(depAddon); err != nil {
additionalInfo, err := depHandler.enableAddon(depAddon)
if err != nil {
return errors.Wrap(err, "fail to dispatch dependent addon resource")
}
if len(additionalInfo) > 0 {
klog.Infof("addon %s installed with additional info: %s\n", addon.Name, additionalInfo)
}
return nil
}
return fmt.Errorf("dependency addon: %s with version: %s cannot be found from all registries", dep.Name, dep.Version)
@@ -1016,14 +1072,16 @@ func (h *Installer) checkDependency(addon *InstallPackage) ([]string, error) {
}
return needEnable, nil
}
func (h *Installer) createOrUpdate(app *v1beta1.Application) error {
// createOrUpdate will return true if updated
func (h *Installer) createOrUpdate(app *v1beta1.Application) (bool, error) {
var getapp v1beta1.Application
err := h.cli.Get(h.ctx, client.ObjectKey{Name: app.Name, Namespace: app.Namespace}, &getapp)
if apierrors.IsNotFound(err) {
return h.cli.Create(h.ctx, app)
return false, h.cli.Create(h.ctx, app)
}
if err != nil {
return err
return false, err
}
getapp.Spec = app.Spec
getapp.Labels = app.Labels
@@ -1031,10 +1089,10 @@ func (h *Installer) createOrUpdate(app *v1beta1.Application) error {
err = h.cli.Update(h.ctx, &getapp)
if err != nil {
klog.Errorf("fail to create application: %v", err)
return errors.Wrap(err, "fail to create application")
return false, errors.Wrap(err, "fail to create application")
}
getapp.DeepCopyInto(app)
return nil
return true, nil
}
func (h *Installer) dispatchAddonResource(addon *InstallPackage) error {
@@ -1096,10 +1154,13 @@ func (h *Installer) dispatchAddonResource(addon *InstallPackage) error {
h.dryRunBuff.Write(result)
h.dryRunBuff.WriteString("\n")
} else {
err = h.createOrUpdate(app)
updated, err := h.createOrUpdate(app)
if err != nil {
return err
}
if updated {
h.installerRuntime["upgrade"] = true
}
}
auxiliaryOutputs = append(auxiliaryOutputs, defs...)
@@ -1146,6 +1207,34 @@ func (h *Installer) dispatchAddonResource(addon *InstallPackage) error {
return nil
}
func (h *Installer) renderNotes(addon *InstallPackage) (string, error) {
r := addonCueTemplateRender{
addon: addon,
inputArgs: h.args,
contextInfo: map[string]interface{}{
"installer": h.installerRuntime,
},
}
contextFile, err := r.formatContext()
if err != nil {
return "", err
}
notesFile := addon.Notes.Data + "\n" + contextFile
val, err := value.NewValue(notesFile, nil, "")
if err != nil {
return "", errors.Wrap(err, "build values for NOTES.cue")
}
notes, err := val.LookupValue(KeyWordNotes)
if err != nil {
return "", errors.Wrap(err, "look up notes in NOTES.cue")
}
notesStr, err := notes.CueValue().String()
if err != nil {
return "", errors.Wrap(err, "convert notes to string")
}
return notesStr, nil
}
// this func will handle such two case
// 1. if last apply failed an workflow have suspend, this func will continue the workflow
// 2. restart the workflow, if the new cluster have been added in KubeVela
+40 -3
View File
@@ -382,7 +382,7 @@ var _ = Describe("test enable addon in local dir", func() {
It("test enable addon by local dir", func() {
ctx := context.Background()
err := EnableAddonByLocalDir(ctx, "example", "./testdata/example", k8sClient, dc, apply.NewAPIApplicator(k8sClient), cfg, map[string]interface{}{"example": "test"})
_, err := EnableAddonByLocalDir(ctx, "example", "./testdata/example", k8sClient, dc, apply.NewAPIApplicator(k8sClient), cfg, map[string]interface{}{"example": "test"})
Expect(err).Should(BeNil())
app := v1beta1.Application{}
Expect(k8sClient.Get(ctx, types2.NamespacedName{Namespace: "vela-system", Name: "addon-example"}, &app)).Should(BeNil())
@@ -420,7 +420,7 @@ var _ = Describe("test dry-run addon from local dir", func() {
h := NewAddonInstaller(ctx, k8sClient, dc, apply.NewAPIApplicator(k8sClient), cfg, &Registry{Name: LocalAddonRegistryName}, map[string]interface{}{"example": "test-dry-run"}, nil, nil, DryRunAddon)
err = h.enableAddon(pkg)
_, err = h.enableAddon(pkg)
Expect(err).Should(BeNil())
decoder := yaml3.NewDecoder(h.dryRunBuff)
@@ -450,7 +450,7 @@ var _ = Describe("test enable addon which applies the views independently", func
It("test enable addon which applies the views independently", func() {
ctx := context.Background()
err := EnableAddonByLocalDir(ctx, "test-view", "./testdata/test-view", k8sClient, dc, apply.NewAPIApplicator(k8sClient), cfg, map[string]interface{}{"example": "test"})
_, err := EnableAddonByLocalDir(ctx, "test-view", "./testdata/test-view", k8sClient, dc, apply.NewAPIApplicator(k8sClient), cfg, map[string]interface{}{"example": "test"})
Expect(err).Should(BeNil())
app := v1beta1.Application{}
Expect(k8sClient.Get(ctx, types2.NamespacedName{Namespace: "vela-system", Name: "addon-test-view"}, &app)).Should(BeNil())
@@ -459,6 +459,43 @@ var _ = Describe("test enable addon which applies the views independently", func
})
})
var _ = Describe("test enable addon with notes", func() {
BeforeEach(func() {
app := v1beta1.Application{ObjectMeta: metav1.ObjectMeta{Namespace: "vela-system", Name: "addon-test-notes"}}
Expect(k8sClient.Delete(ctx, &app)).Should(SatisfyAny(BeNil(), util.NotFoundMatcher{}))
sec := v1.Secret{ObjectMeta: metav1.ObjectMeta{Namespace: "vela-system", Name: "addon-secret-test-notes"}}
Expect(k8sClient.Delete(ctx, &sec)).Should(SatisfyAny(BeNil(), util.NotFoundMatcher{}))
})
It("test 'enable' addon which render notes output", func() {
ctx := context.Background()
addonInputArgs := map[string]interface{}{"example": "test"}
// inject runtime info
addonInputArgs[InstallerRuntimeOption] = map[string]interface{}{
"upgrade": false,
}
notes, err := EnableAddonByLocalDir(ctx, "test-notes", "./testdata/test-notes", k8sClient, dc, apply.NewAPIApplicator(k8sClient), cfg, addonInputArgs)
Expect(err).Should(BeNil())
app := v1beta1.Application{}
Expect(k8sClient.Get(ctx, types2.NamespacedName{Namespace: "vela-system", Name: "addon-test-notes"}, &app)).Should(BeNil())
Expect(notes).Should(ContainSubstring(`Thank you for your first installation!
Please refer to URL.`))
})
It("test 'upgrade' addon which render notes output", func() {
ctx := context.Background()
addonInputArgs := map[string]interface{}{"example": "test"}
// inject runtime info
addonInputArgs[InstallerRuntimeOption] = map[string]interface{}{
"upgrade": true,
}
notes, err := EnableAddonByLocalDir(ctx, "test-notes-upgrade", "./testdata/test-notes", k8sClient, dc, apply.NewAPIApplicator(k8sClient), cfg, addonInputArgs)
Expect(err).Should(BeNil())
Expect(notes).Should(ContainSubstring(`Thank you for your upgrade!
Please refer to URL.`))
})
})
var _ = Describe("test override defs of addon", func() {
It("test compDef exist", func() {
ctx := context.Background()
+1 -2
View File
@@ -1057,7 +1057,7 @@ func TestCheckEnableAddonErrorWhenMissMatch(t *testing.T) {
version2.VelaVersion = "v1.3.0"
i := InstallPackage{Meta: Meta{SystemRequirements: &SystemRequirements{VelaVersion: ">=1.4.0"}}}
installer := &Installer{}
err := installer.enableAddon(&i)
_, err := installer.enableAddon(&i)
assert.Equal(t, errors.As(err, &VersionUnMatchError{}), true)
}
@@ -1082,7 +1082,6 @@ func TestPackageAddon(t *testing.T) {
archiver, err = PackageAddon(invalidAddonMetadata)
assert.NotNil(t, err)
assert.Equal(t, "", archiver)
}
func TestGenerateAnnotation(t *testing.T) {
+11 -20
View File
@@ -52,17 +52,13 @@ const (
)
// EnableAddon will enable addon with dependency check, source is where addon from.
func EnableAddon(ctx context.Context, name string, version string, cli client.Client, discoveryClient *discovery.DiscoveryClient, apply apply.Applicator, config *rest.Config, r Registry, args map[string]interface{}, cache *Cache, registries []Registry, opts ...InstallOption) error {
func EnableAddon(ctx context.Context, name string, version string, cli client.Client, discoveryClient *discovery.DiscoveryClient, apply apply.Applicator, config *rest.Config, r Registry, args map[string]interface{}, cache *Cache, registries []Registry, opts ...InstallOption) (string, error) {
h := NewAddonInstaller(ctx, cli, discoveryClient, apply, config, &r, args, cache, registries, opts...)
pkg, err := h.loadInstallPackage(name, version)
if err != nil {
return err
return "", err
}
err = h.enableAddon(pkg)
if err != nil {
return err
}
return nil
return h.enableAddon(pkg)
}
// DisableAddon will disable addon from cluster.
@@ -91,39 +87,34 @@ func DisableAddon(ctx context.Context, cli client.Client, name string, config *r
}
// EnableAddonByLocalDir enable an addon from local dir
func EnableAddonByLocalDir(ctx context.Context, name string, dir string, cli client.Client, dc *discovery.DiscoveryClient, applicator apply.Applicator, config *rest.Config, args map[string]interface{}, opts ...InstallOption) error {
func EnableAddonByLocalDir(ctx context.Context, name string, dir string, cli client.Client, dc *discovery.DiscoveryClient, applicator apply.Applicator, config *rest.Config, args map[string]interface{}, opts ...InstallOption) (string, error) {
absDir, err := filepath.Abs(dir)
if err != nil {
return err
return "", err
}
r := localReader{dir: absDir, name: name}
metas, err := r.ListAddonMeta()
if err != nil {
return err
return "", err
}
meta := metas[r.name]
UIData, err := GetUIDataFromReader(r, &meta, UIMetaOptions)
if err != nil {
return err
return "", err
}
pkg, err := GetInstallPackageFromReader(r, &meta, UIData)
if err != nil {
return err
return "", err
}
h := NewAddonInstaller(ctx, cli, dc, applicator, config, &Registry{Name: LocalAddonRegistryName}, args, nil, nil, opts...)
needEnableAddonNames, err := h.checkDependency(pkg)
if err != nil {
return err
return "", err
}
if len(needEnableAddonNames) > 0 {
return fmt.Errorf("you must first enable dependencies: %v", needEnableAddonNames)
return "", fmt.Errorf("you must first enable dependencies: %v", needEnableAddonNames)
}
err = h.enableAddon(pkg)
if err != nil {
return err
}
return nil
return h.enableAddon(pkg)
}
// GetAddonStatus is general func for cli and apiServer get addon status
+11 -5
View File
@@ -60,8 +60,9 @@ const (
)
type addonCueTemplateRender struct {
addon *InstallPackage
inputArgs map[string]interface{}
addon *InstallPackage
inputArgs map[string]interface{}
contextInfo map[string]interface{}
}
func (a addonCueTemplateRender) formatContext() (string, error) {
@@ -69,6 +70,10 @@ func (a addonCueTemplateRender) formatContext() (string, error) {
if args == nil {
args = map[string]interface{}{}
}
contextInfo := a.contextInfo
if contextInfo == nil {
contextInfo = map[string]interface{}{}
}
bt, err := json.Marshal(args)
if err != nil {
return "", err
@@ -80,12 +85,13 @@ func (a addonCueTemplateRender) formatContext() (string, error) {
// in case the user defined data has packages
contextFile.WriteString(a.addon.Parameters + "\n")
// addon metadata context
metadataJSON, err := json.Marshal(a.addon.Meta)
// add metadata of addon into context
contextInfo["metadata"] = a.addon.Meta
contextJSON, err := json.Marshal(contextInfo)
if err != nil {
return "", err
}
contextFile.WriteString(fmt.Sprintf("context: metadata: %s\n", string(metadataJSON)))
contextFile.WriteString(fmt.Sprintf("context: %s\n", string(contextJSON)))
// parameter definition
contextFile.WriteString(paramFile + "\n")
+13
View File
@@ -0,0 +1,13 @@
info: string
if !context.installer.upgrade {
info: "first installation!"
}
if context.installer.upgrade {
info: "upgrade!"
}
notes: "Thank you for your " + """
\(info)
Please refer to URL.
"""
+15
View File
@@ -0,0 +1,15 @@
name: test-notes
version: 1.0.0
description: test
icon: https://www.terraform.io/assets/images/logo-text-8c3ba8a6.svg
url: https://terraform.io/
tags: []
deployTo:
controlPlane: true
runtimeCluster: false
dependencies: []
invisible: false
+1
View File
@@ -66,6 +66,7 @@ type InstallPackage struct {
YAMLTemplates []ElementFile `json:"YAMLTemplates,omitempty"`
AppTemplate *v1beta1.Application `json:"appTemplate"`
AppCueTemplate ElementFile `json:"appCueTemplate,omitempty"`
Notes ElementFile `json:"notes,omitempty"`
}
// WholeAddonPackage contains all infos of an addon
+4 -2
View File
@@ -415,7 +415,8 @@ func (u *addonServiceImpl) EnableAddon(ctx context.Context, name string, args ap
if len(args.RegistryName) != 0 && args.RegistryName != r.Name {
continue
}
err = pkgaddon.EnableAddon(ctx, name, args.Version, u.kubeClient, u.discoveryClient, u.apply, u.config, r, args.Args, u.addonRegistryCache, pkgaddon.FilterDependencyRegistries(i, registries))
// TODO: response the additional info to velaux users
_, err = pkgaddon.EnableAddon(ctx, name, args.Version, u.kubeClient, u.discoveryClient, u.apply, u.config, r, args.Args, u.addonRegistryCache, pkgaddon.FilterDependencyRegistries(i, registries))
if err == nil {
return nil
}
@@ -486,7 +487,8 @@ func (u *addonServiceImpl) UpdateAddon(ctx context.Context, name string, args ap
}
for i, r := range registries {
err = pkgaddon.EnableAddon(ctx, name, args.Version, u.kubeClient, u.discoveryClient, u.apply, u.config, r, args.Args, u.addonRegistryCache, pkgaddon.FilterDependencyRegistries(i, registries))
// TODO: response the additional info to velaux users
_, err = pkgaddon.EnableAddon(ctx, name, args.Version, u.kubeClient, u.discoveryClient, u.apply, u.config, r, args.Args, u.addonRegistryCache, pkgaddon.FilterDependencyRegistries(i, registries))
if err == nil {
return nil
}
+45 -30
View File
@@ -150,7 +150,7 @@ func NewAddonEnableCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Com
vela addon enable <registryName>/<addonName>
`,
RunE: func(cmd *cobra.Command, args []string) error {
var additionalInfo string
if len(args) < 1 {
return fmt.Errorf("must specify addon name")
}
@@ -176,6 +176,11 @@ func NewAddonEnableCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Com
}
addonOrDir := args[0]
var name = addonOrDir
// inject runtime info
addonArgs[pkgaddon.InstallerRuntimeOption] = map[string]interface{}{
"upgrade": false,
}
if file, err := os.Stat(addonOrDir); err == nil {
if !file.IsDir() {
return fmt.Errorf("%s is not addon dir", addonOrDir)
@@ -192,7 +197,7 @@ func NewAddonEnableCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Com
return err
}
}
err = enableAddonByLocal(ctx, name, addonOrDir, k8sClient, dc, config, addonArgs)
additionalInfo, err = enableAddonByLocal(ctx, name, addonOrDir, k8sClient, dc, config, addonArgs)
if err != nil {
return err
}
@@ -205,7 +210,7 @@ func NewAddonEnableCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Com
return err
}
}
err = enableAddon(ctx, k8sClient, dc, config, name, addonVersion, addonArgs)
additionalInfo, err = enableAddon(ctx, k8sClient, dc, config, name, addonVersion, addonArgs)
if err != nil {
return err
}
@@ -214,7 +219,7 @@ func NewAddonEnableCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Com
return nil
}
fmt.Printf("Addon %s enabled successfully.\n", name)
AdditionalEndpointPrinter(ctx, c, k8sClient, name, false)
AdditionalEndpointPrinter(ctx, c, k8sClient, name, additionalInfo, false)
return nil
},
}
@@ -229,7 +234,7 @@ func NewAddonEnableCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Com
}
// AdditionalEndpointPrinter will print endpoints
func AdditionalEndpointPrinter(ctx context.Context, c common.Args, k8sClient client.Client, name string, isUpgrade bool) {
func AdditionalEndpointPrinter(ctx context.Context, c common.Args, k8sClient client.Client, name, info string, isUpgrade bool) {
err := printAppEndpoints(ctx, addonutil.Addon2AppName(name), types.DefaultKubeVelaNS, Filter{}, c, true)
if err != nil {
fmt.Println("Get application endpoints error:", err)
@@ -247,6 +252,9 @@ func AdditionalEndpointPrinter(ctx context.Context, c common.Args, k8sClient cli
fmt.Println()
fmt.Println(`Please refer to https://kubevela.io/docs/reference/addons/velaux for more VelaUX addon installation and visiting method.`)
}
if len(info) > 0 {
fmt.Println(info)
}
}
// NewAddonUpgradeCommand create addon upgrade command
@@ -295,7 +303,13 @@ non-empty new arg
addonInputArgs[types.ClustersArg] = clusterArgs
}
addonOrDir := args[0]
var name string
// inject runtime info
addonInputArgs[pkgaddon.InstallerRuntimeOption] = map[string]interface{}{
"upgrade": true,
}
var name, additionalInfo string
if file, err := os.Stat(addonOrDir); err == nil {
if !file.IsDir() {
return fmt.Errorf("%s is not addon dir", addonOrDir)
@@ -315,7 +329,7 @@ non-empty new arg
if err != nil {
return err
}
err = enableAddonByLocal(ctx, name, addonOrDir, k8sClient, dc, config, addonArgs)
additionalInfo, err = enableAddonByLocal(ctx, name, addonOrDir, k8sClient, dc, config, addonArgs)
if err != nil {
return err
}
@@ -332,14 +346,14 @@ non-empty new arg
if err != nil {
return err
}
err = enableAddon(ctx, k8sClient, dc, config, addonOrDir, addonVersion, addonArgs)
additionalInfo, err = enableAddon(ctx, k8sClient, dc, config, addonOrDir, addonVersion, addonArgs)
if err != nil {
return err
}
}
fmt.Printf("Addon %s enabled successfully.", name)
AdditionalEndpointPrinter(ctx, c, k8sClient, name, true)
fmt.Printf("Addon %s enabled successfully.\n", name)
AdditionalEndpointPrinter(ctx, c, k8sClient, name, additionalInfo, true)
return nil
},
}
@@ -547,16 +561,17 @@ $ HELM_REPO_USERNAME=name HELM_REPO_PASSWORD=pswd vela addon push mongo-1.0.0.tg
return cmd
}
func enableAddon(ctx context.Context, k8sClient client.Client, dc *discovery.DiscoveryClient, config *rest.Config, name string, version string, args map[string]interface{}) error {
func enableAddon(ctx context.Context, k8sClient client.Client, dc *discovery.DiscoveryClient, config *rest.Config, name string, version string, args map[string]interface{}) (string, error) {
var err error
var additionalInfo string
registryDS := pkgaddon.NewRegistryDataStore(k8sClient)
registries, err := registryDS.ListRegistries(ctx)
if err != nil {
return err
return "", err
}
registryName, addonName, err := splitSpecifyRegistry(name)
if err != nil {
return err
return "", err
}
if len(registryName) != 0 {
foundRegistry := false
@@ -566,7 +581,7 @@ func enableAddon(ctx context.Context, k8sClient client.Client, dc *discovery.Dis
}
}
if !foundRegistry {
return fmt.Errorf("specified registry %s not exist", registryName)
return "", fmt.Errorf("specified registry %s not exist", registryName)
}
}
for i, registry := range registries {
@@ -574,7 +589,7 @@ func enableAddon(ctx context.Context, k8sClient client.Client, dc *discovery.Dis
if len(registryName) != 0 && registryName != registry.Name {
continue
}
err = pkgaddon.EnableAddon(ctx, addonName, version, k8sClient, dc, apply.NewAPIApplicator(k8sClient), config, registry, args, nil, pkgaddon.FilterDependencyRegistries(i, registries), opts...)
additionalInfo, err = pkgaddon.EnableAddon(ctx, addonName, version, k8sClient, dc, apply.NewAPIApplicator(k8sClient), config, registry, args, nil, pkgaddon.FilterDependencyRegistries(i, registries), opts...)
if errors.Is(err, pkgaddon.ErrNotExist) || errors.Is(err, pkgaddon.ErrFetch) {
continue
}
@@ -582,28 +597,27 @@ func enableAddon(ctx context.Context, k8sClient client.Client, dc *discovery.Dis
// Get available version of the addon
availableVersion, err := unMatchErr.GetAvailableVersion()
if err != nil {
return err
return "", err
}
input := NewUserInput()
if input.AskBool(unMatchErr.Error(), &UserInputOptions{AssumeYes: false}) {
err = pkgaddon.EnableAddon(ctx, addonName, availableVersion, k8sClient, dc, apply.NewAPIApplicator(k8sClient), config, registry, args, nil, pkgaddon.FilterDependencyRegistries(i, registries))
return err
return pkgaddon.EnableAddon(ctx, addonName, availableVersion, k8sClient, dc, apply.NewAPIApplicator(k8sClient), config, registry, args, nil, pkgaddon.FilterDependencyRegistries(i, registries))
}
// The user does not agree to use the version provided by us
return fmt.Errorf("you can try another version by command: \"vela addon enable %s --version <version> \" ", addonName)
return "", fmt.Errorf("you can try another version by command: \"vela addon enable %s --version <version> \" ", addonName)
}
if err != nil {
return err
return "", err
}
if err = waitApplicationRunning(k8sClient, addonName); err != nil {
return err
return "", err
}
return nil
return additionalInfo, nil
}
if len(registryName) != 0 {
return fmt.Errorf("addon: %s not found in registry %s", addonName, registryName)
return "", fmt.Errorf("addon: %s not found in registry %s", addonName, registryName)
}
return fmt.Errorf("addon: %s not found in all candidate registries", addonName)
return "", fmt.Errorf("addon: %s not found in all candidate registries", addonName)
}
func addonOptions() []pkgaddon.InstallOption {
@@ -621,15 +635,16 @@ func addonOptions() []pkgaddon.InstallOption {
}
// enableAddonByLocal enable addon in local dir and return the addon name
func enableAddonByLocal(ctx context.Context, name string, dir string, k8sClient client.Client, dc *discovery.DiscoveryClient, config *rest.Config, args map[string]interface{}) error {
func enableAddonByLocal(ctx context.Context, name string, dir string, k8sClient client.Client, dc *discovery.DiscoveryClient, config *rest.Config, args map[string]interface{}) (string, error) {
opts := addonOptions()
if err := pkgaddon.EnableAddonByLocalDir(ctx, name, dir, k8sClient, dc, apply.NewAPIApplicator(k8sClient), config, args, opts...); err != nil {
return err
info, err := pkgaddon.EnableAddonByLocalDir(ctx, name, dir, k8sClient, dc, apply.NewAPIApplicator(k8sClient), config, args, opts...)
if err != nil {
return "", err
}
if err := waitApplicationRunning(k8sClient, name); err != nil {
return err
if err = waitApplicationRunning(k8sClient, name); err != nil {
return "", err
}
return nil
return info, nil
}
func disableAddon(client client.Client, name string, config *rest.Config, force bool) error {