diff --git a/pkg/kobject/kobject.go b/pkg/kobject/kobject.go index 1d5554ae..8f92665b 100644 --- a/pkg/kobject/kobject.go +++ b/pkg/kobject/kobject.go @@ -16,93 +16,15 @@ limitations under the License. package kobject -import ( - "github.com/Sirupsen/logrus" - "github.com/fatih/structs" - "k8s.io/kubernetes/pkg/api" -) - -var unsupportedKey = map[string]int{ - "Build": 0, - "CgroupParent": 0, - "Devices": 0, - "DependsOn": 0, - "DNS": 0, - "DNSSearch": 0, - "DomainName": 0, - "EnvFile": 0, - "Extends": 0, - "ExternalLinks": 0, - "ExtraHosts": 0, - "Hostname": 0, - "Ipc": 0, - "Logging": 0, - "MacAddress": 0, - "MemLimit": 0, - "MemSwapLimit": 0, - "NetworkMode": 0, - "Pid": 0, - "SecurityOpt": 0, - "ShmSize": 0, - "StopSignal": 0, - "VolumeDriver": 0, - "Uts": 0, - "ReadOnly": 0, - "StdinOpen": 0, - "Tty": 0, - "Ulimits": 0, - "Dockerfile": 0, - "Net": 0, - "Networks": 0, -} - -var composeOptions = map[string]string{ - "Build": "build", - "CapAdd": "cap_add", - "CapDrop": "cap_drop", - "CPUSet": "cpuset", - "CPUShares": "cpu_shares", - "CPUQuota": "cpu_quota", - "CgroupParent": "cgroup_parent", - "Devices": "devices", - "DependsOn": "depends_on", - "DNS": "dns", - "DNSSearch": "dns_search", - "DomainName": "domainname", - "Entrypoint": "entrypoint", - "EnvFile": "env_file", - "Expose": "expose", - "Extends": "extends", - "ExternalLinks": "external_links", - "ExtraHosts": "extra_hosts", - "Hostname": "hostname", - "Ipc": "ipc", - "Logging": "logging", - "MacAddress": "mac_address", - "MemLimit": "mem_limit", - "MemSwapLimit": "memswap_limit", - "NetworkMode": "network_mode", - "Networks": "networks", - "Pid": "pid", - "SecurityOpt": "security_opt", - "ShmSize": "shm_size", - "StopSignal": "stop_signal", - "VolumeDriver": "volume_driver", - "VolumesFrom": "volumes_from", - "Uts": "uts", - "ReadOnly": "read_only", - "StdinOpen": "stdin_open", - "Tty": "tty", - "User": "user", - "Ulimits": "ulimits", - "Dockerfile": "dockerfile", - "Net": "net", - "Args": "args", -} +import "k8s.io/kubernetes/pkg/api" // KomposeObject holds the generic struct of Kompose transformation type KomposeObject struct { ServiceConfigs map[string]ServiceConfig + // LoadedFrom is name of the loader that created KomposeObject + // Transformer need to know origin format in order to tell user what tag is not supported in origin format + // as they can have different names. For example environment variables are called environment in compose but Env in bundle. + LoadedFrom string } type ConvertOptions struct { @@ -122,28 +44,30 @@ type ConvertOptions struct { // ServiceConfig holds the basic struct of a container type ServiceConfig struct { + // use tags to mark from what element this value comes ContainerName string - Image string - Environment []EnvVar - Port []Ports - Command []string - WorkingDir string - Args []string - Volumes []string - Network []string - Labels map[string]string - Annotations map[string]string - CPUSet string - CPUShares int64 - CPUQuota int64 - CapAdd []string - CapDrop []string - Expose []string - Privileged bool - Restart string - User string - VolumesFrom []string - ServiceType string + Image string `compose:"image",bundle:"Image"` + Environment []EnvVar `compose:"environment",bundle:"Env"` + Port []Ports `compose:"ports",bundle:"Ports"` + Command []string `compose:"command",bundle:"Command"` + WorkingDir string `compose:"",bundle:"WorkingDir"` + Args []string `compose:"args",bundle:"Args"` + Volumes []string `compose:"volumes",bundle:"Volumes"` + Network []string `compose:"network",bundle:"Networks"` + Labels map[string]string `compose:"labels",bundle:"Labels"` + Annotations map[string]string `compose:"",bundle:""` + CPUSet string `compose:"cpuset",bundle:""` + CPUShares int64 `compose:"cpu_shares",bundle:""` + CPUQuota int64 `compose:"cpu_quota",bundle:""` + CapAdd []string `compose:"cap_add",bundle:""` + CapDrop []string `compose:"cap_drop",bundle:""` + Expose []string `compose:"expose",bundle:""` + Privileged bool `compose:"privileged",bundle:""` + Restart string `compose:"restart",bundle:""` + User string `compose:"user",bundle:"User"` + VolumesFrom []string `compose:"volumes_from",bundle:""` + ServiceType string `compose:"kompose.service.type",bundle:""` + Build string `compose:"build",bundle:""` } // EnvVar holds the environment variable struct of a container @@ -158,15 +82,3 @@ type Ports struct { ContainerPort int32 Protocol api.Protocol } - -func CheckUnsupportedKey(service interface{}) { - s := structs.New(service) - for _, f := range s.Fields() { - if f.IsExported() && !f.IsZero() && f.Name() != "Networks" { - if count, ok := unsupportedKey[f.Name()]; ok && count == 0 { - logrus.Warningf("Unsupported key %s - ignoring", composeOptions[f.Name()]) - unsupportedKey[f.Name()]++ - } - } - } -} diff --git a/pkg/loader/bundle/bundle.go b/pkg/loader/bundle/bundle.go index 2459f563..78c57b6c 100644 --- a/pkg/loader/bundle/bundle.go +++ b/pkg/loader/bundle/bundle.go @@ -21,11 +21,13 @@ import ( "fmt" "io" "io/ioutil" + "reflect" "strings" "k8s.io/kubernetes/pkg/api" "github.com/Sirupsen/logrus" + "github.com/fatih/structs" "github.com/kubernetes-incubator/kompose/pkg/kobject" ) @@ -57,6 +59,51 @@ type Port struct { Port uint32 } +// checkUnsupportedKey checks if dab contains +// keys that are not supported by this loader. +// list of all unsupported keys are stored in unsupportedKey variable +// returns list of unsupported JSON/YAML keys +func checkUnsupportedKey(bundleStruct *Bundlefile) []string { + // list of all unsupported keys for this loader + // this is map to make searching for keys easier + // to make sure that unsupported key is not going to be reported twice + // by keeping record if already saw this key in another service + var unsupportedKey = map[string]bool{ + "Networks": false, + } + + // collect all keys found in project + var keysFound []string + for _, service := range bundleStruct.Services { + // this reflection is used in check for empty arrays + val := reflect.ValueOf(service) + s := structs.New(service) + + for _, f := range s.Fields() { + // Check if given key is among unsupported keys, and skip it if we already saw this key + if alreadySaw, ok := unsupportedKey[f.Name()]; ok && !alreadySaw { + if f.IsExported() && !f.IsZero() { + jsonTagName := strings.Split(f.Tag("json"), ",")[0] + if jsonTagName == "" { + jsonTagName = f.Name() + } + // IsZero returns false for empty array/slice ([]) + // this check if field is Slice, and then it checks its size + if field := val.FieldByName(f.Name()); field.Kind() == reflect.Slice { + if field.Len() == 0 { + // array is empty it doesn't matter if it is in unsupportedKey or not + continue + } + } + keysFound = append(keysFound, jsonTagName) + unsupportedKey[f.Name()] = true + } + } + } + } + return keysFound +} + // load image from dab file func loadImage(service Service) (string, string) { character := "@" @@ -129,6 +176,7 @@ func loadPorts(service Service) ([]kobject.Ports, string) { func (b *Bundle) LoadFile(file string) kobject.KomposeObject { komposeObject := kobject.KomposeObject{ ServiceConfigs: make(map[string]kobject.ServiceConfig), + LoadedFrom: "bundle", } buf, err := ioutil.ReadFile(file) @@ -141,8 +189,12 @@ func (b *Bundle) LoadFile(file string) kobject.KomposeObject { logrus.Fatalf("Failed to parse bundles file: %s", err) } + noSupKeys := checkUnsupportedKey(bundle) + for _, keyName := range noSupKeys { + logrus.Warningf("Unsupported %s key - ignoring", keyName) + } + for name, service := range bundle.Services { - kobject.CheckUnsupportedKey(service) serviceConfig := kobject.ServiceConfig{} serviceConfig.Command = service.Command diff --git a/pkg/loader/bundle/bundle_test.go b/pkg/loader/bundle/bundle_test.go new file mode 100644 index 00000000..d757d459 --- /dev/null +++ b/pkg/loader/bundle/bundle_test.go @@ -0,0 +1,87 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package bundle + +import ( + "reflect" + "strings" + "testing" +) + +// TestUnsupportedKeys test checkUnsupportedKey function with various +// docker-compose projects +func TestUnsupportedKeys(t *testing.T) { + user := "user" + workDir := "workDir" + + fullBundle := Bundlefile{ + Version: "0.1", + Services: map[string]Service{ + "foo": Service{ + Image: "image", + Command: []string{"cmd"}, + Args: []string{"arg"}, + Env: []string{"env"}, + Labels: map[string]string{"key": "value"}, + Ports: []Port{Port{Protocol: "tcp", Port: uint32(80)}}, + WorkingDir: &workDir, //there is no other way to get pointer to string + User: &user, + Networks: []string{"net"}, + }, + }, + } + + bundleWithEmptyNetworks := Bundlefile{ + Version: "0.1", + Services: map[string]Service{ + "foo": Service{ + Image: "image", + Command: []string{"cmd"}, + Args: []string{"arg"}, + Env: []string{"env"}, + Labels: map[string]string{"key": "value"}, + Ports: []Port{Port{Protocol: "tcp", Port: uint32(80)}}, + WorkingDir: &workDir, //there is no other way to get pointer to string + User: &user, + Networks: []string{}, + }, + }, + } + // define all test cases for checkUnsupportedKey function + testCases := map[string]struct { + bundleFile Bundlefile + expectedUnsupportedKeys []string + }{ + "Full Bundle": { + fullBundle, + []string{"Networks"}, + }, + "Bundle with empty Networks": { + bundleWithEmptyNetworks, + []string(nil), + }, + } + + for name, test := range testCases { + t.Log("Test case:", name) + keys := checkUnsupportedKey(&test.bundleFile) + if !reflect.DeepEqual(keys, test.expectedUnsupportedKeys) { + t.Errorf("ERROR: Expecting unsupported keys: ['%s']. Got: ['%s']", strings.Join(test.expectedUnsupportedKeys, "', '"), strings.Join(keys, "', '")) + } + } + +} diff --git a/pkg/loader/compose/compose.go b/pkg/loader/compose/compose.go index c11a7cc1..43d64ef7 100644 --- a/pkg/loader/compose/compose.go +++ b/pkg/loader/compose/compose.go @@ -20,6 +20,7 @@ import ( "fmt" "os" "path/filepath" + "reflect" "strconv" "strings" @@ -29,12 +30,105 @@ import ( "github.com/docker/libcompose/config" "github.com/docker/libcompose/lookup" "github.com/docker/libcompose/project" + "github.com/fatih/structs" "github.com/kubernetes-incubator/kompose/pkg/kobject" ) type Compose struct { } +// checkUnsupportedKey checks if libcompose project contains +// keys that are not supported by this loader. +// list of all unsupported keys are stored in unsupportedKey variable +// returns list of unsupported YAML keys from docker-compose +func checkUnsupportedKey(composeProject *project.Project) []string { + + // list of all unsupported keys for this loader + // this is map to make searching for keys easier + // to make sure that unsupported key is not going to be reported twice + // by keeping record if already saw this key in another service + var unsupportedKey = map[string]bool{ + "CgroupParent": false, + "Devices": false, + "DependsOn": false, + "DNS": false, + "DNSSearch": false, + "DomainName": false, + "EnvFile": false, + "Extends": false, + "ExternalLinks": false, + "ExtraHosts": false, + "Hostname": false, + "Ipc": false, + "Logging": false, + "MacAddress": false, + "MemLimit": false, + "MemSwapLimit": false, + "NetworkMode": false, + "Pid": false, + "SecurityOpt": false, + "ShmSize": false, + "StopSignal": false, + "VolumeDriver": false, + "Uts": false, + "ReadOnly": false, + "StdinOpen": false, + "Tty": false, + "Ulimits": false, + "Dockerfile": false, + "Net": false, + "Networks": false, // there are special checks for Network in checkUnsupportedKey function + } + + // collect all keys found in project + var keysFound []string + + // Root level keys + // volume config and network config are not supported + if len(composeProject.NetworkConfigs) > 0 { + keysFound = append(keysFound, "root level networks") + } + if len(composeProject.VolumeConfigs) > 0 { + keysFound = append(keysFound, "root level volumes") + } + + for _, serviceConfig := range composeProject.ServiceConfigs.All() { + // this reflection is used in check for empty arrays + val := reflect.ValueOf(serviceConfig).Elem() + s := structs.New(serviceConfig) + + for _, f := range s.Fields() { + // Check if given key is among unsupported keys, and skip it if we already saw this key + if alreadySaw, ok := unsupportedKey[f.Name()]; ok && !alreadySaw { + if f.IsExported() && !f.IsZero() { + // IsZero returns false for empty array/slice ([]) + // this check if field is Slice, and then it checks its size + if field := val.FieldByName(f.Name()); field.Kind() == reflect.Slice { + if field.Len() == 0 { + // array is empty it doesn't matter if it is in unsupportedKey or not + continue + } + } + //get yaml tag name instad of variable name + yamlTagName := strings.Split(f.Tag("yaml"), ",")[0] + if f.Name() == "Networks" { + // networks always contains one default element, even it isn't declared in compose v2. + if len(serviceConfig.Networks.Networks) == 1 && serviceConfig.Networks.Networks[0].Name == "default" { + // this is empty Network definition, skip it + continue + } else { + yamlTagName = "networks" + } + } + keysFound = append(keysFound, yamlTagName) + unsupportedKey[f.Name()] = true + } + } + } + } + return keysFound +} + // load environment variables from compose file func loadEnvVars(envars []string) []kobject.EnvVar { envs := []kobject.EnvVar{} @@ -132,6 +226,7 @@ func loadPorts(composePorts []string) ([]kobject.Ports, error) { func (c *Compose) LoadFile(file string) kobject.KomposeObject { komposeObject := kobject.KomposeObject{ ServiceConfigs: make(map[string]kobject.ServiceConfig), + LoadedFrom: "compose", } context := &project.Context{} if file == "" { @@ -168,31 +263,19 @@ func (c *Compose) LoadFile(file string) kobject.KomposeObject { // transform composeObject into komposeObject composeServiceNames := composeObject.ServiceConfigs.Keys() - // volume config and network config are not supported - if len(composeObject.NetworkConfigs) > 0 { - logrus.Warningf("Unsupported network configuration of compose v2 - ignoring") + noSupKeys := checkUnsupportedKey(composeObject) + for _, keyName := range noSupKeys { + logrus.Warningf("Unsupported %s key - ignoring", keyName) } - if len(composeObject.VolumeConfigs) > 0 { - logrus.Warningf("Unsupported volume configuration of compose v2 - ignoring") - } - - networksWarningFound := false for _, name := range composeServiceNames { if composeServiceConfig, ok := composeObject.ServiceConfigs.Get(name); ok { - //FIXME: networks always contains one default element, even it isn't declared in compose v2. - if composeServiceConfig.Networks != nil && len(composeServiceConfig.Networks.Networks) > 0 && - composeServiceConfig.Networks.Networks[0].Name != "default" && - !networksWarningFound { - logrus.Warningf("Unsupported key networks - ignoring") - networksWarningFound = true - } - kobject.CheckUnsupportedKey(composeServiceConfig) serviceConfig := kobject.ServiceConfig{} serviceConfig.Image = composeServiceConfig.Image serviceConfig.ContainerName = composeServiceConfig.ContainerName serviceConfig.Command = composeServiceConfig.Entrypoint serviceConfig.Args = composeServiceConfig.Command + serviceConfig.Build = composeServiceConfig.Build.Context envs := loadEnvVars(composeServiceConfig.Environment) serviceConfig.Environment = envs diff --git a/pkg/loader/compose/compose_test.go b/pkg/loader/compose/compose_test.go index 91e7131d..3525bf9e 100644 --- a/pkg/loader/compose/compose_test.go +++ b/pkg/loader/compose/compose_test.go @@ -18,9 +18,15 @@ package compose import ( "os" + "reflect" + "strings" "testing" "github.com/kubernetes-incubator/kompose/pkg/kobject" + + "github.com/docker/libcompose/config" + "github.com/docker/libcompose/project" + "github.com/docker/libcompose/yaml" ) // Test if service types are parsed properly on user input @@ -112,3 +118,98 @@ func TestLoadEnvVar(t *testing.T) { } } } + +// TestUnsupportedKeys test checkUnsupportedKey function with various +// docker-compose projects +func TestUnsupportedKeys(t *testing.T) { + // create project that will be used in test cases + projectWithNetworks := project.NewProject(&project.Context{}, nil, nil) + projectWithNetworks.ServiceConfigs = config.NewServiceConfigs() + projectWithNetworks.ServiceConfigs.Add("foo", &config.ServiceConfig{ + Image: "foo/bar", + Build: yaml.Build{ + Context: "./build", + }, + Hostname: "localhost", + Ports: []string{}, // test empty array + Networks: &yaml.Networks{ + Networks: []*yaml.Network{ + &yaml.Network{ + Name: "net1", + }, + }, + }, + }) + projectWithNetworks.ServiceConfigs.Add("bar", &config.ServiceConfig{ + Image: "bar/foo", + Build: yaml.Build{ + Context: "./build", + }, + Hostname: "localhost", + Ports: []string{}, // test empty array + Networks: &yaml.Networks{ + Networks: []*yaml.Network{ + &yaml.Network{ + Name: "net1", + }, + }, + }, + }) + projectWithNetworks.VolumeConfigs = map[string]*config.VolumeConfig{ + "foo": &config.VolumeConfig{ + Driver: "storage", + }, + } + projectWithNetworks.NetworkConfigs = map[string]*config.NetworkConfig{ + "foo": &config.NetworkConfig{ + Driver: "bridge", + }, + } + + projectWithEmptyNetwork := project.NewProject(&project.Context{}, nil, nil) + projectWithEmptyNetwork.ServiceConfigs = config.NewServiceConfigs() + projectWithEmptyNetwork.ServiceConfigs.Add("foo", &config.ServiceConfig{ + Networks: &yaml.Networks{}, + }) + + projectWithDefaultNetwork := project.NewProject(&project.Context{}, nil, nil) + projectWithDefaultNetwork.ServiceConfigs = config.NewServiceConfigs() + + projectWithDefaultNetwork.ServiceConfigs.Add("foo", &config.ServiceConfig{ + Networks: &yaml.Networks{ + Networks: []*yaml.Network{ + &yaml.Network{ + Name: "default", + }, + }, + }, + }) + + // define all test cases for checkUnsupportedKey function + testCases := map[string]struct { + composeProject *project.Project + expectedUnsupportedKeys []string + }{ + "With Networks (service and root level)": { + projectWithNetworks, + []string{"root level networks", "root level volumes", "hostname", "networks"}, + }, + "Empty Networks on Service level": { + projectWithEmptyNetwork, + []string{"networks"}, + }, + "Default root level Network": { + projectWithDefaultNetwork, + []string(nil), + }, + } + + for name, test := range testCases { + t.Log("Test case:", name) + keys := checkUnsupportedKey(test.composeProject) + if !reflect.DeepEqual(keys, test.expectedUnsupportedKeys) { + t.Errorf("ERROR: Expecting unsupported keys: ['%s']. Got: ['%s']", strings.Join(test.expectedUnsupportedKeys, "', '"), strings.Join(keys, "', '")) + } + } + +} diff --git a/pkg/loader/loader.go b/pkg/loader/loader.go index eff70f0e..97a821a5 100644 --- a/pkg/loader/loader.go +++ b/pkg/loader/loader.go @@ -27,6 +27,7 @@ import ( type Loader interface { LoadFile(file string) kobject.KomposeObject + ///Name() string } // GetLoader returns loader for given format diff --git a/pkg/transformer/kubernetes/kubernetes.go b/pkg/transformer/kubernetes/kubernetes.go index d2163182..7abf95ec 100644 --- a/pkg/transformer/kubernetes/kubernetes.go +++ b/pkg/transformer/kubernetes/kubernetes.go @@ -18,11 +18,13 @@ package kubernetes import ( "fmt" + "reflect" "sort" "strconv" "time" "github.com/Sirupsen/logrus" + "github.com/fatih/structs" "github.com/kubernetes-incubator/kompose/pkg/kobject" "github.com/kubernetes-incubator/kompose/pkg/transformer" deployapi "github.com/openshift/origin/pkg/deploy/api" @@ -52,6 +54,52 @@ type Kubernetes struct { // used when undeploying resources from kubernetes const TIMEOUT = 300 +// list of all unsupported keys for this transformer +// Keys are names of variables in kobject struct. +// this is map to make searching for keys easier +// to make sure that unsupported key is not going to be reported twice +// by keeping record if already saw this key in another service +var unsupportedKey = map[string]bool{ + "Build": false, +} + +// checkUnsupportedKey checks if given komposeObject contains +// keys that are not supported by this tranfomer. +// list of all unsupported keys are stored in unsupportedKey variable +// returns list of TODO: .... +func (k *Kubernetes) CheckUnsupportedKey(komposeObject *kobject.KomposeObject, unsupportedKey map[string]bool) []string { + // collect all keys found in project + var keysFound []string + + for _, serviceConfig := range komposeObject.ServiceConfigs { + // this reflection is used in check for empty arrays + val := reflect.ValueOf(serviceConfig) + s := structs.New(serviceConfig) + + for _, f := range s.Fields() { + // Check if given key is among unsupported keys, and skip it if we already saw this key + if alreadySaw, ok := unsupportedKey[f.Name()]; ok && !alreadySaw { + + if f.IsExported() && !f.IsZero() { + // IsZero returns false for empty array/slice ([]) + // this check if field is Slice, and then it checks its size + if field := val.FieldByName(f.Name()); field.Kind() == reflect.Slice { + if field.Len() == 0 { + // array is empty it doesn't matter if it is in unsupportedKey or not + continue + } + } + //get tag from kobject service configure + tag := f.Tag(komposeObject.LoadedFrom) + keysFound = append(keysFound, tag) + unsupportedKey[f.Name()] = true + } + } + } + } + return keysFound +} + // Init RC object func (k *Kubernetes) InitRC(name string, service kobject.ServiceConfig, replicas int) *api.ReplicationController { rc := &api.ReplicationController{ @@ -351,6 +399,12 @@ 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 { + + noSupKeys := k.CheckUnsupportedKey(&komposeObject, unsupportedKey) + for _, keyName := range noSupKeys { + logrus.Warningf("Kubernetes provider doesn't support %s key - ignoring", keyName) + } + // this will hold all the converted data var allobjects []runtime.Object diff --git a/pkg/transformer/kubernetes/kubernetes_test.go b/pkg/transformer/kubernetes/kubernetes_test.go index 0c80e6e5..1319464e 100644 --- a/pkg/transformer/kubernetes/kubernetes_test.go +++ b/pkg/transformer/kubernetes/kubernetes_test.go @@ -18,6 +18,8 @@ package kubernetes import ( "fmt" + "reflect" + "strings" "testing" deployapi "github.com/openshift/origin/pkg/deploy/api" @@ -353,3 +355,36 @@ func TestConvertRestartOptions(t *testing.T) { } } } + +// TestUnsupportedKeys test checkUnsupportedKey function +func TestUnsupportedKeys(t *testing.T) { + + kobjectWithBuild := newKomposeObject() + kobjectWithBuild.LoadedFrom = "compose" + serviceConfig := kobjectWithBuild.ServiceConfigs["app"] + serviceConfig.Build = "./asdf" + serviceConfig.Network = []string{} + kobjectWithBuild.ServiceConfigs = map[string]kobject.ServiceConfig{"app": serviceConfig} + + // define all test cases for checkUnsupportedKey function + testCases := map[string]struct { + bundleFile kobject.KomposeObject + expectedUnsupportedKeys []string + }{ + "Full Bundle": { + kobjectWithBuild, + []string{"build"}, + }, + } + + k := Kubernetes{} + + for name, test := range testCases { + t.Log("Test case:", name) + keys := k.CheckUnsupportedKey(&test.bundleFile, unsupportedKey) + if !reflect.DeepEqual(keys, test.expectedUnsupportedKeys) { + t.Errorf("ERROR: Expecting unsupported keys: ['%s']. Got: ['%s']", strings.Join(test.expectedUnsupportedKeys, "', '"), strings.Join(keys, "', '")) + } + } + +} diff --git a/pkg/transformer/openshift/openshift.go b/pkg/transformer/openshift/openshift.go index b71358b7..78a6b61e 100644 --- a/pkg/transformer/openshift/openshift.go +++ b/pkg/transformer/openshift/openshift.go @@ -33,11 +33,12 @@ import ( oclient "github.com/openshift/origin/pkg/client" ocliconfig "github.com/openshift/origin/pkg/cmd/cli/config" + "time" + deployapi "github.com/openshift/origin/pkg/deploy/api" deploymentconfigreaper "github.com/openshift/origin/pkg/deploy/cmd" imageapi "github.com/openshift/origin/pkg/image/api" "k8s.io/kubernetes/pkg/kubectl" - "time" ) type OpenShift struct { @@ -51,6 +52,13 @@ type OpenShift struct { // used when undeploying resources from OpenShift const TIMEOUT = 300 +// list of all unsupported keys for this transformer +// Keys are names of variables in kobject struct. +// this is map to make searching for keys easier +// to make sure that unsupported key is not going to be reported twice +// by keeping record if already saw this key in another service +var unsupportedKey = map[string]bool{} + // getImageTag get tag name from image name // if no tag is specified return 'latest' func getImageTag(image string) string { @@ -151,6 +159,10 @@ func (o *OpenShift) initDeploymentConfig(name string, service kobject.ServiceCon // 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 { + noSupKeys := o.Kubernetes.CheckUnsupportedKey(&komposeObject, unsupportedKey) + for _, keyName := range noSupKeys { + logrus.Warningf("OpenShift provider doesn't support %s key - ignoring", keyName) + } // this will hold all the converted data var allobjects []runtime.Object diff --git a/script/test/cmd/tests.sh b/script/test/cmd/tests.sh index 66a4d654..7f3096e9 100755 --- a/script/test/cmd/tests.sh +++ b/script/test/cmd/tests.sh @@ -26,9 +26,9 @@ convert::expect_failure "kompose -f $KOMPOSE_ROOT/script/test/fixtures/etherpad/ convert::expect_warning "kompose -f $KOMPOSE_ROOT/script/test/fixtures/etherpad/docker-compose-no-ports.yml convert --stdout" "Service cannot be created because of missing port." export $(cat $KOMPOSE_ROOT/script/test/fixtures/etherpad/envs) # kubernetes test -convert::expect_success_and_warning "kompose -f $KOMPOSE_ROOT/script/test/fixtures/etherpad/docker-compose.yml convert --stdout" "$KOMPOSE_ROOT/script/test/fixtures/etherpad/output-k8s.json" "Unsupported key depends_on - ignoring" +convert::expect_success_and_warning "kompose -f $KOMPOSE_ROOT/script/test/fixtures/etherpad/docker-compose.yml convert --stdout" "$KOMPOSE_ROOT/script/test/fixtures/etherpad/output-k8s.json" "Unsupported depends_on key - ignoring" # openshift test -convert::expect_success_and_warning "kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/etherpad/docker-compose.yml convert --stdout" "$KOMPOSE_ROOT/script/test/fixtures/etherpad/output-os.json" "Unsupported key depends_on - ignoring" +convert::expect_success_and_warning "kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/etherpad/docker-compose.yml convert --stdout" "$KOMPOSE_ROOT/script/test/fixtures/etherpad/output-os.json" "Unsupported depends_on key - ignoring" unset $(cat $KOMPOSE_ROOT/script/test/fixtures/etherpad/envs | cut -d'=' -f1) ###### @@ -44,10 +44,9 @@ unset $(cat $KOMPOSE_ROOT/script/test/fixtures/gitlab/envs | cut -d'=' -f1) ###### # Tests related to docker-compose file in /script/test/fixtures/ngnix-node-redis # kubernetes test -convert::expect_success_and_warning "kompose -f $KOMPOSE_ROOT/script/test/fixtures/ngnix-node-redis/docker-compose.yml convert --stdout" "$KOMPOSE_ROOT/script/test/fixtures/ngnix-node-redis/output-k8s.json" "Unsupported key build - ignoring" +convert::expect_success_and_warning "kompose -f $KOMPOSE_ROOT/script/test/fixtures/ngnix-node-redis/docker-compose.yml convert --stdout" "$KOMPOSE_ROOT/script/test/fixtures/ngnix-node-redis/output-k8s.json" "Kubernetes provider doesn't support build key - ignoring" # openshift test -convert::expect_success_and_warning "kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/ngnix-node-redis/docker-compose.yml convert --stdout" "$KOMPOSE_ROOT/script/test/fixtures/ngnix-node-redis/output-os.json" "Unsupported key build - ignoring" - +convert::expect_success "kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/ngnix-node-redis/docker-compose.yml convert --stdout" "$KOMPOSE_ROOT/script/test/fixtures/ngnix-node-redis/output-os.json" ###### # Tests related to docker-compose file in /script/test/fixtures/entrypoint-command