diff --git a/Makefile b/Makefile index abb6e982..0df2e235 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ STACKER_OPTS=--oci-dir=$(BUILD_D)/oci --roots-dir=$(BUILD_D)/roots --stacker-dir build_stacker = go build -tags "$(BUILD_TAGS) $1" -ldflags "-X main.version=$(VERSION_FULL) -X main.lxc_version=$(LXC_VERSION) $2" -o $3 ./cmd/stacker # See doc/hacking.md for how to use a local oci or docker repository. -STACKER_DOCKER_BASE?=docker:// +STACKER_DOCKER_BASE ?= oci:$(CLONE_D): # They default to their image name in STACKER_DOCKER_BASE STACKER_BUILD_BASE_IMAGE?=$(STACKER_DOCKER_BASE)alpine:edge STACKER_BUILD_CENTOS_IMAGE?=$(STACKER_DOCKER_BASE)centos:latest @@ -44,7 +44,7 @@ STAGE1_STACKER ?= ./stacker-dynamic STACKER_DEPS = $(GO_SRC) go.mod go.sum -stacker: $(STAGE1_STACKER) $(STACKER_DEPS) cmd/stacker/lxc-wrapper/lxc-wrapper.c +stacker: $(STAGE1_STACKER) $(STACKER_DEPS) $(STAGE1_STACKER) --debug $(STACKER_OPTS) build \ -f build.yaml \ --substitute BUILD_D=$(BUILD_D) \ diff --git a/cmd/stacker/bom.go b/cmd/stacker/bom.go index a138d1ab..be8457d2 100644 --- a/cmd/stacker/bom.go +++ b/cmd/stacker/bom.go @@ -10,6 +10,7 @@ import ( "stackerbuild.io/stacker-bom/pkg/bom" "stackerbuild.io/stacker-bom/pkg/distro" "stackerbuild.io/stacker-bom/pkg/fs" + "stackerbuild.io/stacker/pkg/types" ) var bomCmd = cli.Command{ @@ -39,7 +40,7 @@ func doBomDiscover(ctx *cli.Context) error { author := "stacker-internal" org := "stacker-internal" - if err := fs.Discover(author, org, "/stacker/artifacts/installed-packages.json"); err != nil { + if err := fs.Discover(author, org, types.InternalStackerDir+"/artifacts/installed-packages.json"); err != nil { return nil } @@ -57,7 +58,8 @@ func doBomGenerate(ctx *cli.Context) error { org := "stacker-internal" lic := "unknown" - if err := distro.ParsePackage(input, author, org, lic, fmt.Sprintf("/stacker/artifacts/%s.json", filepath.Base(input))); err != nil { + if err := distro.ParsePackage(input, author, org, lic, fmt.Sprintf("%s/artifacts/%s.json", + types.InternalStackerDir, filepath.Base(input))); err != nil { return nil } @@ -98,16 +100,17 @@ func doBomVerify(ctx *cli.Context) error { org := ctx.Args().Get(3) // first merge all individual sbom artifacts that may have been generated - if err := bom.MergeDocuments("/stacker/artifacts", name, author, org, dest); err != nil { + iDir := types.InternalStackerDir + if err := bom.MergeDocuments(iDir+"/artifacts", name, author, org, dest); err != nil { return err } // check against inventory if err := fs.GenerateInventory("/", - []string{"/proc", "/sys", "/dev", "/etc/resolv.conf", "/stacker"}, - "/stacker/artifacts/inventory.json"); err != nil { + []string{"/proc", "/sys", "/dev", "/etc/resolv.conf", iDir}, + iDir+"/artifacts/inventory.json"); err != nil { return err } - return fs.Verify(dest, "/stacker/artifacts/inventory.json", "") + return fs.Verify(dest, iDir+"/artifacts/inventory.json", "") } diff --git a/cmd/stacker/chroot.go b/cmd/stacker/chroot.go index 497d76a6..3703a769 100644 --- a/cmd/stacker/chroot.go +++ b/cmd/stacker/chroot.go @@ -98,11 +98,11 @@ func doChroot(ctx *cli.Context) error { } defer c.Close() - err = stacker.SetupBuildContainerConfig(config, s, c, name) + err = stacker.SetupBuildContainerConfig(config, s, c, types.InternalStackerDir, name) if err != nil { return err } - err = stacker.SetupLayerConfig(config, c, layer, name) + err = stacker.SetupLayerConfig(config, c, layer, types.InternalStackerDir, name) if err != nil { return err } diff --git a/pkg/stacker/bom.go b/pkg/stacker/bom.go index 0cfe3eab..dc2b243c 100644 --- a/pkg/stacker/bom.go +++ b/pkg/stacker/bom.go @@ -5,6 +5,7 @@ import ( "io" "os" "path" + "path/filepath" "stackerbuild.io/stacker/pkg/container" "stackerbuild.io/stacker/pkg/log" @@ -27,24 +28,25 @@ func BuildLayerArtifacts(sc types.StackerConfig, storage types.Storage, l types. } defer c.Close() - err = SetupBuildContainerConfig(sc, storage, c, tag) + inDir := types.InternalStackerDir + err = SetupBuildContainerConfig(sc, storage, c, inDir, tag) if err != nil { log.Errorf("build container %v", err) return err } - err = SetupLayerConfig(sc, c, l, tag) + err = SetupLayerConfig(sc, c, l, inDir, tag) if err != nil { return err } - cmd := []string{insideStaticStacker} + cmd := []string{filepath.Join(inDir, types.BinStacker)} if sc.Debug { cmd = append(cmd, "--debug") } - cmd = append(cmd, "bom", "build", "/stacker/artifacts", + cmd = append(cmd, "bom", "build", filepath.Join(inDir, "artifacts"), l.Annotations[types.AuthorAnnotation], l.Annotations[types.OrgAnnotation], l.Annotations[types.LicenseAnnotation], @@ -71,25 +73,26 @@ func VerifyLayerArtifacts(sc types.StackerConfig, storage types.Storage, l types } defer c.Close() - err = SetupBuildContainerConfig(sc, storage, c, tag) + inDir := types.InternalStackerDir + err = SetupBuildContainerConfig(sc, storage, c, inDir, tag) if err != nil { log.Errorf("build container %v", err) return err } - err = SetupLayerConfig(sc, c, l, tag) + err = SetupLayerConfig(sc, c, l, inDir, tag) if err != nil { return err } - cmd := []string{insideStaticStacker} + cmd := []string{filepath.Join(inDir, types.BinStacker)} if sc.Debug { cmd = append(cmd, "--debug") } cmd = append(cmd, "bom", "verify", - fmt.Sprintf("/stacker/artifacts/%s.json", tag), + fmt.Sprintf(types.InternalStackerDir+"/artifacts/%s.json", tag), tag, l.Annotations[types.AuthorAnnotation], l.Annotations[types.OrgAnnotation]) err = c.Execute(cmd, os.Stdin) diff --git a/pkg/stacker/build.go b/pkg/stacker/build.go index 2866a5cb..84d2e493 100644 --- a/pkg/stacker/build.go +++ b/pkg/stacker/build.go @@ -7,6 +7,7 @@ import ( "os/exec" "os/user" "path" + "path/filepath" "strings" "time" @@ -22,8 +23,7 @@ import ( ) const ( - DefaultShell = "/bin/sh" - insideStaticStacker = "/stacker/tools/static-stacker" + DefaultShell = "/bin/sh" ) type BuildArgs struct { @@ -114,6 +114,11 @@ func (b *Builder) updateOCIConfigForOutput(sf *types.Stackerfile, s types.Storag imageConfig.Labels = map[string]string{} } + inDir := types.InternalStackerDir + if l.WasLegacyImport { + inDir = types.LegacyInternalStackerDir + } + if len(l.GenerateLabels) > 0 { writable, cleanup, err := s.TemporaryWritableSnapshot(name) if err != nil { @@ -133,24 +138,26 @@ func (b *Builder) updateOCIConfigForOutput(sf *types.Stackerfile, s types.Storag } defer c.Close() - err = SetupBuildContainerConfig(opts.Config, s, c, writable) + err = SetupBuildContainerConfig(opts.Config, s, c, inDir, writable) if err != nil { return err } - err = c.BindMount(dir, "/stacker/oci-labels", "") + // /stacker/oci-labels + labelInDir := filepath.Join(inDir, "oci-labels") + const script = ".stacker-run.sh" + err = c.BindMount(dir, labelInDir, "") if err != nil { return err } - rootfs := path.Join(opts.Config.RootFSDir, writable, "rootfs") - runPath := path.Join(dir, ".stacker-run.sh") - err = generateShellForRunning(rootfs, l.GenerateLabels, runPath) + rootfs := filepath.Join(opts.Config.RootFSDir, writable, "rootfs") + err = generateShellForRunning(rootfs, l.GenerateLabels, filepath.Join(dir, script)) if err != nil { return err } - err = c.Execute([]string{"/stacker/oci-labels/.stacker-run.sh"}, nil) + err = c.Execute([]string{filepath.Join(labelInDir, script)}, nil) if err != nil { return err } @@ -165,7 +172,7 @@ func (b *Builder) updateOCIConfigForOutput(sf *types.Stackerfile, s types.Storag continue } - content, err := os.ReadFile(path.Join(dir, ent.Name())) + content, err := os.ReadFile(filepath.Join(dir, ent.Name())) if err != nil { return errors.Wrapf(err, "couldn't read label %s", ent.Name()) } @@ -361,6 +368,10 @@ func (b *Builder) build(s types.Storage, file string) error { } log.Infof("preparing image %s...", name) + inDir := types.InternalStackerDir + if l.WasLegacyImport { + inDir = types.LegacyInternalStackerDir + } // We need to run the imports first since we now compare // against imports for caching layers. Since we don't do @@ -450,18 +461,18 @@ func (b *Builder) build(s types.Storage, file string) error { } defer c.Close() - err = SetupBuildContainerConfig(opts.Config, s, c, name) + err = SetupBuildContainerConfig(opts.Config, s, c, inDir, name) if err != nil { return err } - err = SetupLayerConfig(opts.Config, c, l, name) + err = SetupLayerConfig(opts.Config, c, l, inDir, name) if err != nil { return err } if opts.SetupOnly { - err = c.SaveConfigFile(path.Join(opts.Config.RootFSDir, name, "lxc.conf")) + err = c.SaveConfigFile(filepath.Join(opts.Config.RootFSDir, name, "lxc.conf")) if err != nil { return errors.Wrapf(err, "error saving config file for %s", name) } @@ -471,15 +482,15 @@ func (b *Builder) build(s types.Storage, file string) error { } if len(l.Run) != 0 { - rootfs := path.Join(opts.Config.RootFSDir, name, "rootfs") - shellScript := path.Join(opts.Config.StackerDir, "imports", name, ".stacker-run.sh") + rootfs := filepath.Join(opts.Config.RootFSDir, name, "rootfs") + shellScript := filepath.Join(opts.Config.StackerDir, "imports", name, ".stacker-run.sh") err = generateShellForRunning(rootfs, l.Run, shellScript) if err != nil { return err } // These should all be non-interactive; let's ensure that. - err = c.Execute([]string{"/stacker/imports/.stacker-run.sh"}, nil) + err = c.Execute([]string{filepath.Join(inDir, "imports", ".stacker-run.sh")}, nil) if err != nil { if opts.OnRunFailure != "" { err2 := c.Execute([]string{opts.OnRunFailure}, os.Stdin) @@ -654,7 +665,7 @@ func runInternalGoSubcommand(config types.StackerConfig, args []string) error { return errors.WithStack(c.Run()) } -func SetupBuildContainerConfig(config types.StackerConfig, storage types.Storage, c *container.Container, name string) error { +func SetupBuildContainerConfig(config types.StackerConfig, storage types.Storage, c *container.Container, inDir string, name string) error { rootfsPivot := path.Join(config.StackerDir, "rootfsPivot") if err := os.MkdirAll(rootfsPivot, 0755); err != nil { return err @@ -695,7 +706,7 @@ func SetupBuildContainerConfig(config types.StackerConfig, storage types.Storage } // make stacker binary available inside container - if err := c.BindMount(binary, insideStaticStacker, ""); err != nil { + if err := c.BindMount(binary, filepath.Join(inDir, "bin/stacker"), ""); err != nil { return err } @@ -732,7 +743,7 @@ func SetupBuildContainerConfig(config types.StackerConfig, storage types.Storage return nil } -func SetupLayerConfig(config types.StackerConfig, c *container.Container, l types.Layer, name string) error { +func SetupLayerConfig(config types.StackerConfig, c *container.Container, l types.Layer, inDir, name string) error { env, err := l.BuildEnvironment(name) if err != nil { return err @@ -740,11 +751,19 @@ func SetupLayerConfig(config types.StackerConfig, c *container.Container, l type importsDir := path.Join(config.StackerDir, "imports", name) if _, err := os.Stat(importsDir); err == nil { - log.Debugf("bind mounting %s into container", importsDir) - err = c.BindMount(importsDir, "/stacker/imports", "ro") + d := filepath.Join(inDir, "imports") + log.Debugf("bind mounting %s into container at %s", importsDir, d) + err = c.BindMount(importsDir, d, "ro") if err != nil { return err } + // legacy expect imports in /stacker + if inDir == types.LegacyInternalStackerDir { + err = c.BindMount(importsDir, types.InternalStackerDir, "ro") + if err != nil { + return err + } + } } else { log.Debugf("not bind mounting %s into container", importsDir) } @@ -753,8 +772,9 @@ func SetupLayerConfig(config types.StackerConfig, c *container.Container, l type if l.Bom != nil { artifactsDir := path.Join(config.StackerDir, "artifacts", name) if _, err := os.Stat(artifactsDir); err == nil { - log.Debugf("bind mounting %s into container", artifactsDir) - err = c.BindMount(artifactsDir, "/stacker/artifacts", "rw") + d := filepath.Join(inDir, "artifacts") + log.Debugf("bind mounting %s dir into container at %s", artifactsDir, d) + err = c.BindMount(artifactsDir, d, "rw") if err != nil { return err } diff --git a/pkg/stacker/grab.go b/pkg/stacker/grab.go index fdb58c92..9efca86d 100644 --- a/pkg/stacker/grab.go +++ b/pkg/stacker/grab.go @@ -5,6 +5,7 @@ import ( "io/fs" "os" "path" + "path/filepath" "stackerbuild.io/stacker/pkg/container" "stackerbuild.io/stacker/pkg/types" @@ -19,30 +20,31 @@ func Grab(sc types.StackerConfig, storage types.Storage, name string, source str } defer c.Close() - err = c.BindMount(targetDir, "/stacker", "") + err = c.BindMount(targetDir, types.InternalStackerDir, "") if err != nil { return err } defer os.Remove(path.Join(sc.RootFSDir, name, "rootfs", "stacker")) - err = SetupBuildContainerConfig(sc, storage, c, name) + err = SetupBuildContainerConfig(sc, storage, c, types.InternalStackerDir, name) if err != nil { return err } - bcmd := []string{insideStaticStacker, "internal-go"} + bcmd := []string{filepath.Join(types.InternalStackerDir, types.BinStacker), "internal-go"} + iDestName := filepath.Join(types.InternalStackerDir, path.Base(source)) if idest == "" || source[len(source)-1:] != "/" { - err = c.Execute(append(bcmd, "cp", source, "/stacker/"+path.Base(source)), nil) + err = c.Execute(append(bcmd, "cp", source, iDestName), nil) } else { - err = c.Execute(append(bcmd, "cp", source, "/stacker/"), nil) + err = c.Execute(append(bcmd, "cp", source, types.InternalStackerDir+"/"), nil) } if err != nil { return err } if mode != nil { - err = c.Execute(append(bcmd, "chmod", fmt.Sprintf("%o", *mode), "/stacker/"+path.Base(source)), nil) + err = c.Execute(append(bcmd, "chmod", fmt.Sprintf("%o", *mode), iDestName), nil) if err != nil { return err } @@ -54,7 +56,7 @@ func Grab(sc types.StackerConfig, storage types.Storage, name string, source str owns += fmt.Sprintf(":%d", gid) } - err = c.Execute(append(bcmd, "chown", owns, "/stacker/"+path.Base(source)), nil) + err = c.Execute(append(bcmd, "chown", owns, iDestName), nil) if err != nil { return err } diff --git a/pkg/types/layer.go b/pkg/types/layer.go index 2390384f..44b66587 100644 --- a/pkg/types/layer.go +++ b/pkg/types/layer.go @@ -2,6 +2,7 @@ package types import ( "encoding/json" + "fmt" "io/fs" "os" "path/filepath" @@ -236,27 +237,29 @@ func (bs *Bind) UnmarshalYAML(unmarshal func(interface{}) error) error { } type Layer struct { - From ImageSource `yaml:"from" json:"from"` - Imports Imports `yaml:"import" json:"import,omitempty"` - OverlayDirs OverlayDirs `yaml:"overlay_dirs" json:"overlay_dirs,omitempty"` - Run StringList `yaml:"run" json:"run,omitempty"` - Cmd Command `yaml:"cmd" json:"cmd,omitempty"` - Entrypoint Command `yaml:"entrypoint" json:"entrypoint,omitempty"` - FullCommand Command `yaml:"full_command" json:"full_command,omitempty"` - BuildEnvPt []string `yaml:"build_env_passthrough" json:"build_env_passthrough,omitempty"` - BuildEnv map[string]string `yaml:"build_env" json:"build_env,omitempty"` - Environment map[string]string `yaml:"environment" json:"environment,omitempty"` - Volumes []string `yaml:"volumes" json:"volumes,omitempty"` - Labels map[string]string `yaml:"labels" json:"labels,omitempty"` - GenerateLabels StringList `yaml:"generate_labels" json:"generate_labels,omitempty"` - WorkingDir string `yaml:"working_dir" json:"working_dir,omitempty"` - BuildOnly bool `yaml:"build_only" json:"build_only,omitempty"` - Binds Binds `yaml:"binds" json:"binds,omitempty"` - RuntimeUser string `yaml:"runtime_user" json:"runtime_user,omitempty"` - Annotations map[string]string `yaml:"annotations" json:"annotations,omitempty"` - OS *string `yaml:"os" json:"os,omitempty"` - Arch *string `yaml:"arch" json:"arch,omitempty"` - Bom *Bom `yaml:"bom" json:"bom,omitempty"` + From ImageSource `yaml:"from" json:"from"` + Imports Imports `yaml:"imports" json:"imports,omitempty"` + LegacyImport Imports `yaml:"import" json:"import,omitempty"` + OverlayDirs OverlayDirs `yaml:"overlay_dirs" json:"overlay_dirs,omitempty"` + Run StringList `yaml:"run" json:"run,omitempty"` + Cmd Command `yaml:"cmd" json:"cmd,omitempty"` + Entrypoint Command `yaml:"entrypoint" json:"entrypoint,omitempty"` + FullCommand Command `yaml:"full_command" json:"full_command,omitempty"` + BuildEnvPt []string `yaml:"build_env_passthrough" json:"build_env_passthrough,omitempty"` + BuildEnv map[string]string `yaml:"build_env" json:"build_env,omitempty"` + Environment map[string]string `yaml:"environment" json:"environment,omitempty"` + Volumes []string `yaml:"volumes" json:"volumes,omitempty"` + Labels map[string]string `yaml:"labels" json:"labels,omitempty"` + GenerateLabels StringList `yaml:"generate_labels" json:"generate_labels,omitempty"` + WorkingDir string `yaml:"working_dir" json:"working_dir,omitempty"` + BuildOnly bool `yaml:"build_only" json:"build_only,omitempty"` + Binds Binds `yaml:"binds" json:"binds,omitempty"` + RuntimeUser string `yaml:"runtime_user" json:"runtime_user,omitempty"` + Annotations map[string]string `yaml:"annotations" json:"annotations,omitempty"` + OS *string `yaml:"os" json:"os,omitempty"` + Arch *string `yaml:"arch" json:"arch,omitempty"` + Bom *Bom `yaml:"bom" json:"bom,omitempty"` + WasLegacyImport bool } func parseLayers(referenceDirectory string, lms yaml.MapSlice, requireHash bool) (map[string]Layer, error) { @@ -355,6 +358,15 @@ func parseLayers(referenceDirectory string, lms yaml.MapSlice, requireHash bool) layer.Arch = &arch } + if len(layer.LegacyImport) != 0 && len(layer.Imports) != 0 { + return nil, errors.New(fmt.Sprintf("layer '%s' cannot have both 'import' and 'imports'", name)) + } + if len(layer.LegacyImport) != 0 { + layer.Imports = layer.LegacyImport + layer.LegacyImport = nil + layer.WasLegacyImport = true + } + ret[name], err = layer.absolutify(referenceDirectory) if err != nil { return nil, err diff --git a/pkg/types/stackerfile.go b/pkg/types/stackerfile.go index 7f703fe2..c6faab27 100644 --- a/pkg/types/stackerfile.go +++ b/pkg/types/stackerfile.go @@ -15,6 +15,12 @@ import ( "stackerbuild.io/stacker/pkg/log" ) +const ( + InternalStackerDir = "/stacker" + LegacyInternalStackerDir = "/.stacker" + BinStacker = "bin/stacker" +) + type BuildConfig struct { Prerequisites []string `yaml:"prerequisites"` } @@ -38,6 +44,9 @@ type Stackerfile struct { // directory relative to which the stackerfile content is referenced ReferenceDirectory string + + // directory where internal binds will be done -> /stacker + InternalDir string } func (sf *Stackerfile) Get(name string) (Layer, bool) {