Skip to content

Commit 1cc13dd

Browse files
committed
Implement nerdctl run --annotation (introduced in Docker v24)
An OCI runtime (as well as `nerdctl internal oci-hook`) may consume an annotation and behave differently. e.g., https://github.com/opencontainers/runc/blob/v1.1.12/docs/systemd.md#auxiliary-properties nerdctl v1: - `nerdctl run --annotation` was not implemented. - `nerdctl run --label` is set as a containerd label and an OCI annotation. nerdctl v2: - `nerdctl run --annotation` is only set as an OCI annotation. - `nerdctl run --label` is only set as a containerd label. A label with the `nerdctl/` prefix can no longer be set manually, with an exception for `nerdctl/bypass4netns`. The `nerdctl/bypass4netns` label is still allowed and is propagated to an OCI annotation, for sake of compatibility. Docker v23: - `docker run --annotation` was not implemented. - `docker run --label` is only set as a Docker label. Docker v24 (implemented in docker/cli PR 4156, moby/moby PR 45025): - `docker run --annotation` is only set as an OCI annotation. - `docker run --label` is only set as a Docker label. Signed-off-by: Akihiro Suda <[email protected]>
1 parent c74ca03 commit 1cc13dd

File tree

13 files changed

+57
-22
lines changed

13 files changed

+57
-22
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ Major:
189189
- [P2P image distribution using IPFS](./docs/ipfs.md): `nerdctl run ipfs://CID` .
190190
P2P image distribution (IPFS) is completely optional. Your host is NOT connected to any P2P network, unless you opt in to [install and run IPFS daemon](https://docs.ipfs.io/install/).
191191
- [Cosign integration](./docs/cosign.md): `nerdctl pull --verify=cosign` and `nerdctl push --sign=cosign`, and [in Compose](./docs/cosign.md#cosign-in-compose)
192-
- [Accelerated rootless containers using bypass4netns](./docs/rootless.md): `nerdctl run --label nerdctl/bypass4netns=true`
192+
- [Accelerated rootless containers using bypass4netns](./docs/rootless.md): `nerdctl run --annotation nerdctl/bypass4netns=true`
193193

194194
Minor:
195195

cmd/nerdctl/compose_up_linux_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,7 @@ services:
523523
WORDPRESS_DB_NAME: exampledb
524524
volumes:
525525
- wordpress:/var/www/html
526-
labels:
526+
annotations:
527527
- nerdctl/bypass4netns=1
528528
529529
db:
@@ -536,7 +536,7 @@ services:
536536
MYSQL_RANDOM_ROOT_PASSWORD: '1'
537537
volumes:
538538
- db:/var/lib/mysql
539-
labels:
539+
annotations:
540540
- nerdctl/bypass4netns=1
541541
542542
volumes:

cmd/nerdctl/container_create.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,10 @@ func processContainerCreateOptions(cmd *cobra.Command) (opt types.ContainerCreat
337337
if err != nil {
338338
return
339339
}
340+
opt.Annotations, err = cmd.Flags().GetStringArray("annotation")
341+
if err != nil {
342+
return
343+
}
340344
opt.CidFile, err = cmd.Flags().GetString("cidfile")
341345
if err != nil {
342346
return

cmd/nerdctl/container_run.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,10 @@ func setCreateFlags(cmd *cobra.Command) {
230230
cmd.Flags().String("name", "", "Assign a name to the container")
231231
// label needs to be StringArray, not StringSlice, to prevent "foo=foo1,foo2" from being split to {"foo=foo1", "foo2"}
232232
cmd.Flags().StringArrayP("label", "l", nil, "Set metadata on container")
233-
cmd.RegisterFlagCompletionFunc("label", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
234-
return labels.ShellCompletions, cobra.ShellCompDirectiveNoFileComp
233+
// annotation needs to be StringArray, not StringSlice, to prevent "foo=foo1,foo2" from being split to {"foo=foo1", "foo2"}
234+
cmd.Flags().StringArray("annotation", nil, "Add an annotation to the container (passed through to the OCI runtime)")
235+
cmd.RegisterFlagCompletionFunc("annotation", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
236+
return labels.AnnotationShellCompletions, cobra.ShellCompDirectiveNoFileComp
235237
})
236238

237239
// label-file is defined as StringSlice, not StringArray, to allow specifying "--env-file=FILE1,FILE2" (compatible with Podman)

docs/command-reference.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,9 @@ Env flags:
298298
Metadata flags:
299299

300300
- :whale: :blue_square: `--name`: Assign a name to the container
301-
- :whale: :blue_square: `-l, --label`: Set meta data on a container
301+
- :whale: :blue_square: `-l, --label`: Set meta data on a container (Not passed through the OCI runtime since nerdctl v2.0, with an exception for `nerdctl/bypass4netns`)
302302
- :whale: :blue_square: `--label-file`: Read in a line delimited file of labels
303+
- :whale: :blue_square: `--annotation`: Add an annotation to the container (passed through to the OCI runtime)
303304
- :whale: :blue_square: `--cidfile`: Write the container ID to the file
304305
- :nerd_face: `--pidfile`: file path to write the task's pid. The CLI syntax conforms to Podman convention.
305306

docs/rootless.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,15 @@ The performance benchmark with iperf3 on Ubuntu 21.10 on Hyper-V VM is shown bel
121121

122122
This benchmark can be reproduced with [https://github.com/rootless-containers/bypass4netns/blob/f009d96139e9e38ce69a2ea8a9a746349bad273c/Vagrantfile](https://github.com/rootless-containers/bypass4netns/blob/f009d96139e9e38ce69a2ea8a9a746349bad273c/Vagrantfile)
123123

124-
Acceleration with bypass4netns is available with `--label nerdctl/bypass4netns=true`. You also need to have `bypass4netnsd` (bypass4netns daemon) to be running.
124+
Acceleration with bypass4netns is available with:
125+
- `--annotation nerdctl/bypass4netns=true` (for nerdctl v2.0 and later)
126+
- `--label nerdctl/bypass4netns=true` (deprecated form, used in nerdctl prior to v2.0).
127+
128+
You also need to have `bypass4netnsd` (bypass4netns daemon) to be running.
125129
Example
126130
```console
127131
$ containerd-rootless-setuptool.sh install-bypass4netnsd
128-
$ nerdctl run -it --rm -p 8080:80 --label nerdctl/bypass4netns=true alpine
132+
$ nerdctl run -it --rm -p 8080:80 --annotation nerdctl/bypass4netns=true alpine
129133
```
130134

131135
More detail is available at [https://github.com/rootless-containers/bypass4netns/blob/master/README.md](https://github.com/rootless-containers/bypass4netns/blob/master/README.md)

extras/rootless/containerd-rootless-setuptool.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ cmd_entrypoint_install_bypass4netnsd() {
365365
[Install]
366366
WantedBy=default.target
367367
EOT
368-
INFO "To use bypass4netnsd, set the \"nerdctl/bypass4netns=true\" label on containers, e.g., \`nerdctl run --label nerdctl/bypass4netns=true\`"
368+
INFO "To use bypass4netnsd, set the \"nerdctl/bypass4netns=true\" annotation on containers, e.g., \`nerdctl run --annotation nerdctl/bypass4netns=true\`"
369369
}
370370

371371
# CLI subcommand: "install-fuse-overlayfs"

pkg/api/types/container_types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,12 @@ type ContainerCreateOptions struct {
217217
// Name assign a name to the container
218218
Name string
219219
// Label set meta data on a container
220+
// (not passed through to the OCI runtime since nerdctl v2.0, with an exception for "nerdctl/bypass4netns")
220221
Label []string
221222
// LabelFile read in a line delimited file of labels
222223
LabelFile []string
224+
// Annotations set meta data on a container (passed through to the OCI runtime)
225+
Annotations []string
223226
// CidFile write the container ID to the file
224227
CidFile string
225228
// PidFile specifies the file path to write the task's pid. The CLI syntax conforms to Podman convention.

pkg/bypass4netnsutil/bypass4netnsutil.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ func generateSecurityOpt(listenerPath string) (oci.SpecOpts, error) {
4646
return opt, nil
4747
}
4848

49-
func GenerateBypass4netnsOpts(securityOptsMaps map[string]string, labelMaps map[string]string, id string) ([]oci.SpecOpts, error) {
50-
b4nn, ok := labelMaps[labels.Bypass4netns]
49+
func GenerateBypass4netnsOpts(securityOptsMaps map[string]string, annotations map[string]string, id string) ([]oci.SpecOpts, error) {
50+
b4nn, ok := annotations[labels.Bypass4netns]
5151
if !ok {
5252
return nil, nil
5353
}

pkg/cmd/container/create.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -276,13 +276,15 @@ func Create(ctx context.Context, client *containerd.Client, args []string, netMa
276276
}
277277
}
278278

279+
// TODO: abolish internal labels and only use annotations
279280
ilOpt, err := withInternalLabels(internalLabels)
280281
if err != nil {
281282
return nil, nil, err
282283
}
283284
cOpts = append(cOpts, ilOpt)
284285

285-
opts = append(opts, propagateContainerdLabelsToOCIAnnotations())
286+
opts = append(opts, propagateInternalContainerdLabelsToOCIAnnotations(),
287+
oci.WithAnnotations(strutil.ConvertKVStringsToMap(options.Annotations)))
286288

287289
var s specs.Spec
288290
spec := containerd.WithSpec(&s, opts...)
@@ -506,6 +508,13 @@ func withContainerLabels(label, labelFile []string) ([]containerd.NewContainerOp
506508
if err != nil {
507509
return nil, err
508510
}
511+
for k := range labelMap {
512+
if k == labels.Bypass4netns {
513+
log.L.Warnf("Label %q is deprecated, use an annotation instead", k)
514+
} else if strings.HasPrefix(k, labels.Prefix) {
515+
return nil, fmt.Errorf("internal label %q must not be specified manually", k)
516+
}
517+
}
509518
o := containerd.WithAdditionalContainerLabels(labelMap)
510519
return []containerd.NewContainerOpts{o}, nil
511520
}
@@ -704,9 +713,15 @@ func processeds(mountPoints []dockercompat.MountPoint) []*mountutil.Processed {
704713
return result
705714
}
706715

707-
func propagateContainerdLabelsToOCIAnnotations() oci.SpecOpts {
716+
func propagateInternalContainerdLabelsToOCIAnnotations() oci.SpecOpts {
708717
return func(ctx context.Context, oc oci.Client, c *containers.Container, s *oci.Spec) error {
709-
return oci.WithAnnotations(c.Labels)(ctx, oc, c, s)
718+
allowed := make(map[string]string)
719+
for k, v := range c.Labels {
720+
if strings.Contains(k, labels.Prefix) {
721+
allowed[k] = v
722+
}
723+
}
724+
return oci.WithAnnotations(allowed)(ctx, oc, c, s)
710725
}
711726
}
712727

pkg/cmd/container/run_linux.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,7 @@ func setPlatformOptions(ctx context.Context, client *containerd.Client, id, uts
5959
}
6060
opts = append(opts, cgOpts...)
6161

62-
labelsMap, err := readKVStringsMapfFromLabel(options.Label, options.LabelFile)
63-
if err != nil {
64-
return nil, err
65-
}
62+
annotations := strutil.ConvertKVStringsToMap(options.Annotations)
6663

6764
capOpts, err := generateCapOpts(
6865
strutil.DedupeStrSlice(options.CapAdd),
@@ -78,7 +75,7 @@ func setPlatformOptions(ctx context.Context, client *containerd.Client, id, uts
7875
}
7976
opts = append(opts, secOpts...)
8077

81-
b4nnOpts, err := bypass4netnsutil.GenerateBypass4netnsOpts(securityOptsMaps, labelsMap, id)
78+
b4nnOpts, err := bypass4netnsutil.GenerateBypass4netnsOpts(securityOptsMaps, annotations, id)
8279
if err != nil {
8380
return nil, err
8481
}

pkg/composer/serviceparser/serviceparser.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ const Separator = "-"
5454
func warnUnknownFields(svc types.ServiceConfig) {
5555
if unknown := reflectutil.UnknownNonEmptyFields(&svc,
5656
"Name",
57+
"Annotations",
5758
"Build",
5859
"BlkioConfig",
5960
"CapAdd",
@@ -477,6 +478,14 @@ func newContainer(project *types.Project, parsed *Service, i int) (*Container, e
477478
"--pull=never", // because image will be ensured before running replicas with `nerdctl run`.
478479
}
479480

481+
for k, v := range svc.Annotations {
482+
if v == "" {
483+
c.RunArgs = append(c.RunArgs, fmt.Sprintf("--annotation=%s", k))
484+
} else {
485+
c.RunArgs = append(c.RunArgs, fmt.Sprintf("--annotation=%s=%s", k, v))
486+
}
487+
}
488+
480489
if svc.BlkioConfig != nil && svc.BlkioConfig.Weight != 0 {
481490
c.RunArgs = append(c.RunArgs, fmt.Sprintf("--blkio-weight=%d", svc.BlkioConfig.Weight))
482491
}

pkg/labels/labels.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616

1717
// Package labels defines labels that are set to containerd containers as labels.
18-
// The labels are also passed to OCI containers as annotations.
18+
// The labels defined in this package are also passed to OCI containers as annotations.
1919
package labels
2020

2121
const (
@@ -107,8 +107,8 @@ const (
107107
NerdctlDefaultNetwork = Prefix + "default-network"
108108
)
109109

110-
var ShellCompletions = []string{
110+
var AnnotationShellCompletions = []string{
111111
Bypass4netns + "=true",
112112
Bypass4netns + "=false",
113-
// Other labels should not be set via CLI
113+
// Other annotations should not be set via CLI
114114
}

0 commit comments

Comments
 (0)