From c5e07f4926cb35b0fcdc8079f45d891162c50e5a Mon Sep 17 00:00:00 2001 From: Dusty Mabe Date: Wed, 26 Oct 2016 17:07:12 -0400 Subject: [PATCH 1/2] transformers: make Openshift Inherit from Kubernetes --- cli/app/app.go | 43 ++++++++++++------------ pkg/transformer/kubernetes/k8sutils.go | 36 ++++++++++---------- pkg/transformer/kubernetes/kubernetes.go | 42 +++++++++++------------ pkg/transformer/openshift/openshift.go | 32 ++++++++++-------- 4 files changed, 79 insertions(+), 74 deletions(-) diff --git a/cli/app/app.go b/cli/app/app.go index 26370b8b..1205070e 100644 --- a/cli/app/app.go +++ b/cli/app/app.go @@ -155,14 +155,10 @@ func Convert(c *cli.Context) { } komposeObject = l.LoadFile(opt.InputFile) - // transformer maps komposeObject to provider's primitives - var t transformer.Transformer - if opt.Provider == "kubernetes" { - t = new(kubernetes.Kubernetes) - } else { - t = new(openshift.OpenShift) - } + // Get a transformer that maps komposeObject to provider's primitives + t := getTransformer(opt) + // Do the transformation objects := t.Transform(komposeObject, opt) // Print output @@ -190,13 +186,8 @@ func Up(c *cli.Context) { } komposeObject = l.LoadFile(opt.InputFile) - //get transfomer - var t transformer.Transformer - if opt.Provider == "kubernetes" { - t = new(kubernetes.Kubernetes) - } else { - t = new(openshift.OpenShift) - } + // Get the transformer + t := getTransformer(opt) //Submit objects to provider errDeploy := t.Deploy(komposeObject, opt) @@ -226,13 +217,8 @@ func Down(c *cli.Context) { } komposeObject = l.LoadFile(opt.InputFile) - //get transfomer - var t transformer.Transformer - if opt.Provider == "kubernetes" { - t = new(kubernetes.Kubernetes) - } else { - t = new(openshift.OpenShift) - } + // Get the transformer + t := getTransformer(opt) //Remove deployed application errUndeploy := t.Undeploy(komposeObject, opt) @@ -242,6 +228,21 @@ func Down(c *cli.Context) { } +// Convenience method to return the appropriate Transformer based on +// what provider we are using. +func getTransformer(opt kobject.ConvertOptions) transformer.Transformer { + var t transformer.Transformer + if opt.Provider == "kubernetes" { + // Create/Init new Kubernetes object + t = &kubernetes.Kubernetes{} + } else { + // Create/Init new OpenShift object that is initialized with a newly + // created Kubernetes object. Openshift inherits from Kubernetes + t = &openshift.OpenShift{kubernetes.Kubernetes{}} + } + return t +} + func askForConfirmation() bool { var response string _, err := fmt.Scanln(&response) diff --git a/pkg/transformer/kubernetes/k8sutils.go b/pkg/transformer/kubernetes/k8sutils.go index 38b9acf6..685389ee 100644 --- a/pkg/transformer/kubernetes/k8sutils.go +++ b/pkg/transformer/kubernetes/k8sutils.go @@ -227,7 +227,7 @@ func convertToVersion(obj runtime.Object, groupVersion unversioned.GroupVersion) return convertedObject, nil } -func PortsExist(name string, service kobject.ServiceConfig) bool { +func (k *Kubernetes) PortsExist(name string, service kobject.ServiceConfig) bool { if len(service.Port) == 0 { logrus.Warningf("[%s] Service cannot be created because of missing port.", name) return false @@ -237,11 +237,11 @@ func PortsExist(name string, service kobject.ServiceConfig) bool { } // create a k8s service -func CreateService(name string, service kobject.ServiceConfig, objects []runtime.Object) *api.Service { - svc := InitSvc(name, service) +func (k *Kubernetes) CreateService(name string, service kobject.ServiceConfig, objects []runtime.Object) *api.Service { + svc := k.InitSvc(name, service) // Configure the service ports. - servicePorts := ConfigServicePorts(name, service) + servicePorts := k.ConfigServicePorts(name, service) svc.Spec.Ports = servicePorts // Configure service types @@ -267,12 +267,12 @@ func CreateService(name string, service kobject.ServiceConfig, objects []runtime } // load configurations to k8s objects -func UpdateKubernetesObjects(name string, service kobject.ServiceConfig, objects *[]runtime.Object) { +func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.ServiceConfig, objects *[]runtime.Object) { // Configure the environment variables. - envs := ConfigEnvs(name, service) + envs := k.ConfigEnvs(name, service) // Configure the container volumes. - volumesMount, volumes, pvc := ConfigVolumes(name, service) + volumesMount, volumes, pvc := k.ConfigVolumes(name, service) if pvc != nil { // Looping on the slice pvc instead of `*objects = append(*objects, pvc...)` // because the type of objects and pvc is different, but when doing append @@ -283,7 +283,7 @@ func UpdateKubernetesObjects(name string, service kobject.ServiceConfig, objects } // Configure the container ports. - ports := ConfigPorts(name, service) + ports := k.ConfigPorts(name, service) // Configure annotations annotations := transformer.ConfigAnnotations(service) @@ -327,14 +327,14 @@ func UpdateKubernetesObjects(name string, service kobject.ServiceConfig, objects // update supported controller for _, obj := range *objects { - UpdateController(obj, fillTemplate, fillObjectMeta) + k.UpdateController(obj, fillTemplate, fillObjectMeta) } } // the objects that we get can be in any order this keeps services first // according to best practice kubernetes services should be created first // http://kubernetes.io/docs/user-guide/config-best-practices/ -func SortServicesFirst(objs *[]runtime.Object) { +func (k *Kubernetes) SortServicesFirst(objs *[]runtime.Object) { var svc, others, ret []runtime.Object for _, obj := range *objs { @@ -349,49 +349,49 @@ func SortServicesFirst(objs *[]runtime.Object) { *objs = ret } -func findDependentVolumes(svcname string, komposeObject kobject.KomposeObject) (volumes []api.Volume, volumeMounts []api.VolumeMount) { +func (k *Kubernetes) findDependentVolumes(svcname string, komposeObject kobject.KomposeObject) (volumes []api.Volume, volumeMounts []api.VolumeMount) { // Get all the volumes and volumemounts this particular service is dependent on for _, dependentSvc := range komposeObject.ServiceConfigs[svcname].VolumesFrom { - vols, volMounts := findDependentVolumes(dependentSvc, komposeObject) + vols, volMounts := k.findDependentVolumes(dependentSvc, komposeObject) volumes = append(volumes, vols...) volumeMounts = append(volumeMounts, volMounts...) } // add the volumes info of this service - volMounts, vols, _ := ConfigVolumes(svcname, komposeObject.ServiceConfigs[svcname]) + volMounts, vols, _ := k.ConfigVolumes(svcname, komposeObject.ServiceConfigs[svcname]) volumes = append(volumes, vols...) volumeMounts = append(volumeMounts, volMounts...) return } -func VolumesFrom(objects *[]runtime.Object, komposeObject kobject.KomposeObject) { +func (k *Kubernetes) VolumesFrom(objects *[]runtime.Object, komposeObject kobject.KomposeObject) { for _, obj := range *objects { switch t := obj.(type) { case *api.ReplicationController: svcName := t.ObjectMeta.Name for _, dependentSvc := range komposeObject.ServiceConfigs[svcName].VolumesFrom { - volumes, volumeMounts := findDependentVolumes(dependentSvc, komposeObject) + volumes, volumeMounts := k.findDependentVolumes(dependentSvc, komposeObject) t.Spec.Template.Spec.Volumes = append(t.Spec.Template.Spec.Volumes, volumes...) t.Spec.Template.Spec.Containers[0].VolumeMounts = append(t.Spec.Template.Spec.Containers[0].VolumeMounts, volumeMounts...) } case *extensions.Deployment: svcName := t.ObjectMeta.Name for _, dependentSvc := range komposeObject.ServiceConfigs[svcName].VolumesFrom { - volumes, volumeMounts := findDependentVolumes(dependentSvc, komposeObject) + volumes, volumeMounts := k.findDependentVolumes(dependentSvc, komposeObject) t.Spec.Template.Spec.Volumes = append(t.Spec.Template.Spec.Volumes, volumes...) t.Spec.Template.Spec.Containers[0].VolumeMounts = append(t.Spec.Template.Spec.Containers[0].VolumeMounts, volumeMounts...) } case *extensions.DaemonSet: svcName := t.ObjectMeta.Name for _, dependentSvc := range komposeObject.ServiceConfigs[svcName].VolumesFrom { - volumes, volumeMounts := findDependentVolumes(dependentSvc, komposeObject) + volumes, volumeMounts := k.findDependentVolumes(dependentSvc, komposeObject) t.Spec.Template.Spec.Volumes = append(t.Spec.Template.Spec.Volumes, volumes...) t.Spec.Template.Spec.Containers[0].VolumeMounts = append(t.Spec.Template.Spec.Containers[0].VolumeMounts, volumeMounts...) } case *deployapi.DeploymentConfig: svcName := t.ObjectMeta.Name for _, dependentSvc := range komposeObject.ServiceConfigs[svcName].VolumesFrom { - volumes, volumeMounts := findDependentVolumes(dependentSvc, komposeObject) + volumes, volumeMounts := k.findDependentVolumes(dependentSvc, komposeObject) t.Spec.Template.Spec.Volumes = append(t.Spec.Template.Spec.Volumes, volumes...) t.Spec.Template.Spec.Containers[0].VolumeMounts = append(t.Spec.Template.Spec.Containers[0].VolumeMounts, volumeMounts...) } diff --git a/pkg/transformer/kubernetes/kubernetes.go b/pkg/transformer/kubernetes/kubernetes.go index 6f47bdb2..236b644a 100644 --- a/pkg/transformer/kubernetes/kubernetes.go +++ b/pkg/transformer/kubernetes/kubernetes.go @@ -50,7 +50,7 @@ type Kubernetes struct { const TIMEOUT = 300 // Init RC object -func InitRC(name string, service kobject.ServiceConfig, replicas int) *api.ReplicationController { +func (k *Kubernetes) InitRC(name string, service kobject.ServiceConfig, replicas int) *api.ReplicationController { rc := &api.ReplicationController{ TypeMeta: unversioned.TypeMeta{ Kind: "ReplicationController", @@ -80,7 +80,7 @@ func InitRC(name string, service kobject.ServiceConfig, replicas int) *api.Repli } // Init Svc object -func InitSvc(name string, service kobject.ServiceConfig) *api.Service { +func (k *Kubernetes) InitSvc(name string, service kobject.ServiceConfig) *api.Service { svc := &api.Service{ TypeMeta: unversioned.TypeMeta{ Kind: "Service", @@ -98,7 +98,7 @@ func InitSvc(name string, service kobject.ServiceConfig) *api.Service { } // Init Deployment -func InitD(name string, service kobject.ServiceConfig, replicas int) *extensions.Deployment { +func (k *Kubernetes) InitD(name string, service kobject.ServiceConfig, replicas int) *extensions.Deployment { dc := &extensions.Deployment{ TypeMeta: unversioned.TypeMeta{ Kind: "Deployment", @@ -125,7 +125,7 @@ func InitD(name string, service kobject.ServiceConfig, replicas int) *extensions } // Init DS object -func InitDS(name string, service kobject.ServiceConfig) *extensions.DaemonSet { +func (k *Kubernetes) InitDS(name string, service kobject.ServiceConfig) *extensions.DaemonSet { ds := &extensions.DaemonSet{ TypeMeta: unversioned.TypeMeta{ Kind: "DaemonSet", @@ -151,7 +151,7 @@ func InitDS(name string, service kobject.ServiceConfig) *extensions.DaemonSet { } // Initialize PersistentVolumeClaim -func CreatePVC(name string, mode string) *api.PersistentVolumeClaim { +func (k *Kubernetes) CreatePVC(name string, mode string) *api.PersistentVolumeClaim { size, err := resource.ParseQuantity("100Mi") if err != nil { logrus.Fatalf("Error parsing size") @@ -183,7 +183,7 @@ func CreatePVC(name string, mode string) *api.PersistentVolumeClaim { } // Configure the container ports. -func ConfigPorts(name string, service kobject.ServiceConfig) []api.ContainerPort { +func (k *Kubernetes) ConfigPorts(name string, service kobject.ServiceConfig) []api.ContainerPort { ports := []api.ContainerPort{} for _, port := range service.Port { ports = append(ports, api.ContainerPort{ @@ -196,7 +196,7 @@ func ConfigPorts(name string, service kobject.ServiceConfig) []api.ContainerPort } // Configure the container service ports. -func ConfigServicePorts(name string, service kobject.ServiceConfig) []api.ServicePort { +func (k *Kubernetes) ConfigServicePorts(name string, service kobject.ServiceConfig) []api.ServicePort { servicePorts := []api.ServicePort{} for _, port := range service.Port { if port.HostPort == 0 { @@ -216,7 +216,7 @@ func ConfigServicePorts(name string, service kobject.ServiceConfig) []api.Servic } // Configure the container volumes. -func ConfigVolumes(name string, service kobject.ServiceConfig) ([]api.VolumeMount, []api.Volume, []*api.PersistentVolumeClaim) { +func (k *Kubernetes) ConfigVolumes(name string, service kobject.ServiceConfig) ([]api.VolumeMount, []api.Volume, []*api.PersistentVolumeClaim) { volumesMount := []api.VolumeMount{} volumes := []api.Volume{} var pvc []*api.PersistentVolumeClaim @@ -256,13 +256,13 @@ func ConfigVolumes(name string, service kobject.ServiceConfig) ([]api.VolumeMoun if len(host) > 0 { logrus.Warningf("Volume mount on the host %q isn't supported - ignoring path on the host", host) } - pvc = append(pvc, CreatePVC(volumeName, mode)) + pvc = append(pvc, k.CreatePVC(volumeName, mode)) } return volumesMount, volumes, pvc } // Configure the environment variables. -func ConfigEnvs(name string, service kobject.ServiceConfig) []api.EnvVar { +func (k *Kubernetes) ConfigEnvs(name string, service kobject.ServiceConfig) []api.EnvVar { envs := []api.EnvVar{} for _, v := range service.Environment { envs = append(envs, api.EnvVar{ @@ -275,17 +275,17 @@ func ConfigEnvs(name string, service kobject.ServiceConfig) []api.EnvVar { } // Generate a Kubernetes artifact for each input type service -func CreateKubernetesObjects(name string, service kobject.ServiceConfig, opt kobject.ConvertOptions) []runtime.Object { +func (k *Kubernetes) CreateKubernetesObjects(name string, service kobject.ServiceConfig, opt kobject.ConvertOptions) []runtime.Object { var objects []runtime.Object if opt.CreateD { - objects = append(objects, InitD(name, service, opt.Replicas)) + objects = append(objects, k.InitD(name, service, opt.Replicas)) } if opt.CreateDS { - objects = append(objects, InitDS(name, service)) + objects = append(objects, k.InitDS(name, service)) } if opt.CreateRC { - objects = append(objects, InitRC(name, service, opt.Replicas)) + objects = append(objects, k.InitRC(name, service, opt.Replicas)) } return objects @@ -298,27 +298,27 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject. var allobjects []runtime.Object for name, service := range komposeObject.ServiceConfigs { - objects := CreateKubernetesObjects(name, service, opt) + objects := k.CreateKubernetesObjects(name, service, opt) // If ports not provided in configuration we will not make service - if PortsExist(name, service) { - svc := CreateService(name, service, objects) + if k.PortsExist(name, service) { + svc := k.CreateService(name, service, objects) objects = append(objects, svc) } - UpdateKubernetesObjects(name, service, &objects) + k.UpdateKubernetesObjects(name, service, &objects) allobjects = append(allobjects, objects...) } // If docker-compose has a volumes_from directive it will be handled here - VolumesFrom(&allobjects, komposeObject) + k.VolumesFrom(&allobjects, komposeObject) // sort all object so Services are first - SortServicesFirst(&allobjects) + k.SortServicesFirst(&allobjects) return allobjects } // Updates the given object with the given pod template update function and ObjectMeta update function -func UpdateController(obj runtime.Object, updateTemplate func(*api.PodTemplateSpec), updateMeta func(meta *api.ObjectMeta)) { +func (k *Kubernetes) UpdateController(obj runtime.Object, updateTemplate func(*api.PodTemplateSpec), updateMeta func(meta *api.ObjectMeta)) { switch t := obj.(type) { case *api.ReplicationController: if t.Spec.Template == nil { diff --git a/pkg/transformer/openshift/openshift.go b/pkg/transformer/openshift/openshift.go index 834c6311..b6ec5534 100644 --- a/pkg/transformer/openshift/openshift.go +++ b/pkg/transformer/openshift/openshift.go @@ -42,6 +42,10 @@ import ( ) type OpenShift struct { + // Anonymous field allows for inheritance. We are basically inheriting + // all of kubernetes.Kubernetes Methods and variables here. We'll overwite + // some of those methods with our own for openshift. + kubernetes.Kubernetes } // getImageTag get tag name from image name @@ -56,7 +60,7 @@ func getImageTag(image string) string { } // initImageStream initialize ImageStream object -func initImageStream(name string, service kobject.ServiceConfig) *imageapi.ImageStream { +func (o *OpenShift) initImageStream(name string, service kobject.ServiceConfig) *imageapi.ImageStream { tag := getImageTag(service.Image) is := &imageapi.ImageStream{ @@ -82,7 +86,7 @@ func initImageStream(name string, service kobject.ServiceConfig) *imageapi.Image } // initDeploymentConfig initialize OpenShifts DeploymentConfig object -func initDeploymentConfig(name string, service kobject.ServiceConfig, replicas int) *deployapi.DeploymentConfig { +func (o *OpenShift) initDeploymentConfig(name string, service kobject.ServiceConfig, replicas int) *deployapi.DeploymentConfig { tag := getImageTag(service.Image) dc := &deployapi.DeploymentConfig{ @@ -137,39 +141,39 @@ func initDeploymentConfig(name string, service kobject.ServiceConfig, replicas i // Transform maps komposeObject to openshift objects // returns objects that are already sorted in the way that Services are first -func (k *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) []runtime.Object { +func (o *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) []runtime.Object { // this will hold all the converted data var allobjects []runtime.Object for name, service := range komposeObject.ServiceConfigs { - objects := kubernetes.CreateKubernetesObjects(name, service, opt) + objects := o.CreateKubernetesObjects(name, service, opt) if opt.CreateDeploymentConfig { - objects = append(objects, initDeploymentConfig(name, service, opt.Replicas)) // OpenShift DeploymentConfigs + objects = append(objects, o.initDeploymentConfig(name, service, opt.Replicas)) // OpenShift DeploymentConfigs // create ImageStream after deployment (creating IS will trigger new deployment) - objects = append(objects, initImageStream(name, service)) + objects = append(objects, o.initImageStream(name, service)) } // If ports not provided in configuration we will not make service - if kubernetes.PortsExist(name, service) { - svc := kubernetes.CreateService(name, service, objects) + if o.PortsExist(name, service) { + svc := o.CreateService(name, service, objects) objects = append(objects, svc) } - kubernetes.UpdateKubernetesObjects(name, service, &objects) + o.UpdateKubernetesObjects(name, service, &objects) allobjects = append(allobjects, objects...) } // If docker-compose has a volumes_from directive it will be handled here - kubernetes.VolumesFrom(&allobjects, komposeObject) + o.VolumesFrom(&allobjects, komposeObject) // sort all object so Services are first - kubernetes.SortServicesFirst(&allobjects) + o.SortServicesFirst(&allobjects) return allobjects } -func (k *OpenShift) Deploy(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) error { +func (o *OpenShift) Deploy(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) error { //Convert komposeObject - objects := k.Transform(komposeObject, opt) + objects := o.Transform(komposeObject, opt) fmt.Println("We are going to create OpenShift DeploymentConfigs, Services and PersistentVolumeClaims for your Dockerized application. \n" + "If you need different kind of resources, use the 'kompose convert' and 'oc create -f' commands instead. \n") @@ -230,6 +234,6 @@ func (k *OpenShift) Deploy(komposeObject kobject.KomposeObject, opt kobject.Conv return nil } -func (k *OpenShift) Undeploy(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) error { +func (o *OpenShift) Undeploy(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) error { return errors.New("Not Implemented") } From 51dea8283aea691f5b6e777350657b2b102b7650 Mon Sep 17 00:00:00 2001 From: Dusty Mabe Date: Thu, 27 Oct 2016 10:41:34 -0400 Subject: [PATCH 2/2] transformers: add Opt variable as object data This is so you can set Opts on instance creation of kubernetes.Kubernetes and openshift.Openshift. This is useful so that we can pass option information arround without having to do it on the call stack every time. --- cli/app/app.go | 6 +++--- pkg/transformer/kubernetes/kubernetes.go | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cli/app/app.go b/cli/app/app.go index 1205070e..bb2052d1 100644 --- a/cli/app/app.go +++ b/cli/app/app.go @@ -233,12 +233,12 @@ func Down(c *cli.Context) { func getTransformer(opt kobject.ConvertOptions) transformer.Transformer { var t transformer.Transformer if opt.Provider == "kubernetes" { - // Create/Init new Kubernetes object - t = &kubernetes.Kubernetes{} + // Create/Init new Kubernetes object with CLI opts + t = &kubernetes.Kubernetes{Opt: opt} } else { // Create/Init new OpenShift object that is initialized with a newly // created Kubernetes object. Openshift inherits from Kubernetes - t = &openshift.OpenShift{kubernetes.Kubernetes{}} + t = &openshift.OpenShift{kubernetes.Kubernetes{Opt: opt}} } return t } diff --git a/pkg/transformer/kubernetes/kubernetes.go b/pkg/transformer/kubernetes/kubernetes.go index 236b644a..45761cea 100644 --- a/pkg/transformer/kubernetes/kubernetes.go +++ b/pkg/transformer/kubernetes/kubernetes.go @@ -43,6 +43,8 @@ import ( ) type Kubernetes struct { + // the user provided options from the command line + Opt kobject.ConvertOptions } // timeout is how long we'll wait for the termination of kubernetes resource to be successful