move k8s base functions into k8s package

This commit is contained in:
Tuna 2016-08-09 22:55:42 +07:00
parent f10d6afecf
commit baedd92036
8 changed files with 470 additions and 639 deletions

View File

@ -17,31 +17,25 @@ limitations under the License.
package app
import (
"fmt"
"github.com/Sirupsen/logrus"
"github.com/urfave/cli"
// 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/apis/extensions"
client "k8s.io/kubernetes/pkg/client/unversioned"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/runtime"
// install kubernetes api
_ "github.com/openshift/origin/pkg/deploy/api/install"
"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"
)
@ -195,201 +189,6 @@ func Scale(c *cli.Context) {
//}
}
// Convert komposeObject to K8S controllers
func komposeConvert(komposeObject KomposeObject, opt convertOptions) []runtime.Object {
var svcnames []string
// this will hold all the converted data
var allobjects []runtime.Object
for name, service := range komposeObject.ServiceConfigs {
var objects []runtime.Object
svcnames = append(svcnames, name)
sc := initSC(name, service)
if opt.createD {
objects = append(objects, initDC(name, service, opt.replicas))
}
if opt.createDS {
objects = append(objects, initDS(name, service))
}
if opt.createRC {
objects = append(objects, initRC(name, service, opt.replicas))
}
if opt.createDeploymentConfig {
objects = append(objects, 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 labels
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 supported controller
for _, obj := range objects {
updateController(obj, fillTemplate, fillObjectMeta)
}
// 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 {
objects = append(objects, sc)
}
allobjects = append(allobjects, objects...)
}
return allobjects
}
// PrintList will take the data converted and decide on the commandline attributes given
func PrintList(objects []runtime.Object, opt convertOptions) error {
f := createOutFile(opt.outFile)
defer f.Close()
var err error
var files []string
// if asked to print to stdout or to put in single file
// we will create a list
if opt.toStdout || f != nil {
list := &api.List{}
list.Items = objects
// version each object in the list
list.Items, err = ConvertToVersion(list.Items)
if err != nil {
return err
}
// version list itself
listVersion := unversioned.GroupVersion{Group: "", Version: "v1"}
convertedList, err := api.Scheme.ConvertToVersion(list, listVersion)
if err != nil {
return err
}
data, err := marshal(convertedList, opt.generateYaml)
if err != nil {
return fmt.Errorf("Error in marshalling the List: %v", err)
}
files = append(files, print("", "", data, opt.toStdout, opt.generateYaml, f))
} else {
var file string
// create a separate file for each provider
for _, v := range objects {
data, err := marshal(v, opt.generateYaml)
if err != nil {
return err
}
switch t := v.(type) {
case *api.ReplicationController:
file = print(t.Name, strings.ToLower(t.Kind), data, opt.toStdout, opt.generateYaml, f)
case *extensions.Deployment:
file = print(t.Name, strings.ToLower(t.Kind), data, opt.toStdout, opt.generateYaml, f)
case *extensions.DaemonSet:
file = print(t.Name, strings.ToLower(t.Kind), data, opt.toStdout, opt.generateYaml, f)
case *deployapi.DeploymentConfig:
file = print(t.Name, strings.ToLower(t.Kind), data, opt.toStdout, opt.generateYaml, f)
case *api.Service:
file = print(t.Name, strings.ToLower(t.Kind), data, opt.toStdout, opt.generateYaml, f)
}
files = append(files, file)
}
}
if opt.createChart {
generateHelm(opt.inputFile, files)
}
return nil
}
// marshal object runtime.Object and return byte array
func marshal(obj runtime.Object, yamlFormat bool) (data []byte, err error) {
// convert data to yaml or json
if yamlFormat {
data, err = yaml.Marshal(obj)
} else {
data, err = json.MarshalIndent(obj, "", " ")
}
if err != nil {
data = nil
}
return
}
// Convert all objects in objs to versioned objects
func ConvertToVersion(objs []runtime.Object) ([]runtime.Object, error) {
ret := []runtime.Object{}
for _, obj := range objs {
objectVersion := obj.GetObjectKind().GroupVersionKind()
version := unversioned.GroupVersion{Group: objectVersion.Group, Version: objectVersion.Version}
convertedObject, err := api.Scheme.ConvertToVersion(obj, version)
if err != nil {
return nil, err
}
ret = append(ret, convertedObject)
}
return ret, nil
}
func validateFlags(opt kobject.ConvertOptions, singleOutput bool, dabFile, inputFile string) {
if len(opt.OutFile) != 0 && opt.ToStdout {
logrus.Fatalf("Error: --out and --stdout can't be set at the same time")
@ -453,19 +252,6 @@ func Convert(c *cli.Context) {
file = dabFile
}
// loader parses input from file into komposeObject.
var l loader.Loader
switch inputFormat {
case "bundle":
l = new(bundle.Bundle)
case "compose":
l = new(compose.Compose)
default:
logrus.Fatalf("Input file format is not supported")
}
komposeObject = l.LoadFile(file)
opt := kobject.ConvertOptions{
ToStdout: toStdout,
CreateD: createD,
@ -481,7 +267,20 @@ func Convert(c *cli.Context) {
validateFlags(opt, singleOutput, dabFile, inputFile)
// transformer maps komposeObject to provider(K8S, OpenShift) primitives
// loader parses input from file into komposeObject.
var l loader.Loader
switch inputFormat {
case "bundle":
l = new(bundle.Bundle)
case "compose":
l = new(compose.Compose)
default:
logrus.Fatalf("Input file format is not supported")
}
komposeObject = l.LoadFile(file)
// transformer maps komposeObject to provider's primitives
var t transformer.Transformer
if !createDeploymentConfig {
t = new(kubernetes.Kubernetes)
@ -489,11 +288,10 @@ func Convert(c *cli.Context) {
t = new(openshift.OpenShift)
}
mServices, mDeployments, mDaemonSets, mReplicationControllers, mDeploymentConfigs, svcnames := t.Transform(komposeObject, opt)
objects := t.Transform(komposeObject, opt)
// Print output
transformer.PrintControllers(mServices, mDeployments, mDaemonSets, mReplicationControllers, mDeploymentConfigs, svcnames, opt)
kubernetes.PrintList(objects, opt)
}
// Up brings up deployment, svc.
@ -511,52 +309,52 @@ func Up(c *cli.Context) {
inputFile := c.String("file")
dabFile := c.String("bundle")
komposeObject := KomposeObject{}
opt := convertOptions{
replicas: 1,
createD: true,
komposeObject := kobject.KomposeObject{
ServiceConfigs: make(map[string]kobject.ServiceConfig),
}
file := inputFile
if len(dabFile) > 0 {
inputFormat = "bundle"
file = dabFile
}
opt := kobject.ConvertOptions{
Replicas: 1,
CreateD: true,
}
validateFlags(opt, false, dabFile, inputFile)
if len(dabFile) > 0 {
komposeObject = loadBundlesFile(dabFile)
} else {
komposeObject = loadComposeFile(inputFile)
// loader parses input from file into komposeObject.
var l loader.Loader
switch inputFormat {
case "bundle":
l = new(bundle.Bundle)
case "compose":
l = new(compose.Compose)
default:
logrus.Fatalf("Input file format is not supported")
}
komposeObject = l.LoadFile(file)
t := new(kubernetes.Kubernetes)
//Convert komposeObject to K8S controllers
objects := komposeConvert(komposeObject, opt)
objects = sortServicesFirst(objects)
objects := t.Transform(komposeObject, opt)
sortServicesFirst(&objects)
for _, v := range objects {
switch t := v.(type) {
case *extensions.Deployment:
_, err := client.Deployments(api.NamespaceDefault).Create(t)
if err != nil {
logrus.Fatalf("Error: '%v' while creating deployment: %s", err, t.Name)
}
logrus.Infof("Successfully created deployment: %s", t.Name)
case *api.Service:
_, err := client.Services(api.NamespaceDefault).Create(t)
if err != nil {
logrus.Fatalf("Error: '%v' while creating service: %s", err, t.Name)
}
logrus.Infof("Successfully created service: %s", t.Name)
}
}
fmt.Println("\nApplication has been deployed to Kubernetes. You can run 'kubectl get deployment,svc' for details.")
//Submit objects to K8s endpoint
kubernetes.CreateObjects(client, objects)
}
// 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) []runtime.Object {
var svc []runtime.Object
var others []runtime.Object
var ret []runtime.Object
func sortServicesFirst(objs *[]runtime.Object) {
var svc, others, ret []runtime.Object
for _, obj := range objs {
for _, obj := range *objs {
if obj.GetObjectKind().GroupVersionKind().Kind == "Service" {
svc = append(svc, obj)
} else {
@ -565,5 +363,5 @@ func sortServicesFirst(objs []runtime.Object) []runtime.Object {
}
ret = append(ret, svc...)
ret = append(ret, others...)
return ret
*objs = ret
}

View File

@ -19,6 +19,7 @@ package app
import (
"fmt"
"testing"
"github.com/skippbox/kompose/pkg/transformer"
)
func TestParseVolume(t *testing.T) {
@ -99,7 +100,7 @@ func TestParseVolume(t *testing.T) {
}
for _, test := range tests {
name, host, container, mode, err := parseVolume(test.volume)
name, host, container, mode, err := transformer.ParseVolume(test.volume)
if err != nil {
t.Errorf("In test case %q, returned unexpected error %v", test.test, err)
}

View File

@ -178,7 +178,7 @@ const (
func CheckUnsupportedKey(service interface{}) {
s := structs.New(service)
for _, f := range s.Fields() {
if f.IsExported() && !f.IsZero() {
if f.IsExported() && !f.IsZero() && f.Name() != "Networks" {
if count, ok := unsupportedKey[f.Name()]; ok && count == 0 {
fmt.Println("WARNING: Unsupported key " + composeOptions[f.Name()] + " - ignoring")
unsupportedKey[f.Name()]++

View File

@ -18,18 +18,31 @@ package kubernetes
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"text/template"
"github.com/Sirupsen/logrus"
"github.com/ghodss/yaml"
"github.com/skippbox/kompose/pkg/kobject"
"github.com/skippbox/kompose/pkg/transformer"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/runtime"
deployapi "github.com/openshift/origin/pkg/deploy/api"
)
/**
* Generate Helm Chart configuration
*/
func GenerateHelm(filename string, svcnames []string, generateYaml, createD, createDS, createRC bool, outFile string) error {
func generateHelm(filename string, outFiles []string) error {
type ChartDetails struct {
Name string
}
@ -108,3 +121,95 @@ func cpFileToChart(manifestDir, filename string) error {
return ioutil.WriteFile(manifestDir+string(os.PathSeparator)+filename, infile, 0644)
}
// PrintList will take the data converted and decide on the commandline attributes given
func PrintList(objects []runtime.Object, opt kobject.ConvertOptions) error {
f := transformer.CreateOutFile(opt.OutFile)
defer f.Close()
var err error
var files []string
// if asked to print to stdout or to put in single file
// we will create a list
if opt.ToStdout || f != nil {
list := &api.List{}
list.Items = objects
// version each object in the list
list.Items, err = convertToVersion(list.Items)
if err != nil {
return err
}
// version list itself
listVersion := unversioned.GroupVersion{Group: "", Version: "v1"}
convertedList, err := api.Scheme.ConvertToVersion(list, listVersion)
if err != nil {
return err
}
data, err := marshal(convertedList, opt.GenerateYaml)
if err != nil {
return fmt.Errorf("Error in marshalling the List: %v", err)
}
files = append(files, transformer.Print("", "", data, opt.ToStdout, opt.GenerateYaml, f))
} else {
var file string
// create a separate file for each provider
for _, v := range objects {
data, err := marshal(v, opt.GenerateYaml)
if err != nil {
return err
}
switch t := v.(type) {
case *api.ReplicationController:
file = transformer.Print(t.Name, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f)
case *extensions.Deployment:
file = transformer.Print(t.Name, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f)
case *extensions.DaemonSet:
file = transformer.Print(t.Name, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f)
case *deployapi.DeploymentConfig:
file = transformer.Print(t.Name, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f)
case *api.Service:
file = transformer.Print(t.Name, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f)
}
files = append(files, file)
}
}
if opt.CreateChart {
generateHelm(opt.InputFile, files)
}
return nil
}
// marshal object runtime.Object and return byte array
func marshal(obj runtime.Object, yamlFormat bool) (data []byte, err error) {
// convert data to yaml or json
if yamlFormat {
data, err = yaml.Marshal(obj)
} else {
data, err = json.MarshalIndent(obj, "", " ")
}
if err != nil {
data = nil
}
return
}
// Convert all objects in objs to versioned objects
func convertToVersion(objs []runtime.Object) ([]runtime.Object, error) {
ret := []runtime.Object{}
for _, obj := range objs {
objectVersion := obj.GetObjectKind().GroupVersionKind()
version := unversioned.GroupVersion{Group: objectVersion.Group, Version: objectVersion.Version}
convertedObject, err := api.Scheme.ConvertToVersion(obj, version)
if err != nil {
return nil, err
}
ret = append(ret, convertedObject)
}
return ret, nil
}

View File

@ -18,7 +18,7 @@ package kubernetes
import (
"fmt"
"os"
"strconv"
"github.com/Sirupsen/logrus"
deployapi "github.com/openshift/origin/pkg/deploy/api"
@ -26,43 +26,262 @@ import (
"github.com/skippbox/kompose/pkg/transformer"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/intstr"
)
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)
// 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
}
// 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
}
// 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 := transformer.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 = transformer.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
}
// 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
}
func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) []runtime.Object {
var svcnames []string
// this will hold all the converted data
var allobjects []runtime.Object
for name, service := range komposeObject.ServiceConfigs {
var objects []runtime.Object
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)
sc := InitSC(name, service)
if opt.CreateD {
objects = append(objects, InitDC(name, service, opt.Replicas))
}
if opt.CreateDS {
objects = append(objects, InitDS(name, service))
}
if opt.CreateRC {
objects = append(objects, InitRC(name, service, opt.Replicas))
}
// Configure the environment variables.
envs := transformer.ConfigEnvs(name, service)
envs := ConfigEnvs(name, service)
// Configure the container command.
cmds := transformer.ConfigCommands(service)
// Configure the container volumes.
volumesMount, volumes := transformer.ConfigVolumes(service)
volumesMount, volumes := ConfigVolumes(service)
// Configure the container ports.
ports := transformer.ConfigPorts(name, service)
ports := ConfigPorts(name, service)
// Configure the service ports.
servicePorts := transformer.ConfigServicePorts(name, service)
servicePorts := ConfigServicePorts(name, service)
sc.Spec.Ports = servicePorts
// Configure label
@ -107,48 +326,21 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.
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())
// update supported controller
for _, obj := range objects {
UpdateController(obj, fillTemplate, fillObjectMeta)
}
// 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())
}
} else {
objects = append(objects, sc)
}
mServices[name] = datasvc
mReplicationControllers[name] = datarc
mDeployments[name] = datadc
mDaemonSets[name] = datads
allobjects = append(allobjects, objects...)
}
return mServices, mDeployments, mDaemonSets, mReplicationControllers, nil, svcnames
return allobjects
}
// updateController updates the given object with the given pod template update function and ObjectMeta update function
@ -163,9 +355,6 @@ func UpdateController(obj runtime.Object, updateTemplate func(*api.PodTemplateSp
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)
@ -174,3 +363,23 @@ func UpdateController(obj runtime.Object, updateTemplate func(*api.PodTemplateSp
updateMeta(&t.ObjectMeta)
}
}
func CreateObjects(client *client.Client, objects []runtime.Object) {
for _, v := range objects {
switch t := v.(type) {
case *extensions.Deployment:
_, err := client.Deployments(api.NamespaceDefault).Create(t)
if err != nil {
logrus.Fatalf("Error: '%v' while creating deployment: %s", err, t.Name)
}
logrus.Infof("Successfully created deployment: %s", t.Name)
case *api.Service:
_, err := client.Services(api.NamespaceDefault).Create(t)
if err != nil {
logrus.Fatalf("Error: '%v' while creating service: %s", err, t.Name)
}
logrus.Infof("Successfully created service: %s", t.Name)
}
}
fmt.Println("\nApplication has been deployed to Kubernetes. You can run 'kubectl get deployment,svc' for details.")
}

View File

@ -25,6 +25,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/runtime"
)
type OpenShift struct {
@ -63,40 +64,45 @@ func initDeploymentConfig(name string, service kobject.ServiceConfig, replicas i
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)
func (k *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) []runtime.Object {
var svcnames []string
// this will hold all the converted data
var allobjects []runtime.Object
for name, service := range komposeObject.ServiceConfigs {
var objects []runtime.Object
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
sc := kubernetes.InitSC(name, service)
if opt.CreateD {
objects = append(objects, kubernetes.InitDC(name, service, opt.Replicas))
}
if opt.CreateDS {
objects = append(objects, kubernetes.InitDS(name, service))
}
if opt.CreateRC {
objects = append(objects, kubernetes.InitRC(name, service, opt.Replicas))
}
if opt.CreateDeploymentConfig {
objects = append(objects, initDeploymentConfig(name, service, opt.Replicas)) // OpenShift DeploymentConfigs
}
// Configure the environment variables.
envs := transformer.ConfigEnvs(name, service)
envs := kubernetes.ConfigEnvs(name, service)
// Configure the container command.
cmds := transformer.ConfigCommands(service)
// Configure the container volumes.
volumesMount, volumes := transformer.ConfigVolumes(service)
volumesMount, volumes := kubernetes.ConfigVolumes(service)
// Configure the container ports.
ports := transformer.ConfigPorts(name, service)
ports := kubernetes.ConfigPorts(name, service)
// Configure the service ports.
servicePorts := transformer.ConfigServicePorts(name, service)
servicePorts := kubernetes.ConfigServicePorts(name, service)
sc.Spec.Ports = servicePorts
// Configure label
@ -141,55 +147,19 @@ func (k *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.C
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())
// update supported controller
for _, obj := range objects {
kubernetes.UpdateController(obj, fillTemplate, fillObjectMeta)
}
// 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())
}
} else {
objects = append(objects, sc)
}
// 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
allobjects = append(allobjects, objects...)
}
return mServices, mDeployments, mDaemonSets, mReplicationControllers, mDeploymentConfigs, svcnames
return allobjects
}

View File

@ -16,9 +16,11 @@ limitations under the License.
package transformer
import "github.com/skippbox/kompose/pkg/kobject"
import (
"github.com/skippbox/kompose/pkg/kobject"
"k8s.io/kubernetes/pkg/runtime"
)
type Transformer interface {
Transform(kobject.KomposeObject, kobject.ConvertOptions) (map[string][]byte, map[string][]byte, map[string][]byte, map[string][]byte, map[string][]byte, []string)
Transform(kobject.KomposeObject, kobject.ConvertOptions) []runtime.Object
}

View File

@ -6,7 +6,6 @@ import (
"io/ioutil"
"math/rand"
"os"
"strconv"
"strings"
"github.com/Sirupsen/logrus"
@ -15,16 +14,13 @@ import (
"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))]
@ -33,7 +29,7 @@ func randStringBytes(n int) string {
}
// Create the file to write to if --out is specified
func createOutFile(out string) *os.File {
func CreateOutFile(out string) *os.File {
var f *os.File
var err error
if len(out) != 0 {
@ -45,133 +41,6 @@ func createOutFile(out string) *os.File {
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
}
// 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 commands
func ConfigCommands(service kobject.ServiceConfig) []string {
var cmds []string
@ -182,40 +51,8 @@ func ConfigCommands(service kobject.ServiceConfig) []string {
return cmds
}
// 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) {
func ParseVolume(volume string) (name, host, container, mode string, err error) {
separator := ":"
volumeStrings := strings.Split(volume, separator)
if len(volumeStrings) == 0 {
@ -250,57 +87,6 @@ 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
}
// Configure label
func ConfigLabels(name string) map[string]string {
return map[string]string{"service": name}
@ -338,21 +124,21 @@ func TransformData(obj runtime.Object, GenerateYaml bool) ([]byte, error) {
return data, nil
}
// Either print to stdout or to file/s
func Print(name, trailing string, data []byte, toStdout, generateYaml bool, f *os.File) string {
func print(name, trailing string, data []byte, toStdout, generateYaml bool, f *os.File) {
file := fmt.Sprintf("%s-%s.json", name, trailing)
file := ""
if generateYaml {
file = fmt.Sprintf("%s-%s.yaml", name, trailing)
}
separator := ""
if generateYaml {
separator = "---"
} else {
file = fmt.Sprintf("%s-%s.json", name, trailing)
}
if toStdout {
fmt.Fprintf(os.Stdout, "%s%s\n", string(data), separator)
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%s\n", string(data), separator)); err != nil {
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()
@ -361,47 +147,7 @@ func print(name, trailing string, data []byte, toStdout, generateYaml bool, f *o
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()
for k, v := range mServices {
if v != nil {
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 {
for k, v := range mDeployments {
print(k, "deployment", v, opt.ToStdout, opt.GenerateYaml, f)
}
}
if opt.CreateDS {
for k, v := range mDaemonSets {
print(k, "daemonset", v, opt.ToStdout, opt.GenerateYaml, f)
}
}
if opt.CreateRC {
for k, v := range mReplicationControllers {
print(k, "rc", v, opt.ToStdout, opt.GenerateYaml, f)
}
}
if f != nil {
fmt.Fprintf(os.Stdout, "file %q created\n", opt.OutFile)
}
if opt.CreateChart {
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: %s\n", err)
}
logrus.Printf("file %q created", file)
}
return file
}