From f10d6afecf82f168b7772ef64e38dad0fda1008f Mon Sep 17 00:00:00 2001 From: Tuna Date: Mon, 8 Aug 2016 00:00:55 +0700 Subject: [PATCH] make loader, transformer as interfaces --- cli/app/app.go | 24 +- pkg/loader/{ => bundle}/bundle.go | 12 +- pkg/loader/{ => compose}/compose.go | 52 ++-- pkg/loader/loader.go | 23 ++ pkg/transformer/{ => kubernetes}/k8sutils.go | 4 +- pkg/transformer/kubernetes/kubernetes.go | 176 +++++++++++ pkg/transformer/openshift.go | 58 ---- pkg/transformer/openshift/openshift.go | 195 ++++++++++++ pkg/transformer/transformer.go | 24 ++ pkg/transformer/{kubernetes.go => utils.go} | 300 +++++-------------- 10 files changed, 548 insertions(+), 320 deletions(-) rename pkg/loader/{ => bundle}/bundle.go (97%) rename pkg/loader/{ => compose}/compose.go (91%) create mode 100644 pkg/loader/loader.go rename pkg/transformer/{ => kubernetes}/k8sutils.go (94%) create mode 100644 pkg/transformer/kubernetes/kubernetes.go delete mode 100644 pkg/transformer/openshift.go create mode 100644 pkg/transformer/openshift/openshift.go create mode 100644 pkg/transformer/transformer.go rename pkg/transformer/{kubernetes.go => utils.go} (56%) diff --git a/cli/app/app.go b/cli/app/app.go index 3633a851..b1fdc599 100644 --- a/cli/app/app.go +++ b/cli/app/app.go @@ -34,12 +34,16 @@ import ( "github.com/skippbox/kompose/pkg/kobject" "github.com/skippbox/kompose/pkg/loader" + "github.com/skippbox/kompose/pkg/loader/bundle" + "github.com/skippbox/kompose/pkg/loader/compose" "github.com/skippbox/kompose/pkg/transformer" "github.com/docker/libcompose/lookup" "github.com/docker/libcompose/config" "github.com/docker/libcompose/project" "fmt" "strings" + "github.com/skippbox/kompose/pkg/transformer/kubernetes" + "github.com/skippbox/kompose/pkg/transformer/openshift" ) const ( @@ -450,16 +454,18 @@ func Convert(c *cli.Context) { } // loader parses input from file into komposeObject. + var l loader.Loader switch inputFormat { case "bundle": - komposeObject = loader.LoadBundle(file) + l = new(bundle.Bundle) case "compose": - komposeObject = loader.LoadCompose(file) + l = new(compose.Compose) default: logrus.Fatalf("Input file format is not supported") - } + komposeObject = l.LoadFile(file) + opt := kobject.ConvertOptions{ ToStdout: toStdout, CreateD: createD, @@ -476,10 +482,18 @@ func Convert(c *cli.Context) { validateFlags(opt, singleOutput, dabFile, inputFile) // transformer maps komposeObject to provider(K8S, OpenShift) primitives - mServices, mDeployments, mDaemonSets, mReplicationControllers, mDeploymentConfigs, svcnames := transformer.Transform(komposeObject, opt) + var t transformer.Transformer + if !createDeploymentConfig { + t = new(kubernetes.Kubernetes) + } else { + t = new(openshift.OpenShift) + } + + mServices, mDeployments, mDaemonSets, mReplicationControllers, mDeploymentConfigs, svcnames := t.Transform(komposeObject, opt) // Print output - transformer.PrintControllers(mServices, mDeployments, mDaemonSets, mReplicationControllers, mDeploymentConfigs, svcnames, opt, f) + transformer.PrintControllers(mServices, mDeployments, mDaemonSets, mReplicationControllers, mDeploymentConfigs, svcnames, opt) + } // Up brings up deployment, svc. diff --git a/pkg/loader/bundle.go b/pkg/loader/bundle/bundle.go similarity index 97% rename from pkg/loader/bundle.go rename to pkg/loader/bundle/bundle.go index 77d459c9..4a4c684e 100644 --- a/pkg/loader/bundle.go +++ b/pkg/loader/bundle/bundle.go @@ -14,16 +14,20 @@ See the License for the specific language governing permissions and limitations under the License. */ -package loader +package bundle import ( + "io/ioutil" + "strings" + "github.com/Sirupsen/logrus" "github.com/docker/docker/api/client/bundlefile" "github.com/skippbox/kompose/pkg/kobject" - "io/ioutil" - "strings" ) +type Bundle struct { +} + // load Image from bundles file func loadImage(service bundlefile.Service) (string, string) { character := "@" @@ -93,7 +97,7 @@ func loadPortsfromBundle(service bundlefile.Service) ([]kobject.Ports, string) { } // load Bundlefile into KomposeObject -func LoadBundle(file string) kobject.KomposeObject { +func (b *Bundle) LoadFile(file string) kobject.KomposeObject { komposeObject := kobject.KomposeObject{ ServiceConfigs: make(map[string]kobject.ServiceConfig), } diff --git a/pkg/loader/compose.go b/pkg/loader/compose/compose.go similarity index 91% rename from pkg/loader/compose.go rename to pkg/loader/compose/compose.go index 743038cf..b3ca6c7a 100644 --- a/pkg/loader/compose.go +++ b/pkg/loader/compose/compose.go @@ -14,21 +14,25 @@ See the License for the specific language governing permissions and limitations under the License. */ -package loader +package compose import ( - "github.com/Sirupsen/logrus" - "github.com/docker/libcompose/docker" - "github.com/docker/libcompose/project" - "github.com/skippbox/kompose/pkg/kobject" + "os" + "path/filepath" "strconv" "strings" - "github.com/docker/libcompose/lookup" - "os" + + "github.com/Sirupsen/logrus" "github.com/docker/libcompose/config" - "path/filepath" + "github.com/docker/libcompose/docker" + "github.com/docker/libcompose/lookup" + "github.com/docker/libcompose/project" + "github.com/skippbox/kompose/pkg/kobject" ) +type Compose struct { +} + // load Environment Variable from compose file func loadEnvVarsfromCompose(e map[string]string) []kobject.EnvVar { envs := []kobject.EnvVar{} @@ -81,7 +85,7 @@ func loadPortsFromCompose(composePorts []string) ([]kobject.Ports, string) { } // load Docker Compose file into KomposeObject -func LoadCompose(file string) kobject.KomposeObject { +func (c *Compose) LoadFile(file string) kobject.KomposeObject { komposeObject := kobject.KomposeObject{ ServiceConfigs: make(map[string]kobject.ServiceConfig), } @@ -92,23 +96,23 @@ func LoadCompose(file string) kobject.KomposeObject { context.ComposeFiles = []string{file} if context.ResourceLookup == nil { - context.ResourceLookup = &lookup.FileResourceLookup{} - } + context.ResourceLookup = &lookup.FileResourceLookup{} + } - if context.EnvironmentLookup == nil { - cwd, err := os.Getwd() - if err != nil { - return kobject.KomposeObject{} - } - context.EnvironmentLookup = &lookup.ComposableEnvLookup{ - Lookups: []config.EnvironmentLookup{ - &lookup.EnvfileLookup{ - Path: filepath.Join(cwd, ".env"), - }, - &lookup.OsEnvLookup{}, - }, - } + if context.EnvironmentLookup == nil { + cwd, err := os.Getwd() + if err != nil { + return kobject.KomposeObject{} } + context.EnvironmentLookup = &lookup.ComposableEnvLookup{ + Lookups: []config.EnvironmentLookup{ + &lookup.EnvfileLookup{ + Path: filepath.Join(cwd, ".env"), + }, + &lookup.OsEnvLookup{}, + }, + } + } // load compose file into composeObject composeObject := project.NewProject(&context.Context, nil, nil) diff --git a/pkg/loader/loader.go b/pkg/loader/loader.go new file mode 100644 index 00000000..d4f07e19 --- /dev/null +++ b/pkg/loader/loader.go @@ -0,0 +1,23 @@ +/* +Copyright 2016 Skippbox, Ltd All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package loader + +import "github.com/skippbox/kompose/pkg/kobject" + +type Loader interface { + LoadFile(file string) kobject.KomposeObject +} diff --git a/pkg/transformer/k8sutils.go b/pkg/transformer/kubernetes/k8sutils.go similarity index 94% rename from pkg/transformer/k8sutils.go rename to pkg/transformer/kubernetes/k8sutils.go index f75738e8..5137ec9a 100644 --- a/pkg/transformer/k8sutils.go +++ b/pkg/transformer/kubernetes/k8sutils.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package transformer +package kubernetes import ( "bytes" @@ -29,7 +29,7 @@ import ( /** * Generate Helm Chart configuration */ -func generateHelm(filename string, outFiles []string) error { +func GenerateHelm(filename string, svcnames []string, generateYaml, createD, createDS, createRC bool, outFile string) error { type ChartDetails struct { Name string } diff --git a/pkg/transformer/kubernetes/kubernetes.go b/pkg/transformer/kubernetes/kubernetes.go new file mode 100644 index 00000000..ea2e09fb --- /dev/null +++ b/pkg/transformer/kubernetes/kubernetes.go @@ -0,0 +1,176 @@ +/* +Copyright 2016 Skippbox, Ltd All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kubernetes + +import ( + "fmt" + "os" + + "github.com/Sirupsen/logrus" + deployapi "github.com/openshift/origin/pkg/deploy/api" + "github.com/skippbox/kompose/pkg/kobject" + "github.com/skippbox/kompose/pkg/transformer" + + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/runtime" +) + +type Kubernetes struct { +} + +func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) (map[string][]byte, map[string][]byte, map[string][]byte, map[string][]byte, map[string][]byte, []string){ + mServices := make(map[string][]byte) + mReplicationControllers := make(map[string][]byte) + mDeployments := make(map[string][]byte) + mDaemonSets := make(map[string][]byte) + + var svcnames []string + + for name, service := range komposeObject.ServiceConfigs { + svcnames = append(svcnames, name) + + rc := transformer.InitRC(name, service, opt.Replicas) + sc := transformer.InitSC(name, service) + dc := transformer.InitDC(name, service, opt.Replicas) + ds := transformer.InitDS(name, service) + + // Configure the environment variables. + envs := transformer.ConfigEnvs(name, service) + + // Configure the container command. + cmds := transformer.ConfigCommands(service) + + // Configure the container volumes. + volumesMount, volumes := transformer.ConfigVolumes(service) + + // Configure the container ports. + ports := transformer.ConfigPorts(name, service) + + // Configure the service ports. + servicePorts := transformer.ConfigServicePorts(name, service) + sc.Spec.Ports = servicePorts + + // Configure label + labels := transformer.ConfigLabels(name) + sc.ObjectMeta.Labels = labels + + // Configure annotations + annotations := transformer.ConfigAnnotations(service) + sc.ObjectMeta.Annotations = annotations + + // fillTemplate fills the pod template with the value calculated from config + fillTemplate := func(template *api.PodTemplateSpec) { + template.Spec.Containers[0].Env = envs + template.Spec.Containers[0].Command = cmds + template.Spec.Containers[0].WorkingDir = service.WorkingDir + template.Spec.Containers[0].VolumeMounts = volumesMount + template.Spec.Volumes = volumes + // Configure the container privileged mode + if service.Privileged == true { + template.Spec.Containers[0].SecurityContext = &api.SecurityContext{ + Privileged: &service.Privileged, + } + } + template.Spec.Containers[0].Ports = ports + template.ObjectMeta.Labels = labels + // Configure the container restart policy. + switch service.Restart { + case "", "always": + template.Spec.RestartPolicy = api.RestartPolicyAlways + case "no": + template.Spec.RestartPolicy = api.RestartPolicyNever + case "on-failure": + template.Spec.RestartPolicy = api.RestartPolicyOnFailure + default: + logrus.Fatalf("Unknown restart policy %s for service %s", service.Restart, name) + } + } + + // fillObjectMeta fills the metadata with the value calculated from config + fillObjectMeta := func(meta *api.ObjectMeta) { + meta.Labels = labels + meta.Annotations = annotations + } + + // Update each supported controllers + UpdateController(rc, fillTemplate, fillObjectMeta) + UpdateController(dc, fillTemplate, fillObjectMeta) + UpdateController(ds, fillTemplate, fillObjectMeta) + + // convert datarc to json / yaml + datarc, err := transformer.TransformData(rc, opt.GenerateYaml) + if err != nil { + logrus.Fatalf(err.Error()) + } + + // convert datadc to json / yaml + datadc, err := transformer.TransformData(dc, opt.GenerateYaml) + if err != nil { + logrus.Fatalf(err.Error()) + } + + // convert datads to json / yaml + datads, err := transformer.TransformData(ds, opt.GenerateYaml) + if err != nil { + logrus.Fatalf(err.Error()) + } + + var datasvc []byte + // If ports not provided in configuration we will not make service + if len(ports) == 0 { + logrus.Warningf("[%s] Service cannot be created because of missing port.", name) + } else if len(ports) != 0 { + // convert datasvc to json / yaml + datasvc, err = transformer.TransformData(sc, opt.GenerateYaml) + if err != nil { + logrus.Fatalf(err.Error()) + } + } + + mServices[name] = datasvc + mReplicationControllers[name] = datarc + mDeployments[name] = datadc + mDaemonSets[name] = datads + } + + return mServices, mDeployments, mDaemonSets, mReplicationControllers, nil, svcnames +} + +// updateController 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)) { + switch t := obj.(type) { + case *api.ReplicationController: + if t.Spec.Template == nil { + t.Spec.Template = &api.PodTemplateSpec{} + } + updateTemplate(t.Spec.Template) + updateMeta(&t.ObjectMeta) + case *extensions.Deployment: + updateTemplate(&t.Spec.Template) + updateMeta(&t.ObjectMeta) + case *extensions.ReplicaSet: + updateTemplate(&t.Spec.Template) + updateMeta(&t.ObjectMeta) + case *extensions.DaemonSet: + updateTemplate(&t.Spec.Template) + updateMeta(&t.ObjectMeta) + case *deployapi.DeploymentConfig: + updateTemplate(t.Spec.Template) + updateMeta(&t.ObjectMeta) + } +} diff --git a/pkg/transformer/openshift.go b/pkg/transformer/openshift.go deleted file mode 100644 index 10d8bac4..00000000 --- a/pkg/transformer/openshift.go +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright 2016 Skippbox, Ltd All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package transformer - -import ( - "github.com/skippbox/kompose/pkg/kobject" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - - deployapi "github.com/openshift/origin/pkg/deploy/api" -) - -// initDeploymentConfig initialize OpenShifts DeploymentConfig object -func initDeploymentConfig(name string, service kobject.ServiceConfig, replicas int) *deployapi.DeploymentConfig { - dc := &deployapi.DeploymentConfig{ - TypeMeta: unversioned.TypeMeta{ - Kind: "DeploymentConfig", - APIVersion: "v1", - }, - ObjectMeta: api.ObjectMeta{ - Name: name, - Labels: map[string]string{"service": name}, - }, - Spec: deployapi.DeploymentConfigSpec{ - Replicas: int32(replicas), - Selector: map[string]string{"service": name}, - //UniqueLabelKey: p.Name, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"service": name}, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: name, - Image: service.Image, - }, - }, - }, - }, - }, - } - return dc -} diff --git a/pkg/transformer/openshift/openshift.go b/pkg/transformer/openshift/openshift.go new file mode 100644 index 00000000..d2334718 --- /dev/null +++ b/pkg/transformer/openshift/openshift.go @@ -0,0 +1,195 @@ +/* +Copyright 2016 Skippbox, Ltd All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package openshift + +import ( + "github.com/Sirupsen/logrus" + deployapi "github.com/openshift/origin/pkg/deploy/api" + "github.com/skippbox/kompose/pkg/kobject" + "github.com/skippbox/kompose/pkg/transformer" + "github.com/skippbox/kompose/pkg/transformer/kubernetes" + + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/unversioned" +) + +type OpenShift struct { +} + +// initDeploymentConfig initialize OpenShifts DeploymentConfig object +func initDeploymentConfig(name string, service kobject.ServiceConfig, replicas int) *deployapi.DeploymentConfig { + dc := &deployapi.DeploymentConfig{ + TypeMeta: unversioned.TypeMeta{ + Kind: "DeploymentConfig", + APIVersion: "v1", + }, + ObjectMeta: api.ObjectMeta{ + Name: name, + Labels: map[string]string{"service": name}, + }, + Spec: deployapi.DeploymentConfigSpec{ + Replicas: int32(replicas), + Selector: map[string]string{"service": name}, + //UniqueLabelKey: p.Name, + Template: &api.PodTemplateSpec{ + ObjectMeta: api.ObjectMeta{ + Labels: map[string]string{"service": name}, + }, + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: name, + Image: service.Image, + }, + }, + }, + }, + }, + } + return dc +} + +func (k *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) (map[string][]byte, map[string][]byte, map[string][]byte, map[string][]byte, map[string][]byte, []string){ + mServices := make(map[string][]byte) + mReplicationControllers := make(map[string][]byte) + mDeployments := make(map[string][]byte) + mDaemonSets := make(map[string][]byte) + + // OpenShift DeploymentConfigs + mDeploymentConfigs := make(map[string][]byte) + + var svcnames []string + + for name, service := range komposeObject.ServiceConfigs { + svcnames = append(svcnames, name) + + rc := transformer.InitRC(name, service, opt.Replicas) + sc := transformer.InitSC(name, service) + dc := transformer.InitDC(name, service, opt.Replicas) + ds := transformer.InitDS(name, service) + osDC := initDeploymentConfig(name, service, opt.Replicas) // OpenShift DeploymentConfigs + + // Configure the environment variables. + envs := transformer.ConfigEnvs(name, service) + + // Configure the container command. + cmds := transformer.ConfigCommands(service) + + // Configure the container volumes. + volumesMount, volumes := transformer.ConfigVolumes(service) + + // Configure the container ports. + ports := transformer.ConfigPorts(name, service) + + // Configure the service ports. + servicePorts := transformer.ConfigServicePorts(name, service) + sc.Spec.Ports = servicePorts + + // Configure label + labels := transformer.ConfigLabels(name) + sc.ObjectMeta.Labels = labels + + // Configure annotations + annotations := transformer.ConfigAnnotations(service) + sc.ObjectMeta.Annotations = annotations + + // fillTemplate fills the pod template with the value calculated from config + fillTemplate := func(template *api.PodTemplateSpec) { + template.Spec.Containers[0].Env = envs + template.Spec.Containers[0].Command = cmds + template.Spec.Containers[0].WorkingDir = service.WorkingDir + template.Spec.Containers[0].VolumeMounts = volumesMount + template.Spec.Volumes = volumes + // Configure the container privileged mode + if service.Privileged == true { + template.Spec.Containers[0].SecurityContext = &api.SecurityContext{ + Privileged: &service.Privileged, + } + } + template.Spec.Containers[0].Ports = ports + template.ObjectMeta.Labels = labels + // Configure the container restart policy. + switch service.Restart { + case "", "always": + template.Spec.RestartPolicy = api.RestartPolicyAlways + case "no": + template.Spec.RestartPolicy = api.RestartPolicyNever + case "on-failure": + template.Spec.RestartPolicy = api.RestartPolicyOnFailure + default: + logrus.Fatalf("Unknown restart policy %s for service %s", service.Restart, name) + } + } + + // fillObjectMeta fills the metadata with the value calculated from config + fillObjectMeta := func(meta *api.ObjectMeta) { + meta.Labels = labels + meta.Annotations = annotations + } + + // Update each supported controllers + kubernetes.UpdateController(rc, fillTemplate, fillObjectMeta) + kubernetes.UpdateController(dc, fillTemplate, fillObjectMeta) + kubernetes.UpdateController(ds, fillTemplate, fillObjectMeta) + // OpenShift DeploymentConfigs + kubernetes.UpdateController(osDC, fillTemplate, fillObjectMeta) + + // convert datarc to json / yaml + datarc, err := transformer.TransformData(rc, opt.GenerateYaml) + if err != nil { + logrus.Fatalf(err.Error()) + } + + // convert datadc to json / yaml + datadc, err := transformer.TransformData(dc, opt.GenerateYaml) + if err != nil { + logrus.Fatalf(err.Error()) + } + + // convert datads to json / yaml + datads, err := transformer.TransformData(ds, opt.GenerateYaml) + if err != nil { + logrus.Fatalf(err.Error()) + } + + var datasvc []byte + // If ports not provided in configuration we will not make service + if len(ports) == 0 { + logrus.Warningf("[%s] Service cannot be created because of missing port.", name) + } else if len(ports) != 0 { + // convert datasvc to json / yaml + datasvc, err = transformer.TransformData(sc, opt.GenerateYaml) + if err != nil { + logrus.Fatalf(err.Error()) + } + } + + // convert OpenShift DeploymentConfig to json / yaml + dataDeploymentConfig, err := transformer.TransformData(osDC, opt.GenerateYaml) + if err != nil { + logrus.Fatalf(err.Error()) + } + + mServices[name] = datasvc + mReplicationControllers[name] = datarc + mDeployments[name] = datadc + mDaemonSets[name] = datads + mDeploymentConfigs[name] = dataDeploymentConfig + } + + return mServices, mDeployments, mDaemonSets, mReplicationControllers, mDeploymentConfigs, svcnames +} diff --git a/pkg/transformer/transformer.go b/pkg/transformer/transformer.go new file mode 100644 index 00000000..f0deacb5 --- /dev/null +++ b/pkg/transformer/transformer.go @@ -0,0 +1,24 @@ +/* +Copyright 2016 Skippbox, Ltd All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package transformer + +import "github.com/skippbox/kompose/pkg/kobject" + +type Transformer interface { + Transform(kobject.KomposeObject, kobject.ConvertOptions) (map[string][]byte, map[string][]byte, map[string][]byte, map[string][]byte, map[string][]byte, []string) +} + diff --git a/pkg/transformer/kubernetes.go b/pkg/transformer/utils.go similarity index 56% rename from pkg/transformer/kubernetes.go rename to pkg/transformer/utils.go index 1b995a4f..a96f7a26 100644 --- a/pkg/transformer/kubernetes.go +++ b/pkg/transformer/utils.go @@ -1,45 +1,30 @@ -/* -Copyright 2016 Skippbox, Ltd All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - package transformer import ( "encoding/json" "fmt" - "github.com/Sirupsen/logrus" - "github.com/ghodss/yaml" - "github.com/skippbox/kompose/pkg/kobject" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/util/intstr" + "io/ioutil" "math/rand" "os" "strconv" "strings" - deployapi "github.com/openshift/origin/pkg/deploy/api" - "io/ioutil" + "github.com/Sirupsen/logrus" + "github.com/ghodss/yaml" + "github.com/skippbox/kompose/pkg/kobject" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util/intstr" + "github.com/skippbox/kompose/pkg/transformer/kubernetes" ) const letterBytes = "abcdefghijklmnopqrstuvwxyz0123456789" // RandStringBytes generates randomly n-character string -func RandStringBytes(n int) string { +func randStringBytes(n int) string { b := make([]byte, n) for i := range b { b[i] = letterBytes[rand.Intn(len(letterBytes))] @@ -61,7 +46,7 @@ func createOutFile(out string) *os.File { } // Init RC object -func initRC(name string, service kobject.ServiceConfig, replicas int) *api.ReplicationController { +func InitRC(name string, service kobject.ServiceConfig, replicas int) *api.ReplicationController { rc := &api.ReplicationController{ TypeMeta: unversioned.TypeMeta{ Kind: "ReplicationController", @@ -93,7 +78,7 @@ func initRC(name string, service kobject.ServiceConfig, replicas int) *api.Repli } // Init SC object -func initSC(name string, service kobject.ServiceConfig) *api.Service { +func InitSC(name string, service kobject.ServiceConfig) *api.Service { sc := &api.Service{ TypeMeta: unversioned.TypeMeta{ Kind: "Service", @@ -111,7 +96,7 @@ func initSC(name string, service kobject.ServiceConfig) *api.Service { } // Init DC object -func initDC(name string, service kobject.ServiceConfig, replicas int) *extensions.Deployment { +func InitDC(name string, service kobject.ServiceConfig, replicas int) *extensions.Deployment { dc := &extensions.Deployment{ TypeMeta: unversioned.TypeMeta{ Kind: "Deployment", @@ -146,7 +131,7 @@ func initDC(name string, service kobject.ServiceConfig, replicas int) *extension } // Init DS object -func initDS(name string, service kobject.ServiceConfig) *extensions.DaemonSet { +func InitDS(name string, service kobject.ServiceConfig) *extensions.DaemonSet { ds := &extensions.DaemonSet{ TypeMeta: unversioned.TypeMeta{ Kind: "DaemonSet", @@ -175,7 +160,7 @@ func initDS(name string, service kobject.ServiceConfig) *extensions.DaemonSet { } // Configure the environment variables. -func configEnvs(name string, service kobject.ServiceConfig) []api.EnvVar { +func ConfigEnvs(name string, service kobject.ServiceConfig) []api.EnvVar { envs := []api.EnvVar{} for _, v := range service.Environment { envs = append(envs, api.EnvVar{ @@ -187,8 +172,18 @@ func configEnvs(name string, service kobject.ServiceConfig) []api.EnvVar { return envs } +// Configure the container commands +func ConfigCommands(service kobject.ServiceConfig) []string { + var cmds []string + for _, cmd := range service.Command { + cmds = append(cmds, cmd) + } + + return cmds +} + // Configure the container volumes. -func configVolumes(service kobject.ServiceConfig) ([]api.VolumeMount, []api.Volume) { +func ConfigVolumes(service kobject.ServiceConfig) ([]api.VolumeMount, []api.Volume) { volumesMount := []api.VolumeMount{} volumes := []api.Volume{} volumeSource := api.VolumeSource{} @@ -201,7 +196,7 @@ func configVolumes(service kobject.ServiceConfig) ([]api.VolumeMount, []api.Volu // if volume name isn't specified, set it to a random string of 20 chars if len(name) == 0 { - name = RandStringBytes(20) + name = randStringBytes(20) } // check if ro/rw mode is defined, default rw readonly := len(mode) > 0 && mode == "ro" @@ -256,7 +251,7 @@ func isPath(substring string) bool { } // Configure the container ports. -func configPorts(name string, service kobject.ServiceConfig) []api.ContainerPort { +func ConfigPorts(name string, service kobject.ServiceConfig) []api.ContainerPort { ports := []api.ContainerPort{} for _, port := range service.Port { var p api.Protocol @@ -278,7 +273,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 ConfigServicePorts(name string, service kobject.ServiceConfig) []api.ServicePort { servicePorts := []api.ServicePort{} for _, port := range service.Port { if port.HostPort == 0 { @@ -306,8 +301,23 @@ func configServicePorts(name string, service kobject.ServiceConfig) []api.Servic return servicePorts } +// Configure label +func ConfigLabels(name string) map[string]string { + return map[string]string{"service": name} +} + +// Configure annotations +func ConfigAnnotations(service kobject.ServiceConfig) map[string]string { + annotations := map[string]string{} + for key, value := range service.Annotations { + annotations[key] = value + } + + return annotations +} + // Transform data to json/yaml -func transformer(obj runtime.Object, GenerateYaml bool) ([]byte, error) { +func TransformData(obj runtime.Object, GenerateYaml bool) ([]byte, error) { // Convert to versioned object objectVersion := obj.GetObjectKind().GroupVersionKind() version := unversioned.GroupVersion{Group: objectVersion.Group, Version: objectVersion.Version} @@ -328,145 +338,37 @@ func transformer(obj runtime.Object, GenerateYaml bool) ([]byte, error) { return data, nil } -func Transform(komposeObject *kobject.KomposeObject, opt kobject.ConvertOptions) (map[string][]byte, map[string][]byte, map[string][]byte, map[string][]byte, map[string][]byte, []string) { - mServices := make(map[string][]byte) - mReplicationControllers := make(map[string][]byte) - mDeployments := make(map[string][]byte) - mDaemonSets := make(map[string][]byte) - // OpenShift DeploymentConfigs - mDeploymentConfigs := make(map[string][]byte) +func print(name, trailing string, data []byte, toStdout, generateYaml bool, f *os.File) { + file := fmt.Sprintf("%s-%s.json", name, trailing) + if generateYaml { + file = fmt.Sprintf("%s-%s.yaml", name, trailing) + } + separator := "" + if generateYaml { + separator = "---" + } + if toStdout { + fmt.Fprintf(os.Stdout, "%s%s\n", string(data), separator) + } else if f != nil { + // Write all content to a single file f + if _, err := f.WriteString(fmt.Sprintf("%s%s\n", string(data), separator)); err != nil { + logrus.Fatalf("Failed to write %s to file: %v", trailing, err) + } + f.Sync() + } else { + // Write content separately to each file + if err := ioutil.WriteFile(file, []byte(data), 0644); err != nil { + logrus.Fatalf("Failed to write %s: %v", trailing, err) + } + fmt.Fprintf(os.Stdout, "file %q created\n", file) + } +} +func PrintControllers(mServices, mDeployments, mDaemonSets, mReplicationControllers, mDeploymentConfigs map[string][]byte, svcnames []string, opt kobject.ConvertOptions) { f := createOutFile(opt.OutFile) defer f.Close() - var svcnames []string - - for name, service := range komposeObject.ServiceConfigs { - svcnames = append(svcnames, name) - - rc := initRC(name, service, opt.Replicas) - sc := initSC(name, service) - dc := initDC(name, service, opt.Replicas) - ds := initDS(name, service) - osDC := initDeploymentConfig(name, service, opt.Replicas) // OpenShift DeploymentConfigs - - // Configure the environment variables. - envs := configEnvs(name, service) - - // Configure the container command. - var cmds []string - for _, cmd := range service.Command { - cmds = append(cmds, cmd) - } - // Configure the container volumes. - volumesMount, volumes := configVolumes(service) - - // Configure the container ports. - ports := configPorts(name, service) - - // Configure the service ports. - servicePorts := configServicePorts(name, service) - sc.Spec.Ports = servicePorts - - // Configure label - labels := map[string]string{"service": name} - sc.ObjectMeta.Labels = labels - // Configure annotations - annotations := map[string]string{} - for key, value := range service.Annotations { - annotations[key] = value - } - sc.ObjectMeta.Annotations = annotations - - // fillTemplate fills the pod template with the value calculated from config - fillTemplate := func(template *api.PodTemplateSpec) { - template.Spec.Containers[0].Env = envs - template.Spec.Containers[0].Command = cmds - template.Spec.Containers[0].WorkingDir = service.WorkingDir - template.Spec.Containers[0].VolumeMounts = volumesMount - template.Spec.Volumes = volumes - // Configure the container privileged mode - if service.Privileged == true { - template.Spec.Containers[0].SecurityContext = &api.SecurityContext{ - Privileged: &service.Privileged, - } - } - template.Spec.Containers[0].Ports = ports - template.ObjectMeta.Labels = labels - // Configure the container restart policy. - switch service.Restart { - case "", "always": - template.Spec.RestartPolicy = api.RestartPolicyAlways - case "no": - template.Spec.RestartPolicy = api.RestartPolicyNever - case "on-failure": - template.Spec.RestartPolicy = api.RestartPolicyOnFailure - default: - logrus.Fatalf("Unknown restart policy %s for service %s", service.Restart, name) - } - } - - // fillObjectMeta fills the metadata with the value calculated from config - fillObjectMeta := func(meta *api.ObjectMeta) { - meta.Labels = labels - meta.Annotations = annotations - } - - // Update each supported controllers - updateController(rc, fillTemplate, fillObjectMeta) - updateController(dc, fillTemplate, fillObjectMeta) - updateController(ds, fillTemplate, fillObjectMeta) - // OpenShift DeploymentConfigs - updateController(osDC, fillTemplate, fillObjectMeta) - - // convert datarc to json / yaml - datarc, err := transformer(rc, opt.GenerateYaml) - if err != nil { - logrus.Fatalf(err.Error()) - } - - // convert datadc to json / yaml - datadc, err := transformer(dc, opt.GenerateYaml) - if err != nil { - logrus.Fatalf(err.Error()) - } - - // convert datads to json / yaml - datads, err := transformer(ds, opt.GenerateYaml) - if err != nil { - logrus.Fatalf(err.Error()) - } - - var datasvc []byte - // If ports not provided in configuration we will not make service - if len(ports) == 0 { - logrus.Warningf("[%s] Service cannot be created because of missing port.", name) - } else if len(ports) != 0 { - // convert datasvc to json / yaml - datasvc, err = transformer(sc, opt.GenerateYaml) - if err != nil { - logrus.Fatalf(err.Error()) - } - } - - // convert OpenShift DeploymentConfig to json / yaml - dataDeploymentConfig, err := transformer(osDC, opt.GenerateYaml) - if err != nil { - logrus.Fatalf(err.Error()) - } - - mServices[name] = datasvc - mReplicationControllers[name] = datarc - mDeployments[name] = datadc - mDaemonSets[name] = datads - mDeploymentConfigs[name] = dataDeploymentConfig - } - - return mServices, mDeployments, mDaemonSets, mReplicationControllers, mDeploymentConfigs, svcnames -} - -func PrintControllers(mServices, mDeployments, mDaemonSets, mReplicationControllers, mDeploymentConfigs map[string][]byte, svcnames []string, opt kobject.ConvertOptions, f *os.File) { for k, v := range mServices { if v != nil { print(k, "svc", v, opt.ToStdout, opt.GenerateYaml, f) @@ -497,65 +399,9 @@ func PrintControllers(mServices, mDeployments, mDaemonSets, mReplicationControll } if opt.CreateChart { - err := generateHelm(opt.InputFile, svcnames, opt.GenerateYaml, opt.CreateD, opt.CreateDS, opt.CreateRC, opt.OutFile) + err := kubernetes.GenerateHelm(opt.InputFile, svcnames, opt.GenerateYaml, opt.CreateD, opt.CreateDS, opt.CreateRC, opt.OutFile) if err != nil { - logrus.Fatalf("Failed to create Chart data: %v", err) - } - } - - if opt.CreateDeploymentConfig { - for k, v := range mDeploymentConfigs { - print(k, "deploymentconfig", v, opt.ToStdout, opt.GenerateYaml, f) + logrus.Fatalf("Failed to create Chart data: %s\n", err) } } } - -// updateController 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)) { - switch t := obj.(type) { - case *api.ReplicationController: - if t.Spec.Template == nil { - t.Spec.Template = &api.PodTemplateSpec{} - } - updateTemplate(t.Spec.Template) - updateMeta(&t.ObjectMeta) - case *extensions.Deployment: - updateTemplate(&t.Spec.Template) - updateMeta(&t.ObjectMeta) - case *extensions.ReplicaSet: - updateTemplate(&t.Spec.Template) - updateMeta(&t.ObjectMeta) - case *extensions.DaemonSet: - updateTemplate(&t.Spec.Template) - updateMeta(&t.ObjectMeta) - case *deployapi.DeploymentConfig: - updateTemplate(t.Spec.Template) - updateMeta(&t.ObjectMeta) - } -} - -func print(name, trailing string, data []byte, toStdout, generateYaml bool, f *os.File) { - file := fmt.Sprintf("%s-%s.json", name, trailing) - if generateYaml { - file = fmt.Sprintf("%s-%s.yaml", name, trailing) - } - separator := "" - if generateYaml { - separator = "---" - } - if toStdout { - fmt.Fprintf(os.Stdout, "%s%s\n", string(data), separator) - } else if f != nil { - // Write all content to a single file f - if _, err := f.WriteString(fmt.Sprintf("%s%s\n", string(data), separator)); err != nil { - logrus.Fatalf("Failed to write %s to file: %v", trailing, err) - } - f.Sync() - } else { - // Write content separately to each file - if err := ioutil.WriteFile(file, []byte(data), 0644); err != nil { - logrus.Fatalf("Failed to write %s: %v", trailing, err) - } - fmt.Fprintf(os.Stdout, "file %q created\n", file) - } -}