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

Refactoring of "get instances" CLI command #553

Merged
merged 7 commits into from
Jul 11, 2019
Merged
Show file tree
Hide file tree
Changes from 5 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
22 changes: 15 additions & 7 deletions pkg/kudoctl/cmd/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,23 @@ import (
"github.com/spf13/cobra"
)

// newGetCmd creates a new command that lists instances
// newGetCmd creates a command that lists the instances in the cluster
func newGetCmd() *cobra.Command {
newCmd := &cobra.Command{
Use: "get",
Short: "-> Show all available instances.",
Long: `The get command has subcommands to show all available instances.`,
options := get.DefaultOptions
getCmd := &cobra.Command{
Use: "get instances",
Short: "Gets all available instances.",
Long: `
# Get all available instances
kudoctl get instances`,
RunE: func(cmd *cobra.Command, args []string) error {
// Prior to command execution we parse and validate passed parameters
fabianbaier marked this conversation as resolved.
Show resolved Hide resolved
return get.Run(args, options)
},
}

newCmd.AddCommand(get.NewGetInstancesCmd())
getCmd.Flags().StringVar(&options.KubeConfigPath, "kubeconfig", "", "The file path to kubernetes configuration file; defaults to $HOME/.kube/config")
getCmd.Flags().StringVar(&options.Namespace, "namespace", "default", "The namespace where the operator watches for changes.")

return newCmd
return getCmd
}
94 changes: 94 additions & 0 deletions pkg/kudoctl/cmd/get/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package get

import (
"fmt"
"log"
"os"

"github.com/kudobuilder/kudo/pkg/kudoctl/util/kudo"
"github.com/pkg/errors"

"github.com/xlab/treeprint"
"k8s.io/client-go/tools/clientcmd"

"github.com/kudobuilder/kudo/pkg/kudoctl/util/check"
)

// Options defines configuration options for the get command
type Options struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this copy-paste error? How is skipInstance relevant to get instances? 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't call it that. More good intensions to make parts of the CLI following same patterns/look alike.

KubeConfigPath string
Namespace string
}

// DefaultOptions initializes the get command options to its defaults
var DefaultOptions = &Options{
Namespace: "default",
}

// Run returns the errors associated with cmd env
func Run(args []string, options *Options) error {

err := validate(args, options)
if err != nil {
return err
}

kc, err := kudo.NewClient(options.Namespace, options.KubeConfigPath)
if err != nil {
return errors.Wrap(err, "creating kudo client")
}

p, err := getInstances(kc, options)
if err != nil {
log.Printf("Error: %v", err)
}
tree := treeprint.New()

for _, plan := range p {
tree.AddBranch(plan)
}
fmt.Printf("List of current installed instances in namespace \"%s\":\n", options.Namespace)
fmt.Println(tree.String())
return err
}

func validate(args []string, options *Options) error {
if len(args) != 1 {
return fmt.Errorf("expecting exactly one argument - \"instances\"")
}

if args[0] != "instances" {
return fmt.Errorf("expecting \"instances\" and not \"%s\"", args[0])
}

// If the $KUBECONFIG environment variable is set, use that
if len(os.Getenv("KUBECONFIG")) > 0 {
options.KubeConfigPath = os.Getenv("KUBECONFIG")
}

configPath, err := check.KubeConfigLocationOrDefault(options.KubeConfigPath)
if err != nil {
return fmt.Errorf("error when getting default kubeconfig path: %+v", err)
}
options.KubeConfigPath = configPath
if err := check.ValidateKubeConfigPath(options.KubeConfigPath); err != nil {
return errors.WithMessage(err, "could not check kubeconfig path")
}
_, err = clientcmd.BuildConfigFromFlags("", options.KubeConfigPath)
if err != nil {
return errors.Wrap(err, "getting config failed")
}

return nil

}

func getInstances(kc *kudo.Client, options *Options) ([]string, error) {

instanceList, err := kc.ListInstances(options.Namespace)
if err != nil {
return nil, errors.Wrap(err, "getting instances")
}

return instanceList, nil
}
109 changes: 109 additions & 0 deletions pkg/kudoctl/cmd/get/get_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package get

import (
"testing"

"github.com/kudobuilder/kudo/pkg/apis/kudo/v1alpha1"
"github.com/kudobuilder/kudo/pkg/client/clientset/versioned/fake"
"github.com/kudobuilder/kudo/pkg/kudoctl/util/kudo"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestValidate(t *testing.T) {
tests := []struct {
arg []string
opt Options
err string
}{
{nil, *DefaultOptions, "expecting exactly one argument - \"instances\""}, // 1
{[]string{"arg", "arg2"}, *DefaultOptions, "expecting exactly one argument - \"instances\""}, // 2
{[]string{}, *DefaultOptions, "expecting exactly one argument - \"instances\""}, // 3
{[]string{"somethingelse"}, *DefaultOptions, "expecting \"instances\" and not \"somethingelse\""}, // 4
}

for _, tt := range tests {
err := validate(tt.arg, DefaultOptions)
if err != nil {
if err.Error() != tt.err {
t.Errorf("Expecting error message '%s' but got '%s'", tt.err, err)
}
}
}
}

func newTestClient() *kudo.Client {
return kudo.NewClientFromK8s(fake.NewSimpleClientset())
}

func TestGetInstances(t *testing.T) {
testInstance := &v1alpha1.Instance{
TypeMeta: metav1.TypeMeta{
APIVersion: "kudo.k8s.io/v1alpha1",
Kind: "Instance",
},
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"controller-tools.k8s.io": "1.0",
"operator": "test",
},
Name: "test",
},
Spec: v1alpha1.InstanceSpec{
OperatorVersion: v1.ObjectReference{
Name: "test-1.0",
},
},
}
tests := []struct {
arg []string
opt Options
err string
instances []string
}{
{nil, *DefaultOptions, "expecting exactly one argument - \"instances\"", nil}, // 1
{[]string{"arg", "arg2"}, *DefaultOptions, "expecting exactly one argument - \"instances\"", nil}, // 2
{[]string{}, *DefaultOptions, "expecting exactly one argument - \"instances\"", nil}, // 3
{[]string{"somethingelse"}, *DefaultOptions, "expecting \"instances\" and not \"somethingelse\"", nil}, // 4
{[]string{"instances"}, *DefaultOptions, "expecting \"instances\" and not \"somethingelse\"", []string{"test"}}, // 5
}

for i, tt := range tests {
kc := newTestClient()
kc.InstallInstanceObjToCluster(testInstance, "default")
instanceList, err := getInstances(kc, DefaultOptions)
if err != nil {
if err.Error() != tt.err {
t.Errorf("%d: Expecting error message '%s' but got '%s'", i+1, tt.err, err)
}
}
missing := compareSlice(tt.instances, instanceList)
for _, m := range missing {
t.Errorf("%d: Missed expected instance \"%v\"", i+1, m)
}
}
}

func compareSlice(real, mock []string) []string {
lm := len(mock)

var diff []string

for _, rv := range real {
i := 0
j := 0
for _, mv := range mock {
i++
if rv == mv {
continue
}
if rv != mv {
j++
}
if lm <= j {
diff = append(diff, rv)
}
}
}
return diff
}
116 changes: 0 additions & 116 deletions pkg/kudoctl/cmd/get/instances.go

This file was deleted.

14 changes: 14 additions & 0 deletions pkg/kudoctl/util/kudo/kudo.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,20 @@ func (c *Client) InstanceExistsInCluster(name, namespace, version, instanceName
return true, nil
}

// ListInstances lists all instances of given operator installed in the cluster in a given ns
func (c *Client) ListInstances(namespace string) ([]string, error) {
instances, err := c.clientset.KudoV1alpha1().Instances(namespace).List(v1.ListOptions{})
if err != nil {
return nil, err
}
existingInstances := []string{}

for _, v := range instances.Items {
existingInstances = append(existingInstances, v.Name)
}
return existingInstances, nil
}

// OperatorVersionsInstalled lists all the versions of given operator installed in the cluster in given ns
func (c *Client) OperatorVersionsInstalled(operatorName, namespace string) ([]string, error) {
fv, err := c.clientset.KudoV1alpha1().OperatorVersions(namespace).List(v1.ListOptions{})
Expand Down
Loading