Skip to content
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

Handle multiple stack api versions #899

Merged

Conversation

silvin-lubecki
Copy link
Contributor

- What I did
I integrated the new version of the Stack API (v1beta2). The CLI can now communicates with both versions.

⚠️ This PR has a dependency on #898 ⚠️

- How I did it
I added a common interface between v1beta1 and v1beta2 stack clients and one common object stack, so the code in the commands remains the same.

- How to verify it
Execute a docker version command against latest docker for mac, it should output at the end:

 Orchestrator:
   Kubernetes:  v1.9.2
   Stack:       v1beta2

- Description for the changelog

  • Add support for the new Stack API for Kubernetes v1beta2

- A picture of a cute animal (not mandatory but encouraged)

image

@codecov-io
Copy link

codecov-io commented Feb 22, 2018

Codecov Report

❗ No coverage uploaded for pull request base (master@99bd7ed). Click here to learn what that means.
The diff coverage is 23.17%.

@@            Coverage Diff            @@
##             master     #899   +/-   ##
=========================================
  Coverage          ?   53.51%           
=========================================
  Files             ?      266           
  Lines             ?    16891           
  Branches          ?        0           
=========================================
  Hits              ?     9040           
  Misses            ?     7248           
  Partials          ?      603

Copy link
Collaborator

@vdemeester vdemeester left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • I think we can clean more code. There is quite a few code in the kubernetes api package that are dead code (in terms of client's code) — like all the Clone functions, Discovery, etc..
  • We should re-enable linters of kubernetes package(s)
  • I wonder what is bringing golang-lru and the other new vendoring code 🤔


fileName := path.Base(config.File)
content, err := ioutil.ReadFile(config.File)
if err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Do we want to group errors ?
  • If a file can't read (but not the first one), a configMap will be created in k8s and not used (orphan) — and will probably cause errors next time we do the docker stack deploy, right ?

I think we should "read all", and if there is no error, "create all". Then, if one of the creation fails, what should we do with the previous one created ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I'll fix that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I propose to fix this in a follow-up, as we may remove all configmaps and secrets if any error occurs, even while deploying the stack.


fileName := path.Base(secret.File)
content, err := ioutil.ReadFile(secret.File)
if err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll fix that too.

stacks composev1beta2.StackInterface
}

func (c *KubeCli) newStackV1Beta2() (stackClient, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather have func newStackV1Beta2(config *rest.Config, namespace string) … here (not tying it up with KubeCli)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, fixed

stacks composev1beta1.StackInterface
}

func (c *KubeCli) newStackV1Beta1() (stackClient, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather have func newStackV1Beta1(config *rest.Config, namespace string) … here (not tying it up with KubeCli)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, fixed

@@ -1,4 +0,0 @@
# Kubernetes client libraries
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we keep the README ? 👼

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure!

glog "github.com/golang/glog"
discovery "k8s.io/client-go/discovery"
rest "k8s.io/client-go/rest"
flowcontrol "k8s.io/client-go/util/flowcontrol"
)

// Interface defines the methods a compose kube client should have
// FIXME(vdemeester) is it required ?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah 😅 I need to find the answer for it 😛

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes please 😄

// Deprecated: Compose retrieves the default version of ComposeClient.
// Please explicitly pick a version.
// Compose retrieves the default version of ComposeClient.
// deprecated: please explicitly pick a version.
func (c *Clientset) Compose() composev1beta1.ComposeV1beta1Interface {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hum even if it's deprecated, should we return ComposeV1beta2Client here instead 👼

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or I may remove it...

@silvin-lubecki silvin-lubecki force-pushed the handle-multiple-stack-api-versions branch 2 times, most recently from 38df84c to 96b7779 Compare February 26, 2018 00:41
@silvin-lubecki silvin-lubecki force-pushed the handle-multiple-stack-api-versions branch from 96b7779 to b26d649 Compare February 28, 2018 22:21
@thaJeztah
Copy link
Member

looks like this needs a rebase

@silvin-lubecki silvin-lubecki force-pushed the handle-multiple-stack-api-versions branch from b26d649 to eb91268 Compare March 13, 2018 10:47
@silvin-lubecki
Copy link
Contributor Author

Done @thaJeztah

@thaJeztah
Copy link
Member

@silvin-lubecki @vdemeester what's the status on this one? I think there's some outstanding review comments, but perhaps I looked wrong 😅

@silvin-lubecki silvin-lubecki force-pushed the handle-multiple-stack-api-versions branch from eb91268 to e89100b Compare March 27, 2018 13:59
@silvin-lubecki
Copy link
Contributor Author

I think I addressed all @vdemeester comments, but I'd be pleased to hear your comments @thaJeztah 😄
Also #903 depends on this PR.

Copy link
Collaborator

@vdemeester vdemeester left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🐸

)

// LoadStack loads a stack from a Compose config, with a given name.
func LoadStack(name string, cfg composetypes.Config) (*apiv1beta1.Stack, error) {
func loadStack(name string, cfg composetypes.Config) (stack, error) {
res, err := yaml.Marshal(cfg)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we only need to do that in v1beta1 case right ? We shouldn't even "try" to do it all the time.

@silvin-lubecki
Copy link
Contributor Author

@vdemeester PTAL

@@ -100,6 +103,18 @@ func verify(services corev1.ServiceInterface, stackName string, service string)
return nil
}

func (s *stackV1Beta1) LoadStack(name string, cfg composetypes.Config) (stack, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: not sure if LoadStack is the correct name for those too 👼

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PTAL

@silvin-lubecki silvin-lubecki force-pushed the handle-multiple-stack-api-versions branch from 48bf2ad to 92ba690 Compare March 30, 2018 09:04
Copy link
Collaborator

@vdemeester vdemeester left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🦁

@silvin-lubecki
Copy link
Contributor Author

@thaJeztah PTAL

Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left some comments and questions

}

func fromComposeServiceConfig(s composeTypes.ServiceConfig) v1beta2.ServiceConfig {
var userID *int64
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we use an int elsewhere (uids are 32-bit max IIRC (https://serverfault.com/a/585435))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This userID is used in the compose controller as a Kubernetes SecurityContext.RunAsUser (https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/api/core/v1/types.go#L4889) which is an *int64. And by the way the user is declared as a string in the Compose ServiceConfig type.

numerical, err := strconv.Atoi(s.User)
if err == nil {
unixUserID := int64(numerical)
userID = &unixUserID
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a nice (clean) approach as well;

func uint32Ptr(value uint32) *uint32 {
return &value
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, fixed!

return c
}

func fromComposeHealthcheck(h *composeTypes.HealthCheckConfig) *v1beta2.HealthCheckConfig {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was comparing these to their "swarm mode" counterparts, and wondering if we need validation/error-handling for these conversions (could be a follow-up), e.g. comparing to

func convertHealthcheck(healthcheck *composetypes.HealthCheckConfig) (*container.HealthConfig, error) {
if healthcheck == nil {
return nil, nil
}
var (
timeout, interval, startPeriod time.Duration
retries int
)
if healthcheck.Disable {
if len(healthcheck.Test) != 0 {
return nil, errors.Errorf("test and disable can't be set at the same time")
}
return &container.HealthConfig{
Test: []string{"NONE"},
}, nil
}
if healthcheck.Timeout != nil {
timeout = *healthcheck.Timeout
}
if healthcheck.Interval != nil {
interval = *healthcheck.Interval
}
if healthcheck.StartPeriod != nil {
startPeriod = *healthcheck.StartPeriod
}
if healthcheck.Retries != nil {
retries = int(*healthcheck.Retries)
}
return &container.HealthConfig{
Test: healthcheck.Test,
Timeout: timeout,
Interval: interval,
Retries: retries,
StartPeriod: startPeriod,
}, nil
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I add it, as it is an error and not a warning (which are in the next PR).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After a second thought, the Disable flag is just not used on Kubernetes compose controller, see https://github.com/docker/cli/pull/899/files#diff-9f8dfaca00498f5f3543480c12fec02aR198 , so I don't know if we really need to error on this...

func (s *stack) createFileBasedSecrets(secrets corev1.SecretInterface) error {
for name, secret := range s.spec.Secrets {
if secret.File == "" {
continue
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to print a warning in this case?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps also if other options (permissions/ownership) is set?

(oh, just realise thats probably on the service, not on creation of the secret)

return err
}

if _, err := secrets.Create(toSecret(s.name, name, fileName, content)); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as for "config": what happens if a secret already exists?

Namespace(c.ns).
Resource("stacks").
Name(stack.Name).
Body(stack).
Do().
Into(result)
return
return result, err
}

// UpdateStatus was generated because the type contains a Status member.
// Add a +genclientstatus=false comment above the type to avoid generating UpdateStatus().
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like UpdateStatus() isn't used? should we remove this? (same for the other API version)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same answer 😄

@@ -1,11 +1,10 @@
// Package v1beta1 holds the v1beta1 versions of our stack structures.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, why was the Package description move further down? The first line is what will be documented as package description

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't really know, I'll revert it.

@@ -0,0 +1 @@
package v1beta1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is here to get representable coverage?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep exactly.

// backward compatibility by support multiple concurrent versions
// of the same resource

// Package v1beta2 is the second version of the stack, containing a structured spec
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question here for positioning of the Package description (should likely be the first line in this file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -83,3 +83,4 @@ k8s.io/client-go kubernetes-1.8.2
k8s.io/kubernetes v1.8.2
k8s.io/kube-openapi 61b46af70dfed79c6d24530cd23b41440a7f22a5
vbom.ml/util 928aaa586d7718c70f4090ddf83f2b34c16fdc8d
github.com/hashicorp/golang-lru 0a025b7e63adc15a622f29b0b2c4c3848243bbf6
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: please add a newline

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@silvin-lubecki silvin-lubecki force-pushed the handle-multiple-stack-api-versions branch from 92ba690 to ece1de4 Compare April 27, 2018 16:45
@silvin-lubecki
Copy link
Contributor Author

PTAL @thaJeztah
I also rebased onto master.

@vieux
Copy link
Contributor

vieux commented Apr 30, 2018

LGTM ping @thaJeztah

Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like there's a linting failure;

Successfully built 4fd3db512de8
Successfully tagged cli-linter:11601
kubernetes/compose/v1beta1/doc.go:7:1:warning: package comment should be of the form "Package v1beta1 ..." (golint)
Exited with code 1

but looks good otherwise

@mat007 mat007 force-pushed the handle-multiple-stack-api-versions branch from ece1de4 to dc3d8d7 Compare April 30, 2018 09:22
@mat007
Copy link
Member

mat007 commented Apr 30, 2018

Linter should be happy now, thanks @thaJeztah !

…s and use it in kubernetes stack commands

* Add kubernetes Stack API v1beta2 client
* Upgrade v1beta1 client to remove generated code

Signed-off-by: Silvin Lubecki <[email protected]>
@mat007 mat007 force-pushed the handle-multiple-stack-api-versions branch from dc3d8d7 to f958c66 Compare April 30, 2018 09:27
Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants