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 }