Skip to content

Commit

Permalink
Merge pull request #324 from rtnpro/unsupported-keys-per-provider
Browse files Browse the repository at this point in the history
Unsupported keys per provider
  • Loading branch information
surajssd authored Dec 21, 2016
2 parents 5ddd48e + 3419ae7 commit 48e3ba8
Show file tree
Hide file tree
Showing 10 changed files with 475 additions and 139 deletions.
144 changes: 28 additions & 116 deletions pkg/kobject/kobject.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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()]++
}
}
}
}
54 changes: 53 additions & 1 deletion pkg/loader/bundle/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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 := "@"
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down
87 changes: 87 additions & 0 deletions pkg/loader/bundle/bundle_test.go
Original file line number Diff line number Diff line change
@@ -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, "', '"))
}
}

}
Loading

0 comments on commit 48e3ba8

Please sign in to comment.