diff --git a/cmd/convert.go b/cmd/convert.go index 8e87627b..15a66fbc 100644 --- a/cmd/convert.go +++ b/cmd/convert.go @@ -106,8 +106,7 @@ var convertCmd = &cobra.Command{ MultipleContainerMode: MultipleContainerMode, } - // Validate before doing anything else. Use "bundle" if passed in. - app.ValidateFlags(GlobalBundle, args, cmd, &ConvertOpt) + app.ValidateFlags(args, cmd, &ConvertOpt) app.ValidateComposeFile(&ConvertOpt) }, Run: func(cmd *cobra.Command, args []string) { diff --git a/cmd/root.go b/cmd/root.go index 7759ff04..30c29e8c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -39,7 +39,6 @@ func (errorOnWarningHook) Fire(entry *log.Entry) error { // TODO: comment var ( - GlobalBundle string GlobalProvider string GlobalVerbose bool GlobalSuppressWarnings bool @@ -95,10 +94,5 @@ func init() { RootCmd.PersistentFlags().BoolVar(&GlobalSuppressWarnings, "suppress-warnings", false, "Suppress all warnings") RootCmd.PersistentFlags().BoolVar(&GlobalErrorOnWarning, "error-on-warning", false, "Treat any warning as an error") RootCmd.PersistentFlags().StringArrayVarP(&GlobalFiles, "file", "f", []string{}, "Specify an alternative compose file") - RootCmd.PersistentFlags().StringVarP(&GlobalBundle, "bundle", "b", "", "Specify a Distributed Application Bundle (DAB) file") RootCmd.PersistentFlags().StringVar(&GlobalProvider, "provider", "kubernetes", "Specify a provider. Kubernetes or OpenShift.") - - // Mark DAB / bundle as deprecated, see issue: https://github.com/kubernetes/kompose/issues/390 - // As DAB is still EXPERIMENTAL - RootCmd.PersistentFlags().MarkDeprecated("bundle", "DAB / Bundle is deprecated, see: https://github.com/kubernetes/kompose/issues/390") } diff --git a/docs/architecture.md b/docs/architecture.md index 13c28d04..2ee19ab9 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -9,7 +9,7 @@ ## Loader -Loader reads input file (now `kompose` supports [Docker Compose](https://docs.docker.com/compose) v1, v2 and [Docker Distributed Application Bundle](https://blog.docker.com/2016/06/docker-app-bundle/) file) and converts it to KomposeObject. +Loader reads input file (now `kompose` supports [Docker Compose](https://docs.docker.com/compose) v1, v2 and converts it to KomposeObject. Loader is represented by a Loader interface: @@ -19,10 +19,9 @@ type Loader interface { } ``` -Every loader “implementation” should be placed into `kompose/pkg/loader` (like compose & bundle). More input formats will be supported in future. You can take a look for more details at: +Every loader “implementation” should be placed into `kompose/pkg/loader` (like compose). More input formats will be supported in future. You can take a look for more details at: * [kompose/pkg/loader](https://github.com/kubernetes/kompose/tree/master/pkg/loader) -* [kompose/pkg/loader/bundle](https://github.com/kubernetes/kompose/tree/master/pkg/loader/bundle) * [kompose/pkg/loader/compose](https://github.com/kubernetes/kompose/tree/master/pkg/loader/compose) ## KomposeObject diff --git a/docs/images/design_diagram.png b/docs/images/design_diagram.png index 92988033..460d4abd 100644 Binary files a/docs/images/design_diagram.png and b/docs/images/design_diagram.png differ diff --git a/pkg/app/app.go b/pkg/app/app.go index d1558ebd..2039e595 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -53,10 +53,7 @@ const ( var inputFormat = "compose" // ValidateFlags validates all command line flags -func ValidateFlags(bundle string, args []string, cmd *cobra.Command, opt *kobject.ConvertOptions) { - // Check to see if the "file" has changed from the default flag value - isFileSet := cmd.Flags().Lookup("file").Changed - +func ValidateFlags(args []string, cmd *cobra.Command, opt *kobject.ConvertOptions) { if opt.OutFile == "-" { opt.ToStdout = true opt.OutFile = "" @@ -127,16 +124,6 @@ func ValidateFlags(bundle string, args []string, cmd *cobra.Command, opt *kobjec log.Fatalf("Error: --replicas cannot be negative") } - if len(bundle) > 0 { - inputFormat = "bundle" - log.Fatalf("DAB / bundle (--bundle | -b) is no longer supported. See issue: https://github.com/kubernetes/kompose/issues/390") - opt.InputFiles = []string{bundle} - } - - if len(bundle) > 0 && isFileSet { - log.Fatalf("Error: 'compose' file and 'dab' file cannot be specified at the same time") - } - if len(args) != 0 { log.Fatal("Unknown Argument(s): ", strings.Join(args, ",")) } diff --git a/pkg/loader/bundle/bundle.go b/pkg/loader/bundle/bundle.go deleted file mode 100644 index 93988c0e..00000000 --- a/pkg/loader/bundle/bundle.go +++ /dev/null @@ -1,252 +0,0 @@ -/* -Copyright 2017 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 ( - "encoding/json" - "fmt" - "io" - "io/ioutil" - "reflect" - "strings" - - api "k8s.io/api/core/v1" - - "github.com/fatih/structs" - "github.com/kubernetes/kompose/pkg/kobject" - "github.com/pkg/errors" - log "github.com/sirupsen/logrus" -) - -// Bundle is docker bundle file loader, implements Loader interface -type Bundle struct { -} - -// Bundlefile stores the contents of a bundlefile -type Bundlefile struct { - Version string - Services map[string]Service -} - -// Service is a service from a bundlefile -type Service struct { - Image string - Command []string `json:",omitempty"` - Args []string `json:",omitempty"` - Env []string `json:",omitempty"` - Labels map[string]string `json:",omitempty"` - Ports []Port `json:",omitempty"` - WorkingDir *string `json:",omitempty"` - User *string `json:",omitempty"` - Networks []string `json:",omitempty"` -} - -// Port is a port as defined in a bundlefile -type Port struct { - Protocol string - 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, error) { - character := "@" - if strings.Contains(service.Image, character) { - return service.Image[0:strings.Index(service.Image, character)], nil - } - return "", errors.New("Invalid image format") -} - -// load environment variables from dab file -func loadEnvVars(service Service) ([]kobject.EnvVar, error) { - envs := []kobject.EnvVar{} - for _, env := range service.Env { - character := "=" - if strings.Contains(env, character) { - value := env[strings.Index(env, character)+1:] - name := env[0:strings.Index(env, character)] - name = strings.TrimSpace(name) - value = strings.TrimSpace(value) - envs = append(envs, kobject.EnvVar{ - Name: name, - Value: value, - }) - } else { - character = ":" - if strings.Contains(env, character) { - charQuote := "'" - value := env[strings.Index(env, character)+1:] - name := env[0:strings.Index(env, character)] - name = strings.TrimSpace(name) - value = strings.TrimSpace(value) - if strings.Contains(value, charQuote) { - value = strings.Trim(value, "'") - } - envs = append(envs, kobject.EnvVar{ - Name: name, - Value: value, - }) - } else { - return envs, errors.New("Invalid container env") - } - } - } - return envs, nil -} - -// load ports from dab file -func loadPorts(service Service) ([]kobject.Ports, error) { - ports := []kobject.Ports{} - for _, port := range service.Ports { - komposePorts := kobject.Ports{ - HostPort: int32(port.Port), - ContainerPort: int32(port.Port), - Protocol: port.Protocol, - } - if protocol := api.Protocol(port.Protocol); protocol != api.ProtocolTCP && protocol != api.ProtocolUDP { - komposePorts.Protocol = string(api.ProtocolTCP) - } - ports = append(ports, komposePorts) - } - return ports, nil -} - -// LoadFile loads dab file into KomposeObject -func (b *Bundle) LoadFile(files []string) (kobject.KomposeObject, error) { - komposeObject := kobject.KomposeObject{ - ServiceConfigs: make(map[string]kobject.ServiceConfig), - LoadedFrom: "bundle", - } - file := files[0] - buf, err := ioutil.ReadFile(file) - if err != nil { - 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 { - return kobject.KomposeObject{}, errors.Wrap(err, "loadFile failed, Failed to parse bundles file") - } - - noSupKeys := checkUnsupportedKey(bundle) - for _, keyName := range noSupKeys { - log.Warningf("Unsupported %s key - ignoring", keyName) - } - - for name, service := range bundle.Services { - serviceConfig := kobject.ServiceConfig{} - serviceConfig.Command = service.Command - serviceConfig.Args = service.Args - // convert bundle labels to annotations - serviceConfig.Annotations = service.Labels - - image, err := loadImage(service) - 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 != 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 != nil { - return kobject.KomposeObject{}, errors.Wrap(err, "loadPorts failed, Failed to load ports from bundles file") - } - serviceConfig.Port = ports - - if service.WorkingDir != nil { - serviceConfig.WorkingDir = *service.WorkingDir - } - - komposeObject.ServiceConfigs[name] = serviceConfig - } - - return komposeObject, nil -} - -// LoadFile loads a bundlefile from a path to the file -func loadFile(reader io.Reader) (*Bundlefile, error) { - bundlefile := &Bundlefile{} - - decoder := json.NewDecoder(reader) - if err := decoder.Decode(bundlefile); err != nil { - switch jsonErr := err.(type) { - case *json.SyntaxError: - return nil, fmt.Errorf( - "JSON syntax error at byte %v: %s", - jsonErr.Offset, - jsonErr.Error()) - case *json.UnmarshalTypeError: - return nil, fmt.Errorf( - "unexpected type at byte %v. expected %s but received %s", - jsonErr.Offset, - jsonErr.Type, - jsonErr.Value) - } - return nil, err - } - - return bundlefile, nil -} diff --git a/pkg/loader/bundle/bundle_test.go b/pkg/loader/bundle/bundle_test.go deleted file mode 100644 index 8184a7e9..00000000 --- a/pkg/loader/bundle/bundle_test.go +++ /dev/null @@ -1,86 +0,0 @@ -/* -Copyright 2017 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/loader.go b/pkg/loader/loader.go index aded6c8f..bbea343c 100644 --- a/pkg/loader/loader.go +++ b/pkg/loader/loader.go @@ -20,7 +20,6 @@ import ( "fmt" "github.com/kubernetes/kompose/pkg/kobject" - "github.com/kubernetes/kompose/pkg/loader/bundle" "github.com/kubernetes/kompose/pkg/loader/compose" ) @@ -32,16 +31,8 @@ type Loader interface { // GetLoader returns loader for given format func GetLoader(format string) (Loader, error) { - var l Loader - - switch format { - case "bundle": - l = new(bundle.Bundle) - case "compose": - l = new(compose.Compose) - default: + if format != "compose" { return nil, fmt.Errorf("input file format %s is not supported", format) } - - return l, nil + return new(compose.Compose), nil }