diff --git a/docs/docs.md b/docs/docs.md index 2aaedd4..8291cd7 100644 --- a/docs/docs.md +++ b/docs/docs.md @@ -2,38 +2,6 @@ public API - - -## oci_image - -
-load("@rules_oci//oci:defs.bzl", "oci_image")
-
-oci_image(name, annotations, arch, base, entrypoint, env, labels, layers, os, stamp, tars)
-
- -Creates a new image manifest and config by appending the `layers` to an existing image -manifest and config defined by `base`. If `base` is an image index, then `os` and `arch` will -be used to extract the image manifest. - -**ATTRIBUTES** - - -| Name | Description | Type | Mandatory | Default | -| :------------- | :------------- | :------------- | :------------- | :------------- | -| name | A unique name for this target. | Name | required | | -| annotations | [OCI Annotations](https://github.com/opencontainers/image-spec/blob/main/annotations.md) to add to the manifest. | Dictionary: String -> String | optional | `{}` | -| arch | Used to extract a manifest from base if base is an index | String | optional | `""` | -| base | A base image, as defined by oci_pull or oci_image | Label | required | | -| entrypoint | A list of entrypoints for the image; these will be inserted into the generated OCI image config | List of strings | optional | `[]` | -| env | Entries are in the format of `VARNAME=VARVALUE`. These values act as defaults and are merged with any specified when creating a container. | List of strings | optional | `[]` | -| labels | labels that will be applied to the image configuration, as defined in [the OCI config](https://github.com/opencontainers/image-spec/blob/main/config.md#properties). These behave the same way as [docker LABEL](https://docs.docker.com/engine/reference/builder/#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. | Dictionary: String -> String | optional | `{}` | -| layers | A list of layers defined by oci_image_layer | List of labels | optional | `[]` | -| os | Used to extract a manifest from base if base is an index | String | optional | `""` | -| stamp | Whether to encode build information into the output. Possible values:

- `stamp = 1`: Always stamp the build information into the output, even in [--nostamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) builds. This setting should be avoided, since it is non-deterministic. It potentially causes remote cache misses for the target and any downstream actions that depend on the result. - `stamp = 0`: Never stamp, instead replace build information by constant values. This gives good build result caching. - `stamp = -1`: Embedding of build information is controlled by the [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag. Stamped targets are not rebuilt unless their dependencies change. | Integer | optional | `-1` | -| tars | A list of tars to add as layers | List of labels | optional | `[]` | - - ## oci_image_config @@ -167,6 +135,41 @@ not be an issue when using the `oci_image` rule). The config file named after the rule, os, and arch + + +## oci_image + +
+load("@rules_oci//oci:defs.bzl", "oci_image")
+
+oci_image(name, base, annotations, arch, entrypoint, env, labels, layers, os, tars, kwargs)
+
+ +oci_image + +Creates a new image manifest and config by appending the `layers` to an +existing image manifest and config defined by `base`. If `base` is an image +index, then `os` and `arch` will be used to extract the image manifest. + + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| name | The name of the rule. | none | +| base | A base image, as defined by oci_pull or oci_image. | none | +| annotations | OCI Annotations to add to the manifest. | `None` | +| arch | Used to extract a manifest from base if base is an index. | `None` | +| entrypoint | A list of entrypoints for the image; these will be inserted into the generated container configuration. | `None` | +| 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` | +| 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` | +| layers | A list of layers defined by oci_image_layer. | `None` | +| os | Used to extract a manifest from base if base is an index. | `None` | +| tars | A list of tars to add as layers. | `None` | +| kwargs | Additional keyword arguments, e.g. tags or visibility | none | + + ## oci_image_layer diff --git a/go/cmd/ocitool/appendlayer_cmd.go b/go/cmd/ocitool/appendlayer_cmd.go index cb1f4d3..e475d75 100644 --- a/go/cmd/ocitool/appendlayer_cmd.go +++ b/go/cmd/ocitool/appendlayer_cmd.go @@ -207,18 +207,16 @@ func AppendLayersCmd(c *cli.Context) error { layerDescs = append(layerDescs, tarDesc) } - var entrypoint []string - if entrypoint_file := c.String("entrypoint"); entrypoint_file != "" { + var entrypoint *[]string + if entrypointPath := c.String("entrypoint"); entrypointPath != "" { var entrypointStruct struct { Entrypoint []string `json:"entrypoint"` } - - err := jsonutil.DecodeFromFile(entrypoint_file, &entrypointStruct) + err := jsonutil.DecodeFromFile(entrypointPath, &entrypointStruct) if err != nil { return fmt.Errorf("failed to read entrypoint config file: %w", err) } - - entrypoint = entrypointStruct.Entrypoint + entrypoint = &entrypointStruct.Entrypoint } outIngestor := layer.NewAppendIngester(c.String("out-manifest"), c.String("out-config")) diff --git a/go/pkg/layer/append.go b/go/pkg/layer/append.go index ce4f4a8..03e355d 100644 --- a/go/pkg/layer/append.go +++ b/go/pkg/layer/append.go @@ -28,7 +28,7 @@ func AppendLayers( labels map[string]string, env []string, created time.Time, - entrypoint []string, + entrypoint *[]string, platform ocispec.Platform, ) (ocispec.Descriptor, ocispec.Descriptor, error) { if annotations == nil { @@ -136,7 +136,9 @@ func AppendLayers( imageConfig.History = append(imageConfig.History, history...) imageConfig.Author = "rules_oci" - imageConfig.Config.Entrypoint = entrypoint + if entrypoint != nil { + imageConfig.Config.Entrypoint = *entrypoint + } imageConfig.Config.Env = append(imageConfig.Config.Env, env...) newConfig, err := ociutil.IngestorJSONEncode( diff --git a/oci/image.bzl b/oci/image.bzl index faa4f0c..fd32f86 100755 --- a/oci/image.bzl +++ b/oci/image.bzl @@ -3,6 +3,65 @@ load("@aspect_bazel_lib//lib:stamping.bzl", "STAMP_ATTRS", "maybe_stamp") load("@com_github_datadog_rules_oci//oci:providers.bzl", "OCIDescriptor", "OCILayout") +def oci_image( + name, + base, + annotations = None, + arch = None, + entrypoint = None, + env = None, + labels = None, + layers = None, + os = None, + tars = None, + **kwargs): + """ oci_image + + Creates a new image manifest and config by appending the `layers` to an + existing image manifest and config defined by `base`. If `base` is an image + index, then `os` and `arch` will be used to extract the image manifest. + + Args: + name: The name of the rule. + base: A base image, as defined by oci_pull or oci_image. + annotations: OCI Annotations to add to the manifest. + arch: Used to extract a manifest from base if base is an index. + entrypoint: A list of entrypoints for the image; these will be inserted + into the generated container configuration. + env: Entries are in the format of `VARNAME=VARVALUE`. These values act + as defaults and are merged with any specified when creating a + container. + 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. + layers: A list of layers defined by oci_image_layer. + os: Used to extract a manifest from base if base is an index. + tars: A list of tars to add as layers. + **kwargs: Additional keyword arguments, e.g. tags or visibility + """ + if entrypoint == None: + entrypoint_override = False + else: + entrypoint_override = True + + _oci_image( + name = name, + base = base, + annotations = annotations, + arch = arch, + entrypoint = entrypoint, + entrypoint_override = entrypoint_override, + env = env, + labels = labels, + layers = layers, + os = os, + tars = tars, + **kwargs + ) + # buildifier: disable=function-docstring def get_descriptor_file(ctx, desc): if hasattr(desc, "descriptor_file"): @@ -98,16 +157,6 @@ def _oci_image_impl(ctx): config_file = ctx.actions.declare_file("{}.config.json".format(ctx.label.name)) layout_file = ctx.actions.declare_file("{}.layout.json".format(ctx.label.name)) - entrypoint_config_file = ctx.actions.declare_file("{}.entrypoint.config.json".format(ctx.label.name)) - entrypoint_config = struct( - entrypoint = ctx.attr.entrypoint, - ) - - ctx.actions.write( - output = entrypoint_config_file, - content = json.encode(entrypoint_config), - ) - annotations = ctx.attr.annotations # Backwards compatibility: code that doesn't use the labels attr will expect annotations to be @@ -132,44 +181,69 @@ def _oci_image_impl(ctx): if maybe_stamp(ctx): stamp_args.append("--bazel-version-file={}".format(ctx.version_file.path)) + arguments = [ + "--layout={}".format(base_layout.blob_index.path), + "append-layers", + "--base={}".format(base_desc.path), + "--os={}".format(ctx.attr.os), + "--arch={}".format(ctx.attr.arch), + "--out-manifest={}".format(manifest_file.path), + "--out-config={}".format(config_file.path), + "--out-layout={}".format(layout_file.path), + "--outd={}".format(manifest_desc_file.path), + ] + [ + "--layer={}={}".format(layer, descriptor) + for layer, descriptor in layer_and_descriptor_paths + ] + [ + "--tar={}".format(tar.path) + for tar in tars + ] + [ + "--annotations={}={}".format(k, v) + for k, v in annotations.items() + ] + [ + "--labels={}={}".format(k, v) + for k, v in labels.items() + ] + [ + "--env={}".format(env) + for env in ctx.attr.env + ] + stamp_args + + default_info_files = [ + config_file, + layout_file, + manifest_desc_file, + manifest_file, + ] + + inputs = [ + ctx.version_file, + base_desc, + base_layout.blob_index, + ] + ctx.files.layers + layer_descriptor_files + base_layout.files.to_list() + tars + + if ctx.attr.entrypoint_override: + entrypoint_config_file = ctx.actions.declare_file("{}.entrypoint.config.json".format(ctx.label.name)) + entrypoint_config = struct( + entrypoint = ctx.attr.entrypoint, + ) + ctx.actions.write( + output = entrypoint_config_file, + content = json.encode(entrypoint_config), + ) + arguments.append("--entrypoint={}".format(entrypoint_config_file.path)) + default_info_files.append(entrypoint_config_file) + inputs.append(entrypoint_config_file) + ctx.actions.run( executable = toolchain.sdk.ocitool, - arguments = [ - "--layout={}".format(base_layout.blob_index.path), - "append-layers", - "--base={}".format(base_desc.path), - "--os={}".format(ctx.attr.os), - "--arch={}".format(ctx.attr.arch), - "--out-manifest={}".format(manifest_file.path), - "--out-config={}".format(config_file.path), - "--out-layout={}".format(layout_file.path), - "--outd={}".format(manifest_desc_file.path), - "--entrypoint={}".format(entrypoint_config_file.path), - ] + - [ - "--layer={}={}".format(layer, descriptor) - for layer, descriptor in layer_and_descriptor_paths - ] + - ["--tar={}".format(tar.path) for tar in tars] + - ["--annotations={}={}".format(k, v) for k, v in annotations.items()] + - ["--labels={}={}".format(k, v) for k, v in labels.items()] + - ["--env={}".format(env) for env in ctx.attr.env] + - stamp_args, - inputs = [ - ctx.version_file, - base_desc, - base_layout.blob_index, - entrypoint_config_file, - ] + ctx.files.layers + - layer_descriptor_files + - base_layout.files.to_list() + - tars, + arguments = arguments, + inputs = inputs, mnemonic = "OCIImageAppendLayers", outputs = [ - manifest_file, config_file, layout_file, manifest_desc_file, + manifest_file, ], ) @@ -180,72 +254,41 @@ def _oci_image_impl(ctx): OCILayout( blob_index = layout_file, files = depset( - ctx.files.layers + ctx.files.tars + [manifest_file, config_file, layout_file], + ctx.files.layers + ctx.files.tars + [ + manifest_file, + config_file, + layout_file, + ], transitive = [base_layout.files], ), ), DefaultInfo( - files = depset([ - entrypoint_config_file, - manifest_file, - config_file, - layout_file, - manifest_desc_file, - ]), + files = depset(default_info_files), ), ] -oci_image = rule( +_oci_image = rule( implementation = _oci_image_impl, - doc = """Creates a new image manifest and config by appending the `layers` to an existing image - manifest and config defined by `base`. If `base` is an image index, then `os` and `arch` will - be used to extract the image manifest.""", attrs = dict({ + "annotations": attr.string_dict(), + "arch": attr.string(), "base": attr.label( - doc = """A base image, as defined by oci_pull or oci_image""", mandatory = True, - providers = [ - OCIDescriptor, - OCILayout, - ], - ), - "entrypoint": attr.string_list( - doc = """A list of entrypoints for the image; these will be inserted into the generated - OCI image config""", - ), - "os": attr.string( - doc = "Used to extract a manifest from base if base is an index", - ), - "arch": attr.string( - doc = "Used to extract a manifest from base if base is an index", - ), - "env": attr.string_list( - doc = """Entries are in the format of `VARNAME=VARVALUE`. These values act as defaults and - are merged with any specified when creating a container.""", + providers = [OCIDescriptor, OCILayout], ), + "entrypoint": attr.string_list(), + "entrypoint_override": attr.bool(), + "env": attr.string_list(), + "labels": attr.string_dict(), "layers": attr.label_list( - doc = "A list of layers defined by oci_image_layer", providers = [ OCIDescriptor, ], ), + "os": attr.string(), "tars": attr.label_list( - doc = "A list of tars to add as layers", allow_files = [".tar", ".tar.gz", ".tgz", ".tar.zst"], ), - "annotations": attr.string_dict( - doc = """[OCI Annotations](https://github.com/opencontainers/image-spec/blob/main/annotations.md) - to add to the manifest.""", - ), - "labels": attr.string_dict( - doc = """labels that will be applied to the image configuration, as defined in - [the OCI config](https://github.com/opencontainers/image-spec/blob/main/config.md#properties). - These behave the same way as - [docker LABEL](https://docs.docker.com/engine/reference/builder/#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.""", - ), }, **STAMP_ATTRS), toolchains = ["@com_github_datadog_rules_oci//oci:toolchain"], provides = [OCIDescriptor, OCILayout],