Skip to content

Commit 133b576

Browse files
authored
Add support for CMD in addition to ENTRYPOINT (#102)
OCI supports both. We should support both. This is especially useful for _removing_ `CMD` if it is set in base images. Previously there was no way to do this and it would always be appended to whatever `ENTRYPOINT` the image had (per OCI spec)
1 parent 0c14644 commit 133b576

5 files changed

Lines changed: 61 additions & 18 deletions

File tree

docs/docs.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ The config file named after the rule, os, and arch
142142
<pre>
143143
load("@rules_oci//oci:defs.bzl", "oci_image")
144144

145-
oci_image(<a href="#oci_image-name">name</a>, <a href="#oci_image-base">base</a>, <a href="#oci_image-annotations">annotations</a>, <a href="#oci_image-arch">arch</a>, <a href="#oci_image-entrypoint">entrypoint</a>, <a href="#oci_image-env">env</a>, <a href="#oci_image-labels">labels</a>, <a href="#oci_image-layers">layers</a>, <a href="#oci_image-os">os</a>, <a href="#oci_image-tars">tars</a>, <a href="#oci_image-kwargs">kwargs</a>)
145+
oci_image(<a href="#oci_image-name">name</a>, <a href="#oci_image-base">base</a>, <a href="#oci_image-annotations">annotations</a>, <a href="#oci_image-arch">arch</a>, <a href="#oci_image-cmd">cmd</a>, <a href="#oci_image-entrypoint">entrypoint</a>, <a href="#oci_image-env">env</a>, <a href="#oci_image-labels">labels</a>, <a href="#oci_image-layers">layers</a>, <a href="#oci_image-os">os</a>, <a href="#oci_image-tars">tars</a>, <a href="#oci_image-kwargs">kwargs</a>)
146146
</pre>
147147

148148
oci_image
@@ -161,7 +161,8 @@ index, then `os` and `arch` will be used to extract the image manifest.
161161
| <a id="oci_image-base"></a>base | A base image, as defined by oci_pull or oci_image. | none |
162162
| <a id="oci_image-annotations"></a>annotations | OCI Annotations to add to the manifest. | `None` |
163163
| <a id="oci_image-arch"></a>arch | Used to extract a manifest from base if base is an index. | `None` |
164-
| <a id="oci_image-entrypoint"></a>entrypoint | A list of entrypoints for the image; these will be inserted into the generated container configuration. | `None` |
164+
| <a id="oci_image-cmd"></a>cmd | Default arguments to the entrypoint of the container. If an Entrypoint value is not specified, then the first entry of the Cmd array will be interpreted as the executable to run | `None` |
165+
| <a id="oci_image-entrypoint"></a>entrypoint | A list of arguments to use as the command to execute when the container starts; these will be inserted into the generated OCI image config | `None` |
165166
| <a id="oci_image-env"></a>env | Entries are in the format of `VARNAME=VARVALUE`. These values act as defaults and are merged with any specified when creating a container. | `None` |
166167
| <a id="oci_image-labels"></a>labels | Labels that will be applied to the image configuration, as defined in the OCI config. These behave the same way as docker LABEL. In particular, labels from the base image are inherited. An empty value for a label will cause that label to be deleted. For backwards compatibility, if this is not set, then the value of annotations will be used instead. | `None` |
167168
| <a id="oci_image-layers"></a>layers | A list of layers defined by oci_image_layer. | `None` |

go/cmd/ocitool/appendlayer_cmd.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,18 @@ func AppendLayersCmd(c *cli.Context) error {
207207
layerDescs = append(layerDescs, tarDesc)
208208
}
209209

210+
var cmd *[]string
211+
if cmdPath := c.String("cmd"); cmdPath != "" {
212+
var cmdStruct struct {
213+
Cmd []string `json:"cmd"`
214+
}
215+
err := jsonutil.DecodeFromFile(cmdPath, &cmdStruct)
216+
if err != nil {
217+
return fmt.Errorf("failed to read cmd config file: %w", err)
218+
}
219+
cmd = &cmdStruct.Cmd
220+
}
221+
210222
var entrypoint *[]string
211223
if entrypointPath := c.String("entrypoint"); entrypointPath != "" {
212224
var entrypointStruct struct {
@@ -230,6 +242,7 @@ func AppendLayersCmd(c *cli.Context) error {
230242
c.Generic("labels").(*flagutil.KeyValueFlag).Map,
231243
c.StringSlice("env"),
232244
createdTimestamp,
245+
cmd,
233246
entrypoint,
234247
targetPlatform,
235248
)

go/cmd/ocitool/main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ var app = &cli.App{
130130
&cli.StringFlag{
131131
Name: "out-layout",
132132
},
133+
&cli.StringFlag{
134+
Name: "cmd",
135+
},
133136
&cli.StringFlag{
134137
Name: "entrypoint",
135138
},

go/pkg/layer/append.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ func AppendLayers(
2828
labels map[string]string,
2929
env []string,
3030
created time.Time,
31+
cmd *[]string,
3132
entrypoint *[]string,
3233
platform ocispec.Platform,
3334
) (ocispec.Descriptor, ocispec.Descriptor, error) {
@@ -136,6 +137,9 @@ func AppendLayers(
136137
imageConfig.History = append(imageConfig.History, history...)
137138

138139
imageConfig.Author = "rules_oci"
140+
if cmd != nil {
141+
imageConfig.Config.Cmd = *cmd
142+
}
139143
if entrypoint != nil {
140144
imageConfig.Config.Entrypoint = *entrypoint
141145
}

oci/image.bzl

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,17 @@ load("@aspect_bazel_lib//lib:stamping.bzl", "STAMP_ATTRS", "maybe_stamp")
44
load("@com_github_datadog_rules_oci//oci:providers.bzl", "OCIDescriptor", "OCILayout")
55

66
def oci_image(
7-
name,
8-
base,
9-
annotations = None,
10-
arch = None,
11-
entrypoint = None,
12-
env = None,
13-
labels = None,
14-
layers = None,
15-
os = None,
16-
tars = None,
7+
name, # type: string
8+
base, # type: Label
9+
annotations = None, # type: list[string] | None
10+
arch = None, # type: string | None
11+
cmd = None, # type: string | list[string] | None
12+
entrypoint = None, # type: string | list[string] | None
13+
env = None, # type: list[string] | None
14+
labels = None, # type: dict[string, string] | None
15+
layers = None, # type: list[Label] | None
16+
os = None, # type: string | None
17+
tars = None, # type: list[Label]
1718
**kwargs):
1819
""" oci_image
1920
@@ -26,8 +27,12 @@ def oci_image(
2627
base: A base image, as defined by oci_pull or oci_image.
2728
annotations: OCI Annotations to add to the manifest.
2829
arch: Used to extract a manifest from base if base is an index.
29-
entrypoint: A list of entrypoints for the image; these will be inserted
30-
into the generated container configuration.
30+
cmd: Default arguments to the entrypoint of the container. If an
31+
Entrypoint value is not specified, then the first entry of the Cmd
32+
array will be interpreted as the executable to run
33+
entrypoint: A list of arguments to use as the command to execute when
34+
the container starts; these will be inserted into the generated OCI
35+
image config
3136
env: Entries are in the format of `VARNAME=VARVALUE`. These values act
3237
as defaults and are merged with any specified when creating a
3338
container.
@@ -42,16 +47,18 @@ def oci_image(
4247
tars: A list of tars to add as layers.
4348
**kwargs: Additional keyword arguments, e.g. tags or visibility
4449
"""
45-
if entrypoint == None:
46-
entrypoint_override = False
47-
else:
48-
entrypoint_override = True
50+
51+
# Override if non-None
52+
cmd_override = (cmd != None)
53+
entrypoint_override = (entrypoint != None)
4954

5055
_oci_image(
5156
name = name,
5257
base = base,
5358
annotations = annotations,
5459
arch = arch,
60+
cmd = cmd,
61+
cmd_override = cmd_override,
5562
entrypoint = entrypoint,
5663
entrypoint_override = entrypoint_override,
5764
env = env,
@@ -221,6 +228,19 @@ def _oci_image_impl(ctx):
221228
base_layout.blob_index,
222229
] + ctx.files.layers + layer_descriptor_files + base_layout.files.to_list() + tars
223230

231+
if ctx.attr.cmd_override:
232+
cmd_config_file = ctx.actions.declare_file("{}.cmd.config.json".format(ctx.label.name))
233+
cmd_config = struct(
234+
cmd = ctx.attr.cmd,
235+
)
236+
ctx.actions.write(
237+
output = cmd_config_file,
238+
content = json.encode(cmd_config),
239+
)
240+
arguments.append("--cmd={}".format(cmd_config_file.path))
241+
default_info_files.append(cmd_config_file)
242+
inputs.append(cmd_config_file)
243+
224244
if ctx.attr.entrypoint_override:
225245
entrypoint_config_file = ctx.actions.declare_file("{}.entrypoint.config.json".format(ctx.label.name))
226246
entrypoint_config = struct(
@@ -276,6 +296,8 @@ _oci_image = rule(
276296
mandatory = True,
277297
providers = [OCIDescriptor, OCILayout],
278298
),
299+
"cmd": attr.string_list(),
300+
"cmd_override": attr.bool(),
279301
"entrypoint": attr.string_list(),
280302
"entrypoint_override": attr.bool(),
281303
"env": attr.string_list(),

0 commit comments

Comments
 (0)