Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion img/private/manifest.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ def _image_manifest_impl(ctx):
pull_info = None
computed_annotations = {}
if base != None:
history = base.structured_config.get("history", [])
history.extend(base.structured_config.get("history", []))
layers.extend(base.layers)
inputs.append(base.manifest)
inputs.append(base.config)
Expand All @@ -284,6 +284,9 @@ def _image_manifest_impl(ctx):
if LayerInfo in layer:
# Use pre-built layer metadata
layers.append(layer[LayerInfo])
history.append({
"created_by": "%s" % layer.label,
})
continue
elif DefaultInfo not in layer:
fail("layer {} needs to provide LayerInfo or DefaultInfo: {}".format(layer_idx, layer))
Expand Down Expand Up @@ -316,6 +319,14 @@ def _image_manifest_impl(ctx):

args.add("--os", os)
args.add("--architecture", arch)
if history != []:
history_file = ctx.actions.declare_file(ctx.label.name + "_history.json")
ctx.actions.write(
history_file,
json.encode({"history": history}),
)
args.add("--history-file", history_file.path)
inputs.append(history_file)
if variant != "":
args.add("--variant", variant)
for layer in layers:
Expand Down
26 changes: 23 additions & 3 deletions img_tool/cmd/manifest/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var (
operatingSystem string
architecture string
variant string
historyFile string
layerFromMetadataArgs fileList
configFragment string
configMediaType string
Expand All @@ -47,6 +48,10 @@ var (
artifactType string
)

type historyConfig struct {
History []specv1.History `json:"history"`
}

func ManifestProcess(_ context.Context, args []string) {
flagSet := flag.NewFlagSet("manifest", flag.ExitOnError)
flagSet.Usage = func() {
Expand Down Expand Up @@ -85,6 +90,7 @@ func ManifestProcess(_ context.Context, args []string) {
flagSet.Var(&annotations, "annotation", `Metadata annotations for the manifest (can be specified multiple times as key=value).`)
flagSet.StringVar(&stopSignal, "stop-signal", "", `Signal to stop the container.`)
flagSet.StringVar(&created, "created", "", `A file containing a datetime string (RFC 3339 format) for when the image was created.`)
flagSet.StringVar(&historyFile, "history-file", "", `A file containing the history of all layers`)
flagSet.StringVar(&artifactType, "artifact-type", "", `Optional IANA media type of the artifact when the manifest is used for an artifact (e.g. application/vnd.cncf.helm.chart.v1, application/spdx+json).`)

if err := flagSet.Parse(args); err != nil {
Expand Down Expand Up @@ -139,12 +145,24 @@ func ManifestProcess(_ context.Context, args []string) {
}
createdTime = ct
}
var historyConfig historyConfig
if historyFile != "" {
content, err := os.ReadFile(historyFile)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed reading history file: %v\n", err)
os.Exit(1)
}
if err := json.Unmarshal(content, &historyConfig); err != nil {
fmt.Fprintf(os.Stderr, "Failed unmarshaling history config: %v\n", err)
os.Exit(1)
}
}

var configRaw []byte
if configMediaType == "" {
configMediaType = specv1.MediaTypeImageConfig

config, err := prepareConfig(layers, templatesData, createdTime)
config, err := prepareConfig(layers, templatesData, createdTime, &historyConfig)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to prepare config: %v\n", err)
os.Exit(1)
Expand Down Expand Up @@ -286,7 +304,7 @@ func ManifestProcess(_ context.Context, args []string) {
}
}

func prepareConfig(layers []api.Descriptor, templatesData *ConfigTemplates, createdTime *time.Time) (specv1.Image, error) {
func prepareConfig(layers []api.Descriptor, templatesData *ConfigTemplates, createdTime *time.Time, history *historyConfig) (specv1.Image, error) {
// first, read the base config
// then, layer the config fragment on top of it
// finally, add our own stuff
Expand All @@ -302,7 +320,9 @@ func prepareConfig(layers []api.Descriptor, templatesData *ConfigTemplates, crea
return config, fmt.Errorf("reading config fragment: %w", err)
}
}

if len(history.History) > 0 {
config.History = append(config.History, history.History...)
}
if err := overlayNewConfigValues(&config, layers, templatesData); err != nil {
return config, fmt.Errorf("overlaying new config values: %w", err)
}
Expand Down
17 changes: 13 additions & 4 deletions tests/img_toolchain/testcases/manifest_comprehensive.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,35 @@
name = manifest_comprehensive
description = Comprehensive manifest test with environment and labels

[file]
name = history.json
{
"history": [
{"created_by": "//some_bazel:target"}
]
}

[command]
subcommand = manifest
args = --env HOME=/root --env PATH=/usr/local/bin:/usr/bin --label version=1.0 --label maintainer=test --entrypoint /app/server --manifest manifest.json --config config.json
args = --env HOME=/root --env PATH=/usr/local/bin:/usr/bin --label version=1.0 --label maintainer=test --entrypoint /app/server --manifest manifest.json --config config.json --history-file history.json
expect_exit = 0

[assert]
file_exists = manifest.json
file_exists = config.json
file_valid_json = manifest.json
file_valid_json = config.json
file_sha256 = config.json, "3fb31b605e4f79b48cdcdcf474f06633d11dba0b6ce631eee73dd6f7523b0b63"
file_sha256 = manifest.json, "62f9a4fbc61c8c7c0aefcdd6c2cce5dfe37fbd4d3f3d544c6c2c984915f038b0"
file_sha256 = config.json, "138dd8cf8dbc2067cfe184bc406e896d24ab7356c9f7ecc06fa3b7423b2999c8"
file_sha256 = manifest.json, "5f170aa356078b5be4d3002803850137ca85f3237b802b269f7e1d5463bd916f"
json_field_exists = manifest.json, schemaVersion
json_field_exists = manifest.json, mediaType
file_contains = manifest.json, "application/vnd.oci.image.config.v1+json"
json_field_exists = config.json, architecture
json_field_exists = config.json, os
file_contains = config.json, "//some_bazel:target"
file_contains = config.json, "HOME=/root"
file_contains = config.json, "PATH=/usr/local/bin:/usr/bin"
file_contains = config.json, "version"
file_contains = config.json, "1.0"
file_contains = config.json, "/app/server"
file_contains = manifest.json, "sha256:3fb31b605e4f79b48cdcdcf474f06633d11dba0b6ce631eee73dd6f7523b0b63"
file_contains = manifest.json, "sha256:138dd8cf8dbc2067cfe184bc406e896d24ab7356c9f7ecc06fa3b7423b2999c8"
Loading