Skip to content

Commit

Permalink
Go tar (#2672)
Browse files Browse the repository at this point in the history
* Fixed pod readiness check

Detect also if pod did not exited prematurely.

Signed-off-by: Matej Vašek <[email protected]>

* Removed dependency on sh/tar from alpine image

This commit removes depencency on sh and tar binaries by implementing
the logic in our func-util binary.

Signed-off-by: Matej Vašek <[email protected]>

---------

Signed-off-by: Matej Vašek <[email protected]>
  • Loading branch information
matejvasek authored Jan 28, 2025
1 parent 23668cb commit 4de3725
Show file tree
Hide file tree
Showing 8 changed files with 617 additions and 1 deletion.
1 change: 1 addition & 0 deletions Dockerfile.utils
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ COPY --from=builder /workspace/func-util /usr/local/bin/
RUN ln -s /usr/local/bin/func-util /usr/local/bin/deploy && \
ln -s /usr/local/bin/func-util /usr/local/bin/scaffold && \
ln -s /usr/local/bin/func-util /usr/local/bin/s2i && \
ln -s /usr/local/bin/func-util /usr/local/bin/sh && \
ln -s /usr/local/bin/func-util /usr/local/bin/socat

LABEL \
Expand Down
21 changes: 21 additions & 0 deletions cmd/func-util/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ import (
"os"
"os/signal"
"path/filepath"
"slices"
"syscall"

"golang.org/x/sys/unix"

"github.com/openshift/source-to-image/pkg/cmd/cli"
"k8s.io/klog/v2"

Expand All @@ -20,6 +23,7 @@ import (
"knative.dev/func/pkg/k8s"
"knative.dev/func/pkg/knative"
"knative.dev/func/pkg/scaffolding"
"knative.dev/func/pkg/tar"
)

func main() {
Expand All @@ -46,6 +50,8 @@ func main() {
cmd = s2iCmd
case "socat":
cmd = socat
case "sh":
cmd = sh
}

err := cmd(ctx)
Expand Down Expand Up @@ -167,3 +173,18 @@ func (d deployDecorator) UpdateLabels(function fn.Function, labels map[string]st
}
return labels
}

func sh(ctx context.Context) error {
if !slices.Equal(os.Args[1:], []string{"-c", "umask 0000 && exec tar -xmf -"}) {
return fmt.Errorf("this is a fake sh (only for backward compatiblility purposes)")
}

wd, err := os.Getwd()
if err != nil {
return fmt.Errorf("cannot get working directory: %w", err)
}

unix.Umask(0)

return tar.Extract(os.Stdin, wd)
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ require (
golang.org/x/net v0.34.0
golang.org/x/oauth2 v0.24.0
golang.org/x/sync v0.10.0
golang.org/x/sys v0.29.0
golang.org/x/term v0.28.0
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
Expand Down Expand Up @@ -272,7 +273,6 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.7.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
Expand Down
5 changes: 5 additions & 0 deletions pkg/k8s/dialer.go
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,11 @@ func podReady(ctx context.Context, core v1.CoreV1Interface, podName, namespace s
d <- nil
return
}
if status.State.Terminated != nil {
msg, _ := GetPodLogs(ctx, namespace, podName, podName)
d <- fmt.Errorf("pod prematurely exited (output: %q, exitcode: %d)", msg, status.State.Terminated.ExitCode)
return
}
if status.State.Waiting != nil {
switch status.State.Waiting.Reason {
case "ErrImagePull",
Expand Down
Binary file modified pkg/k8s/testdata/content.tar
Binary file not shown.
104 changes: 104 additions & 0 deletions pkg/tar/tar.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package tar

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

func Extract(input io.Reader, destDir string) error {
var err error

des, err := os.ReadDir(destDir)
if err != nil {
return fmt.Errorf("cannot read dest dir: %w", err)
}
for _, de := range des {
err = os.RemoveAll(filepath.Join(destDir, de.Name()))
if err != nil {
return fmt.Errorf("cannot purge dest dir: %w", err)
}
}

r := tar.NewReader(input)

var first bool = true
for {
var hdr *tar.Header
hdr, err = r.Next()
if err != nil {
if errors.Is(err, io.EOF) {
if first {
// mimic tar output on empty input
return fmt.Errorf("does not look like a tar")
}
return nil
}
return err
}
first = false

name := hdr.Name
linkname := hdr.Linkname
if strings.Contains(name, "..") {
return fmt.Errorf("name contains '..': %s", name)
}
if path.IsAbs(linkname) {
return fmt.Errorf("absolute symlink: %s->%s", name, linkname)
}
if strings.HasPrefix(path.Clean(path.Join(path.Dir(name), linkname)), "..") {
return fmt.Errorf("link target escapes: %s->%s", name, linkname)
}

var destPath, rel string
destPath = filepath.Join(destDir, filepath.FromSlash(name))
rel, err = filepath.Rel(destDir, destPath)
if err != nil {
return fmt.Errorf("cannot get relative path: %w", err)
}
if strings.HasPrefix(rel, "..") {
return fmt.Errorf("name escapes")
}

// ensure parent
err = os.MkdirAll(filepath.Dir(destPath), os.FileMode(hdr.Mode)&fs.ModePerm|0111)
if err != nil {
return fmt.Errorf("cannot ensure parent: %w", err)
}

switch {
case hdr.Typeflag == tar.TypeReg:
err = writeRegularFile(destPath, os.FileMode(hdr.Mode&0777), r)
case hdr.Typeflag == tar.TypeDir:
err = os.MkdirAll(destPath, os.FileMode(hdr.Mode)&fs.ModePerm)
case hdr.Typeflag == tar.TypeSymlink:
err = os.Symlink(linkname, destPath)
default:
_, _ = fmt.Printf("unsupported type flag: %d\n", hdr.Typeflag)
}
if err != nil {
return fmt.Errorf("cannot create entry: %w", err)
}
}
}

func writeRegularFile(target string, perm os.FileMode, content io.Reader) error {
f, err := os.OpenFile(target, os.O_CREATE|os.O_EXCL|os.O_WRONLY, perm)
if err != nil {
return err
}
defer func(f *os.File) {
_ = f.Close()
}(f)
_, err = io.Copy(f, content)
if err != nil {
return err
}
return nil
}
169 changes: 169 additions & 0 deletions pkg/tar/tar_basic_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package tar_test

import (
"archive/tar"
"bytes"
"io"
"os"
"path/filepath"
"testing"

tarutil "knative.dev/func/pkg/tar"
)

const (
aTxt1 = "a.txt first revision"
bTxt1 = "b.txt first revision"
aTxt2 = "a.txt second revision"
bTxt2 = "b.txt second revision"
)

func TestExtract(t *testing.T) {
var err error
d := t.TempDir()
err = tarutil.Extract(tarballV1(t), d)
if err != nil {
t.Fatal(err)
}

bs, err := os.ReadFile(filepath.Join(d, "dir/a.txt"))
if err != nil {
t.Fatal(err)
}
s := string(bs)
if s != aTxt1 {
t.Errorf("unexpected data: %s", s)
}
bs, err = os.ReadFile(filepath.Join(d, "dir/b.txt"))
if err != nil {
t.Fatal(err)
}
s = string(bs)
if s != bTxt1 {
t.Errorf("unexpected data: %s", s)
}

err = tarutil.Extract(tarballV2(t), d)
if err != nil {
t.Fatal(err)
}

bs, err = os.ReadFile(filepath.Join(d, "dir/a.txt"))
if err != nil {
t.Fatal(err)
}
s = string(bs)
if s != aTxt2 {
t.Errorf("unexpected data: %s", s)
}
bs, err = os.ReadFile(filepath.Join(d, "dir/b.txt"))
if err != nil {
t.Fatal(err)
}
s = string(bs)
if s != bTxt2 {
t.Errorf("unexpected data: %s", s)
}
}

func tarballV1(t *testing.T) io.Reader {
t.Helper()

var err error
var buff bytes.Buffer

w := tar.NewWriter(&buff)
defer func(w *tar.Writer) {
_ = w.Close()
}(w)

err = w.WriteHeader(&tar.Header{
Name: "dir/a.txt",
Typeflag: tar.TypeReg,
Mode: 0644,
Size: int64(len(aTxt1)),
})
if err != nil {
t.Fatal(err)
}
_, err = w.Write([]byte(aTxt1))
if err != nil {
t.Fatal(err)
}

err = w.WriteHeader(&tar.Header{
Name: "dir/data1",
Typeflag: tar.TypeReg,
Mode: 0644,
Size: int64(len(bTxt1)),
})
if err != nil {
t.Fatal(err)
}
_, err = w.Write([]byte(bTxt1))
if err != nil {
t.Fatal(err)
}

err = w.WriteHeader(&tar.Header{
Name: "dir/b.txt",
Linkname: "data1",
Typeflag: tar.TypeSymlink,
})
if err != nil {
t.Fatal(err)
}

return &buff
}

func tarballV2(t *testing.T) io.Reader {
t.Helper()

var err error
var buff bytes.Buffer

w := tar.NewWriter(&buff)
defer func(w *tar.Writer) {
_ = w.Close()
}(w)

err = w.WriteHeader(&tar.Header{
Name: "dir/a.txt",
Typeflag: tar.TypeReg,
Mode: 0644,
Size: int64(len(aTxt2)),
})
if err != nil {
t.Fatal(err)
}
_, err = w.Write([]byte(aTxt2))
if err != nil {
t.Fatal(err)
}

err = w.WriteHeader(&tar.Header{
Name: "dir/b.txt",
Linkname: "data2",
Typeflag: tar.TypeSymlink,
})
if err != nil {
t.Fatal(err)
}

err = w.WriteHeader(&tar.Header{
Name: "dir/data2",
Typeflag: tar.TypeReg,
Mode: 0644,
Size: int64(len(bTxt2)),
})
if err != nil {
t.Fatal(err)
}
_, err = w.Write([]byte(bTxt2))
if err != nil {
t.Fatal(err)
}

return &buff
}
Loading

0 comments on commit 4de3725

Please sign in to comment.