// GetPodForContainer creates a new Pod that runs specified container func GetPodForContainer(container kapi.Container) *kapi.Pod { name := namer.GetPodName("test-pod", string(uuid.NewUUID())) return &kapi.Pod{ TypeMeta: unversioned.TypeMeta{ Kind: "Pod", APIVersion: "v1", }, ObjectMeta: kapi.ObjectMeta{ Name: name, Labels: map[string]string{"name": name}, }, Spec: kapi.PodSpec{ Containers: []kapi.Container{container}, RestartPolicy: kapi.RestartPolicyNever, }, } }
// CreatePodForImage creates a Pod for the given image name. The dockerImageReference // must be full docker pull spec. func CreatePodForImage(dockerImageReference string) *kapi.Pod { podName := namer.GetPodName("test-pod", string(kutil.NewUUID())) return &kapi.Pod{ TypeMeta: kapi.TypeMeta{ Kind: "Pod", APIVersion: "v1", }, ObjectMeta: kapi.ObjectMeta{ Name: podName, Labels: map[string]string{"name": podName}, }, Spec: kapi.PodSpec{ Containers: []kapi.Container{ { Name: podName, Image: dockerImageReference, }, }, RestartPolicy: kapi.RestartPolicyNever, }, } }
// GetBuildPodName returns name of the build pod. func GetBuildPodName(build *buildapi.Build) string { return namer.GetPodName(build.Name, BuildPodSuffix) }
// 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 }
// DeployerPodNameForDeployment returns the name of a pod for a given deployment func DeployerPodNameForDeployment(deployment string) string { return namer.GetPodName(deployment, DeployerPodSuffix) }
// makeHookPod makes a pod spec from a hook and deployment. func makeHookPod(hook *deployapi.LifecycleHook, deployment *kapi.ReplicationController, label 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]string{} mergedEnv := []kapi.EnvVar{} for _, env := range baseContainer.Env { envMap[env.Name] = env.Value } for _, env := range exec.Env { envMap[env.Name] = env.Value } for k, v := range envMap { mergedEnv = append(mergedEnv, kapi.EnvVar{Name: k, Value: v}) } 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); err != nil { return nil, fmt.Errorf("couldn't clone ResourceRequirements: %v", err) } // Assigning to a variable since its address is required maxDeploymentDurationSeconds := deployapi.MaxDeploymentDurationSeconds // Let the kubelet manage retries if requested restartPolicy := kapi.RestartPolicyNever if hook.FailurePolicy == deployapi.LifecycleHookFailurePolicyRetry { restartPolicy = kapi.RestartPolicyOnFailure } pod := &kapi.Pod{ ObjectMeta: kapi.ObjectMeta{ Name: namer.GetPodName(deployment.Name, label), Annotations: map[string]string{ deployapi.DeploymentAnnotation: deployment.Name, }, Labels: map[string]string{ deployapi.DeployerPodForDeploymentLabel: deployment.Name, }, }, Spec: kapi.PodSpec{ Containers: []kapi.Container{ { Name: "lifecycle", Image: baseContainer.Image, Command: exec.Command, WorkingDir: baseContainer.WorkingDir, Env: mergedEnv, Resources: resources, }, }, 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, }, } return pod, nil }
func TestHookExecutor_makeHookPod(t *testing.T) { deploymentName := "deployment-1" deploymentNamespace := "test" maxDeploymentDurationSeconds := deployapi.MaxDeploymentDurationSeconds tests := []struct { name string hook *deployapi.LifecycleHook expected *kapi.Pod strategyLabels map[string]string strategyAnnotations map[string]string }{ { name: "overrides", hook: &deployapi.LifecycleHook{ FailurePolicy: deployapi.LifecycleHookFailurePolicyAbort, ExecNewPod: &deployapi.ExecNewPodHook{ ContainerName: "container1", Command: []string{"overridden"}, Env: []kapi.EnvVar{ { Name: "name", Value: "value", }, { Name: "ENV1", Value: "overridden", }, }, Volumes: []string{"volume-2"}, }, }, expected: &kapi.Pod{ ObjectMeta: kapi.ObjectMeta{ Name: namer.GetPodName(deploymentName, "hook"), Labels: map[string]string{ deployapi.DeploymentPodTypeLabel: "hook", deployapi.DeployerPodForDeploymentLabel: deploymentName, }, Annotations: map[string]string{ deployapi.DeploymentAnnotation: deploymentName, }, }, Spec: kapi.PodSpec{ RestartPolicy: kapi.RestartPolicyNever, Volumes: []kapi.Volume{ { Name: "volume-2", }, }, ActiveDeadlineSeconds: &maxDeploymentDurationSeconds, Containers: []kapi.Container{ { Name: "lifecycle", Image: "registry:8080/repo1:ref1", Command: []string{"overridden"}, Env: []kapi.EnvVar{ { Name: "name", Value: "value", }, { Name: "ENV1", Value: "overridden", }, { Name: "OPENSHIFT_DEPLOYMENT_NAME", Value: deploymentName, }, { Name: "OPENSHIFT_DEPLOYMENT_NAMESPACE", Value: deploymentNamespace, }, }, Resources: kapi.ResourceRequirements{ Limits: kapi.ResourceList{ kapi.ResourceCPU: resource.MustParse("10"), kapi.ResourceMemory: resource.MustParse("10M"), }, }, VolumeMounts: []kapi.VolumeMount{ { Name: "volume-2", ReadOnly: true, MountPath: "/mnt/volume-2", }, }, }, }, ImagePullSecrets: []kapi.LocalObjectReference{ { Name: "secret-1", }, }, }, }, }, { name: "no overrides", hook: &deployapi.LifecycleHook{ FailurePolicy: deployapi.LifecycleHookFailurePolicyAbort, ExecNewPod: &deployapi.ExecNewPodHook{ ContainerName: "container1", }, }, expected: &kapi.Pod{ ObjectMeta: kapi.ObjectMeta{ Name: namer.GetPodName(deploymentName, "hook"), Labels: map[string]string{ deployapi.DeploymentPodTypeLabel: "hook", deployapi.DeployerPodForDeploymentLabel: deploymentName, }, Annotations: map[string]string{ deployapi.DeploymentAnnotation: deploymentName, }, }, Spec: kapi.PodSpec{ RestartPolicy: kapi.RestartPolicyNever, ActiveDeadlineSeconds: &maxDeploymentDurationSeconds, Containers: []kapi.Container{ { Name: "lifecycle", Image: "registry:8080/repo1:ref1", Env: []kapi.EnvVar{ { Name: "ENV1", Value: "VAL1", }, { Name: "OPENSHIFT_DEPLOYMENT_NAME", Value: deploymentName, }, { Name: "OPENSHIFT_DEPLOYMENT_NAMESPACE", Value: deploymentNamespace, }, }, Resources: kapi.ResourceRequirements{ Limits: kapi.ResourceList{ kapi.ResourceCPU: resource.MustParse("10"), kapi.ResourceMemory: resource.MustParse("10M"), }, }, }, }, ImagePullSecrets: []kapi.LocalObjectReference{ { Name: "secret-1", }, }, }, }, }, { name: "labels and annotations", hook: &deployapi.LifecycleHook{ FailurePolicy: deployapi.LifecycleHookFailurePolicyAbort, ExecNewPod: &deployapi.ExecNewPodHook{ ContainerName: "container1", }, }, expected: &kapi.Pod{ ObjectMeta: kapi.ObjectMeta{ Name: namer.GetPodName(deploymentName, "hook"), Labels: map[string]string{ deployapi.DeploymentPodTypeLabel: "hook", deployapi.DeployerPodForDeploymentLabel: deploymentName, "label1": "value1", }, Annotations: map[string]string{ deployapi.DeploymentAnnotation: deploymentName, "annotation2": "value2", }, }, Spec: kapi.PodSpec{ RestartPolicy: kapi.RestartPolicyNever, ActiveDeadlineSeconds: &maxDeploymentDurationSeconds, Containers: []kapi.Container{ { Name: "lifecycle", Image: "registry:8080/repo1:ref1", Env: []kapi.EnvVar{ { Name: "ENV1", Value: "VAL1", }, { Name: "OPENSHIFT_DEPLOYMENT_NAME", Value: deploymentName, }, { Name: "OPENSHIFT_DEPLOYMENT_NAMESPACE", Value: deploymentNamespace, }, }, Resources: kapi.ResourceRequirements{ Limits: kapi.ResourceList{ kapi.ResourceCPU: resource.MustParse("10"), kapi.ResourceMemory: resource.MustParse("10M"), }, }, }, }, ImagePullSecrets: []kapi.LocalObjectReference{ { Name: "secret-1", }, }, }, }, strategyLabels: map[string]string{ deployapi.DeployerPodForDeploymentLabel: "ignoredValue", "label1": "value1", }, strategyAnnotations: map[string]string{"annotation2": "value2"}, }, } for _, test := range tests { t.Logf("evaluating test: %s", test.name) config, deployment := deployment("deployment", "test", test.strategyLabels, test.strategyAnnotations) pod, err := makeHookPod(test.hook, deployment, &config.Spec.Strategy, "hook") if err != nil { t.Fatalf("unexpected error: %s", err) } for _, c := range pod.Spec.Containers { sort.Sort(envByNameAsc(c.Env)) } for _, c := range test.expected.Spec.Containers { sort.Sort(envByNameAsc(c.Env)) } if !kapi.Semantic.DeepEqual(pod, test.expected) { t.Errorf("unexpected pod diff: %v", diff.ObjectDiff(pod, test.expected)) } } }
func TestHookExecutor_makeHookPodOk(t *testing.T) { hook := &deployapi.LifecycleHook{ FailurePolicy: deployapi.LifecycleHookFailurePolicyAbort, ExecNewPod: &deployapi.ExecNewPodHook{ ContainerName: "container1", Command: []string{"overridden"}, Env: []kapi.EnvVar{ { Name: "name", Value: "value", }, { Name: "ENV1", Value: "overridden", }, }, }, } config := deploytest.OkDeploymentConfig(1) cpuLimit := resource.MustParse("10") memoryLimit := resource.MustParse("10M") config.Template.ControllerTemplate.Template.Spec.Containers[0].Resources = kapi.ResourceRequirements{ Limits: kapi.ResourceList{ kapi.ResourceCPU: cpuLimit, kapi.ResourceMemory: memoryLimit, }, } deployment, _ := deployutil.MakeDeployment(config, kapi.Codec) pod, err := makeHookPod(hook, deployment, "hook") if err != nil { t.Fatalf("unexpected error: %s", err) } if e, a := namer.GetPodName(deployment.Name, "hook"), pod.Name; e != a { t.Errorf("expected pod name %s, got %s", e, a) } if e, a := kapi.RestartPolicyNever, pod.Spec.RestartPolicy; e != a { t.Errorf("expected pod restart policy %s, got %s", e, a) } gotContainer := pod.Spec.Containers[0] // Verify the correct image was selected if e, a := deployment.Spec.Template.Spec.Containers[0].Image, gotContainer.Image; e != a { t.Fatalf("expected container image %s, got %s", e, a) } // Verify command overriding if e, a := "overridden", gotContainer.Command[0]; e != a { t.Fatalf("expected container command %s, got %s", e, a) } // Verify environment merging expectedEnv := map[string]string{ "name": "value", "ENV1": "overridden", "OPENSHIFT_DEPLOYMENT_NAME": deployment.Name, "OPENSHIFT_DEPLOYMENT_NAMESPACE": deployment.Namespace, } for k, v := range expectedEnv { found := false for _, env := range gotContainer.Env { if env.Name == k && env.Value == v { found = true break } } if !found { t.Errorf("expected to find %s=%s in pod environment", k, v) } } for _, env := range gotContainer.Env { val, found := expectedEnv[env.Name] if !found || val != env.Value { t.Errorf("container has unexpected environment entry %s=%s", env.Name, env.Value) } } // Verify resource limit inheritance if cpu := gotContainer.Resources.Limits.Cpu(); cpu.Value() != cpuLimit.Value() { t.Errorf("expected cpu %v, got: %v", cpuLimit, cpu) } if memory := gotContainer.Resources.Limits.Memory(); memory.Value() != memoryLimit.Value() { t.Errorf("expected memory %v, got: %v", memoryLimit, memory) } // Verify restart policy if e, a := kapi.RestartPolicyNever, pod.Spec.RestartPolicy; e != a { t.Fatalf("expected restart policy %s, got %s", e, a) } // Verify correlation stuff if l, e, a := deployapi.DeployerPodForDeploymentLabel, deployment.Name, pod.Labels[deployapi.DeployerPodForDeploymentLabel]; e != a { t.Errorf("expected label %s=%s, got %s", l, e, a) } if l, e, a := deployapi.DeploymentAnnotation, deployment.Name, pod.Annotations[deployapi.DeploymentAnnotation]; e != a { t.Errorf("expected annotation %s=%s, got %s", l, e, a) } }