diff --git a/hack/test-cmd.sh b/hack/test-cmd.sh index 5d4678182089..e3ee8f13502d 100755 --- a/hack/test-cmd.sh +++ b/hack/test-cmd.sh @@ -190,6 +190,7 @@ echo "routes: ok" osc get deploymentConfigs osc get dc osc create -f test/integration/fixtures/test-deployment-config.json +osc describe deploymentConfigs test-deployment-config osc delete deploymentConfigs test-deployment-config echo "deploymentConfigs: ok" diff --git a/pkg/cmd/cli/cmd/factory.go b/pkg/cmd/cli/cmd/factory.go index 9342b5c870c5..abe02245eaf5 100644 --- a/pkg/cmd/cli/cmd/factory.go +++ b/pkg/cmd/cli/cmd/factory.go @@ -64,7 +64,11 @@ func NewFactory(clientConfig clientcmd.ClientConfig) *Factory { if err != nil { return nil, fmt.Errorf("unable to describe %s: %v", mapping.Kind, err) } - describer, ok := describe.DescriberFor(mapping.Kind, cli, "") + kubeClient, err := kclient.New(cfg) + if err != nil { + return nil, fmt.Errorf("unable to describe %s: %v", mapping.Kind, err) + } + describer, ok := describe.DescriberFor(mapping.Kind, cli, kubeClient, "") if !ok { return nil, fmt.Errorf("no description has been implemented for %q", mapping.Kind) } diff --git a/pkg/cmd/cli/describe/deployments.go b/pkg/cmd/cli/describe/deployments.go index 700a0b28af9f..eeb8525580c9 100644 --- a/pkg/cmd/cli/describe/deployments.go +++ b/pkg/cmd/cli/describe/deployments.go @@ -8,9 +8,13 @@ import ( "text/tabwriter" kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" + kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client" + labels "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/openshift/origin/pkg/client" deployapi "github.com/openshift/origin/pkg/deploy/api" + deployutil "github.com/openshift/origin/pkg/deploy/util" ) // DeploymentConfigDescriber generates information about a DeploymentConfig @@ -19,39 +23,63 @@ type DeploymentConfigDescriber struct { } type deploymentDescriberClient interface { - GetDeploymentConfig(namespace, name string) (*deployapi.DeploymentConfig, error) + getDeploymentConfig(namespace, name string) (*deployapi.DeploymentConfig, error) + getDeployment(namespace, name string) (*kapi.ReplicationController, error) + listPods(namespace string, selector labels.Selector) (*kapi.PodList, error) } type genericDeploymentDescriberClient struct { - getDeploymentConfig func(namespace, name string) (*deployapi.DeploymentConfig, error) + getDeploymentConfigFunc func(namespace, name string) (*deployapi.DeploymentConfig, error) + getDeploymentFunc func(namespace, name string) (*kapi.ReplicationController, error) + listPodsFunc func(namespace string, selector labels.Selector) (*kapi.PodList, error) } -func (c *genericDeploymentDescriberClient) GetDeploymentConfig(namespace, name string) (*deployapi.DeploymentConfig, error) { - return c.getDeploymentConfig(namespace, name) +func (c *genericDeploymentDescriberClient) getDeploymentConfig(namespace, name string) (*deployapi.DeploymentConfig, error) { + return c.getDeploymentConfigFunc(namespace, name) +} + +func (c *genericDeploymentDescriberClient) getDeployment(namespace, name string) (*kapi.ReplicationController, error) { + return c.getDeploymentFunc(namespace, name) +} + +func (c *genericDeploymentDescriberClient) listPods(namespace string, selector labels.Selector) (*kapi.PodList, error) { + return c.listPodsFunc(namespace, selector) } func NewDeploymentConfigDescriberForConfig(config *deployapi.DeploymentConfig) *DeploymentConfigDescriber { return &DeploymentConfigDescriber{ client: &genericDeploymentDescriberClient{ - getDeploymentConfig: func(namespace, name string) (*deployapi.DeploymentConfig, error) { + getDeploymentConfigFunc: func(namespace, name string) (*deployapi.DeploymentConfig, error) { return config, nil }, + getDeploymentFunc: func(namespace, name string) (*kapi.ReplicationController, error) { + return nil, kerrors.NewNotFound("ReplicatonController", name) + }, + listPodsFunc: func(namespace string, selector labels.Selector) (*kapi.PodList, error) { + return nil, kerrors.NewNotFound("PodList", fmt.Sprintf("%v", selector)) + }, }, } } -func NewDeploymentConfigDescriber(client client.Interface) *DeploymentConfigDescriber { +func NewDeploymentConfigDescriber(client client.Interface, kclient kclient.Interface) *DeploymentConfigDescriber { return &DeploymentConfigDescriber{ client: &genericDeploymentDescriberClient{ - getDeploymentConfig: func(namespace, name string) (*deployapi.DeploymentConfig, error) { + getDeploymentConfigFunc: func(namespace, name string) (*deployapi.DeploymentConfig, error) { return client.DeploymentConfigs(namespace).Get(name) }, + getDeploymentFunc: func(namespace, name string) (*kapi.ReplicationController, error) { + return kclient.ReplicationControllers(namespace).Get(name) + }, + listPodsFunc: func(namespace string, selector labels.Selector) (*kapi.PodList, error) { + return kclient.Pods(namespace).List(selector) + }, }, } } func (d *DeploymentConfigDescriber) Describe(namespace, name string) (string, error) { - deploymentConfig, err := d.client.GetDeploymentConfig(namespace, name) + deploymentConfig, err := d.client.getDeploymentConfig(namespace, name) if err != nil { return "", err } @@ -67,7 +95,19 @@ func (d *DeploymentConfigDescriber) Describe(namespace, name string) (string, er printStrategy(deploymentConfig.Template.Strategy, out) printTriggers(deploymentConfig.Triggers, out) - printReplicationController(deploymentConfig.Template.ControllerTemplate, out) + printReplicationControllerSpec(deploymentConfig.Template.ControllerTemplate, out) + + deploymentName := deployutil.LatestDeploymentNameForConfig(deploymentConfig) + deployment, err := d.client.getDeployment(namespace, deploymentName) + if err != nil { + if kerrors.IsNotFound(err) { + formatString(out, "Latest Deployment", "") + } else { + formatString(out, "Latest Deployment", fmt.Sprintf("error: %v", err)) + } + } else { + printDeploymentRc(deployment, d.client, out) + } return nil }) @@ -92,7 +132,7 @@ func printStrategy(strategy deployapi.DeploymentStrategy, w io.Writer) { func printTriggers(triggers []deployapi.DeploymentTriggerPolicy, w io.Writer) { if len(triggers) == 0 { - fmt.Fprint(w, "No triggers.") + fmt.Fprint(w, "Triggers:\t\n") return } @@ -103,18 +143,26 @@ func printTriggers(triggers []deployapi.DeploymentTriggerPolicy, w io.Writer) { case deployapi.DeploymentTriggerOnConfigChange: fmt.Fprintf(w, "\t\t\n") case deployapi.DeploymentTriggerOnImageChange: - fmt.Fprintf(w, "\t\tAutomatic:\t%v\n\t\tRepository:\t%s\n\t\tTag:\t%s\n", - t.ImageChangeParams.Automatic, - t.ImageChangeParams.RepositoryName, - t.ImageChangeParams.Tag, - ) + if len(t.ImageChangeParams.RepositoryName) > 0 { + fmt.Fprintf(w, "\t\tAutomatic:\t%v\n\t\tRepository:\t%s\n\t\tTag:\t%s\n", + t.ImageChangeParams.Automatic, + t.ImageChangeParams.RepositoryName, + t.ImageChangeParams.Tag, + ) + } else if len(t.ImageChangeParams.From.Name) > 0 { + fmt.Fprintf(w, "\t\tAutomatic:\t%v\n\t\tImage Repository:\t%s\n\t\tTag:\t%s\n", + t.ImageChangeParams.Automatic, + t.ImageChangeParams.From.Name, + t.ImageChangeParams.Tag, + ) + } default: fmt.Fprint(w, "unknown\n") } } } -func printReplicationController(spec kapi.ReplicationControllerSpec, w io.Writer) error { +func printReplicationControllerSpec(spec kapi.ReplicationControllerSpec, w io.Writer) error { fmt.Fprint(w, "Template:\n") fmt.Fprintf(w, "\tSelector:\t%s\n\tReplicas:\t%d\n", @@ -131,6 +179,43 @@ func printReplicationController(spec kapi.ReplicationControllerSpec, w io.Writer return nil } +func printDeploymentRc(deployment *kapi.ReplicationController, client deploymentDescriberClient, w io.Writer) error { + running, waiting, succeeded, failed, err := getPodStatusForDeployment(deployment, client) + if err != nil { + return err + } + + fmt.Fprint(w, "Latest Deployment:\n") + fmt.Fprintf(w, "\tName:\t%s\n", deployment.Name) + fmt.Fprintf(w, "\tStatus:\t%s\n", deployment.Annotations[deployapi.DeploymentStatusAnnotation]) + fmt.Fprintf(w, "\tSelector:\t%s\n", formatLabels(deployment.Spec.Selector)) + fmt.Fprintf(w, "\tLabels:\t%s\n", formatLabels(deployment.Labels)) + fmt.Fprintf(w, "\tReplicas:\t%d current / %d desired\n", deployment.Status.Replicas, deployment.Spec.Replicas) + fmt.Fprintf(w, "\tPods Status:\t%d Running / %d Waiting / %d Succeeded / %d Failed\n", running, waiting, succeeded, failed) + + return nil +} + +func getPodStatusForDeployment(deployment *kapi.ReplicationController, client deploymentDescriberClient) (running, waiting, succeeded, failed int, err error) { + rcPods, err := client.listPods(deployment.Namespace, labels.SelectorFromSet(deployment.Spec.Selector)) + if err != nil { + return + } + for _, pod := range rcPods.Items { + switch pod.Status.Phase { + case kapi.PodRunning: + running++ + case kapi.PodPending: + waiting++ + case kapi.PodSucceeded: + succeeded++ + case kapi.PodFailed: + failed++ + } + } + return +} + // DeploymentDescriber generates information about a deployment // DEPRECATED. type DeploymentDescriber struct { diff --git a/pkg/cmd/cli/describe/describer.go b/pkg/cmd/cli/describe/describer.go index ea559452cabe..3df9f24184b2 100644 --- a/pkg/cmd/cli/describe/describer.go +++ b/pkg/cmd/cli/describe/describer.go @@ -6,6 +6,7 @@ import ( "strings" "text/tabwriter" + kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client" kctl "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" kruntime "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" @@ -13,7 +14,7 @@ import ( "github.com/openshift/origin/pkg/client" ) -func DescriberFor(kind string, c *client.Client, host string) (kctl.Describer, bool) { +func DescriberFor(kind string, c *client.Client, kclient kclient.Interface, host string) (kctl.Describer, bool) { switch kind { case "Build": return &BuildDescriber{c}, true @@ -22,7 +23,7 @@ func DescriberFor(kind string, c *client.Client, host string) (kctl.Describer, b case "Deployment": return &DeploymentDescriber{c}, true case "DeploymentConfig": - return NewDeploymentConfigDescriber(c), true + return NewDeploymentConfigDescriber(c, kclient), true case "Image": return &ImageDescriber{c}, true case "ImageRepository": diff --git a/pkg/cmd/cli/describe/describer_test.go b/pkg/cmd/cli/describe/describer_test.go index a3e84a671eb1..1ddf6ecf0921 100644 --- a/pkg/cmd/cli/describe/describer_test.go +++ b/pkg/cmd/cli/describe/describer_test.go @@ -4,10 +4,15 @@ import ( "strings" "testing" + kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" + "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/openshift/origin/pkg/client" + deployapi "github.com/openshift/origin/pkg/deploy/api" deployapitest "github.com/openshift/origin/pkg/deploy/api/test" + deployutil "github.com/openshift/origin/pkg/deploy/util" ) type describeClient struct { @@ -24,7 +29,7 @@ func TestDescribeFor(t *testing.T) { "Image", "ImageRepository", "Route", "Project", } for _, o := range testTypesList { - _, ok := DescriberFor(o, c, "") + _, ok := DescriberFor(o, c, &kclient.Fake{}, "") if !ok { t.Errorf("Unable to obtain describer for %s", o) } @@ -59,8 +64,23 @@ func TestDescribers(t *testing.T) { } func TestDeploymentConfigDescriber(t *testing.T) { - config := deployapitest.OkDeploymentConfig(0) - d := NewDeploymentConfigDescriberForConfig(config) + config := deployapitest.OkDeploymentConfig(1) + deployment, _ := deployutil.MakeDeployment(config, kapi.Codec) + podList := &kapi.PodList{} + + d := &DeploymentConfigDescriber{ + client: &genericDeploymentDescriberClient{ + getDeploymentConfigFunc: func(namespace, name string) (*deployapi.DeploymentConfig, error) { + return config, nil + }, + getDeploymentFunc: func(namespace, name string) (*kapi.ReplicationController, error) { + return deployment, nil + }, + listPodsFunc: func(namespace string, selector labels.Selector) (*kapi.PodList, error) { + return podList, nil + }, + }, + } describe := func() { if output, err := d.Describe("test", "deployment"); err != nil { @@ -70,6 +90,7 @@ func TestDeploymentConfigDescriber(t *testing.T) { } } + podList.Items = []kapi.Pod{*mkPod(kapi.PodRunning, 0)} describe() config.Triggers = append(config.Triggers, deployapitest.OkConfigChangeTrigger()) @@ -77,4 +98,24 @@ func TestDeploymentConfigDescriber(t *testing.T) { config.Template.Strategy = deployapitest.OkCustomStrategy() describe() + + config.Triggers[0].ImageChangeParams.RepositoryName = "" + config.Triggers[0].ImageChangeParams.From = kapi.ObjectReference{Name: "imageRepo"} + describe() +} + +func mkPod(status kapi.PodPhase, exitCode int) *kapi.Pod { + return &kapi.Pod{ + ObjectMeta: kapi.ObjectMeta{Name: "PodName"}, + Status: kapi.PodStatus{ + Phase: status, + Info: kapi.PodInfo{ + "container1": kapi.ContainerStatus{ + State: kapi.ContainerState{ + Termination: &kapi.ContainerStateTerminated{ExitCode: exitCode}, + }, + }, + }, + }, + } }