Add support for kubernetes in docker cli
- Add support for kubernetes for docker stack command - Update to go 1.9 - Add kubernetes to vendors - Print orchestrator in docker version command Signed-off-by: Vincent Demeester <vincent@sbr.pm> Signed-off-by: Silvin Lubecki <silvin.lubecki@docker.com>
This commit is contained in:
committed by
Silvin Lubecki
parent
70db7cc0fc
commit
8417e49792
158
cli/command/stack/kubernetes/deploy.go
Normal file
158
cli/command/stack/kubernetes/deploy.go
Normal file
@ -0,0 +1,158 @@
|
||||
package kubernetes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/stack/common"
|
||||
composeTypes "github.com/docker/cli/cli/compose/types"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
)
|
||||
|
||||
type deployOptions struct {
|
||||
composefile string
|
||||
stack string
|
||||
}
|
||||
|
||||
func newDeployCommand(dockerCli command.Cli, kubeCli *kubeCli) *cobra.Command {
|
||||
var opts deployOptions
|
||||
cmd := &cobra.Command{
|
||||
Use: "deploy [OPTIONS] STACK",
|
||||
Aliases: []string{"up"},
|
||||
Short: "Deploy a new stack or update an existing stack",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.stack = args[0]
|
||||
return runDeploy(dockerCli, kubeCli, opts)
|
||||
},
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
common.AddComposefileFlag(&opts.composefile, flags)
|
||||
// FIXME(vdemeester) other flags ? (bundlefile, registry-auth, prune, resolve-image) ?
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runDeploy(dockerCli command.Cli, kubeCli *kubeCli, opts deployOptions) error {
|
||||
cmdOut := dockerCli.Out()
|
||||
// Check arguments
|
||||
if opts.composefile == "" {
|
||||
return errors.Errorf("Please specify a Compose file (with --compose-file).")
|
||||
}
|
||||
// Initialize clients
|
||||
stacks, err := kubeCli.Stacks()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
composeClient, err := kubeCli.ComposeClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
configMaps := composeClient.ConfigMaps()
|
||||
secrets := composeClient.Secrets()
|
||||
services := composeClient.Services()
|
||||
pods := composeClient.Pods()
|
||||
watcher := DeployWatcher{
|
||||
Pods: pods,
|
||||
}
|
||||
|
||||
// Parse the compose file
|
||||
stack, cfg, err := LoadStack(opts.stack, opts.composefile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// FIXME(vdemeester) handle warnings server-side
|
||||
|
||||
if err = IsColliding(services, stack); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = createFileBasedConfigMaps(stack.Name, cfg.Configs, configMaps); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = createFileBasedSecrets(stack.Name, cfg.Secrets, secrets); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if in, err := stacks.Get(stack.Name, metav1.GetOptions{}); err == nil {
|
||||
in.Spec = stack.Spec
|
||||
|
||||
if _, err = stacks.Update(in); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Stack %s was updated\n", stack.Name)
|
||||
} else {
|
||||
if _, err = stacks.Create(stack); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(cmdOut, "Stack %s was created\n", stack.Name)
|
||||
}
|
||||
|
||||
fmt.Fprintln(cmdOut, "Waiting for the stack to be stable and running...")
|
||||
|
||||
<-watcher.Watch(stack, serviceNames(cfg))
|
||||
|
||||
fmt.Fprintf(cmdOut, "Stack %s is stable and running\n\n", stack.Name)
|
||||
// fmt.Fprintf(cmdOut, "Read the logs with:\n $ %s stack logs %s\n", filepath.Base(os.Args[0]), stack.Name)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createFileBasedConfigMaps creates a Kubernetes ConfigMap for each Compose global file-based config.
|
||||
func createFileBasedConfigMaps(stackName string, globalConfigs map[string]composeTypes.ConfigObjConfig, configMaps corev1.ConfigMapInterface) error {
|
||||
for name, config := range globalConfigs {
|
||||
if config.File == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
fileName := path.Base(config.File)
|
||||
content, err := ioutil.ReadFile(config.File)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configMap := toConfigMap(stackName, name, fileName, content)
|
||||
configMaps.Create(configMap)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func serviceNames(cfg *composeTypes.Config) []string {
|
||||
names := []string{}
|
||||
|
||||
for _, service := range cfg.Services {
|
||||
names = append(names, service.Name)
|
||||
}
|
||||
|
||||
return names
|
||||
}
|
||||
|
||||
// createFileBasedSecrets creates a Kubernetes Secret for each Compose global file-based secret.
|
||||
func createFileBasedSecrets(stackName string, globalSecrets map[string]composeTypes.SecretConfig, secrets corev1.SecretInterface) error {
|
||||
for name, secret := range globalSecrets {
|
||||
if secret.File == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
fileName := path.Base(secret.File)
|
||||
content, err := ioutil.ReadFile(secret.File)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
secret := toSecret(stackName, name, fileName, content)
|
||||
secrets.Create(secret)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user