Merge pull request #1274 from justinsb/kops_inspect

First step towards cluster-dump command
This commit is contained in:
Justin Santa Barbara 2017-01-04 23:19:36 -05:00 committed by GitHub
commit 94c4e5fb4c
5 changed files with 213 additions and 22 deletions

View File

@ -100,6 +100,7 @@ func NewCmdRoot(f *util.Factory, out io.Writer) *cobra.Command {
cmd.AddCommand(NewCmdEdit(f, out))
cmd.AddCommand(NewCmdUpdate(f, out))
cmd.AddCommand(NewCmdReplace(f, out))
cmd.AddCommand(NewCmdToolbox(f, out))
cmd.AddCommand(NewCmdValidate(f, out))
return cmd

View File

@ -18,14 +18,18 @@ package main
import (
"github.com/spf13/cobra"
"io"
"k8s.io/kops/cmd/kops/util"
)
// toolboxCmd represents the toolbox command
var toolboxCmd = &cobra.Command{
Use: "toolbox",
Short: "Misc infrequently used commands",
}
func NewCmdToolbox(f *util.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "toolbox",
Short: "Misc infrequently used commands",
}
func init() {
rootCommand.AddCommand(toolboxCmd)
cmd.AddCommand(NewCmdToolboxConvertImported(f, out))
cmd.AddCommand(NewCmdToolboxDump(f, out))
return cmd
}

View File

@ -19,46 +19,64 @@ package main
import (
"fmt"
"github.com/spf13/cobra"
"io"
"k8s.io/kops/cmd/kops/util"
api "k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
"k8s.io/kops/upup/pkg/kutil"
k8sapi "k8s.io/kubernetes/pkg/api"
)
type ConvertImportedCmd struct {
type ToolboxConvertImportedOptions struct {
NewClusterName string
// Channel is the location of the api.Channel to use for our defaults
Channel string
ClusterName string
}
var convertImported ConvertImportedCmd
func (o *ToolboxConvertImportedOptions) InitDefaults() {
o.Channel = api.DefaultChannel
}
func NewCmdToolboxConvertImported(f *util.Factory, out io.Writer) *cobra.Command {
options := &ToolboxConvertImportedOptions{}
func init() {
cmd := &cobra.Command{
Use: "convert-imported",
Short: "Convert an imported cluster into a kops cluster",
Run: func(cmd *cobra.Command, args []string) {
err := convertImported.Run()
if err := rootCommand.ProcessArgs(args); err != nil {
exitWithError(err)
}
options.ClusterName = rootCommand.ClusterName()
err := RunToolboxConvertImported(f, out, options)
if err != nil {
exitWithError(err)
}
},
}
toolboxCmd.AddCommand(cmd)
cmd.Flags().StringVar(&options.NewClusterName, "newname", options.NewClusterName, "new cluster name")
cmd.Flags().StringVar(&options.Channel, "channel", options.Channel, "Channel to use for upgrade")
cmd.Flags().StringVar(&convertImported.NewClusterName, "newname", "", "new cluster name")
cmd.Flags().StringVar(&convertImported.Channel, "channel", api.DefaultChannel, "Channel to use for upgrade")
return cmd
}
func (c *ConvertImportedCmd) Run() error {
cluster, err := rootCommand.Cluster()
func RunToolboxConvertImported(f *util.Factory, out io.Writer, options *ToolboxConvertImportedOptions) error {
clientset, err := f.Clientset()
if err != nil {
return err
}
clientset, err := rootCommand.Clientset()
if options.ClusterName == "" {
return fmt.Errorf("ClusterName is required")
}
cluster, err := clientset.Clusters().Get(options.ClusterName)
if err != nil {
return err
}
@ -76,7 +94,7 @@ func (c *ConvertImportedCmd) Run() error {
return fmt.Errorf("cluster %q does not appear to be a cluster imported using kops import", cluster.ObjectMeta.Name)
}
if c.NewClusterName == "" {
if options.NewClusterName == "" {
return fmt.Errorf("--newname is required for converting an imported cluster")
}
@ -110,13 +128,13 @@ func (c *ConvertImportedCmd) Run() error {
return fmt.Errorf("error initializing AWS client: %v", err)
}
channel, err := api.LoadChannel(c.Channel)
channel, err := api.LoadChannel(options.Channel)
if err != nil {
return err
}
d := &kutil.ConvertKubeupCluster{
NewClusterName: c.NewClusterName,
NewClusterName: options.NewClusterName,
OldClusterName: oldClusterName,
Cloud: cloud,
ClusterConfig: cluster,

146
cmd/kops/toolbox_dump.go Normal file
View File

@ -0,0 +1,146 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"encoding/json"
"fmt"
"github.com/golang/glog"
"github.com/spf13/cobra"
"io"
"k8s.io/kops/cmd/kops/util"
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/upup/pkg/fi/cloudup"
"k8s.io/kops/upup/pkg/kutil"
)
type ToolboxDumpOptions struct {
Output string
ClusterName string
}
func (o *ToolboxDumpOptions) InitDefaults() {
o.Output = OutputYaml
}
func NewCmdToolboxDump(f *util.Factory, out io.Writer) *cobra.Command {
options := &ToolboxDumpOptions{}
options.InitDefaults()
cmd := &cobra.Command{
Use: "dump",
Short: "Dump information about a cluster",
Run: func(cmd *cobra.Command, args []string) {
if err := rootCommand.ProcessArgs(args); err != nil {
exitWithError(err)
}
options.ClusterName = rootCommand.ClusterName()
err := RunToolboxDump(f, out, options)
if err != nil {
exitWithError(err)
}
},
}
// TODO: Push up to top-level command?
cmd.Flags().StringVarP(&options.Output, "output", "o", options.Output, "output format. One of: yaml, json")
return cmd
}
func RunToolboxDump(f *util.Factory, out io.Writer, options *ToolboxDumpOptions) error {
clientset, err := f.Clientset()
if err != nil {
return err
}
if options.ClusterName == "" {
return fmt.Errorf("ClusterName is required")
}
cluster, err := clientset.Clusters().Get(options.ClusterName)
if err != nil {
return err
}
if cluster == nil {
return fmt.Errorf("cluster not found %q", options.ClusterName)
}
cloud, err := cloudup.BuildCloud(cluster)
if err != nil {
return err
}
d := &kutil.DeleteCluster{}
d.ClusterName = options.ClusterName
d.Cloud = cloud
resources, err := d.ListResources()
if err != nil {
return err
}
data := make(map[string]interface{})
dumpedResources := []interface{}{}
for k, r := range resources {
if r.Dumper == nil {
glog.V(8).Infof("skipping dump of %q (no Dumper)", k)
continue
}
o, err := r.Dumper(r)
if err != nil {
return fmt.Errorf("error dumping %q: %v", k, err)
}
if o != nil {
dumpedResources = append(dumpedResources, o)
}
}
data["resources"] = dumpedResources
switch options.Output {
case OutputYaml:
b, err := kops.ToRawYaml(data)
if err != nil {
return fmt.Errorf("error marshaling yaml: %v", err)
}
_, err = out.Write(b)
if err != nil {
return fmt.Errorf("error writing to stdout: %v", err)
}
return nil
case OutputJSON:
b, err := json.MarshalIndent(data, "", " ")
if err != nil {
return fmt.Errorf("error marshaling json: %v", err)
}
_, err = out.Write(b)
if err != nil {
return fmt.Errorf("error writing to stdout: %v", err)
}
return nil
default:
return fmt.Errorf("Unsupported output format: %q", options.Output)
}
}

View File

@ -65,6 +65,8 @@ type ResourceTracker struct {
groupKey string
groupDeleter func(cloud fi.Cloud, trackers []*ResourceTracker) error
Dumper func(r *ResourceTracker) (interface{}, error)
obj interface{}
}
@ -484,8 +486,10 @@ func ListInstances(cloud fi.Cloud, clusterName string) ([]*ResourceTracker, erro
tracker := &ResourceTracker{
Name: FindName(instance.Tags),
ID: id,
Type: "instance",
Type: ec2.ResourceTypeInstance,
deleter: DeleteInstance,
Dumper: DumpInstance,
obj: instance,
}
var blocks []string
@ -518,6 +522,14 @@ func ListInstances(cloud fi.Cloud, clusterName string) ([]*ResourceTracker, erro
return trackers, nil
}
func DumpInstance(r *ResourceTracker) (interface{}, error) {
data := make(map[string]interface{})
data["id"] = r.ID
data["type"] = ec2.ResourceTypeInstance
data["raw"] = r.obj
return data, nil
}
func DeleteSecurityGroup(cloud fi.Cloud, t *ResourceTracker) error {
c := cloud.(awsup.AWSCloud)
@ -1236,6 +1248,14 @@ func DeleteVPC(cloud fi.Cloud, r *ResourceTracker) error {
return nil
}
func DumpVPC(r *ResourceTracker) (interface{}, error) {
data := make(map[string]interface{})
data["id"] = r.ID
data["type"] = ec2.ResourceTypeVpc
data["raw"] = r.obj
return data, nil
}
func DescribeVPCs(cloud fi.Cloud) ([]*ec2.Vpc, error) {
c := cloud.(awsup.AWSCloud)
@ -1262,8 +1282,10 @@ func ListVPCs(cloud fi.Cloud, clusterName string) ([]*ResourceTracker, error) {
tracker := &ResourceTracker{
Name: FindName(v.Tags),
ID: aws.StringValue(v.VpcId),
Type: "vpc",
Type: ec2.ResourceTypeVpc,
deleter: DeleteVPC,
Dumper: DumpVPC,
obj: v,
}
var blocks []string