diff --git a/cli/command/command.go b/cli/command/command.go index df4f65bf2..703ca406f 100644 --- a/cli/command/command.go +++ b/cli/command/command.go @@ -154,7 +154,7 @@ func commonConvertFlags() []cli.Flag { return []cli.Flag{ cli.StringFlag{ Name: "out,o", - Usage: "Specify file name in order to save objects into", + Usage: "Specify path to a file or a directory to save generated objects into. If path is a directory, the objects are stored in that directory. If path is a file, then objects are stored in that single file. File is created if it does not exist.", EnvVar: "OUTPUT_FILE", }, cli.IntFlag{ diff --git a/pkg/transformer/kubernetes/k8sutils.go b/pkg/transformer/kubernetes/k8sutils.go index 5e36b5eba..0f5c6aa8c 100644 --- a/pkg/transformer/kubernetes/k8sutils.go +++ b/pkg/transformer/kubernetes/k8sutils.go @@ -124,10 +124,42 @@ func cpFileToChart(manifestDir, filename string) error { return ioutil.WriteFile(manifestDir+string(os.PathSeparator)+filename, infile, 0644) } +// Check if given path is a directory +func isDir(name string) bool { + + // Open file to get stat later + f, err := os.Open(name) + if err != nil { + return false + } + defer f.Close() + + // Get file attributes and information + fileStat, err := f.Stat() + if err != nil { + logrus.Fatalf("error retrieving file information: %v", err) + } + + // Check if given path is a directory + if fileStat.IsDir() { + return true + } + return false +} + // PrintList will take the data converted and decide on the commandline attributes given func PrintList(objects []runtime.Object, opt kobject.ConvertOptions) error { - f := transformer.CreateOutFile(opt.OutFile) - defer f.Close() + + var f *os.File + var dirName string + + // Check if output file is a directory + if isDir(opt.OutFile) { + dirName = opt.OutFile + } else { + f = transformer.CreateOutFile(opt.OutFile) + defer f.Close() + } var files []string @@ -155,7 +187,7 @@ func PrintList(objects []runtime.Object, opt kobject.ConvertOptions) error { if err != nil { return fmt.Errorf("Error in marshalling the List: %v", err) } - files = append(files, transformer.Print("", "", data, opt.ToStdout, opt.GenerateYaml, f)) + files = append(files, transformer.Print("", dirName, "", data, opt.ToStdout, opt.GenerateYaml, f)) } else { var file string // create a separate file for each provider @@ -172,21 +204,21 @@ func PrintList(objects []runtime.Object, opt kobject.ConvertOptions) error { switch t := v.(type) { case *api.ReplicationController: - file = transformer.Print(t.Name, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f) + file = transformer.Print(t.Name, dirName, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f) case *extensions.Deployment: - file = transformer.Print(t.Name, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f) + file = transformer.Print(t.Name, dirName, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f) case *extensions.DaemonSet: - file = transformer.Print(t.Name, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f) + file = transformer.Print(t.Name, dirName, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f) case *deployapi.DeploymentConfig: - file = transformer.Print(t.Name, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f) + file = transformer.Print(t.Name, dirName, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f) case *imageapi.ImageStream: - file = transformer.Print(t.Name, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f) + file = transformer.Print(t.Name, dirName, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f) case *api.Service: - file = transformer.Print(t.Name, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f) + file = transformer.Print(t.Name, dirName, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f) case *api.PersistentVolumeClaim: - file = transformer.Print(t.Name, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f) + file = transformer.Print(t.Name, dirName, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f) case *api.Pod: - file = transformer.Print(t.Name, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f) + file = transformer.Print(t.Name, dirName, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f) } files = append(files, file) } diff --git a/pkg/transformer/kubernetes/k8sutils_test.go b/pkg/transformer/kubernetes/k8sutils_test.go index 0ec154223..190db589c 100644 --- a/pkg/transformer/kubernetes/k8sutils_test.go +++ b/pkg/transformer/kubernetes/k8sutils_test.go @@ -23,6 +23,8 @@ import ( "github.com/kubernetes-incubator/kompose/pkg/kobject" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/extensions" + "os" + "path/filepath" ) /* @@ -115,3 +117,47 @@ func TestCreateServiceWithServiceUser(t *testing.T) { } } + +func TestIsDir(t *testing.T) { + tempPath := "/tmp/kompose_unit" + tempDir := filepath.Join(tempPath, "i_am_dir") + tempFile := filepath.Join(tempPath, "i_am_file") + tempAbsentDirPath := filepath.Join(tempPath, "i_do_not_exist") + + // create directory + err := os.MkdirAll(tempDir, 0744) + if err != nil { + t.Errorf("Unable to create directory: %v", err) + } + + // create empty file + f, err := os.Create(tempFile) + if err != nil { + t.Errorf("Unable to create empty file: %v", err) + } + f.Close() + + // Check output if directory exists + output := isDir(tempDir) + if output != true { + t.Errorf("directory %v exists but isDir() returned %v", tempDir, output) + } + + // Check output if file is provided + output = isDir(tempFile) + if output != false { + t.Errorf("%v is a file but isDir() returned %v", tempDir, output) + } + + // Check output if path does not exist + output = isDir(tempAbsentDirPath) + if output != false { + t.Errorf("Directory %v does not exist, but isDir() returned %v", tempAbsentDirPath, output) + } + + // delete temporary directory + err = os.RemoveAll(tempPath) + if err != nil { + t.Errorf("Error removing the temporary directory during cleanup: %v", err) + } +} diff --git a/pkg/transformer/utils.go b/pkg/transformer/utils.go index f64473ad4..cb951ee3d 100644 --- a/pkg/transformer/utils.go +++ b/pkg/transformer/utils.go @@ -31,6 +31,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/runtime" + "path/filepath" ) const letterBytes = "abcdefghijklmnopqrstuvwxyz0123456789" @@ -51,7 +52,7 @@ func CreateOutFile(out string) *os.File { if len(out) != 0 { f, err = os.Create(out) if err != nil { - logrus.Fatalf("error opening file: %v", err) + logrus.Fatalf("error creating file: %v", err) } } return f @@ -131,7 +132,7 @@ func TransformData(obj runtime.Object, GenerateYaml bool) ([]byte, error) { } // Either print to stdout or to file/s -func Print(name, trailing string, data []byte, toStdout, generateYaml bool, f *os.File) string { +func Print(name, path string, trailing string, data []byte, toStdout, generateYaml bool, f *os.File) string { file := "" if generateYaml { @@ -150,6 +151,7 @@ func Print(name, trailing string, data []byte, toStdout, generateYaml bool, f *o f.Sync() } else { // Write content separately to each file + file = filepath.Join(path, file) if err := ioutil.WriteFile(file, []byte(data), 0644); err != nil { logrus.Fatalf("Failed to write %s: %v", trailing, err) } diff --git a/script/test/cmd/lib.sh b/script/test/cmd/lib.sh index f2d7e626d..1fbd8d5a9 100644 --- a/script/test/cmd/lib.sh +++ b/script/test/cmd/lib.sh @@ -166,3 +166,29 @@ function convert::expect_failure() { return $exit_status } readonly -f convert::expect_failure + +function convert::files_exist() { + local cmd=$1 + local dir=$2 + + convert::start_test "convert::files_exist: Running: '${cmd}'" + mkdir -p $dir 2> /dev/null + + cd $TEMP_DIR && convert::run_cmd $cmd + exit_status=$? + + shift + shift + + for var in "$@" + do + if [ ! -f $var ] + then + convert::print_fail "file $var does not exist\n" + return 1 + fi + convert::print_pass "file $var exists\n" + done + convert::teardown + return 0 +} \ No newline at end of file diff --git a/script/test/cmd/tests.sh b/script/test/cmd/tests.sh index 061908585..66a4d6540 100755 --- a/script/test/cmd/tests.sh +++ b/script/test/cmd/tests.sh @@ -112,10 +112,26 @@ convert::expect_success "kompose -f $KOMPOSE_ROOT/script/test/fixtures/restart-o convert::expect_success "kompose -f $KOMPOSE_ROOT/script/test/fixtures/restart-options/docker-compose-restart-no.yml --provider openshift convert --stdout" "$KOMPOSE_ROOT/script/test/fixtures/restart-options/output-os-restart-no.json" convert::expect_success "kompose -f $KOMPOSE_ROOT/script/test/fixtures/restart-options/docker-compose-restart-onfail.yml --provider openshift convert --stdout" "$KOMPOSE_ROOT/script/test/fixtures/restart-options/output-os-restart-onfail.json" + ###### # Test key-only envrionment variable export $(cat $KOMPOSE_ROOT/script/test/fixtures/keyonly-envs/envs) convert::expect_success "kompose --file $KOMPOSE_ROOT/script/test/fixtures/keyonly-envs/env.yml convert --stdout" "$KOMPOSE_ROOT/script/test/fixtures/keyonly-envs/output-k8s.json" unset $(cat $KOMPOSE_ROOT/script/test/fixtures/keyonly-envs/envs | cut -d'=' -f1) + +###### +# Test the output file behavior of kompose convert +# Default behavior without -o +convert::files_exist "kompose -f $KOMPOSE_ROOT/examples/docker-compose.yml convert" "" "$TEMP_DIR/redis-deployment.json" "$TEMP_DIR/redis-service.json" "$TEMP_DIR/web-deployment.json" "$TEMP_DIR/web-service.json" +# Behavior with -o +convert::files_exist "kompose -f $KOMPOSE_ROOT/examples/docker-compose.yml convert -o output_file" "" "$TEMP_DIR/output_file" +# Behavior with -o +convert::files_exist "kompose -f $KOMPOSE_ROOT/examples/docker-compose.yml convert -o output_dir" "$TEMP_DIR/output_dir/" "$TEMP_DIR/output_dir/redis-deployment.json" "$TEMP_DIR/output_dir/redis-service.json" "$TEMP_DIR/output_dir/web-deployment.json" "$TEMP_DIR/output_dir/web-service.json" +# Behavior with -o / +convert::files_exist "kompose -f $KOMPOSE_ROOT/examples/docker-compose.yml convert -o output_dir/output_file" "$TEMP_DIR/output_dir/" "$TEMP_DIR/output_dir/output_file" +# Behavior with -o // +convert::files_exist "kompose -f $KOMPOSE_ROOT/examples/docker-compose.yml convert -o output_dir/output_dir_nested/output_file" "$TEMP_DIR/output_dir/output_dir_nested" "$TEMP_DIR/output_dir/output_dir_nested/output_file" + + exit $EXIT_STATUS