diff --git a/cli/command/orchestrator.go b/cli/command/orchestrator.go index 5c7dd08a3b..9fa9c713d3 100644 --- a/cli/command/orchestrator.go +++ b/cli/command/orchestrator.go @@ -3,6 +3,8 @@ package command import ( "os" "strings" + + "github.com/sirupsen/logrus" ) // Orchestrator type acts as an enum describing supported orchestrators. @@ -43,6 +45,7 @@ func GetOrchestrator(orchestrator string) Orchestrator { return o } + logrus.Infof("Specified orchestrator %q is invalid. Please use either kubernetes or swarm", orchestrator) // Nothing set, use default orchestrator return defaultOrchestrator } diff --git a/cli/command/stack/kubernetes/cli.go b/cli/command/stack/kubernetes/cli.go index 74c77891c1..44790d7819 100644 --- a/cli/command/stack/kubernetes/cli.go +++ b/cli/command/stack/kubernetes/cli.go @@ -19,7 +19,7 @@ type KubeCli struct { kubeNamespace string } -// WrapCli wraps command.Cli with kubernetes specifiecs +// WrapCli wraps command.Cli with kubernetes specifics func WrapCli(dockerCli command.Cli, cmd *cobra.Command) (*KubeCli, error) { var err error cli := &KubeCli{ @@ -34,7 +34,7 @@ func WrapCli(dockerCli command.Cli, cmd *cobra.Command) (*KubeCli, error) { } kubeConfig := "" if cmd.PersistentFlags().Changed("kubeconfig") { - kubeConfig, err = cmd.PersistentFlags().GetString("namespace") + kubeConfig, err = cmd.PersistentFlags().GetString("kubeconfig") if err != nil { return nil, err } diff --git a/cli/command/stack/kubernetes/client.go b/cli/command/stack/kubernetes/client.go index 168436ef92..a44bf44590 100644 --- a/cli/command/stack/kubernetes/client.go +++ b/cli/command/stack/kubernetes/client.go @@ -1,9 +1,6 @@ package kubernetes import ( - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/client-go/discovery" - "k8s.io/client-go/kubernetes" appsv1beta2 "k8s.io/client-go/kubernetes/typed/apps/v1beta2" typesappsv1beta2 "k8s.io/client-go/kubernetes/typed/apps/v1beta2" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" @@ -38,23 +35,6 @@ func NewFactory(namespace string, config *restclient.Config) (*Factory, error) { }, nil } -// RESTMapper returns a PriorityRESTMapper based on the discovered -// groups and resources passed in. -func (s *Factory) RESTMapper() (meta.RESTMapper, error) { - clientSet, err := kubernetes.NewForConfig(s.config) - if err != nil { - return nil, err - } - - groupResources, err := discovery.GetAPIGroupResources(clientSet.DiscoveryClient) - if err != nil { - return nil, err - } - mapper := discovery.NewRESTMapper(groupResources, meta.InterfacesForUnstructured) - - return mapper, nil -} - // ConfigMaps returns a client for kubernetes's config maps func (s *Factory) ConfigMaps() corev1.ConfigMapInterface { return s.coreClientSet.ConfigMaps(s.namespace) diff --git a/cli/command/stack/kubernetes/collision.go b/cli/command/stack/kubernetes/collision.go index 8421e07f2f..283e792f88 100644 --- a/cli/command/stack/kubernetes/collision.go +++ b/cli/command/stack/kubernetes/collision.go @@ -4,8 +4,7 @@ import ( "fmt" "sort" - "github.com/docker/cli/cli/compose/loader" - "github.com/docker/cli/cli/compose/types" + composetypes "github.com/docker/cli/cli/compose/types" apiv1beta1 "github.com/docker/cli/kubernetes/compose/v1beta1" "github.com/docker/cli/kubernetes/labels" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -13,11 +12,8 @@ import ( ) // IsColliding verify that services defined in the stack collides with already deployed services -func IsColliding(services corev1.ServiceInterface, stack *apiv1beta1.Stack) error { - stackObjects, err := getServices(stack.Spec.ComposeFile) - if err != nil { - return err - } +func IsColliding(services corev1.ServiceInterface, stack *apiv1beta1.Stack, cfg *composetypes.Config) error { + stackObjects := getServices(cfg) for _, srv := range stackObjects { if err := verify(services, stack.Name, srv); err != nil { @@ -28,6 +24,9 @@ func IsColliding(services corev1.ServiceInterface, stack *apiv1beta1.Stack) erro return nil } +// verify checks wether the service is already present in kubernetes. +// If we find the service by name but it doesn't have our label or it has a different value +// than the stack name for the label, we fail (i.e. it will collide) func verify(services corev1.ServiceInterface, stackName string, service string) error { svc, err := services.Get(service, metav1.GetOptions{}) if err == nil { @@ -45,27 +44,11 @@ func verify(services corev1.ServiceInterface, stackName string, service string) return nil } -func getServices(composeFile string) ([]string, error) { - parsed, err := loader.ParseYAML([]byte(composeFile)) - if err != nil { - return nil, err - } - - config, err := loader.Load(types.ConfigDetails{ - WorkingDir: ".", - ConfigFiles: []types.ConfigFile{ - { - Config: parsed, - }, - }, - }) - if err != nil { - return nil, err - } - services := make([]string, len(config.Services)) - for i := range config.Services { - services[i] = config.Services[i].Name +func getServices(cfg *composetypes.Config) []string { + services := make([]string, len(cfg.Services)) + for i := range cfg.Services { + services[i] = cfg.Services[i].Name } sort.Strings(services) - return services, nil + return services } diff --git a/cli/command/stack/kubernetes/deploy.go b/cli/command/stack/kubernetes/deploy.go index e350d353d6..da4679d2e7 100644 --- a/cli/command/stack/kubernetes/deploy.go +++ b/cli/command/stack/kubernetes/deploy.go @@ -20,7 +20,7 @@ func RunDeploy(dockerCli *KubeCli, opts options.Deploy) error { return errors.Errorf("Please specify a Compose file (with --compose-file).") } // Initialize clients - stacks, err := dockerCli.stacks() + stackInterface, err := dockerCli.stacks() if err != nil { return err } @@ -28,12 +28,12 @@ func RunDeploy(dockerCli *KubeCli, opts options.Deploy) error { if err != nil { return err } - configMaps := composeClient.ConfigMaps() - secrets := composeClient.Secrets() - services := composeClient.Services() - pods := composeClient.Pods() + configMapInterface := composeClient.ConfigMaps() + secretInterface := composeClient.Secrets() + serviceInterface := composeClient.Services() + podInterface := composeClient.Pods() watcher := DeployWatcher{ - Pods: pods, + Pods: podInterface, } // Parse the compose file @@ -43,29 +43,28 @@ func RunDeploy(dockerCli *KubeCli, opts options.Deploy) error { } // FIXME(vdemeester) handle warnings server-side - - if err = IsColliding(services, stack); err != nil { + if err = IsColliding(serviceInterface, stack, cfg); err != nil { return err } - if err = createFileBasedConfigMaps(stack.Name, cfg.Configs, configMaps); err != nil { + if err = createFileBasedConfigMaps(stack.Name, cfg.Configs, configMapInterface); err != nil { return err } - if err = createFileBasedSecrets(stack.Name, cfg.Secrets, secrets); err != nil { + if err = createFileBasedSecrets(stack.Name, cfg.Secrets, secretInterface); err != nil { return err } - if in, err := stacks.Get(stack.Name, metav1.GetOptions{}); err == nil { + if in, err := stackInterface.Get(stack.Name, metav1.GetOptions{}); err == nil { in.Spec = stack.Spec - if _, err = stacks.Update(in); err != nil { + if _, err = stackInterface.Update(in); err != nil { return err } fmt.Printf("Stack %s was updated\n", stack.Name) } else { - if _, err = stacks.Create(stack); err != nil { + if _, err = stackInterface.Create(stack); err != nil { return err } diff --git a/cli/command/stack/kubernetes/list.go b/cli/command/stack/kubernetes/list.go index 0fae8b981a..5b4d229676 100644 --- a/cli/command/stack/kubernetes/list.go +++ b/cli/command/stack/kubernetes/list.go @@ -5,6 +5,8 @@ import ( "github.com/docker/cli/cli/command/formatter" "github.com/docker/cli/cli/command/stack/options" + "github.com/docker/cli/cli/compose/loader" + composetypes "github.com/docker/cli/cli/compose/types" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "vbom.ml/util/sortorder" ) @@ -45,14 +47,28 @@ func getStacks(kubeCli *KubeCli) ([]*formatter.Stack, error) { } var formattedStacks []*formatter.Stack for _, stack := range stacks.Items { - services, err := getServices(stack.Spec.ComposeFile) + cfg, err := loadStack(stack.Spec.ComposeFile) if err != nil { return nil, err } formattedStacks = append(formattedStacks, &formatter.Stack{ Name: stack.Name, - Services: len(services), + Services: len(getServices(cfg)), }) } return formattedStacks, nil } + +func loadStack(composefile string) (*composetypes.Config, error) { + parsed, err := loader.ParseYAML([]byte(composefile)) + if err != nil { + return nil, err + } + return loader.Load(composetypes.ConfigDetails{ + ConfigFiles: []composetypes.ConfigFile{ + { + Config: parsed, + }, + }, + }) +} diff --git a/cli/command/stack/kubernetes/loader.go b/cli/command/stack/kubernetes/loader.go index b4da2bfb01..c32517527a 100644 --- a/cli/command/stack/kubernetes/loader.go +++ b/cli/command/stack/kubernetes/loader.go @@ -17,6 +17,7 @@ import ( ) // LoadStack loads a stack from a Compose file, with a given name. +// FIXME(vdemeester) remove this and use cli/compose/loader for both swarm and kubernetes func LoadStack(name, composeFile string) (*apiv1beta1.Stack, *composetypes.Config, error) { if composeFile == "" { return nil, nil, errors.New("compose-file must be set") @@ -38,13 +39,14 @@ func LoadStack(name, composeFile string) (*apiv1beta1.Stack, *composetypes.Confi binary, err := ioutil.ReadFile(composePath) if err != nil { - return nil, nil, errors.Wrap(err, "cannot load compose file") + return nil, nil, errors.Wrap(err, "cannot read compose file") } - return load(name, binary, env()) + env := env(workingDir) + return load(name, binary, workingDir, env) } -func load(name string, binary []byte, env map[string]string) (*apiv1beta1.Stack, *composetypes.Config, error) { +func load(name string, binary []byte, workingDir string, env map[string]string) (*apiv1beta1.Stack, *composetypes.Config, error) { processed, err := template.Substitute(string(binary), func(key string) (string, bool) { return env[key], true }) if err != nil { return nil, nil, errors.Wrap(err, "cannot load compose file") @@ -56,7 +58,7 @@ func load(name string, binary []byte, env map[string]string) (*apiv1beta1.Stack, } cfg, err := loader.Load(composetypes.ConfigDetails{ - WorkingDir: ".", + WorkingDir: workingDir, ConfigFiles: []composetypes.ConfigFile{ { Config: parsed, @@ -111,9 +113,9 @@ func processEnvFiles(input string, parsed map[string]interface{}, config *compos return string(res), nil } -func env() map[string]string { +func env(workingDir string) map[string]string { // Apply .env file first - config := readEnvFile(".env") + config := readEnvFile(filepath.Join(workingDir, ".env")) // Apply env variables for k, v := range envToMap(os.Environ()) { diff --git a/cli/command/stack/kubernetes/loader_test.go b/cli/command/stack/kubernetes/loader_test.go index 0b9808990a..8c5f920393 100644 --- a/cli/command/stack/kubernetes/loader_test.go +++ b/cli/command/stack/kubernetes/loader_test.go @@ -27,7 +27,7 @@ func TestPlaceholders(t *testing.T) { } for _, test := range tests { - output, _, err := load("stack", []byte(test.input), env) + output, _, err := load("stack", []byte(test.input), ".", env) require.NoError(t, err) assert.Equal(t, test.expectedOutput, output.Spec.ComposeFile) }