func describeDeploymentPodSummaryInline(deploy *kapi.ReplicationController, includeEmpty bool) string { s := describeDeploymentPodSummary(deploy, includeEmpty) if len(s) == 0 { return s } change := "" if changing, ok := deployutil.DeploymentDesiredReplicas(deploy); ok { switch { case changing < deploy.Spec.Replicas: change = fmt.Sprintf(" reducing to %d", changing) case changing > deploy.Spec.Replicas: change = fmt.Sprintf(" growing to %d", changing) } } return fmt.Sprintf(" - %s%s", s, change) }
// Deploy starts the deployment process for deploymentName. func (d *Deployer) Deploy(namespace, deploymentName string) error { // Look up the new deployment. to, err := d.getDeployment(namespace, deploymentName) if err != nil { return fmt.Errorf("couldn't get deployment %s/%s: %v", namespace, deploymentName, err) } // Decode the config from the deployment. config, err := deployutil.DecodeDeploymentConfig(to, latest.Codec) if err != nil { return fmt.Errorf("couldn't decode DeploymentConfig from deployment %s/%s: %v", to.Namespace, to.Name, err) } // Get a strategy for the deployment. strategy, err := d.strategyFor(config) if err != nil { return err } // New deployments must have a desired replica count. desiredReplicas, hasDesired := deployutil.DeploymentDesiredReplicas(to) if !hasDesired { return fmt.Errorf("deployment %s has no desired replica count", deployutil.LabelForDeployment(to)) } // Find all deployments for the config. unsortedDeployments, err := d.getDeployments(namespace, config.Name) if err != nil { return fmt.Errorf("couldn't get controllers in namespace %s: %v", namespace, err) } deployments := unsortedDeployments.Items // Sort all the deployments by version. sort.Sort(deployutil.DeploymentsByLatestVersionDesc(deployments)) // Find any last completed deployment. var from *kapi.ReplicationController for _, candidate := range deployments { if candidate.Name == to.Name { continue } if deployutil.DeploymentStatusFor(&candidate) == deployapi.DeploymentStatusComplete { from = &candidate break } } // Scale down any deployments which aren't the new or last deployment. for _, candidate := range deployments { // Skip the from/to deployments. if candidate.Name == to.Name { continue } if from != nil && candidate.Name == from.Name { continue } // Skip the deployment if it's already scaled down. if candidate.Spec.Replicas == 0 { continue } // Scale the deployment down to zero. retryWaitParams := kubectl.NewRetryParams(1*time.Second, 120*time.Second) if err := d.scaler.Scale(candidate.Namespace, candidate.Name, uint(0), &kubectl.ScalePrecondition{-1, ""}, retryWaitParams, retryWaitParams); err != nil { glog.Errorf("Couldn't scale down prior deployment %s: %v", deployutil.LabelForDeployment(&candidate), err) } else { glog.Infof("Scaled down prior deployment %s", deployutil.LabelForDeployment(&candidate)) } } // Perform the deployment. if from == nil { glog.Infof("Deploying %s for the first time (replicas: %d)", deployutil.LabelForDeployment(to), desiredReplicas) } else { glog.Infof("Deploying from %s to %s (replicas: %d)", deployutil.LabelForDeployment(from), deployutil.LabelForDeployment(to), desiredReplicas) } return strategy.Deploy(from, to, desiredReplicas) }
func (c *DeployerPodController) cleanupFailedDeployment(deployment *kapi.ReplicationController) error { // Scale down the current failed deployment configName := deployutil.DeploymentConfigNameFor(deployment) existingDeployments, err := c.deploymentClient.listDeploymentsForConfig(deployment.Namespace, configName) if err != nil { return fmt.Errorf("couldn't list Deployments for DeploymentConfig %s: %v", configName, err) } desiredReplicas, ok := deployutil.DeploymentDesiredReplicas(deployment) if !ok { // if desired replicas could not be found, then log the error // and update the failed deployment // this cannot be treated as a transient error kutil.HandleError(fmt.Errorf("Could not determine desired replicas from %s to reset replicas for last completed deployment", deployutil.LabelForDeployment(deployment))) } if ok && len(existingDeployments.Items) > 0 { sort.Sort(deployutil.DeploymentsByLatestVersionDesc(existingDeployments.Items)) for index, existing := range existingDeployments.Items { // if a newer deployment exists: // - set the replicas for the current failed deployment to 0 // - there is no point in scaling up the last completed deployment // since that will be scaled down by the later deployment if index == 0 && existing.Name != deployment.Name { break } // the latest completed deployment is the one that needs to be scaled back up if deployutil.DeploymentStatusFor(&existing) == deployapi.DeploymentStatusComplete { if existing.Spec.Replicas == desiredReplicas { break } // scale back the completed deployment to the target of the failed deployment existing.Spec.Replicas = desiredReplicas if _, err := c.deploymentClient.updateDeployment(existing.Namespace, &existing); err != nil { if kerrors.IsNotFound(err) { return nil } return fmt.Errorf("couldn't update replicas to %d for deployment %s: %v", desiredReplicas, deployutil.LabelForDeployment(&existing), err) } glog.V(4).Infof("Updated replicas to %d for deployment %s", desiredReplicas, deployutil.LabelForDeployment(&existing)) break } } } // set the replicas for the failed deployment to 0 // and set the status to Failed deployment.Spec.Replicas = 0 deployment.Annotations[deployapi.DeploymentStatusAnnotation] = string(deployapi.DeploymentStatusFailed) if _, err := c.deploymentClient.updateDeployment(deployment.Namespace, deployment); err != nil { if kerrors.IsNotFound(err) { return nil } return fmt.Errorf("couldn't scale down the deployment %s and mark it as failed: %v", deployutil.LabelForDeployment(deployment), err) } glog.V(4).Infof("Scaled down the deployment %s and marked it as failed", deployutil.LabelForDeployment(deployment)) return nil }
// TestHandle_updateOk ensures that an updated config (version >0) results in // a new deployment with the appropriate replica count based on a variety of // existing prior deployments. func TestHandle_updateOk(t *testing.T) { var ( config *deployapi.DeploymentConfig deployed *kapi.ReplicationController existingDeployments *kapi.ReplicationControllerList ) controller := &DeploymentConfigController{ makeDeployment: func(config *deployapi.DeploymentConfig) (*kapi.ReplicationController, error) { return deployutil.MakeDeployment(config, api.Codec) }, deploymentClient: &deploymentClientImpl{ createDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) { deployed = deployment return deployment, nil }, listDeploymentsForConfigFunc: func(namespace, configName string) (*kapi.ReplicationControllerList, error) { return existingDeployments, nil }, updateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) { t.Fatalf("unexpected update call with deployment %v", deployment) return nil, nil }, }, recorder: &record.FakeRecorder{}, } type existing struct { version int replicas int status deployapi.DeploymentStatus } type scenario struct { version int expectedReplicas int existing []existing } scenarios := []scenario{ {1, 1, []existing{}}, {2, 1, []existing{ {1, 1, deployapi.DeploymentStatusComplete}, }}, {3, 4, []existing{ {1, 0, deployapi.DeploymentStatusComplete}, {2, 4, deployapi.DeploymentStatusComplete}, }}, {3, 4, []existing{ {1, 4, deployapi.DeploymentStatusComplete}, {2, 1, deployapi.DeploymentStatusFailed}, }}, {4, 2, []existing{ {1, 0, deployapi.DeploymentStatusComplete}, {2, 0, deployapi.DeploymentStatusFailed}, {3, 2, deployapi.DeploymentStatusComplete}, }}, // Scramble the order of the previous to ensure we still get it right. {4, 2, []existing{ {2, 0, deployapi.DeploymentStatusFailed}, {3, 2, deployapi.DeploymentStatusComplete}, {1, 0, deployapi.DeploymentStatusComplete}, }}, } for _, scenario := range scenarios { deployed = nil config = deploytest.OkDeploymentConfig(scenario.version) existingDeployments = &kapi.ReplicationControllerList{} for _, e := range scenario.existing { d, _ := deployutil.MakeDeployment(deploytest.OkDeploymentConfig(e.version), api.Codec) d.Spec.Replicas = e.replicas d.Annotations[deployapi.DeploymentStatusAnnotation] = string(e.status) existingDeployments.Items = append(existingDeployments.Items, *d) } err := controller.Handle(config) if deployed == nil { t.Fatalf("expected a deployment") } if err != nil { t.Fatalf("unexpected error: %v", err) } desired, hasDesired := deployutil.DeploymentDesiredReplicas(deployed) if !hasDesired { t.Fatalf("expected desired replicas") } if e, a := scenario.expectedReplicas, desired; e != a { t.Errorf("expected desired replicas %d, got %d", e, a) } } }