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

Add OomScoreAdj to "docker service create" and "docker compose" #5145

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

psaintlaurent
Copy link

@psaintlaurent psaintlaurent commented Jun 11, 2024

- What I did
I added oom-score-adj flag to docker service create and docker service update

- How I did it
By modifying container service to accept the option as well as adding a flag to the CLI

- How to test it
Compile this PR and copy the CLI binary to a running moby dev shell using moby/moby#47950

- Description for the changelog

Add OOMScoreAdj to docker service create and docker compose

@psaintlaurent psaintlaurent changed the title Add OomScoreAdj to "docker service create" Add OomScoreAdj to "docker service create" and "docker compose" Jun 12, 2024
@@ -0,0 +1,680 @@
{
Copy link
Member

Choose a reason for hiding this comment

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

Schema 3.13 was added in #5125, which has not been released yet, and will be for the v27.0 release, so we don't need to add a new schema and can update 3.13 instead.

OomScoreAdj: copts.oomScoreAdj,
OomScoreAdj: int(copts.oomScoreAdj),
Copy link
Member

Choose a reason for hiding this comment

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

Both sides should already be an int I think, so this change should probably be removed.

opts/opts.go Outdated
Comment on lines 519 to 536

type OomScoreAdj int64

func (o *OomScoreAdj) Type() string { return "int64" }

func (o *OomScoreAdj) Value() int64 { return int64(*o) }

func (o *OomScoreAdj) String() string {

val := strconv.FormatInt(int64(*o), 10)
return val
}

func (o *OomScoreAdj) Set(value string) error {

var conv int64
conv, err := strconv.ParseInt(value, 10, 64)
if err != nil || conv < -1000 || conv > 1000 {
return err
}
*o = OomScoreAdj(conv)
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.

Bit on the fence here if we should have a special option-type for this. It does allow for validation on the client-side, but also makes assumption about what the daemon-side allows (given; it's unlikely to change); for docker run it looks like we leave the validation to the API, and using a regular IntVar option;

flags.IntVar(&copts.oomScoreAdj, "oom-score-adj", 0, "Tune host's OOM preferences (-1000 to 1000)")

@psaintlaurent psaintlaurent force-pushed the ENGINE-903 branch 3 times, most recently from 2b9f928 to 25083e3 Compare June 20, 2024 19:33
opts/opts.go Outdated
Comment on lines 536 to 538
if err != nil || conv < -1000 || conv > 1000 {
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.

In the case where err == nil but conv is out of range, the Set() call will silently be a no-op. Either generate a client-side error, or (preferably) do what @thaJeztah recommends and leave range validation to the API / daemon.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, I think we can keep it simple for now, and do the same as we do for docker run, which is a regular IntVar.

I tend to reduce client-side validation to a minimum to keep the CLI agnostic and make the daemon "source of truth". That's not all set in stone, and there are some grey areas where it's worth the trade-ff but I'd like to avoid the CLI making assumptions that may not be correct.

Comment on lines 171 to 182
"type": "object",
"type": "object",

"properties": {
"driver": {"type": "string"},
"options": {
"type": "object",
"patternProperties": {
"^.+$": {"type": ["string", "number", "null"]}
}
}
},
"additionalProperties": false
"properties": {
"driver": {"type": "string"},
"options": {
"type": "object",
"patternProperties": {
"^.+$": {"type": ["string", "number", "null"]}
}
}
},
"additionalProperties": false
Copy link
Member

Choose a reason for hiding this comment

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

Can you undo these whitespace changes here?

Comment on lines 679 to 680
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Same here (missing newline at end of file)

opts/opts.go Outdated
Comment on lines 526 to 529
func (o *OomScoreAdj) String() string {

val := strconv.FormatInt(int64(*o), 10)
return val
Copy link
Member

Choose a reason for hiding this comment

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

Don't think we need an intermediate var here, and there's a stray empty line at the start of the function

Suggested change
func (o *OomScoreAdj) String() string {
val := strconv.FormatInt(int64(*o), 10)
return val
func (o *OomScoreAdj) String() string {
return strconv.FormatInt(int64(*o), 10)

opts/opts.go Outdated
Comment on lines 536 to 538
if err != nil || conv < -1000 || conv > 1000 {
return err
}
Copy link
Member

Choose a reason for hiding this comment

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

Yes, I think we can keep it simple for now, and do the same as we do for docker run, which is a regular IntVar.

I tend to reduce client-side validation to a minimum to keep the CLI agnostic and make the daemon "source of truth". That's not all set in stone, and there are some grey areas where it's worth the trade-ff but I'd like to avoid the CLI making assumptions that may not be correct.

Comment on lines 259 to 260
func updateService(ctx context.Context, apiClient client.NetworkAPIClient, flags *pflag.FlagSet, spec *swarm.ServiceSpec) error {

Copy link
Member

Choose a reason for hiding this comment

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

Can you remove the whitespace here?

Comment on lines 374 to 375
if anyChanged(flags, flagOomScoreAdj) {

Copy link
Member

Choose a reason for hiding this comment

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

Can you remove whitespace here as well?

Comment on lines 135 to 136
service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})

Copy link
Member

Choose a reason for hiding this comment

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

Same here (whitespace)

@@ -68,6 +68,7 @@ func newCreateCommand(dockerCli command.Cli) *cobra.Command {
flags.SetAnnotation(flagSysCtl, "version", []string{"1.40"})
flags.Var(&opts.ulimits, flagUlimit, "Ulimit options")
flags.SetAnnotation(flagUlimit, "version", []string{"1.41"})
flags.Var(&opts.oomScoreAdj, flagOomScoreAdj, "oom score adjustment (-1000 to 1000)")
Copy link
Member

Choose a reason for hiding this comment

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

See other comments; this could probably just be an flags.IntVar

Signed-off-by: plaurent <[email protected]>
opts/opts.go Outdated
Comment on lines 533 to 534
conv, _ = strconv.ParseInt(value, 10, 64)
*o = OomScoreAdj(conv)
Copy link
Contributor

Choose a reason for hiding this comment

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

If value is not parseable as a number, the option gets silently set to 0? That doesn't seem right.

Copy link
Author

@psaintlaurent psaintlaurent Jun 21, 2024

Choose a reason for hiding this comment

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

I've run into silent defaults over multiple code bases, if we want different/better standards I didn't see anything documented.

Example...

return "0"

Copy link
Contributor

Choose a reason for hiding this comment

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

I've run into silent defaults over multiple code bases

What do "silent defaults" mean to you? Could you please elaborate?

Example...

You have linked to the definition of the String() method of a pflag.Value implementation, which contains a documented special case for how its zero value is formatted for user display.

cli/opts/opts.go

Lines 448 to 450 in e174fe5

// NOTE: In spf13/pflag/flag.go, "0" is considered as "zero value" while "0 B" is not.
// We return "0" in case value is 0 here so that the default value is hidden.
// (Sometimes "default 0 B" is actually misleading)

I'm not following how this is relevant to the topic being discussed. Could you please explain?

Copy link
Author

Choose a reason for hiding this comment

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

I consider silent defaults to be any configuration options that are:

1.) set to values that have to be infered
2.) not explicitly documented outside of source code

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm still lost. Why are we on this tangent about silent defaults? What do silent defaults have to do with a (pflag.Value).Set() method?

Copy link
Author

@psaintlaurent psaintlaurent Jun 25, 2024

Choose a reason for hiding this comment

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

There is no tangent, I'm not worried about this. I'm just answering your question.

Copy link
Contributor

Choose a reason for hiding this comment

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

Claiming that you have answered my question is not an answer. Please answer my question:

What do silent defaults have to do with a (pflag.Value).Set() method?

Copy link
Author

Choose a reason for hiding this comment

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

I didn't actually reference that, you did.

Copy link
Contributor

Choose a reason for hiding this comment

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

func (o *OomScoreAdj) Set(value string) error, the body of which is the context for this discussion thread, implements a Set method necessary for *OomScoreAdj to satisfy the pflag.Value interface. In other words, func (o *OomScoreAdj) Set(value string) error is a (pflag.Value).Set() method.

@@ -529,6 +529,7 @@ type serviceOptions struct {
capAdd opts.ListOpts
capDrop opts.ListOpts
ulimits opts.UlimitOpt
oomScoreAdj opts.OomScoreAdj
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
oomScoreAdj opts.OomScoreAdj
oomScoreAdj Uint64Opt

Copy link
Author

@psaintlaurent psaintlaurent Jun 21, 2024

Choose a reason for hiding this comment

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

That's an unsigned 64 bit integer for a value that ranges from -1000 to 1000

Copy link
Contributor

Choose a reason for hiding this comment

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

My bad. You're correct; Uint64Opt would not be appropriate. No custom Value type is needed at all, since it's just a signed int.

Suggested change
oomScoreAdj opts.OomScoreAdj
oomScoreAdj int

Copy link
Author

Choose a reason for hiding this comment

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

It was a custom type originally because I had a range constraint in place. It should be fine, I need to fix the pipeline issues.

Copy link
Contributor

Choose a reason for hiding this comment

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

It was a custom type originally because I had a range constraint in place.

The history of how this PR has evolved is irrelevant. The custom type is not needed in the current iteration, therefore merging this PR with the custom type in place increases the ongoing maintenance cost of this project without any offsetting benefit.

It should be fine, I need to fix the pipeline issues.

Please stay on topic.

opts/opts.go Outdated
Comment on lines 520 to 536
type OomScoreAdj int64

func (o *OomScoreAdj) Type() string { return "int64" }

func (o *OomScoreAdj) Value() int64 { return int64(*o) }

func (o *OomScoreAdj) String() string {
return strconv.FormatInt(int64(*o), 10)
}

func (o *OomScoreAdj) Set(value string) error {

var conv int64
conv, _ = strconv.ParseInt(value, 10, 64)
*o = OomScoreAdj(conv)
return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

There already exists a generic Uint64Opt type in the cli/command/service package. No need to define a new one.

Copy link
Author

Choose a reason for hiding this comment

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

That's an Unsigned 64 bit integer

Copy link
Contributor

Choose a reason for hiding this comment

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

Right, my mistake. You don't need any custom type at all! https://pkg.go.dev/github.com/spf13/pflag#FlagSet.IntVar

@codecov-commenter
Copy link

codecov-commenter commented Jun 24, 2024

Codecov Report

Attention: Patch coverage is 31.25000% with 11 lines in your changes missing coverage. Please review.

Project coverage is 61.40%. Comparing base (7fafd33) to head (bd276ea).
Report is 13 commits behind head on master.

Current head bd276ea differs from pull request most recent head 02761fd

Please upload reports for the commit 02761fd to get more accurate results.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #5145      +/-   ##
==========================================
- Coverage   61.42%   61.40%   -0.03%     
==========================================
  Files         298      298              
  Lines       20797    20813      +16     
==========================================
+ Hits        12775    12780       +5     
- Misses       7109     7119      +10     
- Partials      913      914       +1     

Signed-off-by: plaurent <[email protected]>
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.

None yet

5 participants