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

Generic Task Runtime support #2014

Merged
merged 1 commit into from
Mar 22, 2017
Merged

Generic Task Runtime support #2014

merged 1 commit into from
Mar 22, 2017

Conversation

aluzzardi
Copy link
Member

@aluzzardi aluzzardi commented Mar 7, 2017

This mostly needs naming & datastructure validation.

The PR includes:

  • A new runtime type, custom, along with a CustomSpec
  • API validation, Store indexing, API filters

Both services and tasks can be filtered by runtime. Custom runtimes can be filtered by their custom type (e.g. runtime=plugin for plugins), while built-in types can be filtered by what they are (e.g. runtime=container).

/cc @ehazlett @aaronlehmann @stevvooe

api/specs.proto Outdated
@@ -128,6 +130,11 @@ message TaskSpec {
uint64 force_update = 9;
}

message CustomSpec {
string name = 1;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I suppose this is describing which kind of spec is enclosed - i.e. plugin, container etc (perhaps with some com.docker. scheme) - not an arbitrary name. If so, maybe kind is a better name? I would also have suggested type, but that's a reserved word.

Copy link
Contributor

Choose a reason for hiding this comment

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

runtime?

}

if payload.TypeUrl == "" || len(payload.Value) == 0 {
return grpc.Errorf(codes.InvalidArgument, "Custom runtime has malformed payload")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Specific errors for missing TypeUrl and Value might be useful.

Name: indexRuntime,
AllowMissing: true,
Indexer: serviceIndexerByRuntime{},
},
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just curious - why is it necessary to index by runtime? I don't think this was part of the original plugin runtime PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

Discussed IRL - this is to allow filtering by container for things such as docker service ls

api/ca.pb.go Outdated
@@ -2062,7 +2062,7 @@ func init() { proto.RegisterFile("ca.proto", fileDescriptorCa) }

var fileDescriptorCa = []byte{
// 610 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x54, 0xcd, 0x6e, 0xd3, 0x40,
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xcd, 0x6e, 0xd3, 0x40,
Copy link
Collaborator

Choose a reason for hiding this comment

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

There are some unrelated changes to generated .pb.go files happening here. I think #1968 needs to be merged first. Can you please revive that one?

api/specs.proto Outdated
@@ -101,6 +102,7 @@ message TaskSpec {
oneof runtime {
NetworkAttachmentSpec attachment = 8;
ContainerSpec container = 1;
CustomSpec custom = 10;
Copy link
Contributor

Choose a reason for hiding this comment

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

The word "custom" is probably a little narrow. "dynamic" or something in that area would would be more apt. Basically, the goal is to decouple the swarmkit schema from the runtime schema.

Why not just embed the any field here directly and use the url to name the runtime?

Copy link
Member Author

Choose a reason for hiding this comment

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

We thought about that and ruled in favor for a wrapper message for a few reason, but still open for debate.

  1. the Url field of the Any type is supposed to represent the encoding, not the "type"

https://github.com/google/protobuf/blob/master/src/google/protobuf/any.proto#L113

  // A URL/resource name whose content describes the type of the
  // serialized protocol buffer message.
  1. The type in the URL won't be "friendly", e.g., it's going to be a long, namespaced versioned type. The type in the spec will be akin to a top-level type (e.g. "plugin", or "unikernel")

  2. By wrapping it into a *Spec, we leave ourselves open to provide additional supporting fields (version? controller name? whatever)

Thoughts?

Choose a reason for hiding this comment

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

Ping @stevvooe

Copy link
Contributor

Choose a reason for hiding this comment

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

@aluzzardi I think I understand the encoding issue. The bigger issue here is that the thing is called "custom", but will likely be used most commonly. Custom usually denotes something that is exceptional or bespoke, where as we are trying to communicate that this merely a generic type holder.

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe RuntimeSpec?

Copy link
Member Author

@aluzzardi aluzzardi Mar 16, 2017

Choose a reason for hiding this comment

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

Yes, I don't like CustomSpec / custom either. I was just pointing out why it's a wrapper object.

We thought about RuntimeSpec, the problem however is that we already have a oneof runtime in TaskSpec, e.g.

 	oneof runtime {
		RuntimeSpec runtime = 10;

It's a bit weird to have a runtime inside a runtime.

Also, this won't compile I believe - Spec.GetRuntime() would be defined twice

Suggestions?

/cc @stevvooe @aaronlehmann

@aluzzardi
Copy link
Member Author

/cc @ijc25

return err
}
case *api.TaskSpec_Custom:
if err := validateCustomSpec(taskSpec.GetCustom()); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: I think it's nicer if functions which match a naming pattern follow a pattern for their arguments/returns to so either all take the type associated with $1 in validate(.*)Spec or all take the generic type and cast themselves. Here you have one of each. Since this is internal and the functions are all pretty near each other it's less important than if it were a public API though.

Custom: &api.CustomSpec{
Name: runtime,
Payload: &gogotypes.Any{
TypeUrl: "com.docker.custom.runtime",
Copy link
Contributor

Choose a reason for hiding this comment

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

Should perhaps be com.docker.swarmkit.custom.runtime to be properly namespaced? (maybe then the custom becomes redundant?) and/or maybe there should be a spec in there somewhere?

Copy link
Contributor

Choose a reason for hiding this comment

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

OTOH I just spotted this was in service_test.go so it doesn't really matter.

Copy link
Contributor

Choose a reason for hiding this comment

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

Should we not follow the conventions laid out in the protobuf specification?

Here are some examples: https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/any.

The proposed format in this PR is not a url.

Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like we need something like type.docker.com/docker.swarmkit.v1.PluginSpec to be compliant but I am not sure where they have this specified.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is a unit test

@tiborvass
Copy link

Ping @aluzzardi

@codecov
Copy link

codecov bot commented Mar 16, 2017

Codecov Report

Merging #2014 into master will increase coverage by 0.11%.
The diff coverage is 60%.

@@            Coverage Diff             @@
##           master    #2014      +/-   ##
==========================================
+ Coverage   53.86%   53.97%   +0.11%     
==========================================
  Files         111      111              
  Lines       19233    19317      +84     
==========================================
+ Hits        10360    10427      +67     
- Misses       7609     7616       +7     
- Partials     1264     1274      +10

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0c052c7...11b975f. Read the comment docs.

@aluzzardi
Copy link
Member Author

Fixed most comments.

The only remaining part is how to name this.

Anyone has suggestions?

Remember it's going to fit here:

message TaskSpec {
  oneof runtime {
    <Something>Spec something;
    ContainerSpec container;
    [...]
  }
}

/cc @ehazlett @aaronlehmann @stevvooe @ijc25 @tiborvass

@aaronlehmann
Copy link
Collaborator

OpaqueSpec?

@aluzzardi
Copy link
Member Author

aluzzardi commented Mar 17, 2017

ThirdPartyRuntimeSpec?

ExtendedRuntimeSpec

ExtensionRuntimeSpec

ExternalRuntimeSpec

[...] getting somewhere

@aaronlehmann
Copy link
Collaborator

I think ExtensionRuntimeSpec or ExternalRuntimeSpec are good.

@tiborvass
Copy link

ForeignRuntimeSpec, AlienRuntimeSpec, ResidentAlienRuntimeSpec (sorry :P)

What's wrong with AnyRuntimeSpec ? I also like OpaqueRuntimeSpec except my mind is OCD about the ambiguity of associativity: Opaque-RuntimeSpec vs OpaqueRuntime-Spec :P

Happens to not be a problem with Any, because it doesn't matter whether it's Any-RuntimeSpec or AnyRuntime-Spec. I know this is the weakest argument ever. Other than that, I simply like the simplicity of AnyRuntimeSpec.

@ehazlett
Copy link
Contributor

ExternalRuntimeSpec sgtm

@aluzzardi
Copy link
Member Author

@stevvooe proposed GenericRuntimeSpec, which I also like.

ExternalRuntimeSpec vs GenericRuntimeSpec everyone?

@tiborvass
Copy link

@aluzzardi I like GenericRuntimeSpec, but I don't know what's wrong with AnyRuntimeSpec which is shorter.

@aluzzardi
Copy link
Member Author

@tiborvass hahah I figured you like AnyRuntimeSpec :)

I'm not too fond - it's like hungarian notation for datatypes.

@tiborvass
Copy link

tiborvass commented Mar 18, 2017 via email

@aluzzardi aluzzardi force-pushed the any-runtime branch 2 times, most recently from ed3e02d to b9d0cfa Compare March 18, 2017 00:34
@aluzzardi aluzzardi changed the title Custom Task Runtime support Generic Task Runtime support Mar 18, 2017
@aluzzardi aluzzardi force-pushed the any-runtime branch 2 times, most recently from 653b126 to 3b6e027 Compare March 18, 2017 00:39
@aluzzardi
Copy link
Member Author

Updated to GenericRuntimeSpec and "generic runtime".

Going once, going twice...

@@ -191,6 +191,8 @@ message ListTasksRequest {
repeated docker.swarmkit.v1.TaskState desired_states = 6;
// NamePrefixes matches all objects with the given prefixes
repeated string name_prefixes = 7;
repeated string runtimes = 9;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you think it makes sense to make this TaskSpec.runtime instead of a string, so we can select by type instead of using a string describing the type?

I'm not sure it makes sense, but just wanted to share the idea.

Copy link
Member Author

Choose a reason for hiding this comment

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

The problem is it works for built-in types (Container, NetworkAttachment) but not for Generic.

For Generic, we index by GenericRuntime.Kind.

Basically, we assume runtimes are indexed by a string. Container and NetworkAttachment are special cases.

Makes sense?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Makes sense. Matches what I did in the draft of the external store API.

case *api.TaskSpec_Generic:
return strings.ToLower(r.Generic.Kind)
default:
return "none"
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 return an error here instead of "none"

Copy link
Member Author

Choose a reason for hiding this comment

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

It used to panic. But we have so much code in our unit tests that just pass a service (or task) with no runtime. I'm not sure it was worth the hassle of fixing it all.

Thoughts?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think it should panic. But I think returning an error is the right thing to do. Returning "none" here means that passing "none" as the filter parameter would return unrecognized runtimes, which would be a weird undocumented quirk in the API.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fair enough - fixed it.

Could you double check the way I use the indexing?

AllowMissing: true plus returning false if naming.Runtime yields an error.

Also, changed the control API filters to properly handle errors. If a filter was specified, then we skip the invalid runtime. If not, then we include it.

@aaronlehmann
Copy link
Collaborator

Overall LGTM. Left some non-blocking comments.

@aluzzardi
Copy link
Member Author

Addressed the remaining comment. PTAL

@@ -123,6 +125,14 @@ func (s *Server) ListTasks(ctx context.Context, request *api.ListTasksRequest) (
return filterContains(e.NodeID, request.Filters.NodeIDs)
},
func(e *api.Task) bool {
r, err := naming.Runtime(e.Spec)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Very minor: I would try to avoid this call to Runtime when no runtime filter is specified. Could we have something above like:

if len(request.Filters.Runtimes) == 0 {
        return true
}

and then the return below can be simplified to return false.

Copy link
Member Author

Choose a reason for hiding this comment

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

👍

Much cleaner

s := obj.(serviceEntry)
r, err := naming.Runtime(s.Spec.Task)
if err != nil {
return false, []byte{}, nil
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can be return false, nil, nil

t := obj.(taskEntry)
r, err := naming.Runtime(t.Spec)
if err != nil {
return false, []byte{}, nil
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can be return false, nil, nil

@aluzzardi
Copy link
Member Author

Thanks - updated

@aaronlehmann
Copy link
Collaborator

LGTM

@aaronlehmann
Copy link
Collaborator

@aluzzardi: Needs a rebase.

@aluzzardi
Copy link
Member Author

Rebased

@aaronlehmann
Copy link
Collaborator

CI failing - the wrapper types serviceEntry and taskEntry no longer exist

@aluzzardi
Copy link
Member Author

I tried to jedi merge/commit :) Fixing

Signed-off-by: Andrea Luzzardi <[email protected]>
@aluzzardi
Copy link
Member Author

By the way: in that code, we have a bunch of

	s, ok := obj.(*api.Service)
	if !ok {
		panic("unexpected type passed to FromObject")
	}

I added mine simply as s := obj.(*api.Service) - go will already panic if we cast the wrong object, so I don't see a point of doing a manual check just to panic. Was there a reason?

@aaronlehmann
Copy link
Collaborator

It's absolutely fine to rely on the implicit panic. This came up here:

#2039 (comment)

@aaronlehmann
Copy link
Collaborator

I'm going to open a PR to remove those checks just to simplify the code and preempt further questions about why the checks exist.

@aluzzardi aluzzardi merged commit 8e323f8 into moby:master Mar 22, 2017
@aluzzardi aluzzardi deleted the any-runtime branch March 22, 2017 01:34
@aaronlehmann aaronlehmann mentioned this pull request Mar 23, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants