diff --git a/pkg/app/app.go b/pkg/app/app.go index 9c96f83e..cf6e7cc7 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -35,6 +35,7 @@ import ( "os" + "github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus" "github.com/kubernetes-incubator/kompose/pkg/kobject" "github.com/kubernetes-incubator/kompose/pkg/loader" "github.com/kubernetes-incubator/kompose/pkg/transformer" @@ -211,16 +212,26 @@ func Convert(opt kobject.ConvertOptions) { komposeObject := kobject.KomposeObject{ ServiceConfigs: make(map[string]kobject.ServiceConfig), } - komposeObject = l.LoadFile(opt.InputFiles) + komposeObject, err = l.LoadFile(opt.InputFiles) + if err != nil { + logrus.Fatalf(err.Error()) + } // Get a transformer that maps komposeObject to provider's primitives t := getTransformer(opt) // Do the transformation - objects := t.Transform(komposeObject, opt) + objects, err := t.Transform(komposeObject, opt) + + if err != nil { + logrus.Fatalf(err.Error()) + } // Print output - kubernetes.PrintList(objects, opt) + err = kubernetes.PrintList(objects, opt) + if err != nil { + logrus.Fatalf(err.Error()) + } } // Up brings up deployment, svc. @@ -237,7 +248,10 @@ func Up(opt kobject.ConvertOptions) { komposeObject := kobject.KomposeObject{ ServiceConfigs: make(map[string]kobject.ServiceConfig), } - komposeObject = l.LoadFile(opt.InputFiles) + komposeObject, err = l.LoadFile(opt.InputFiles) + if err != nil { + logrus.Fatalf(err.Error()) + } // Get the transformer t := getTransformer(opt) @@ -263,7 +277,10 @@ func Down(opt kobject.ConvertOptions) { komposeObject := kobject.KomposeObject{ ServiceConfigs: make(map[string]kobject.ServiceConfig), } - komposeObject = l.LoadFile(opt.InputFiles) + komposeObject, err = l.LoadFile(opt.InputFiles) + if err != nil { + logrus.Fatalf(err.Error()) + } // Get the transformer t := getTransformer(opt) diff --git a/pkg/loader/bundle/bundle.go b/pkg/loader/bundle/bundle.go index 1477c108..45890c89 100644 --- a/pkg/loader/bundle/bundle.go +++ b/pkg/loader/bundle/bundle.go @@ -29,6 +29,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/fatih/structs" "github.com/kubernetes-incubator/kompose/pkg/kobject" + "github.com/pkg/errors" ) // Bundle is docker bundle file loader, implements Loader interface @@ -106,16 +107,16 @@ func checkUnsupportedKey(bundleStruct *Bundlefile) []string { } // load image from dab file -func loadImage(service Service) (string, string) { +func loadImage(service Service) (string, error) { character := "@" if strings.Contains(service.Image, character) { - return service.Image[0:strings.Index(service.Image, character)], "" + return service.Image[0:strings.Index(service.Image, character)], nil } - return "", "Invalid image format" + return "", errors.New("Invalid image format") } // load environment variables from dab file -func loadEnvVars(service Service) ([]kobject.EnvVar, string) { +func loadEnvVars(service Service) ([]kobject.EnvVar, error) { envs := []kobject.EnvVar{} for _, env := range service.Env { character := "=" @@ -144,15 +145,15 @@ func loadEnvVars(service Service) ([]kobject.EnvVar, string) { Value: value, }) } else { - return envs, "Invalid container env " + env + return envs, errors.New("Invalid container env") } } } - return envs, "" + return envs, nil } // load ports from dab file -func loadPorts(service Service) ([]kobject.Ports, string) { +func loadPorts(service Service) ([]kobject.Ports, error) { ports := []kobject.Ports{} for _, port := range service.Ports { var p api.Protocol @@ -170,11 +171,11 @@ func loadPorts(service Service) ([]kobject.Ports, string) { Protocol: p, }) } - return ports, "" + return ports, nil } // LoadFile loads dab file into KomposeObject -func (b *Bundle) LoadFile(files []string) kobject.KomposeObject { +func (b *Bundle) LoadFile(files []string) (kobject.KomposeObject, error) { komposeObject := kobject.KomposeObject{ ServiceConfigs: make(map[string]kobject.ServiceConfig), LoadedFrom: "bundle", @@ -182,12 +183,12 @@ func (b *Bundle) LoadFile(files []string) kobject.KomposeObject { file := files[0] buf, err := ioutil.ReadFile(file) if err != nil { - log.Fatalf("Failed to read bundles file: %s ", err) + return kobject.KomposeObject{}, errors.Wrap(err, "ioutil.ReadFile failed, Failed to read bundles file") } reader := strings.NewReader(string(buf)) bundle, err := loadFile(reader) if err != nil { - log.Fatalf("Failed to parse bundles file: %s", err) + return kobject.KomposeObject{}, errors.Wrap(err, "loadFile failed, Failed to parse bundles file") } noSupKeys := checkUnsupportedKey(bundle) @@ -204,20 +205,20 @@ func (b *Bundle) LoadFile(files []string) kobject.KomposeObject { serviceConfig.Annotations = service.Labels image, err := loadImage(service) - if err != "" { - log.Fatalf("Failed to load image from bundles file: " + err) + if err != nil { + return kobject.KomposeObject{}, errors.Wrap(err, "loadImage failed, Failed to load image from bundles file") } serviceConfig.Image = image envs, err := loadEnvVars(service) - if err != "" { - log.Fatalf("Failed to load envvar from bundles file: " + err) + if err != nil { + return kobject.KomposeObject{}, errors.Wrap(err, "loadEnvVars failed, Failed to load envvar from bundles file") } serviceConfig.Environment = envs ports, err := loadPorts(service) - if err != "" { - log.Fatalf("Failed to load ports from bundles file: " + err) + if err != nil { + return kobject.KomposeObject{}, errors.Wrap(err, "loadPorts failed, Failed to load ports from bundles file") } serviceConfig.Port = ports @@ -228,7 +229,7 @@ func (b *Bundle) LoadFile(files []string) kobject.KomposeObject { komposeObject.ServiceConfigs[name] = serviceConfig } - return komposeObject + return komposeObject, nil } // LoadFile loads a bundlefile from a path to the file diff --git a/pkg/loader/compose/compose.go b/pkg/loader/compose/compose.go index a0fee828..dd623e37 100644 --- a/pkg/loader/compose/compose.go +++ b/pkg/loader/compose/compose.go @@ -33,6 +33,7 @@ import ( "github.com/docker/libcompose/project" "github.com/fatih/structs" "github.com/kubernetes-incubator/kompose/pkg/kobject" + "github.com/pkg/errors" ) // Compose is docker compose file loader, implements Loader interface @@ -275,7 +276,7 @@ func loadPorts(composePorts []string) ([]kobject.Ports, error) { } // LoadFile loads compose file into KomposeObject -func (c *Compose) LoadFile(files []string) kobject.KomposeObject { +func (c *Compose) LoadFile(files []string) (kobject.KomposeObject, error) { komposeObject := kobject.KomposeObject{ ServiceConfigs: make(map[string]kobject.ServiceConfig), LoadedFrom: "compose", @@ -290,7 +291,7 @@ func (c *Compose) LoadFile(files []string) kobject.KomposeObject { if context.EnvironmentLookup == nil { cwd, err := os.Getwd() if err != nil { - return kobject.KomposeObject{} + return kobject.KomposeObject{}, nil } context.EnvironmentLookup = &lookup.ComposableEnvLookup{ Lookups: []config.EnvironmentLookup{ @@ -306,7 +307,7 @@ func (c *Compose) LoadFile(files []string) kobject.KomposeObject { composeObject := project.NewProject(context, nil, nil) err := composeObject.Parse() if err != nil { - log.Fatalf("Failed to load compose file: %v", err) + return kobject.KomposeObject{}, errors.Wrap(err, "composeObject.Parse() failed, Failed to load compose file") } noSupKeys := checkUnsupportedKey(composeObject) @@ -329,7 +330,7 @@ func (c *Compose) LoadFile(files []string) kobject.KomposeObject { // load ports ports, err := loadPorts(composeServiceConfig.Ports) if err != nil { - log.Fatalf("%q failed to load ports from compose file: %v", name, err) + return kobject.KomposeObject{}, errors.Wrap(err, "loadPorts failed. "+name+" failed to load ports from compose file") } serviceConfig.Port = ports @@ -347,7 +348,12 @@ func (c *Compose) LoadFile(files []string) kobject.KomposeObject { for key, value := range composeServiceConfig.Labels { switch key { case "kompose.service.type": - serviceConfig.ServiceType = handleServiceType(value) + serviceType, err := handleServiceType(value) + if err != nil { + return kobject.KomposeObject{}, errors.Wrap(err, "handleServiceType failed") + } + + serviceConfig.ServiceType = serviceType case "kompose.service.expose": serviceConfig.ExposeService = strings.ToLower(value) } @@ -373,20 +379,19 @@ func (c *Compose) LoadFile(files []string) kobject.KomposeObject { komposeObject.ServiceConfigs[name] = serviceConfig } - return komposeObject + return komposeObject, nil } -func handleServiceType(ServiceType string) string { +func handleServiceType(ServiceType string) (string, error) { switch strings.ToLower(ServiceType) { case "", "clusterip": - return string(api.ServiceTypeClusterIP) + return string(api.ServiceTypeClusterIP), nil case "nodeport": - return string(api.ServiceTypeNodePort) + return string(api.ServiceTypeNodePort), nil case "loadbalancer": - return string(api.ServiceTypeLoadBalancer) + return string(api.ServiceTypeLoadBalancer), nil default: - log.Fatalf("Unknown value '%s', supported values are 'NodePort, ClusterIP or LoadBalancer'", ServiceType) - return "" + return "", errors.New("Unknown value " + ServiceType + " , supported values are 'NodePort, ClusterIP or LoadBalancer'") } } diff --git a/pkg/loader/compose/compose_test.go b/pkg/loader/compose/compose_test.go index 79d44413..6960f4af 100644 --- a/pkg/loader/compose/compose_test.go +++ b/pkg/loader/compose/compose_test.go @@ -28,6 +28,7 @@ import ( "github.com/docker/libcompose/config" "github.com/docker/libcompose/project" "github.com/docker/libcompose/yaml" + "github.com/pkg/errors" ) // Test if service types are parsed properly on user input @@ -47,7 +48,10 @@ func TestHandleServiceType(t *testing.T) { } for _, tt := range tests { - result := handleServiceType(tt.labelValue) + result, err := handleServiceType(tt.labelValue) + if err != nil { + t.Error(errors.Wrap(err, "handleServiceType failed")) + } if result != tt.serviceType { t.Errorf("Expected %q, got %q", tt.serviceType, result) } diff --git a/pkg/loader/loader.go b/pkg/loader/loader.go index 9f96082c..777674f7 100644 --- a/pkg/loader/loader.go +++ b/pkg/loader/loader.go @@ -26,7 +26,7 @@ import ( // Loader interface defines loader that loads files and converts it to kobject representation type Loader interface { - LoadFile(files []string) kobject.KomposeObject + LoadFile(files []string) (kobject.KomposeObject, error) ///Name() string } diff --git a/pkg/transformer/kubernetes/k8sutils.go b/pkg/transformer/kubernetes/k8sutils.go index 240be37b..c01824d2 100644 --- a/pkg/transformer/kubernetes/k8sutils.go +++ b/pkg/transformer/kubernetes/k8sutils.go @@ -39,6 +39,7 @@ import ( "k8s.io/kubernetes/pkg/runtime" deployapi "github.com/openshift/origin/pkg/deploy/api" + "github.com/pkg/errors" "k8s.io/kubernetes/pkg/api/resource" ) @@ -92,7 +93,7 @@ home: t, err := template.New("ChartTmpl").Parse(chart) if err != nil { - log.Fatalf("Failed to generate Chart.yaml template: %s\n", err) + return errors.Wrap(err, "Failed to generate Chart.yaml template, template.New failed") } var chartData bytes.Buffer _ = t.Execute(&chartData, details) @@ -127,26 +128,26 @@ func cpFileToChart(manifestDir, filename string) error { } // Check if given path is a directory -func isDir(name string) bool { +func isDir(name string) (bool, error) { // Open file to get stat later f, err := os.Open(name) if err != nil { - return false + return false, nil } defer f.Close() // Get file attributes and information fileStat, err := f.Stat() if err != nil { - log.Fatalf("error retrieving file information: %v", err) + return false, errors.Wrap(err, "error retrieving file information, f.Stat failed") } // Check if given path is a directory if fileStat.IsDir() { - return true + return true, nil } - return false + return false, nil } // PrintList will take the data converted and decide on the commandline attributes given @@ -156,10 +157,17 @@ func PrintList(objects []runtime.Object, opt kobject.ConvertOptions) error { var dirName string // Check if output file is a directory - if isDir(opt.OutFile) { + isDirVal, err := isDir(opt.OutFile) + if err != nil { + return errors.Wrap(err, "isDir failed") + } + if isDirVal { dirName = opt.OutFile } else { - f = transformer.CreateOutFile(opt.OutFile) + f, err = transformer.CreateOutFile(opt.OutFile) + if err != nil { + return errors.Wrap(err, "transformer.CreateOutFile failed") + } defer f.Close() } @@ -189,7 +197,11 @@ 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("", dirName, "", data, opt.ToStdout, opt.GenerateJSON, f)) + printVal, err := transformer.Print("", dirName, "", data, opt.ToStdout, opt.GenerateJSON, f) + if err != nil { + return errors.Wrap(err, "transformer.Print failed") + } + files = append(files, printVal) } else { var file string // create a separate file for each provider @@ -212,13 +224,19 @@ func PrintList(objects []runtime.Object, opt kobject.ConvertOptions) error { // cast it to correct type - api.ObjectMeta objectMeta := val.FieldByName("ObjectMeta").Interface().(api.ObjectMeta) - file = transformer.Print(objectMeta.Name, dirName, strings.ToLower(typeMeta.Kind), data, opt.ToStdout, opt.GenerateJSON, f) + file, err = transformer.Print(objectMeta.Name, dirName, strings.ToLower(typeMeta.Kind), data, opt.ToStdout, opt.GenerateJSON, f) + if err != nil { + return errors.Wrap(err, "transformer.Print failed") + } files = append(files, file) } } if opt.CreateChart { - generateHelm(opt.InputFiles, files) + err = generateHelm(opt.InputFiles, files) + if err != nil { + return errors.Wrap(err, "generateHelm failed") + } } return nil } @@ -309,12 +327,16 @@ func (k *Kubernetes) CreateHeadlessService(name string, service kobject.ServiceC } // UpdateKubernetesObjects loads configurations to k8s objects -func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.ServiceConfig, objects *[]runtime.Object) { +func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.ServiceConfig, objects *[]runtime.Object) error { // Configure the environment variables. envs := k.ConfigEnvs(name, service) // Configure the container volumes. - volumesMount, volumes, pvc := k.ConfigVolumes(name, service) + volumesMount, volumes, pvc, err := k.ConfigVolumes(name, service) + if err != nil { + return errors.Wrap(err, "k.ConfigVolumes failed") + } + if pvc != nil { // Looping on the slice pvc instead of `*objects = append(*objects, pvc...)` // because the type of objects and pvc is different, but when doing append @@ -331,7 +353,7 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic annotations := transformer.ConfigAnnotations(service) // fillTemplate fills the pod template with the value calculated from config - fillTemplate := func(template *api.PodTemplateSpec) { + fillTemplate := func(template *api.PodTemplateSpec) error { if len(service.ContainerName) > 0 { template.Spec.Containers[0].Name = service.ContainerName } @@ -384,8 +406,9 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic case "on-failure": template.Spec.RestartPolicy = api.RestartPolicyOnFailure default: - log.Fatalf("Unknown restart policy %s for service %s", service.Restart, name) + return errors.New("Unknown restart policy " + service.Restart + " for service" + name) } + return nil } // fillObjectMeta fills the metadata with the value calculated from config @@ -395,8 +418,10 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic // update supported controller for _, obj := range *objects { - k.UpdateController(obj, fillTemplate, fillObjectMeta) - + err = k.UpdateController(obj, fillTemplate, fillObjectMeta) + if err != nil { + return errors.Wrap(err, "k.UpdateController failed") + } if len(service.Volumes) > 0 { switch objType := obj.(type) { case *extensions.Deployment: @@ -406,6 +431,7 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic } } } + return nil } // SortServicesFirst - the objects that we get can be in any order this keeps services first @@ -426,52 +452,73 @@ func (k *Kubernetes) SortServicesFirst(objs *[]runtime.Object) { *objs = ret } -func (k *Kubernetes) findDependentVolumes(svcname string, komposeObject kobject.KomposeObject) (volumes []api.Volume, volumeMounts []api.VolumeMount) { +func (k *Kubernetes) findDependentVolumes(svcname string, komposeObject kobject.KomposeObject) (volumes []api.Volume, volumeMounts []api.VolumeMount, err error) { // Get all the volumes and volumemounts this particular service is dependent on for _, dependentSvc := range komposeObject.ServiceConfigs[svcname].VolumesFrom { - vols, volMounts := k.findDependentVolumes(dependentSvc, komposeObject) + vols, volMounts, err := k.findDependentVolumes(dependentSvc, komposeObject) + if err != nil { + err = errors.Wrap(err, "k.findDependentVolumes failed") + return nil, nil, err + } volumes = append(volumes, vols...) volumeMounts = append(volumeMounts, volMounts...) } // add the volumes info of this service - volMounts, vols, _ := k.ConfigVolumes(svcname, komposeObject.ServiceConfigs[svcname]) + volMounts, vols, _, err := k.ConfigVolumes(svcname, komposeObject.ServiceConfigs[svcname]) + if err != nil { + err = errors.Wrap(err, "k.ConfigVolumes failed") + return nil, nil, err + } volumes = append(volumes, vols...) volumeMounts = append(volumeMounts, volMounts...) - return + return volumes, volumeMounts, nil } // VolumesFrom creates volums and volumeMounts for volumes_from -func (k *Kubernetes) VolumesFrom(objects *[]runtime.Object, komposeObject kobject.KomposeObject) { +func (k *Kubernetes) VolumesFrom(objects *[]runtime.Object, komposeObject kobject.KomposeObject) error { for _, obj := range *objects { switch t := obj.(type) { case *api.ReplicationController: svcName := t.ObjectMeta.Name for _, dependentSvc := range komposeObject.ServiceConfigs[svcName].VolumesFrom { - volumes, volumeMounts := k.findDependentVolumes(dependentSvc, komposeObject) + volumes, volumeMounts, err := k.findDependentVolumes(dependentSvc, komposeObject) + if err != nil { + return errors.Wrap(err, "k.findDependentVolumes") + } t.Spec.Template.Spec.Volumes = append(t.Spec.Template.Spec.Volumes, volumes...) t.Spec.Template.Spec.Containers[0].VolumeMounts = append(t.Spec.Template.Spec.Containers[0].VolumeMounts, volumeMounts...) } case *extensions.Deployment: svcName := t.ObjectMeta.Name for _, dependentSvc := range komposeObject.ServiceConfigs[svcName].VolumesFrom { - volumes, volumeMounts := k.findDependentVolumes(dependentSvc, komposeObject) + volumes, volumeMounts, err := k.findDependentVolumes(dependentSvc, komposeObject) + if err != nil { + return errors.Wrap(err, "k.findDependentVolumes") + } t.Spec.Template.Spec.Volumes = append(t.Spec.Template.Spec.Volumes, volumes...) t.Spec.Template.Spec.Containers[0].VolumeMounts = append(t.Spec.Template.Spec.Containers[0].VolumeMounts, volumeMounts...) } case *extensions.DaemonSet: svcName := t.ObjectMeta.Name for _, dependentSvc := range komposeObject.ServiceConfigs[svcName].VolumesFrom { - volumes, volumeMounts := k.findDependentVolumes(dependentSvc, komposeObject) + volumes, volumeMounts, err := k.findDependentVolumes(dependentSvc, komposeObject) + if err != nil { + return errors.Wrap(err, "k.findDependentVolumes") + } t.Spec.Template.Spec.Volumes = append(t.Spec.Template.Spec.Volumes, volumes...) t.Spec.Template.Spec.Containers[0].VolumeMounts = append(t.Spec.Template.Spec.Containers[0].VolumeMounts, volumeMounts...) } case *deployapi.DeploymentConfig: svcName := t.ObjectMeta.Name for _, dependentSvc := range komposeObject.ServiceConfigs[svcName].VolumesFrom { - volumes, volumeMounts := k.findDependentVolumes(dependentSvc, komposeObject) + volumes, volumeMounts, err := k.findDependentVolumes(dependentSvc, komposeObject) + if err != nil { + return errors.Wrap(err, "k.findDependentVolumes") + } t.Spec.Template.Spec.Volumes = append(t.Spec.Template.Spec.Volumes, volumes...) t.Spec.Template.Spec.Containers[0].VolumeMounts = append(t.Spec.Template.Spec.Containers[0].VolumeMounts, volumeMounts...) } } } + return nil } diff --git a/pkg/transformer/kubernetes/k8sutils_test.go b/pkg/transformer/kubernetes/k8sutils_test.go index a7fa152a..4c6c8087 100644 --- a/pkg/transformer/kubernetes/k8sutils_test.go +++ b/pkg/transformer/kubernetes/k8sutils_test.go @@ -26,6 +26,7 @@ import ( "github.com/kubernetes-incubator/kompose/pkg/kobject" "github.com/kubernetes-incubator/kompose/pkg/testutils" + "github.com/pkg/errors" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/extensions" ) @@ -63,7 +64,10 @@ func TestCreateService(t *testing.T) { ServiceConfigs: map[string]kobject.ServiceConfig{"app": service}, } k := Kubernetes{} - objects := k.Transform(komposeObject, kobject.ConvertOptions{CreateD: true, Replicas: 3}) + objects, err := k.Transform(komposeObject, kobject.ConvertOptions{CreateD: true, Replicas: 3}) + if err != nil { + t.Error(errors.Wrap(err, "k.Transform failed")) + } // Test the creation of the service svc := k.CreateService("foo", service, objects) @@ -107,7 +111,10 @@ func TestCreateServiceWithMemLimit(t *testing.T) { ServiceConfigs: map[string]kobject.ServiceConfig{"app": service}, } k := Kubernetes{} - objects := k.Transform(komposeObject, kobject.ConvertOptions{CreateD: true, Replicas: 3}) + objects, err := k.Transform(komposeObject, kobject.ConvertOptions{CreateD: true, Replicas: 3}) + if err != nil { + t.Error(errors.Wrap(err, "k.Transform failed")) + } // Retrieve the deployment object and test that it matches the MemLimit value for _, obj := range objects { @@ -155,7 +162,10 @@ func TestCreateServiceWithServiceUser(t *testing.T) { } k := Kubernetes{} - objects := k.Transform(komposeObject, kobject.ConvertOptions{CreateD: true, Replicas: 1}) + objects, err := k.Transform(komposeObject, kobject.ConvertOptions{CreateD: true, Replicas: 1}) + if err != nil { + t.Error(errors.Wrap(err, "k.Transform failed")) + } for _, obj := range objects { if deploy, ok := obj.(*extensions.Deployment); ok { @@ -188,19 +198,28 @@ func TestIsDir(t *testing.T) { f.Close() // Check output if directory exists - output := isDir(tempDir) + output, err := isDir(tempDir) + if err != nil { + t.Error(errors.Wrap(err, "isDir failed")) + } if output != true { t.Errorf("directory %v exists but isDir() returned %v", tempDir, output) } // Check output if file is provided - output = isDir(tempFile) + output, err = isDir(tempFile) + if err != nil { + t.Error(errors.Wrap(err, "isDir failed")) + } 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) + output, err = isDir(tempAbsentDirPath) + if err != nil { + t.Error(errors.Wrap(err, "isDir failed")) + } if output != false { t.Errorf("Directory %v does not exist, but isDir() returned %v", tempAbsentDirPath, output) } @@ -224,7 +243,10 @@ func TestServiceWithoutPort(t *testing.T) { } k := Kubernetes{} - objects := k.Transform(komposeObject, kobject.ConvertOptions{CreateD: true, Replicas: 1}) + objects, err := k.Transform(komposeObject, kobject.ConvertOptions{CreateD: true, Replicas: 1}) + if err != nil { + t.Error(errors.Wrap(err, "k.Transform failed")) + } if err := testutils.CheckForHeadless(objects); err != nil { t.Error(err) } @@ -245,7 +267,10 @@ func TestRecreateStrategyWithVolumesPresent(t *testing.T) { } k := Kubernetes{} - objects := k.Transform(komposeObject, kobject.ConvertOptions{CreateD: true, Replicas: 1}) + objects, err := k.Transform(komposeObject, kobject.ConvertOptions{CreateD: true, Replicas: 1}) + if err != nil { + t.Error(errors.Wrap(err, "k.Transform failed")) + } for _, obj := range objects { if deployment, ok := obj.(*extensions.Deployment); ok { if deployment.Spec.Strategy.Type != extensions.RecreateDeploymentStrategyType { diff --git a/pkg/transformer/kubernetes/kubernetes.go b/pkg/transformer/kubernetes/kubernetes.go index a04cdaaa..11af5070 100644 --- a/pkg/transformer/kubernetes/kubernetes.go +++ b/pkg/transformer/kubernetes/kubernetes.go @@ -46,6 +46,7 @@ import ( "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/intstr" //"k8s.io/kubernetes/pkg/controller/daemon" + "github.com/pkg/errors" ) // Kubernetes implements Transformer interface and represents Kubernetes transformer @@ -237,10 +238,10 @@ func (k *Kubernetes) initIngress(name string, service kobject.ServiceConfig, por } // CreatePVC initializes PersistentVolumeClaim -func (k *Kubernetes) CreatePVC(name string, mode string) *api.PersistentVolumeClaim { +func (k *Kubernetes) CreatePVC(name string, mode string) (*api.PersistentVolumeClaim, error) { size, err := resource.ParseQuantity("100Mi") if err != nil { - log.Fatalf("Error parsing size") + return nil, errors.Wrap(err, "resource.ParseQuantity failed, Error parsing size") } pvc := &api.PersistentVolumeClaim{ @@ -265,7 +266,7 @@ func (k *Kubernetes) CreatePVC(name string, mode string) *api.PersistentVolumeCl } else { pvc.Spec.AccessModes = []api.PersistentVolumeAccessMode{api.ReadWriteOnce} } - return pvc + return pvc, nil } // ConfigPorts configures the container ports. @@ -324,7 +325,7 @@ func (k *Kubernetes) ConfigServicePorts(name string, service kobject.ServiceConf } // ConfigVolumes configure the container volumes. -func (k *Kubernetes) ConfigVolumes(name string, service kobject.ServiceConfig) ([]api.VolumeMount, []api.Volume, []*api.PersistentVolumeClaim) { +func (k *Kubernetes) ConfigVolumes(name string, service kobject.ServiceConfig) ([]api.VolumeMount, []api.Volume, []*api.PersistentVolumeClaim, error) { volumeMounts := []api.VolumeMount{} volumes := []api.Volume{} var PVCs []*api.PersistentVolumeClaim @@ -371,7 +372,13 @@ func (k *Kubernetes) ConfigVolumes(name string, service kobject.ServiceConfig) ( volsource = k.ConfigEmptyVolumeSource() } else { volsource = k.ConfigPVCVolumeSource(volumeName, readonly) - PVCs = append(PVCs, k.CreatePVC(volumeName, mode)) + + createdPVC, err := k.CreatePVC(volumeName, mode) + if err != nil { + return nil, nil, nil, errors.Wrap(err, "k.CreatePVC failed") + } + + PVCs = append(PVCs, createdPVC) } // create a new volume object using the volsource and add to list @@ -385,7 +392,7 @@ func (k *Kubernetes) ConfigVolumes(name string, service kobject.ServiceConfig) ( log.Warningf("Volume mount on the host %q isn't supported - ignoring path on the host", host) } } - return volumeMounts, volumes, PVCs + return volumeMounts, volumes, PVCs, nil } // ConfigEmptyVolumeSource is helper function to create an EmptyDir api.VolumeSource @@ -452,7 +459,7 @@ func (k *Kubernetes) InitPod(name string, service kobject.ServiceConfig) *api.Po // Transform maps komposeObject to k8s objects // 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 { +func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) ([]runtime.Object, error) { noSupKeys := k.CheckUnsupportedKey(&komposeObject, unsupportedKey) for _, keyName := range noSupKeys { @@ -477,7 +484,7 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject. if service.Restart == "no" || service.Restart == "on-failure" { // Error out if Controller Object is specified with restart: 'on-failure' if opt.IsDeploymentFlag || opt.IsDaemonSetFlag || opt.IsReplicationControllerFlag { - log.Fatalf("Controller object cannot be specified with restart: 'on-failure'") + return nil, errors.New("Controller object cannot be specified with restart: 'on-failure'") } pod := k.InitPod(name, service) objects = append(objects, pod) @@ -505,38 +512,54 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject. k.VolumesFrom(&allobjects, komposeObject) // sort all object so Services are first k.SortServicesFirst(&allobjects) - return allobjects + return allobjects, nil } // UpdateController updates the given object with the given pod template update function and ObjectMeta update function -func (k *Kubernetes) UpdateController(obj runtime.Object, updateTemplate func(*api.PodTemplateSpec), updateMeta func(meta *api.ObjectMeta)) { +func (k *Kubernetes) UpdateController(obj runtime.Object, updateTemplate func(*api.PodTemplateSpec) error, updateMeta func(meta *api.ObjectMeta)) (err error) { switch t := obj.(type) { case *api.ReplicationController: if t.Spec.Template == nil { t.Spec.Template = &api.PodTemplateSpec{} } - updateTemplate(t.Spec.Template) + err = updateTemplate(t.Spec.Template) + if err != nil { + return errors.Wrap(err, "updateTemplate failed") + } updateMeta(&t.ObjectMeta) case *extensions.Deployment: - updateTemplate(&t.Spec.Template) + err = updateTemplate(&t.Spec.Template) + if err != nil { + return errors.Wrap(err, "updateTemplate failed") + } updateMeta(&t.ObjectMeta) case *extensions.DaemonSet: - updateTemplate(&t.Spec.Template) + err = updateTemplate(&t.Spec.Template) + if err != nil { + return errors.Wrap(err, "updateTemplate failed") + } updateMeta(&t.ObjectMeta) case *deployapi.DeploymentConfig: - updateTemplate(t.Spec.Template) + err = updateTemplate(t.Spec.Template) + if err != nil { + return errors.Wrap(err, "updateTemplate failed") + } updateMeta(&t.ObjectMeta) case *api.Pod: p := api.PodTemplateSpec{ ObjectMeta: t.ObjectMeta, Spec: t.Spec, } - updateTemplate(&p) + err = updateTemplate(&p) + if err != nil { + return errors.Wrap(err, "updateTemplate failed") + } t.Spec = p.Spec t.ObjectMeta = p.ObjectMeta case *buildapi.BuildConfig: updateMeta(&t.ObjectMeta) } + return nil } // GetKubernetesClient creates the k8s Client, returns k8s client and namespace @@ -560,7 +583,11 @@ func (k *Kubernetes) GetKubernetesClient() (*client.Client, string, error) { // Deploy submits deployment and svc to k8s endpoint func (k *Kubernetes) Deploy(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) error { //Convert komposeObject - objects := k.Transform(komposeObject, opt) + objects, err := k.Transform(komposeObject, opt) + + if err != nil { + return errors.Wrap(err, "k.Transform failed") + } pvcStr := " " if !opt.EmptyVols { @@ -622,7 +649,11 @@ func (k *Kubernetes) Deploy(komposeObject kobject.KomposeObject, opt kobject.Con // Undeploy deletes deployed objects from Kubernetes cluster func (k *Kubernetes) Undeploy(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) error { //Convert komposeObject - objects := k.Transform(komposeObject, opt) + objects, err := k.Transform(komposeObject, opt) + + if err != nil { + return errors.Wrap(err, "k.Transform failed") + } client, namespace, err := k.GetKubernetesClient() if err != nil { diff --git a/pkg/transformer/kubernetes/kubernetes_test.go b/pkg/transformer/kubernetes/kubernetes_test.go index b84bff5d..ec54ae81 100644 --- a/pkg/transformer/kubernetes/kubernetes_test.go +++ b/pkg/transformer/kubernetes/kubernetes_test.go @@ -27,10 +27,9 @@ import ( "github.com/kubernetes-incubator/kompose/pkg/kobject" "github.com/kubernetes-incubator/kompose/pkg/transformer" + "github.com/pkg/errors" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/extensions" - "os" - "os/exec" ) func newServiceConfig() kobject.ServiceConfig { @@ -239,7 +238,10 @@ func TestKomposeConvertIngress(t *testing.T) { } // Run Transform - objs := k.Transform(test.komposeObject, test.opt) + objs, err := k.Transform(test.komposeObject, test.opt) + if err != nil { + t.Error(errors.Wrap(err, "k.Transform failed")) + } // Check results for _, obj := range objs { @@ -282,7 +284,10 @@ func TestKomposeConvert(t *testing.T) { t.Log("Test case:", name) k := Kubernetes{} // Run Transform - objs := k.Transform(test.komposeObject, test.opt) + objs, err := k.Transform(test.komposeObject, test.opt) + if err != nil { + t.Error(errors.Wrap(err, "k.Transform failed")) + } if len(objs) != test.expectedNumObjs { t.Errorf("Expected %d objects returned, got %d", test.expectedNumObjs, len(objs)) } @@ -402,7 +407,10 @@ func TestConvertRestartOptions(t *testing.T) { for name, test := range testCases { t.Log("Test Case:", name) - objs := k.Transform(test.svc, opt) + objs, err := k.Transform(test.svc, opt) + if err != nil { + t.Error(errors.Wrap(err, "k.Transform failed")) + } if len(objs) != 1 { t.Errorf("Expected only one pod, more elements generated.") @@ -454,13 +462,6 @@ func TestUnsupportedKeys(t *testing.T) { } -// Here we are testing a function which results in `logus.Fatalf()` when a condition is met, which further call `os.Exit()` and exits the process. -// If we write a test in the usual way that will call the function, -// it will exit the process and the running process is actually the test process and our test would fail. -// So to test the function resulting in `os.Exit()` we need invoke go test again in a separate process through `exec.Command`, -// limiting execution to the TestRestartOnFailure test using `-test.run=TestRestartOnFailure` flag set. -// The `TestRestartOnFailure` doing is two things simultaneously, -// it is going to the be the test itself and second it will a be `subprocess` that the test runs. func TestRestartOnFailure(t *testing.T) { kobjectWithRestartOnFailure := newKomposeObject() @@ -483,24 +484,11 @@ func TestRestartOnFailure(t *testing.T) { for name, test := range testCase { t.Log("Test case:", name) k := Kubernetes{} - if os.Getenv("BE_CRASHER") == "1" { - k.Transform(test.komposeObject, test.opt) + _, err := k.Transform(test.komposeObject, test.opt) + if err == nil { + t.Errorf("Expected an error, got %v instead", err) } } - - // cmd := exec.Command(os.Args[0], "-test.run=TestRestartOnFailure") will execute the test binary - // with the flag -test.run=TestRestartOnFailure and set the environment variable BE_CRASHER=1. - cmd := exec.Command(os.Args[0], "-test.run=TestRestartOnFailure") - cmd.Env = append(os.Environ(), "BE_CRASHER=1") - - // err := cmd.Run() will re-execute the test binary and this time os.Getenv("BE_CRASHER") == "1" - // will return true and we can call o.Transform(test.komposeObject, test.opt). - // so that the test binary that calls itself and execute the code on behalf of the parent process. - err := cmd.Run() - if e, ok := err.(*exec.ExitError); ok && !e.Success() { - return - } - t.Fatalf("Process ran with err %v, want exit status 1", err) } func TestInitPodSpec(t *testing.T) { diff --git a/pkg/transformer/openshift/openshift.go b/pkg/transformer/openshift/openshift.go index 63cf5c0c..293d25d0 100644 --- a/pkg/transformer/openshift/openshift.go +++ b/pkg/transformer/openshift/openshift.go @@ -44,6 +44,7 @@ import ( deploymentconfigreaper "github.com/openshift/origin/pkg/deploy/cmd" imageapi "github.com/openshift/origin/pkg/image/api" routeapi "github.com/openshift/origin/pkg/route/api" + "github.com/pkg/errors" "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/util/intstr" ) @@ -170,10 +171,10 @@ func (o *OpenShift) initImageStream(name string, service kobject.ServiceConfig) } // initBuildConfig initialize Openshifts BuildConfig Object -func initBuildConfig(name string, service kobject.ServiceConfig, composeFileDir string, repo string, branch string) *buildapi.BuildConfig { +func initBuildConfig(name string, service kobject.ServiceConfig, composeFileDir string, repo string, branch string) (*buildapi.BuildConfig, error) { contextDir, err := getAbsBuildContext(service.Build, composeFileDir) if err != nil { - log.Fatalf("[%s] Buildconfig cannot be created due to error in creating build context.", name) + return nil, errors.Wrap(err, name+"buildconfig cannot be created due to error in creating build context, getAbsBuildContext failed") } bc := &buildapi.BuildConfig{ @@ -210,7 +211,7 @@ func initBuildConfig(name string, service kobject.ServiceConfig, composeFileDir }, }, } - return bc + return bc, nil } // initDeploymentConfig initialize OpenShifts DeploymentConfig object @@ -295,7 +296,7 @@ func (o *OpenShift) initRoute(name string, service kobject.ServiceConfig, port i // Transform maps komposeObject to openshift objects // returns objects that are already sorted in the way that Services are first -func (o *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) []runtime.Object { +func (o *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) ([]runtime.Object, error) { noSupKeys := o.Kubernetes.CheckUnsupportedKey(&komposeObject, unsupportedKey) for _, keyName := range noSupKeys { log.Warningf("OpenShift provider doesn't support %s key - ignoring", keyName) @@ -315,7 +316,7 @@ func (o *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.C if service.Restart == "no" || service.Restart == "on-failure" { // Error out if Controller Object is specified with restart: 'on-failure' if opt.IsDeploymentConfigFlag { - log.Fatalf("Controller object cannot be specified with restart: 'on-failure'") + return nil, errors.New("Controller object cannot be specified with restart: 'on-failure'") } pod := o.InitPod(name, service) objects = append(objects, pod) @@ -337,26 +338,30 @@ func (o *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.C continue } if !hasGitBinary() && (buildRepo == "" || buildBranch == "") { - log.Fatalf("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") + 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 { - log.Fatalf("Buildconfig cannot be created because current git branch couldn't be detected.") + return nil, errors.Wrap(err, "Buildconfig cannot be created because current git branch couldn't be detected.") } } if opt.BuildRepo == "" { if err != nil { - log.Fatalf("Buildconfig cannot be created because remote for current git branch couldn't be detected.") + 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 { - log.Fatalf("Buildconfig cannot be created because git remote origin repo couldn't be detected.") + return nil, errors.Wrap(err, "Buildconfig cannot be created because git remote origin repo couldn't be detected.") } } hasBuild = true } - objects = append(objects, initBuildConfig(name, service, composeFileDir, buildRepo, buildBranch)) // Openshift BuildConfigs + bc, err := initBuildConfig(name, service, composeFileDir, buildRepo, buildBranch) + if err != nil { + return nil, errors.Wrap(err, "initBuildConfig failed") + } + objects = append(objects, bc) // Openshift BuildConfigs } // If ports not provided in configuration we will not make service @@ -384,7 +389,7 @@ func (o *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.C o.VolumesFrom(&allobjects, komposeObject) // sort all object so Services are first o.SortServicesFirst(&allobjects) - return allobjects + return allobjects, nil } // Create OpenShift client, returns OpenShift client @@ -403,7 +408,12 @@ func (o *OpenShift) getOpenShiftClient() (*oclient.Client, error) { // Deploy transofrms and deploys kobject to OpenShift func (o *OpenShift) Deploy(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) error { //Convert komposeObject - objects := o.Transform(komposeObject, opt) + objects, err := o.Transform(komposeObject, opt) + + if err != nil { + return errors.Wrap(err, "o.Transform failed") + } + pvcStr := " " if !opt.EmptyVols { pvcStr = " and PersistentVolumeClaims " @@ -480,7 +490,11 @@ func (o *OpenShift) Deploy(komposeObject kobject.KomposeObject, opt kobject.Conv //Undeploy removes deployed artifacts from OpenShift cluster func (o *OpenShift) Undeploy(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) error { //Convert komposeObject - objects := o.Transform(komposeObject, opt) + objects, err := o.Transform(komposeObject, opt) + + if err != nil { + return errors.Wrap(err, "o.Transform failed") + } oclient, err := o.getOpenShiftClient() if err != nil { diff --git a/pkg/transformer/openshift/openshift_test.go b/pkg/transformer/openshift/openshift_test.go index 4df78dac..c366b812 100644 --- a/pkg/transformer/openshift/openshift_test.go +++ b/pkg/transformer/openshift/openshift_test.go @@ -29,7 +29,7 @@ import ( "github.com/kubernetes-incubator/kompose/pkg/kobject" "github.com/kubernetes-incubator/kompose/pkg/testutils" "github.com/kubernetes-incubator/kompose/pkg/transformer/kubernetes" - "os/exec" + "github.com/pkg/errors" ) func newServiceConfig() kobject.ServiceConfig { @@ -292,7 +292,10 @@ func TestInitBuildConfig(t *testing.T) { sc := kobject.ServiceConfig{ Build: "./build", } - bc := initBuildConfig(serviceName, sc, composeFileDir, repo, branch) + bc, err := initBuildConfig(serviceName, sc, composeFileDir, repo, branch) + if err != nil { + t.Error(errors.Wrap(err, "initBuildConfig failed")) + } testCases := map[string]struct { field string @@ -324,20 +327,16 @@ func TestServiceWithoutPort(t *testing.T) { } o := OpenShift{Kubernetes: kubernetes.Kubernetes{}} - objects := o.Transform(komposeObject, kobject.ConvertOptions{CreateD: true, Replicas: 1}) + objects, err := o.Transform(komposeObject, kobject.ConvertOptions{CreateD: true, Replicas: 1}) + if err != nil { + t.Error(errors.Wrap(err, "o.Transform failed")) + } if err := testutils.CheckForHeadless(objects); err != nil { t.Error(err) } } -// Here we are testing a function which results in `logus.Fatalf()` when a condition is met, which further call `os.Exit()` and exits the process. -// If we write a test in the usual way that will call the function, -// it will exit the process and the running process is actually the test process and our test would fail. -// So to test the function resulting in `os.Exit()` we need invoke go test again in a separate process through `exec.Command`, -// limiting execution to the TestRestartOnFailure test using `-test.run=TestRestartOnFailure` flag set. -// The `TestRestartOnFailure` doing is two things simultaneously, -// it is going to the be the test itself and second it will a be `subprocess` that the test runs. func TestRestartOnFailure(t *testing.T) { service := kobject.ServiceConfig{ @@ -361,24 +360,13 @@ func TestRestartOnFailure(t *testing.T) { for name, test := range testCase { t.Log("Test case:", name) o := OpenShift{} - if os.Getenv("BE_CRASHER") == "1" { - o.Transform(test.komposeObject, test.opt) + + _, err := o.Transform(test.komposeObject, test.opt) + if err == nil { + t.Errorf("Expected an error, got %v instead", err) } } - // cmd := exec.Command(os.Args[0], "-test.run=TestRestartOnFailure") will execute the test binary - // with the flag -test.run=TestRestartOnFailure and set the environment variable BE_CRASHER=1 - cmd := exec.Command(os.Args[0], "-test.run=TestRestartOnFailure") - cmd.Env = append(os.Environ(), "BE_CRASHER=1") - - // err := cmd.Run() will re-execute the test binary and this time os.Getenv("BE_CRASHER") == "1". - // will return true and we can call o.Transform(test.komposeObject, test.opt). - // so that the test binary that calls itself and execute the code on behalf of the parent process. - err := cmd.Run() - if e, ok := err.(*exec.ExitError); ok && !e.Success() { - return - } - t.Fatalf("Process ran with err %v, want exit status 1", err) } // Tests if deployment strategy is being set to Recreate when volumes are @@ -395,7 +383,10 @@ func TestRecreateStrategyWithVolumesPresent(t *testing.T) { o := OpenShift{Kubernetes: kubernetes.Kubernetes{}} - objects := o.Transform(komposeObject, kobject.ConvertOptions{CreateDeploymentConfig: true, Replicas: 1}) + objects, err := o.Transform(komposeObject, kobject.ConvertOptions{CreateDeploymentConfig: true, Replicas: 1}) + if err != nil { + t.Error(errors.Wrap(err, "o.Transform failed")) + } for _, obj := range objects { if deploymentConfig, ok := obj.(*deployapi.DeploymentConfig); ok { if deploymentConfig.Spec.Strategy.Type != deployapi.DeploymentStrategyTypeRecreate { diff --git a/pkg/transformer/transformer.go b/pkg/transformer/transformer.go index 3ae765a5..9919ee31 100644 --- a/pkg/transformer/transformer.go +++ b/pkg/transformer/transformer.go @@ -24,7 +24,7 @@ import ( // Transformer interface defines transformer that is converting kobject to other resources type Transformer interface { // Transform converts KomposeObject to transformer specific objects. - Transform(kobject.KomposeObject, kobject.ConvertOptions) []runtime.Object + Transform(kobject.KomposeObject, kobject.ConvertOptions) ([]runtime.Object, error) // Deploy deploys KomposeObject to provider Deploy(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) error // Undeploy deletes/undeploys KomposeObject from provider diff --git a/pkg/transformer/utils.go b/pkg/transformer/utils.go index c9e4bd7c..c5ebfc18 100644 --- a/pkg/transformer/utils.go +++ b/pkg/transformer/utils.go @@ -30,6 +30,7 @@ import ( "path/filepath" + "github.com/pkg/errors" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/runtime" @@ -47,16 +48,16 @@ func RandStringBytes(n int) string { } // CreateOutFile creates the file to write to if --out is specified -func CreateOutFile(out string) *os.File { +func CreateOutFile(out string) (*os.File, error) { var f *os.File var err error if len(out) != 0 { f, err = os.Create(out) if err != nil { - log.Fatalf("error creating file: %v", err) + return nil, errors.Wrap(err, "error creating file, os.Create failed") } } - return f + return f, nil } // ParseVolume parses a given volume, which might be [name:][host:]container[:access_mode] @@ -151,7 +152,7 @@ func TransformData(obj runtime.Object, GenerateJSON bool) ([]byte, error) { } // Print either prints to stdout or to file/s -func Print(name, path string, trailing string, data []byte, toStdout, generateJSON bool, f *os.File) string { +func Print(name, path string, trailing string, data []byte, toStdout, generateJSON bool, f *os.File) (string, error) { file := "" if generateJSON { file = fmt.Sprintf("%s-%s.json", name, trailing) @@ -160,20 +161,20 @@ func Print(name, path string, trailing string, data []byte, toStdout, generateJS } if toStdout { fmt.Fprintf(os.Stdout, "%s\n", string(data)) - return "" + return "", nil } else if f != nil { // Write all content to a single file f if _, err := f.WriteString(fmt.Sprintf("%s\n", string(data))); err != nil { - log.Fatalf("Failed to write %s to file: %v", trailing, err) + return "", errors.Wrap(err, "f.WriteString failed, Failed to write %s to file: "+trailing) } f.Sync() } else { // Write content separately to each file file = filepath.Join(path, file) if err := ioutil.WriteFile(file, []byte(data), 0644); err != nil { - log.Fatalf("Failed to write %s: %v", trailing, err) + return "", errors.Wrap(err, "Failed to write %s: "+trailing) } log.Printf("file %q created", file) } - return file + return file, nil }