Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(import): add "copy" support for import #345

Merged
merged 1 commit into from Dec 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion base.go
Expand Up @@ -31,14 +31,16 @@ type BaseLayerOpts struct {
func GetBase(o BaseLayerOpts) error {
switch o.Layer.From.Type {
case types.BuiltLayer:
fallthrough
case types.ScratchLayer:
return nil
case types.TarLayer:
cacheDir := path.Join(o.Config.StackerDir, "layer-bases")
if err := os.MkdirAll(cacheDir, 0755); err != nil {
return err
}

_, err := acquireUrl(o.Config, o.Storage, o.Layer.From.Url, cacheDir, o.Progress, "")
_, err := acquireUrl(o.Config, o.Storage, o.Layer.From.Url, cacheDir, o.Progress, "", nil, -1, -1)
return err
/* now we can do all the containers/image types */
case types.OCILayer:
Expand Down Expand Up @@ -74,6 +76,8 @@ func SetupRootfs(o BaseLayerOpts) error {
}

switch o.Layer.From.Type {
case types.ScratchLayer:
return o.Storage.SetupEmptyRootfs(o.Name)
case types.TarLayer:
err := o.Storage.SetupEmptyRootfs(o.Name)
if err != nil {
Expand Down
4 changes: 3 additions & 1 deletion build.go
Expand Up @@ -352,10 +352,12 @@ func (b *Builder) build(s types.Storage, file string) error {
return err
}

if err := Import(opts.Config, s, name, l.Imports, opts.Progress); err != nil {
if err := Import(opts.Config, s, name, l.Imports, &l.OverlayDirs, opts.Progress); err != nil {
return err
}

log.Debugf("overlay-dirs, possibly modified after import: %v", l.OverlayDirs)

// Need to check if the image has bind mounts, if the image has bind mounts,
// it needs to be rebuilt regardless of the build cache
// The reason is that tracking build cache for bind mounted folders
Expand Down
13 changes: 13 additions & 0 deletions cache.go
Expand Up @@ -207,6 +207,11 @@ func (c *BuildCache) Lookup(name string) (*CacheEntry, bool, error) {
}

for _, imp := range l.Imports {
if imp.Dest != "" {
// ignore imports which are copied
continue
}

cachedImport, ok := result.Imports[imp.Path]
if !ok {
log.Infof("cache miss because of new import: %s", imp.Path)
Expand Down Expand Up @@ -401,6 +406,9 @@ func (c *BuildCache) getBaseHash(name string) (string, error) {
}

return fmt.Sprintf("%d", baseHash), nil
case types.ScratchLayer:
// no base hash to use
return "", nil
case types.TarLayer:
// use the hash of the input tarball
cacheDir := path.Join(c.config.StackerDir, "layer-bases")
Expand Down Expand Up @@ -457,6 +465,11 @@ func (c *BuildCache) Put(name string, manifests map[types.LayerType]ispec.Descri
}

for _, imp := range l.Imports {
if imp.Dest != "" {
// ignore imports which are copied
continue
}

fname := path.Base(imp.Path)
importsDir := path.Join(c.config.StackerDir, "imports")
diskPath := path.Join(importsDir, name, fname)
Expand Down
2 changes: 1 addition & 1 deletion cmd/grab.go
Expand Up @@ -43,5 +43,5 @@ func doGrab(ctx *cli.Context) error {
return err
}

return stacker.Grab(config, s, name, parts[1], cwd)
return stacker.Grab(config, s, name, parts[1], cwd, nil, -1, -1)
}
30 changes: 30 additions & 0 deletions cmd/internal_go.go
Expand Up @@ -24,6 +24,14 @@ var internalGoCmd = cli.Command{
Name: "cp",
Action: doCP,
},
cli.Command{
Name: "chmod",
Action: doChmod,
},
cli.Command{
Name: "chown",
Action: doChown,
},
cli.Command{
Name: "check-aa-profile",
Action: doCheckAAProfile,
Expand Down Expand Up @@ -109,6 +117,28 @@ func doCP(ctx *cli.Context) error {
)
}

func doChmod(ctx *cli.Context) error {
if len(ctx.Args()) != 2 {
return errors.Errorf("wrong number of args")
}

return lib.Chmod(
ctx.Args()[0],
ctx.Args()[1],
)
}

func doChown(ctx *cli.Context) error {
if len(ctx.Args()) != 2 {
return errors.Errorf("wrong number of args")
}

return lib.Chown(
ctx.Args()[0],
ctx.Args()[1],
)
}

func doCheckAAProfile(ctx *cli.Context) error {
toCheck := ctx.Args()[0]
command := fmt.Sprintf("changeprofile %s", toCheck)
Expand Down
2 changes: 1 addition & 1 deletion cmd/main.go
Expand Up @@ -72,7 +72,7 @@ func shouldSkipInternalUserns(ctx *cli.Context) bool {
}

if len(args) >= 2 && args[0] == "internal-go" {
if args[1] == "atomfs" || args[1] == "cp" {
if args[1] == "atomfs" || args[1] == "cp" || args[1] == "chown" || args[1] == "chmod" {
return true
}
}
Expand Down
11 changes: 11 additions & 0 deletions doc/stacker_yaml.md
Expand Up @@ -110,6 +110,17 @@ import:
- /path/to/file
```

#### `import dest`

The `import` directive also supports specifying the destination path (specified
by `dest`) in the resulting container image, where the source file (specified
by `path`) will be copyied to, for example:
```
import:
- path: config.json
dest: /
```

### `overlay_dirs`
This directive works only with OverlayFS backend storage.

Expand Down
31 changes: 29 additions & 2 deletions grab.go
Expand Up @@ -2,6 +2,7 @@ package stacker

import (
"fmt"
"io/fs"
"os"
"path"

Expand All @@ -10,7 +11,9 @@ import (
"stackerbuild.io/stacker/types"
)

func Grab(sc types.StackerConfig, storage types.Storage, name string, source string, targetDir string) error {
func Grab(sc types.StackerConfig, storage types.Storage, name string, source string, targetDir string,
mode *fs.FileMode, uid, gid int,
) error {
c, err := container.New(sc, name)
if err != nil {
return err
Expand Down Expand Up @@ -38,5 +41,29 @@ func Grab(sc types.StackerConfig, storage types.Storage, name string, source str
return err
}

return c.Execute(fmt.Sprintf("/static-stacker internal-go cp %s /stacker/%s", source, path.Base(source)), nil)
err = c.Execute(fmt.Sprintf("/static-stacker internal-go cp %s /stacker/%s", source, path.Base(source)), nil)
if err != nil {
return err
}

if mode != nil {
err = c.Execute(fmt.Sprintf("/static-stacker internal-go chmod %s /stacker/%s", fmt.Sprintf("%o", *mode), path.Base(source)), nil)
if err != nil {
return err
}
}

if uid > 0 {
owns := fmt.Sprintf("%d", uid)
if gid > 0 {
owns += fmt.Sprintf(":%d", gid)
}

err = c.Execute(fmt.Sprintf("/static-stacker internal-go chown %s /stacker/%s", owns, path.Base(source)), nil)
if err != nil {
return err
}
}

return nil
}
47 changes: 37 additions & 10 deletions import.go
@@ -1,6 +1,7 @@
package stacker

import (
"io/fs"
"os"
"path"
"strings"
Expand Down Expand Up @@ -84,7 +85,7 @@ func verifyImportFileHash(imp string, hash string) error {
return nil
}

func importFile(imp string, cacheDir string, hash string) (string, error) {
func importFile(imp string, cacheDir string, hash string, mode *fs.FileMode, uid, gid int) (string, error) {
e1, err := os.Lstat(imp)
if err != nil {
return "", errors.Wrapf(err, "couldn't stat import %s", imp)
Expand All @@ -110,7 +111,7 @@ func importFile(imp string, cacheDir string, hash string) (string, error) {

if needsCopy {
log.Infof("copying %s", imp)
if err := lib.FileCopy(dest, imp); err != nil {
if err := lib.FileCopy(dest, imp, mode, uid, gid); err != nil {
return "", errors.Wrapf(err, "couldn't copy import %s", imp)
}
} else {
Expand Down Expand Up @@ -187,7 +188,7 @@ func importFile(imp string, cacheDir string, hash string) (string, error) {
if err != nil {
return "", err
}
err = lib.FileCopy(destpath, srcpath)
err = lib.FileCopy(destpath, srcpath, mode, uid, gid)
}
if err != nil {
return "", err
Expand All @@ -213,7 +214,9 @@ func validateHash(hash string) error {
return nil
}

func acquireUrl(c types.StackerConfig, storage types.Storage, i string, cache string, progress bool, expectedHash string) (string, error) {
func acquireUrl(c types.StackerConfig, storage types.Storage, i string, cache string, progress bool, expectedHash string,
mode *fs.FileMode, uid, gid int,
) (string, error) {
url, err := types.NewDockerishUrl(i)
if err != nil {
return "", err
Expand All @@ -226,7 +229,7 @@ func acquireUrl(c types.StackerConfig, storage types.Storage, i string, cache st

// It's just a path, let's copy it to .stacker.
if url.Scheme == "" {
return importFile(i, cache, expectedHash)
return importFile(i, cache, expectedHash, mode, uid, gid)
} else if url.Scheme == "http" || url.Scheme == "https" {
// otherwise, we need to download it
// first verify the hashes
Expand All @@ -242,7 +245,7 @@ func acquireUrl(c types.StackerConfig, storage types.Storage, i string, cache st
return "", errors.Errorf("The requested hash of %s import is different than the actual hash: %s != %s",
i, expectedHash, remoteHash)
}
return Download(cache, i, progress, expectedHash, remoteHash, remoteSize)
return Download(cache, i, progress, expectedHash, remoteHash, remoteSize, mode, uid, gid)
} else if url.Scheme == "stacker" {
// we always Grab() things from stacker://, because we need to
// mount the container's rootfs to get them and don't
Expand All @@ -254,7 +257,7 @@ func acquireUrl(c types.StackerConfig, storage types.Storage, i string, cache st
return "", err
}
defer cleanup()
err = Grab(c, storage, snap, url.Path, cache)
err = Grab(c, storage, snap, url.Path, cache, mode, uid, gid)
if err != nil {
return "", err
}
Expand All @@ -271,7 +274,11 @@ func acquireUrl(c types.StackerConfig, storage types.Storage, i string, cache st
}

func CleanImportsDir(c types.StackerConfig, name string, imports types.Imports, cache *BuildCache) error {
dir := path.Join(c.StackerDir, "imports", name)
// remove all copied imports
dir := path.Join(c.StackerDir, "imports-copy", name)
_ = os.RemoveAll(dir)

dir = path.Join(c.StackerDir, "imports", name)

cacheEntry, cacheHit := cache.Cache[name]
if !cacheHit {
Expand All @@ -298,20 +305,40 @@ func CleanImportsDir(c types.StackerConfig, name string, imports types.Imports,
return nil
}

func Import(c types.StackerConfig, storage types.Storage, name string, imports types.Imports, progress bool) error {
// Import files from different sources to an ephemeral or permanent destination.
func Import(c types.StackerConfig, storage types.Storage, name string, imports types.Imports, overlayDirs *types.OverlayDirs, progress bool) error {
dir := path.Join(c.StackerDir, "imports", name)

if err := os.MkdirAll(dir, 0755); err != nil {
return err
}

cpdir := path.Join(c.StackerDir, "imports-copy", name)

if err := os.MkdirAll(cpdir, 0755); err != nil {
return err
}

existing, err := os.ReadDir(dir)
if err != nil {
return errors.Wrapf(err, "couldn't read existing directory")
}

for _, i := range imports {
name, err := acquireUrl(c, storage, i.Path, dir, progress, i.Hash)
cache := dir
if i.Dest != "" {
tmpdir, err := os.MkdirTemp(cpdir, "")
if err != nil {
return errors.Wrapf(err, "couldn't create temp import copy directory")
}

ovl := types.OverlayDir{Source: tmpdir, Dest: "/"}
*overlayDirs = append(*overlayDirs, ovl)

cache = tmpdir
}

name, err := acquireUrl(c, storage, i.Path, cache, progress, i.Hash, i.Mode, i.Uid, i.Gid)
if err != nil {
return err
}
Expand Down