-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add kubernetes support to docker/cli #721
Conversation
Codecov Report
@@ Coverage Diff @@
## master #721 +/- ##
=========================================
- Coverage 53.46% 50.9% -2.57%
=========================================
Files 218 237 +19
Lines 14642 15338 +696
=========================================
- Hits 7829 7808 -21
- Misses 6327 7028 +701
- Partials 486 502 +16 |
afe4fc0
to
6c2693f
Compare
unset = Orchestrator("unset") | ||
|
||
defaultOrchestrator = Swarm | ||
dockerOrchestrator = "DOCKER_ORCHESTRATOR" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/dockerOrchestrator/envVarDockerOrchestrator
so it's obvious this is an environment variable key and not something else.
return o | ||
} | ||
// Check config file | ||
if configFile := cliconfig.LoadDefaultConfigFile(dockerCli.Err()); configFile != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is going to break anyone that uses the --config
flag because GetOrchestrator()
is called from New...Comand
which happens before flag parsing.
@@ -0,0 +1,88 @@ | |||
package clientset |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This path is kind of overly long.
How about moving cli/command/stack/kubernetes
to /kubernetes
? It's not really part of the command router, so it doesn't belong under cli/command
.
(more on this here #721 (comment))
@@ -0,0 +1,4 @@ | |||
// This package is generated by client-gen with custom arguments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is client-gen
? It's strange that it only puts this comment in doc.go
and not in every file.
Can we include the steps to re-generate these files in a go:generate
comment, or in the Makefile?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If possible could we have this tool write out the "this file is generated" comment to every file it generates, not just doc
. There is actually a GO convention for this which it should use:
^// Code generated .* DO NOT EDIT\.$
e2e/orchestrator/version_test.go
Outdated
assert.Contains(t, result.Stdout(), "Orchestrator: kubernetes") | ||
} | ||
|
||
func TestVersionWithOverridenConfigOrchestrator(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/docker/cli/blob/master/TESTING.md#guidelines
e2e tests are for commands. orchestrator
is not a command. Please move one of these to e2e/system/version_test.go
.
The rest should not be e2e tests. They test client-only logic, so they should be unit tests.
vendor.conf
Outdated
github.com/go-openapi/jsonpointer 46af16f9f7b149af66e5d1bd010e3574dc06de98 | ||
github.com/go-openapi/jsonreference 13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272 | ||
github.com/go-openapi/spec 6aced65f8501fe1217321abf0749d354824ba2ff | ||
github.com/go-openapi/swag 1d0bd113de87027671077d3c71eb3ac5d7dbba72 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to look into some of these dependencies, this still seems like a crazy number of dependencies for a client.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess the k8s machinery dependencies are just bloated, so there isn't much we can do about this for now...
@@ -0,0 +1,53 @@ | |||
package orchestrator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this be in cli/command
package, or are there issues with circular imports?
cli/command/stack/cmd.go
Outdated
kubernetes.AddStackCommands(cmd, dockerCli) | ||
case orchestrator.Swarm: | ||
swarm.AddStackCommands(cmd, dockerCli) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see how this can work if we want to be able to set the orchestrator from the config or a flag, and I think we should support both. This happens before flag parsing and config loading (which depends on flag parsing).
I think this branching needs to be done as part of the Run()
of the commands.
The same commands exits for both orchestrators, so the implementation of the Run()
can be switch and call the Run()
for the correct orchestrator.
For directory structure, how about this: Move non-generated code out of the generated code tree:
Move generated code out of the
Add a README to the
|
cli/command/stack/cmd.go
Outdated
case orchestrator.Kubernetes: | ||
cmd = kubernetes.NewTopLevelDeployCommand(dockerCli) | ||
case orchestrator.Swarm: | ||
cmd = swarm.NewTopLevelDeployCommand(dockerCli) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nil panic on default?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I meant it should not panic)
cli/command/stack/cmd.go
Outdated
cmd = kubernetes.NewTopLevelDeployCommand(dockerCli) | ||
case orchestrator.Swarm: | ||
cmd = swarm.NewTopLevelDeployCommand(dockerCli) | ||
} | ||
// Remove the aliases at the top level | ||
cmd.Aliases = []string{} | ||
cmd.Annotations = map[string]string{"experimental": "", "version": "1.25"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"version_kubernetes": "v1beta1"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AkihiroSuda depends on what you wanna check here : the version of kubernetes, the version of the "k8s" native api or the version of our api extension — or all of those 😝.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the version of the stack API extension is correct to check here. (And the stack API extension may provide info for minimum supported k8s version and so on)
cli/command/stack/common/opts.go
Outdated
flags.StringVarP(opt, "compose-file", "c", "", "Path to a Compose file") | ||
flags.SetAnnotation("compose-file", "version", []string{"1.25"}) | ||
} | ||
|
||
func addBundlefileFlag(opt *string, flags *pflag.FlagSet) { | ||
// AddBundlefileFlag adds bundle-file file to the specified flagset | ||
func AddBundlefileFlag(opt *string, flags *pflag.FlagSet) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess you can drop support for DAB now
|
||
// ComposeV1beta1 retrieves the ComposeV1beta1Client | ||
func (c *Clientset) ComposeV1beta1() composev1beta1.ComposeV1beta1Interface { | ||
if c == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How c can be nil?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's generated code 😓
vendor.conf
Outdated
@@ -1,7 +1,7 @@ | |||
github.com/agl/ed25519 d2b94fd789ea21d12fac1a4443dd3a3f79cda72c | |||
github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109 | |||
github.com/containerd/continuity 35d55c5e8dd23b32037d56cf97174aff3efdfa83 | |||
github.com/coreos/etcd v3.2.1 | |||
# github.com/coreos/etcd v3.2.1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can remove comments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes please, also would be good to keep this file sorted. Right now the diff is hard to read because some deps moved
It might be helpful to add |
987aabb
to
6851afc
Compare
@vdemeester once the CI is green, could we maybe split the PR in 3 commits: vendor, generated code and actual new code ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First step of review on this big PR.
Congrats for the work 👏
cli/command/orchestrator.go
Outdated
orchestratorUnset = Orchestrator("unset") | ||
|
||
defaultOrchestrator = OrchestratorSwarm | ||
dockerOrchestrator = "DOCKER_ORCHESTRATOR" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: dockerEnvOrchestrator?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ping
cli/command/orchestrator.go
Outdated
|
||
func normalize(flag string) Orchestrator { | ||
switch strings.ToLower(flag) { | ||
case "kubernetes", "k8s": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would store these hardcoded accepted values in const variables and refer to them in tests.
cli/command/orchestrator.go
Outdated
func GetOrchestrator(orchestrator string) Orchestrator { | ||
// Check environment variable | ||
env := os.Getenv(dockerOrchestrator) | ||
if o := normalize(env); o != orchestratorUnset { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should make sure we specify the precedence in:
environment variable > config file > default
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also if makes a typo on "kubernetes" or "k8s", it will default to swarm without any warning/failure. Should we fail (as the user might want to use kubernetes) or at least have a warning that the environment variable value was invalid?
) | ||
flags := cmd.PersistentFlags() | ||
flags.String("namespace", "default", "Kubernetes namespace to use") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd probably be cleaner to have those flags in a stackOptions
struct
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't do that one, given how cobra works, we can just use PersistentFlags().Changed("namespace")
for those.
cli/command/stack/kubernetes/cli.go
Outdated
kubeNamespace string | ||
} | ||
|
||
// WrapCli wraps command.Cli with kubernetes specifiecs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit-typo: specifics
} | ||
|
||
// Parse the compose file | ||
stack, cfg, err := LoadStack(opts.Namespace, opts.Composefile) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have stack
and stacks
variables where one is not the subset of the other, it's kinda confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😝
return err | ||
} | ||
|
||
if in, err := stacks.Get(stack.Name, metav1.GetOptions{}); err == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You also may want to rename in
to stackObject
or something more explicit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also the Get
call might fail for other reasons than the stack.Name
not being referenced. Worth adding a test on the error value before trying a stacks.Create
?
} | ||
|
||
cfg, err := loader.Load(composetypes.ConfigDetails{ | ||
WorkingDir: ".", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as before for the os.Getwd()
for k, v := range svc.Environment { | ||
env[k] = v | ||
} | ||
parsed["services"].(iMap)[svc.Name].(iMap)["environment"] = env |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we have some form of checking of key presence in the successive maps?
|
||
for _, value := range env { | ||
parts := strings.SplitN(value, "=", 2) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should have length checking here too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, it comes from os.Environ
so.. it will always have the =
I think. That said, this whole file should go away in a follow up
@thaJeztah I believe @silvin-lubecki and I found the issue with secrets: you cannot have a kube secret with an underscore. kubectl create secret generic my_secret --from-literal=file=toto
The Secret "my_secret" is invalid: metadata.name: Invalid value: "my_secret": a DNS-1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*') If you change your example to The underlying issue is that we weren't checking for returned errors when creating the secret. Silvin will push a fix for this. |
oh, awesome! was just running this PR again for another test 👍 |
Signed-off-by: Silvin Lubecki <silvin.lubecki@docker.com>
PTAL @thaJeztah, just fixed it. |
Is it correct that we don't namespace services from a stack? Deploy the stack as "foobar"; $ docker stack deploy -c docker-compose.yml foobar
Stack foobar was created
Waiting for the stack to be stable and running...
- Service db has one container running
- Service web has one container running
Stack foobar is stable and running Deployg the stack as "foobar2": $ docker stack deploy -c docker-compose.yml foobar2
service db already present in stack named foobar We should have a solution for that, because multiple stacks will have the same service names (and this is the reason (when using |
Hm, we also need to find a solution for re-deploying a stack that uses secrets. Secrets are immutable, but when using swarmkit, docker detects that if the content didn't change, and in that case skips creating/updating the secret (and only produces an error if the secret/config changed); $ docker stack deploy -c docker-compose.yml foobar
secrets "mysecret" already exists |
@thaJeztah we should create issues for those to discuss them and keep track of updates on it 👼 |
@thaJeztah you can use the namespace flag:
|
@silvin-lubecki yes; probably something we should do by default (e.g., create a namespace @vdemeester I'll create an issue 👍 |
Regarding However, with moby/moby#22554, I don't think this is still a problem, so we may want to change this behavior and, for Docker 1.12 and up, enable the default seccomp profile I'll open an issue for that as well |
@thaJeztah yes we thought about that too, but as we wanted to keep it simple, we decided to put everything in default namespace. Depending the feedbacks we will receive, we may change it in a further PR? |
I don't think we should add the namespace flag and ignore the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we can fix the remaining issues in a follow up
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM as well; it's a preview, so let's get this out so that people can play with it 👍
🎉 |
When I created the YAML files for 18.01 after this patch was applied, the one for |
This PR introduces
docker stack
support forkubernetes
in the docker cli using the docker compose controller embedded in docker4mac beta.orchestrator
config field, a new--orchestrator
and aDOCKER_ORCHESTRATOR
environment variable to switch between swarm and kubernetes orchestrators.kubernetes
mode:--namespace
onstack
subcommands to be able to point to the correct kubernetes namespace--kubeconfig
flag tostack
subcommands — and takeKUBECONFIG
environment variable into account — to be able to point to a kubernetes cluster using the kubernetes configuration file.--filter
ondocker stack services
are disabled.secret
,service
,config
,node
will error out because they are not implemented yet (it's on the way, but won't be present in this PR)This adds new
kubernetes
andswarm
annotation to define if a command/flag is supported by one of the other (or both).All of this is marked experimental (thus we depend on #758 for this one to get merged)
🦁
Signed-off-by: Vincent Demeester vincent@sbr.pm
Signed-off-by: Silvin Lubecki silvin.lubecki@docker.com