From 586eea06916f20977f5b71acd65cfe8d58f10d21 Mon Sep 17 00:00:00 2001 From: jianhuiz Date: Tue, 26 Apr 2016 07:33:47 -0700 Subject: [PATCH 1/2] add get/create/delete for clusters --- contrib/completions/bash/kubectl | 10 ++++++ pkg/kubectl/cmd/util/clientcache.go | 47 +++++++++++++++++++++++++++-- pkg/kubectl/cmd/util/factory.go | 17 +++++++++++ pkg/kubectl/resource_printer.go | 36 ++++++++++++++++++++++ 4 files changed, 107 insertions(+), 3 deletions(-) diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl index 528ae4c6d6a..cbbff861852 100644 --- a/contrib/completions/bash/kubectl +++ b/contrib/completions/bash/kubectl @@ -404,6 +404,7 @@ _kubectl_get() must_have_one_flag=() must_have_one_noun=() + must_have_one_noun+=("cluster") must_have_one_noun+=("componentstatus") must_have_one_noun+=("configmap") must_have_one_noun+=("daemonset") @@ -431,6 +432,7 @@ _kubectl_get() must_have_one_noun+=("thirdpartyresource") must_have_one_noun+=("thirdpartyresourcedata") noun_aliases=() + noun_aliases+=("clusters") noun_aliases+=("componentstatuses") noun_aliases+=("configmaps") noun_aliases+=("cs") @@ -1186,6 +1188,7 @@ _kubectl_patch() must_have_one_flag+=("--patch=") must_have_one_flag+=("-p") must_have_one_noun=() + must_have_one_noun+=("cluster") must_have_one_noun+=("componentstatus") must_have_one_noun+=("configmap") must_have_one_noun+=("daemonset") @@ -1213,6 +1216,7 @@ _kubectl_patch() must_have_one_noun+=("thirdpartyresource") must_have_one_noun+=("thirdpartyresourcedata") noun_aliases=() + noun_aliases+=("clusters") noun_aliases+=("componentstatuses") noun_aliases+=("configmaps") noun_aliases+=("cs") @@ -1317,6 +1321,7 @@ _kubectl_delete() must_have_one_flag=() must_have_one_noun=() + must_have_one_noun+=("cluster") must_have_one_noun+=("componentstatus") must_have_one_noun+=("configmap") must_have_one_noun+=("daemonset") @@ -1344,6 +1349,7 @@ _kubectl_delete() must_have_one_noun+=("thirdpartyresource") must_have_one_noun+=("thirdpartyresourcedata") noun_aliases=() + noun_aliases+=("clusters") noun_aliases+=("componentstatuses") noun_aliases+=("configmaps") noun_aliases+=("cs") @@ -1444,6 +1450,7 @@ _kubectl_edit() must_have_one_flag=() must_have_one_noun=() + must_have_one_noun+=("cluster") must_have_one_noun+=("componentstatus") must_have_one_noun+=("configmap") must_have_one_noun+=("daemonset") @@ -1471,6 +1478,7 @@ _kubectl_edit() must_have_one_noun+=("thirdpartyresource") must_have_one_noun+=("thirdpartyresourcedata") noun_aliases=() + noun_aliases+=("clusters") noun_aliases+=("componentstatuses") noun_aliases+=("configmaps") noun_aliases+=("cs") @@ -2769,6 +2777,7 @@ _kubectl_label() must_have_one_flag=() must_have_one_noun=() + must_have_one_noun+=("cluster") must_have_one_noun+=("componentstatus") must_have_one_noun+=("configmap") must_have_one_noun+=("daemonset") @@ -2796,6 +2805,7 @@ _kubectl_label() must_have_one_noun+=("thirdpartyresource") must_have_one_noun+=("thirdpartyresourcedata") noun_aliases=() + noun_aliases+=("clusters") noun_aliases+=("componentstatuses") noun_aliases+=("configmaps") noun_aliases+=("cs") diff --git a/pkg/kubectl/cmd/util/clientcache.go b/pkg/kubectl/cmd/util/clientcache.go index 5e6551cdee7..43ddf3e9823 100644 --- a/pkg/kubectl/cmd/util/clientcache.go +++ b/pkg/kubectl/cmd/util/clientcache.go @@ -17,6 +17,7 @@ limitations under the License. package util import ( + fed_clientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_internalclientset" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/client/restclient" @@ -26,9 +27,10 @@ import ( func NewClientCache(loader clientcmd.ClientConfig) *ClientCache { return &ClientCache{ - clients: make(map[unversioned.GroupVersion]*client.Client), - configs: make(map[unversioned.GroupVersion]*restclient.Config), - loader: loader, + clients: make(map[unversioned.GroupVersion]*client.Client), + configs: make(map[unversioned.GroupVersion]*restclient.Config), + fedClientSets: make(map[unversioned.GroupVersion]fed_clientset.Interface), + loader: loader, } } @@ -37,6 +39,7 @@ func NewClientCache(loader clientcmd.ClientConfig) *ClientCache { type ClientCache struct { loader clientcmd.ClientConfig clients map[unversioned.GroupVersion]*client.Client + fedClientSets map[unversioned.GroupVersion]fed_clientset.Interface configs map[unversioned.GroupVersion]*restclient.Config defaultConfig *restclient.Config defaultClient *client.Client @@ -125,3 +128,41 @@ func (c *ClientCache) ClientForVersion(version *unversioned.GroupVersion) (*clie return kubeclient, nil } + +func (c *ClientCache) FederationClientSetForVersion(version *unversioned.GroupVersion) (fed_clientset.Interface, error) { + if version != nil { + if clientSet, found := c.fedClientSets[*version]; found { + return clientSet, nil + } + } + config, err := c.ClientConfigForVersion(version) + if err != nil { + return nil, err + } + + // TODO: support multi versions of client with clientset + clientSet, err := fed_clientset.NewForConfig(config) + if err != nil { + return nil, err + } + c.fedClientSets[*config.GroupVersion] = clientSet + + if version != nil { + configCopy := *config + clientSet, err := fed_clientset.NewForConfig(&configCopy) + if err != nil { + return nil, err + } + c.fedClientSets[*version] = clientSet + } + + return clientSet, nil +} + +func (c *ClientCache) FederationClientForVersion(version *unversioned.GroupVersion) (*restclient.RESTClient, error) { + fedClientSet, err := c.FederationClientSetForVersion(version) + if err != nil { + return nil, err + } + return fedClientSet.(*fed_clientset.Clientset).FederationClient.RESTClient, nil +} diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 2f265251866..0f1350b8518 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -36,6 +36,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" + "k8s.io/kubernetes/federation/apis/federation" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/unversioned" @@ -295,11 +296,13 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { {Group: api.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource}, {Group: extensions.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource}, {Group: metrics.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource}, + {Group: federation.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource}, }, KindPriority: []unversioned.GroupVersionKind{ {Group: api.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind}, {Group: extensions.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind}, {Group: metrics.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind}, + {Group: federation.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind}, }, } return priorityRESTMapper, api.Scheme @@ -334,6 +337,8 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { return c.RESTClient, nil case extensions.SchemeGroupVersion.Group: return c.ExtensionsClient.RESTClient, nil + case federation.GroupName: + return clients.FederationClientForVersion(&mappingVersion) default: if !registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) { return nil, fmt.Errorf("unknown api group/version: %s", gvk.String()) @@ -612,8 +617,13 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { } dir = path.Join(cacheDir, version.String()) } + fedClient, err := clients.FederationClientForVersion(nil) + if err != nil { + return nil, err + } return &clientSwaggerSchema{ c: client, + fedc: fedClient, cacheDir: dir, mapper: api.RESTMapper, }, nil @@ -810,6 +820,7 @@ func getServiceProtocols(spec api.ServiceSpec) map[string]string { type clientSwaggerSchema struct { c *client.Client + fedc *restclient.RESTClient cacheDir string mapper meta.RESTMapper } @@ -974,6 +985,12 @@ func (c *clientSwaggerSchema) ValidateBytes(data []byte) error { } return getSchemaAndValidate(c.c.ExtensionsClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c) } + if gvk.Group == federation.GroupName { + if c.fedc == nil { + return errors.New("unable to validate: no federation client") + } + return getSchemaAndValidate(c.fedc, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c) + } return getSchemaAndValidate(c.c.RESTClient, data, "api", gvk.GroupVersion().String(), c.cacheDir, c) } diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index e29a952d27c..0412c3d55de 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -32,6 +32,7 @@ import ( "github.com/ghodss/yaml" "github.com/golang/glog" + "k8s.io/kubernetes/federation/apis/federation" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/unversioned" @@ -442,6 +443,7 @@ var withNamespacePrefixColumns = []string{"NAMESPACE"} // TODO(erictune): print var deploymentColumns = []string{"NAME", "DESIRED", "CURRENT", "UP-TO-DATE", "AVAILABLE", "AGE"} var configMapColumns = []string{"NAME", "DATA", "AGE"} var podSecurityPolicyColumns = []string{"NAME", "PRIV", "CAPS", "VOLUMEPLUGINS", "SELINUX", "RUNASUSER"} +var clusterColumns = []string{"NAME", "STATUS", "VERSION", "AGE"} // addDefaultHandlers adds print handlers for default Kubernetes types. func (h *HumanReadablePrinter) addDefaultHandlers() { @@ -497,6 +499,8 @@ func (h *HumanReadablePrinter) addDefaultHandlers() { h.Handler(podSecurityPolicyColumns, printPodSecurityPolicyList) h.Handler(thirdPartyResourceDataColumns, printThirdPartyResourceData) h.Handler(thirdPartyResourceDataColumns, printThirdPartyResourceDataList) + h.Handler(clusterColumns, printCluster) + h.Handler(clusterColumns, printClusterList) } func (h *HumanReadablePrinter) unknown(data []byte, w io.Writer) error { @@ -802,6 +806,38 @@ func printReplicaSetList(list *extensions.ReplicaSetList, w io.Writer, options P return nil } +func printCluster(c *federation.Cluster, w io.Writer, options PrintOptions) error { + var statuses []string + for _, condition := range c.Status.Conditions { + if condition.Status == api.ConditionTrue { + statuses = append(statuses, string(condition.Type)) + } else { + statuses = append(statuses, "Not"+string(condition.Type)) + } + } + if len(statuses) == 0 { + statuses = append(statuses, "Unknown") + } + + if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", + c.Name, + strings.Join(statuses, ","), + c.Status.Version, + translateTimestamp(c.CreationTimestamp), + ); err != nil { + return err + } + return nil +} +func printClusterList(list *federation.ClusterList, w io.Writer, options PrintOptions) error { + for _, rs := range list.Items { + if err := printCluster(&rs, w, options); err != nil { + return err + } + } + return nil +} + func printJob(job *batch.Job, w io.Writer, options PrintOptions) error { name := job.Name namespace := job.Namespace From 6f8fc60d06ff73aa0fada4b8eca4532bf40c19b7 Mon Sep 17 00:00:00 2001 From: jianhuiz Date: Thu, 5 May 2016 10:30:50 -0700 Subject: [PATCH 2/2] describe for clusters --- pkg/kubectl/cmd/util/factory.go | 9 +++++ pkg/kubectl/describe.go | 59 +++++++++++++++++++++++++++++++++ pkg/kubectl/describe_test.go | 36 ++++++++++++++++++++ 3 files changed, 104 insertions(+) diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 0f1350b8518..f2f213e3796 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -356,6 +356,15 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { }, Describer: func(mapping *meta.RESTMapping) (kubectl.Describer, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() + if mapping.GroupVersionKind.Group == federation.GroupName { + fedClientSet, err := clients.FederationClientSetForVersion(&mappingVersion) + if err != nil { + return nil, err + } + if mapping.GroupVersionKind.Kind == "Cluster" { + return &kubectl.ClusterDescriber{Interface: fedClientSet}, nil + } + } client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err diff --git a/pkg/kubectl/describe.go b/pkg/kubectl/describe.go index c0189f56041..09e234e4227 100644 --- a/pkg/kubectl/describe.go +++ b/pkg/kubectl/describe.go @@ -29,6 +29,8 @@ import ( "time" "github.com/golang/glog" + "k8s.io/kubernetes/federation/apis/federation" + fed_clientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_internalclientset" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/resource" @@ -2016,6 +2018,63 @@ func describeConfigMap(configMap *api.ConfigMap) (string, error) { }) } +type ClusterDescriber struct { + fed_clientset.Interface +} + +func (d *ClusterDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { + cluster, err := d.Federation().Clusters().Get(name) + if err != nil { + return "", err + } + return describeCluster(cluster) +} + +func describeCluster(cluster *federation.Cluster) (string, error) { + return tabbedString(func(out io.Writer) error { + fmt.Fprintf(out, "Name:\t%s\n", cluster.Name) + fmt.Fprintf(out, "Labels:\t%s\n", labels.FormatLabels(cluster.Labels)) + + fmt.Fprintf(out, "ServerAddressByClientCIDRs:\n ClientCIDR\tServerAddress\n") + fmt.Fprintf(out, " ----\t----\n") + for _, cidrAddr := range cluster.Spec.ServerAddressByClientCIDRs { + fmt.Fprintf(out, " %v \t%v\n\n", cidrAddr.ClientCIDR, cidrAddr.ServerAddress) + } + + if len(cluster.Status.Conditions) > 0 { + fmt.Fprint(out, "Conditions:\n Type\tStatus\tLastUpdateTime\tLastTransitionTime\tReason\tMessage\n") + fmt.Fprint(out, " ----\t------\t-----------------\t------------------\t------\t-------\n") + for _, c := range cluster.Status.Conditions { + fmt.Fprintf(out, " %v \t%v \t%s \t%s \t%v \t%v\n", + c.Type, + c.Status, + c.LastProbeTime.Time.Format(time.RFC1123Z), + c.LastTransitionTime.Time.Format(time.RFC1123Z), + c.Reason, + c.Message) + } + } + + fmt.Fprintf(out, "Version:\t%s\n", cluster.Status.Version) + + if len(cluster.Status.Capacity) > 0 { + fmt.Fprintf(out, "Capacity:\n") + for resource, value := range cluster.Status.Capacity { + fmt.Fprintf(out, " %s:\t%s\n", resource, value.String()) + } + } + + if len(cluster.Status.Allocatable) > 0 { + fmt.Fprintf(out, "Allocatable:\n") + for resource, value := range cluster.Status.Allocatable { + fmt.Fprintf(out, " %s:\t%s\n", resource, value.String()) + } + } + + return nil + }) +} + // newErrNoDescriber creates a new ErrNoDescriber with the names of the provided types. func newErrNoDescriber(types ...reflect.Type) error { names := []string{} diff --git a/pkg/kubectl/describe_test.go b/pkg/kubectl/describe_test.go index f073d3050cd..be6e1d3f9cb 100644 --- a/pkg/kubectl/describe_test.go +++ b/pkg/kubectl/describe_test.go @@ -24,6 +24,8 @@ import ( "testing" "time" + "k8s.io/kubernetes/federation/apis/federation" + fed_fake "k8s.io/kubernetes/federation/client/clientset_generated/federation_internalclientset/fake" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/resource" "k8s.io/kubernetes/pkg/api/unversioned" @@ -522,6 +524,40 @@ func TestDescribeDeployment(t *testing.T) { } } +func TestDescribeCluster(t *testing.T) { + cluster := federation.Cluster{ + ObjectMeta: api.ObjectMeta{ + Name: "foo", + ResourceVersion: "4", + Labels: map[string]string{ + "name": "foo", + }, + }, + Spec: federation.ClusterSpec{ + ServerAddressByClientCIDRs: []federation.ServerAddressByClientCIDR{ + { + ClientCIDR: "0.0.0.0/0", + ServerAddress: "localhost:8888", + }, + }, + }, + Status: federation.ClusterStatus{ + Conditions: []federation.ClusterCondition{ + {Type: federation.ClusterReady, Status: api.ConditionTrue}, + }, + }, + } + fake := fed_fake.NewSimpleClientset(&cluster) + d := ClusterDescriber{Interface: fake} + out, err := d.Describe("any", "foo", DescriberSettings{ShowEvents: true}) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if !strings.Contains(out, "foo") || !strings.Contains(out, "Version:") { + t.Errorf("unexpected out: %s", out) + } +} + func TestDescribeEvents(t *testing.T) { events := &api.EventList{