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 }