mirror of https://github.com/knative/func.git
Go tar (#2672)
* Fixed pod readiness check Detect also if pod did not exited prematurely. Signed-off-by: Matej Vašek <mvasek@redhat.com> * 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 <mvasek@redhat.com> --------- Signed-off-by: Matej Vašek <mvasek@redhat.com>
This commit is contained in:
parent
23668cbf36
commit
4de372564a
|
@ -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 \
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -46,6 +50,8 @@ func main() {
|
|||
cmd = s2iCmd
|
||||
case "socat":
|
||||
cmd = socat
|
||||
case "sh":
|
||||
cmd = sh
|
||||
}
|
||||
|
||||
err := cmd(ctx)
|
||||
|
@ -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
go.mod
2
go.mod
|
@ -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
|
||||
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
Binary file not shown.
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,316 @@
|
|||
package tar_test
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
tarutil "knative.dev/func/pkg/tar"
|
||||
)
|
||||
|
||||
func TestExtractErrors(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
createReader func(*testing.T) io.Reader
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "non escaping link",
|
||||
createReader: nonEscapingSymlink,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "missing parent of regular file",
|
||||
createReader: missingParentRegular,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "missing parent of link",
|
||||
createReader: missingParentLink,
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "absolute symlink",
|
||||
createReader: absoluteSymlink,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "direct escape",
|
||||
createReader: directEscape,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "indirect link escape",
|
||||
createReader: indirectLinkEscape,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "indirect link escape with overwrite",
|
||||
createReader: indirectLinkEscapeWithOverwrites,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "double dot in name",
|
||||
createReader: doubleDotInName,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
d := t.TempDir()
|
||||
if err := tarutil.Extract(tt.createReader(t), d); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Extract() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func nonEscapingSymlink(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: "subdir",
|
||||
Typeflag: tar.TypeDir,
|
||||
Mode: 0777,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = w.WriteHeader(&tar.Header{
|
||||
Name: "subdir/parent",
|
||||
Linkname: "..",
|
||||
Typeflag: tar.TypeSymlink,
|
||||
Mode: 0777,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return &buff
|
||||
|
||||
}
|
||||
|
||||
func absoluteSymlink(t *testing.T) io.Reader {
|
||||
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: "a.lnk",
|
||||
Linkname: "/etc/shadow",
|
||||
Typeflag: tar.TypeSymlink,
|
||||
Mode: 0777,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return &buff
|
||||
}
|
||||
|
||||
func directEscape(t *testing.T) io.Reader {
|
||||
t.Helper()
|
||||
|
||||
var err error
|
||||
var buff bytes.Buffer
|
||||
var msg = "I am free!!!"
|
||||
w := tar.NewWriter(&buff)
|
||||
defer func(w *tar.Writer) {
|
||||
_ = w.Close()
|
||||
}(w)
|
||||
err = w.WriteHeader(&tar.Header{
|
||||
Name: "../escaped.txt",
|
||||
Typeflag: tar.TypeReg,
|
||||
Mode: 0644,
|
||||
Size: int64(len(msg)),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = w.Write([]byte(msg))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return &buff
|
||||
}
|
||||
|
||||
func indirectLinkEscape(t *testing.T) io.Reader {
|
||||
t.Helper()
|
||||
t.Skip("we are not checking for this since the core utils tar does not too")
|
||||
|
||||
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: "subdir",
|
||||
Typeflag: tar.TypeDir,
|
||||
Mode: 0777,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = w.WriteHeader(&tar.Header{
|
||||
Name: "subdir/parent",
|
||||
Linkname: "..",
|
||||
Typeflag: tar.TypeSymlink,
|
||||
Mode: 0777,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = w.WriteHeader(&tar.Header{
|
||||
Name: "escape",
|
||||
Linkname: "subdir/parent/..",
|
||||
Typeflag: tar.TypeSymlink,
|
||||
Mode: 0777,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return &buff
|
||||
}
|
||||
|
||||
func indirectLinkEscapeWithOverwrites(t *testing.T) io.Reader {
|
||||
t.Helper()
|
||||
t.Skip("we are not checking for this since the core utils tar does not too")
|
||||
|
||||
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: "subdir",
|
||||
Typeflag: tar.TypeDir,
|
||||
Mode: 0777,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = w.WriteHeader(&tar.Header{
|
||||
Name: "subdir/parent",
|
||||
Typeflag: tar.TypeDir,
|
||||
Mode: 0777,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = w.WriteHeader(&tar.Header{
|
||||
Name: "escape",
|
||||
Linkname: "subdir/parent/..",
|
||||
Typeflag: tar.TypeSymlink,
|
||||
Mode: 0777,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = w.WriteHeader(&tar.Header{
|
||||
Name: "subdir/parent",
|
||||
Linkname: "..",
|
||||
Typeflag: tar.TypeSymlink,
|
||||
Mode: 0777,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return &buff
|
||||
}
|
||||
|
||||
func doubleDotInName(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: "subdir",
|
||||
Typeflag: tar.TypeDir,
|
||||
Mode: 0777,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = w.WriteHeader(&tar.Header{
|
||||
Name: "subdir/parent",
|
||||
Typeflag: tar.TypeDir,
|
||||
Mode: 0777,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = w.WriteHeader(&tar.Header{
|
||||
Name: "subdir/parent/../../a.txt",
|
||||
Typeflag: tar.TypeReg,
|
||||
Mode: 0644,
|
||||
Size: 0,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return &buff
|
||||
}
|
||||
|
||||
func missingParentRegular(t *testing.T) io.Reader {
|
||||
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: "missing/a.txt",
|
||||
Typeflag: tar.TypeReg,
|
||||
Size: 0,
|
||||
Mode: 0644,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return &buff
|
||||
}
|
||||
|
||||
func missingParentLink(t *testing.T) io.Reader {
|
||||
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: "missing/a.lnk",
|
||||
Linkname: "a.txt",
|
||||
Typeflag: tar.TypeSymlink,
|
||||
Mode: 0777,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return &buff
|
||||
}
|
Loading…
Reference in New Issue