From c8436b4912656766df79c37aed3479afe72ca111 Mon Sep 17 00:00:00 2001 From: baude Date: Wed, 21 Nov 2018 16:00:50 -0600 Subject: generate kube add the ability to generate kubernetes pod and service yaml representations of libpod containers and pods. Signed-off-by: baude --- libpod/kube.go | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 131 insertions(+), 11 deletions(-) (limited to 'libpod') diff --git a/libpod/kube.go b/libpod/kube.go index 1a5f80878..05a6537c4 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -2,7 +2,10 @@ package libpod import ( "fmt" + "math/rand" + "strconv" "strings" + "time" "github.com/containers/libpod/pkg/lookup" "github.com/containers/libpod/pkg/util" @@ -15,23 +18,127 @@ import ( v12 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// InspectForKube takes a slice of libpod containers and generates +// GenerateForKube takes a slice of libpod containers and generates // one v1.Pod description that includes just a single container. -func (c *Container) InspectForKube() (*v1.Pod, error) { +func (c *Container) GenerateForKube() (*v1.Pod, error) { // Generate the v1.Pod yaml description return simplePodWithV1Container(c) } -// simplePodWithV1Container is a function used by inspect when kube yaml needs to be generated -// for a single container. we "insert" that container description in a pod. -func simplePodWithV1Container(ctr *Container) (*v1.Pod, error) { - var containers []v1.Container - result, err := containerToV1Container(ctr) +// GenerateForKube takes a slice of libpod containers and generates +// one v1.Pod description +func (p *Pod) GenerateForKube() (*v1.Pod, []v1.ServicePort, error) { + // Generate the v1.Pod yaml description + var servicePorts []v1.ServicePort + + allContainers, err := p.allContainers() + if err != nil { + return nil, servicePorts, err + } + // If the pod has no containers, no sense to generate YAML + if len(allContainers) == 0 { + return nil, servicePorts, errors.Errorf("pod %s has no containers", p.ID()) + } + // If only an infra container is present, makes no sense to generate YAML + if len(allContainers) == 1 && p.HasInfraContainer() { + return nil, servicePorts, errors.Errorf("pod %s only has an infra container", p.ID()) + } + + if p.HasInfraContainer() { + infraContainer, err := p.getInfraContainer() + if err != nil { + return nil, servicePorts, err + } + + ports, err := ocicniPortMappingToContainerPort(infraContainer.config.PortMappings) + if err != nil { + return nil, servicePorts, err + } + servicePorts = containerPortsToServicePorts(ports) + } + pod, err := p.podWithContainers(allContainers) + return pod, servicePorts, err +} + +func (p *Pod) getInfraContainer() (*Container, error) { + infraID, err := p.InfraContainerID() if err != nil { return nil, err } - containers = append(containers, result) + return p.runtime.LookupContainer(infraID) +} + +// GenerateKubeServiceFromV1Pod creates a v1 service object from a v1 pod object +func GenerateKubeServiceFromV1Pod(pod *v1.Pod, servicePorts []v1.ServicePort) v1.Service { + service := v1.Service{} + selector := make(map[string]string) + selector["app"] = pod.Labels["app"] + ports := servicePorts + if len(ports) == 0 { + ports = containersToServicePorts(pod.Spec.Containers) + } + serviceSpec := v1.ServiceSpec{ + Ports: ports, + Selector: selector, + Type: v1.ServiceTypeNodePort, + } + service.Spec = serviceSpec + service.ObjectMeta = pod.ObjectMeta + tm := v12.TypeMeta{ + Kind: "Service", + APIVersion: pod.TypeMeta.APIVersion, + } + service.TypeMeta = tm + return service +} +// containerPortsToServicePorts takes a slice of containerports and generates a +// slice of service ports +func containerPortsToServicePorts(containerPorts []v1.ContainerPort) []v1.ServicePort { + var sps []v1.ServicePort + for _, cp := range containerPorts { + nodePort := 30000 + rand.Intn(32767-30000+1) + servicePort := v1.ServicePort{ + Protocol: cp.Protocol, + Port: cp.ContainerPort, + NodePort: int32(nodePort), + Name: strconv.Itoa(int(cp.ContainerPort)), + } + sps = append(sps, servicePort) + } + return sps +} + +// containersToServicePorts takes a slice of v1.Containers and generates an +// inclusive list of serviceports to expose +func containersToServicePorts(containers []v1.Container) []v1.ServicePort { + var sps []v1.ServicePort + // Without the call to rand.Seed, a program will produce the same sequence of pseudo-random numbers + // for each execution. Legal nodeport range is 30000-32767 + rand.Seed(time.Now().UnixNano()) + + for _, ctr := range containers { + sps = append(sps, containerPortsToServicePorts(ctr.Ports)...) + } + return sps +} + +func (p *Pod) podWithContainers(containers []*Container) (*v1.Pod, error) { + var podContainers []v1.Container + for _, ctr := range containers { + result, err := containerToV1Container(ctr) + if err != nil { + return nil, err + } + if !ctr.IsInfra() { + podContainers = append(podContainers, result) + } + } + + return addContainersToPodObject(podContainers, p.Name()), nil +} + +func addContainersToPodObject(containers []v1.Container, podName string) *v1.Pod { tm := v12.TypeMeta{ Kind: "Pod", APIVersion: "v1", @@ -39,10 +146,10 @@ func simplePodWithV1Container(ctr *Container) (*v1.Pod, error) { // Add a label called "app" with the containers name as a value labels := make(map[string]string) - labels["app"] = removeUnderscores(ctr.Name()) + labels["app"] = removeUnderscores(podName) om := v12.ObjectMeta{ // The name of the pod is container_name-libpod - Name: fmt.Sprintf("%s-libpod", removeUnderscores(ctr.Name())), + Name: fmt.Sprintf("%s-libpod", removeUnderscores(podName)), Labels: labels, // CreationTimestamp seems to be required, so adding it; in doing so, the timestamp // will reflect time this is run (not container create time) because the conversion @@ -57,7 +164,20 @@ func simplePodWithV1Container(ctr *Container) (*v1.Pod, error) { ObjectMeta: om, Spec: ps, } - return &p, nil + return &p +} + +// simplePodWithV1Container is a function used by inspect when kube yaml needs to be generated +// for a single container. we "insert" that container description in a pod. +func simplePodWithV1Container(ctr *Container) (*v1.Pod, error) { + var containers []v1.Container + result, err := containerToV1Container(ctr) + if err != nil { + return nil, err + } + containers = append(containers, result) + return addContainersToPodObject(containers, ctr.Name()), nil + } // containerToV1Container converts information we know about a libpod container -- cgit v1.2.3-54-g00ecf