forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
lifecycle.go
135 lines (119 loc) · 4.23 KB
/
lifecycle.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package support
import (
"fmt"
"reflect"
"github.com/golang/glog"
kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
deployapi "github.com/openshift/origin/pkg/deploy/api"
)
// HookExecutor executes a deployment lifecycle hook.
type HookExecutor struct {
// PodClient provides access to pods.
PodClient HookExecutorPodClient
}
// Execute executes hook in the context of deployment.
func (e *HookExecutor) Execute(hook *deployapi.LifecycleHook, deployment *kapi.ReplicationController) error {
if hook.ExecNewPod != nil {
return e.executeExecNewPod(hook.ExecNewPod, deployment)
}
return nil
}
// executeExecNewPod executes a ExecNewPod hook by creating a new pod based on
// the hook parameters and deployment. The pod is then synchronously watched
// until the pod completes, and if the pod failed, an error is returned.
func (e *HookExecutor) executeExecNewPod(hook *deployapi.ExecNewPodHook, deployment *kapi.ReplicationController) error {
// Build a pod spec from the hook config and deployment
var image string
for _, container := range deployment.Spec.Template.Spec.Containers {
if container.Name != hook.ContainerName {
continue
}
image = container.Image
}
if len(image) == 0 {
return fmt.Errorf("no container named '%s' found in deployment template", hook.ContainerName)
}
podName := kapi.SimpleNameGenerator.GenerateName(fmt.Sprintf("deployment-%s-hook-", deployment.Name))
podSpec := &kapi.Pod{
ObjectMeta: kapi.ObjectMeta{
Name: podName,
Annotations: map[string]string{
deployapi.DeploymentAnnotation: deployment.Name,
},
},
Spec: kapi.PodSpec{
Containers: []kapi.Container{
{
Name: "lifecycle",
Image: image,
Command: hook.Command,
Env: hook.Env,
},
},
RestartPolicy: kapi.RestartPolicyNever,
},
}
// Set up a watch for the pod
podWatch, err := e.PodClient.WatchPod(deployment.Namespace, podName)
if err != nil {
return fmt.Errorf("couldn't create watch for pod %s/%s: %s", deployment.Namespace, podName, err)
}
defer podWatch.Stop()
// Try to create the pod
pod, err := e.PodClient.CreatePod(deployment.Namespace, podSpec)
if err != nil {
if !kerrors.IsAlreadyExists(err) {
return fmt.Errorf("couldn't create lifecycle pod for %s: %v", labelForDeployment(deployment), err)
}
} else {
glog.V(0).Infof("Created lifecycle pod %s for deployment %s", pod.Name, labelForDeployment(deployment))
}
// Wait for the pod to finish.
// TODO: Delete pod before returning?
glog.V(0).Infof("Waiting for hook pod %s/%s to complete", pod.Namespace, pod.Name)
for {
select {
case event, ok := <-podWatch.ResultChan():
if !ok {
return fmt.Errorf("couldn't watch pod %s/%s", pod.Namespace, pod.Name)
}
if event.Type == watch.Error {
return kerrors.FromObject(event.Object)
}
pod, podOk := event.Object.(*kapi.Pod)
if !podOk {
return fmt.Errorf("expected a pod event, got a %s", reflect.TypeOf(event.Object))
}
glog.V(0).Infof("Lifecycle pod %s/%s in phase %s", pod.Namespace, pod.Name, pod.Status.Phase)
switch pod.Status.Phase {
case kapi.PodSucceeded:
return nil
case kapi.PodFailed:
// TODO: Add context
return fmt.Errorf("pod failed")
}
}
}
}
// labelForDeployment builds a string identifier for a deployment.
func labelForDeployment(deployment *kapi.ReplicationController) string {
return fmt.Sprintf("%s/%s", deployment.Namespace, deployment.Name)
}
// HookExecutorPodClient abstracts access to pods.
type HookExecutorPodClient interface {
CreatePod(namespace string, pod *kapi.Pod) (*kapi.Pod, error)
WatchPod(namespace, name string) (watch.Interface, error)
}
// HookExecutorPodClientImpl is a pluggable HookExecutorPodClient.
type HookExecutorPodClientImpl struct {
CreatePodFunc func(namespace string, pod *kapi.Pod) (*kapi.Pod, error)
WatchPodFunc func(namespace, name string) (watch.Interface, error)
}
func (i *HookExecutorPodClientImpl) CreatePod(namespace string, pod *kapi.Pod) (*kapi.Pod, error) {
return i.CreatePodFunc(namespace, pod)
}
func (i *HookExecutorPodClientImpl) WatchPod(namespace, name string) (watch.Interface, error) {
return i.WatchPodFunc(namespace, name)
}