Take @nass review into account

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
This commit is contained in:
Vincent Demeester 2017-12-05 09:35:52 +01:00 committed by Silvin Lubecki
parent dedd0db51a
commit b5170bde03
8 changed files with 55 additions and 72 deletions

View File

@ -3,6 +3,8 @@ package command
import ( import (
"os" "os"
"strings" "strings"
"github.com/sirupsen/logrus"
) )
// Orchestrator type acts as an enum describing supported orchestrators. // Orchestrator type acts as an enum describing supported orchestrators.
@ -43,6 +45,7 @@ func GetOrchestrator(orchestrator string) Orchestrator {
return o return o
} }
logrus.Infof("Specified orchestrator %q is invalid. Please use either kubernetes or swarm", orchestrator)
// Nothing set, use default orchestrator // Nothing set, use default orchestrator
return defaultOrchestrator return defaultOrchestrator
} }

View File

@ -19,7 +19,7 @@ type KubeCli struct {
kubeNamespace string 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) { func WrapCli(dockerCli command.Cli, cmd *cobra.Command) (*KubeCli, error) {
var err error var err error
cli := &KubeCli{ cli := &KubeCli{
@ -34,7 +34,7 @@ func WrapCli(dockerCli command.Cli, cmd *cobra.Command) (*KubeCli, error) {
} }
kubeConfig := "" kubeConfig := ""
if cmd.PersistentFlags().Changed("kubeconfig") { if cmd.PersistentFlags().Changed("kubeconfig") {
kubeConfig, err = cmd.PersistentFlags().GetString("namespace") kubeConfig, err = cmd.PersistentFlags().GetString("kubeconfig")
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -1,9 +1,6 @@
package kubernetes package kubernetes
import ( 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" appsv1beta2 "k8s.io/client-go/kubernetes/typed/apps/v1beta2"
typesappsv1beta2 "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" corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
@ -38,23 +35,6 @@ func NewFactory(namespace string, config *restclient.Config) (*Factory, error) {
}, nil }, 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 // ConfigMaps returns a client for kubernetes's config maps
func (s *Factory) ConfigMaps() corev1.ConfigMapInterface { func (s *Factory) ConfigMaps() corev1.ConfigMapInterface {
return s.coreClientSet.ConfigMaps(s.namespace) return s.coreClientSet.ConfigMaps(s.namespace)

View File

@ -4,8 +4,7 @@ import (
"fmt" "fmt"
"sort" "sort"
"github.com/docker/cli/cli/compose/loader" composetypes "github.com/docker/cli/cli/compose/types"
"github.com/docker/cli/cli/compose/types"
apiv1beta1 "github.com/docker/cli/kubernetes/compose/v1beta1" apiv1beta1 "github.com/docker/cli/kubernetes/compose/v1beta1"
"github.com/docker/cli/kubernetes/labels" "github.com/docker/cli/kubernetes/labels"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 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 // IsColliding verify that services defined in the stack collides with already deployed services
func IsColliding(services corev1.ServiceInterface, stack *apiv1beta1.Stack) error { func IsColliding(services corev1.ServiceInterface, stack *apiv1beta1.Stack, cfg *composetypes.Config) error {
stackObjects, err := getServices(stack.Spec.ComposeFile) stackObjects := getServices(cfg)
if err != nil {
return err
}
for _, srv := range stackObjects { for _, srv := range stackObjects {
if err := verify(services, stack.Name, srv); err != nil { if err := verify(services, stack.Name, srv); err != nil {
@ -28,6 +24,9 @@ func IsColliding(services corev1.ServiceInterface, stack *apiv1beta1.Stack) erro
return nil 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 { func verify(services corev1.ServiceInterface, stackName string, service string) error {
svc, err := services.Get(service, metav1.GetOptions{}) svc, err := services.Get(service, metav1.GetOptions{})
if err == nil { if err == nil {
@ -45,27 +44,11 @@ func verify(services corev1.ServiceInterface, stackName string, service string)
return nil return nil
} }
func getServices(composeFile string) ([]string, error) { func getServices(cfg *composetypes.Config) []string {
parsed, err := loader.ParseYAML([]byte(composeFile)) services := make([]string, len(cfg.Services))
if err != nil { for i := range cfg.Services {
return nil, err services[i] = cfg.Services[i].Name
}
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
} }
sort.Strings(services) sort.Strings(services)
return services, nil return services
} }

View File

@ -20,7 +20,7 @@ func RunDeploy(dockerCli *KubeCli, opts options.Deploy) error {
return errors.Errorf("Please specify a Compose file (with --compose-file).") return errors.Errorf("Please specify a Compose file (with --compose-file).")
} }
// Initialize clients // Initialize clients
stacks, err := dockerCli.stacks() stackInterface, err := dockerCli.stacks()
if err != nil { if err != nil {
return err return err
} }
@ -28,12 +28,12 @@ func RunDeploy(dockerCli *KubeCli, opts options.Deploy) error {
if err != nil { if err != nil {
return err return err
} }
configMaps := composeClient.ConfigMaps() configMapInterface := composeClient.ConfigMaps()
secrets := composeClient.Secrets() secretInterface := composeClient.Secrets()
services := composeClient.Services() serviceInterface := composeClient.Services()
pods := composeClient.Pods() podInterface := composeClient.Pods()
watcher := DeployWatcher{ watcher := DeployWatcher{
Pods: pods, Pods: podInterface,
} }
// Parse the compose file // Parse the compose file
@ -43,29 +43,28 @@ func RunDeploy(dockerCli *KubeCli, opts options.Deploy) error {
} }
// FIXME(vdemeester) handle warnings server-side // FIXME(vdemeester) handle warnings server-side
if err = IsColliding(serviceInterface, stack, cfg); err != nil {
if err = IsColliding(services, stack); err != nil {
return err return err
} }
if err = createFileBasedConfigMaps(stack.Name, cfg.Configs, configMaps); err != nil { if err = createFileBasedConfigMaps(stack.Name, cfg.Configs, configMapInterface); err != nil {
return err return err
} }
if err = createFileBasedSecrets(stack.Name, cfg.Secrets, secrets); err != nil { if err = createFileBasedSecrets(stack.Name, cfg.Secrets, secretInterface); err != nil {
return err 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 in.Spec = stack.Spec
if _, err = stacks.Update(in); err != nil { if _, err = stackInterface.Update(in); err != nil {
return err return err
} }
fmt.Printf("Stack %s was updated\n", stack.Name) fmt.Printf("Stack %s was updated\n", stack.Name)
} else { } else {
if _, err = stacks.Create(stack); err != nil { if _, err = stackInterface.Create(stack); err != nil {
return err return err
} }

View File

@ -5,6 +5,8 @@ import (
"github.com/docker/cli/cli/command/formatter" "github.com/docker/cli/cli/command/formatter"
"github.com/docker/cli/cli/command/stack/options" "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" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"vbom.ml/util/sortorder" "vbom.ml/util/sortorder"
) )
@ -45,14 +47,28 @@ func getStacks(kubeCli *KubeCli) ([]*formatter.Stack, error) {
} }
var formattedStacks []*formatter.Stack var formattedStacks []*formatter.Stack
for _, stack := range stacks.Items { for _, stack := range stacks.Items {
services, err := getServices(stack.Spec.ComposeFile) cfg, err := loadStack(stack.Spec.ComposeFile)
if err != nil { if err != nil {
return nil, err return nil, err
} }
formattedStacks = append(formattedStacks, &formatter.Stack{ formattedStacks = append(formattedStacks, &formatter.Stack{
Name: stack.Name, Name: stack.Name,
Services: len(services), Services: len(getServices(cfg)),
}) })
} }
return formattedStacks, nil 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,
},
},
})
}

View File

@ -17,6 +17,7 @@ import (
) )
// LoadStack loads a stack from a Compose file, with a given name. // 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) { func LoadStack(name, composeFile string) (*apiv1beta1.Stack, *composetypes.Config, error) {
if composeFile == "" { if composeFile == "" {
return nil, nil, errors.New("compose-file must be set") 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) binary, err := ioutil.ReadFile(composePath)
if err != nil { 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 }) processed, err := template.Substitute(string(binary), func(key string) (string, bool) { return env[key], true })
if err != nil { if err != nil {
return nil, nil, errors.Wrap(err, "cannot load compose file") 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{ cfg, err := loader.Load(composetypes.ConfigDetails{
WorkingDir: ".", WorkingDir: workingDir,
ConfigFiles: []composetypes.ConfigFile{ ConfigFiles: []composetypes.ConfigFile{
{ {
Config: parsed, Config: parsed,
@ -111,9 +113,9 @@ func processEnvFiles(input string, parsed map[string]interface{}, config *compos
return string(res), nil return string(res), nil
} }
func env() map[string]string { func env(workingDir string) map[string]string {
// Apply .env file first // Apply .env file first
config := readEnvFile(".env") config := readEnvFile(filepath.Join(workingDir, ".env"))
// Apply env variables // Apply env variables
for k, v := range envToMap(os.Environ()) { for k, v := range envToMap(os.Environ()) {

View File

@ -27,7 +27,7 @@ func TestPlaceholders(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
output, _, err := load("stack", []byte(test.input), env) output, _, err := load("stack", []byte(test.input), ".", env)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, test.expectedOutput, output.Spec.ComposeFile) assert.Equal(t, test.expectedOutput, output.Spec.ComposeFile)
} }