github.com/AliyunContainerService/cli@v0.0.0-20181009023821-814ced4b30d0/cli/command/stack/kubernetes/conversion.go (about)

     1  package kubernetes
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/docker/cli/cli/command/formatter"
    10  	"github.com/docker/cli/kubernetes/labels"
    11  	"github.com/docker/docker/api/types/filters"
    12  	"github.com/docker/docker/api/types/swarm"
    13  	appsv1beta2 "k8s.io/api/apps/v1beta2"
    14  	apiv1 "k8s.io/api/core/v1"
    15  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    16  	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
    17  )
    18  
    19  // Pod conversion
    20  func podToTask(pod apiv1.Pod) swarm.Task {
    21  	var startTime time.Time
    22  	if pod.Status.StartTime != nil {
    23  		startTime = (*pod.Status.StartTime).Time
    24  	}
    25  	task := swarm.Task{
    26  		ID:     string(pod.UID),
    27  		NodeID: pod.Spec.NodeName,
    28  		Spec: swarm.TaskSpec{
    29  			ContainerSpec: &swarm.ContainerSpec{
    30  				Image: getContainerImage(pod.Spec.Containers),
    31  			},
    32  		},
    33  		DesiredState: podPhaseToState(pod.Status.Phase),
    34  		Status: swarm.TaskStatus{
    35  			State:     podPhaseToState(pod.Status.Phase),
    36  			Timestamp: startTime,
    37  			PortStatus: swarm.PortStatus{
    38  				Ports: getPorts(pod.Spec.Containers),
    39  			},
    40  		},
    41  	}
    42  
    43  	return task
    44  }
    45  
    46  func podPhaseToState(phase apiv1.PodPhase) swarm.TaskState {
    47  	switch phase {
    48  	case apiv1.PodPending:
    49  		return swarm.TaskStatePending
    50  	case apiv1.PodRunning:
    51  		return swarm.TaskStateRunning
    52  	case apiv1.PodSucceeded:
    53  		return swarm.TaskStateComplete
    54  	case apiv1.PodFailed:
    55  		return swarm.TaskStateFailed
    56  	default:
    57  		return swarm.TaskState("unknown")
    58  	}
    59  }
    60  
    61  func toSwarmProtocol(protocol apiv1.Protocol) swarm.PortConfigProtocol {
    62  	switch protocol {
    63  	case apiv1.ProtocolTCP:
    64  		return swarm.PortConfigProtocolTCP
    65  	case apiv1.ProtocolUDP:
    66  		return swarm.PortConfigProtocolUDP
    67  	}
    68  	return swarm.PortConfigProtocol("unknown")
    69  }
    70  
    71  func fetchPods(stackName string, pods corev1.PodInterface, f filters.Args) ([]apiv1.Pod, error) {
    72  	services := f.Get("service")
    73  	// for existing script compatibility, support either <servicename> or <stackname>_<servicename> format
    74  	stackNamePrefix := stackName + "_"
    75  	for _, s := range services {
    76  		if strings.HasPrefix(s, stackNamePrefix) {
    77  			services = append(services, strings.TrimPrefix(s, stackNamePrefix))
    78  		}
    79  	}
    80  	listOpts := metav1.ListOptions{LabelSelector: labels.SelectorForStack(stackName, services...)}
    81  	var result []apiv1.Pod
    82  	podsList, err := pods.List(listOpts)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	nodes := f.Get("node")
    87  	for _, pod := range podsList.Items {
    88  		if filterPod(pod, nodes) &&
    89  			// name filter is done client side for matching partials
    90  			f.FuzzyMatch("name", stackNamePrefix+pod.Name) {
    91  
    92  			result = append(result, pod)
    93  		}
    94  	}
    95  	return result, nil
    96  }
    97  
    98  func filterPod(pod apiv1.Pod, nodes []string) bool {
    99  	if len(nodes) == 0 {
   100  		return true
   101  	}
   102  	for _, name := range nodes {
   103  		if pod.Spec.NodeName == name {
   104  			return true
   105  		}
   106  	}
   107  	return false
   108  }
   109  
   110  func getContainerImage(containers []apiv1.Container) string {
   111  	if len(containers) == 0 {
   112  		return ""
   113  	}
   114  	return containers[0].Image
   115  }
   116  
   117  func getPorts(containers []apiv1.Container) []swarm.PortConfig {
   118  	if len(containers) == 0 || len(containers[0].Ports) == 0 {
   119  		return nil
   120  	}
   121  	ports := make([]swarm.PortConfig, len(containers[0].Ports))
   122  	for i, port := range containers[0].Ports {
   123  		ports[i] = swarm.PortConfig{
   124  			PublishedPort: uint32(port.HostPort),
   125  			TargetPort:    uint32(port.ContainerPort),
   126  			Protocol:      toSwarmProtocol(port.Protocol),
   127  		}
   128  	}
   129  	return ports
   130  }
   131  
   132  type tasksBySlot []swarm.Task
   133  
   134  func (t tasksBySlot) Len() int {
   135  	return len(t)
   136  }
   137  
   138  func (t tasksBySlot) Swap(i, j int) {
   139  	t[i], t[j] = t[j], t[i]
   140  }
   141  
   142  func (t tasksBySlot) Less(i, j int) bool {
   143  	// Sort by slot.
   144  	if t[i].Slot != t[j].Slot {
   145  		return t[i].Slot < t[j].Slot
   146  	}
   147  
   148  	// If same slot, sort by most recent.
   149  	return t[j].Meta.CreatedAt.Before(t[i].CreatedAt)
   150  }
   151  
   152  const (
   153  	publishedServiceSuffix      = "-published"
   154  	publishedOnRandomPortSuffix = "-random-ports"
   155  )
   156  
   157  func convertToServices(replicas *appsv1beta2.ReplicaSetList, daemons *appsv1beta2.DaemonSetList, services *apiv1.ServiceList) ([]swarm.Service, map[string]formatter.ServiceListInfo, error) {
   158  	result := make([]swarm.Service, len(replicas.Items))
   159  	infos := make(map[string]formatter.ServiceListInfo, len(replicas.Items)+len(daemons.Items))
   160  	for i, r := range replicas.Items {
   161  		s, err := convertToService(r.Labels[labels.ForServiceName], services, r.Spec.Template.Spec.Containers)
   162  		if err != nil {
   163  			return nil, nil, err
   164  		}
   165  		result[i] = *s
   166  		infos[s.ID] = formatter.ServiceListInfo{
   167  			Mode:     "replicated",
   168  			Replicas: fmt.Sprintf("%d/%d", r.Status.AvailableReplicas, r.Status.Replicas),
   169  		}
   170  	}
   171  	for _, d := range daemons.Items {
   172  		s, err := convertToService(d.Labels[labels.ForServiceName], services, d.Spec.Template.Spec.Containers)
   173  		if err != nil {
   174  			return nil, nil, err
   175  		}
   176  		result = append(result, *s)
   177  		infos[s.ID] = formatter.ServiceListInfo{
   178  			Mode:     "global",
   179  			Replicas: fmt.Sprintf("%d/%d", d.Status.NumberReady, d.Status.DesiredNumberScheduled),
   180  		}
   181  	}
   182  	sort.Slice(result, func(i, j int) bool {
   183  		return result[i].ID < result[j].ID
   184  	})
   185  	return result, infos, nil
   186  }
   187  
   188  func convertToService(serviceName string, services *apiv1.ServiceList, containers []apiv1.Container) (*swarm.Service, error) {
   189  	serviceHeadless, err := findService(services, serviceName)
   190  	if err != nil {
   191  		return nil, err
   192  	}
   193  	stack, ok := serviceHeadless.Labels[labels.ForStackName]
   194  	if ok {
   195  		stack += "_"
   196  	}
   197  	uid := string(serviceHeadless.UID)
   198  	s := &swarm.Service{
   199  		ID: uid,
   200  		Spec: swarm.ServiceSpec{
   201  			Annotations: swarm.Annotations{
   202  				Name: stack + serviceHeadless.Name,
   203  			},
   204  			TaskTemplate: swarm.TaskSpec{
   205  				ContainerSpec: &swarm.ContainerSpec{
   206  					Image: getContainerImage(containers),
   207  				},
   208  			},
   209  		},
   210  	}
   211  	if serviceNodePort, err := findService(services, serviceName+publishedOnRandomPortSuffix); err == nil && serviceNodePort.Spec.Type == apiv1.ServiceTypeNodePort {
   212  		s.Endpoint = serviceEndpoint(serviceNodePort, swarm.PortConfigPublishModeHost)
   213  	}
   214  	if serviceLoadBalancer, err := findService(services, serviceName+publishedServiceSuffix); err == nil && serviceLoadBalancer.Spec.Type == apiv1.ServiceTypeLoadBalancer {
   215  		s.Endpoint = serviceEndpoint(serviceLoadBalancer, swarm.PortConfigPublishModeIngress)
   216  	}
   217  	return s, nil
   218  }
   219  
   220  func findService(services *apiv1.ServiceList, name string) (apiv1.Service, error) {
   221  	for _, s := range services.Items {
   222  		if s.Name == name {
   223  			return s, nil
   224  		}
   225  	}
   226  	return apiv1.Service{}, fmt.Errorf("could not find service '%s'", name)
   227  }
   228  
   229  func serviceEndpoint(service apiv1.Service, publishMode swarm.PortConfigPublishMode) swarm.Endpoint {
   230  	configs := make([]swarm.PortConfig, len(service.Spec.Ports))
   231  	for i, p := range service.Spec.Ports {
   232  		configs[i] = swarm.PortConfig{
   233  			PublishMode:   publishMode,
   234  			PublishedPort: uint32(p.Port),
   235  			TargetPort:    uint32(p.TargetPort.IntValue()),
   236  			Protocol:      toSwarmProtocol(p.Protocol),
   237  		}
   238  	}
   239  	return swarm.Endpoint{Ports: configs}
   240  }