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