// makeDeployerPod creates a pod which implements deployment behavior. The pod is correlated to // the deployment with an annotation. func (c *DeploymentController) makeDeployerPod(deployment *kapi.ReplicationController) (*kapi.Pod, error) { deploymentConfig, err := c.decodeConfig(deployment) if err != nil { return nil, err } container, err := c.makeContainer(&deploymentConfig.Spec.Strategy) if err != nil { return nil, err } // Add deployment environment variables to the container. envVars := []kapi.EnvVar{} for _, env := range container.Env { envVars = append(envVars, env) } envVars = append(envVars, kapi.EnvVar{Name: "OPENSHIFT_DEPLOYMENT_NAME", Value: deployment.Name}) envVars = append(envVars, kapi.EnvVar{Name: "OPENSHIFT_DEPLOYMENT_NAMESPACE", Value: deployment.Namespace}) // Assigning to a variable since its address is required maxDeploymentDurationSeconds := deployapi.MaxDeploymentDurationSeconds pod := &kapi.Pod{ ObjectMeta: kapi.ObjectMeta{ Name: deployutil.DeployerPodNameForDeployment(deployment.Name), Annotations: map[string]string{ deployapi.DeploymentAnnotation: deployment.Name, }, Labels: map[string]string{ deployapi.DeployerPodForDeploymentLabel: deployment.Name, }, }, Spec: kapi.PodSpec{ Containers: []kapi.Container{ { Name: "deployment", Command: container.Command, Args: container.Args, Image: container.Image, Env: envVars, Resources: deploymentConfig.Spec.Strategy.Resources, }, }, ActiveDeadlineSeconds: &maxDeploymentDurationSeconds, // Setting the node selector on the deployer pod so that it is created // on the same set of nodes as the pods. NodeSelector: deployment.Spec.Template.Spec.NodeSelector, RestartPolicy: kapi.RestartPolicyNever, ServiceAccountName: c.serviceAccount, }, } // MergeInfo will not overwrite values unless the flag OverwriteExistingDstKey is set. util.MergeInto(pod.Labels, deploymentConfig.Spec.Strategy.Labels, 0) util.MergeInto(pod.Annotations, deploymentConfig.Spec.Strategy.Annotations, 0) pod.Spec.Containers[0].ImagePullPolicy = kapi.PullIfNotPresent return pod, nil }
// makeHookPod makes a pod spec from a hook and deployment. func makeHookPod(hook *deployapi.LifecycleHook, deployment *kapi.ReplicationController, deployerPod *kapi.Pod, strategy *deployapi.DeploymentStrategy, suffix string) (*kapi.Pod, error) { exec := hook.ExecNewPod var baseContainer *kapi.Container for _, container := range deployment.Spec.Template.Spec.Containers { if container.Name == exec.ContainerName { baseContainer = &container break } } if baseContainer == nil { return nil, fmt.Errorf("no container named '%s' found in deployment template", exec.ContainerName) } // Build a merged environment; hook environment takes precedence over base // container environment envMap := map[string]kapi.EnvVar{} mergedEnv := []kapi.EnvVar{} for _, env := range baseContainer.Env { envMap[env.Name] = env } for _, env := range exec.Env { envMap[env.Name] = env } for k, v := range envMap { mergedEnv = append(mergedEnv, kapi.EnvVar{Name: k, Value: v.Value, ValueFrom: v.ValueFrom}) } mergedEnv = append(mergedEnv, kapi.EnvVar{Name: "OPENSHIFT_DEPLOYMENT_NAME", Value: deployment.Name}) mergedEnv = append(mergedEnv, kapi.EnvVar{Name: "OPENSHIFT_DEPLOYMENT_NAMESPACE", Value: deployment.Namespace}) // Inherit resources from the base container resources := kapi.ResourceRequirements{} if err := kapi.Scheme.Convert(&baseContainer.Resources, &resources, nil); err != nil { return nil, fmt.Errorf("couldn't clone ResourceRequirements: %v", err) } // Assigning to a variable since its address is required maxDeploymentDurationSeconds := deployapi.MaxDeploymentDurationSeconds - int64(time.Since(deployerPod.Status.StartTime.Time).Seconds()) // Let the kubelet manage retries if requested restartPolicy := kapi.RestartPolicyNever if hook.FailurePolicy == deployapi.LifecycleHookFailurePolicyRetry { restartPolicy = kapi.RestartPolicyOnFailure } // Transfer any requested volumes to the hook pod. volumes := []kapi.Volume{} volumeNames := sets.NewString() for _, volume := range deployment.Spec.Template.Spec.Volumes { for _, name := range exec.Volumes { if volume.Name == name { volumes = append(volumes, volume) volumeNames.Insert(volume.Name) } } } // Transfer any volume mounts associated with transferred volumes. volumeMounts := []kapi.VolumeMount{} for _, mount := range baseContainer.VolumeMounts { if volumeNames.Has(mount.Name) { volumeMounts = append(volumeMounts, kapi.VolumeMount{ Name: mount.Name, ReadOnly: mount.ReadOnly, MountPath: mount.MountPath, }) } } // Transfer image pull secrets from the pod spec. imagePullSecrets := []kapi.LocalObjectReference{} for _, pullSecret := range deployment.Spec.Template.Spec.ImagePullSecrets { imagePullSecrets = append(imagePullSecrets, kapi.LocalObjectReference{Name: pullSecret.Name}) } gracePeriod := int64(10) pod := &kapi.Pod{ ObjectMeta: kapi.ObjectMeta{ Name: namer.GetPodName(deployment.Name, suffix), Annotations: map[string]string{ deployapi.DeploymentAnnotation: deployment.Name, }, Labels: map[string]string{ deployapi.DeploymentPodTypeLabel: suffix, deployapi.DeployerPodForDeploymentLabel: deployment.Name, }, }, Spec: kapi.PodSpec{ Containers: []kapi.Container{ { Name: HookContainerName, Image: baseContainer.Image, Command: exec.Command, WorkingDir: baseContainer.WorkingDir, Env: mergedEnv, Resources: resources, VolumeMounts: volumeMounts, }, }, Volumes: volumes, ActiveDeadlineSeconds: &maxDeploymentDurationSeconds, // Setting the node selector on the hook pod so that it is created // on the same set of nodes as the deployment pods. NodeSelector: deployment.Spec.Template.Spec.NodeSelector, RestartPolicy: restartPolicy, ImagePullSecrets: imagePullSecrets, TerminationGracePeriodSeconds: &gracePeriod, }, } util.MergeInto(pod.Labels, strategy.Labels, 0) util.MergeInto(pod.Annotations, strategy.Annotations, 0) return pod, nil }
// DeploymentConfig creates a deploymentConfig resource from the deployment configuration reference // // TODO: take a pod template spec as argument func (r *DeploymentConfigRef) DeploymentConfig() (*deployapi.DeploymentConfig, error) { if len(r.Name) == 0 { suggestions := NameSuggestions{} for i := range r.Images { suggestions = append(suggestions, r.Images[i]) } name, ok := suggestions.SuggestName() if !ok { return nil, fmt.Errorf("unable to suggest a name for this DeploymentConfig") } r.Name = name } selector := map[string]string{ "deploymentconfig": r.Name, } if len(r.Labels) > 0 { if err := util.MergeInto(selector, r.Labels, 0); err != nil { return nil, err } } triggers := []deployapi.DeploymentTriggerPolicy{ // By default, always deploy on change { Type: deployapi.DeploymentTriggerOnConfigChange, }, } template := kapi.PodSpec{} for i := range r.Images { c, containerTriggers, err := r.Images[i].DeployableContainer() if err != nil { return nil, err } triggers = append(triggers, containerTriggers...) template.Containers = append(template.Containers, *c) } // Create EmptyDir volumes for all container volume mounts for _, c := range template.Containers { for _, v := range c.VolumeMounts { template.Volumes = append(template.Volumes, kapi.Volume{ Name: v.Name, VolumeSource: kapi.VolumeSource{ EmptyDir: &kapi.EmptyDirVolumeSource{Medium: kapi.StorageMediumDefault}, }, }) } } for i := range template.Containers { template.Containers[i].Env = append(template.Containers[i].Env, r.Env.List()...) } return &deployapi.DeploymentConfig{ ObjectMeta: kapi.ObjectMeta{ Name: r.Name, }, Template: deployapi.DeploymentTemplate{ ControllerTemplate: kapi.ReplicationControllerSpec{ Replicas: 1, Selector: selector, Template: &kapi.PodTemplateSpec{ ObjectMeta: kapi.ObjectMeta{ Labels: selector, }, Spec: template, }, }, }, Triggers: triggers, }, nil }
// DeploymentConfig creates a deploymentConfig resource from the deployment configuration reference // // TODO: take a pod template spec as argument func (r *DeploymentConfigRef) DeploymentConfig() (*deployapi.DeploymentConfig, error) { if len(r.Name) == 0 { suggestions := NameSuggestions{} for i := range r.Images { suggestions = append(suggestions, r.Images[i]) } name, ok := suggestions.SuggestName() if !ok { return nil, fmt.Errorf("unable to suggest a name for this DeploymentConfig") } r.Name = name } selector := map[string]string{ "deploymentconfig": r.Name, } if len(r.Labels) > 0 { if err := util.MergeInto(selector, r.Labels, 0); err != nil { return nil, err } } triggers := []deployapi.DeploymentTriggerPolicy{ // By default, always deploy on change { Type: deployapi.DeploymentTriggerOnConfigChange, }, } annotations := make(map[string]string) template := kapi.PodSpec{} for i := range r.Images { c, containerTriggers, err := r.Images[i].DeployableContainer() if err != nil { return nil, err } triggers = append(triggers, containerTriggers...) template.Containers = append(template.Containers, *c) if cmd, ok := r.Images[i].Command(); ok { imageapi.SetContainerImageEntrypointAnnotation(annotations, c.Name, cmd) } } // Create EmptyDir volumes for all container volume mounts for _, c := range template.Containers { for _, v := range c.VolumeMounts { template.Volumes = append(template.Volumes, kapi.Volume{ Name: v.Name, VolumeSource: kapi.VolumeSource{ EmptyDir: &kapi.EmptyDirVolumeSource{Medium: kapi.StorageMediumDefault}, }, }) } } for i := range template.Containers { template.Containers[i].Env = append(template.Containers[i].Env, r.Env.List()...) } dc := &deployapi.DeploymentConfig{ ObjectMeta: kapi.ObjectMeta{ Name: r.Name, }, Spec: deployapi.DeploymentConfigSpec{ Replicas: 1, Test: r.AsTest, Selector: selector, Template: &kapi.PodTemplateSpec{ ObjectMeta: kapi.ObjectMeta{ Labels: selector, Annotations: annotations, }, Spec: template, }, Triggers: triggers, }, } if r.PostHook != nil { //dc.Spec.Strategy.Type = "Rolling" if len(r.PostHook.Shell) > 0 { dc.Spec.Strategy.RecreateParams = &deployapi.RecreateDeploymentStrategyParams{ Post: &deployapi.LifecycleHook{ ExecNewPod: &deployapi.ExecNewPodHook{ Command: []string{"/bin/sh", "-c", r.PostHook.Shell}, }, }, } } } return dc, nil }