diff --git a/cmd/kops/edit_cluster.go b/cmd/kops/edit_cluster.go index d8126e0f11..7383dcf753 100644 --- a/cmd/kops/edit_cluster.go +++ b/cmd/kops/edit_cluster.go @@ -11,8 +11,6 @@ import ( "path/filepath" ) -var editorEnvs = []string{"KUBE_EDITOR", "EDITOR"} - type EditClusterCmd struct { } diff --git a/cmd/kops/edit_instancegroup.go b/cmd/kops/edit_instancegroup.go new file mode 100644 index 0000000000..7602aef215 --- /dev/null +++ b/cmd/kops/edit_instancegroup.go @@ -0,0 +1,81 @@ +package main + +import ( + "fmt" + + "bytes" + "github.com/golang/glog" + "github.com/spf13/cobra" + "k8s.io/kubernetes/pkg/kubectl/cmd/util/editor" + "os" + "path/filepath" +) + +type EditInstanceGroupCmd struct { +} + +var editInstanceGroupCmd EditInstanceGroupCmd + +func init() { + cmd := &cobra.Command{ + Use: "instancegroup", + Aliases: []string{"instancegroups", "ig"}, + Short: "Edit instancegroup", + Long: `Edit an instancegroup configuration.`, + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + glog.Exitf("Specify name of instance group to edit") + } + if len(args) != 1 { + glog.Exitf("Can only edit one instance group at a time!") + } + err := editInstanceGroupCmd.Run(args[0]) + if err != nil { + glog.Exitf("%v", err) + } + }, + } + + editCmd.AddCommand(cmd) +} + +func (c *EditInstanceGroupCmd) Run(groupName string) error { + stateStore, err := rootCommand.StateStore() + if err != nil { + return err + } + + var ( + edit = editor.NewDefaultEditor(editorEnvs) + ) + + ext := "yaml" + + raw, err := stateStore.VFSPath().Join("instancegroup", groupName).ReadFile() + if err != nil { + return fmt.Errorf("error reading instancegroup file: %v", err) + } + + // launch the editor + edited, file, err := edit.LaunchTempFile(fmt.Sprintf("%s-edit-", filepath.Base(os.Args[0])), ext, bytes.NewReader(raw)) + defer func() { + if file != "" { + os.Remove(file) + } + }() + if err != nil { + return fmt.Errorf("error launching editor: %v", err) + } + + if bytes.Equal(edited, raw) { + fmt.Fprintln(os.Stderr, "Edit cancelled, no changes made.") + return nil + } + + err = stateStore.VFSPath().Join("instancegroup", groupName).WriteFile(edited) + if err != nil { + return fmt.Errorf("error writing instancegroup file: %v", err) + } + + return nil +} diff --git a/cmd/kops/editor.go b/cmd/kops/editor.go new file mode 100644 index 0000000000..04d411201a --- /dev/null +++ b/cmd/kops/editor.go @@ -0,0 +1,3 @@ +package main + +var editorEnvs = []string{"KUBE_EDITOR", "EDITOR"} diff --git a/cmd/kops/format.go b/cmd/kops/format.go new file mode 100644 index 0000000000..de987cb1ba --- /dev/null +++ b/cmd/kops/format.go @@ -0,0 +1,92 @@ +package main + +import ( + "bytes" + "fmt" + "github.com/golang/glog" + "io" + "k8s.io/kops/upup/pkg/fi" + "reflect" + "text/tabwriter" +) + +// Table renders tables to stdout +type Table struct { + columns []string + getters []reflect.Value +} + +// AddColumn registers an available column for formatting +func (t *Table) AddColumn(name string, getter interface{}) { + getterVal := reflect.ValueOf(getter) + + t.columns = append(t.columns, name) + t.getters = append(t.getters, getterVal) +} + +// Render writes the items in a table, to out +func (t *Table) Render(items interface{}, out io.Writer) error { + itemsValue := reflect.ValueOf(items) + if itemsValue.Kind() != reflect.Slice { + glog.Fatal("unexpected kind for items: ", itemsValue.Kind()) + } + + length := itemsValue.Len() + + var b bytes.Buffer + w := new(tabwriter.Writer) + + // Format in tab-separated columns with a tab stop of 8. + w.Init(out, 0, 8, 0, '\t', tabwriter.StripEscape) + + writeHeader := true + if writeHeader { + for i, c := range t.columns { + if i != 0 { + b.WriteByte('\t') + } + b.WriteByte(tabwriter.Escape) + b.WriteString(c) + b.WriteByte(tabwriter.Escape) + } + b.WriteByte('\n') + + _, err := w.Write(b.Bytes()) + if err != nil { + return fmt.Errorf("error writing to output: %v", err) + } + b.Reset() + } + + for i := 0; i < length; i++ { + item := itemsValue.Index(i) + + for j := range t.columns { + if j != 0 { + b.WriteByte('\t') + } + + getter := t.getters[j] + var args []reflect.Value + args = append(args, item) + fvs := getter.Call(args) + fv := fvs[0] + + s := fi.ValueAsString(fv) + + b.WriteByte(tabwriter.Escape) + b.WriteString(s) + b.WriteByte(tabwriter.Escape) + } + b.WriteByte('\n') + + _, err := w.Write(b.Bytes()) + if err != nil { + return fmt.Errorf("error writing to output: %v", err) + } + b.Reset() + } + w.Flush() + + return nil +} diff --git a/cmd/kops/get_cluster.go b/cmd/kops/get_cluster.go index 62532e98af..dc597cd3a1 100644 --- a/cmd/kops/get_cluster.go +++ b/cmd/kops/get_cluster.go @@ -1,16 +1,11 @@ package main import ( - "fmt" + "os" - "bytes" "github.com/golang/glog" "github.com/spf13/cobra" "k8s.io/kops/upup/pkg/api" - "k8s.io/kops/upup/pkg/fi" - "os" - "reflect" - "text/tabwriter" ) type GetClustersCmd struct { @@ -20,8 +15,8 @@ var getClustersCmd GetClustersCmd func init() { cmd := &cobra.Command{ - Use: "cluster", - Aliases: []string{"clusters"}, + Use: "clusters", + Aliases: []string{"cluster"}, Short: "get clusters", Long: `List or get clusters.`, Run: func(cmd *cobra.Command, args []string) { @@ -41,14 +36,6 @@ func (c *GetClustersCmd) Run() error { return err } - columns := []string{} - fields := []func(*api.Cluster) string{} - - columns = append(columns, "NAME") - fields = append(fields, func(c *api.Cluster) string { - return c.Name - }) - var clusters []*api.Cluster for _, clusterName := range clusterNames { @@ -58,82 +45,17 @@ func (c *GetClustersCmd) Run() error { } // TODO: Faster if we don't read groups... - // We probably can just have a comand which directly reads all cluster config files + // We probably can just have a command which directly reads all cluster config files cluster, _, err := api.ReadConfig(stateStore) clusters = append(clusters, cluster) } if len(clusters) == 0 { return nil } - return WriteTable(clusters, columns, fields) -} - -func WriteTable(items interface{}, columns []string, fields interface{}) error { - itemsValue := reflect.ValueOf(items) - if itemsValue.Kind() != reflect.Slice { - glog.Fatal("unexpected kind for items in WriteTable: ", itemsValue.Kind()) - } - fieldsValue := reflect.ValueOf(fields) - if fieldsValue.Kind() != reflect.Slice { - glog.Fatal("unexpected kind for fields in WriteTable: ", fieldsValue.Kind()) - } - - length := itemsValue.Len() - - var b bytes.Buffer - w := new(tabwriter.Writer) - - // Format in tab-separated columns with a tab stop of 8. - w.Init(os.Stdout, 0, 8, 0, '\t', tabwriter.StripEscape) - - writeHeader := true - if writeHeader { - for i, c := range columns { - if i != 0 { - b.WriteByte('\t') - } - b.WriteByte(tabwriter.Escape) - b.WriteString(c) - b.WriteByte(tabwriter.Escape) - } - b.WriteByte('\n') - - _, err := w.Write(b.Bytes()) - if err != nil { - return fmt.Errorf("error writing to output: %v", err) - } - b.Reset() - } - - for i := 0; i < length; i++ { - item := itemsValue.Index(i) - - for j := range columns { - if j != 0 { - b.WriteByte('\t') - } - - fieldFunc := fieldsValue.Index(j) - var args []reflect.Value - args = append(args, item) - fvs := fieldFunc.Call(args) - fv := fvs[0] - - s := fi.ValueAsString(fv) - - b.WriteByte(tabwriter.Escape) - b.WriteString(s) - b.WriteByte(tabwriter.Escape) - } - b.WriteByte('\n') - - _, err := w.Write(b.Bytes()) - if err != nil { - return fmt.Errorf("error writing to output: %v", err) - } - b.Reset() - } - w.Flush() - - return nil + + t := &Table{} + t.AddColumn("NAME", func(c *api.Cluster) string { + return c.Name + }) + return t.Render(clusters, os.Stdout) } diff --git a/cmd/kops/get_instancegroups.go b/cmd/kops/get_instancegroups.go new file mode 100644 index 0000000000..354b54d885 --- /dev/null +++ b/cmd/kops/get_instancegroups.go @@ -0,0 +1,53 @@ +package main + +import ( + "os" + + "github.com/golang/glog" + "github.com/spf13/cobra" + "k8s.io/kops/upup/pkg/api" +) + +type GetInstanceGroupsCmd struct { +} + +var getInstanceGroupsCmd GetInstanceGroupsCmd + +func init() { + cmd := &cobra.Command{ + Use: "instancegroups", + Aliases: []string{"instancegroup", "ig"}, + Short: "get instancegroups", + Long: `List or get InstanceGroups.`, + Run: func(cmd *cobra.Command, args []string) { + err := getInstanceGroupsCmd.Run() + if err != nil { + glog.Exitf("%v", err) + } + }, + } + + getCmd.AddCommand(cmd) +} + +func (c *GetInstanceGroupsCmd) Run() error { + stateStore, err := rootCommand.StateStore() + if err != nil { + return err + } + + _, instancegroups, err := api.ReadConfig(stateStore) + if err != nil { + return err + } + + if len(instancegroups) == 0 { + return nil + } + + t := &Table{} + t.AddColumn("NAME", func(c *api.InstanceGroup) string { + return c.Name + }) + return t.Render(instancegroups, os.Stdout) +}