Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: addon search #44

Merged
merged 24 commits into from
Nov 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/user_docs/cli/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions docs/user_docs/cli/kbcli_addon.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

1 change: 1 addition & 0 deletions pkg/cmd/addon/addon.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
31 changes: 19 additions & 12 deletions pkg/cmd/addon/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
Expand All @@ -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
}
Expand All @@ -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
}
Expand All @@ -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
}
Expand Down
27 changes: 27 additions & 0 deletions pkg/cmd/addon/index_test.go
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

package addon

import (
Expand All @@ -16,6 +35,7 @@ var _ = Describe("index test", func() {
defaultIndexName = "kubeblocks"
testIndexName = "kb-other"
testIndexURL = "unknown"
testIndexDir = "./testdata"
)
BeforeEach(func() {
streams, _, out, _ = genericiooptions.NewTestIOStreams()
Expand Down Expand Up @@ -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))
})
})
20 changes: 20 additions & 0 deletions pkg/cmd/addon/install.go
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

package addon
132 changes: 132 additions & 0 deletions pkg/cmd/addon/search.go
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

package addon
1aal marked this conversation as resolved.
Show resolved Hide resolved

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
}
82 changes: 82 additions & 0 deletions pkg/cmd/addon/search_test.go
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

package addon
ldming marked this conversation as resolved.
Show resolved Hide resolved

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))
}
})

})
Loading