DockerCLI/cli/command/stack/kubernetes/watcher.go

118 lines
2.4 KiB
Go

package kubernetes
import (
"fmt"
"time"
apiv1beta1 "github.com/docker/cli/kubernetes/compose/v1beta1"
"github.com/docker/cli/kubernetes/labels"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
)
// DeployWatcher watches a stack deployement
type DeployWatcher struct {
Pods corev1.PodInterface
}
// Watch watches a stuck deployement and return a chan that will holds the state of the stack
func (w DeployWatcher) Watch(stack *apiv1beta1.Stack, serviceNames []string) chan bool {
stop := make(chan bool)
go w.waitForPods(stack.Name, serviceNames, stop)
return stop
}
func (w DeployWatcher) waitForPods(stackName string, serviceNames []string, stop chan bool) {
starts := map[string]int32{}
for {
time.Sleep(1 * time.Second)
list, err := w.Pods.List(metav1.ListOptions{
LabelSelector: labels.SelectorForStack(stackName),
IncludeUninitialized: true,
})
if err != nil {
stop <- true
return
}
for i := range list.Items {
pod := list.Items[i]
if pod.Status.Phase != apiv1.PodRunning {
continue
}
startCount := startCount(pod)
serviceName := pod.Labels[labels.ForServiceName]
if startCount != starts[serviceName] {
if startCount == 1 {
fmt.Printf(" - Service %s has one container running\n", serviceName)
} else {
fmt.Printf(" - Service %s was restarted %d %s\n", serviceName, startCount-1, timeTimes(startCount-1))
}
starts[serviceName] = startCount
}
}
if allReady(list.Items, serviceNames) {
stop <- true
return
}
}
}
func startCount(pod apiv1.Pod) int32 {
restart := int32(0)
for _, status := range pod.Status.ContainerStatuses {
restart += status.RestartCount
}
return 1 + restart
}
func allReady(pods []apiv1.Pod, serviceNames []string) bool {
serviceUp := map[string]bool{}
for _, pod := range pods {
if time.Since(pod.GetCreationTimestamp().Time) < 10*time.Second {
return false
}
ready := false
for _, cond := range pod.Status.Conditions {
if cond.Type == apiv1.PodReady && cond.Status == apiv1.ConditionTrue {
ready = true
}
}
if !ready {
return false
}
serviceName := pod.Labels[labels.ForServiceName]
serviceUp[serviceName] = true
}
for _, serviceName := range serviceNames {
if !serviceUp[serviceName] {
return false
}
}
return true
}
func timeTimes(n int32) string {
if n == 1 {
return "time"
}
return "times"
}