Skip to content

Commit

Permalink
Explicitly create dirs with modes
Browse files Browse the repository at this point in the history
  • Loading branch information
andrew committed Feb 1, 2024
1 parent 27056a3 commit bcb2791
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 26 deletions.
20 changes: 17 additions & 3 deletions dinkerlib/args.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
package dinkerlib

type BuildImageArgsDir struct {
// Name in parent in destination tree
Name string `json:"name"`
// Parsed as octal, defaults to 0755
Mode string `json:"mode"`
// Child dirs
Dirs []BuildImageArgsDir
// Child files
Files []BuildImageArgsFile
}

type BuildImageArgsFile struct {
// Name in parent in destination tree. Defaults to filename of source if empty.
Name string `json:"name"`
// Path of file to copy from
Source AbsPath `json:"source"`
// Defaults to the filename of Source in /. Ex: if source is `a/b/c` the resulting image will have the file at `/c`
Dest string `json:"dest"`
// Parsed as octal, defaults to 0644
Mode string `json:"mode"`
}
Expand All @@ -21,7 +33,9 @@ type BuildImageArgs struct {
Architecture string
// Defaults to FROM image os
Os string
// Files to add to the image
// Directories to build in the image root
Dirs []BuildImageArgsDir
// Files to add to the image root
Files []BuildImageArgsFile
// Don't inherit env from FROM image
ClearEnv bool
Expand Down
107 changes: 84 additions & 23 deletions dinkerlib/dinkerlib.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,84 @@ func canonicalJsonMarshal(sym any) []byte {
return ser
}

func writeDestFile(destTar *tar.Writer, parentPath string, f BuildImageArgsFile) error {
if strings.Contains(f.Name, "/") {
return fmt.Errorf("Dir %s name contains slashes; subdirs must be nested as objects", f.Name)
}
destName := Def(f.Name, f.Source.Filename())
var destPath string
if parentPath == "" {
destPath = destName
} else {
destPath = fmt.Sprintf("%s/%s", parentPath, destName)
}
mode, err := strconv.ParseInt(Def(f.Mode, "644"), 8, 32)
if err != nil {
return fmt.Errorf("file %s mode %s is not valid octal: %w", destPath, f.Mode, err)
}
stat, err := os.Stat(f.Source.Raw())
if err != nil {
return fmt.Errorf("error looking up metadata for layer file %s: %w", f.Source, err)
}
if err := destTar.WriteHeader(&tar.Header{
Typeflag: tar.TypeReg,
Name: destPath,
Mode: mode,
Size: stat.Size(),
}); err != nil {
return fmt.Errorf("error writing tar header for %s: %w", f.Source, err)
}
fSource, err := os.Open(f.Source.Raw())
if err != nil {
return fmt.Errorf("error opening source file %s for adding to layer: %w", f.Source, err)
}
_, err = io.Copy(destTar, fSource)
if err != nil {
return fmt.Errorf("error copying data from %s: %w", f.Source, err)
}
err = fSource.Close()
if err != nil {
return fmt.Errorf("error closing %s after reading: %w", f.Source, err)
}
return nil
}

func buildDestDir(destTar *tar.Writer, parentPath string, d BuildImageArgsDir) error {
if strings.Contains(d.Name, "/") {
return fmt.Errorf("Dir %s name contains slashes; subdirs must be nested as objects", d.Name)
}
var destPath string
if parentPath == "" {
destPath = d.Name
} else {
destPath = fmt.Sprintf("%s/%s", parentPath, d.Name)
}
mode, err := strconv.ParseInt(Def(d.Mode, "644"), 8, 32)
if err != nil {
return fmt.Errorf("file %s mode %s is not valid octal: %w", destPath, d.Mode, err)
}
if err := destTar.WriteHeader(&tar.Header{
Typeflag: tar.TypeDir,
Name: destPath,
Mode: mode,
}); err != nil {
return fmt.Errorf("error writing tar header for %s: %w", destPath, err)
}
for _, f := range d.Dirs {
err := buildDestDir(destTar, destPath, f)
if err != nil {
return err
}
}
for _, f := range d.Files {
err := writeDestFile(destTar, destPath, f)
if err != nil {
return err
}
}
return nil
}

func BuildImage(args BuildImageArgs) (hash string, err error) {
hashData := map[string]any{}

Expand Down Expand Up @@ -156,32 +234,15 @@ func BuildImage(args BuildImageArgs) (hash string, err error) {
gzWriter,
))
for _, f := range args.Files {
stat, err := os.Stat(f.Source.Raw())
if err != nil {
return "", fmt.Errorf("error looking up metadata for layer file %s: %w", f.Source, err)
}
mode, err := strconv.ParseInt(Def(f.Mode, "644"), 8, 32)
err := writeDestFile(destTar, "", f)
if err != nil {
return "", fmt.Errorf("file %s mode %s is not valid octal: %w", f.Source, f.Mode, err)
return "", err
}
if err := destTar.WriteHeader(&tar.Header{
Name: strings.TrimPrefix(Def(f.Dest, f.Source.Filename()), "/"),
Mode: mode,
Size: stat.Size(),
}); err != nil {
return "", fmt.Errorf("error writing tar header for %s: %w", f.Source, err)
}
fSource, err := os.Open(f.Source.Raw())
if err != nil {
return "", fmt.Errorf("error opening source file %s for adding to layer: %w", f.Source, err)
}
_, err = io.Copy(destTar, fSource)
if err != nil {
return "", fmt.Errorf("error copying data from %s: %w", f.Source, err)
}
err = fSource.Close()
}
for _, d := range args.Dirs {
err := buildDestDir(destTar, "", d)
if err != nil {
return "", fmt.Errorf("error closing %s after reading: %w", f.Source, err)
return "", err
}
}
if err := destTar.Close(); err != nil {
Expand Down

0 comments on commit bcb2791

Please sign in to comment.