creat kobject package, make loader and transformer refering to it

This commit is contained in:
Tuna 2016-08-01 15:21:38 +07:00
parent 85b0b6a2b8
commit e867d35e39
7 changed files with 541 additions and 522 deletions

View File

@ -17,104 +17,37 @@ limitations under the License.
package app
import (
"fmt"
"math/rand"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/Sirupsen/logrus"
"github.com/urfave/cli"
"encoding/json"
"io/ioutil"
// install kubernetes api
_ "k8s.io/kubernetes/pkg/api/install"
_ "k8s.io/kubernetes/pkg/apis/extensions/install"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions"
client "k8s.io/kubernetes/pkg/client/unversioned"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/intstr"
deployapi "github.com/openshift/origin/pkg/deploy/api"
// install kubernetes api
_ "github.com/openshift/origin/pkg/deploy/api/install"
"github.com/ghodss/yaml"
"github.com/skippbox/kompose/pkg/kobject"
"github.com/skippbox/kompose/pkg/loader"
"github.com/skippbox/kompose/pkg/transformer"
"github.com/docker/libcompose/lookup"
"github.com/docker/libcompose/config"
"github.com/docker/libcompose/project"
"fmt"
"strings"
)
const (
letterBytes = "abcdefghijklmnopqrstuvwxyz0123456789"
DefaultComposeFile = "docker-compose.yml"
)
var inputFormat = "compose"
var composeOptions = map[string]string{
"Build": "build",
"CapAdd": "cap_add",
"CapDrop": "cap_drop",
"CPUSet": "cpuset",
"CPUShares": "cpu_shares",
"CPUQuota": "cpu_quota",
"CgroupParent": "cgroup_parent",
"Devices": "devices",
"DependsOn": "depends_on",
"DNS": "dns",
"DNSSearch": "dns_search",
"DomainName": "domainname",
"Entrypoint": "entrypoint",
"EnvFile": "env_file",
"Expose": "expose",
"Extends": "extends",
"ExternalLinks": "external_links",
"ExtraHosts": "extra_hosts",
"Hostname": "hostname",
"Ipc": "ipc",
"Logging": "logging",
"MacAddress": "mac_address",
"MemLimit": "mem_limit",
"MemSwapLimit": "memswap_limit",
"NetworkMode": "network_mode",
"Networks": "networks",
"Pid": "pid",
"SecurityOpt": "security_opt",
"ShmSize": "shm_size",
"StopSignal": "stop_signal",
"VolumeDriver": "volume_driver",
"VolumesFrom": "volumes_from",
"Uts": "uts",
"ReadOnly": "read_only",
"StdinOpen": "stdin_open",
"Tty": "tty",
"User": "user",
"Ulimits": "ulimits",
"Dockerfile": "dockerfile",
"Net": "net",
"Args": "args",
}
// RandStringBytes generates randomly n-character string
func RandStringBytes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}
// BeforeApp is an action that is executed before any cli command.
func BeforeApp(c *cli.Context) error {
if c.GlobalBool("verbose") {
@ -258,351 +191,6 @@ func Scale(c *cli.Context) {
//}
}
// Create the file to write to if --out is specified
func createOutFile(out string) *os.File {
var f *os.File
var err error
if len(out) != 0 {
f, err = os.Create(out)
if err != nil {
logrus.Fatalf("error opening file: %v", err)
}
}
return f
}
// Init RC object
func initRC(name string, service kobject.ServiceConfig, replicas int) *api.ReplicationController {
rc := &api.ReplicationController{
TypeMeta: unversioned.TypeMeta{
Kind: "ReplicationController",
APIVersion: "v1",
},
ObjectMeta: api.ObjectMeta{
Name: name,
//Labels: map[string]string{"service": name},
},
Spec: api.ReplicationControllerSpec{
Selector: map[string]string{"service": name},
Replicas: int32(replicas),
Template: &api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
//Labels: map[string]string{"service": name},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: name,
Image: service.Image,
},
},
},
},
},
}
return rc
}
// Init SC object
func initSC(name string, service kobject.ServiceConfig) *api.Service {
sc := &api.Service{
TypeMeta: unversioned.TypeMeta{
Kind: "Service",
APIVersion: "v1",
},
ObjectMeta: api.ObjectMeta{
Name: name,
//Labels: map[string]string{"service": name},
},
Spec: api.ServiceSpec{
Selector: map[string]string{"service": name},
},
}
return sc
}
// Init DC object
func initDC(name string, service kobject.ServiceConfig, replicas int) *extensions.Deployment {
dc := &extensions.Deployment{
TypeMeta: unversioned.TypeMeta{
Kind: "Deployment",
APIVersion: "extensions/v1beta1",
},
ObjectMeta: api.ObjectMeta{
Name: name,
Labels: map[string]string{"service": name},
},
Spec: extensions.DeploymentSpec{
Replicas: int32(replicas),
Selector: &unversioned.LabelSelector{
MatchLabels: 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
}
// Init DS object
func initDS(name string, service kobject.ServiceConfig) *extensions.DaemonSet {
ds := &extensions.DaemonSet{
TypeMeta: unversioned.TypeMeta{
Kind: "DaemonSet",
APIVersion: "extensions/v1beta1",
},
ObjectMeta: api.ObjectMeta{
Name: name,
},
Spec: extensions.DaemonSetSpec{
Template: api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Name: name,
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: name,
Image: service.Image,
},
},
},
},
},
}
return ds
}
// Init RS object
func initRS(name string, service kobject.ServiceConfig, replicas int) *extensions.ReplicaSet {
rs := &extensions.ReplicaSet{
TypeMeta: unversioned.TypeMeta{
Kind: "ReplicaSet",
APIVersion: "extensions/v1beta1",
},
ObjectMeta: api.ObjectMeta{
Name: name,
},
Spec: extensions.ReplicaSetSpec{
Replicas: int32(replicas),
Selector: &unversioned.LabelSelector{
MatchLabels: map[string]string{"service": name},
},
Template: api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: name,
Image: service.Image,
},
},
},
},
},
}
return rs
}
// 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
}
// Configure the environment variables.
func configEnvs(name string, service kobject.ServiceConfig) []api.EnvVar {
envs := []api.EnvVar{}
for _, v := range service.Environment {
envs = append(envs, api.EnvVar{
Name: v.Name,
Value: v.Value,
})
}
return envs
}
// Configure the container volumes.
func configVolumes(service kobject.ServiceConfig) ([]api.VolumeMount, []api.Volume) {
volumesMount := []api.VolumeMount{}
volumes := []api.Volume{}
volumeSource := api.VolumeSource{}
for _, volume := range service.Volumes {
name, host, container, mode, err := parseVolume(volume)
if err != nil {
logrus.Warningf("Failed to configure container volume: %v", err)
continue
}
// if volume name isn't specified, set it to a random string of 20 chars
if len(name) == 0 {
name = RandStringBytes(20)
}
// check if ro/rw mode is defined, default rw
readonly := len(mode) > 0 && mode == "ro"
volumesMount = append(volumesMount, api.VolumeMount{Name: name, ReadOnly: readonly, MountPath: container})
if len(host) > 0 {
volumeSource = api.VolumeSource{HostPath: &api.HostPathVolumeSource{Path: host}}
} else {
volumeSource = api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}
}
volumes = append(volumes, api.Volume{Name: name, VolumeSource: volumeSource})
}
return volumesMount, volumes
}
// parseVolume parse a given volume, which might be [name:][host:]container[:access_mode]
func parseVolume(volume string) (name, host, container, mode string, err error) {
separator := ":"
volumeStrings := strings.Split(volume, separator)
if len(volumeStrings) == 0 {
return
}
// Set name if existed
if !isPath(volumeStrings[0]) {
name = volumeStrings[0]
volumeStrings = volumeStrings[1:]
}
if len(volumeStrings) == 0 {
err = fmt.Errorf("invalid volume format: %s", volume)
return
}
if volumeStrings[len(volumeStrings)-1] == "rw" || volumeStrings[len(volumeStrings)-1] == "ro" {
mode = volumeStrings[len(volumeStrings)-1]
volumeStrings = volumeStrings[:len(volumeStrings)-1]
}
container = volumeStrings[len(volumeStrings)-1]
volumeStrings = volumeStrings[:len(volumeStrings)-1]
if len(volumeStrings) == 1 {
host = volumeStrings[0]
}
if !isPath(container) || (len(host) > 0 && !isPath(host)) || len(volumeStrings) > 1 {
err = fmt.Errorf("invalid volume format: %s", volume)
return
}
return
}
func isPath(substring string) bool {
return strings.Contains(substring, "/")
}
// Configure the container ports.
func configPorts(name string, service kobject.ServiceConfig) []api.ContainerPort {
ports := []api.ContainerPort{}
for _, port := range service.Port {
var p api.Protocol
switch port.Protocol {
default:
p = api.ProtocolTCP
case kobject.ProtocolTCP:
p = api.ProtocolTCP
case kobject.ProtocolUDP:
p = api.ProtocolUDP
}
ports = append(ports, api.ContainerPort{
ContainerPort: port.ContainerPort,
Protocol: p,
})
}
return ports
}
// Configure the container service ports.
func configServicePorts(name string, service kobject.ServiceConfig) []api.ServicePort {
servicePorts := []api.ServicePort{}
for _, port := range service.Port {
if port.HostPort == 0 {
port.HostPort = port.ContainerPort
}
var p api.Protocol
switch port.Protocol {
default:
p = api.ProtocolTCP
case kobject.ProtocolTCP:
p = api.ProtocolTCP
case kobject.ProtocolUDP:
p = api.ProtocolUDP
}
var targetPort intstr.IntOrString
targetPort.IntVal = port.ContainerPort
targetPort.StrVal = strconv.Itoa(int(port.ContainerPort))
servicePorts = append(servicePorts, api.ServicePort{
Name: strconv.Itoa(int(port.HostPort)),
Protocol: p,
Port: port.HostPort,
TargetPort: targetPort,
})
}
return servicePorts
}
// Transform data to json/yaml
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}
versionedObj, err := api.Scheme.ConvertToVersion(obj, version)
if err != nil {
return nil, err
}
// convert data to json / yaml
data, err := json.MarshalIndent(versionedObj, "", " ")
if generateYaml == true {
data, err = yaml.Marshal(versionedObj)
}
if err != nil {
return nil, err
}
logrus.Debugf("%s\n", data)
return data, nil
}
// Convert komposeObject to K8S controllers
func komposeConvert(komposeObject KomposeObject, opt convertOptions) []runtime.Object {
var svcnames []string
@ -861,6 +449,17 @@ func Convert(c *cli.Context) {
file = dabFile
}
//komposeObject.Loader(file, inputFormat)
switch inputFormat {
case "bundle":
komposeObject = loader.LoadBundle(file)
case "compose":
komposeObject = loader.LoadCompose(file)
default:
logrus.Fatalf("Input file format is not supported")
}
opt := kobject.ConvertOptions{
ToStdout: toStdout,
CreateD: createD,
@ -874,14 +473,8 @@ func Convert(c *cli.Context) {
OutFile: outFile,
}
f := createOutFile(opt.OutFile)
defer f.Close()
validateFlags(opt, singleOutput, dabFile, inputFile)
komposeObject.Loader(file, inputFormat)
//komposeObject.Transformer(opt)
// Convert komposeObject to K8S controllers
mServices, mDeployments, mDaemonSets, mReplicationControllers, mDeploymentConfigs, svcnames := transformer.Transform(komposeObject, opt)
@ -889,34 +482,6 @@ func Convert(c *cli.Context) {
transformer.PrintControllers(mServices, mDeployments, mDaemonSets, mReplicationControllers, mDeploymentConfigs, svcnames, opt, f)
}
// Either print to stdout or to file/s
func print(name, trailing string, data []byte, toStdout, generateYaml bool, f *os.File) string {
file := ""
if generateYaml {
file = fmt.Sprintf("%s-%s.yaml", name, trailing)
} else {
file = fmt.Sprintf("%s-%s.json", name, trailing)
}
if toStdout {
fmt.Fprintf(os.Stdout, "%s\n", string(data))
return ""
} else if f != nil {
// Write all content to a single file f
if _, err := f.WriteString(fmt.Sprintf("%s\n", string(data))); 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)
}
logrus.Printf("file %q created", file)
}
return file
}
// Up brings up deployment, svc.
func Up(c *cli.Context) {
fmt.Println("We are going to create Kubernetes deployment and service for your dockerized application. \n" +
@ -988,27 +553,3 @@ func sortServicesFirst(objs []runtime.Object) []runtime.Object {
ret = append(ret, others...)
return ret
}
// 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)
}
}

View File

@ -17,12 +17,9 @@ limitations under the License.
package kobject
import (
"github.com/Sirupsen/logrus"
"fmt"
"github.com/skippbox/kompose/pkg/loader"
"github.com/fatih/structs"
"github.com/skippbox/kompose/pkg/transformer"
"math/rand"
)
var unsupportedKey = map[string]int{
@ -69,6 +66,50 @@ var unsupportedKey = map[string]int{
"Args": 0,
}
var composeOptions = map[string]string{
"Build": "build",
"CapAdd": "cap_add",
"CapDrop": "cap_drop",
"CPUSet": "cpuset",
"CPUShares": "cpu_shares",
"CPUQuota": "cpu_quota",
"CgroupParent": "cgroup_parent",
"Devices": "devices",
"DependsOn": "depends_on",
"DNS": "dns",
"DNSSearch": "dns_search",
"DomainName": "domainname",
"Entrypoint": "entrypoint",
"EnvFile": "env_file",
"Expose": "expose",
"Extends": "extends",
"ExternalLinks": "external_links",
"ExtraHosts": "extra_hosts",
"Hostname": "hostname",
"Ipc": "ipc",
"Logging": "logging",
"MacAddress": "mac_address",
"MemLimit": "mem_limit",
"MemSwapLimit": "memswap_limit",
"NetworkMode": "network_mode",
"Networks": "networks",
"Pid": "pid",
"SecurityOpt": "security_opt",
"ShmSize": "shm_size",
"StopSignal": "stop_signal",
"VolumeDriver": "volume_driver",
"VolumesFrom": "volumes_from",
"Uts": "uts",
"ReadOnly": "read_only",
"StdinOpen": "stdin_open",
"Tty": "tty",
"User": "user",
"Ulimits": "ulimits",
"Dockerfile": "dockerfile",
"Net": "net",
"Args": "args",
}
// KomposeObject holds the generic struct of Kompose transformation
type KomposeObject struct {
ServiceConfigs map[string]ServiceConfig
@ -99,6 +140,7 @@ type ServiceConfig struct {
Volumes []string
Network []string
Labels map[string]string
Annotations map[string]string
CPUSet string
CPUShares int64
CPUQuota int64
@ -135,24 +177,24 @@ const (
)
// loader takes input and converts to KomposeObject
func (k *KomposeObject) Loader(file string, inp string) {
switch inp {
case "bundle":
//k.loadBundleFile(file)
loader.LoadBundle(k, file)
case "compose":
//k.loadComposeFile(file)
loader.LoadCompose(k, file)
default:
logrus.Fatalf("Input file format is not supported")
}
}
//func (k *KomposeObject) Loader(file string, inp string) {
// switch inp {
// case "bundle":
// //k.loadBundleFile(file)
// loader.LoadBundle(k, file)
// case "compose":
// //k.loadComposeFile(file)
// loader.LoadCompose(k, file)
// default:
// logrus.Fatalf("Input file format is not supported")
//
// }
//}
// transformer takes KomposeObject and converts to K8S / OpenShift primitives
func (k *KomposeObject) Transformer(opt ConvertOptions) {
transformer.Transform(k, opt)
}
//func (k *KomposeObject) Transformer(opt ConvertOptions) {
// transformer.Transform(k, opt)
//}
func CheckUnsupportedKey(service interface{}) {
s := structs.New(service)

View File

@ -19,9 +19,9 @@ package loader
import (
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api/client/bundlefile"
"github.com/skippbox/kompose/pkg/kobject"
"io/ioutil"
"strings"
"github.com/skippbox/kompose/pkg/kobject"
)
// load Image from bundles file
@ -93,7 +93,11 @@ func loadPortsfromBundle(service bundlefile.Service) ([]kobject.Ports, string) {
}
// load Bundlefile into KomposeObject
func LoadBundle(komposeObject *kobject.KomposeObject, file string) {
func LoadBundle(file string) (kobject.KomposeObject) {
komposeObject := kobject.KomposeObject{
ServiceConfigs: make(map[string]kobject.ServiceConfig),
}
buf, err := ioutil.ReadFile(file)
if err != nil {
logrus.Fatalf("Failed to read bundles file: ", err)
@ -137,4 +141,6 @@ func LoadBundle(komposeObject *kobject.KomposeObject, file string) {
komposeObject.ServiceConfigs[name] = serviceConfig
}
return komposeObject
}

View File

@ -23,6 +23,10 @@ import (
"github.com/skippbox/kompose/pkg/kobject"
"strconv"
"strings"
"github.com/docker/libcompose/lookup"
"os"
"github.com/docker/libcompose/config"
"path/filepath"
)
// load Environment Variable from compose file
@ -77,7 +81,10 @@ func loadPortsFromCompose(composePorts []string) ([]kobject.Ports, string) {
}
// load Docker Compose file into KomposeObject
func LoadCompose(komposeObject *kobject.KomposeObject, file string) {
func LoadCompose(file string) (kobject.KomposeObject) {
komposeObject := kobject.KomposeObject{
ServiceConfigs: make(map[string]kobject.ServiceConfig),
}
context := &docker.Context{}
if file == "" {
file = "docker-compose.yml"
@ -91,7 +98,7 @@ func LoadCompose(komposeObject *kobject.KomposeObject, file string) {
if context.EnvironmentLookup == nil {
cwd, err := os.Getwd()
if err != nil {
return KomposeObject{}
return kobject.KomposeObject{}
}
context.EnvironmentLookup = &lookup.ComposableEnvLookup{
Lookups: []config.EnvironmentLookup{
@ -167,4 +174,6 @@ func LoadCompose(komposeObject *kobject.KomposeObject, file string) {
komposeObject.ServiceConfigs[name] = serviceConfig
}
}
return komposeObject
}

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package app
package transformer
import (
"bytes"

View File

@ -1,21 +1,376 @@
/*
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"
"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"
"math/rand"
"os"
"strconv"
"strings"
deployapi "github.com/openshift/origin/pkg/deploy/api"
"io/ioutil"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/runtime"
)
const letterBytes = "abcdefghijklmnopqrstuvwxyz0123456789"
// RandStringBytes generates randomly n-character string
func RandStringBytes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}
// Create the file to write to if --out is specified
func createOutFile(out string) *os.File {
var f *os.File
var err error
if len(out) != 0 {
f, err = os.Create(out)
if err != nil {
logrus.Fatalf("error opening file: %v", err)
}
}
return f
}
// Init RC object
func initRC(name string, service kobject.ServiceConfig, replicas int) *api.ReplicationController {
rc := &api.ReplicationController{
TypeMeta: unversioned.TypeMeta{
Kind: "ReplicationController",
APIVersion: "v1",
},
ObjectMeta: api.ObjectMeta{
Name: name,
//Labels: map[string]string{"service": name},
},
Spec: api.ReplicationControllerSpec{
Selector: map[string]string{"service": name},
Replicas: int32(replicas),
Template: &api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
//Labels: map[string]string{"service": name},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: name,
Image: service.Image,
},
},
},
},
},
}
return rc
}
// Init SC object
func initSC(name string, service kobject.ServiceConfig) *api.Service {
sc := &api.Service{
TypeMeta: unversioned.TypeMeta{
Kind: "Service",
APIVersion: "v1",
},
ObjectMeta: api.ObjectMeta{
Name: name,
//Labels: map[string]string{"service": name},
},
Spec: api.ServiceSpec{
Selector: map[string]string{"service": name},
},
}
return sc
}
// Init DC object
func initDC(name string, service kobject.ServiceConfig, replicas int) *extensions.Deployment {
dc := &extensions.Deployment{
TypeMeta: unversioned.TypeMeta{
Kind: "Deployment",
APIVersion: "extensions/v1beta1",
},
ObjectMeta: api.ObjectMeta{
Name: name,
Labels: map[string]string{"service": name},
},
Spec: extensions.DeploymentSpec{
Replicas: int32(replicas),
Selector: &unversioned.LabelSelector{
MatchLabels: 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
}
// Init DS object
func initDS(name string, service kobject.ServiceConfig) *extensions.DaemonSet {
ds := &extensions.DaemonSet{
TypeMeta: unversioned.TypeMeta{
Kind: "DaemonSet",
APIVersion: "extensions/v1beta1",
},
ObjectMeta: api.ObjectMeta{
Name: name,
},
Spec: extensions.DaemonSetSpec{
Template: api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Name: name,
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: name,
Image: service.Image,
},
},
},
},
},
}
return ds
}
// 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
}
// Configure the environment variables.
func configEnvs(name string, service kobject.ServiceConfig) []api.EnvVar {
envs := []api.EnvVar{}
for _, v := range service.Environment {
envs = append(envs, api.EnvVar{
Name: v.Name,
Value: v.Value,
})
}
return envs
}
// Configure the container volumes.
func configVolumes(service kobject.ServiceConfig) ([]api.VolumeMount, []api.Volume) {
volumesMount := []api.VolumeMount{}
volumes := []api.Volume{}
volumeSource := api.VolumeSource{}
for _, volume := range service.Volumes {
name, host, container, mode, err := parseVolume(volume)
if err != nil {
logrus.Warningf("Failed to configure container volume: %v", err)
continue
}
// if volume name isn't specified, set it to a random string of 20 chars
if len(name) == 0 {
name = RandStringBytes(20)
}
// check if ro/rw mode is defined, default rw
readonly := len(mode) > 0 && mode == "ro"
volumesMount = append(volumesMount, api.VolumeMount{Name: name, ReadOnly: readonly, MountPath: container})
if len(host) > 0 {
volumeSource = api.VolumeSource{HostPath: &api.HostPathVolumeSource{Path: host}}
} else {
volumeSource = api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}
}
volumes = append(volumes, api.Volume{Name: name, VolumeSource: volumeSource})
}
return volumesMount, volumes
}
// parseVolume parse a given volume, which might be [name:][host:]container[:access_mode]
func parseVolume(volume string) (name, host, container, mode string, err error) {
separator := ":"
volumeStrings := strings.Split(volume, separator)
if len(volumeStrings) == 0 {
return
}
// Set name if existed
if !isPath(volumeStrings[0]) {
name = volumeStrings[0]
volumeStrings = volumeStrings[1:]
}
if len(volumeStrings) == 0 {
err = fmt.Errorf("invalid volume format: %s", volume)
return
}
if volumeStrings[len(volumeStrings)-1] == "rw" || volumeStrings[len(volumeStrings)-1] == "ro" {
mode = volumeStrings[len(volumeStrings)-1]
volumeStrings = volumeStrings[:len(volumeStrings)-1]
}
container = volumeStrings[len(volumeStrings)-1]
volumeStrings = volumeStrings[:len(volumeStrings)-1]
if len(volumeStrings) == 1 {
host = volumeStrings[0]
}
if !isPath(container) || (len(host) > 0 && !isPath(host)) || len(volumeStrings) > 1 {
err = fmt.Errorf("invalid volume format: %s", volume)
return
}
return
}
func isPath(substring string) bool {
return strings.Contains(substring, "/")
}
// Configure the container ports.
func configPorts(name string, service kobject.ServiceConfig) []api.ContainerPort {
ports := []api.ContainerPort{}
for _, port := range service.Port {
var p api.Protocol
switch port.Protocol {
default:
p = api.ProtocolTCP
case kobject.ProtocolTCP:
p = api.ProtocolTCP
case kobject.ProtocolUDP:
p = api.ProtocolUDP
}
ports = append(ports, api.ContainerPort{
ContainerPort: port.ContainerPort,
Protocol: p,
})
}
return ports
}
// Configure the container service ports.
func configServicePorts(name string, service kobject.ServiceConfig) []api.ServicePort {
servicePorts := []api.ServicePort{}
for _, port := range service.Port {
if port.HostPort == 0 {
port.HostPort = port.ContainerPort
}
var p api.Protocol
switch port.Protocol {
default:
p = api.ProtocolTCP
case kobject.ProtocolTCP:
p = api.ProtocolTCP
case kobject.ProtocolUDP:
p = api.ProtocolUDP
}
var targetPort intstr.IntOrString
targetPort.IntVal = port.ContainerPort
targetPort.StrVal = strconv.Itoa(int(port.ContainerPort))
servicePorts = append(servicePorts, api.ServicePort{
Name: strconv.Itoa(int(port.HostPort)),
Protocol: p,
Port: port.HostPort,
TargetPort: targetPort,
})
}
return servicePorts
}
// Transform data to json/yaml
func transformer(obj runtime.Object, GenerateYaml bool) ([]byte, error) {
// Convert to versioned object
objectVersion := obj.GetObjectKind().GroupVersionKind()
version := unversioned.GroupVersion{Group: objectVersion.Group, Version: objectVersion.Version}
versionedObj, err := api.Scheme.ConvertToVersion(obj, version)
if err != nil {
return nil, err
}
// convert data to json / yaml
data, err := json.MarshalIndent(versionedObj, "", " ")
if GenerateYaml == true {
data, err = yaml.Marshal(versionedObj)
}
if err != nil {
return nil, err
}
logrus.Debugf("%s\n", data)
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)
mReplicaSets := make(map[string][]byte)
// OpenShift DeploymentConfigs
mDeploymentConfigs := make(map[string][]byte)
f := createOutFile(opt.outFile)
f := createOutFile(opt.OutFile)
defer f.Close()
var svcnames []string
@ -23,11 +378,11 @@ func Transform(komposeObject *kobject.KomposeObject, opt kobject.ConvertOptions)
for name, service := range komposeObject.ServiceConfigs {
svcnames = append(svcnames, name)
rc := initRC(name, service, opt.replicas)
rc := initRC(name, service, opt.Replicas)
sc := initSC(name, service)
dc := initDC(name, service, opt.replicas)
dc := initDC(name, service, opt.Replicas)
ds := initDS(name, service)
osDC := initDeploymentConfig(name, service, opt.replicas) // OpenShift DeploymentConfigs
osDC := initDeploymentConfig(name, service, opt.Replicas) // OpenShift DeploymentConfigs
// Configure the environment variables.
envs := configEnvs(name, service)
@ -99,19 +454,19 @@ func Transform(komposeObject *kobject.KomposeObject, opt kobject.ConvertOptions)
updateController(osDC, fillTemplate, fillObjectMeta)
// convert datarc to json / yaml
datarc, err := transformer(rc, opt.generateYaml)
datarc, err := transformer(rc, opt.GenerateYaml)
if err != nil {
logrus.Fatalf(err.Error())
}
// convert datadc to json / yaml
datadc, err := transformer(dc, opt.generateYaml)
datadc, err := transformer(dc, opt.GenerateYaml)
if err != nil {
logrus.Fatalf(err.Error())
}
// convert datads to json / yaml
datads, err := transformer(ds, opt.generateYaml)
datads, err := transformer(ds, opt.GenerateYaml)
if err != nil {
logrus.Fatalf(err.Error())
}
@ -122,14 +477,14 @@ func Transform(komposeObject *kobject.KomposeObject, opt kobject.ConvertOptions)
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)
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)
dataDeploymentConfig, err := transformer(osDC, opt.GenerateYaml)
if err != nil {
logrus.Fatalf(err.Error())
}
@ -144,46 +499,96 @@ func Transform(komposeObject *kobject.KomposeObject, opt kobject.ConvertOptions)
return mServices, mDeployments, mDaemonSets, mReplicationControllers, mDeploymentConfigs, svcnames
}
func PrintControllers(mServices, mDeployments, mDaemonSets, mReplicationControllers, mDeploymentConfigs map[string][]byte, svcnames []string, opt convertOptions, f *os.File) {
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)
print(k, "svc", v, opt.ToStdout, opt.GenerateYaml, f)
}
}
// If --out or --stdout is set, the validation should already prevent multiple controllers being generated
if opt.createD {
if opt.CreateD {
for k, v := range mDeployments {
print(k, "deployment", v, opt.toStdout, opt.generateYaml, f)
print(k, "deployment", v, opt.ToStdout, opt.GenerateYaml, f)
}
}
if opt.createDS {
if opt.CreateDS {
for k, v := range mDaemonSets {
print(k, "daemonset", v, opt.toStdout, opt.generateYaml, f)
print(k, "daemonset", v, opt.ToStdout, opt.GenerateYaml, f)
}
}
if opt.createRC {
if opt.CreateRC {
for k, v := range mReplicationControllers {
print(k, "rc", v, opt.toStdout, opt.generateYaml, f)
print(k, "rc", v, opt.ToStdout, opt.GenerateYaml, f)
}
}
if f != nil {
fmt.Fprintf(os.Stdout, "file %q created\n", opt.outFile)
fmt.Fprintf(os.Stdout, "file %q created\n", opt.OutFile)
}
if opt.createChart {
err := generateHelm(opt.inputFile, svcnames, opt.generateYaml, opt.createD, opt.createDS, opt.createRC, opt.outFile)
if opt.CreateChart {
err := 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 {
if opt.CreateDeploymentConfig {
for k, v := range mDeploymentConfigs {
print(k, "deploymentconfig", v, opt.toStdout, opt.generateYaml, f)
print(k, "deploymentconfig", v, opt.ToStdout, opt.GenerateYaml, f)
}
}
}
// 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)
}
}

View File

@ -1 +1,17 @@
/*
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