From 632b4065d40e2539930f2cfa7837823287ef6342 Mon Sep 17 00:00:00 2001 From: Avi Deitcher Date: Thu, 18 Apr 2024 18:31:29 +0300 Subject: [PATCH] add support for input-tar Signed-off-by: Avi Deitcher --- src/cmd/linuxkit/build.go | 44 +++-- src/cmd/linuxkit/moby/build.go | 184 +++++++++++++++--- src/cmd/linuxkit/moby/config.go | 22 +++ src/cmd/linuxkit/moby/opts.go | 1 + .../010_reproducible/004_input_tar/test.sh | 26 +++ test/cases/000_build/060_input_tar/README.md | 18 ++ test/cases/000_build/060_input_tar/test.sh | 49 +++++ test/cases/000_build/060_input_tar/test1.yml | 37 ++++ test/cases/000_build/060_input_tar/test2.yml | 36 ++++ 9 files changed, 369 insertions(+), 48 deletions(-) create mode 100644 test/cases/000_build/010_reproducible/004_input_tar/test.sh create mode 100644 test/cases/000_build/060_input_tar/README.md create mode 100644 test/cases/000_build/060_input_tar/test.sh create mode 100644 test/cases/000_build/060_input_tar/test1.yml create mode 100644 test/cases/000_build/060_input_tar/test2.yml diff --git a/src/cmd/linuxkit/build.go b/src/cmd/linuxkit/build.go index 95d106b3e..9e41b2493 100644 --- a/src/cmd/linuxkit/build.go +++ b/src/cmd/linuxkit/build.go @@ -55,6 +55,7 @@ func buildCmd() *cobra.Command { outputTypes = moby.OutputTypes() noSbom bool sbomOutputFilename string + inputTar string sbomCurrentTime bool dryRun bool ) @@ -94,7 +95,7 @@ The generated image can be in one of multiple formats which can be run on variou if len(buildFormats) > 1 { for _, o := range buildFormats { if moby.Streamable(o) { - return fmt.Errorf("Format type %s must be the only format specified", o) + return fmt.Errorf("format type %s must be the only format specified", o) } } } @@ -109,23 +110,27 @@ The generated image can be in one of multiple formats which can be run on variou } else { err := moby.ValidateFormats(buildFormats, cacheDir.String()) if err != nil { - return fmt.Errorf("Error parsing formats: %v", err) + return fmt.Errorf("error parsing formats: %v", err) } } + if inputTar != "" && pull { + return fmt.Errorf("cannot use --input-tar and --pull together") + } + var outfile *os.File if outputFile != "" { if len(buildFormats) > 1 { - return fmt.Errorf("The -output option can only be specified when generating a single output format") + return fmt.Errorf("the -output option can only be specified when generating a single output format") } if name != "" { - return fmt.Errorf("The -output option cannot be specified with -name") + return fmt.Errorf("the -output option cannot be specified with -name") } if dir != "" { - return fmt.Errorf("The -output option cannot be specified with -dir") + return fmt.Errorf("the -output option cannot be specified with -dir") } if !moby.Streamable(buildFormats[0]) { - return fmt.Errorf("The -output option cannot be specified for build type %s as it cannot be streamed", buildFormats[0]) + return fmt.Errorf("the -output option cannot be specified for build type %s as it cannot be streamed", buildFormats[0]) } if outputFile == "-" { outfile = os.Stdout @@ -133,7 +138,7 @@ The generated image can be in one of multiple formats which can be run on variou var err error outfile, err = os.Create(outputFile) if err != nil { - log.Fatalf("Cannot open output file: %v", err) + log.Fatalf("cannot open output file: %v", err) } defer outfile.Close() } @@ -141,7 +146,7 @@ The generated image can be in one of multiple formats which can be run on variou size, err := getDiskSizeMB(sizeString) if err != nil { - log.Fatalf("Unable to parse disk size: %v", err) + log.Fatalf("unable to parse disk size: %v", err) } var ( @@ -154,25 +159,25 @@ The generated image can be in one of multiple formats which can be run on variou var err error config, err = io.ReadAll(os.Stdin) if err != nil { - return fmt.Errorf("Cannot read stdin: %v", err) + return fmt.Errorf("cannot read stdin: %v", err) } } else if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") { buffer := new(bytes.Buffer) response, err := http.Get(arg) if err != nil { - return fmt.Errorf("Cannot fetch remote yaml file: %v", err) + return fmt.Errorf("cannot fetch remote yaml file: %v", err) } defer response.Body.Close() _, err = io.Copy(buffer, response.Body) if err != nil { - return fmt.Errorf("Error reading http body: %v", err) + return fmt.Errorf("error reading http body: %v", err) } config = buffer.Bytes() } else { var err error config, err = os.ReadFile(conf) if err != nil { - return fmt.Errorf("Cannot open config file: %v", err) + return fmt.Errorf("cannot open config file: %v", err) } // templates are only supported for local files templatesSupported = true @@ -183,18 +188,18 @@ The generated image can be in one of multiple formats which can be run on variou } c, err := moby.NewConfig(config, pkgFinder) if err != nil { - return fmt.Errorf("Invalid config: %v", err) + return fmt.Errorf("invalid config: %v", err) } m, err = moby.AppendConfig(m, c) if err != nil { - return fmt.Errorf("Cannot append config files: %v", err) + return fmt.Errorf("cannot append config files: %v", err) } } if dryRun { yml, err := yaml.Marshal(m) if err != nil { - return fmt.Errorf("Error generating YAML: %v", err) + return fmt.Errorf("error generating YAML: %v", err) } fmt.Println(string(yml)) return nil @@ -206,7 +211,7 @@ The generated image can be in one of multiple formats which can be run on variou w = outfile } else { if tf, err = os.CreateTemp("", ""); err != nil { - log.Fatalf("Error creating tempfile: %v", err) + log.Fatalf("error creating tempfile: %v", err) } defer os.Remove(tf.Name()) w = tf @@ -225,7 +230,7 @@ The generated image can be in one of multiple formats which can be run on variou return fmt.Errorf("error creating sbom generator: %v", err) } } - err = moby.Build(m, w, moby.BuildOpts{Pull: pull, BuilderType: tp, DecompressKernel: decompressKernel, CacheDir: cacheDir.String(), DockerCache: docker, Arch: arch, SbomGenerator: sbomGenerator}) + err = moby.Build(m, w, moby.BuildOpts{Pull: pull, BuilderType: tp, DecompressKernel: decompressKernel, CacheDir: cacheDir.String(), DockerCache: docker, Arch: arch, SbomGenerator: sbomGenerator, InputTar: inputTar}) if err != nil { return fmt.Errorf("%v", err) } @@ -233,13 +238,13 @@ The generated image can be in one of multiple formats which can be run on variou if outfile == nil { image := tf.Name() if err := tf.Close(); err != nil { - return fmt.Errorf("Error closing tempfile: %v", err) + return fmt.Errorf("error closing tempfile: %v", err) } log.Infof("Create outputs:") err = moby.Formats(filepath.Join(dir, name), image, buildFormats, size, arch, cacheDir.String()) if err != nil { - return fmt.Errorf("Error writing outputs: %v", err) + return fmt.Errorf("error writing outputs: %v", err) } } return nil @@ -255,6 +260,7 @@ The generated image can be in one of multiple formats which can be run on variou cmd.Flags().BoolVar(&decompressKernel, "decompress-kernel", false, "Decompress the Linux kernel (default false)") cmd.Flags().StringVar(&arch, "arch", runtime.GOARCH, "target architecture for which to build") cmd.Flags().VarP(&buildFormats, "format", "f", "Formats to create [ "+strings.Join(outputTypes, " ")+" ]") + cmd.Flags().StringVar(&inputTar, "input-tar", "", "path to tar from previous linuxkit build to use as input; if provided, will take files from images from this tar, using OCI images only to replace or update files. Always copies to a temporary working directory to avoid overwriting. Only works if input-tar file has the linuxkit.yaml used to build it in the exact same location. Incompatible with --pull") cacheDir = flagOverEnvVarOverDefaultString{def: defaultLinuxkitCache(), envVar: envVarCacheDir} cmd.Flags().Var(&cacheDir, "cache", fmt.Sprintf("Directory for caching and finding cached image, overrides env var %s", envVarCacheDir)) cmd.Flags().BoolVar(&noSbom, "no-sbom", false, "suppress consolidation of sboms on input container images to a single sbom and saving in the output filesystem") diff --git a/src/cmd/linuxkit/moby/build.go b/src/cmd/linuxkit/moby/build.go index 8d4b43740..5b19c38e3 100644 --- a/src/cmd/linuxkit/moby/build.go +++ b/src/cmd/linuxkit/moby/build.go @@ -83,7 +83,7 @@ func OutputTypes() []string { return ts } -func outputImage(image *Image, section string, prefix string, m Moby, idMap map[string]uint32, dupMap map[string]string, iw *tar.Writer, opts BuildOpts) error { +func outputImage(image *Image, section string, index int, prefix string, m Moby, idMap map[string]uint32, dupMap map[string]string, iw *tar.Writer, opts BuildOpts) error { log.Infof(" Create OCI config for %s", image.Image) imageName := util.ReferenceExpand(image.Image) ref, err := reference.Parse(imageName) @@ -108,14 +108,15 @@ func outputImage(image *Image, section string, prefix string, m Moby, idMap map[ } path := path.Join("containers", section, prefix+image.Name) readonly := oci.Root.Readonly - err = ImageBundle(path, section, image.ref, config, runtime, iw, readonly, dupMap, opts) + err = ImageBundle(path, fmt.Sprintf("%s[%d]", section, index), image.ref, config, runtime, iw, readonly, dupMap, opts) if err != nil { return fmt.Errorf("failed to extract root filesystem for %s: %v", image.Image, err) } return nil } -// Build performs the actual build process +// Build performs the actual build process. The output is the filesystem +// in a tar stream written to w. func Build(m Moby, w io.Writer, opts BuildOpts) error { if MobyDir == "" { MobyDir = defaultMobyConfigDir() @@ -126,6 +127,68 @@ func Build(m Moby, w io.Writer, opts BuildOpts) error { return err } + // find the Moby config file from the existing tar + var metadataLocation string + if m.Files != nil { + for _, f := range m.Files { + if f.Metadata == "" { + continue + } + metadataLocation = strings.TrimPrefix(f.Path, "/") + } + } + var ( + oldConfig *Moby + tmpfile *os.File + err error + ) + if metadataLocation != "" && opts.InputTar != "" { + // copy the file over, in case it ends up being the same output + tmpfile, err = os.CreateTemp("", "linuxkit-input.tar") + if err != nil { + return fmt.Errorf("failed to create temporary file: %w", err) + } + defer tmpfile.Close() + in, err := os.Open(opts.InputTar) + if err != nil { + return fmt.Errorf("failed to open input tar: %w", err) + } + if _, err := io.Copy(tmpfile, in); err != nil { + return fmt.Errorf("failed to copy input tar: %w", err) + } + if err := in.Close(); err != nil { + return fmt.Errorf("failed to close input file: %w", err) + } + if _, err := tmpfile.Seek(0, 0); err != nil { + return fmt.Errorf("failed to seek to beginning of tmpfile: %w", err) + } + // for efficiency, get the trimmed metadata path in advance + tmpTar := tar.NewReader(tmpfile) + // read the tar until we find the metadata file + for { + hdr, err := tmpTar.Next() + if err == io.EOF { + break + } + if err != nil { + return fmt.Errorf("failed to read input tar: %w", err) + } + if strings.TrimPrefix(hdr.Name, "/") == metadataLocation { + buf := new(bytes.Buffer) + if _, err := buf.ReadFrom(tmpTar); err != nil { + return fmt.Errorf("failed to read metadata file from input tar: %w", err) + } + config, err := NewConfig(buf.Bytes(), nil) + if err != nil { + return fmt.Errorf("invalid config in existing tar file: %v", err) + } + oldConfig = &config + break + } + } + } + + // do we have an inTar iw := tar.NewWriter(w) // add additions @@ -151,16 +214,24 @@ func Build(m Moby, w io.Writer, opts BuildOpts) error { dupMap := map[string]string{} if m.Kernel.ref != nil { - // get kernel and initrd tarball and ucode cpio archive from container - log.Infof("Extract kernel image: %s", m.Kernel.ref) - kf := newKernelFilter(m.Kernel.ref, iw, m.Kernel.Cmdline, m.Kernel.Binary, m.Kernel.Tar, m.Kernel.UCode, opts.DecompressKernel) - err := ImageTar("kernel", m.Kernel.ref, "", kf, "", opts) - if err != nil { - return fmt.Errorf("failed to extract kernel image and tarball: %v", err) - } - err = kf.Close() - if err != nil { - return fmt.Errorf("close error: %v", err) + // first check if the existing one had it + //if config != nil && len(oldConfig.initRefs) > index+1 && oldConfig.initRefs[index].String() == image { + if oldConfig != nil && oldConfig.Kernel.ref != nil && oldConfig.Kernel.ref.String() == m.Kernel.ref.String() { + if err := extractPackageFilesFromTar(tmpfile, iw, m.Kernel.ref.String(), "kernel"); err != nil { + return err + } + } else { + // get kernel and initrd tarball and ucode cpio archive from container + log.Infof("Extract kernel image: %s", m.Kernel.ref) + kf := newKernelFilter(m.Kernel.ref, iw, m.Kernel.Cmdline, m.Kernel.Binary, m.Kernel.Tar, m.Kernel.UCode, opts.DecompressKernel) + err := ImageTar("kernel", m.Kernel.ref, "", kf, "", opts) + if err != nil { + return fmt.Errorf("failed to extract kernel image and tarball: %v", err) + } + err = kf.Close() + if err != nil { + return fmt.Errorf("close error: %v", err) + } } } @@ -169,11 +240,17 @@ func Build(m Moby, w io.Writer, opts BuildOpts) error { log.Infof("Add init containers:") } apkTar := newAPKTarWriter(iw, "init") - for _, ii := range m.initRefs { - log.Infof("Process init image: %s", ii) - err := ImageTar("init", ii, "", apkTar, resolvconfSymlink, opts) - if err != nil { - return fmt.Errorf("failed to build init tarball from %s: %v", ii, err) + for i, ii := range m.initRefs { + if oldConfig != nil && len(oldConfig.initRefs) > i && oldConfig.initRefs[i].String() == ii.String() { + if err := extractPackageFilesFromTar(tmpfile, apkTar, ii.String(), fmt.Sprintf("init[%d]", i)); err != nil { + return err + } + } else { + log.Infof("Process init image: %s", ii) + err := ImageTar(fmt.Sprintf("init[%d]", i), ii, "", apkTar, resolvconfSymlink, opts) + if err != nil { + return fmt.Errorf("failed to build init tarball from %s: %v", ii, err) + } } } if err := apkTar.WriteAPKDB(); err != nil { @@ -184,9 +261,15 @@ func Build(m Moby, w io.Writer, opts BuildOpts) error { log.Infof("Add onboot containers:") } for i, image := range m.Onboot { - so := fmt.Sprintf("%03d", i) - if err := outputImage(image, "onboot", so+"-", m, idMap, dupMap, iw, opts); err != nil { - return err + if oldConfig != nil && len(oldConfig.Onboot) > i && oldConfig.Onboot[i].Equal(image) { + if err := extractPackageFilesFromTar(tmpfile, iw, image.Image, fmt.Sprintf("onboot[%d]", i)); err != nil { + return err + } + } else { + so := fmt.Sprintf("%03d", i) + if err := outputImage(image, "onboot", i, so+"-", m, idMap, dupMap, iw, opts); err != nil { + return err + } } } @@ -194,24 +277,35 @@ func Build(m Moby, w io.Writer, opts BuildOpts) error { log.Infof("Add onshutdown containers:") } for i, image := range m.Onshutdown { - so := fmt.Sprintf("%03d", i) - if err := outputImage(image, "onshutdown", so+"-", m, idMap, dupMap, iw, opts); err != nil { - return err + if oldConfig != nil && len(oldConfig.Onshutdown) > i && oldConfig.Onshutdown[i].Equal(image) { + if err := extractPackageFilesFromTar(tmpfile, iw, image.Image, fmt.Sprintf("onshutdown[%d]", i)); err != nil { + return err + } + } else { + so := fmt.Sprintf("%03d", i) + if err := outputImage(image, "onshutdown", i, so+"-", m, idMap, dupMap, iw, opts); err != nil { + return err + } } } if len(m.Services) != 0 { log.Infof("Add service containers:") } - for _, image := range m.Services { - if err := outputImage(image, "services", "", m, idMap, dupMap, iw, opts); err != nil { - return err + for i, image := range m.Services { + if oldConfig != nil && len(oldConfig.Services) > i && oldConfig.Services[i].Equal(image) { + if err := extractPackageFilesFromTar(tmpfile, iw, image.Image, fmt.Sprintf("services[%d]", i)); err != nil { + return err + } + } else { + if err := outputImage(image, "services", i, "", m, idMap, dupMap, iw, opts); err != nil { + return err + } } } // add files - err := filesystem(m, iw, idMap) - if err != nil { + if err := filesystem(m, iw, idMap); err != nil { return fmt.Errorf("failed to add filesystem parts: %v", err) } @@ -711,3 +805,35 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error { } return nil } + +// extractPackageFilesFromTar reads files from the input tar and extracts those that have the correct +// PAXRecords - keys and values - to the tarWriter. +func extractPackageFilesFromTar(inTar *os.File, tw tarWriter, image, section string) error { + log.Infof("Copy %s files from input tar: %s", section, image) + // copy kernel files over + if _, err := inTar.Seek(0, 0); err != nil { + return fmt.Errorf("failed to seek to beginning of input tar: %w", err) + } + tr := tar.NewReader(inTar) + for { + hdr, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + return fmt.Errorf("failed to read input tar: %w", err) + } + if hdr.PAXRecords == nil { + continue + } + if hdr.PAXRecords[PaxRecordLinuxkitSource] == image && hdr.PAXRecords[PaxRecordLinuxkitLocation] == section { + if err := tw.WriteHeader(hdr); err != nil { + return fmt.Errorf("failed to write header: %w", err) + } + if _, err := io.Copy(tw, tr); err != nil { + return fmt.Errorf("failed to copy %s file: %w", section, err) + } + } + } + return nil +} diff --git a/src/cmd/linuxkit/moby/config.go b/src/cmd/linuxkit/moby/config.go index ae5cb0268..5984f1cf2 100644 --- a/src/cmd/linuxkit/moby/config.go +++ b/src/cmd/linuxkit/moby/config.go @@ -1,6 +1,7 @@ package moby import ( + "bytes" "fmt" "os" "sort" @@ -62,6 +63,27 @@ type Image struct { ImageConfig `yaml:",inline"` } +// Equal check if another Image is functionally equal to this one. +// Takes the easy path by marshaling both into yaml and then comparing the yaml. +// There may be a more efficient way to do this, but this is simplest. +func (i *Image) Equal(o *Image) bool { + // if we are going to compare, we must canonicalized both image names + i0 := i + i0.Image = util.ReferenceExpand(i.Image) + iy, err := yaml.Marshal(i0) + if err != nil { + return false + } + + o0 := o + o0.Image = util.ReferenceExpand(o.Image) + oy, err := yaml.Marshal(o) + if err != nil { + return false + } + return bytes.Equal(iy, oy) +} + // ImageConfig is the configuration part of Image, it is the subset // which is valid in a "org.mobyproject.config" label on an image. // Everything except Runtime and ref is used to build the OCI spec diff --git a/src/cmd/linuxkit/moby/opts.go b/src/cmd/linuxkit/moby/opts.go index c2cff0a52..fb584052a 100644 --- a/src/cmd/linuxkit/moby/opts.go +++ b/src/cmd/linuxkit/moby/opts.go @@ -9,4 +9,5 @@ type BuildOpts struct { DockerCache bool Arch string SbomGenerator *SbomGenerator + InputTar string } diff --git a/test/cases/000_build/010_reproducible/004_input_tar/test.sh b/test/cases/000_build/010_reproducible/004_input_tar/test.sh new file mode 100644 index 000000000..47a3b0135 --- /dev/null +++ b/test/cases/000_build/010_reproducible/004_input_tar/test.sh @@ -0,0 +1,26 @@ +#!/bin/sh +# SUMMARY: Check that tar output format build is reproducible after leveraging input tar +# LABELS: + +set -e + +# Source libraries. Uncomment if needed/defined +#. "${RT_LIB}" +. "${RT_PROJECT_ROOT}/_lib/lib.sh" + +NAME=check + +clean_up() { + rm -f ${NAME}* +} + +trap clean_up EXIT + +# do not include the sbom, because the SBoM unique IDs per file/package are *not* deterministic, +# (currently based upon syft), and thus will make the file non-reproducible +linuxkit build --no-sbom --format tar --o "${NAME}-1.tar" ../test.yml +linuxkit build --no-sbom --format tar --input-tar "${NAME}-1.tar" --o "${NAME}-2.tar" ../test.yml + +diff -q "${NAME}-1.tar" "${NAME}-2.tar" || exit 1 + +exit 0 diff --git a/test/cases/000_build/060_input_tar/README.md b/test/cases/000_build/060_input_tar/README.md new file mode 100644 index 000000000..845a79806 --- /dev/null +++ b/test/cases/000_build/060_input_tar/README.md @@ -0,0 +1,18 @@ +# testing --input-tar + +This test works by building two tar files, and checking logs. +This only works because we use verbose logs. + +The two files - `test1.yml` and `test2.yml` are identical, except for some changed lines. + +The test script - `test.sh` - builds an image from `test1.yml`, then uses its output +as `--input-tar` for building from `test2.yml`. It then checks the output logs to make sure +that expected sections are copied over, and unexpected ones are not. + +**Note:** If you make any changes to either test file, mark here and in `test.sh` so we know what has changed. + +Changes: + +- added one entry in `init` +- changed the command in `onboot[1]` +- removed `services[1]`, which causes `services[2]` to become `services[1]`, and thus should not be copied either, as order may matter. diff --git a/test/cases/000_build/060_input_tar/test.sh b/test/cases/000_build/060_input_tar/test.sh new file mode 100644 index 000000000..e2f35feed --- /dev/null +++ b/test/cases/000_build/060_input_tar/test.sh @@ -0,0 +1,49 @@ +#!/bin/sh +# SUMMARY: Check that tar output format build is reproducible after leveraging input tar +# LABELS: + +set -e + +# Source libraries. Uncomment if needed/defined +#. "${RT_LIB}" +. "${RT_PROJECT_ROOT}/_lib/lib.sh" + +NAME=check + +clean_up() { + rm -f ${NAME}-*.tar +} + +trap clean_up EXIT + +logfile=$(mktemp) + +# do not include the sbom, because the SBoM unique IDs per file/package are *not* deterministic, +# (currently based upon syft), and thus will make the file non-reproducible +linuxkit build --no-sbom --format tar --o "${NAME}-1.tar" ./test1.yml +linuxkit build -v --no-sbom --format tar --input-tar "${NAME}-1.tar" --o "${NAME}-2.tar" ./test2.yml 2>&1 | tee ${logfile} + +# the logfile should indicate which parts were copied and which not +# we only know this because we built the test2.yml manually + +# should have 3 entries copied from init, but not a 4th +errors="" +grep -q "Copy init\[0\]" ${logfile} || errors="${errors}\nmissing Copy init[0]" +grep -q "Copy init\[1\]" ${logfile} || errors="${errors}\nmissing Copy init[1]" +grep -q "Copy init\[2\]" ${logfile} || errors="${errors}\nmissing Copy init[2]" +grep -q "Copy init\[3\]" ${logfile} && errors="${errors}\nunexpected Copy init[3]" +# should have one entry copied from onboot, but not a second +grep -q "Copy onboot\[0\]" ${logfile} || errors="${errors}\nmissing Copy onboot[0]" +grep -q "Copy onboot\[1\]" ${logfile} && errors="${errors}\nunexpected Copy onboot[1]" +# should have one entry copied from services, but not a second or third +grep -q "Copy services\[0\]" ${logfile} || errors="${errors}\nmissing Copy services[0]" +grep -q "Copy services\[1\]" ${logfile} && errors="${errors}\nunexpected Copy services[1]" +grep -q "Copy services\[2\]" ${logfile} && errors="${errors}\nunexpected Copy services[2]" + +if [ -n "${errors}" ]; then + echo "Errors: ${errors}" + echo "logfile: ${logfile}" + exit 1 +fi + +exit 0 diff --git a/test/cases/000_build/060_input_tar/test1.yml b/test/cases/000_build/060_input_tar/test1.yml new file mode 100644 index 000000000..cd7ecc5aa --- /dev/null +++ b/test/cases/000_build/060_input_tar/test1.yml @@ -0,0 +1,37 @@ +kernel: + image: linuxkit/kernel:6.6.13 + cmdline: "console=tty0 console=ttyS0 console=ttyAMA0" +init: + - linuxkit/init:45a1ad5919f0b6acf0f0cf730e9434abfae11fe6 + - linuxkit/runc:6062483d748609d505f2bcde4e52ee64a3329f5f + - linuxkit/containerd:e7a92d9f3282039eac5fb1b07cac2b8664cbf0ad +onboot: + - name: sysctl + image: linuxkit/sysctl:5a374e4bf3e5a7deeacff6571d0f30f7ea8f56db + - name: dhcpcd + image: linuxkit/dhcpcd:e9e3580f2de00e73e7b316a007186d22fea056ee + command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"] +onshutdown: + - name: shutdown + image: busybox:latest + command: ["/bin/echo", "so long and thanks for all the fish"] +services: + - name: getty + image: linuxkit/getty:5d86a2ce2d890c14ab66b13638dcadf74f29218b + env: + - INSECURE=true + - name: rngd + image: linuxkit/rngd:cdb919e4aee49fed0bf6075f0a104037cba83c39 + - name: nginx + image: nginx:1.19.5-alpine + capabilities: + - CAP_NET_BIND_SERVICE + - CAP_CHOWN + - CAP_SETUID + - CAP_SETGID + - CAP_DAC_OVERRIDE + binds: + - /etc/resolv.conf:/etc/resolv.conf +files: + - path: etc/linuxkit-config + metadata: yaml diff --git a/test/cases/000_build/060_input_tar/test2.yml b/test/cases/000_build/060_input_tar/test2.yml new file mode 100644 index 000000000..7532a8912 --- /dev/null +++ b/test/cases/000_build/060_input_tar/test2.yml @@ -0,0 +1,36 @@ +kernel: + image: linuxkit/kernel:6.6.13 + cmdline: "console=tty0 console=ttyS0 console=ttyAMA0" +init: + - linuxkit/init:45a1ad5919f0b6acf0f0cf730e9434abfae11fe6 + - linuxkit/runc:6062483d748609d505f2bcde4e52ee64a3329f5f + - linuxkit/containerd:e7a92d9f3282039eac5fb1b07cac2b8664cbf0ad + - linuxkit/ca-certificates:5aaa343474e5ac3ac01f8b917e82efb1063d80ff +onboot: + - name: sysctl + image: linuxkit/sysctl:5a374e4bf3e5a7deeacff6571d0f30f7ea8f56db + - name: dhcpcd + image: linuxkit/dhcpcd:e9e3580f2de00e73e7b316a007186d22fea056ee + command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1", "change"] +onshutdown: + - name: shutdown + image: busybox:latest + command: ["/bin/echo", "so long and thanks for all the fish"] +services: + - name: getty + image: linuxkit/getty:5d86a2ce2d890c14ab66b13638dcadf74f29218b + env: + - INSECURE=true + - name: nginx + image: nginx:1.19.5-alpine + capabilities: + - CAP_NET_BIND_SERVICE + - CAP_CHOWN + - CAP_SETUID + - CAP_SETGID + - CAP_DAC_OVERRIDE + binds: + - /etc/resolv.conf:/etc/resolv.conf +files: + - path: etc/linuxkit-config + metadata: yaml