diff --git a/docs/user_docs/cli/cli.md b/docs/user_docs/cli/cli.md
index 8277bb097..47358e2e8 100644
--- a/docs/user_docs/cli/cli.md
+++ b/docs/user_docs/cli/cli.md
@@ -13,6 +13,7 @@ Addon command.
* [kbcli addon enable](kbcli_addon_enable.md) - Enable an addon.
* [kbcli addon index](kbcli_addon_index.md) - Manage custom addon indexes
* [kbcli addon list](kbcli_addon_list.md) - List addons.
+* [kbcli addon search](kbcli_addon_search.md) - search the addon from index
## [alert](kbcli_alert.md)
diff --git a/docs/user_docs/cli/kbcli_addon.md b/docs/user_docs/cli/kbcli_addon.md
index 265b42d78..a0d36c2fe 100644
--- a/docs/user_docs/cli/kbcli_addon.md
+++ b/docs/user_docs/cli/kbcli_addon.md
@@ -42,6 +42,7 @@ Addon command.
* [kbcli addon enable](kbcli_addon_enable.md) - Enable an addon.
* [kbcli addon index](kbcli_addon_index.md) - Manage custom addon indexes
* [kbcli addon list](kbcli_addon_list.md) - List addons.
+* [kbcli addon search](kbcli_addon_search.md) - search the addon from index
#### Go Back to [CLI Overview](cli.md) Homepage.
diff --git a/pkg/cmd/addon/addon.go b/pkg/cmd/addon/addon.go
index c2f91fd02..b0dc5b67b 100644
--- a/pkg/cmd/addon/addon.go
+++ b/pkg/cmd/addon/addon.go
@@ -101,6 +101,7 @@ func NewAddonCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.C
newEnableCmd(f, streams),
newDisableCmd(f, streams),
newIndexCmd(streams),
+ newSearchCmd(streams),
)
return cmd
}
diff --git a/pkg/cmd/addon/index.go b/pkg/cmd/addon/index.go
index 434416b23..e455b72a2 100644
--- a/pkg/cmd/addon/index.go
+++ b/pkg/cmd/addon/index.go
@@ -115,7 +115,11 @@ type updateOption struct {
// validate will check the update index whether existed
func (o *updateOption) validate(args []string) error {
- indexes, err := getAllIndexes()
+ addonDir, err := util.GetCliAddonDir()
+ if err != nil {
+ return err
+ }
+ indexes, err := getAllIndexes(addonDir)
if err != nil {
return err
}
@@ -215,11 +219,14 @@ func addIndex(args []string) error {
}
func listIndexes(out io.Writer) error {
+ addonDir, err := util.GetCliAddonDir()
+ if err != nil {
+ return err
+ }
tbl := printer.NewTablePrinter(out)
tbl.SortBy(1)
tbl.SetHeader("INDEX", "URL")
-
- indexes, err := getAllIndexes()
+ indexes, err := getAllIndexes(addonDir)
if err != nil {
return err
}
@@ -267,7 +274,7 @@ func addDefaultIndex() error {
if err = util.EnsureCloned(types.DefaultAddonIndexURL, defaultIndexDir); err != nil {
return err
}
- fmt.Printf("Default addon index \"kubeblocks\" has been added.")
+ fmt.Printf("Default addon index \"kubeblocks\" has been added.\n")
return nil
} else if err != nil {
return err
@@ -276,12 +283,8 @@ func addDefaultIndex() error {
return nil
}
-func getAllIndexes() ([]index, error) {
- addonDir, err := util.GetCliAddonDir()
- if err != nil {
- return nil, err
- }
- entries, err := os.ReadDir(addonDir)
+func getAllIndexes(indexDir string) ([]index, error) {
+ entries, err := os.ReadDir(indexDir)
if err != nil {
return nil, err
}
@@ -291,7 +294,7 @@ func getAllIndexes() ([]index, error) {
continue
}
indexName := e.Name()
- remote, err := util.GitGetRemoteURL(path.Join(addonDir, indexName))
+ remote, err := util.GitGetRemoteURL(path.Join(indexDir, indexName))
if err != nil {
return nil, err
}
@@ -306,7 +309,11 @@ func getAllIndexes() ([]index, error) {
func indexCompletion() func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
availableComps := []string{}
- indexes, err := getAllIndexes()
+ addonDir, err := util.GetCliAddonDir()
+ if err != nil {
+ return availableComps, cobra.ShellCompDirectiveNoFileComp
+ }
+ indexes, err := getAllIndexes(addonDir)
if err != nil {
return availableComps, cobra.ShellCompDirectiveNoFileComp
}
diff --git a/pkg/cmd/addon/index_test.go b/pkg/cmd/addon/index_test.go
index d3045fc1c..d2a3a3b43 100644
--- a/pkg/cmd/addon/index_test.go
+++ b/pkg/cmd/addon/index_test.go
@@ -1,3 +1,22 @@
+/*
+Copyright (C) 2022-2023 ApeCloud Co., Ltd
+
+This file is part of KubeBlocks project
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see .
+*/
+
package addon
import (
@@ -16,6 +35,7 @@ var _ = Describe("index test", func() {
defaultIndexName = "kubeblocks"
testIndexName = "kb-other"
testIndexURL = "unknown"
+ testIndexDir = "./testdata"
)
BeforeEach(func() {
streams, _, out, _ = genericiooptions.NewTestIOStreams()
@@ -80,4 +100,11 @@ kubeblocks https://github.com/apecloud/block-index.git
}
}
})
+
+ It("test get index", func() {
+ indexes, err := getAllIndexes(testIndexDir)
+ Expect(err).Should(Succeed())
+ Expect(indexes).Should(HaveLen(1))
+ Expect(indexes[0].name).Should(Equal(defaultIndexName))
+ })
})
diff --git a/pkg/cmd/addon/install.go b/pkg/cmd/addon/install.go
new file mode 100644
index 000000000..6a32e487c
--- /dev/null
+++ b/pkg/cmd/addon/install.go
@@ -0,0 +1,20 @@
+/*
+Copyright (C) 2022-2023 ApeCloud Co., Ltd
+
+This file is part of KubeBlocks project
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see .
+*/
+
+package addon
diff --git a/pkg/cmd/addon/search.go b/pkg/cmd/addon/search.go
new file mode 100644
index 000000000..682a922e1
--- /dev/null
+++ b/pkg/cmd/addon/search.go
@@ -0,0 +1,132 @@
+/*
+Copyright (C) 2022-2023 ApeCloud Co., Ltd
+
+This file is part of KubeBlocks project
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see .
+*/
+
+package addon
+
+import (
+ "fmt"
+ "io"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "regexp"
+ "strings"
+
+ "github.com/spf13/cobra"
+ "k8s.io/apimachinery/pkg/util/yaml"
+ "k8s.io/cli-runtime/pkg/genericiooptions"
+ "k8s.io/klog/v2"
+
+ extensionsv1alpha1 "github.com/apecloud/kubeblocks/apis/extensions/v1alpha1"
+ "github.com/apecloud/kubeblocks/pkg/constant"
+
+ "github.com/apecloud/kbcli/pkg/printer"
+ "github.com/apecloud/kbcli/pkg/util"
+)
+
+type searchResult struct {
+ index index
+ addon *extensionsv1alpha1.Addon
+}
+
+func newSearchCmd(streams genericiooptions.IOStreams) *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "search",
+ Short: "search the addon from index",
+ Args: cobra.ExactArgs(1),
+ PersistentPreRun: func(cmd *cobra.Command, _ []string) {
+ util.CheckErr(util.EnableLogToFile(cmd.Flags()))
+ },
+ Run: func(_ *cobra.Command, args []string) {
+ util.CheckErr(search(args, streams.Out))
+ },
+ }
+ return cmd
+}
+
+func search(args []string, out io.Writer) error {
+ tbl := printer.NewTablePrinter(out)
+ tbl.AddRow("ADDON", "VERSION", "INDEX")
+ dir, err := util.GetCliAddonDir()
+ if err != nil {
+ return err
+ }
+ results, err := searchAddon(args[0], dir)
+ if err != nil {
+ return err
+ }
+ if len(results) == 0 {
+ fmt.Fprintf(out, "%s addon not found. Please update your index or check the addon name", args[0])
+ return nil
+ }
+ for _, res := range results {
+ label := res.addon.Labels
+ tbl.AddRow(res.addon.Name, label[constant.AppVersionLabelKey], res.index.name)
+ }
+ tbl.Print()
+ return nil
+}
+
+// searchAddon function will search for the addons with the specified name in the index of the specified directory and return them.
+func searchAddon(name string, indexDir string) ([]searchResult, error) {
+ indexes, err := getAllIndexes(indexDir)
+ if err != nil {
+ return nil, err
+ }
+ var res []searchResult
+ searchInDir := func(i index) error {
+ return filepath.WalkDir(filepath.Join(indexDir, i.name), func(path string, d fs.DirEntry, err error) error {
+ // skip .git .github
+ if ok, _ := regexp.MatchString(`^\..*`, d.Name()); ok && d.IsDir() {
+ return filepath.SkipDir
+ }
+ if d.IsDir() {
+ return nil
+ }
+ if err != nil {
+ klog.V(2).Infof("read the file %s fail : %s", path, err.Error())
+ return nil
+ }
+ if strings.HasSuffix(strings.ToLower(d.Name()), ".yaml") {
+ addon := &extensionsv1alpha1.Addon{}
+ content, _ := os.ReadFile(path)
+ err = yaml.Unmarshal(content, addon)
+ if err != nil {
+ klog.V(2).Infof("unmarshal the yaml %s fail : %s", path, err.Error())
+ }
+ // if there are other types of resources in the current folder, skip it
+ if addon.Kind != "Addon" {
+ return filepath.SkipDir
+ }
+ if addon.Name == name {
+ res = append(res, searchResult{i, addon})
+ }
+ }
+ return nil
+ })
+ }
+
+ for _, e := range indexes {
+ err = searchInDir(e)
+ if err != nil {
+ klog.V(2).Infof("search addon failed due to %s", err.Error())
+ }
+ }
+ return res, nil
+}
diff --git a/pkg/cmd/addon/search_test.go b/pkg/cmd/addon/search_test.go
new file mode 100644
index 000000000..21bcb1018
--- /dev/null
+++ b/pkg/cmd/addon/search_test.go
@@ -0,0 +1,82 @@
+/*
+Copyright (C) 2022-2023 ApeCloud Co., Ltd
+
+This file is part of KubeBlocks project
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see .
+*/
+
+package addon
+
+import (
+ "bytes"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "github.com/apecloud/kubeblocks/pkg/constant"
+
+ "k8s.io/cli-runtime/pkg/genericiooptions"
+)
+
+var _ = Describe("search test", func() {
+ var streams genericiooptions.IOStreams
+ var out *bytes.Buffer
+ const (
+ testAddonName = "apecloud-mysql"
+ testAddonNotExisted = "fake-addon"
+ testIndexDir = "./testdata"
+ )
+ BeforeEach(func() {
+ streams, _, out, _ = genericiooptions.NewTestIOStreams()
+ })
+ It("test search cmd", func() {
+ Expect(newSearchCmd(streams)).ShouldNot(BeNil())
+ })
+
+ It("test search", func() {
+ cmd := newSearchCmd(streams)
+ cmd.Run(cmd, []string{testAddonNotExisted})
+ Expect(out.String()).Should(Equal("fake-addon addon not found. Please update your index or check the addon name"))
+ })
+
+ It("test addon search", func() {
+ expect := []struct {
+ index string
+ kind string
+ addonName string
+ version string
+ }{
+ {
+ "kubeblocks", "Addon", "apecloud-mysql", "0.7.0",
+ },
+ {
+ "kubeblocks", "Addon", "apecloud-mysql", "0.8.0-alpha.5",
+ },
+ {
+ "kubeblocks", "Addon", "apecloud-mysql", "0.8.0-alpha.6",
+ },
+ }
+ result, err := searchAddon(testAddonName, testIndexDir)
+ Expect(err).Should(Succeed())
+ Expect(result).Should(HaveLen(3))
+ for i := range result {
+ Expect(result[i].index.name).Should(Equal(expect[i].index))
+ Expect(result[i].addon.Name).Should(Equal(expect[i].addonName))
+ Expect(result[i].addon.Kind).Should(Equal(expect[i].kind))
+ Expect(result[i].addon.Labels[constant.AppVersionLabelKey]).Should(Equal(expect[i].version))
+ }
+ })
+
+})
diff --git a/pkg/cmd/addon/testdata/kubeblocks/apecloud-mysql/0.7.0.yaml b/pkg/cmd/addon/testdata/kubeblocks/apecloud-mysql/0.7.0.yaml
new file mode 100644
index 000000000..f2a0d6e20
--- /dev/null
+++ b/pkg/cmd/addon/testdata/kubeblocks/apecloud-mysql/0.7.0.yaml
@@ -0,0 +1,21 @@
+apiVersion: extensions.kubeblocks.io/v1alpha1
+kind: Addon
+metadata:
+ annotations:
+ addon.kubeblocks.io/kubeblocks-version: '>=0.7.0'
+ labels:
+ addon.kubeblocks.io/model: RDBMS
+ addon.kubeblocks.io/provider: apecloud
+ app.kubernetes.io/name: apecloud-mysql
+ app.kubernetes.io/version: 0.7.0
+ name: apecloud-mysql
+spec:
+ defaultInstallValues:
+ - enabled: true
+ description: ApeCloud MySQL is a database that is compatible with MySQL syntax and
+ achieves high availability through the utilization of the RAFT consensus protocol.
+ helm:
+ chartLocationURL: https://jihulab.com/api/v4/projects/150246/packages/helm/stable/charts/apecloud-mysql-0.7.0.tgz
+ installable:
+ autoInstall: true
+ type: Helm
diff --git a/pkg/cmd/addon/testdata/kubeblocks/apecloud-mysql/0.8.0-alpha.5.yaml b/pkg/cmd/addon/testdata/kubeblocks/apecloud-mysql/0.8.0-alpha.5.yaml
new file mode 100644
index 000000000..08a7537dd
--- /dev/null
+++ b/pkg/cmd/addon/testdata/kubeblocks/apecloud-mysql/0.8.0-alpha.5.yaml
@@ -0,0 +1,21 @@
+apiVersion: extensions.kubeblocks.io/v1alpha1
+kind: Addon
+metadata:
+ annotations:
+ addon.kubeblocks.io/kubeblocks-version: '>=0.7.0'
+ labels:
+ addon.kubeblocks.io/model: RDBMS
+ addon.kubeblocks.io/provider: apecloud
+ app.kubernetes.io/name: apecloud-mysql
+ app.kubernetes.io/version: 0.8.0-alpha.5
+ name: apecloud-mysql
+spec:
+ defaultInstallValues:
+ - enabled: true
+ description: ApeCloud MySQL is a database that is compatible with MySQL syntax and
+ achieves high availability through the utilization of the RAFT consensus protocol.
+ helm:
+ chartLocationURL: https://jihulab.com/api/v4/projects/150246/packages/helm/stable/charts/apecloud-mysql-0.8.0-alpha.5.tgz
+ installable:
+ autoInstall: true
+ type: Helm
diff --git a/pkg/cmd/addon/testdata/kubeblocks/apecloud-mysql/0.8.0-alpha.6.yaml b/pkg/cmd/addon/testdata/kubeblocks/apecloud-mysql/0.8.0-alpha.6.yaml
new file mode 100644
index 000000000..314f263d0
--- /dev/null
+++ b/pkg/cmd/addon/testdata/kubeblocks/apecloud-mysql/0.8.0-alpha.6.yaml
@@ -0,0 +1,22 @@
+apiVersion: extensions.kubeblocks.io/v1alpha1
+kind: Addon
+metadata:
+ annotations:
+ addon.kubeblocks.io/kubeblocks-version: '>=0.7.0'
+ addons.extensions.kubeblocks.io/default-is-empty: 'true'
+ labels:
+ addon.kubeblocks.io/model: RDBMS
+ addon.kubeblocks.io/provider: apecloud
+ app.kubernetes.io/name: apecloud-mysql
+ app.kubernetes.io/version: 0.8.0-alpha.6
+ name: apecloud-mysql
+spec:
+ defaultInstallValues:
+ - enabled: true
+ description: ApeCloud MySQL is a database that is compatible with MySQL syntax and
+ achieves high availability through the utilization of the RAFT consensus protocol.
+ helm:
+ chartLocationURL: https://jihulab.com/api/v4/projects/150246/packages/helm/stable/charts/apecloud-mysql-0.8.0-alpha.6.tgz
+ installable:
+ autoInstall: true
+ type: Helm
diff --git a/pkg/cmd/addon/testdata/kubeblocks/not-addon/test.txt b/pkg/cmd/addon/testdata/kubeblocks/not-addon/test.txt
new file mode 100644
index 000000000..8d5f8cc3b
--- /dev/null
+++ b/pkg/cmd/addon/testdata/kubeblocks/not-addon/test.txt
@@ -0,0 +1 @@
+Testing a non-YAML file.
diff --git a/pkg/cmd/addon/testdata/kubeblocks/not-addon/test2.yaml b/pkg/cmd/addon/testdata/kubeblocks/not-addon/test2.yaml
new file mode 100644
index 000000000..5de712098
--- /dev/null
+++ b/pkg/cmd/addon/testdata/kubeblocks/not-addon/test2.yaml
@@ -0,0 +1,11 @@
+# Testing a YAML file that is not of addon type.
+apiVersion: v1
+kind: Pod
+metadata:
+ name: random-pod
+spec:
+ containers:
+ - name: random-container
+ image: nginx:latest
+ ports:
+ - containerPort: 80
diff --git a/pkg/cmd/auth/login.go b/pkg/cmd/auth/login.go
index 98c226ed3..09d840b52 100644
--- a/pkg/cmd/auth/login.go
+++ b/pkg/cmd/auth/login.go
@@ -246,7 +246,7 @@ func getFirstContext(token string, orgName string) string {
return ""
}
- if contexts != nil && len(contexts) > 0 {
+ if len(contexts) > 0 {
return contexts[0].Name
}
return ""