From 7a41edb65e6e4948c78ca37d3f70c9b1eda4a85c Mon Sep 17 00:00:00 2001 From: ngtuna Date: Tue, 19 Jul 2016 23:07:28 +0700 Subject: [PATCH] add komposeObject struct, remove factory, refactor cli/app --- cli/app/app.go | 889 +++++++++++++++++++------------------- cli/app/types.go | 60 ++- cli/command/command.go | 62 +-- cli/docker/app/factory.go | 39 -- cli/main/main.go | 13 +- 5 files changed, 544 insertions(+), 519 deletions(-) delete mode 100644 cli/docker/app/factory.go diff --git a/cli/app/app.go b/cli/app/app.go index 1fdd178c..f965aabb 100644 --- a/cli/app/app.go +++ b/cli/app/app.go @@ -20,7 +20,7 @@ import ( "fmt" "math/rand" "os" - "strconv" + //"strconv" "strings" "github.com/Sirupsen/logrus" @@ -35,10 +35,10 @@ import ( "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" + //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" + //"k8s.io/kubernetes/pkg/util/intstr" "github.com/fatih/structs" "github.com/ghodss/yaml" @@ -101,150 +101,139 @@ func BeforeApp(c *cli.Context) error { return nil } -// WithProject is an helper function to create a cli.Command action with a ProjectFactory. -func WithProject(factory ProjectFactory, action ProjectAction) func(context *cli.Context) { - return func(context *cli.Context) { - p, err := factory.Create(context) - if err != nil { - logrus.Fatalf("Failed to read project: %v", err) - } - action(p, context) - } -} - // ProjectKuberPS lists all rc, svc. -func ProjectKuberPS(p *project.Project, c *cli.Context) { - factory := cmdutil.NewFactory(nil) - clientConfig, err := factory.ClientConfig() - if err != nil { - logrus.Fatalf("Failed to get Kubernetes client config: %v", err) - } - client := client.NewOrDie(clientConfig) - - if c.BoolT("svc") { - fmt.Printf("%-20s%-20s%-20s%-20s\n", "Name", "Cluster IP", "Ports", "Selectors") - for name := range p.Configs { - var ports string - var selectors string - services, err := client.Services(api.NamespaceDefault).Get(name) - - if err != nil { - logrus.Debugf("Cannot find service for: ", name) - } else { - - for i := range services.Spec.Ports { - p := strconv.Itoa(int(services.Spec.Ports[i].Port)) - ports += ports + string(services.Spec.Ports[i].Protocol) + "(" + p + ")," - } - - for k, v := range services.ObjectMeta.Labels { - selectors += selectors + k + "=" + v + "," - } - - ports = strings.TrimSuffix(ports, ",") - selectors = strings.TrimSuffix(selectors, ",") - - fmt.Printf("%-20s%-20s%-20s%-20s\n", services.ObjectMeta.Name, - services.Spec.ClusterIP, ports, selectors) - } - - } - } - - if c.BoolT("rc") { - fmt.Printf("%-15s%-15s%-30s%-10s%-20s\n", "Name", "Containers", "Images", - "Replicas", "Selectors") - for name := range p.Configs { - var selectors string - var containers string - var images string - rc, err := client.ReplicationControllers(api.NamespaceDefault).Get(name) - - /* Should grab controller, container, image, selector, replicas */ - - if err != nil { - logrus.Debugf("Cannot find rc for: ", string(name)) - } else { - - for k, v := range rc.Spec.Selector { - selectors += selectors + k + "=" + v + "," - } - - for i := range rc.Spec.Template.Spec.Containers { - c := rc.Spec.Template.Spec.Containers[i] - containers += containers + c.Name + "," - images += images + c.Image + "," - } - selectors = strings.TrimSuffix(selectors, ",") - containers = strings.TrimSuffix(containers, ",") - images = strings.TrimSuffix(images, ",") - - fmt.Printf("%-15s%-15s%-30s%-10d%-20s\n", rc.ObjectMeta.Name, containers, - images, rc.Spec.Replicas, selectors) - } - } - } +func Ps(c *cli.Context) { + //factory := cmdutil.NewFactory(nil) + //clientConfig, err := factory.ClientConfig() + //if err != nil { + // logrus.Fatalf("Failed to get Kubernetes client config: %v", err) + //} + //client := client.NewOrDie(clientConfig) + // + //if c.BoolT("svc") { + // fmt.Printf("%-20s%-20s%-20s%-20s\n", "Name", "Cluster IP", "Ports", "Selectors") + // for name := range p.Configs { + // var ports string + // var selectors string + // services, err := client.Services(api.NamespaceDefault).Get(name) + // + // if err != nil { + // logrus.Debugf("Cannot find service for: ", name) + // } else { + // + // for i := range services.Spec.Ports { + // p := strconv.Itoa(int(services.Spec.Ports[i].Port)) + // ports += ports + string(services.Spec.Ports[i].Protocol) + "(" + p + ")," + // } + // + // for k, v := range services.ObjectMeta.Labels { + // selectors += selectors + k + "=" + v + "," + // } + // + // ports = strings.TrimSuffix(ports, ",") + // selectors = strings.TrimSuffix(selectors, ",") + // + // fmt.Printf("%-20s%-20s%-20s%-20s\n", services.ObjectMeta.Name, + // services.Spec.ClusterIP, ports, selectors) + // } + // + // } + //} + // + //if c.BoolT("rc") { + // fmt.Printf("%-15s%-15s%-30s%-10s%-20s\n", "Name", "Containers", "Images", + // "Replicas", "Selectors") + // for name := range p.Configs { + // var selectors string + // var containers string + // var images string + // rc, err := client.ReplicationControllers(api.NamespaceDefault).Get(name) + // + // /* Should grab controller, container, image, selector, replicas */ + // + // if err != nil { + // logrus.Debugf("Cannot find rc for: ", string(name)) + // } else { + // + // for k, v := range rc.Spec.Selector { + // selectors += selectors + k + "=" + v + "," + // } + // + // for i := range rc.Spec.Template.Spec.Containers { + // c := rc.Spec.Template.Spec.Containers[i] + // containers += containers + c.Name + "," + // images += images + c.Image + "," + // } + // selectors = strings.TrimSuffix(selectors, ",") + // containers = strings.TrimSuffix(containers, ",") + // images = strings.TrimSuffix(images, ",") + // + // fmt.Printf("%-15s%-15s%-30s%-10d%-20s\n", rc.ObjectMeta.Name, containers, + // images, rc.Spec.Replicas, selectors) + // } + // } + //} } // ProjectKuberDelete deletes all rc, svc. -func ProjectKuberDelete(p *project.Project, c *cli.Context) { - factory := cmdutil.NewFactory(nil) - clientConfig, err := factory.ClientConfig() - if err != nil { - logrus.Fatalf("Failed to get Kubernetes client config: %v", err) - } - client := client.NewOrDie(clientConfig) - - for name := range p.Configs { - if len(c.String("name")) > 0 && name != c.String("name") { - continue - } - - if c.BoolT("svc") { - err := client.Services(api.NamespaceDefault).Delete(name) - if err != nil { - logrus.Fatalf("Unable to delete service %s: %s\n", name, err) - } - } else if c.BoolT("rc") { - err := client.ReplicationControllers(api.NamespaceDefault).Delete(name) - if err != nil { - logrus.Fatalf("Unable to delete replication controller %s: %s\n", name, err) - } - } - } +func Delete(c *cli.Context) { + //factory := cmdutil.NewFactory(nil) + //clientConfig, err := factory.ClientConfig() + //if err != nil { + // logrus.Fatalf("Failed to get Kubernetes client config: %v", err) + //} + //client := client.NewOrDie(clientConfig) + // + //for name := range p.Configs { + // if len(c.String("name")) > 0 && name != c.String("name") { + // continue + // } + // + // if c.BoolT("svc") { + // err := client.Services(api.NamespaceDefault).Delete(name) + // if err != nil { + // logrus.Fatalf("Unable to delete service %s: %s\n", name, err) + // } + // } else if c.BoolT("rc") { + // err := client.ReplicationControllers(api.NamespaceDefault).Delete(name) + // if err != nil { + // logrus.Fatalf("Unable to delete replication controller %s: %s\n", name, err) + // } + // } + //} } // ProjectKuberScale scales rc. -func ProjectKuberScale(p *project.Project, c *cli.Context) { - factory := cmdutil.NewFactory(nil) - clientConfig, err := factory.ClientConfig() - if err != nil { - logrus.Fatalf("Failed to get Kubernetes client config: %v", err) - } - client := client.NewOrDie(clientConfig) - - if c.Int("scale") <= 0 { - logrus.Fatalf("Scale must be defined and a positive number") - } - - for name := range p.Configs { - if len(c.String("rc")) == 0 || c.String("rc") == name { - s, err := client.ExtensionsClient.Scales(api.NamespaceDefault).Get("ReplicationController", name) - if err != nil { - logrus.Fatalf("Error retrieving scaling data: %s\n", err) - } - - s.Spec.Replicas = int32(c.Int("scale")) - - s, err = client.ExtensionsClient.Scales(api.NamespaceDefault).Update("ReplicationController", s) - if err != nil { - logrus.Fatalf("Error updating scaling data: %s\n", err) - } - - fmt.Printf("Scaling %s to: %d\n", name, s.Spec.Replicas) - } - } +func Scale(c *cli.Context) { + //factory := cmdutil.NewFactory(nil) + //clientConfig, err := factory.ClientConfig() + //if err != nil { + // logrus.Fatalf("Failed to get Kubernetes client config: %v", err) + //} + //client := client.NewOrDie(clientConfig) + // + //if c.Int("scale") <= 0 { + // logrus.Fatalf("Scale must be defined and a positive number") + //} + // + //for name := range p.Configs { + // if len(c.String("rc")) == 0 || c.String("rc") == name { + // s, err := client.ExtensionsClient.Scales(api.NamespaceDefault).Get("ReplicationController", name) + // if err != nil { + // logrus.Fatalf("Error retrieving scaling data: %s\n", err) + // } + // + // s.Spec.Replicas = int32(c.Int("scale")) + // + // s, err = client.ExtensionsClient.Scales(api.NamespaceDefault).Update("ReplicationController", s) + // if err != nil { + // logrus.Fatalf("Error updating scaling data: %s\n", err) + // } + // + // fmt.Printf("Scaling %s to: %d\n", name, s.Spec.Replicas) + // } + //} } // Create the file to write to if --out is specified @@ -261,7 +250,7 @@ func createOutFile(out string) *os.File { } // Init RC object -func initRC(name string, service *project.ServiceConfig, replicas int) *api.ReplicationController { +func initRC(name string, service ServiceConfig, replicas int) *api.ReplicationController { rc := &api.ReplicationController{ TypeMeta: unversioned.TypeMeta{ Kind: "ReplicationController", @@ -293,7 +282,7 @@ func initRC(name string, service *project.ServiceConfig, replicas int) *api.Repl } // Init SC object -func initSC(name string, service *project.ServiceConfig) *api.Service { +func initSC(name string, service ServiceConfig) *api.Service { sc := &api.Service{ TypeMeta: unversioned.TypeMeta{ Kind: "Service", @@ -311,7 +300,7 @@ func initSC(name string, service *project.ServiceConfig) *api.Service { } // Init DC object -func initDC(name string, service *project.ServiceConfig) *extensions.Deployment { +func initDC(name string, service ServiceConfig) *extensions.Deployment { dc := &extensions.Deployment{ TypeMeta: unversioned.TypeMeta{ Kind: "Deployment", @@ -346,7 +335,7 @@ func initDC(name string, service *project.ServiceConfig) *extensions.Deployment } // Init DS object -func initDS(name string, service *project.ServiceConfig) *extensions.DaemonSet { +func initDS(name string, service ServiceConfig) *extensions.DaemonSet { ds := &extensions.DaemonSet{ TypeMeta: unversioned.TypeMeta{ Kind: "DaemonSet", @@ -375,7 +364,7 @@ func initDS(name string, service *project.ServiceConfig) *extensions.DaemonSet { } // Init RS object -func initRS(name string, service *project.ServiceConfig) *extensions.ReplicaSet { +func initRS(name string, service ServiceConfig) *extensions.ReplicaSet { rs := &extensions.ReplicaSet{ TypeMeta: unversioned.TypeMeta{ Kind: "ReplicaSet", @@ -406,138 +395,126 @@ func initRS(name string, service *project.ServiceConfig) *extensions.ReplicaSet } // Configure the environment variables. -func configEnvs(name string, service *project.ServiceConfig) ([]api.EnvVar, string) { - var envs []api.EnvVar - for _, env := range service.Environment.Slice() { - var character string = "=" - if strings.Contains(env, character) { - value := env[strings.Index(env, character)+1:] - name := env[0:strings.Index(env, character)] - name = strings.TrimSpace(name) - value = strings.TrimSpace(value) - envs = append(envs, api.EnvVar{ - Name: name, - Value: value, - }) - } else { - character = ":" - if strings.Contains(env, character) { - var charQuote string = "'" - value := env[strings.Index(env, character)+1:] - name := env[0:strings.Index(env, character)] - name = strings.TrimSpace(name) - value = strings.TrimSpace(value) - if strings.Contains(value, charQuote) { - value = strings.Trim(value, "'") - } - envs = append(envs, api.EnvVar{ - Name: name, - Value: value, - }) - } else { - return nil, "Invalid container env " + env + " for service " + name - } - } +func configEnvs(name string, service ServiceConfig) ([]api.EnvVar) { + envs := []api.EnvVar{} + for _, v := range service.Environment { + envs = append(envs, api.EnvVar{ + Name: v.Name, + Value: v.Value, + }) } - return envs, "" + return envs } // Configure the container volumes. -func configVolumes(service *project.ServiceConfig) ([]api.VolumeMount, []api.Volume) { - var volumesMount []api.VolumeMount - var volumes []api.Volume - for _, volume := range service.Volumes { - var character string = ":" - if strings.Contains(volume, character) { - hostDir := volume[0:strings.Index(volume, character)] - hostDir = strings.TrimSpace(hostDir) - containerDir := volume[strings.Index(volume, character)+1:] - containerDir = strings.TrimSpace(containerDir) - - // check if ro/rw mode is defined - var readonly bool = true - if strings.Index(volume, character) != strings.LastIndex(volume, character) { - mode := volume[strings.LastIndex(volume, character)+1:] - if strings.Compare(mode, "rw") == 0 { - readonly = false - } - containerDir = containerDir[0:strings.Index(containerDir, character)] - } - - // volumeName = random string of 20 chars - volumeName := RandStringBytes(20) - - volumesMount = append(volumesMount, api.VolumeMount{Name: volumeName, ReadOnly: readonly, MountPath: containerDir}) - p := &api.HostPathVolumeSource{ - Path: hostDir, - } - //p.Path = hostDir - volumeSource := api.VolumeSource{HostPath: p} - volumes = append(volumes, api.Volume{Name: volumeName, VolumeSource: volumeSource}) - } - } +func configVolumes(service ServiceConfig) ([]api.VolumeMount, []api.Volume) { + volumesMount := []api.VolumeMount{} + volumes := []api.Volume{} + //for _, volume := range service.Volumes { + // var character string = ":" + // if strings.Contains(volume, character) { + // hostDir := volume[0:strings.Index(volume, character)] + // hostDir = strings.TrimSpace(hostDir) + // containerDir := volume[strings.Index(volume, character)+1:] + // containerDir = strings.TrimSpace(containerDir) + // + // // check if ro/rw mode is defined + // var readonly bool = true + // if strings.Index(volume, character) != strings.LastIndex(volume, character) { + // mode := volume[strings.LastIndex(volume, character)+1:] + // if strings.Compare(mode, "rw") == 0 { + // readonly = false + // } + // containerDir = containerDir[0:strings.Index(containerDir, character)] + // } + // + // // volumeName = random string of 20 chars + // volumeName := RandStringBytes(20) + // + // volumesMount = append(volumesMount, api.VolumeMount{Name: volumeName, ReadOnly: readonly, MountPath: containerDir}) + // p := &api.HostPathVolumeSource{ + // Path: hostDir, + // } + // //p.Path = hostDir + // volumeSource := api.VolumeSource{HostPath: p} + // volumes = append(volumes, api.Volume{Name: volumeName, VolumeSource: volumeSource}) + // } + //} return volumesMount, volumes } // Configure the container ports. -func configPorts(name string, service *project.ServiceConfig) ([]api.ContainerPort, string) { - var ports []api.ContainerPort - for _, port := range service.Ports { - var character string = ":" - if strings.Contains(port, character) { - //portNumber := port[0:strings.Index(port, character)] - targetPortNumber := port[strings.Index(port, character)+1:] - targetPortNumber = strings.TrimSpace(targetPortNumber) - targetPortNumberInt, err := strconv.Atoi(targetPortNumber) - if err != nil { - return nil, "Invalid container port " + port + " for service " + name - } - ports = append(ports, api.ContainerPort{ContainerPort: int32(targetPortNumberInt)}) - } else { - portNumber, err := strconv.Atoi(port) - if err != nil { - return nil, "Invalid container port " + port + " for service " + name - } - ports = append(ports, api.ContainerPort{ContainerPort: int32(portNumber)}) - } +func configPorts(name string, service ServiceConfig) ([]api.ContainerPort, string) { + ports := []api.ContainerPort{} + //for _, port := range service.Port { + // var character string = ":" + // if strings.Contains(port, character) { + // //portNumber := port[0:strings.Index(port, character)] + // targetPortNumber := port[strings.Index(port, character)+1:] + // targetPortNumber = strings.TrimSpace(targetPortNumber) + // targetPortNumberInt, err := strconv.Atoi(targetPortNumber) + // if err != nil { + // return nil, "Invalid container port " + port + " for service " + name + // } + // ports = append(ports, api.ContainerPort{ContainerPort: int32(targetPortNumberInt)}) + // } else { + // portNumber, err := strconv.Atoi(port) + // if err != nil { + // return nil, "Invalid container port " + port + " for service " + name + // } + // ports = append(ports, api.ContainerPort{ContainerPort: int32(portNumber)}) + // } + //} + //return ports, "" + for _, port := range service.Port { + ports = append(ports, api.ContainerPort{ + HostPort: port.HostPort, + ContainerPort: port.ContainerPort, + //Protocol: port.Protocol, + }) } + return ports, "" } // Configure the container service ports. -func configServicePorts(name string, service *project.ServiceConfig) ([]api.ServicePort, string) { - var servicePorts []api.ServicePort - for _, port := range service.Ports { - var character string = ":" - if strings.Contains(port, character) { - portNumber := port[0:strings.Index(port, character)] - portNumber = strings.TrimSpace(portNumber) - targetPortNumber := port[strings.Index(port, character)+1:] - targetPortNumber = strings.TrimSpace(targetPortNumber) - portNumberInt, err := strconv.Atoi(portNumber) - if err != nil { - return nil, "Invalid container port " + port + " for service " + name - } - targetPortNumberInt, err1 := strconv.Atoi(targetPortNumber) - if err1 != nil { - return nil, "Invalid container port " + port + " for service " + name - } - var targetPort intstr.IntOrString - targetPort.StrVal = targetPortNumber - targetPort.IntVal = int32(targetPortNumberInt) - servicePorts = append(servicePorts, api.ServicePort{Port: int32(portNumberInt), Name: portNumber, Protocol: "TCP", TargetPort: targetPort}) - } else { - portNumber, err := strconv.Atoi(port) - if err != nil { - return nil, "Invalid container port " + port + " for service " + name - } - var targetPort intstr.IntOrString - targetPort.StrVal = strconv.Itoa(portNumber) - targetPort.IntVal = int32(portNumber) - servicePorts = append(servicePorts, api.ServicePort{Port: int32(portNumber), Name: strconv.Itoa(portNumber), Protocol: "TCP", TargetPort: targetPort}) - } - } +func configServicePorts(name string, service ServiceConfig) ([]api.ServicePort, string) { + servicePorts := []api.ServicePort{} + //for _, port := range service.Ports { + // var character string = ":" + // if strings.Contains(port, character) { + // portNumber := port[0:strings.Index(port, character)] + // portNumber = strings.TrimSpace(portNumber) + // targetPortNumber := port[strings.Index(port, character)+1:] + // targetPortNumber = strings.TrimSpace(targetPortNumber) + // portNumberInt, err := strconv.Atoi(portNumber) + // if err != nil { + // return nil, "Invalid container port " + port + " for service " + name + // } + // targetPortNumberInt, err1 := strconv.Atoi(targetPortNumber) + // if err1 != nil { + // return nil, "Invalid container port " + port + " for service " + name + // } + // var targetPort intstr.IntOrString + // targetPort.StrVal = targetPortNumber + // targetPort.IntVal = int32(targetPortNumberInt) + // servicePorts = append(servicePorts, api.ServicePort{Port: int32(portNumberInt), Name: portNumber, Protocol: "TCP", TargetPort: targetPort}) + // } else { + // portNumber, err := strconv.Atoi(port) + // if err != nil { + // return nil, "Invalid container port " + port + " for service " + name + // } + // var targetPort intstr.IntOrString + // targetPort.StrVal = strconv.Itoa(portNumber) + // targetPort.IntVal = int32(portNumber) + // servicePorts = append(servicePorts, api.ServicePort{Port: int32(portNumber), Name: strconv.Itoa(portNumber), Protocol: "TCP", TargetPort: targetPort}) + // } + //} + + //for _, port := range service.Port { + // + //} return servicePorts, "" } @@ -555,96 +532,95 @@ func transformer(v interface{}, entity string, generateYaml bool) ([]byte, strin return data, "" } -// ProjectKuberConvert tranforms docker compose or dab file to k8s objects -func ProjectKuberConvert(p *project.Project, c *cli.Context) { - composeFile := c.String("file") - outFile := c.String("out") - generateYaml := c.BoolT("yaml") - toStdout := c.BoolT("stdout") - createD := c.BoolT("deployment") - createDS := c.BoolT("daemonset") - createRS := c.BoolT("replicaset") - createChart := c.BoolT("chart") - fromBundles := c.BoolT("from-bundles") - replicas := c.Int("replicationcontroller") - singleOutput := len(outFile) != 0 || toStdout +// load Environment Variable from bundles file +func loadEnvVars(service bundlefile.Service) ([]EnvVar, string) { + envs := []EnvVar{} + for _, env := range service.Env { + var character string = "=" + if strings.Contains(env, character) { + value := env[strings.Index(env, character)+1:] + name := env[0:strings.Index(env, character)] + name = strings.TrimSpace(name) + value = strings.TrimSpace(value) + envs = append(envs, EnvVar{ + Name: name, + Value: value, + }) + } else { + character = ":" + if strings.Contains(env, character) { + var charQuote string = "'" + value := env[strings.Index(env, character)+1:] + name := env[0:strings.Index(env, character)] + name = strings.TrimSpace(name) + value = strings.TrimSpace(value) + if strings.Contains(value, charQuote) { + value = strings.Trim(value, "'") + } + envs = append(envs, EnvVar{ + Name: name, + Value: value, + }) + } else { + return envs, "Invalid container env " + env + } + } + } + return envs, "" +} - // Create Deployment by default if no controller has be set - if !createD && !createDS && !createRS && replicas == 0 { - createD = true +// Load DAB file into KomposeObject +func loadBundlesFile(file string) (KomposeObject) { + kObject := KomposeObject{ + ServiceConfigs: make(map[string]ServiceConfig), + } + buf, err := ioutil.ReadFile(file) + if err != nil { + logrus.Fatalf("Failed to read bundles file: ", err) + } + reader := strings.NewReader(string(buf)) + bundle, err := bundlefile.LoadFile(reader) + if err != nil { + logrus.Fatalf("Failed to parse bundles file: ", err) } - // Validate the flags - if len(outFile) != 0 && toStdout { - logrus.Fatalf("Error: --out and --stdout can't be set at the same time") - } - if createChart && toStdout { - logrus.Fatalf("Error: chart cannot be generated when --stdout is specified") - } - if singleOutput { - count := 0 - if createD { - count++ - } - if createDS { - count++ - } - if createRS { - count++ - } - if replicas != 0 { - count++ - } - if count > 1 { - logrus.Fatalf("Error: only one type of Kubernetes controller can be generated when --out or --stdout is specified") - } - } + for name, service := range bundle.Services { + serviceConfig := ServiceConfig{} + serviceConfig.Image = service.Image + serviceConfig.Command = service.Command + serviceConfig.Args = service.Args - var f *os.File - if !createChart { - f = createOutFile(outFile) - defer f.Close() - } - - if fromBundles { - p = project.NewProject(&project.Context{ - ProjectName: "kube", - }) - reader := strings.NewReader(composeFile) - bundle, err := bundlefile.LoadFile(reader) - if err != nil { - logrus.Fatalf("Error: failed to load bundles file", err) + envs, err := loadEnvVars(service) + if err != "" { + logrus.Fatalf("Failed to load envvar from bundles file: " + err) } - for name, service := range bundle.Services { - var serviceConfig *project.ServiceConfig - serviceConfig.Image = service.Image - // TODO: mapping other fields + + serviceConfig.Environment = envs + serviceConfig.Labels = service.Labels + if service.WorkingDir != nil { serviceConfig.WorkingDir = *service.WorkingDir - p.Configs[name] = serviceConfig - } - } else { - p = project.NewProject(&project.Context{ - ProjectName: "kube", - ComposeFile: composeFile, - }) - - if err := p.Parse(); err != nil { - logrus.Fatalf("Failed to parse the compose project from %s: %v", composeFile, err) } + kObject.ServiceConfigs[name] = serviceConfig } + fmt.Println(kObject) + + return kObject +} + +// Convert komposeObject to K8S controllers +func komposeConvert(komposeObject KomposeObject, toStdout, createD, createRS, createDS, createChart, generateYaml bool, replicas int, inputFile string, outFile string, f *os.File) { var mServices map[string][]byte = make(map[string][]byte) var mReplicationControllers map[string][]byte = make(map[string][]byte) var mDeployments map[string][]byte = make(map[string][]byte) var mDaemonSets map[string][]byte = make(map[string][]byte) var mReplicaSets map[string][]byte = make(map[string][]byte) - var serviceLinks []string var svcnames []string - for name, service := range p.Configs { + for name, service := range komposeObject.ServiceConfigs { svcnames = append(svcnames, name) - checkUnsupportedKey(*service) + checkUnsupportedKey(service) rc := initRC(name, service, replicas) sc := initSC(name, service) @@ -653,14 +629,11 @@ func ProjectKuberConvert(p *project.Project, c *cli.Context) { rs := initRS(name, service) // Configure the environment variables. - envs, err := configEnvs(name, service) - if err != "" { - logrus.Fatalf(err) - } + envs := configEnvs(name, service) // Configure the container command. var cmds []string - for _, cmd := range service.Command.Slice() { + for _, cmd := range service.Command { cmds = append(cmds, cmd) } // Configure the container volumes. @@ -682,7 +655,7 @@ func ProjectKuberConvert(p *project.Project, c *cli.Context) { // Configure label labels := map[string]string{"service": name} - for key, value := range service.Labels.MapParts() { + for key, value := range service.Labels { labels[key] = value } @@ -762,26 +735,6 @@ func ProjectKuberConvert(p *project.Project, c *cli.Context) { mDeployments[name] = datadc mDaemonSets[name] = datads mReplicaSets[name] = datars - exists := false - - if len(service.Links.Slice()) > 0 { - for i := 0; i < len(service.Links.Slice()); i++ { - var data string = service.Links.Slice()[i] - for _, v := range serviceLinks { - if v == data { - exists = true - break - } - } - if !exists { - serviceLinks = append(serviceLinks, data) - } - } - } - } - - for _, serviceLink := range serviceLinks { - mServices[serviceLink] = nil } for k, v := range mServices { @@ -820,14 +773,76 @@ func ProjectKuberConvert(p *project.Project, c *cli.Context) { } if createChart { - err := generateHelm(composeFile, svcnames, generateYaml, createD, createDS, createRS, replicas) + err := generateHelm(inputFile, svcnames, generateYaml, createD, createDS, createRS, replicas) if err != nil { logrus.Fatalf("Failed to create Chart data: %s\n", err) } } } -func checkUnsupportedKey(service project.ServiceConfig) { +// ProjectKuberConvert tranforms docker compose or dab file to k8s objects +func Convert(c *cli.Context) { + inputFile := c.String("file") + outFile := c.String("out") + generateYaml := c.BoolT("yaml") + toStdout := c.BoolT("stdout") + createD := c.BoolT("deployment") + createDS := c.BoolT("daemonset") + createRS := c.BoolT("replicaset") + createChart := c.BoolT("chart") + fromBundles := c.BoolT("from-bundles") + replicas := c.Int("replicationcontroller") + singleOutput := len(outFile) != 0 || toStdout + + // Create Deployment by default if no controller has be set + if !createD && !createDS && !createRS && replicas == 0 { + createD = true + } + + // Validate the flags + if len(outFile) != 0 && toStdout { + logrus.Fatalf("Error: --out and --stdout can't be set at the same time") + } + if createChart && toStdout { + logrus.Fatalf("Error: chart cannot be generated when --stdout is specified") + } + if singleOutput { + count := 0 + if createD { + count++ + } + if createDS { + count++ + } + if createRS { + count++ + } + if replicas != 0 { + count++ + } + if count > 1 { + logrus.Fatalf("Error: only one type of Kubernetes controller can be generated when --out or --stdout is specified") + } + } + + var f *os.File + if !createChart { + f = createOutFile(outFile) + defer f.Close() + } + + komposeObject := KomposeObject{} + + // Parse DAB file into komposeObject + if fromBundles { + komposeObject = loadBundlesFile(inputFile); + } + + // Convert komposeObject to K8S controllers + komposeConvert(komposeObject, toStdout, createD, createRS, createDS, createChart, generateYaml, replicas, inputFile, outFile, f) +} + +func checkUnsupportedKey(service ServiceConfig) { s := structs.New(service) for _, f := range s.Fields() { if f.IsExported() && !f.IsZero() { @@ -865,80 +880,80 @@ func print(name, trailing string, data []byte, toStdout, generateYaml bool, f *o } // ProjectKuberUp brings up rc, svc. -func ProjectKuberUp(p *project.Project, c *cli.Context) { - factory := cmdutil.NewFactory(nil) - clientConfig, err := factory.ClientConfig() - if err != nil { - logrus.Fatalf("Failed to get Kubernetes client config: %v", err) - } - client := client.NewOrDie(clientConfig) - - files, err := ioutil.ReadDir(".") - if err != nil { - logrus.Fatalf("Failed to load rc, svc manifest files: %s\n", err) - } - - // submit svc first - sc := &api.Service{} - for _, file := range files { - if strings.Contains(file.Name(), "svc") { - datasvc, err := ioutil.ReadFile(file.Name()) - - if err != nil { - logrus.Fatalf("Failed to load %s: %s\n", file.Name(), err) - } - - if strings.Contains(file.Name(), "json") { - err := json.Unmarshal(datasvc, &sc) - if err != nil { - logrus.Fatalf("Failed to unmarshal file %s to svc object: %s\n", file.Name(), err) - } - } - if strings.Contains(file.Name(), "yaml") { - err := yaml.Unmarshal(datasvc, &sc) - if err != nil { - logrus.Fatalf("Failed to unmarshal file %s to svc object: %s\n", file.Name(), err) - } - } - // submit sc to k8s - scCreated, err := client.Services(api.NamespaceDefault).Create(sc) - if err != nil { - fmt.Println(err) - } - logrus.Debugf("%s\n", scCreated) - } - } - - // then submit rc - rc := &api.ReplicationController{} - for _, file := range files { - if strings.Contains(file.Name(), "rc") { - datarc, err := ioutil.ReadFile(file.Name()) - - if err != nil { - logrus.Fatalf("Failed to load %s: %s\n", file.Name(), err) - } - - if strings.Contains(file.Name(), "json") { - err := json.Unmarshal(datarc, &rc) - if err != nil { - logrus.Fatalf("Failed to unmarshal file %s to rc object: %s\n", file.Name(), err) - } - } - if strings.Contains(file.Name(), "yaml") { - err := yaml.Unmarshal(datarc, &rc) - if err != nil { - logrus.Fatalf("Failed to unmarshal file %s to rc object: %s\n", file.Name(), err) - } - } - // submit rc to k8s - rcCreated, err := client.ReplicationControllers(api.NamespaceDefault).Create(rc) - if err != nil { - fmt.Println(err) - } - logrus.Debugf("%s\n", rcCreated) - } - } +func Up(c *cli.Context) { + //factory := cmdutil.NewFactory(nil) + //clientConfig, err := factory.ClientConfig() + //if err != nil { + // logrus.Fatalf("Failed to get Kubernetes client config: %v", err) + //} + //client := client.NewOrDie(clientConfig) + // + //files, err := ioutil.ReadDir(".") + //if err != nil { + // logrus.Fatalf("Failed to load rc, svc manifest files: %s\n", err) + //} + // + //// submit svc first + //sc := &api.Service{} + //for _, file := range files { + // if strings.Contains(file.Name(), "svc") { + // datasvc, err := ioutil.ReadFile(file.Name()) + // + // if err != nil { + // logrus.Fatalf("Failed to load %s: %s\n", file.Name(), err) + // } + // + // if strings.Contains(file.Name(), "json") { + // err := json.Unmarshal(datasvc, &sc) + // if err != nil { + // logrus.Fatalf("Failed to unmarshal file %s to svc object: %s\n", file.Name(), err) + // } + // } + // if strings.Contains(file.Name(), "yaml") { + // err := yaml.Unmarshal(datasvc, &sc) + // if err != nil { + // logrus.Fatalf("Failed to unmarshal file %s to svc object: %s\n", file.Name(), err) + // } + // } + // // submit sc to k8s + // scCreated, err := client.Services(api.NamespaceDefault).Create(sc) + // if err != nil { + // fmt.Println(err) + // } + // logrus.Debugf("%s\n", scCreated) + // } + //} + // + //// then submit rc + //rc := &api.ReplicationController{} + //for _, file := range files { + // if strings.Contains(file.Name(), "rc") { + // datarc, err := ioutil.ReadFile(file.Name()) + // + // if err != nil { + // logrus.Fatalf("Failed to load %s: %s\n", file.Name(), err) + // } + // + // if strings.Contains(file.Name(), "json") { + // err := json.Unmarshal(datarc, &rc) + // if err != nil { + // logrus.Fatalf("Failed to unmarshal file %s to rc object: %s\n", file.Name(), err) + // } + // } + // if strings.Contains(file.Name(), "yaml") { + // err := yaml.Unmarshal(datarc, &rc) + // if err != nil { + // logrus.Fatalf("Failed to unmarshal file %s to rc object: %s\n", file.Name(), err) + // } + // } + // // submit rc to k8s + // rcCreated, err := client.ReplicationControllers(api.NamespaceDefault).Create(rc) + // if err != nil { + // fmt.Println(err) + // } + // logrus.Debugf("%s\n", rcCreated) + // } + //} } diff --git a/cli/app/types.go b/cli/app/types.go index 9dc62bb3..fed274a2 100644 --- a/cli/app/types.go +++ b/cli/app/types.go @@ -16,13 +16,57 @@ limitations under the License. package app -import ( - "github.com/urfave/cli" - "github.com/docker/libcompose/project" -) +// +//// ProjectFactory is an interface that helps creating libcompose project. +//type ProjectFactory interface { +// // Create creates a libcompose project from the command line options (codegangsta cli context). +// Create(c *cli.Context) (*project.Project, error) +//} -// ProjectFactory is an interface that helps creating libcompose project. -type ProjectFactory interface { - // Create creates a libcompose project from the command line options (codegangsta cli context). - Create(c *cli.Context) (*project.Project, error) +type KomposeObject struct { + ServiceConfigs map[string]ServiceConfig } + +type ServiceConfig struct { + ContainerName string + Image string + Environment []EnvVar + Port []Ports + Command []string + WorkingDir string + Args []string + //Volume []Volumes + Network []string + Labels map[string]string + CPUSet string + CPUShares int64 + CPUQuota int64 + CapAdd []string + CapDrop []string + Entrypoint []string + Expose []string + Privileged bool + Restart string + User string +} + +type EnvVar struct { + Name string + Value string +} + +type Ports struct { + HostPort int32 + ContainerPort int32 + Protocol Protocol +} + +// Protocol defines network protocols supported for things like container ports. +type Protocol string + +const ( +// ProtocolTCP is the TCP protocol. + ProtocolTCP Protocol = "TCP" +// ProtocolUDP is the UDP protocol. + ProtocolUDP Protocol = "UDP" +) \ No newline at end of file diff --git a/cli/command/command.go b/cli/command/command.go index 7b9b20eb..0be693cd 100644 --- a/cli/command/command.go +++ b/cli/command/command.go @@ -17,17 +17,18 @@ limitations under the License. package command import ( - "github.com/docker/libcompose/project" "github.com/skippbox/kompose/cli/app" "github.com/urfave/cli" ) // ConvertCommand defines the kompose convert subcommand. -func ConvertCommand(factory app.ProjectFactory) cli.Command { +func ConvertCommand() cli.Command { return cli.Command{ Name: "convert", Usage: "Convert docker-compose.yml to Kubernetes objects", - Action: app.WithProject(factory, app.ProjectKuberConvert), + Action: func (c *cli.Context) { + app.Convert(c) + }, Flags: []cli.Flag{ cli.StringFlag{ Name: "file,f", @@ -79,20 +80,24 @@ func ConvertCommand(factory app.ProjectFactory) cli.Command { } // UpCommand defines the kompose up subcommand. -func UpCommand(factory app.ProjectFactory) cli.Command { +func UpCommand() cli.Command { return cli.Command{ Name: "up", Usage: "Submit rc, svc objects to kubernetes API endpoint", - Action: app.WithProject(factory, app.ProjectKuberUp), + Action: func (c *cli.Context) { + app.Up(c) + }, } } // PsCommand defines the kompose ps subcommand. -func PsCommand(factory app.ProjectFactory) cli.Command { +func PsCommand() cli.Command { return cli.Command{ Name: "ps", Usage: "Get active data in the kubernetes cluster", - Action: app.WithProject(factory, app.ProjectKuberPS), + Action: func (c *cli.Context) { + app.Ps(c) + }, Flags: []cli.Flag{ cli.BoolFlag{ Name: "service,svc", @@ -107,11 +112,13 @@ func PsCommand(factory app.ProjectFactory) cli.Command { } // DeleteCommand defines the kompose delete subcommand. -func DeleteCommand(factory app.ProjectFactory) cli.Command { +func DeleteCommand() cli.Command { return cli.Command{ Name: "delete", Usage: "Remove instantiated services/rc from kubernetes", - Action: app.WithProject(factory, app.ProjectKuberDelete), + Action: func (c *cli.Context) { + app.Delete(c) + }, Flags: []cli.Flag{ cli.BoolFlag{ Name: "replicationcontroller,rc", @@ -130,11 +137,13 @@ func DeleteCommand(factory app.ProjectFactory) cli.Command { } // ScaleCommand defines the kompose up subcommand. -func ScaleCommand(factory app.ProjectFactory) cli.Command { +func ScaleCommand() cli.Command { return cli.Command{ Name: "scale", Usage: "Globally scale instantiated replication controllers", - Action: app.WithProject(factory, app.ProjectKuberScale), + Action: func (c *cli.Context) { + app.Scale(c) + }, Flags: []cli.Flag{ cli.IntFlag{ Name: "scale", @@ -162,19 +171,18 @@ func CommonFlags() []cli.Flag { }, } } - -// Populate updates the specified project context based on command line arguments and subcommands. -func Populate(context *project.Context, c *cli.Context) { - context.ComposeFile = c.GlobalString("file") - //context.ProjectName = c.GlobalString("project-name") - - if c.Command.Name == "logs" { - context.Log = true - } else if c.Command.Name == "up" { - context.Log = !c.Bool("d") - context.NoRecreate = c.Bool("no-recreate") - context.ForceRecreate = c.Bool("force-recreate") - } else if c.Command.Name == "scale" { - context.Timeout = uint(c.Int("timeout")) - } -} +// +//// Populate updates the specified project context based on command line arguments and subcommands. +//func Populate(context *project.Context, c *cli.Context) { +// context.ComposeFiles = append(context.ComposeFiles, c.GlobalString("file")) +// +// //if c.Command.Name == "logs" { +// // context.Log = true +// //} else if c.Command.Name == "up" { +// // context.Log = !c.Bool("d") +// // context.NoRecreate = c.Bool("no-recreate") +// // context.ForceRecreate = c.Bool("force-recreate") +// //} else if c.Command.Name == "scale" { +// // context.Timeout = uint(c.Int("timeout")) +// //} +//} diff --git a/cli/docker/app/factory.go b/cli/docker/app/factory.go deleted file mode 100644 index 90090a8d..00000000 --- a/cli/docker/app/factory.go +++ /dev/null @@ -1,39 +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 app - -import ( - "github.com/urfave/cli" - "github.com/skippbox/kompose/cli/command" - "github.com/docker/libcompose/cli/logger" - "github.com/docker/libcompose/docker" - "github.com/docker/libcompose/project" -) - -// ProjectFactory is a struct that hold the app.ProjectFactory implementation. -type ProjectFactory struct { -} - -// Create implements ProjectFactory.Create using docker client. -func (p *ProjectFactory) Create(c *cli.Context) (*project.Project, error) { - context := &docker.Context{} - context.LoggerFactory = logger.NewColorLoggerFactory() - //Populate(context, c) - command.Populate(&context.Context, c) - - return docker.NewProject(context) -} diff --git a/cli/main/main.go b/cli/main/main.go index d0f309f6..f09a1056 100644 --- a/cli/main/main.go +++ b/cli/main/main.go @@ -20,15 +20,12 @@ import ( "os" "github.com/urfave/cli" - dockerApp "github.com/skippbox/kompose/cli/docker/app" "github.com/skippbox/kompose/version" "github.com/skippbox/kompose/cli/command" cliApp "github.com/skippbox/kompose/cli/app" ) func main() { - factory := &dockerApp.ProjectFactory{} - app := cli.NewApp() app.Name = "kompose" app.Usage = "Command line interface for Skippbox." @@ -39,11 +36,11 @@ func main() { app.Before = cliApp.BeforeApp app.Flags = append(command.CommonFlags()) app.Commands = []cli.Command{ - command.ConvertCommand(factory), - command.UpCommand(factory), - command.PsCommand(factory), - command.DeleteCommand(factory), - command.ScaleCommand(factory), + command.ConvertCommand(), + command.UpCommand(), + command.PsCommand(), + command.DeleteCommand(), + command.ScaleCommand(), } app.Run(os.Args)