diff --git a/cmd/convert.go b/cmd/convert.go index 1da94657..bbe9b5b9 100644 --- a/cmd/convert.go +++ b/cmd/convert.go @@ -19,6 +19,7 @@ package cmd import ( "strings" + log "github.com/Sirupsen/logrus" "github.com/kubernetes-incubator/kompose/pkg/app" "github.com/kubernetes-incubator/kompose/pkg/kobject" "github.com/spf13/cobra" @@ -30,6 +31,7 @@ var ( ConvertOut string ConvertBuildRepo string ConvertBuildBranch string + ConvertBuild string ConvertChart bool ConvertDeployment bool ConvertDaemonSet bool @@ -52,6 +54,12 @@ var convertCmd = &cobra.Command{ Short: "Convert a Docker Compose file", PreRun: func(cmd *cobra.Command, args []string) { + // Check that build-config wasn't passed in with --provider=kubernetes + provider := strings.ToLower(GlobalProvider) + if provider == "kubernetes" && UpBuild == "build-config" { + log.Fatalf("build-config is not a valid --build parameter with provider Kubernetes") + } + // Create the Convert Options. ConvertOpt = kobject.ConvertOptions{ ToStdout: ConvertStdout, @@ -65,6 +73,7 @@ var convertCmd = &cobra.Command{ CreateD: ConvertDeployment, CreateDS: ConvertDaemonSet, CreateRC: ConvertReplicationController, + Build: ConvertBuild, BuildRepo: ConvertBuildRepo, BuildBranch: ConvertBuildBranch, CreateDeploymentConfig: ConvertDeploymentConfig, @@ -112,6 +121,7 @@ func init() { convertCmd.Flags().MarkHidden("build-branch") // Standard between the two + convertCmd.Flags().StringVar(&ConvertBuild, "build", "none", `Set the type of build ("local"|"build-config" (OpenShift only)|"none")`) convertCmd.Flags().BoolVarP(&ConvertYaml, "yaml", "y", false, "Generate resource files into YAML format") convertCmd.Flags().MarkDeprecated("yaml", "YAML is the default format now.") convertCmd.Flags().MarkShorthandDeprecated("y", "YAML is the default format now.") @@ -135,15 +145,17 @@ Examples: Available Commands:{{range .Commands}}{{if .IsAvailableCommand}} {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableLocalFlags}} -Resource Flags: - --build-branch Specify repository branch to use for buildconfig (default is current branch name) - --build-repo Specify source repository for buildconfig (default is current branch's remote url - -c, --chart Create a Helm chart for converted objects +Kubernetes Flags: --daemon-set Generate a Kubernetes daemonset object -d, --deployment Generate a Kubernetes deployment object + -c, --chart Create a Helm chart for converted objects + --replication-controller Generate a Kubernetes replication controller object + +OpenShift Flags: + --build-branch Specify repository branch to use for buildconfig (default is current branch name) + --build-repo Specify source repository for buildconfig (default is current branch's remote url --deployment-config Generate an OpenShift deployment config object --insecure-repository Specify to use insecure docker repository while generating Openshift image stream object - --replication-controller Generate a Kubernetes replication controller object Flags: {{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasAvailableInheritedFlags}} diff --git a/cmd/up.go b/cmd/up.go index ac4700cf..c7b08860 100644 --- a/cmd/up.go +++ b/cmd/up.go @@ -17,6 +17,7 @@ limitations under the License. package cmd import ( + log "github.com/Sirupsen/logrus" "strings" "github.com/kubernetes-incubator/kompose/pkg/app" @@ -31,6 +32,7 @@ var ( UpInsecureRepo bool UpNamespace string UpOpt kobject.ConvertOptions + UpBuild string ) var upCmd = &cobra.Command{ @@ -39,8 +41,15 @@ var upCmd = &cobra.Command{ Long: `Deploy your Dockerized application to a container orchestrator. (default "kubernetes")`, PreRun: func(cmd *cobra.Command, args []string) { + // Check that build-config wasn't passed in with --provider=kubernetes + provider := strings.ToLower(GlobalProvider) + if provider == "kubernetes" && UpBuild == "build-config" { + log.Fatalf("build-config is not a valid --build parameter with provider Kubernetes") + } + // Create the Convert options. UpOpt = kobject.ConvertOptions{ + Build: UpBuild, Replicas: UpReplicas, InputFiles: GlobalFiles, Provider: strings.ToLower(GlobalProvider), @@ -63,5 +72,6 @@ func init() { upCmd.Flags().IntVar(&UpReplicas, "replicas", 1, "Specify the number of replicas generated") upCmd.Flags().BoolVar(&UpInsecureRepo, "insecure-repository", false, "Use an insecure Docker repository for OpenShift ImageStream") upCmd.Flags().StringVar(&UpNamespace, "namespace", "default", "Specify Namespace to deploy your application") + upCmd.Flags().StringVar(&UpBuild, "build", "local", `Set the type of build ("local"|"build-config" (OpenShift only)|"none")`) RootCmd.AddCommand(upCmd) } diff --git a/examples/buildconfig/docker-compose.yml b/examples/buildconfig/docker-compose.yml index facaa266..c526fa29 100644 --- a/examples/buildconfig/docker-compose.yml +++ b/examples/buildconfig/docker-compose.yml @@ -3,5 +3,4 @@ version: "2" services: foo: build: "./build" - command: "sleep 3600" - + image: docker.io/cdrage/foobar diff --git a/pkg/kobject/kobject.go b/pkg/kobject/kobject.go index 51fdda93..88b8355c 100644 --- a/pkg/kobject/kobject.go +++ b/pkg/kobject/kobject.go @@ -39,6 +39,7 @@ type ConvertOptions struct { CreateDeploymentConfig bool BuildRepo string BuildBranch string + Build string CreateChart bool GenerateYaml bool GenerateJSON bool diff --git a/pkg/transformer/kubernetes/kubernetes.go b/pkg/transformer/kubernetes/kubernetes.go index 3c9393f6..5619f551 100644 --- a/pkg/transformer/kubernetes/kubernetes.go +++ b/pkg/transformer/kubernetes/kubernetes.go @@ -64,15 +64,6 @@ const TIMEOUT = 300 //default size of Persistent Volume Claim const PVCRequestSize = "100Mi" -// list of all unsupported keys for this transformer -// Keys are names of variables in kobject struct. -// this is map to make searching for keys easier -// to make sure that unsupported key is not going to be reported twice -// by keeping record if already saw this key in another service -var unsupportedKey = map[string]bool{ - "Build": false, -} - // CheckUnsupportedKey checks if given komposeObject contains // keys that are not supported by this tranfomer. // list of all unsupported keys are stored in unsupportedKey variable @@ -532,11 +523,6 @@ func (k *Kubernetes) InitPod(name string, service kobject.ServiceConfig) *api.Po // returns object that are already sorted in the way that Services are first func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) ([]runtime.Object, error) { - noSupKeys := k.CheckUnsupportedKey(&komposeObject, unsupportedKey) - for _, keyName := range noSupKeys { - log.Warningf("Kubernetes provider doesn't support %s key - ignoring", keyName) - } - // this will hold all the converted data var allobjects []runtime.Object @@ -545,6 +531,44 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject. service := komposeObject.ServiceConfigs[name] var objects []runtime.Object + // Must build the images before conversion (got to add service.Image in case 'image' key isn't provided + // Check that --build is set to true + // Check to see if there is an InputFile (required!) before we build the container + // Check that there's actually a Build key + // Lastly, we must have an Image name to continue + if opt.Build == "local" && opt.InputFiles != nil && service.Build != "" { + + if service.Image == "" { + return nil, fmt.Errorf("image key required within build parameters in order to build and push service '%s'", name) + } + + log.Infof("Build key detected. Attempting to build and push image '%s'", service.Image) + + // Get the directory where the compose file is + composeFileDir, err := transformer.GetComposeFileDir(opt.InputFiles) + if err != nil { + return nil, err + } + + // Build the container! + err = transformer.BuildDockerImage(service, name, composeFileDir) + if err != nil { + return nil, errors.Wrapf(err, "Unable to build Docker image for service %v", name) + } + + // Push the built container to the repo! + err = transformer.PushDockerImage(service, name) + if err != nil { + return nil, errors.Wrapf(err, "Unable to push Docker image for service %v", name) + } + + } + + // If there's no "image" key, use the name of the container that's built + if service.Image == "" { + service.Image = name + } + // Generate pod only and nothing more if service.Restart == "no" || service.Restart == "on-failure" { // Error out if Controller Object is specified with restart: 'on-failure' diff --git a/pkg/transformer/kubernetes/kubernetes_test.go b/pkg/transformer/kubernetes/kubernetes_test.go index 9489c033..6292bc80 100644 --- a/pkg/transformer/kubernetes/kubernetes_test.go +++ b/pkg/transformer/kubernetes/kubernetes_test.go @@ -19,7 +19,6 @@ package kubernetes import ( "fmt" "reflect" - "strings" "testing" deployapi "github.com/openshift/origin/pkg/deploy/api" @@ -428,39 +427,6 @@ func TestConvertRestartOptions(t *testing.T) { } } -// TestUnsupportedKeys test checkUnsupportedKey function -func TestUnsupportedKeys(t *testing.T) { - - kobjectWithBuild := newKomposeObject() - kobjectWithBuild.LoadedFrom = "compose" - serviceConfig := kobjectWithBuild.ServiceConfigs["app"] - serviceConfig.Build = "./asdf" - serviceConfig.Network = []string{} - kobjectWithBuild.ServiceConfigs = map[string]kobject.ServiceConfig{"app": serviceConfig} - - // define all test cases for checkUnsupportedKey function - testCases := map[string]struct { - bundleFile kobject.KomposeObject - expectedUnsupportedKeys []string - }{ - "Full Bundle": { - kobjectWithBuild, - []string{"build"}, - }, - } - - k := Kubernetes{} - - for name, test := range testCases { - t.Log("Test case:", name) - keys := k.CheckUnsupportedKey(&test.bundleFile, unsupportedKey) - if !reflect.DeepEqual(keys, test.expectedUnsupportedKeys) { - t.Errorf("ERROR: Expecting unsupported keys: ['%s']. Got: ['%s']", strings.Join(test.expectedUnsupportedKeys, "', '"), strings.Join(keys, "', '")) - } - } - -} - func TestRestartOnFailure(t *testing.T) { kobjectWithRestartOnFailure := newKomposeObject() diff --git a/pkg/transformer/openshift/openshift.go b/pkg/transformer/openshift/openshift.go index 77becc02..9b394cba 100644 --- a/pkg/transformer/openshift/openshift.go +++ b/pkg/transformer/openshift/openshift.go @@ -20,7 +20,6 @@ import ( "fmt" "os" "os/exec" - "path/filepath" "strings" "github.com/kubernetes-incubator/kompose/pkg/kobject" @@ -100,6 +99,27 @@ func getImageTag(image string) string { } +// Inputs name of the service + provided image +// Outputs name is image is blank, but still provide a tag. +func taggedImage(name string, serviceImage string) string { + var image string + tag := getImageTag(serviceImage) + + // Use the image name if not blank + if serviceImage != "" { + image = serviceImage + } else { + image = name + } + + // Add a tag if not present + if !strings.Contains(image, ":") { + image = image + ":" + tag + } + + return image +} + // hasGitBinary checks if the 'git' binary is available on the system func hasGitBinary() bool { _, err := exec.LookPath("git") @@ -134,20 +154,6 @@ func getGitCurrentBranch(composeFileDir string) (string, error) { return strings.TrimRight(string(out), "\n"), nil } -// getComposeFileDir returns compose file directory -func getComposeFileDir(inputFiles []string) (string, error) { - // Lets assume all the docker-compose files are in the same directory - inputFile := inputFiles[0] - if strings.Index(inputFile, "/") != 0 { - workDir, err := os.Getwd() - if err != nil { - return "", err - } - inputFile = filepath.Join(workDir, inputFile) - } - return filepath.Dir(inputFile), nil -} - // getAbsBuildContext returns build context relative to project root dir func getAbsBuildContext(context string) (string, error) { cmd := exec.Command("git", "rev-parse", "--show-prefix") @@ -163,6 +169,8 @@ func getAbsBuildContext(context string) (string, error) { // initImageStream initialize ImageStream object func (o *OpenShift) initImageStream(name string, service kobject.ServiceConfig, opt kobject.ConvertOptions) *imageapi.ImageStream { + + // Retrieve tags and image name for mapping tag := getImageTag(service.Image) var importPolicy imageapi.TagImportPolicy @@ -171,7 +179,8 @@ func (o *OpenShift) initImageStream(name string, service kobject.ServiceConfig, } var tags map[string]imageapi.TagReference - if service.Build == "" { + + if service.Build != "" || opt.Build != "build-config" { tags = map[string]imageapi.TagReference{ tag: imageapi.TagReference{ From: &kapi.ObjectReference{ @@ -199,7 +208,6 @@ func (o *OpenShift) initImageStream(name string, service kobject.ServiceConfig, return is } -// initBuildConfig initialize Openshifts BuildConfig Object func initBuildConfig(name string, service kobject.ServiceConfig, repo string, branch string) (*buildapi.BuildConfig, error) { contextDir, err := getAbsBuildContext(service.Build) envList := transformer.EnvSort{} @@ -230,7 +238,6 @@ func initBuildConfig(name string, service kobject.ServiceConfig, repo string, br Spec: buildapi.BuildConfigSpec{ Triggers: []buildapi.BuildTriggerPolicy{ {Type: "ConfigChange"}, - {Type: "ImageChange"}, }, RunPolicy: "Serial", CommonSpec: buildapi.CommonSpec{ @@ -261,9 +268,11 @@ func initBuildConfig(name string, service kobject.ServiceConfig, repo string, br // initDeploymentConfig initialize OpenShifts DeploymentConfig object func (o *OpenShift) initDeploymentConfig(name string, service kobject.ServiceConfig, replicas int) *deployapi.DeploymentConfig { - tag := getImageTag(service.Image) containerName := []string{name} + // Properly add tags to the image name + tag := getImageTag(service.Image) + // Use ContainerName if it was set if service.ContainerName != "" { containerName = []string{service.ContainerName} @@ -351,7 +360,6 @@ func (o *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.C var allobjects []runtime.Object var err error var composeFileDir string - hasBuild := false buildRepo := opt.BuildRepo buildBranch := opt.BuildBranch @@ -360,6 +368,41 @@ func (o *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.C service := komposeObject.ServiceConfigs[name] var objects []runtime.Object + // Must build the images before conversion (got to add service.Image in case 'image' key isn't provided + // Check to see if there is an InputFile (required!) before we build the container + // Check that there's actually a Build key + // Lastly, we must have an Image name to continue + if opt.Build == "local" && opt.InputFiles != nil && service.Build != "" { + + if service.Image == "" { + return nil, fmt.Errorf("image key required within build parameters in order to build and push service '%s'", name) + } + + // Get the directory where the compose file is + composeFileDir, err := transformer.GetComposeFileDir(opt.InputFiles) + if err != nil { + return nil, err + } + + // Build the container! + err = transformer.BuildDockerImage(service, name, composeFileDir) + if err != nil { + log.Fatalf("Unable to build Docker container for service %v: %v", name, err) + } + + // Push the built container to the repo! + err = transformer.PushDockerImage(service, name) + if err != nil { + log.Fatalf("Unable to push Docker image for service %v: %v", name, err) + } + + } + + // If there's no "image" key, use the name of the container that's built + if service.Image == "" { + service.Image = name + } + // Generate pod only and nothing more if service.Restart == "no" || service.Restart == "on-failure" { // Error out if Controller Object is specified with restart: 'on-failure' @@ -378,38 +421,49 @@ func (o *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.C } // buildconfig needs to be added to objects after imagestream because of this Openshift bug: https://github.com/openshift/origin/issues/4518 - if service.Build != "" { - if !hasBuild { - composeFileDir, err = getComposeFileDir(opt.InputFiles) - if err != nil { - log.Warningf("Error in detecting compose file's directory.") - continue - } - if !hasGitBinary() && (buildRepo == "" || buildBranch == "") { - return nil, errors.New("Git is not installed! Please install Git to create buildconfig, else supply source repository and branch to use for build using '--build-repo', '--build-branch' options respectively") - } - if buildBranch == "" { - buildBranch, err = getGitCurrentBranch(composeFileDir) - if err != nil { - return nil, errors.Wrap(err, "Buildconfig cannot be created because current git branch couldn't be detected.") - } - } - if opt.BuildRepo == "" { - if err != nil { - return nil, errors.Wrap(err, "Buildconfig cannot be created because remote for current git branch couldn't be detected.") - } - buildRepo, err = getGitCurrentRemoteURL(composeFileDir) - if err != nil { - return nil, errors.Wrap(err, "Buildconfig cannot be created because git remote origin repo couldn't be detected.") - } - } - hasBuild = true + // Generate BuildConfig if the parameter has been passed + if service.Build != "" && opt.Build == "build-config" { + + // Get the compose file directory + composeFileDir, err = transformer.GetComposeFileDir(opt.InputFiles) + if err != nil { + log.Warningf("Error in detecting compose file's directory.") + continue } + + // Check for Git + if !hasGitBinary() && (buildRepo == "" || buildBranch == "") { + return nil, errors.New("Git is not installed! Please install Git to create buildconfig, else supply source repository and branch to use for build using '--build-repo', '--build-branch' options respectively") + } + + // Check the Git branch + if buildBranch == "" { + buildBranch, err = getGitCurrentBranch(composeFileDir) + if err != nil { + return nil, errors.Wrap(err, "Buildconfig cannot be created because current git branch couldn't be detected.") + } + } + + // Detect the remote branches + if opt.BuildRepo == "" { + if err != nil { + return nil, errors.Wrap(err, "Buildconfig cannot be created because remote for current git branch couldn't be detected.") + } + buildRepo, err = getGitCurrentRemoteURL(composeFileDir) + if err != nil { + return nil, errors.Wrap(err, "Buildconfig cannot be created because git remote origin repo couldn't be detected.") + } + } + + // Initialize and build BuildConfig bc, err := initBuildConfig(name, service, buildRepo, buildBranch) if err != nil { return nil, errors.Wrap(err, "initBuildConfig failed") } objects = append(objects, bc) // Openshift BuildConfigs + + // Log what we're doing + log.Infof("Buildconfig using %s::%s as source.", buildRepo, buildBranch) } // If ports not provided in configuration we will not make service @@ -425,18 +479,18 @@ func (o *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.C objects = append(objects, svc) } } - o.UpdateKubernetesObjects(name, service, &objects) + // Update and then append the objects (we're done generating) + o.UpdateKubernetesObjects(name, service, &objects) allobjects = append(allobjects, objects...) } - if hasBuild { - log.Infof("Buildconfig using %s::%s as source.", buildRepo, buildBranch) - } // If docker-compose has a volumes_from directive it will be handled here o.VolumesFrom(&allobjects, komposeObject) - // sort all object so Services are first + + // sort all object so all services are first o.SortServicesFirst(&allobjects) + return allobjects, nil } diff --git a/pkg/transformer/openshift/openshift_test.go b/pkg/transformer/openshift/openshift_test.go index ded80e65..363e2646 100644 --- a/pkg/transformer/openshift/openshift_test.go +++ b/pkg/transformer/openshift/openshift_test.go @@ -28,6 +28,7 @@ import ( "github.com/kubernetes-incubator/kompose/pkg/kobject" "github.com/kubernetes-incubator/kompose/pkg/testutils" + "github.com/kubernetes-incubator/kompose/pkg/transformer" "github.com/kubernetes-incubator/kompose/pkg/transformer/kubernetes" "github.com/pkg/errors" ) @@ -223,7 +224,7 @@ func TestGetComposeFileDir(t *testing.T) { for name, test := range testCases { t.Log("Test case: ", name) - output, err = getComposeFileDir(test.inputFiles) + output, err = transformer.GetComposeFileDir(test.inputFiles) if err != nil { t.Errorf("Expected success, got error: %#v", err) diff --git a/pkg/transformer/utils.go b/pkg/transformer/utils.go index 7cb4285c..f8dfc070 100644 --- a/pkg/transformer/utils.go +++ b/pkg/transformer/utils.go @@ -20,11 +20,13 @@ import ( "fmt" "io/ioutil" "os" + "path" "strings" log "github.com/Sirupsen/logrus" "github.com/kubernetes-incubator/kompose/pkg/kobject" + "github.com/kubernetes-incubator/kompose/pkg/utils/docker" "path/filepath" "github.com/pkg/errors" @@ -171,3 +173,80 @@ func (env EnvSort) Less(i, j int) bool { func (env EnvSort) Swap(i, j int) { env[i], env[j] = env[j], env[i] } + +// GetComposeFileDir returns compose file directory +func GetComposeFileDir(inputFiles []string) (string, error) { + // Lets assume all the docker-compose files are in the same directory + inputFile := inputFiles[0] + if strings.Index(inputFile, "/") != 0 { + workDir, err := os.Getwd() + if err != nil { + return "", err + } + inputFile = filepath.Join(workDir, inputFile) + } + log.Debugf("Compose file dir: %s", filepath.Dir(inputFile)) + return filepath.Dir(inputFile), nil +} + +func BuildDockerImage(service kobject.ServiceConfig, name string, relativePath string) error { + + // First, let's figure out the relative path of the Dockerfile! + // else, we error out. + if _, err := os.Stat(service.Build); err != nil { + return errors.Wrapf(err, "%s is not a valid path for building image %s. Check if this dir exists.", service.Build, name) + } + + // Get the appropriate image source and name + // use path.Base to get the last element of the relative build path + imagePath := path.Join(relativePath, path.Base(service.Build)) + imageName := name + if service.Image != "" { + imageName = service.Image + } + + // Connect to the Docker client + client, err := docker.DockerClient() + if err != nil { + return err + } + + // Use the build struct function to build the image + // Build the image! + build := docker.Build{*client} + err = build.BuildImage(imagePath, imageName) + + if err != nil { + return err + } + + return nil +} + +func PushDockerImage(service kobject.ServiceConfig, serviceName string) error { + + log.Debugf("Pushing Docker image '%s'", service.Image) + + // Don't do anything if service.Image is blank, but at least WARN about it + // lse, let's push the image + if service.Image == "" { + log.Warnf("No image name has been passed for service %s, skipping pushing to repository", serviceName) + return nil + } else { + + // Connect to the Docker client + client, err := docker.DockerClient() + if err != nil { + return err + } + + push := docker.Push{*client} + err = push.PushImage(service.Image) + + if err != nil { + return err + } + } + + return nil +} diff --git a/pkg/transformer/utils_test.go b/pkg/transformer/utils_test.go index e6ba83ab..fec5c3ef 100644 --- a/pkg/transformer/utils_test.go +++ b/pkg/transformer/utils_test.go @@ -18,6 +18,7 @@ package transformer import ( "fmt" + "strings" "testing" ) @@ -138,3 +139,13 @@ func TestParseVolume(t *testing.T) { } } } + +func TestGetComposeFileDir(t *testing.T) { + output, err := GetComposeFileDir([]string{"foobar/docker-compose.yaml"}) + if err != nil { + t.Errorf("Error with GetComposeFileDir %v", err) + } + if !strings.Contains(output, "foobar") { + t.Errorf("Expected $PWD/foobar, got %v", output) + } +} diff --git a/pkg/utils/archive/tar.go b/pkg/utils/archive/tar.go new file mode 100644 index 00000000..64ff1e1b --- /dev/null +++ b/pkg/utils/archive/tar.go @@ -0,0 +1,90 @@ +/* +Copyright 2016 The Kubernetes Authors 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 archive + +import ( + "archive/tar" + "io" + "os" + "path/filepath" + "strings" +) + +/* +CreateTarball creates a tarball for source and dumps it to target path + +Function modified and added from https://github.com/mholt/archiver/blob/master/tar.go +*/ +func CreateTarball(source, target string) error { + tarfile, err := os.Create(target) + if err != nil { + return err + } + defer tarfile.Close() + + tarball := tar.NewWriter(tarfile) + defer tarball.Close() + + info, err := os.Stat(source) + if err != nil { + return nil + } + + var baseDir string + if info.IsDir() { + baseDir = filepath.Base(source) + } + + return filepath.Walk(source, + func(path string, info os.FileInfo, err error) error { + if baseDir == path { + return nil + } + if err != nil { + return err + } + header, err := tar.FileInfoHeader(info, info.Name()) + if err != nil { + return err + } + + if baseDir != "" { + if strings.HasSuffix(source, "/") { + header.Name = strings.TrimPrefix(path, source) + } else { + header.Name = filepath.Join(baseDir, strings.TrimPrefix(path, source)) + } + //println("Header name", header.Name) + } + + if err := tarball.WriteHeader(header); err != nil { + return err + } + + if info.IsDir() { + return nil + } + + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + _, err = io.Copy(tarball, file) + return err + }) +} diff --git a/pkg/utils/docker/build.go b/pkg/utils/docker/build.go new file mode 100644 index 00000000..6cfe2d7a --- /dev/null +++ b/pkg/utils/docker/build.go @@ -0,0 +1,80 @@ +/* +Copyright 2016 The Kubernetes Authors 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 docker + +import ( + "bytes" + log "github.com/Sirupsen/logrus" + dockerlib "github.com/fsouza/go-dockerclient" + "github.com/kubernetes-incubator/kompose/pkg/utils/archive" + "github.com/pkg/errors" + "io/ioutil" + "os" + "path" + "strings" +) + +type Build struct { + Client dockerlib.Client +} + +/* +Build a Docker image via the Docker API. Takes the source directory +and image name and then builds the appropriate image. Tarball is utilized +in order to make building easier. +*/ +func (c *Build) BuildImage(source string, image string) error { + + log.Infof("Building image '%s' from directory '%s'", image, path.Base(source)) + + // Create a temporary file for tarball image packaging + tmpFile, err := ioutil.TempFile("/tmp", "kompose-image-build-") + if err != nil { + return err + } + log.Debugf("Created temporary file %v for Docker image tarballing", tmpFile.Name()) + + // Create a tarball of the source directory in order to build the resulting image + err = archive.CreateTarball(strings.Join([]string{source, ""}, "/"), tmpFile.Name()) + if err != nil { + return errors.Wrap(err, "Unable to create a tarball") + } + + // Load the file into memory + tarballSource, err := os.Open(tmpFile.Name()) + if err != nil { + return errors.Wrap(err, "Unable to load tarball into memory") + } + + // Let's create all the options for the image building. + outputBuffer := bytes.NewBuffer(nil) + opts := dockerlib.BuildImageOptions{ + Name: image, + InputStream: tarballSource, + OutputStream: outputBuffer, + } + + // Build it! + if err := c.Client.BuildImage(opts); err != nil { + return errors.Wrap(err, "Unable to build image. For more output, use -v or --verbose when converting.") + } + + log.Infof("Image '%s' from directory '%s' built successfully", image, path.Base(source)) + log.Debugf("Image %s build output:\n%s", image, outputBuffer) + + return nil +} diff --git a/pkg/utils/docker/client.go b/pkg/utils/docker/client.go new file mode 100644 index 00000000..58225d88 --- /dev/null +++ b/pkg/utils/docker/client.go @@ -0,0 +1,36 @@ +/* +Copyright 2016 The Kubernetes Authors 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 docker + +import ( + "github.com/fsouza/go-dockerclient" +) + +func DockerClient() (*docker.Client, error) { + + // Default end-point, HTTP + TLS support to be added in the future + // Eventually functionality to specify end-point added to command-line + endpoint := "unix:///var/run/docker.sock" + + // Use the unix socker end-point. No support for TLS (yet) + client, err := docker.NewClient(endpoint) + if err != nil { + return client, err + } + + return client, nil +} diff --git a/pkg/utils/docker/push.go b/pkg/utils/docker/push.go new file mode 100644 index 00000000..d5794a61 --- /dev/null +++ b/pkg/utils/docker/push.go @@ -0,0 +1,84 @@ +/* +Copyright 2016 The Kubernetes Authors 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 docker + +import ( + "bytes" + log "github.com/Sirupsen/logrus" + dockerlib "github.com/fsouza/go-dockerclient" + "github.com/novln/docker-parser" + "github.com/pkg/errors" +) + +type Push struct { + Client dockerlib.Client +} + +/* +Push a Docker image via the Docker API. Takes the image name, +parses the URL details and then push based on environment authentication +credentials. +*/ +func (c *Push) PushImage(fullImageName string) error { + outputBuffer := bytes.NewBuffer(nil) + + // Using https://github.com/novln/docker-parser in order to parse the appropriate + // name and registry. + parsedImage, err := dockerparser.Parse(fullImageName) + if err != nil { + return err + } + image, registry := parsedImage.Name(), parsedImage.Registry() + + log.Infof("Pushing image '%s' to registry '%s'", image, registry) + + // Let's setup the push and authentication options + options := dockerlib.PushImageOptions{ + Name: parsedImage.Name(), + Registry: parsedImage.Registry(), + OutputStream: outputBuffer, + } + + // Retrieve the authentication configuration file + // Files checked as per https://godoc.org/github.com/fsouza/go-dockerclient#NewAuthConfigurationsFromFile + // $DOCKER_CONFIG/config.json, $HOME/.docker/config.json , $HOME/.dockercfg + credentials, err := dockerlib.NewAuthConfigurationsFromDockerCfg() + if err != nil { + return errors.Wrap(err, "Unable to retrieve .docker/config.json authentication details. Check that 'docker login' works successfully on the command line.") + } + + // Push the image to the repository (based on the URL) + // We will iterate through all available authentication configurations until we find one that pushes successfully + // and then return nil. + if len(credentials.Configs) > 1 { + log.Info("Multiple authentication credentials detected. Will try each configuration.") + } + + for k, v := range credentials.Configs { + log.Infof("Attempting authentication credentials '%s", k) + err = c.Client.PushImage(options, v) + if err != nil { + log.Errorf("Unable to push image '%s' to registry '%s'. Error: %s", image, registry, err) + } else { + log.Debugf("Image '%s' push output:\n%s", image, outputBuffer) + log.Infof("Successfully pushed image '%s' to registry '%s'", image, registry) + return nil + } + } + + return errors.New("Unable to push docker image(s). Check that `docker login` works successfully on the command line.") +} diff --git a/script/test/cmd/tests.sh b/script/test/cmd/tests.sh index 2ec00aab..9049f914 100755 --- a/script/test/cmd/tests.sh +++ b/script/test/cmd/tests.sh @@ -27,6 +27,8 @@ fi # Warning Template warning="Buildconfig using $uri::$branch as source." +# Replacing variables with current branch and uri +sed -e "s;%URI%;$uri;g" -e "s;%REF%;$branch;g" $KOMPOSE_ROOT/script/test/fixtures/nginx-node-redis/output-os-template.json > /tmp/output-os.json ####### # Tests related to docker-compose file in /script/test/fixtures/etherpad @@ -54,11 +56,13 @@ unset $(cat $KOMPOSE_ROOT/script/test/fixtures/gitlab/envs | cut -d'=' -f1) ###### # Tests related to docker-compose file in /script/test/fixtures/nginx-node-redis # kubernetes test -convert::expect_success_and_warning "kompose -f $KOMPOSE_ROOT/script/test/fixtures/nginx-node-redis/docker-compose.yml convert --stdout -j" "$KOMPOSE_ROOT/script/test/fixtures/nginx-node-redis/output-k8s.json" "Kubernetes provider doesn't support build key - ignoring" +convert::expect_success "kompose -f $KOMPOSE_ROOT/script/test/fixtures/nginx-node-redis/docker-compose.yml convert --stdout -j" "$KOMPOSE_ROOT/script/test/fixtures/nginx-node-redis/output-k8s.json" # openshift test + # Replacing variables with current branch and uri +# Test BuildConfig sed -e "s;%URI%;$uri;g" -e "s;%REF%;$branch;g" $KOMPOSE_ROOT/script/test/fixtures/nginx-node-redis/output-os-template.json > /tmp/output-os.json -convert::expect_success_and_warning "kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/nginx-node-redis/docker-compose.yml convert --stdout -j" "/tmp/output-os.json" "$warning" +convert::expect_success_and_warning "kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/nginx-node-redis/docker-compose.yml convert --stdout -j --build build-config" "/tmp/output-os.json" "$warning" rm /tmp/output-os.json ###### @@ -214,23 +218,25 @@ convert::check_artifacts_generated "kompose -f $KOMPOSE_ROOT/script/test/fixture #### # Test regarding build context (running kompose from various directories) # Replacing variables with current branch and uri +# Test BuildConfig sed -e "s;%URI%;$uri;g" -e "s;%REF%;$branch;g" $KOMPOSE_ROOT/script/test/fixtures/nginx-node-redis/output-os-template.json > /tmp/output-os.json CURRENT_DIR=$(pwd) cd "$KOMPOSE_ROOT/script/test/fixtures/nginx-node-redis/" -convert::expect_success_and_warning "kompose convert --provider openshift --stdout -j" "/tmp/output-os.json" "$warning" +convert::expect_success_and_warning "kompose convert --provider openshift --stdout -j --build build-config" "/tmp/output-os.json" "$warning" cd "$KOMPOSE_ROOT/script/test/fixtures/" -convert::expect_success_and_warning "kompose convert --provider openshift --stdout -j -f nginx-node-redis/docker-compose.yml" "/tmp/output-os.json" "$warning" +convert::expect_success_and_warning "kompose convert --provider openshift --stdout -j -f nginx-node-redis/docker-compose.yml --build build-config" "/tmp/output-os.json" "$warning" cd "$KOMPOSE_ROOT/script/test/fixtures/nginx-node-redis/node" -convert::expect_success_and_warning "kompose convert --provider openshift --stdout -j -f ../docker-compose.yml" "/tmp/output-os.json" "$warning" +convert::expect_success_and_warning "kompose convert --provider openshift --stdout -j -f ../docker-compose.yml --build build-config" "/tmp/output-os.json" "$warning" cd $CURRENT_DIR rm /tmp/output-os.json # Test the presence of build args in buildconfig # Replacing variables with current branch and uri +# Test BuildConfig sed -e "s;%URI%;$uri;g" -e "s;%REF%;$branch;g" $KOMPOSE_ROOT/script/test/fixtures/buildargs/output-os-template.json > /tmp/output-buildarg-os.json export $(cat $KOMPOSE_ROOT/script/test/fixtures/buildargs/envs) -convert::expect_success_and_warning "kompose --provider openshift -f $KOMPOSE_ROOT/script/test/fixtures/buildargs/docker-compose.yml convert --stdout -j" "/tmp/output-buildarg-os.json" "$warning" +convert::expect_success_and_warning "kompose --provider openshift -f $KOMPOSE_ROOT/script/test/fixtures/buildargs/docker-compose.yml convert --stdout -j --build build-config" "/tmp/output-buildarg-os.json" "$warning" rm /tmp/output-buildarg-os.json # Test related to support docker-compose.yaml beside docker-compose.yml diff --git a/script/test/fixtures/buildargs/output-os-template.json b/script/test/fixtures/buildargs/output-os-template.json index 4150b830..ca6329c6 100644 --- a/script/test/fixtures/buildargs/output-os-template.json +++ b/script/test/fixtures/buildargs/output-os-template.json @@ -3,33 +3,6 @@ "apiVersion": "v1", "metadata": {}, "items": [ - { - "kind": "Service", - "apiVersion": "v1", - "metadata": { - "name": "foo1", - "creationTimestamp": null, - "labels": { - "io.kompose.service": "foo1" - } - }, - "spec": { - "ports": [ - { - "name": "headless", - "port": 55555, - "targetPort": 0 - } - ], - "selector": { - "io.kompose.service": "foo1" - }, - "clusterIP": "None" - }, - "status": { - "loadBalancer": {} - } - }, { "kind": "Service", "apiVersion": "v1", @@ -58,7 +31,7 @@ } }, { - "kind": "DeploymentConfig", + "kind": "Service", "apiVersion": "v1", "metadata": { "name": "foo1", @@ -68,127 +41,20 @@ } }, "spec": { - "strategy": { - "resources": {} - }, - "triggers": [ + "ports": [ { - "type": "ConfigChange" - }, - { - "type": "ImageChange", - "imageChangeParams": { - "automatic": true, - "containerNames": [ - "foo1" - ], - "from": { - "kind": "ImageStreamTag", - "name": "foo1:latest" - } - } + "name": "headless", + "port": 55555, + "targetPort": 0 } ], - "replicas": 1, - "test": false, "selector": { "io.kompose.service": "foo1" }, - "template": { - "metadata": { - "creationTimestamp": null, - "labels": { - "io.kompose.service": "foo1" - } - }, - "spec": { - "containers": [ - { - "name": "foo1", - "image": " ", - "args": [ - "sleep", - "3600" - ], - "resources": {} - } - ], - "restartPolicy": "Always" - } - } - }, - "status": {} - }, - { - "kind": "ImageStream", - "apiVersion": "v1", - "metadata": { - "name": "foo1", - "creationTimestamp": null, - "labels": { - "io.kompose.service": "foo1" - } - }, - "spec": {}, - "status": { - "dockerImageRepository": "" - } - }, - { - "kind": "BuildConfig", - "apiVersion": "v1", - "metadata": { - "name": "foo1", - "creationTimestamp": null, - "labels": { - "io.kompose.service": "foo1" - } - }, - "spec": { - "triggers": [ - { - "type": "ConfigChange" - }, - { - "type": "ImageChange" - } - ], - "runPolicy": "Serial", - "source": { - "type": "Git", - "git": { - "uri": "%URI%", - "ref": "%REF%" - }, - "contextDir": "script/test/fixtures/buildargs/build/" - }, - "strategy": { - "type": "Docker", - "dockerStrategy": { - "env": [ - { - "name": "NAME", - "value": "web" - }, - { - "name": "foo", - "value": "bar" - } - ] - } - }, - "output": { - "to": { - "kind": "ImageStreamTag", - "name": "foo1:latest" - } - }, - "resources": {}, - "postCommit": {}, - "nodeSelector": null + "clusterIP": "None" }, "status": { - "lastVersion": 0 + "loadBalancer": {} } }, { @@ -263,7 +129,20 @@ "io.kompose.service": "foo" } }, - "spec": {}, + "spec": { + "tags": [ + { + "name": "latest", + "annotations": null, + "from": { + "kind": "DockerImage", + "name": "foo" + }, + "generation": null, + "importPolicy": {} + } + ] + }, "status": { "dockerImageRepository": "" } @@ -282,9 +161,6 @@ "triggers": [ { "type": "ConfigChange" - }, - { - "type": "ImageChange" } ], "runPolicy": "Serial", @@ -320,6 +196,150 @@ "status": { "lastVersion": 0 } + }, + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "foo1", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "foo1" + } + }, + "spec": { + "strategy": { + "resources": {} + }, + "triggers": [ + { + "type": "ConfigChange" + }, + { + "type": "ImageChange", + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "foo1" + ], + "from": { + "kind": "ImageStreamTag", + "name": "foo1:latest" + } + } + } + ], + "replicas": 1, + "test": false, + "selector": { + "io.kompose.service": "foo1" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "foo1" + } + }, + "spec": { + "containers": [ + { + "name": "foo1", + "image": " ", + "args": [ + "sleep", + "3600" + ], + "resources": {} + } + ], + "restartPolicy": "Always" + } + } + }, + "status": {} + }, + { + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "foo1", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "foo1" + } + }, + "spec": { + "tags": [ + { + "name": "latest", + "annotations": null, + "from": { + "kind": "DockerImage", + "name": "foo1" + }, + "generation": null, + "importPolicy": {} + } + ] + }, + "status": { + "dockerImageRepository": "" + } + }, + { + "kind": "BuildConfig", + "apiVersion": "v1", + "metadata": { + "name": "foo1", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "foo1" + } + }, + "spec": { + "triggers": [ + { + "type": "ConfigChange" + } + ], + "runPolicy": "Serial", + "source": { + "type": "Git", + "git": { + "uri": "%URI%", + "ref": "%REF%" + }, + "contextDir": "script/test/fixtures/buildargs/build/" + }, + "strategy": { + "type": "Docker", + "dockerStrategy": { + "env": [ + { + "name": "NAME", + "value": "web" + }, + { + "name": "foo", + "value": "bar" + } + ] + } + }, + "output": { + "to": { + "kind": "ImageStreamTag", + "name": "foo1:latest" + } + }, + "resources": {}, + "postCommit": {}, + "nodeSelector": null + }, + "status": { + "lastVersion": 0 + } } ] } diff --git a/script/test/fixtures/buildconfig/build/Dockerfile b/script/test/fixtures/buildconfig/build/Dockerfile new file mode 100644 index 00000000..f094e685 --- /dev/null +++ b/script/test/fixtures/buildconfig/build/Dockerfile @@ -0,0 +1,2 @@ +FROM busybox +RUN touch /test \ No newline at end of file diff --git a/script/test/fixtures/buildconfig/docker-compose-build-no-image.yml b/script/test/fixtures/buildconfig/docker-compose-build-no-image.yml new file mode 100644 index 00000000..f9226929 --- /dev/null +++ b/script/test/fixtures/buildconfig/docker-compose-build-no-image.yml @@ -0,0 +1,5 @@ +version: "2" + +services: + foo: + build: "./build" diff --git a/script/test/fixtures/buildconfig/docker-compose.yml b/script/test/fixtures/buildconfig/docker-compose.yml new file mode 100644 index 00000000..c526fa29 --- /dev/null +++ b/script/test/fixtures/buildconfig/docker-compose.yml @@ -0,0 +1,6 @@ +version: "2" + +services: + foo: + build: "./build" + image: docker.io/cdrage/foobar diff --git a/script/test/fixtures/etherpad/output-os.json b/script/test/fixtures/etherpad/output-os.json index b41d0a46..6b0edf7e 100644 --- a/script/test/fixtures/etherpad/output-os.json +++ b/script/test/fixtures/etherpad/output-os.json @@ -110,14 +110,6 @@ } ], "env": [ - { - "name": "DB_DBID", - "value": "etherpad" - }, - { - "name": "DB_HOST", - "value": "mariadb" - }, { "name": "DB_PASS", "value": "etherpad" @@ -129,6 +121,14 @@ { "name": "DB_USER", "value": "etherpad" + }, + { + "name": "DB_DBID", + "value": "etherpad" + }, + { + "name": "DB_HOST", + "value": "mariadb" } ], "resources": {} diff --git a/script/test/fixtures/gitlab/output-os.json b/script/test/fixtures/gitlab/output-os.json index c1272125..e71053a6 100644 --- a/script/test/fixtures/gitlab/output-os.json +++ b/script/test/fixtures/gitlab/output-os.json @@ -3,6 +3,42 @@ "apiVersion": "v1", "metadata": {}, "items": [ + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "gitlab", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "gitlab" + } + }, + "spec": { + "ports": [ + { + "name": "30000", + "port": 30000, + "targetPort": 80 + }, + { + "name": "30001", + "port": 30001, + "targetPort": 443 + }, + { + "name": "30002", + "port": 30002, + "targetPort": 22 + } + ], + "selector": { + "io.kompose.service": "gitlab" + } + }, + "status": { + "loadBalancer": {} + } + }, { "kind": "Service", "apiVersion": "v1", @@ -56,7 +92,7 @@ } }, { - "kind": "Service", + "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { "name": "gitlab", @@ -66,29 +102,124 @@ } }, "spec": { - "ports": [ + "strategy": { + "resources": {} + }, + "triggers": [ { - "name": "30000", - "port": 30000, - "targetPort": 80 + "type": "ConfigChange" }, { - "name": "30001", - "port": 30001, - "targetPort": 443 - }, - { - "name": "30002", - "port": 30002, - "targetPort": 22 + "type": "ImageChange", + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "gitlab" + ], + "from": { + "kind": "ImageStreamTag", + "name": "gitlab:latest" + } + } } ], + "replicas": 1, + "test": false, "selector": { "io.kompose.service": "gitlab" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "gitlab" + } + }, + "spec": { + "containers": [ + { + "name": "gitlab", + "image": " ", + "ports": [ + { + "containerPort": 80 + }, + { + "containerPort": 443 + }, + { + "containerPort": 22 + } + ], + "env": [ + { + "name": "DB_HOST", + "value": "postgresql" + }, + { + "name": "DB_NAME", + "value": "gitlab" + }, + { + "name": "DB_PASS", + "value": "gitlab" + }, + { + "name": "DB_PORT", + "value": "5432" + }, + { + "name": "DB_TYPE", + "value": "postgres" + }, + { + "name": "DB_USER", + "value": "gitlab" + }, + { + "name": "REDIS_HOST", + "value": "redis" + }, + { + "name": "REDIS_PORT", + "value": "6379" + } + ], + "resources": {} + } + ], + "restartPolicy": "Always" + } + } + }, + "status": {} + }, + { + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "gitlab", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "gitlab" } }, + "spec": { + "tags": [ + { + "name": "latest", + "annotations": null, + "from": { + "kind": "DockerImage", + "name": "swordphilic/gitlab" + }, + "generation": null, + "importPolicy": {} + } + ] + }, "status": { - "loadBalancer": {} + "dockerImageRepository": "" } }, { @@ -286,137 +417,6 @@ "status": { "dockerImageRepository": "" } - }, - { - "kind": "DeploymentConfig", - "apiVersion": "v1", - "metadata": { - "name": "gitlab", - "creationTimestamp": null, - "labels": { - "io.kompose.service": "gitlab" - } - }, - "spec": { - "strategy": { - "resources": {} - }, - "triggers": [ - { - "type": "ConfigChange" - }, - { - "type": "ImageChange", - "imageChangeParams": { - "automatic": true, - "containerNames": [ - "gitlab" - ], - "from": { - "kind": "ImageStreamTag", - "name": "gitlab:latest" - } - } - } - ], - "replicas": 1, - "test": false, - "selector": { - "io.kompose.service": "gitlab" - }, - "template": { - "metadata": { - "creationTimestamp": null, - "labels": { - "io.kompose.service": "gitlab" - } - }, - "spec": { - "containers": [ - { - "name": "gitlab", - "image": " ", - "ports": [ - { - "containerPort": 80 - }, - { - "containerPort": 443 - }, - { - "containerPort": 22 - } - ], - "env": [ - { - "name": "DB_TYPE", - "value": "postgres" - }, - { - "name": "DB_USER", - "value": "gitlab" - }, - { - "name": "REDIS_HOST", - "value": "redis" - }, - { - "name": "REDIS_PORT", - "value": "6379" - }, - { - "name": "DB_HOST", - "value": "postgresql" - }, - { - "name": "DB_NAME", - "value": "gitlab" - }, - { - "name": "DB_PASS", - "value": "gitlab" - }, - { - "name": "DB_PORT", - "value": "5432" - } - ], - "resources": {} - } - ], - "restartPolicy": "Always" - } - } - }, - "status": {} - }, - { - "kind": "ImageStream", - "apiVersion": "v1", - "metadata": { - "name": "gitlab", - "creationTimestamp": null, - "labels": { - "io.kompose.service": "gitlab" - } - }, - "spec": { - "tags": [ - { - "name": "latest", - "annotations": null, - "from": { - "kind": "DockerImage", - "name": "swordphilic/gitlab" - }, - "generation": null, - "importPolicy": {} - } - ] - }, - "status": { - "dockerImageRepository": "" - } } ] } diff --git a/script/test/fixtures/multiple-compose-files/output-openshift.json b/script/test/fixtures/multiple-compose-files/output-openshift.json index 1d97edee..fb7b35a1 100644 --- a/script/test/fixtures/multiple-compose-files/output-openshift.json +++ b/script/test/fixtures/multiple-compose-files/output-openshift.json @@ -123,6 +123,14 @@ } ], "env": [ + { + "name": "DB_HOST", + "value": "openshift" + }, + { + "name": "DB_PASS", + "value": "openshift" + }, { "name": "DB_PORT", "value": "openshift" @@ -134,14 +142,6 @@ { "name": "DB_DBID", "value": "openshift" - }, - { - "name": "DB_HOST", - "value": "openshift" - }, - { - "name": "DB_PASS", - "value": "openshift" } ], "resources": {} @@ -254,14 +254,6 @@ } ], "env": [ - { - "name": "MYSQL_PASSWORD", - "value": "openshift" - }, - { - "name": "MYSQL_ROOT_PASSWORD", - "value": "openshift" - }, { "name": "MYSQL_USER", "value": "openshift" @@ -269,6 +261,14 @@ { "name": "MYSQL_DATABASE", "value": "openshift" + }, + { + "name": "MYSQL_PASSWORD", + "value": "openshift" + }, + { + "name": "MYSQL_ROOT_PASSWORD", + "value": "openshift" } ], "resources": {}, diff --git a/script/test/fixtures/nginx-node-redis/output-k8s.json b/script/test/fixtures/nginx-node-redis/output-k8s.json index 2e7fb750..0cdc0c2a 100644 --- a/script/test/fixtures/nginx-node-redis/output-k8s.json +++ b/script/test/fixtures/nginx-node-redis/output-k8s.json @@ -156,6 +156,7 @@ "containers": [ { "name": "nginx", + "image": "nginx", "ports": [ { "containerPort": 80 @@ -194,6 +195,7 @@ "containers": [ { "name": "node1", + "image": "node1", "ports": [ { "containerPort": 8080 @@ -232,6 +234,7 @@ "containers": [ { "name": "node2", + "image": "node2", "ports": [ { "containerPort": 8080 @@ -270,6 +273,7 @@ "containers": [ { "name": "node3", + "image": "node3", "ports": [ { "containerPort": 8080 diff --git a/script/test/fixtures/nginx-node-redis/output-os-template.json b/script/test/fixtures/nginx-node-redis/output-os-template.json index 384e0a0a..1863b9ab 100644 --- a/script/test/fixtures/nginx-node-redis/output-os-template.json +++ b/script/test/fixtures/nginx-node-redis/output-os-template.json @@ -206,7 +206,20 @@ "io.kompose.service": "nginx" } }, - "spec": {}, + "spec": { + "tags": [ + { + "name": "latest", + "annotations": null, + "from": { + "kind": "DockerImage", + "name": "nginx" + }, + "generation": null, + "importPolicy": {} + } + ] + }, "status": { "dockerImageRepository": "" } @@ -225,9 +238,6 @@ "triggers": [ { "type": "ConfigChange" - }, - { - "type": "ImageChange" } ], "runPolicy": "Serial", @@ -330,7 +340,20 @@ "io.kompose.service": "node1" } }, - "spec": {}, + "spec": { + "tags": [ + { + "name": "latest", + "annotations": null, + "from": { + "kind": "DockerImage", + "name": "node1" + }, + "generation": null, + "importPolicy": {} + } + ] + }, "status": { "dockerImageRepository": "" } @@ -349,9 +372,6 @@ "triggers": [ { "type": "ConfigChange" - }, - { - "type": "ImageChange" } ], "runPolicy": "Serial", @@ -454,7 +474,20 @@ "io.kompose.service": "node2" } }, - "spec": {}, + "spec": { + "tags": [ + { + "name": "latest", + "annotations": null, + "from": { + "kind": "DockerImage", + "name": "node2" + }, + "generation": null, + "importPolicy": {} + } + ] + }, "status": { "dockerImageRepository": "" } @@ -473,9 +506,6 @@ "triggers": [ { "type": "ConfigChange" - }, - { - "type": "ImageChange" } ], "runPolicy": "Serial", @@ -578,7 +608,20 @@ "io.kompose.service": "node3" } }, - "spec": {}, + "spec": { + "tags": [ + { + "name": "latest", + "annotations": null, + "from": { + "kind": "DockerImage", + "name": "node3" + }, + "generation": null, + "importPolicy": {} + } + ] + }, "status": { "dockerImageRepository": "" } @@ -597,9 +640,6 @@ "triggers": [ { "type": "ConfigChange" - }, - { - "type": "ImageChange" } ], "runPolicy": "Serial", @@ -702,23 +742,10 @@ "io.kompose.service": "redis" } }, - "spec": { - "tags": [ - { - "name": "latest", - "annotations": null, - "from": { - "kind": "DockerImage", - "name": "redis" - }, - "generation": null, - "importPolicy": {} - } - ] - }, + "spec": {}, "status": { "dockerImageRepository": "" } } ] -} \ No newline at end of file +} diff --git a/script/test/fixtures/nginx-node-redis/output-os.json b/script/test/fixtures/nginx-node-redis/output-os.json new file mode 100644 index 00000000..b8458cff --- /dev/null +++ b/script/test/fixtures/nginx-node-redis/output-os.json @@ -0,0 +1,592 @@ +{ + "kind": "List", + "apiVersion": "v1", + "metadata": {}, + "items": [ + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "nginx", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "nginx" + } + }, + "spec": { + "ports": [ + { + "name": "80", + "port": 80, + "targetPort": 80 + } + ], + "selector": { + "io.kompose.service": "nginx" + } + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "node1", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "node1" + } + }, + "spec": { + "ports": [ + { + "name": "8080", + "port": 8080, + "targetPort": 8080 + } + ], + "selector": { + "io.kompose.service": "node1" + } + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "node2", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "node2" + } + }, + "spec": { + "ports": [ + { + "name": "8080", + "port": 8080, + "targetPort": 8080 + } + ], + "selector": { + "io.kompose.service": "node2" + } + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "node3", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "node3" + } + }, + "spec": { + "ports": [ + { + "name": "8080", + "port": 8080, + "targetPort": 8080 + } + ], + "selector": { + "io.kompose.service": "node3" + } + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "redis", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "redis" + } + }, + "spec": { + "ports": [ + { + "name": "6379", + "port": 6379, + "targetPort": 6379 + } + ], + "selector": { + "io.kompose.service": "redis" + } + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "nginx", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "nginx" + } + }, + "spec": { + "strategy": { + "resources": {} + }, + "triggers": [ + { + "type": "ConfigChange" + }, + { + "type": "ImageChange", + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "nginx" + ], + "from": { + "kind": "ImageStreamTag", + "name": "nginx:latest" + } + } + } + ], + "replicas": 1, + "test": false, + "selector": { + "io.kompose.service": "nginx" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "nginx" + } + }, + "spec": { + "containers": [ + { + "name": "nginx", + "image": " ", + "ports": [ + { + "containerPort": 80 + } + ], + "resources": {} + } + ], + "restartPolicy": "Always" + } + } + }, + "status": {} + }, + { + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "nginx", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "nginx" + } + }, + "spec": { + "tags": [ + { + "name": "latest", + "annotations": null, + "from": { + "kind": "DockerImage", + "name": "nginx" + }, + "generation": null, + "importPolicy": {} + } + ] + }, + "status": { + "dockerImageRepository": "" + } + }, + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "node1", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "node1" + } + }, + "spec": { + "strategy": { + "resources": {} + }, + "triggers": [ + { + "type": "ConfigChange" + }, + { + "type": "ImageChange", + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "node1" + ], + "from": { + "kind": "ImageStreamTag", + "name": "node1:latest" + } + } + } + ], + "replicas": 1, + "test": false, + "selector": { + "io.kompose.service": "node1" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "node1" + } + }, + "spec": { + "containers": [ + { + "name": "node1", + "image": " ", + "ports": [ + { + "containerPort": 8080 + } + ], + "resources": {} + } + ], + "restartPolicy": "Always" + } + } + }, + "status": {} + }, + { + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "node1", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "node1" + } + }, + "spec": { + "tags": [ + { + "name": "latest", + "annotations": null, + "from": { + "kind": "DockerImage", + "name": "node1" + }, + "generation": null, + "importPolicy": {} + } + ] + }, + "status": { + "dockerImageRepository": "" + } + }, + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "node2", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "node2" + } + }, + "spec": { + "strategy": { + "resources": {} + }, + "triggers": [ + { + "type": "ConfigChange" + }, + { + "type": "ImageChange", + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "node2" + ], + "from": { + "kind": "ImageStreamTag", + "name": "node2:latest" + } + } + } + ], + "replicas": 1, + "test": false, + "selector": { + "io.kompose.service": "node2" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "node2" + } + }, + "spec": { + "containers": [ + { + "name": "node2", + "image": " ", + "ports": [ + { + "containerPort": 8080 + } + ], + "resources": {} + } + ], + "restartPolicy": "Always" + } + } + }, + "status": {} + }, + { + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "node2", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "node2" + } + }, + "spec": { + "tags": [ + { + "name": "latest", + "annotations": null, + "from": { + "kind": "DockerImage", + "name": "node2" + }, + "generation": null, + "importPolicy": {} + } + ] + }, + "status": { + "dockerImageRepository": "" + } + }, + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "node3", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "node3" + } + }, + "spec": { + "strategy": { + "resources": {} + }, + "triggers": [ + { + "type": "ConfigChange" + }, + { + "type": "ImageChange", + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "node3" + ], + "from": { + "kind": "ImageStreamTag", + "name": "node3:latest" + } + } + } + ], + "replicas": 1, + "test": false, + "selector": { + "io.kompose.service": "node3" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "node3" + } + }, + "spec": { + "containers": [ + { + "name": "node3", + "image": " ", + "ports": [ + { + "containerPort": 8080 + } + ], + "resources": {} + } + ], + "restartPolicy": "Always" + } + } + }, + "status": {} + }, + { + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "node3", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "node3" + } + }, + "spec": { + "tags": [ + { + "name": "latest", + "annotations": null, + "from": { + "kind": "DockerImage", + "name": "node3" + }, + "generation": null, + "importPolicy": {} + } + ] + }, + "status": { + "dockerImageRepository": "" + } + }, + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "redis", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "redis" + } + }, + "spec": { + "strategy": { + "resources": {} + }, + "triggers": [ + { + "type": "ConfigChange" + }, + { + "type": "ImageChange", + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "redis" + ], + "from": { + "kind": "ImageStreamTag", + "name": "redis:latest" + } + } + } + ], + "replicas": 1, + "test": false, + "selector": { + "io.kompose.service": "redis" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "redis" + } + }, + "spec": { + "containers": [ + { + "name": "redis", + "image": " ", + "ports": [ + { + "containerPort": 6379 + } + ], + "resources": {} + } + ], + "restartPolicy": "Always" + } + } + }, + "status": {} + }, + { + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "redis", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "redis" + } + }, + "spec": { + "tags": [ + { + "name": "latest", + "annotations": null, + "from": { + "kind": "DockerImage", + "name": "redis" + }, + "generation": null, + "importPolicy": {} + } + ] + }, + "status": { + "dockerImageRepository": "" + } + } + ] +} diff --git a/script/test/fixtures/yaml-and-yml/yml/output-os.json b/script/test/fixtures/yaml-and-yml/yml/output-os.json index 0c7de2ef..75863268 100644 --- a/script/test/fixtures/yaml-and-yml/yml/output-os.json +++ b/script/test/fixtures/yaml-and-yml/yml/output-os.json @@ -3,32 +3,6 @@ "apiVersion": "v1", "metadata": {}, "items": [ - { - "kind": "Service", - "apiVersion": "v1", - "metadata": { - "name": "web", - "creationTimestamp": null, - "labels": { - "io.kompose.service": "web" - } - }, - "spec": { - "ports": [ - { - "name": "5000", - "port": 5000, - "targetPort": 5000 - } - ], - "selector": { - "io.kompose.service": "web" - } - }, - "status": { - "loadBalancer": {} - } - }, { "kind": "Service", "apiVersion": "v1", @@ -56,7 +30,7 @@ } }, { - "kind": "DeploymentConfig", + "kind": "Service", "apiVersion": "v1", "metadata": { "name": "web", @@ -66,84 +40,19 @@ } }, "spec": { - "strategy": { - "resources": {} - }, - "triggers": [ + "ports": [ { - "type": "ConfigChange" - }, - { - "type": "ImageChange", - "imageChangeParams": { - "automatic": true, - "containerNames": [ - "web" - ], - "from": { - "kind": "ImageStreamTag", - "name": "web:latest" - } - } + "name": "5000", + "port": 5000, + "targetPort": 5000 } ], - "replicas": 1, - "test": false, "selector": { "io.kompose.service": "web" - }, - "template": { - "metadata": { - "creationTimestamp": null, - "labels": { - "io.kompose.service": "web" - } - }, - "spec": { - "containers": [ - { - "name": "web", - "image": " ", - "ports": [ - { - "containerPort": 5000 - } - ], - "resources": {} - } - ], - "restartPolicy": "Always" - } } }, - "status": {} - }, - { - "kind": "ImageStream", - "apiVersion": "v1", - "metadata": { - "name": "web", - "creationTimestamp": null, - "labels": { - "io.kompose.service": "web" - } - }, - "spec": { - "tags": [ - { - "name": "latest", - "annotations": null, - "from": { - "kind": "DockerImage", - "name": "tuna/docker-counter23" - }, - "generation": null, - "importPolicy": {} - } - ] - }, "status": { - "dockerImageRepository": "" + "loadBalancer": {} } }, { @@ -236,6 +145,97 @@ "status": { "dockerImageRepository": "" } + }, + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "web", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "web" + } + }, + "spec": { + "strategy": { + "resources": {} + }, + "triggers": [ + { + "type": "ConfigChange" + }, + { + "type": "ImageChange", + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "web" + ], + "from": { + "kind": "ImageStreamTag", + "name": "web:latest" + } + } + } + ], + "replicas": 1, + "test": false, + "selector": { + "io.kompose.service": "web" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "web" + } + }, + "spec": { + "containers": [ + { + "name": "web", + "image": " ", + "ports": [ + { + "containerPort": 5000 + } + ], + "resources": {} + } + ], + "restartPolicy": "Always" + } + } + }, + "status": {} + }, + { + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "web", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "web" + } + }, + "spec": { + "tags": [ + { + "name": "latest", + "annotations": null, + "from": { + "kind": "DockerImage", + "name": "tuna/docker-counter23" + }, + "generation": null, + "importPolicy": {} + } + ] + }, + "status": { + "dockerImageRepository": "" + } } ] }