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

[WIP] [DNM] From scratch #2671

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
97 changes: 97 additions & 0 deletions pkg/tar/tar.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package tar

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

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

r := tar.NewReader(input)

var first bool = true
for {
var hdr *tar.Header
hdr, err = r.Next()
Fixed Show fixed Hide fixed
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

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

var targetPath, rel string
targetPath = filepath.Join(targetDir, filepath.FromSlash(hdr.Name))
Dismissed Show dismissed Hide dismissed
rel, err = filepath.Rel(targetDir, targetPath)
if err != nil {
return fmt.Errorf("cannot get relative path: %w", err)
}
if strings.HasPrefix(rel, "..") {
return fmt.Errorf("name escapes")
}

// remove if already exists
err = os.Remove(targetPath)
if err != nil && !errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("cannot remove: %w", err)
}

// ensure parent
err = os.MkdirAll(filepath.Dir(targetPath), 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(targetPath, os.FileMode(hdr.Mode&0777), r)
case hdr.Typeflag == tar.TypeDir:
err = os.MkdirAll(targetPath, os.FileMode(hdr.Mode)&fs.ModePerm)
case hdr.Typeflag == tar.TypeSymlink:
err = os.Symlink(hdr.Linkname, targetPath)
Dismissed Show dismissed Hide dismissed
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_TRUNC|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
}
168 changes: 168 additions & 0 deletions pkg/tar/tar_basic_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
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/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)
}

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)
}
return &buff
}
Loading
Loading