-
Notifications
You must be signed in to change notification settings - Fork 616
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
api: Centralize defaults and add InsertDefaults flag #1744
Conversation
Codecov Report
@@ Coverage Diff @@
## master #1744 +/- ##
==========================================
+ Coverage 54.33% 54.35% +0.01%
==========================================
Files 111 111
Lines 19323 19327 +4
==========================================
+ Hits 10499 10505 +6
+ Misses 7559 7557 -2
Partials 1265 1265 Continue to review full report at Codecov.
|
manager/controlapi/service.go
Outdated
@@ -463,6 +464,8 @@ func (s *Server) GetService(ctx context.Context, request *api.GetServiceRequest) | |||
return nil, grpc.Errorf(codes.NotFound, "service %s not found", request.ServiceID) | |||
} | |||
|
|||
service.InterpolatedSpec = defaults.InterpolateService(&service.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.
add to ListServices
as well
This is a decision to make. Inconsistency in different objects would cause confusion. Can we list the alternatives and what Docker has been using in other places? |
I only meant that I haven't gone through the effort of adding this to other objects because the design hasn't been finalized yet. I think it should be part of every object. |
Since swarmkit is part of Docker, if we are going to implement this, should objects in other Docker |
To the extent that other objects have |
4fd3c63
to
e5274d2
Compare
Rebased (fairly involved one, since so many things changed since this was opened - new deepcopy, native protobuf types, orchestrator changes, updater changes, etc.) Would love to get this going again |
}, | ||
Update: &api.UpdateConfig{ | ||
FailureAction: api.UpdateConfig_PAUSE, | ||
Monitor: gogotypes.DurationProto(5 * time.Second), |
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 more parameters like parallelism, update-delay, etc.
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 default value of UpdateDelay
is 0, so it's not necessary to include it here. You're right that Parallelism
should be included. It used to default to 0, but now defaults to 1. Fixing.
api/objects.proto
Outdated
// and it is useful for that to return detailed information about | ||
// what values are actually being used to run the service. In the | ||
// future, we should add add InterpolatedSpec to other objects as well. | ||
ServiceSpec interpolated_spec = 7; |
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 have concern to add another ServiceSpec in service object. It might confuse developer which one to update.
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'd welcome any other suggestions.
I actually wanted to support updating based on InterpolatedSpec
as a feature. It would let the end user lock in the current default values.
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 it's only generated on the fly, it's ok.
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.
Expanded the comment to clarify.
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 agree with this design.
Do you think the user should be able to view both the service spec they provided, and the full spec on inspect? As a user, maybe I don't want to always see the defaults.
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 will be the behavior with docker inspect <servicename>
, which just dumps the JSON. I think docker inspect --pretty <servicename>
should show values from the interpolated 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.
That sounds right.
e5274d2
to
c2a8bd3
Compare
LGTM |
c2a8bd3
to
1a4b9a1
Compare
I don't think the client should have to set all fields, from a usability perspective. It makes it harder to change defaults later for the user of the API, and for us to update protos. |
What bothers me is I don't recall ever seeing one API doing this. How is the world dealing wit h this right now? I'm sure we're not the first ones struggling with API defaults :) |
@aluzzardi: I'm not sure. This seems to be a problem that's specific to declarative APIs - otherwise the server can just fill in default values when it returns objects. k8s manages defaults in the apiserver - see kubernetes/enhancements#55 and the issue linked from there. I couldn't find any information about how apiserver defaults are exposed to end users. I don't think this is particularly complex. We basically have two choices: offer a version of the spec that has missing fields filled in with defaults ( |
I think it's generic to REST APIs - What I don't like is we're going to make the API response more complex with another thing to understand for API users. e.g. getting the interpolated spec and putting it back as the spec is bad. Also, what happens to the CLI - if we display both it's just going be a pain to get meaningful data back. |
The reason I think it's specific to declarative APIs is that in most other cases it's fine to fill in extra fields in the response. For example, if I
I understand your concerns but I'm wondering if you think there's a better way than what I'm proposing here. As I mentioned above, I see two basic approaches. We can either include something like BTW, I don't think getting the
I would suggest that Currently |
Discussed with @aluzzardi - let's change this so that instead of having The CLI can use the Consider clearing the |
1a4b9a1
to
bd68850
Compare
bd68850
to
dacc9da
Compare
Updated according to the above comment. PTAL. |
Rebased |
dacc9da
to
de7eb77
Compare
manager/controlapi/service.go
Outdated
service.Spec = *defaults.InterpolateService(&service.Spec) | ||
// Clear version to prevent the returned service from being the | ||
// basis for an update. | ||
service.Meta.Version = api.Version{} |
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 having second thought about that.
It would make docker service inspect
not include the version which is weird
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.
Okay, removed that part.
Currently, there is a problem with visibility of defaults. The best way to create a service is to only specify fields that you care about, because then the manager can apply appropriate defaults for the other fields. However, there's no way to know which values are actually being used for those fields. The spec is under the user's control, so if they don't specify a value (for example, `Spec.Task.Restart.Delay`), this field will show up as blank when the service is retrieved. We don't want to break the spec ownership model, but we do want a way to communicate actual values in use back to the client. For this, we add an `InsertDefaults` flag to the ControlAPI which allows fetching a version of the service with defaults inserted into empty fields. This also has the benefit of centralizing defaults. Default values for these fields that were defined in various places around the code are now centralized in `github.com/docker/swarmkit/api/defaults`. Right now this flag is only is only available for `Service`, but as we encounter situations where other objects have default values which need to be exposed, we should add this to other objects as well. Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
de7eb77
to
27d3eb8
Compare
LGTM |
Currently, there is a problem with visibility of defaults. The best way
to create a service is to only specify fields that you care about,
because then the manager can apply appropriate defaults for the other
fields. However, there's no way to know which values are actually being
used for those fields. The spec is under the user's control, so if they
don't specify a value (for example,
Spec.Task.Restart.Delay
), thisfield will show up as blank when the service is retrieved.
We don't want to break the spec ownership model, but we do want a way to
communicate actual values in use back to the client. For this, we add an
InsertDefaults
flag to the ControlAPI which allows fetching a versionof the service with defaults inserted into empty fields.
This also has the benefit of centralizing defaults. Default values for
these fields that were defined in various places around the code are now
centralized in
github.com/docker/swarmkit/api/defaults
.Right now this flag is only is only available for
Service
, but as weencounter situations where other objects have default values which need
to be exposed, we should add this to other objects as well.
cc @aluzzardi @stevvooe