From bcb279121c1f3633396164ec4e36e2a17634468e Mon Sep 17 00:00:00 2001 From: andrew <> Date: Fri, 2 Feb 2024 00:16:59 +0900 Subject: [PATCH] Explicitly create dirs with modes --- dinkerlib/args.go | 20 ++++++-- dinkerlib/dinkerlib.go | 107 ++++++++++++++++++++++++++++++++--------- 2 files changed, 101 insertions(+), 26 deletions(-) diff --git a/dinkerlib/args.go b/dinkerlib/args.go index 511317f..e8a78cc 100644 --- a/dinkerlib/args.go +++ b/dinkerlib/args.go @@ -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"` } @@ -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 diff --git a/dinkerlib/dinkerlib.go b/dinkerlib/dinkerlib.go index cdcdca0..6a0fb2f 100644 --- a/dinkerlib/dinkerlib.go +++ b/dinkerlib/dinkerlib.go @@ -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{} @@ -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 {