2017-11-20 09:30:52 -05:00
|
|
|
package kubernetes
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/docker/cli/cli/command/formatter"
|
2017-12-04 03:44:06 -05:00
|
|
|
"github.com/docker/cli/kubernetes/labels"
|
2017-11-20 09:30:52 -05:00
|
|
|
"github.com/docker/docker/api/types/swarm"
|
|
|
|
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
|
|
|
apiv1 "k8s.io/api/core/v1"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Pod conversion
|
|
|
|
func podToTask(pod apiv1.Pod) swarm.Task {
|
|
|
|
var startTime time.Time
|
|
|
|
if pod.Status.StartTime != nil {
|
|
|
|
startTime = (*pod.Status.StartTime).Time
|
|
|
|
}
|
|
|
|
task := swarm.Task{
|
|
|
|
ID: string(pod.UID),
|
|
|
|
NodeID: pod.Spec.NodeName,
|
|
|
|
Spec: swarm.TaskSpec{
|
|
|
|
ContainerSpec: &swarm.ContainerSpec{
|
|
|
|
Image: getContainerImage(pod.Spec.Containers),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
DesiredState: podPhaseToState(pod.Status.Phase),
|
|
|
|
Status: swarm.TaskStatus{
|
|
|
|
State: podPhaseToState(pod.Status.Phase),
|
|
|
|
Timestamp: startTime,
|
|
|
|
PortStatus: swarm.PortStatus{
|
|
|
|
Ports: getPorts(pod.Spec.Containers),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
return task
|
|
|
|
}
|
|
|
|
|
|
|
|
func podPhaseToState(phase apiv1.PodPhase) swarm.TaskState {
|
|
|
|
switch phase {
|
|
|
|
case apiv1.PodPending:
|
|
|
|
return swarm.TaskStatePending
|
|
|
|
case apiv1.PodRunning:
|
|
|
|
return swarm.TaskStateRunning
|
|
|
|
case apiv1.PodSucceeded:
|
|
|
|
return swarm.TaskStateComplete
|
|
|
|
case apiv1.PodFailed:
|
|
|
|
return swarm.TaskStateFailed
|
|
|
|
default:
|
|
|
|
return swarm.TaskState("unknown")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func toSwarmProtocol(protocol apiv1.Protocol) swarm.PortConfigProtocol {
|
|
|
|
switch protocol {
|
|
|
|
case apiv1.ProtocolTCP:
|
|
|
|
return swarm.PortConfigProtocolTCP
|
|
|
|
case apiv1.ProtocolUDP:
|
|
|
|
return swarm.PortConfigProtocolUDP
|
|
|
|
}
|
|
|
|
return swarm.PortConfigProtocol("unknown")
|
|
|
|
}
|
|
|
|
|
|
|
|
func fetchPods(namespace string, pods corev1.PodInterface) ([]apiv1.Pod, error) {
|
|
|
|
labelSelector := labels.SelectorForStack(namespace)
|
|
|
|
podsList, err := pods.List(metav1.ListOptions{LabelSelector: labelSelector})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return podsList.Items, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getContainerImage(containers []apiv1.Container) string {
|
|
|
|
if len(containers) == 0 {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return containers[0].Image
|
|
|
|
}
|
|
|
|
|
|
|
|
func getPorts(containers []apiv1.Container) []swarm.PortConfig {
|
|
|
|
if len(containers) == 0 || len(containers[0].Ports) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
ports := make([]swarm.PortConfig, len(containers[0].Ports))
|
|
|
|
for i, port := range containers[0].Ports {
|
|
|
|
ports[i] = swarm.PortConfig{
|
|
|
|
PublishedPort: uint32(port.HostPort),
|
|
|
|
TargetPort: uint32(port.ContainerPort),
|
|
|
|
Protocol: toSwarmProtocol(port.Protocol),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ports
|
|
|
|
}
|
|
|
|
|
|
|
|
type tasksBySlot []swarm.Task
|
|
|
|
|
|
|
|
func (t tasksBySlot) Len() int {
|
|
|
|
return len(t)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t tasksBySlot) Swap(i, j int) {
|
|
|
|
t[i], t[j] = t[j], t[i]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t tasksBySlot) Less(i, j int) bool {
|
|
|
|
// Sort by slot.
|
|
|
|
if t[i].Slot != t[j].Slot {
|
|
|
|
return t[i].Slot < t[j].Slot
|
|
|
|
}
|
|
|
|
|
|
|
|
// If same slot, sort by most recent.
|
|
|
|
return t[j].Meta.CreatedAt.Before(t[i].CreatedAt)
|
|
|
|
}
|
|
|
|
|
2018-03-13 13:31:44 -04:00
|
|
|
const (
|
|
|
|
publishedServiceSuffix = "-published"
|
|
|
|
publishedOnRandomPortSuffix = "-random-ports"
|
|
|
|
)
|
|
|
|
|
2017-11-20 09:30:52 -05:00
|
|
|
// Replicas conversion
|
|
|
|
func replicasToServices(replicas *appsv1beta2.ReplicaSetList, services *apiv1.ServiceList) ([]swarm.Service, map[string]formatter.ServiceListInfo, error) {
|
|
|
|
result := make([]swarm.Service, len(replicas.Items))
|
|
|
|
infos := make(map[string]formatter.ServiceListInfo, len(replicas.Items))
|
|
|
|
for i, r := range replicas.Items {
|
2018-03-13 13:31:44 -04:00
|
|
|
serviceName := r.Labels[labels.ForServiceName]
|
|
|
|
serviceHeadless, ok := findService(services, serviceName)
|
2017-11-20 09:30:52 -05:00
|
|
|
if !ok {
|
2018-03-13 13:31:44 -04:00
|
|
|
return nil, nil, fmt.Errorf("could not find service '%s'", serviceName)
|
2017-11-20 09:30:52 -05:00
|
|
|
}
|
2018-03-13 13:31:44 -04:00
|
|
|
stack, ok := serviceHeadless.Labels[labels.ForStackName]
|
2018-01-02 17:56:07 -05:00
|
|
|
if ok {
|
|
|
|
stack += "_"
|
|
|
|
}
|
2018-03-13 13:31:44 -04:00
|
|
|
uid := string(serviceHeadless.UID)
|
2017-11-20 09:30:52 -05:00
|
|
|
s := swarm.Service{
|
|
|
|
ID: uid,
|
|
|
|
Spec: swarm.ServiceSpec{
|
|
|
|
Annotations: swarm.Annotations{
|
2018-03-13 13:31:44 -04:00
|
|
|
Name: stack + serviceHeadless.Name,
|
2017-11-20 09:30:52 -05:00
|
|
|
},
|
|
|
|
TaskTemplate: swarm.TaskSpec{
|
|
|
|
ContainerSpec: &swarm.ContainerSpec{
|
|
|
|
Image: getContainerImage(r.Spec.Template.Spec.Containers),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2018-03-13 13:31:44 -04:00
|
|
|
if serviceNodePort, ok := findService(services, serviceName+publishedOnRandomPortSuffix); ok && serviceNodePort.Spec.Type == apiv1.ServiceTypeNodePort {
|
|
|
|
s.Endpoint = serviceEndpoint(serviceNodePort, swarm.PortConfigPublishModeHost)
|
|
|
|
}
|
|
|
|
if serviceLoadBalancer, ok := findService(services, serviceName+publishedServiceSuffix); ok && serviceLoadBalancer.Spec.Type == apiv1.ServiceTypeLoadBalancer {
|
|
|
|
s.Endpoint = serviceEndpoint(serviceLoadBalancer, swarm.PortConfigPublishModeIngress)
|
2017-11-20 09:30:52 -05:00
|
|
|
}
|
|
|
|
result[i] = s
|
|
|
|
infos[uid] = formatter.ServiceListInfo{
|
|
|
|
Mode: "replicated",
|
|
|
|
Replicas: fmt.Sprintf("%d/%d", r.Status.AvailableReplicas, r.Status.Replicas),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result, infos, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func findService(services *apiv1.ServiceList, name string) (apiv1.Service, bool) {
|
|
|
|
for _, s := range services.Items {
|
|
|
|
if s.Name == name {
|
|
|
|
return s, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return apiv1.Service{}, false
|
|
|
|
}
|
2018-03-13 13:31:44 -04:00
|
|
|
|
|
|
|
func serviceEndpoint(service apiv1.Service, publishMode swarm.PortConfigPublishMode) swarm.Endpoint {
|
|
|
|
configs := make([]swarm.PortConfig, len(service.Spec.Ports))
|
|
|
|
for i, p := range service.Spec.Ports {
|
|
|
|
configs[i] = swarm.PortConfig{
|
|
|
|
PublishMode: publishMode,
|
|
|
|
PublishedPort: uint32(p.Port),
|
|
|
|
TargetPort: uint32(p.TargetPort.IntValue()),
|
|
|
|
Protocol: toSwarmProtocol(p.Protocol),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return swarm.Endpoint{Ports: configs}
|
|
|
|
}
|