Skip to content

Commit

Permalink
feat(client): support kube build
Browse files Browse the repository at this point in the history
Fixes #14527

Signed-off-by: fixomatic-ctrl <[email protected]>
  • Loading branch information
fixomatic-ctrl committed Nov 5, 2024
1 parent de99041 commit d320148
Show file tree
Hide file tree
Showing 18 changed files with 1,135 additions and 240 deletions.
26 changes: 19 additions & 7 deletions cmd/podman/kube/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"net/http"
"os"
"os/signal"
"path"
"path/filepath"
"strings"
"syscall"

Expand Down Expand Up @@ -174,6 +176,13 @@ func playFlags(cmd *cobra.Command) {
flags.BoolVar(&playOptions.UseLongAnnotations, noTruncFlagName, false, "Use annotations that are not truncated to the Kubernetes maximum length of 63 characters")
_ = flags.MarkHidden(noTruncFlagName)

buildFlagName := "build"
flags.BoolVar(&playOptions.BuildCLI, buildFlagName, false, "Build all images in a YAML (given Containerfiles exist)")

contextDirFlagName := "context-dir"
flags.StringVar(&playOptions.ContextDir, contextDirFlagName, "", "Path to top level of context directory")
_ = cmd.RegisterFlagCompletionFunc(contextDirFlagName, completion.AutocompleteDefault)

if !registry.IsRemote() {
certDirFlagName := "cert-dir"
flags.StringVar(&playOptions.CertDir, certDirFlagName, "", "`Pathname` of a directory containing TLS certificates and keys")
Expand All @@ -183,13 +192,6 @@ func playFlags(cmd *cobra.Command) {
flags.StringVar(&playOptions.SeccompProfileRoot, seccompProfileRootFlagName, defaultSeccompRoot, "Directory path for seccomp profiles")
_ = cmd.RegisterFlagCompletionFunc(seccompProfileRootFlagName, completion.AutocompleteDefault)

buildFlagName := "build"
flags.BoolVar(&playOptions.BuildCLI, buildFlagName, false, "Build all images in a YAML (given Containerfiles exist)")

contextDirFlagName := "context-dir"
flags.StringVar(&playOptions.ContextDir, contextDirFlagName, "", "Path to top level of context directory")
_ = cmd.RegisterFlagCompletionFunc(contextDirFlagName, completion.AutocompleteDefault)

flags.StringVar(&playOptions.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)")

_ = flags.MarkHidden("signature-policy")
Expand Down Expand Up @@ -228,6 +230,16 @@ func play(cmd *cobra.Command, args []string) error {
return err
}
playOptions.SystemContext = systemContext
// if we specified --build=true but did not provide any context dir, default to directory of args[0]
if playOptions.ContextDir == "" {
parentDir := path.Dir(args[0])
if _, err := os.Stat(parentDir); err == nil {
playOptions.ContextDir, err = filepath.Abs(parentDir)
if err != nil {
return err
}
}
}
}
}
if cmd.Flags().Changed("authfile") {
Expand Down
11 changes: 4 additions & 7 deletions pkg/bindings/images/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import (
"strconv"
"strings"

util2 "github.com/containers/podman/v5/pkg/bindings/internal/util"

"github.com/containers/buildah/define"
imageTypes "github.com/containers/image/v5/types"
ldefine "github.com/containers/podman/v5/libpod/define"
Expand All @@ -35,11 +37,6 @@ import (
"github.com/sirupsen/logrus"
)

type devino struct {
Dev uint64
Ino uint64
}

var iidRegex = regexp.Delayed(`^[0-9a-f]{12}`)

type BuildResponse struct {
Expand Down Expand Up @@ -723,7 +720,7 @@ func nTar(excludes []string, sources ...string) (io.ReadCloser, error) {
defer pw.Close()
defer gw.Close()
defer tw.Close()
seen := make(map[devino]string)
seen := make(map[util2.Devino]string)
for i, src := range sources {
source, err := filepath.Abs(src)
if err != nil {
Expand Down Expand Up @@ -786,7 +783,7 @@ func nTar(excludes []string, sources ...string) (io.ReadCloser, error) {
if err != nil {
return err
}
di, isHardLink := checkHardLink(info)
di, isHardLink := util2.CheckHardLink(info)
if err != nil {
return err
}
Expand Down
9 changes: 0 additions & 9 deletions pkg/bindings/images/build_windows.go

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
//go:build !windows

package images
package util

import (
"os"
"syscall"
)

func checkHardLink(fi os.FileInfo) (devino, bool) {
func CheckHardLink(fi os.FileInfo) (Devino, bool) {
st := fi.Sys().(*syscall.Stat_t)
return devino{
return Devino{
Dev: uint64(st.Dev), //nolint: unconvert
Ino: st.Ino,
}, st.Nlink > 1
Expand Down
9 changes: 9 additions & 0 deletions pkg/bindings/internal/util/build_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package util

import (
"os"
)

func CheckHardLink(fi os.FileInfo) (Devino, bool) {
return Devino{}, false
}
189 changes: 189 additions & 0 deletions pkg/bindings/internal/util/tar_builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package util

import (
"archive/tar"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"

"github.com/containers/storage/pkg/fileutils"
"github.com/containers/storage/pkg/ioutils"
"github.com/hashicorp/go-multierror"
gzip "github.com/klauspost/pgzip"
)

type Devino struct {
Dev uint64
Ino uint64
}

type TarBuilder struct {
sources []sourceMapping
excludes []string
}

type sourceMapping struct {
source string // Absolute path of the source directory/file
target string // Custom path inside the tar archive
}

// NewTarBuilder returns a new TarBuilder
func NewTarBuilder() *TarBuilder {
return &TarBuilder{
sources: []sourceMapping{},
excludes: []string{},
}
}

// Add adds a new source directory or file and the corresponding target inside the tar.
func (tb *TarBuilder) Add(source string, target string) error {
absSource, err := filepath.Abs(source)
if err != nil {
return fmt.Errorf("failed to get absolute path for source: %v", err)
}
tb.sources = append(tb.sources, sourceMapping{source: absSource, target: target})
return nil
}

// Exclude adds patterns to be excluded during tar creation.
func (tb *TarBuilder) Exclude(patterns ...string) {
tb.excludes = append(tb.excludes, patterns...)
}

// Build generates the tarball and returns a ReadCloser for the tar stream.
func (tb *TarBuilder) Build() (io.ReadCloser, error) {
if len(tb.sources) == 0 {
return nil, fmt.Errorf("no source(s) added for tar creation")
}

pm, err := fileutils.NewPatternMatcher(tb.excludes)
if err != nil {
return nil, fmt.Errorf("processing excludes list %v: %w", tb.excludes, err)
}

pr, pw := io.Pipe()

var merr *multierror.Error
go func() {
gw := gzip.NewWriter(pw)
tw := tar.NewWriter(gw)

defer pw.Close()
defer gw.Close()
defer tw.Close()

seen := make(map[Devino]string)

for _, src := range tb.sources {
err = filepath.WalkDir(src.source, func(path string, dentry fs.DirEntry, err error) error {
if err != nil {
return err
}

// Build the relative path under the custom target path
relPath, err := filepath.Rel(src.source, path)
if err != nil {
return err
}
targetPath := filepath.ToSlash(filepath.Join(src.target, relPath))

// Check exclusion patterns
if !filepath.IsAbs(targetPath) {
excluded, err := pm.IsMatch(targetPath)
if err != nil {
return fmt.Errorf("checking if %q is excluded: %w", targetPath, err)
}
if excluded {
return nil
}
}

switch {
case dentry.Type().IsRegular(): // Handle files
info, err := dentry.Info()
if err != nil {
return err
}
di, isHardLink := CheckHardLink(info)
if err != nil {
return err
}

hdr, err := tar.FileInfoHeader(info, "")
if err != nil {
return err
}
hdr.Name = targetPath
hdr.Uid, hdr.Gid = 0, 0
orig, ok := seen[di]
if ok {
hdr.Typeflag = tar.TypeLink
hdr.Linkname = orig
hdr.Size = 0
return tw.WriteHeader(hdr)
}

f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()

if err := tw.WriteHeader(hdr); err != nil {
return err
}
_, err = io.Copy(tw, f)
if err == nil && isHardLink {
seen[di] = targetPath
}
return err
case dentry.IsDir(): // Handle directories
info, err := dentry.Info()
if err != nil {
return err
}
hdr, lerr := tar.FileInfoHeader(info, targetPath)
if lerr != nil {
return lerr
}
hdr.Name = targetPath
hdr.Uid, hdr.Gid = 0, 0
return tw.WriteHeader(hdr)
case dentry.Type()&os.ModeSymlink != 0: // Handle symlinks
link, err := os.Readlink(path)
if err != nil {
return err
}
info, err := dentry.Info()
if err != nil {
return err
}
hdr, lerr := tar.FileInfoHeader(info, link)
if lerr != nil {
return lerr
}
hdr.Name = targetPath
hdr.Uid, hdr.Gid = 0, 0
return tw.WriteHeader(hdr)
}
return nil
})

if err != nil {
merr = multierror.Append(merr, err)
}
}
}()

rc := ioutils.NewReadCloserWrapper(pr, func() error {
if merr != nil {
merr = multierror.Append(merr, pr.Close())
return merr.ErrorOrNil()
}
return pr.Close()
})

return rc, nil
}
Loading

0 comments on commit d320148

Please sign in to comment.