// 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}) } // 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_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", } 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) } }
// DeployerPodNameForDeployment returns the name of a pod for a given deployment func DeployerPodNameForDeployment(deployment string) string { return namer.GetPodName(deployment, DeployerPodSuffix) }