-
Notifications
You must be signed in to change notification settings - Fork 615
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
Conversation
api/specs.proto
Outdated
@@ -128,6 +130,11 @@ message TaskSpec { | |||
uint64 force_update = 9; | |||
} | |||
|
|||
message CustomSpec { | |||
string name = 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.
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.
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.
runtime
?
manager/controlapi/service.go
Outdated
} | ||
|
||
if payload.TypeUrl == "" || len(payload.Value) == 0 { | ||
return grpc.Errorf(codes.InvalidArgument, "Custom runtime has malformed payload") |
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.
Specific errors for missing TypeUrl
and Value
might be useful.
Name: indexRuntime, | ||
AllowMissing: true, | ||
Indexer: serviceIndexerByRuntime{}, | ||
}, |
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.
Just curious - why is it necessary to index by runtime? I don't think this was part of the original plugin runtime PR.
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.
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, |
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.
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; |
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.
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?
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 thought about that and ruled in favor for a wrapper message for a few reason, but still open for debate.
- 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.
-
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")
-
By wrapping it into a
*Spec
, we leave ourselves open to provide additional supporting fields (version? controller name? whatever)
Thoughts?
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 @stevvooe
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.
@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.
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.
Maybe RuntimeSpec
?
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, 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 @ijc25 |
manager/controlapi/service.go
Outdated
return err | ||
} | ||
case *api.TaskSpec_Custom: | ||
if err := validateCustomSpec(taskSpec.GetCustom()); 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.
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.
manager/controlapi/service_test.go
Outdated
Custom: &api.CustomSpec{ | ||
Name: runtime, | ||
Payload: &gogotypes.Any{ | ||
TypeUrl: "com.docker.custom.runtime", |
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 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?
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.
OTOH I just spotted this was in service_test.go
so it doesn't really matter.
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 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.
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 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.
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 a unit test
Ping @aluzzardi |
Codecov Report
@@ 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.
|
Fixed most comments. The only remaining part is how to name this. Anyone has suggestions? Remember it's going to fit here:
/cc @ehazlett @aaronlehmann @stevvooe @ijc25 @tiborvass |
|
ThirdPartyRuntimeSpec? ExtendedRuntimeSpec ExtensionRuntimeSpec ExternalRuntimeSpec [...] getting somewhere |
I think |
What's wrong with Happens to not be a problem with |
|
@stevvooe proposed
|
@aluzzardi I like |
@tiborvass hahah I figured you like I'm not too fond - it's like hungarian notation for datatypes. |
Lol that must be why I like it then haha 🇭🇺
Alright I give my points to GenericRuntimeSpec
…On Fri, Mar 17, 2017 at 5:27 PM Andrea Luzzardi ***@***.***> wrote:
@tiborvass <https://github.com/tiborvass> hahah I figured you like
AnyRuntimeSpec :)
I'm not too fond - it's like hungarian notation for datatypes.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#2014 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAye-6bEpd2MfMP66L_wUeB-3gmaMZIpks5rmyTfgaJpZM4MVCg->
.
|
ed3e02d
to
b9d0cfa
Compare
653b126
to
3b6e027
Compare
Updated to 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; |
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.
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.
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.
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?
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.
Makes sense. Matches what I did in the draft of the external store API.
api/naming/naming.go
Outdated
case *api.TaskSpec_Generic: | ||
return strings.ToLower(r.Generic.Kind) | ||
default: | ||
return "none" |
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 return an error here instead of "none"
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 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?
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 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.
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.
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.
Overall LGTM. Left some non-blocking comments. |
Addressed the remaining comment. PTAL |
manager/controlapi/task.go
Outdated
@@ -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) |
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.
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
.
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.
👍
Much cleaner
manager/state/store/services.go
Outdated
s := obj.(serviceEntry) | ||
r, err := naming.Runtime(s.Spec.Task) | ||
if err != nil { | ||
return false, []byte{}, 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.
Can be return false, nil, nil
manager/state/store/tasks.go
Outdated
t := obj.(taskEntry) | ||
r, err := naming.Runtime(t.Spec) | ||
if err != nil { | ||
return false, []byte{}, 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.
Can be return false, nil, nil
Thanks - updated |
LGTM |
@aluzzardi: Needs a rebase. |
Rebased |
CI failing - the wrapper types |
I tried to jedi merge/commit :) Fixing |
Signed-off-by: Andrea Luzzardi <[email protected]>
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 |
It's absolutely fine to rely on the implicit panic. This came up here: |
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. |
This mostly needs naming & datastructure validation.
The PR includes:
custom
, along with aCustomSpec
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