in buildlet/kube.go [71:236]
func StartPod(ctx context.Context, kubeClient *kubernetes.Client, podName, hostType string, opts PodOpts) (Client, error) {
conf, ok := dashboard.Hosts[hostType]
if !ok || conf.ContainerImage == "" {
return nil, fmt.Errorf("invalid builder type %q", hostType)
}
pod := &api.Pod{
TypeMeta: api.TypeMeta{
APIVersion: "v1",
Kind: "Pod",
},
ObjectMeta: api.ObjectMeta{
Name: podName,
Labels: map[string]string{
"name": podName,
"type": hostType,
"role": "buildlet",
},
Annotations: map[string]string{},
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyNever,
Containers: []api.Container{
{
Name: "buildlet",
Image: imageID(opts.ImageRegistry, conf.ContainerImage),
ImagePullPolicy: api.PullAlways,
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceCPU: BuildletCPU,
api.ResourceMemory: BuildletMemory,
},
Limits: api.ResourceList{
api.ResourceCPU: BuildletCPULimit,
api.ResourceMemory: BuildletMemory,
},
},
Command: []string{"/usr/local/bin/stage0"},
Ports: []api.ContainerPort{
{
ContainerPort: 80,
},
},
Env: []api.EnvVar{},
},
},
},
}
addEnv := func(name, value string) {
for i, _ := range pod.Spec.Containers {
pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, api.EnvVar{
Name: name,
Value: value,
})
}
}
// The buildlet-binary-url is the URL of the buildlet binary
// which the pods are configured to download at boot and run.
// This lets us/ update the buildlet more easily than
// rebuilding the whole pod image.
addEnv("META_BUILDLET_BINARY_URL", conf.BuildletBinaryURL(buildenv.ByProjectID(opts.ProjectID)))
addEnv("META_BUILDLET_HOST_TYPE", hostType)
if !opts.TLS.IsZero() {
addEnv("META_TLS_CERT", opts.TLS.CertPEM)
addEnv("META_TLS_KEY", opts.TLS.KeyPEM)
addEnv("META_PASSWORD", opts.TLS.Password())
}
if opts.DeleteIn != 0 {
// In case the pod gets away from us (generally: if the
// coordinator dies while a build is running), then we
// set this annotation of when it should be killed so
// we can kill it later when the coordinator is
// restarted. The cleanUpOldPods goroutine loop handles
// that killing.
pod.ObjectMeta.Annotations["delete-at"] = fmt.Sprint(time.Now().Add(opts.DeleteIn).Unix())
}
condRun(opts.OnPodCreating)
podStatus, err := kubeClient.RunLongLivedPod(ctx, pod)
if err != nil {
return nil, err
}
// The new pod must be in Running phase. Possible phases are described at
// http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-phase
if podStatus.Phase != api.PodRunning {
return nil, fmt.Errorf("pod is in invalid state %q: %v", podStatus.Phase, podStatus.Message)
}
condRun(opts.OnPodCreated)
// Wait for the pod to boot and its buildlet to come up.
var buildletURL string
var ipPort string
if !opts.TLS.IsZero() {
buildletURL = "https://" + podStatus.PodIP
ipPort = podStatus.PodIP + ":443"
} else {
buildletURL = "http://" + podStatus.PodIP
ipPort = podStatus.PodIP + ":80"
}
condRun(opts.OnGotPodInfo)
impatientClient := &http.Client{
Timeout: 5 * time.Second,
Transport: &http.Transport{
Dial: defaultDialer(),
DisableKeepAlives: true,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
ctx, cancel := context.WithTimeout(ctx, 3*time.Minute)
defer cancel()
c := make(chan error, 1)
go func() {
defer close(c)
try := 0
for {
try++
// Make sure pod is still running
podStatus, err := kubeClient.PodStatus(ctx, pod.Name)
if err != nil {
c <- fmt.Errorf("polling the buildlet pod for its status failed: %v", err)
return
}
if podStatus.Phase != api.PodRunning {
podLog, err := kubeClient.PodLog(ctx, pod.Name)
if err != nil {
log.Printf("failed to retrieve log for pod %q: %v", pod.Name, err)
c <- fmt.Errorf("buildlet pod left the Running phase and entered phase %q", podStatus.Phase)
return
}
log.Printf("log from pod %q: %v", pod.Name, podLog)
c <- fmt.Errorf("buildlet pod left the Running phase and entered phase %q", podStatus.Phase)
return
}
res, err := ctxhttp.Get(ctx, impatientClient, buildletURL)
if err != nil {
time.Sleep(1 * time.Second)
continue
}
res.Body.Close()
if res.StatusCode != 200 {
c <- fmt.Errorf("buildlet returned HTTP status code %d on try number %d", res.StatusCode, try)
}
return
}
}()
// Wait for the buildlet to respond to an HTTP request. If the timeout happens first, or
// if the buildlet pod leaves the running state, return an error.
for {
select {
case <-ctx.Done():
return nil, ctx.Err()
case err = <-c:
if err != nil {
return nil, err
}
return NewClient(ipPort, opts.TLS), nil
}
}
}