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

--isolation for setting swarm service isolation mode #426

Merged
merged 2 commits into from
Nov 17, 2017

Conversation

simonferquel
Copy link
Contributor

@simonferquel simonferquel commented Aug 7, 2017

fixes #414

- What I did
Added --isolation to service create/update and to compose files, to bypass default isolation mode on the host.
- How I did it
Updated dependencies to moby/moby and swarmkit (also had to depend on the logrus mega PR), added the flag in the service update/create cmd, passing the value accordingly
Updated the compose service struct and conversion to ServiceSpec accordingly
- How to verify it
Tests incoming
- Description for the changelog

  • Service creation and update supports --isolation flag (default, process, hyperv) for bypassing default isolation mode on the host
  • Isolation can also be set in compose files using the "isolation" property

Depends on #424

@codecov-io
Copy link

codecov-io commented Aug 8, 2017

Codecov Report

Merging #426 into master will increase coverage by 0.53%.
The diff coverage is 69.23%.

@@            Coverage Diff             @@
##           master     #426      +/-   ##
==========================================
+ Coverage   50.75%   51.28%   +0.53%     
==========================================
  Files         216      216              
  Lines       17730    17743      +13     
==========================================
+ Hits         8998     9099     +101     
+ Misses       8276     8175     -101     
- Partials      456      469      +13

@simonferquel simonferquel force-pushed the service-isolation branch 2 times, most recently from a81a3fe to 9843598 Compare August 9, 2017 09:40
@vdemeester
Copy link
Collaborator

@simonferquel can you link PRs in swarmkit and moby related to this feature ? 👼

@simonferquel simonferquel force-pushed the service-isolation branch 2 times, most recently from 537ba2f to 83faf7d Compare September 6, 2017 16:12
@simonferquel simonferquel force-pushed the service-isolation branch 2 times, most recently from 4aa7e53 to 27b818a Compare September 19, 2017 09:43
@simonferquel
Copy link
Contributor Author

PR on Swarmkit: moby/swarmkit#2342
PR on Moby: moby/moby#34424

@thaJeztah
Copy link
Member

moby/moby#34424 was merged, so this PR can be worked on again; @simonferquel can you do a rebase/revendor?

@simonferquel simonferquel force-pushed the service-isolation branch 2 times, most recently from 92b49da to ee86d46 Compare November 2, 2017 14:43
@simonferquel
Copy link
Contributor Author

Requires moby/moby#35382. in the mean time i'll add tests to improve coverage (mainly on update)

@@ -4,6 +4,8 @@ import (
"testing"
"time"

"github.com/stretchr/testify/require"

Copy link
Contributor

Choose a reason for hiding this comment

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

nit: This extra newline can be removed

err := o.Set(val)
if err != nil {
return err
}
Copy link
Contributor

Choose a reason for hiding this comment

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

You don't need to create a new isolationOpts struct here. Value is that struct, you just have to cast it (like the functions above).

assert.Equal(t, container.IsolationProcess, spec.TaskTemplate.ContainerSpec.Isolation)
}

func TestUpdateIsolationInvalid(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this test needs to call updateService() otherwise it's only testing isolationOpt

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, makes sense, I'll remove the test as opts is already tested and updateService won't be called if flags parsing fails

@@ -122,6 +122,11 @@ func Service(
}
}

isolation := container.Isolation(service.Isolation)
if !isolation.IsDefault() && !isolation.IsHyperV() && !isolation.IsProcess() {
return swarm.ServiceSpec{}, fmt.Errorf("Unsupported isolation %s. Valid values are %s, %s, %s", isolation, container.IsolationDefault, container.IsolationProcess, container.IsolationHyperV)
Copy link
Contributor

Choose a reason for hiding this comment

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

This is already validated by the isolationOpt we shouldn't need to do it again here, right?

The validation should be handled by the flags/value types so that any value stored in the opt is valid to send to the API.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In this case, the source is not an isolationOpt flag, but a compose-file field (which is of type string)

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, I missed that! For the compose file we do the validation as part of the types/loading. Could this validation be moved there?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Did not see that, thank! I changed validation by typing the isolation field in service type to container.Isolation, and introduced a transformValidation function at loading time (that does the validation). Also added a parsing test for invalid isolation.

@@ -372,3 +372,14 @@ func TestConvertUpdateConfigOrder(t *testing.T) {
})
assert.Equal(t, updateConfig.Order, "stop-first")
}

func TestIsolation(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

s/TestIsolation/TestServiceConvertsIsolation/

result, err := Service("1.32", Namespace{name: "foo"}, src, nil, nil, nil, nil)
if err != nil {
t.Fatal(err)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

require.NoError(t, err) ?

o.value = &tv
return nil
}
return fmt.Errorf("Unknown isolation mode %s. Valid values are %v, %v, %v", value, container.IsolationDefault, container.IsolationProcess, container.IsolationHyperV)
Copy link
Member

Choose a reason for hiding this comment

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

Ideally, this would be checked by the daemon, instead of the client. Do we have a similar check already daemon-side?

Trying to delegate validation to the daemon as much as possible, because (supported) options can differ, depending on the version of the daemon, and we should not have to keep track of that in the client.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. Actually it is already validated daemon-side with different validation logic on Linux (which only supports empty or default isolation) and windows (that also supports process and hyperv). I'll remove that client-side validation alltogether, and I'll make sure daemon-side errors are surfaced in a good way to the user

@thaJeztah
Copy link
Member

This will also need;

@thaJeztah
Copy link
Member

Also opened a moby bump in #679, which should bring in the required changes

@simonferquel
Copy link
Contributor Author

@thaJeztah sorry for the delay, it has been quite a busy time lately...
I will implement your feedback today.

@thaJeztah
Copy link
Member

Thanks!

o.source = value
tv := container.Isolation(value)
o.value = &tv
return nil
Copy link
Member

Choose a reason for hiding this comment

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

This never returns an error; is the error to satisfy an interface?

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, it is the flag.Value interface.

Copy link
Member

Choose a reason for hiding this comment

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

Check ✅

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.

one question, but otherwise I think this is ready to go after squashing

@simonferquel
Copy link
Contributor Author

Squashed & rebased

if !ok {
return value, errors.Errorf("invalid type %T for isolation", value)
}
return container.Isolation(strings.ToLower(str)), nil
Copy link
Member

Choose a reason for hiding this comment

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

Missed this in my previous review; can you remove the strings.ToLower() here as well?

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, thanks!

@thaJeztah
Copy link
Member

ping @vdemeester @dnephin PTAL

Copy link
Contributor

@dnephin dnephin left a comment

Choose a reason for hiding this comment

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

Just one small issue with the Compose support, otherwise looks good

}

func (o *isolationOpts) Type() string {
return "isolation"
Copy link
Contributor

Choose a reason for hiding this comment

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

This type is what gets reported in the help text. I wonder if string would be more helpful to users.

@@ -125,7 +127,8 @@ type ServiceConfig struct {
Ulimits map[string]*UlimitsConfig
User string
Volumes []ServiceVolumeConfig
WorkingDir string `mapstructure:"working_dir"`
WorkingDir string `mapstructure:"working_dir"`
Isolation container.Isolation `mapstructure:"isolation"`
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this should be string. Everywhere else we accept plain types and do the convert to swarm/api types in convert. This helps keep the compose config packages decoupled from the api.

I believe this change will remove the need for the custom transform in loader.go as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this was here previously because of validation. I should have looked closer when I was commenting the first time. Sorry about that.

@simonferquel
Copy link
Contributor Author

@dnephin I just reverted service options to use plain string type for isolation, and modified compose types to remove all traces of validation (removing transformIsolation). Should be ok now.

@@ -42,6 +42,7 @@ Options:
--help Print usage
--host list Set one or more custom host-to-IP mappings (host:ip)
--hostname string Container hostname
--isolation isolation Service container isolation mode
Copy link
Member

Choose a reason for hiding this comment

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

this should be "string" now then? (same for the other usage output)

@simonferquel
Copy link
Contributor Author

Good catch @thaJeztah , fixed :)

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.

still LGTM

@thaJeztah
Copy link
Member

Oh, actually; dang, test is failing:

--- FAIL: TestLoadV35 (0.00s)
	Error Trace:	loader_test.go:1473
	Error:      	Not equal: 
	            	expected: container.Isolation("process")
	            	received: string("process")
=== RUN   TestLoadV35InvalidIsolation
--- FAIL: TestLoadV35InvalidIsolation (0.00s)
	Error Trace:	loader_test.go:1490
	Error:      	Not equal: 
	            	expected: container.Isolation("invalid")
	            	received: string("invalid")

@@ -0,0 +1,544 @@
{
Copy link
Member

Choose a reason for hiding this comment

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

Wondering what we did previous times when adding a new version; perhaps it's cleaner if there were two commits; one that adds a "pristine" compose 3.5 (exact copy of 3.4), and one commit that adds your changes

Signed-off-by: Simon Ferquel <simon.ferquel@docker.com>
Signed-off-by: Simon Ferquel <simon.ferquel@docker.com>
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 🐮

@vdemeester vdemeester merged commit 5e2be65 into docker:master Nov 17, 2017
@GordonTheTurtle GordonTheTurtle added this to the 17.12.0 milestone Nov 17, 2017
@thaJeztah
Copy link
Member

🍪

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.

Add support for "isolaton" in Compose files
8 participants