return errors instead of logrus.Fatal calls

This commit refactors the code to remove more or less
all occurences of logrus.Fatalf() from the code under
pkg/ except for app.go where all the errors are being
handled currently.

This is being done since random logrus.Fatalf() calls
all around the code was making handling the errors,
unit testing and troubleshooting a bit more painful.

logrus.Fatalf() calls are either replaced by
return errors.New("new error")
or
return errors.Wrap(err, "annonate error")
calls, and the function signatures are accordingly
changed to accomodate the new return values.

The unit tests which previously used to check
if logrus.Fatalf() calls worked fine have also
been fixed to only check for errors now.

Fixes #416
This commit is contained in:
Shubham Minglani 2017-02-20 18:51:40 +05:30
parent d05e8c522a
commit 5cb598fa5b
13 changed files with 288 additions and 164 deletions

View File

@ -35,6 +35,7 @@ import (
"os" "os"
"github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus"
"github.com/kubernetes-incubator/kompose/pkg/kobject" "github.com/kubernetes-incubator/kompose/pkg/kobject"
"github.com/kubernetes-incubator/kompose/pkg/loader" "github.com/kubernetes-incubator/kompose/pkg/loader"
"github.com/kubernetes-incubator/kompose/pkg/transformer" "github.com/kubernetes-incubator/kompose/pkg/transformer"
@ -211,16 +212,26 @@ func Convert(opt kobject.ConvertOptions) {
komposeObject := kobject.KomposeObject{ komposeObject := kobject.KomposeObject{
ServiceConfigs: make(map[string]kobject.ServiceConfig), 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 // Get a transformer that maps komposeObject to provider's primitives
t := getTransformer(opt) t := getTransformer(opt)
// Do the transformation // Do the transformation
objects := t.Transform(komposeObject, opt) objects, err := t.Transform(komposeObject, opt)
if err != nil {
logrus.Fatalf(err.Error())
}
// Print output // Print output
kubernetes.PrintList(objects, opt) err = kubernetes.PrintList(objects, opt)
if err != nil {
logrus.Fatalf(err.Error())
}
} }
// Up brings up deployment, svc. // Up brings up deployment, svc.
@ -237,7 +248,10 @@ func Up(opt kobject.ConvertOptions) {
komposeObject := kobject.KomposeObject{ komposeObject := kobject.KomposeObject{
ServiceConfigs: make(map[string]kobject.ServiceConfig), 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 // Get the transformer
t := getTransformer(opt) t := getTransformer(opt)
@ -263,7 +277,10 @@ func Down(opt kobject.ConvertOptions) {
komposeObject := kobject.KomposeObject{ komposeObject := kobject.KomposeObject{
ServiceConfigs: make(map[string]kobject.ServiceConfig), 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 // Get the transformer
t := getTransformer(opt) t := getTransformer(opt)

View File

@ -29,6 +29,7 @@ import (
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
"github.com/fatih/structs" "github.com/fatih/structs"
"github.com/kubernetes-incubator/kompose/pkg/kobject" "github.com/kubernetes-incubator/kompose/pkg/kobject"
"github.com/pkg/errors"
) )
// Bundle is docker bundle file loader, implements Loader interface // Bundle is docker bundle file loader, implements Loader interface
@ -106,16 +107,16 @@ func checkUnsupportedKey(bundleStruct *Bundlefile) []string {
} }
// load image from dab file // load image from dab file
func loadImage(service Service) (string, string) { func loadImage(service Service) (string, error) {
character := "@" character := "@"
if strings.Contains(service.Image, 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 // load environment variables from dab file
func loadEnvVars(service Service) ([]kobject.EnvVar, string) { func loadEnvVars(service Service) ([]kobject.EnvVar, error) {
envs := []kobject.EnvVar{} envs := []kobject.EnvVar{}
for _, env := range service.Env { for _, env := range service.Env {
character := "=" character := "="
@ -144,15 +145,15 @@ func loadEnvVars(service Service) ([]kobject.EnvVar, string) {
Value: value, Value: value,
}) })
} else { } else {
return envs, "Invalid container env " + env return envs, errors.New("Invalid container env")
} }
} }
} }
return envs, "" return envs, nil
} }
// load ports from dab file // load ports from dab file
func loadPorts(service Service) ([]kobject.Ports, string) { func loadPorts(service Service) ([]kobject.Ports, error) {
ports := []kobject.Ports{} ports := []kobject.Ports{}
for _, port := range service.Ports { for _, port := range service.Ports {
var p api.Protocol var p api.Protocol
@ -170,11 +171,11 @@ func loadPorts(service Service) ([]kobject.Ports, string) {
Protocol: p, Protocol: p,
}) })
} }
return ports, "" return ports, nil
} }
// LoadFile loads dab file into KomposeObject // 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{ komposeObject := kobject.KomposeObject{
ServiceConfigs: make(map[string]kobject.ServiceConfig), ServiceConfigs: make(map[string]kobject.ServiceConfig),
LoadedFrom: "bundle", LoadedFrom: "bundle",
@ -182,12 +183,12 @@ func (b *Bundle) LoadFile(files []string) kobject.KomposeObject {
file := files[0] file := files[0]
buf, err := ioutil.ReadFile(file) buf, err := ioutil.ReadFile(file)
if err != nil { 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)) reader := strings.NewReader(string(buf))
bundle, err := loadFile(reader) bundle, err := loadFile(reader)
if err != nil { 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) noSupKeys := checkUnsupportedKey(bundle)
@ -204,20 +205,20 @@ func (b *Bundle) LoadFile(files []string) kobject.KomposeObject {
serviceConfig.Annotations = service.Labels serviceConfig.Annotations = service.Labels
image, err := loadImage(service) image, err := loadImage(service)
if err != "" { if err != nil {
log.Fatalf("Failed to load image from bundles file: " + err) return kobject.KomposeObject{}, errors.Wrap(err, "loadImage failed, Failed to load image from bundles file")
} }
serviceConfig.Image = image serviceConfig.Image = image
envs, err := loadEnvVars(service) envs, err := loadEnvVars(service)
if err != "" { if err != nil {
log.Fatalf("Failed to load envvar from bundles file: " + err) return kobject.KomposeObject{}, errors.Wrap(err, "loadEnvVars failed, Failed to load envvar from bundles file")
} }
serviceConfig.Environment = envs serviceConfig.Environment = envs
ports, err := loadPorts(service) ports, err := loadPorts(service)
if err != "" { if err != nil {
log.Fatalf("Failed to load ports from bundles file: " + err) return kobject.KomposeObject{}, errors.Wrap(err, "loadPorts failed, Failed to load ports from bundles file")
} }
serviceConfig.Port = ports serviceConfig.Port = ports
@ -228,7 +229,7 @@ func (b *Bundle) LoadFile(files []string) kobject.KomposeObject {
komposeObject.ServiceConfigs[name] = serviceConfig komposeObject.ServiceConfigs[name] = serviceConfig
} }
return komposeObject return komposeObject, nil
} }
// LoadFile loads a bundlefile from a path to the file // LoadFile loads a bundlefile from a path to the file

View File

@ -33,6 +33,7 @@ import (
"github.com/docker/libcompose/project" "github.com/docker/libcompose/project"
"github.com/fatih/structs" "github.com/fatih/structs"
"github.com/kubernetes-incubator/kompose/pkg/kobject" "github.com/kubernetes-incubator/kompose/pkg/kobject"
"github.com/pkg/errors"
) )
// Compose is docker compose file loader, implements Loader interface // 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 // 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{ komposeObject := kobject.KomposeObject{
ServiceConfigs: make(map[string]kobject.ServiceConfig), ServiceConfigs: make(map[string]kobject.ServiceConfig),
LoadedFrom: "compose", LoadedFrom: "compose",
@ -290,7 +291,7 @@ func (c *Compose) LoadFile(files []string) kobject.KomposeObject {
if context.EnvironmentLookup == nil { if context.EnvironmentLookup == nil {
cwd, err := os.Getwd() cwd, err := os.Getwd()
if err != nil { if err != nil {
return kobject.KomposeObject{} return kobject.KomposeObject{}, nil
} }
context.EnvironmentLookup = &lookup.ComposableEnvLookup{ context.EnvironmentLookup = &lookup.ComposableEnvLookup{
Lookups: []config.EnvironmentLookup{ Lookups: []config.EnvironmentLookup{
@ -306,7 +307,7 @@ func (c *Compose) LoadFile(files []string) kobject.KomposeObject {
composeObject := project.NewProject(context, nil, nil) composeObject := project.NewProject(context, nil, nil)
err := composeObject.Parse() err := composeObject.Parse()
if err != nil { 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) noSupKeys := checkUnsupportedKey(composeObject)
@ -329,7 +330,7 @@ func (c *Compose) LoadFile(files []string) kobject.KomposeObject {
// load ports // load ports
ports, err := loadPorts(composeServiceConfig.Ports) ports, err := loadPorts(composeServiceConfig.Ports)
if err != nil { 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 serviceConfig.Port = ports
@ -347,7 +348,12 @@ func (c *Compose) LoadFile(files []string) kobject.KomposeObject {
for key, value := range composeServiceConfig.Labels { for key, value := range composeServiceConfig.Labels {
switch key { switch key {
case "kompose.service.type": 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": case "kompose.service.expose":
serviceConfig.ExposeService = strings.ToLower(value) serviceConfig.ExposeService = strings.ToLower(value)
} }
@ -373,20 +379,19 @@ func (c *Compose) LoadFile(files []string) kobject.KomposeObject {
komposeObject.ServiceConfigs[name] = serviceConfig komposeObject.ServiceConfigs[name] = serviceConfig
} }
return komposeObject return komposeObject, nil
} }
func handleServiceType(ServiceType string) string { func handleServiceType(ServiceType string) (string, error) {
switch strings.ToLower(ServiceType) { switch strings.ToLower(ServiceType) {
case "", "clusterip": case "", "clusterip":
return string(api.ServiceTypeClusterIP) return string(api.ServiceTypeClusterIP), nil
case "nodeport": case "nodeport":
return string(api.ServiceTypeNodePort) return string(api.ServiceTypeNodePort), nil
case "loadbalancer": case "loadbalancer":
return string(api.ServiceTypeLoadBalancer) return string(api.ServiceTypeLoadBalancer), nil
default: default:
log.Fatalf("Unknown value '%s', supported values are 'NodePort, ClusterIP or LoadBalancer'", ServiceType) return "", errors.New("Unknown value " + ServiceType + " , supported values are 'NodePort, ClusterIP or LoadBalancer'")
return ""
} }
} }

View File

@ -28,6 +28,7 @@ import (
"github.com/docker/libcompose/config" "github.com/docker/libcompose/config"
"github.com/docker/libcompose/project" "github.com/docker/libcompose/project"
"github.com/docker/libcompose/yaml" "github.com/docker/libcompose/yaml"
"github.com/pkg/errors"
) )
// Test if service types are parsed properly on user input // Test if service types are parsed properly on user input
@ -47,7 +48,10 @@ func TestHandleServiceType(t *testing.T) {
} }
for _, tt := range tests { 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 { if result != tt.serviceType {
t.Errorf("Expected %q, got %q", tt.serviceType, result) t.Errorf("Expected %q, got %q", tt.serviceType, result)
} }

View File

@ -26,7 +26,7 @@ import (
// Loader interface defines loader that loads files and converts it to kobject representation // Loader interface defines loader that loads files and converts it to kobject representation
type Loader interface { type Loader interface {
LoadFile(files []string) kobject.KomposeObject LoadFile(files []string) (kobject.KomposeObject, error)
///Name() string ///Name() string
} }

View File

@ -39,6 +39,7 @@ import (
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
deployapi "github.com/openshift/origin/pkg/deploy/api" deployapi "github.com/openshift/origin/pkg/deploy/api"
"github.com/pkg/errors"
"k8s.io/kubernetes/pkg/api/resource" "k8s.io/kubernetes/pkg/api/resource"
) )
@ -92,7 +93,7 @@ home:
t, err := template.New("ChartTmpl").Parse(chart) t, err := template.New("ChartTmpl").Parse(chart)
if err != nil { 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 var chartData bytes.Buffer
_ = t.Execute(&chartData, details) _ = t.Execute(&chartData, details)
@ -127,26 +128,26 @@ func cpFileToChart(manifestDir, filename string) error {
} }
// Check if given path is a directory // Check if given path is a directory
func isDir(name string) bool { func isDir(name string) (bool, error) {
// Open file to get stat later // Open file to get stat later
f, err := os.Open(name) f, err := os.Open(name)
if err != nil { if err != nil {
return false return false, nil
} }
defer f.Close() defer f.Close()
// Get file attributes and information // Get file attributes and information
fileStat, err := f.Stat() fileStat, err := f.Stat()
if err != nil { 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 // Check if given path is a directory
if fileStat.IsDir() { 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 // 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 var dirName string
// Check if output file is a directory // 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 dirName = opt.OutFile
} else { } 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() defer f.Close()
} }
@ -189,7 +197,11 @@ func PrintList(objects []runtime.Object, opt kobject.ConvertOptions) error {
if err != nil { if err != nil {
return fmt.Errorf("Error in marshalling the List: %v", err) 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 { } else {
var file string var file string
// create a separate file for each provider // 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 // cast it to correct type - api.ObjectMeta
objectMeta := val.FieldByName("ObjectMeta").Interface().(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) files = append(files, file)
} }
} }
if opt.CreateChart { if opt.CreateChart {
generateHelm(opt.InputFiles, files) err = generateHelm(opt.InputFiles, files)
if err != nil {
return errors.Wrap(err, "generateHelm failed")
}
} }
return nil return nil
} }
@ -309,12 +327,16 @@ func (k *Kubernetes) CreateHeadlessService(name string, service kobject.ServiceC
} }
// UpdateKubernetesObjects loads configurations to k8s objects // 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. // Configure the environment variables.
envs := k.ConfigEnvs(name, service) envs := k.ConfigEnvs(name, service)
// Configure the container volumes. // 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 { if pvc != nil {
// Looping on the slice pvc instead of `*objects = append(*objects, pvc...)` // Looping on the slice pvc instead of `*objects = append(*objects, pvc...)`
// because the type of objects and pvc is different, but when doing append // 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) annotations := transformer.ConfigAnnotations(service)
// fillTemplate fills the pod template with the value calculated from config // 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 { if len(service.ContainerName) > 0 {
template.Spec.Containers[0].Name = service.ContainerName template.Spec.Containers[0].Name = service.ContainerName
} }
@ -384,8 +406,9 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic
case "on-failure": case "on-failure":
template.Spec.RestartPolicy = api.RestartPolicyOnFailure template.Spec.RestartPolicy = api.RestartPolicyOnFailure
default: 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 // 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 // update supported controller
for _, obj := range *objects { 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 { if len(service.Volumes) > 0 {
switch objType := obj.(type) { switch objType := obj.(type) {
case *extensions.Deployment: 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 // 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 *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 // Get all the volumes and volumemounts this particular service is dependent on
for _, dependentSvc := range komposeObject.ServiceConfigs[svcname].VolumesFrom { 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...) volumes = append(volumes, vols...)
volumeMounts = append(volumeMounts, volMounts...) volumeMounts = append(volumeMounts, volMounts...)
} }
// add the volumes info of this service // 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...) volumes = append(volumes, vols...)
volumeMounts = append(volumeMounts, volMounts...) volumeMounts = append(volumeMounts, volMounts...)
return return volumes, volumeMounts, nil
} }
// VolumesFrom creates volums and volumeMounts for volumes_from // 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 { for _, obj := range *objects {
switch t := obj.(type) { switch t := obj.(type) {
case *api.ReplicationController: case *api.ReplicationController:
svcName := t.ObjectMeta.Name svcName := t.ObjectMeta.Name
for _, dependentSvc := range komposeObject.ServiceConfigs[svcName].VolumesFrom { 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.Volumes = append(t.Spec.Template.Spec.Volumes, volumes...)
t.Spec.Template.Spec.Containers[0].VolumeMounts = append(t.Spec.Template.Spec.Containers[0].VolumeMounts, volumeMounts...) t.Spec.Template.Spec.Containers[0].VolumeMounts = append(t.Spec.Template.Spec.Containers[0].VolumeMounts, volumeMounts...)
} }
case *extensions.Deployment: case *extensions.Deployment:
svcName := t.ObjectMeta.Name svcName := t.ObjectMeta.Name
for _, dependentSvc := range komposeObject.ServiceConfigs[svcName].VolumesFrom { 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.Volumes = append(t.Spec.Template.Spec.Volumes, volumes...)
t.Spec.Template.Spec.Containers[0].VolumeMounts = append(t.Spec.Template.Spec.Containers[0].VolumeMounts, volumeMounts...) t.Spec.Template.Spec.Containers[0].VolumeMounts = append(t.Spec.Template.Spec.Containers[0].VolumeMounts, volumeMounts...)
} }
case *extensions.DaemonSet: case *extensions.DaemonSet:
svcName := t.ObjectMeta.Name svcName := t.ObjectMeta.Name
for _, dependentSvc := range komposeObject.ServiceConfigs[svcName].VolumesFrom { 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.Volumes = append(t.Spec.Template.Spec.Volumes, volumes...)
t.Spec.Template.Spec.Containers[0].VolumeMounts = append(t.Spec.Template.Spec.Containers[0].VolumeMounts, volumeMounts...) t.Spec.Template.Spec.Containers[0].VolumeMounts = append(t.Spec.Template.Spec.Containers[0].VolumeMounts, volumeMounts...)
} }
case *deployapi.DeploymentConfig: case *deployapi.DeploymentConfig:
svcName := t.ObjectMeta.Name svcName := t.ObjectMeta.Name
for _, dependentSvc := range komposeObject.ServiceConfigs[svcName].VolumesFrom { 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.Volumes = append(t.Spec.Template.Spec.Volumes, volumes...)
t.Spec.Template.Spec.Containers[0].VolumeMounts = append(t.Spec.Template.Spec.Containers[0].VolumeMounts, volumeMounts...) t.Spec.Template.Spec.Containers[0].VolumeMounts = append(t.Spec.Template.Spec.Containers[0].VolumeMounts, volumeMounts...)
} }
} }
} }
return nil
} }

View File

@ -26,6 +26,7 @@ import (
"github.com/kubernetes-incubator/kompose/pkg/kobject" "github.com/kubernetes-incubator/kompose/pkg/kobject"
"github.com/kubernetes-incubator/kompose/pkg/testutils" "github.com/kubernetes-incubator/kompose/pkg/testutils"
"github.com/pkg/errors"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
) )
@ -63,7 +64,10 @@ func TestCreateService(t *testing.T) {
ServiceConfigs: map[string]kobject.ServiceConfig{"app": service}, ServiceConfigs: map[string]kobject.ServiceConfig{"app": service},
} }
k := Kubernetes{} 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 // Test the creation of the service
svc := k.CreateService("foo", service, objects) svc := k.CreateService("foo", service, objects)
@ -107,7 +111,10 @@ func TestCreateServiceWithMemLimit(t *testing.T) {
ServiceConfigs: map[string]kobject.ServiceConfig{"app": service}, ServiceConfigs: map[string]kobject.ServiceConfig{"app": service},
} }
k := Kubernetes{} 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 // Retrieve the deployment object and test that it matches the MemLimit value
for _, obj := range objects { for _, obj := range objects {
@ -155,7 +162,10 @@ func TestCreateServiceWithServiceUser(t *testing.T) {
} }
k := Kubernetes{} 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 { for _, obj := range objects {
if deploy, ok := obj.(*extensions.Deployment); ok { if deploy, ok := obj.(*extensions.Deployment); ok {
@ -188,19 +198,28 @@ func TestIsDir(t *testing.T) {
f.Close() f.Close()
// Check output if directory exists // 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 { if output != true {
t.Errorf("directory %v exists but isDir() returned %v", tempDir, output) t.Errorf("directory %v exists but isDir() returned %v", tempDir, output)
} }
// Check output if file is provided // 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 { if output != false {
t.Errorf("%v is a file but isDir() returned %v", tempDir, output) t.Errorf("%v is a file but isDir() returned %v", tempDir, output)
} }
// Check output if path does not exist // 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 { if output != false {
t.Errorf("Directory %v does not exist, but isDir() returned %v", tempAbsentDirPath, output) t.Errorf("Directory %v does not exist, but isDir() returned %v", tempAbsentDirPath, output)
} }
@ -224,7 +243,10 @@ func TestServiceWithoutPort(t *testing.T) {
} }
k := Kubernetes{} 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 { if err := testutils.CheckForHeadless(objects); err != nil {
t.Error(err) t.Error(err)
} }
@ -245,7 +267,10 @@ func TestRecreateStrategyWithVolumesPresent(t *testing.T) {
} }
k := Kubernetes{} 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 { for _, obj := range objects {
if deployment, ok := obj.(*extensions.Deployment); ok { if deployment, ok := obj.(*extensions.Deployment); ok {
if deployment.Spec.Strategy.Type != extensions.RecreateDeploymentStrategyType { if deployment.Spec.Strategy.Type != extensions.RecreateDeploymentStrategyType {

View File

@ -46,6 +46,7 @@ import (
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/intstr" "k8s.io/kubernetes/pkg/util/intstr"
//"k8s.io/kubernetes/pkg/controller/daemon" //"k8s.io/kubernetes/pkg/controller/daemon"
"github.com/pkg/errors"
) )
// Kubernetes implements Transformer interface and represents Kubernetes transformer // 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 // 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") size, err := resource.ParseQuantity("100Mi")
if err != nil { if err != nil {
log.Fatalf("Error parsing size") return nil, errors.Wrap(err, "resource.ParseQuantity failed, Error parsing size")
} }
pvc := &api.PersistentVolumeClaim{ pvc := &api.PersistentVolumeClaim{
@ -265,7 +266,7 @@ func (k *Kubernetes) CreatePVC(name string, mode string) *api.PersistentVolumeCl
} else { } else {
pvc.Spec.AccessModes = []api.PersistentVolumeAccessMode{api.ReadWriteOnce} pvc.Spec.AccessModes = []api.PersistentVolumeAccessMode{api.ReadWriteOnce}
} }
return pvc return pvc, nil
} }
// ConfigPorts configures the container ports. // ConfigPorts configures the container ports.
@ -324,7 +325,7 @@ func (k *Kubernetes) ConfigServicePorts(name string, service kobject.ServiceConf
} }
// ConfigVolumes configure the container volumes. // 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{} volumeMounts := []api.VolumeMount{}
volumes := []api.Volume{} volumes := []api.Volume{}
var PVCs []*api.PersistentVolumeClaim var PVCs []*api.PersistentVolumeClaim
@ -371,7 +372,13 @@ func (k *Kubernetes) ConfigVolumes(name string, service kobject.ServiceConfig) (
volsource = k.ConfigEmptyVolumeSource() volsource = k.ConfigEmptyVolumeSource()
} else { } else {
volsource = k.ConfigPVCVolumeSource(volumeName, readonly) 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 // 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) 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 // 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 // Transform maps komposeObject to k8s objects
// returns object that are already sorted in the way that Services are first // 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) noSupKeys := k.CheckUnsupportedKey(&komposeObject, unsupportedKey)
for _, keyName := range noSupKeys { 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" { if service.Restart == "no" || service.Restart == "on-failure" {
// Error out if Controller Object is specified with restart: 'on-failure' // Error out if Controller Object is specified with restart: 'on-failure'
if opt.IsDeploymentFlag || opt.IsDaemonSetFlag || opt.IsReplicationControllerFlag { 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) pod := k.InitPod(name, service)
objects = append(objects, pod) objects = append(objects, pod)
@ -505,38 +512,54 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.
k.VolumesFrom(&allobjects, komposeObject) k.VolumesFrom(&allobjects, komposeObject)
// sort all object so Services are first // sort all object so Services are first
k.SortServicesFirst(&allobjects) k.SortServicesFirst(&allobjects)
return allobjects return allobjects, nil
} }
// UpdateController updates the given object with the given pod template update function and ObjectMeta update function // 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) { switch t := obj.(type) {
case *api.ReplicationController: case *api.ReplicationController:
if t.Spec.Template == nil { if t.Spec.Template == nil {
t.Spec.Template = &api.PodTemplateSpec{} 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) updateMeta(&t.ObjectMeta)
case *extensions.Deployment: case *extensions.Deployment:
updateTemplate(&t.Spec.Template) err = updateTemplate(&t.Spec.Template)
if err != nil {
return errors.Wrap(err, "updateTemplate failed")
}
updateMeta(&t.ObjectMeta) updateMeta(&t.ObjectMeta)
case *extensions.DaemonSet: case *extensions.DaemonSet:
updateTemplate(&t.Spec.Template) err = updateTemplate(&t.Spec.Template)
if err != nil {
return errors.Wrap(err, "updateTemplate failed")
}
updateMeta(&t.ObjectMeta) updateMeta(&t.ObjectMeta)
case *deployapi.DeploymentConfig: case *deployapi.DeploymentConfig:
updateTemplate(t.Spec.Template) err = updateTemplate(t.Spec.Template)
if err != nil {
return errors.Wrap(err, "updateTemplate failed")
}
updateMeta(&t.ObjectMeta) updateMeta(&t.ObjectMeta)
case *api.Pod: case *api.Pod:
p := api.PodTemplateSpec{ p := api.PodTemplateSpec{
ObjectMeta: t.ObjectMeta, ObjectMeta: t.ObjectMeta,
Spec: t.Spec, Spec: t.Spec,
} }
updateTemplate(&p) err = updateTemplate(&p)
if err != nil {
return errors.Wrap(err, "updateTemplate failed")
}
t.Spec = p.Spec t.Spec = p.Spec
t.ObjectMeta = p.ObjectMeta t.ObjectMeta = p.ObjectMeta
case *buildapi.BuildConfig: case *buildapi.BuildConfig:
updateMeta(&t.ObjectMeta) updateMeta(&t.ObjectMeta)
} }
return nil
} }
// GetKubernetesClient creates the k8s Client, returns k8s client and namespace // 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 // Deploy submits deployment and svc to k8s endpoint
func (k *Kubernetes) Deploy(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) error { func (k *Kubernetes) Deploy(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) error {
//Convert komposeObject //Convert komposeObject
objects := k.Transform(komposeObject, opt) objects, err := k.Transform(komposeObject, opt)
if err != nil {
return errors.Wrap(err, "k.Transform failed")
}
pvcStr := " " pvcStr := " "
if !opt.EmptyVols { if !opt.EmptyVols {
@ -622,7 +649,11 @@ func (k *Kubernetes) Deploy(komposeObject kobject.KomposeObject, opt kobject.Con
// Undeploy deletes deployed objects from Kubernetes cluster // Undeploy deletes deployed objects from Kubernetes cluster
func (k *Kubernetes) Undeploy(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) error { func (k *Kubernetes) Undeploy(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) error {
//Convert komposeObject //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() client, namespace, err := k.GetKubernetesClient()
if err != nil { if err != nil {

View File

@ -27,10 +27,9 @@ import (
"github.com/kubernetes-incubator/kompose/pkg/kobject" "github.com/kubernetes-incubator/kompose/pkg/kobject"
"github.com/kubernetes-incubator/kompose/pkg/transformer" "github.com/kubernetes-incubator/kompose/pkg/transformer"
"github.com/pkg/errors"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
"os"
"os/exec"
) )
func newServiceConfig() kobject.ServiceConfig { func newServiceConfig() kobject.ServiceConfig {
@ -239,7 +238,10 @@ func TestKomposeConvertIngress(t *testing.T) {
} }
// Run Transform // 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 // Check results
for _, obj := range objs { for _, obj := range objs {
@ -282,7 +284,10 @@ func TestKomposeConvert(t *testing.T) {
t.Log("Test case:", name) t.Log("Test case:", name)
k := Kubernetes{} k := Kubernetes{}
// Run Transform // 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 { if len(objs) != test.expectedNumObjs {
t.Errorf("Expected %d objects returned, got %d", test.expectedNumObjs, len(objs)) 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 { for name, test := range testCases {
t.Log("Test Case:", name) 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 { if len(objs) != 1 {
t.Errorf("Expected only one pod, more elements generated.") 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) { func TestRestartOnFailure(t *testing.T) {
kobjectWithRestartOnFailure := newKomposeObject() kobjectWithRestartOnFailure := newKomposeObject()
@ -483,24 +484,11 @@ func TestRestartOnFailure(t *testing.T) {
for name, test := range testCase { for name, test := range testCase {
t.Log("Test case:", name) t.Log("Test case:", name)
k := Kubernetes{} k := Kubernetes{}
if os.Getenv("BE_CRASHER") == "1" { _, err := k.Transform(test.komposeObject, test.opt)
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) { func TestInitPodSpec(t *testing.T) {

View File

@ -44,6 +44,7 @@ import (
deploymentconfigreaper "github.com/openshift/origin/pkg/deploy/cmd" deploymentconfigreaper "github.com/openshift/origin/pkg/deploy/cmd"
imageapi "github.com/openshift/origin/pkg/image/api" imageapi "github.com/openshift/origin/pkg/image/api"
routeapi "github.com/openshift/origin/pkg/route/api" routeapi "github.com/openshift/origin/pkg/route/api"
"github.com/pkg/errors"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/util/intstr" "k8s.io/kubernetes/pkg/util/intstr"
) )
@ -170,10 +171,10 @@ func (o *OpenShift) initImageStream(name string, service kobject.ServiceConfig)
} }
// initBuildConfig initialize Openshifts BuildConfig Object // 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) contextDir, err := getAbsBuildContext(service.Build, composeFileDir)
if err != nil { 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{ 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 // 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 // Transform maps komposeObject to openshift objects
// returns objects that are already sorted in the way that Services are first // 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) noSupKeys := o.Kubernetes.CheckUnsupportedKey(&komposeObject, unsupportedKey)
for _, keyName := range noSupKeys { for _, keyName := range noSupKeys {
log.Warningf("OpenShift provider doesn't support %s key - ignoring", keyName) 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" { if service.Restart == "no" || service.Restart == "on-failure" {
// Error out if Controller Object is specified with restart: 'on-failure' // Error out if Controller Object is specified with restart: 'on-failure'
if opt.IsDeploymentConfigFlag { 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) pod := o.InitPod(name, service)
objects = append(objects, pod) objects = append(objects, pod)
@ -337,26 +338,30 @@ func (o *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.C
continue continue
} }
if !hasGitBinary() && (buildRepo == "" || buildBranch == "") { 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 == "" { if buildBranch == "" {
buildBranch, err = getGitCurrentBranch(composeFileDir) buildBranch, err = getGitCurrentBranch(composeFileDir)
if err != nil { 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 opt.BuildRepo == "" {
if err != nil { 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) buildRepo, err = getGitCurrentRemoteURL(composeFileDir)
if err != nil { 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 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 // 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) o.VolumesFrom(&allobjects, komposeObject)
// sort all object so Services are first // sort all object so Services are first
o.SortServicesFirst(&allobjects) o.SortServicesFirst(&allobjects)
return allobjects return allobjects, nil
} }
// Create OpenShift client, returns OpenShift client // Create OpenShift client, returns OpenShift client
@ -403,7 +408,12 @@ func (o *OpenShift) getOpenShiftClient() (*oclient.Client, error) {
// Deploy transofrms and deploys kobject to OpenShift // Deploy transofrms and deploys kobject to OpenShift
func (o *OpenShift) Deploy(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) error { func (o *OpenShift) Deploy(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) error {
//Convert komposeObject //Convert komposeObject
objects := o.Transform(komposeObject, opt) objects, err := o.Transform(komposeObject, opt)
if err != nil {
return errors.Wrap(err, "o.Transform failed")
}
pvcStr := " " pvcStr := " "
if !opt.EmptyVols { if !opt.EmptyVols {
pvcStr = " and PersistentVolumeClaims " pvcStr = " and PersistentVolumeClaims "
@ -480,7 +490,11 @@ func (o *OpenShift) Deploy(komposeObject kobject.KomposeObject, opt kobject.Conv
//Undeploy removes deployed artifacts from OpenShift cluster //Undeploy removes deployed artifacts from OpenShift cluster
func (o *OpenShift) Undeploy(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) error { func (o *OpenShift) Undeploy(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) error {
//Convert komposeObject //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() oclient, err := o.getOpenShiftClient()
if err != nil { if err != nil {

View File

@ -29,7 +29,7 @@ import (
"github.com/kubernetes-incubator/kompose/pkg/kobject" "github.com/kubernetes-incubator/kompose/pkg/kobject"
"github.com/kubernetes-incubator/kompose/pkg/testutils" "github.com/kubernetes-incubator/kompose/pkg/testutils"
"github.com/kubernetes-incubator/kompose/pkg/transformer/kubernetes" "github.com/kubernetes-incubator/kompose/pkg/transformer/kubernetes"
"os/exec" "github.com/pkg/errors"
) )
func newServiceConfig() kobject.ServiceConfig { func newServiceConfig() kobject.ServiceConfig {
@ -292,7 +292,10 @@ func TestInitBuildConfig(t *testing.T) {
sc := kobject.ServiceConfig{ sc := kobject.ServiceConfig{
Build: "./build", 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 { testCases := map[string]struct {
field string field string
@ -324,20 +327,16 @@ func TestServiceWithoutPort(t *testing.T) {
} }
o := OpenShift{Kubernetes: kubernetes.Kubernetes{}} 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 { if err := testutils.CheckForHeadless(objects); err != nil {
t.Error(err) 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) { func TestRestartOnFailure(t *testing.T) {
service := kobject.ServiceConfig{ service := kobject.ServiceConfig{
@ -361,24 +360,13 @@ func TestRestartOnFailure(t *testing.T) {
for name, test := range testCase { for name, test := range testCase {
t.Log("Test case:", name) t.Log("Test case:", name)
o := OpenShift{} 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 // 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{}} 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 { for _, obj := range objects {
if deploymentConfig, ok := obj.(*deployapi.DeploymentConfig); ok { if deploymentConfig, ok := obj.(*deployapi.DeploymentConfig); ok {
if deploymentConfig.Spec.Strategy.Type != deployapi.DeploymentStrategyTypeRecreate { if deploymentConfig.Spec.Strategy.Type != deployapi.DeploymentStrategyTypeRecreate {

View File

@ -24,7 +24,7 @@ import (
// Transformer interface defines transformer that is converting kobject to other resources // Transformer interface defines transformer that is converting kobject to other resources
type Transformer interface { type Transformer interface {
// Transform converts KomposeObject to transformer specific objects. // 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 deploys KomposeObject to provider
Deploy(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) error Deploy(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) error
// Undeploy deletes/undeploys KomposeObject from provider // Undeploy deletes/undeploys KomposeObject from provider

View File

@ -30,6 +30,7 @@ import (
"path/filepath" "path/filepath"
"github.com/pkg/errors"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/runtime" "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 // 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 f *os.File
var err error var err error
if len(out) != 0 { if len(out) != 0 {
f, err = os.Create(out) f, err = os.Create(out)
if err != nil { 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] // 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 // 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 := "" file := ""
if generateJSON { if generateJSON {
file = fmt.Sprintf("%s-%s.json", name, trailing) 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 { if toStdout {
fmt.Fprintf(os.Stdout, "%s\n", string(data)) fmt.Fprintf(os.Stdout, "%s\n", string(data))
return "" return "", nil
} else if f != nil { } else if f != nil {
// Write all content to a single file f // Write all content to a single file f
if _, err := f.WriteString(fmt.Sprintf("%s\n", string(data))); err != nil { 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() f.Sync()
} else { } else {
// Write content separately to each file // Write content separately to each file
file = filepath.Join(path, file) file = filepath.Join(path, file)
if err := ioutil.WriteFile(file, []byte(data), 0644); err != nil { 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) log.Printf("file %q created", file)
} }
return file return file, nil
} }