diff options
Diffstat (limited to 'pkg/bindings')
-rw-r--r-- | pkg/bindings/containers/attach.go | 25 | ||||
-rw-r--r-- | pkg/bindings/containers/checkpoint.go | 9 | ||||
-rw-r--r-- | pkg/bindings/containers/term_unix.go | 25 | ||||
-rw-r--r-- | pkg/bindings/containers/term_windows.go | 69 | ||||
-rw-r--r-- | pkg/bindings/containers/types.go | 23 | ||||
-rw-r--r-- | pkg/bindings/containers/types_restore_options.go | 15 | ||||
-rw-r--r-- | pkg/bindings/generator/generator.go | 1 | ||||
-rw-r--r-- | pkg/bindings/images/build_unix.go | 1 | ||||
-rw-r--r-- | pkg/bindings/images/types.go | 8 | ||||
-rw-r--r-- | pkg/bindings/images/types_import_options.go | 45 | ||||
-rw-r--r-- | pkg/bindings/images/types_remove_options.go | 15 | ||||
-rw-r--r-- | pkg/bindings/play/play.go | 39 | ||||
-rw-r--r-- | pkg/bindings/play/types.go | 2 | ||||
-rw-r--r-- | pkg/bindings/play/types_kube_options.go | 15 | ||||
-rw-r--r-- | pkg/bindings/test/attach_test.go | 3 | ||||
-rw-r--r-- | pkg/bindings/test/auth_test.go | 3 | ||||
-rw-r--r-- | pkg/bindings/test/common_test.go | 2 | ||||
-rw-r--r-- | pkg/bindings/test/containers_test.go | 3 |
18 files changed, 263 insertions, 40 deletions
diff --git a/pkg/bindings/containers/attach.go b/pkg/bindings/containers/attach.go index c6d434c87..0c6ebdd2f 100644 --- a/pkg/bindings/containers/attach.go +++ b/pkg/bindings/containers/attach.go @@ -10,14 +10,12 @@ import ( "net/http" "net/url" "os" - "os/signal" "reflect" "strconv" "time" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/bindings" - sig "github.com/containers/podman/v4/pkg/signal" "github.com/containers/podman/v4/utils" "github.com/moby/term" "github.com/pkg/errors" @@ -94,7 +92,8 @@ func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Wri // Unless all requirements are met, don't use "stdin" is a terminal file, ok := stdin.(*os.File) - needTTY := ok && terminal.IsTerminal(int(file.Fd())) && ctnr.Config.Tty + outFile, outOk := stdout.(*os.File) + needTTY := ok && outOk && terminal.IsTerminal(int(file.Fd())) && ctnr.Config.Tty if needTTY { state, err := setRawTerminal(file) if err != nil { @@ -142,11 +141,10 @@ func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Wri if needTTY { winChange := make(chan os.Signal, 1) - signal.Notify(winChange, sig.SIGWINCH) winCtx, winCancel := context.WithCancel(ctx) defer winCancel() - - attachHandleResize(ctx, winCtx, winChange, false, nameOrID, file) + notifyWinChange(winCtx, winChange, file, outFile) + attachHandleResize(ctx, winCtx, winChange, false, nameOrID, file, outFile) } // If we are attaching around a start, we need to "signal" @@ -281,7 +279,7 @@ func DemuxFrame(r io.Reader, buffer []byte, length int) (frame []byte, err error n, err := io.ReadFull(r, buffer[0:length]) if err != nil { - return nil, nil + return nil, err } if n < length { err = io.ErrUnexpectedEOF @@ -345,9 +343,9 @@ func (f *rawFormatter) Format(entry *logrus.Entry) ([]byte, error) { // This is intended to not be run as a goroutine, handling resizing for a container // or exec session. It will call resize once and then starts a goroutine which calls resize on winChange -func attachHandleResize(ctx, winCtx context.Context, winChange chan os.Signal, isExec bool, id string, file *os.File) { +func attachHandleResize(ctx, winCtx context.Context, winChange chan os.Signal, isExec bool, id string, file *os.File, outFile *os.File) { resize := func() { - w, h, err := terminal.GetSize(int(file.Fd())) + w, h, err := getTermSize(file, outFile) if err != nil { logrus.Warnf("Failed to obtain TTY size: %v", err) } @@ -379,7 +377,7 @@ func attachHandleResize(ctx, winCtx context.Context, winChange chan os.Signal, i // Configure the given terminal for raw mode func setRawTerminal(file *os.File) (*terminal.State, error) { - state, err := terminal.MakeRaw(int(file.Fd())) + state, err := makeRawTerm(file) if err != nil { return nil, err } @@ -402,6 +400,7 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStar // TODO: Make this configurable (can't use streams' InputStream as it's // buffered) terminalFile := os.Stdin + terminalOutFile := os.Stdout logrus.Debugf("Starting & Attaching to exec session ID %q", sessionID) @@ -447,7 +446,7 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStar } logrus.SetFormatter(&logrus.TextFormatter{}) }() - w, h, err := terminal.GetSize(int(terminalFile.Fd())) + w, h, err := getTermSize(terminalFile, terminalOutFile) if err != nil { logrus.Warnf("Failed to obtain TTY size: %v", err) } @@ -490,11 +489,11 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStar if needTTY { winChange := make(chan os.Signal, 1) - signal.Notify(winChange, sig.SIGWINCH) winCtx, winCancel := context.WithCancel(ctx) defer winCancel() - attachHandleResize(ctx, winCtx, winChange, true, sessionID, terminalFile) + notifyWinChange(winCtx, winChange, terminalFile, terminalOutFile) + attachHandleResize(ctx, winCtx, winChange, true, sessionID, terminalFile, terminalOutFile) } if options.GetAttachInput() { diff --git a/pkg/bindings/containers/checkpoint.go b/pkg/bindings/containers/checkpoint.go index 1d8c34b33..bcb944488 100644 --- a/pkg/bindings/containers/checkpoint.go +++ b/pkg/bindings/containers/checkpoint.go @@ -79,7 +79,14 @@ func Restore(ctx context.Context, nameOrID string, options *RestoreOptions) (*en // Open the to-be-imported archive if needed. var r io.Reader - if i := options.GetImportAchive(); i != "" { + i := options.GetImportArchive() + if i == "" { + // backwards compat, ImportAchive is a typo but we still have to + // support this to avoid breaking users + // TODO: remove ImportAchive with 5.0 + i = options.GetImportAchive() + } + if i != "" { params.Set("import", "true") r, err = os.Open(i) if err != nil { diff --git a/pkg/bindings/containers/term_unix.go b/pkg/bindings/containers/term_unix.go new file mode 100644 index 000000000..2c976393f --- /dev/null +++ b/pkg/bindings/containers/term_unix.go @@ -0,0 +1,25 @@ +//go:build !windows +// +build !windows + +package containers + +import ( + "context" + "os" + "os/signal" + + sig "github.com/containers/podman/v4/pkg/signal" + "golang.org/x/crypto/ssh/terminal" +) + +func makeRawTerm(stdin *os.File) (*terminal.State, error) { + return terminal.MakeRaw(int(stdin.Fd())) +} + +func notifyWinChange(ctx context.Context, winChange chan os.Signal, stdin *os.File, stdout *os.File) { + signal.Notify(winChange, sig.SIGWINCH) +} + +func getTermSize(stdin *os.File, stdout *os.File) (width, height int, err error) { + return terminal.GetSize(int(stdin.Fd())) +} diff --git a/pkg/bindings/containers/term_windows.go b/pkg/bindings/containers/term_windows.go new file mode 100644 index 000000000..11d4bd50d --- /dev/null +++ b/pkg/bindings/containers/term_windows.go @@ -0,0 +1,69 @@ +package containers + +import ( + "context" + "os" + "time" + + sig "github.com/containers/podman/v4/pkg/signal" + "golang.org/x/crypto/ssh/terminal" + "golang.org/x/sys/windows" +) + +func makeRawTerm(stdin *os.File) (*terminal.State, error) { + state, err := terminal.MakeRaw(int(stdin.Fd())) + if err != nil { + return nil, err + } + + // Attempt VT if supported (recent versions of Windows 10+) + var raw uint32 + handle := windows.Handle(stdin.Fd()) + if err := windows.GetConsoleMode(handle, &raw); err != nil { + return nil, err + } + + tryVT := raw | windows.ENABLE_VIRTUAL_TERMINAL_INPUT + + if err := windows.SetConsoleMode(handle, tryVT); err != nil { + if err := windows.SetConsoleMode(handle, raw); err != nil { + return nil, err + } + } + + return state, nil +} + +func notifyWinChange(ctx context.Context, winChange chan os.Signal, stdin *os.File, stdout *os.File) { + // Simulate WINCH with polling + go func() { + var lastW int + var lastH int + + d := time.Millisecond * 250 + timer := time.NewTimer(d) + defer timer.Stop() + for ; ; timer.Reset(d) { + select { + case <-ctx.Done(): + return + case <-timer.C: + break + } + + w, h, err := terminal.GetSize(int(stdout.Fd())) + if err != nil { + continue + } + if w != lastW || h != lastH { + winChange <- sig.SIGWINCH + lastW, lastH = w, h + } + } + }() + +} + +func getTermSize(stdin *os.File, stdout *os.File) (width, height int, err error) { + return terminal.GetSize(int(stdout.Fd())) +} diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go index 66b90af9b..c87f82bf4 100644 --- a/pkg/bindings/containers/types.go +++ b/pkg/bindings/containers/types.go @@ -64,14 +64,21 @@ type RestoreOptions struct { IgnoreVolumes *bool IgnoreStaticIP *bool IgnoreStaticMAC *bool - ImportAchive *string - Keep *bool - Name *string - TCPEstablished *bool - Pod *string - PrintStats *bool - PublishPorts []string - FileLocks *bool + // ImportAchive is the path to an archive which contains the checkpoint data. + // + // Deprecated: Use ImportArchive instead. This field name is a typo and + // will be removed in a future major release. + ImportAchive *string + // ImportArchive is the path to an archive which contains the checkpoint data. + // ImportArchive is preferred over ImportAchive when both are set. + ImportArchive *string + Keep *bool + Name *string + TCPEstablished *bool + Pod *string + PrintStats *bool + PublishPorts []string + FileLocks *bool } //go:generate go run ../generator/generator.go CreateOptions diff --git a/pkg/bindings/containers/types_restore_options.go b/pkg/bindings/containers/types_restore_options.go index d2778396a..b1b14a704 100644 --- a/pkg/bindings/containers/types_restore_options.go +++ b/pkg/bindings/containers/types_restore_options.go @@ -92,6 +92,21 @@ func (o *RestoreOptions) GetImportAchive() string { return *o.ImportAchive } +// WithImportArchive set field ImportArchive to given value +func (o *RestoreOptions) WithImportArchive(value string) *RestoreOptions { + o.ImportArchive = &value + return o +} + +// GetImportArchive returns value of field ImportArchive +func (o *RestoreOptions) GetImportArchive() string { + if o.ImportArchive == nil { + var z string + return z + } + return *o.ImportArchive +} + // WithKeep set field Keep to given value func (o *RestoreOptions) WithKeep(value bool) *RestoreOptions { o.Keep = &value diff --git a/pkg/bindings/generator/generator.go b/pkg/bindings/generator/generator.go index a224013ea..e69973be1 100644 --- a/pkg/bindings/generator/generator.go +++ b/pkg/bindings/generator/generator.go @@ -1,3 +1,4 @@ +//go:build ignore // +build ignore package main diff --git a/pkg/bindings/images/build_unix.go b/pkg/bindings/images/build_unix.go index 0afb1deb6..67a5e2998 100644 --- a/pkg/bindings/images/build_unix.go +++ b/pkg/bindings/images/build_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package images diff --git a/pkg/bindings/images/types.go b/pkg/bindings/images/types.go index a44a3527f..75cb38a0a 100644 --- a/pkg/bindings/images/types.go +++ b/pkg/bindings/images/types.go @@ -11,6 +11,8 @@ type RemoveOptions struct { All *bool // Forces removes all containers based on the image Force *bool + // Ignore if a specified image does not exist and do not throw an error. + Ignore *bool } //go:generate go run ../generator/generator.go DiffOptions @@ -101,6 +103,12 @@ type ImportOptions struct { Reference *string // Url to option image to import. Cannot be used with the reader URL *string + // OS for the imported image + OS *string + // Architecture for the imported image + Architecture *string + // Variant for the imported image + Variant *string } //go:generate go run ../generator/generator.go PushOptions diff --git a/pkg/bindings/images/types_import_options.go b/pkg/bindings/images/types_import_options.go index ea66fa312..f958fe8b4 100644 --- a/pkg/bindings/images/types_import_options.go +++ b/pkg/bindings/images/types_import_options.go @@ -76,3 +76,48 @@ func (o *ImportOptions) GetURL() string { } return *o.URL } + +// WithOS set field OS to given value +func (o *ImportOptions) WithOS(value string) *ImportOptions { + o.OS = &value + return o +} + +// GetOS returns value of field OS +func (o *ImportOptions) GetOS() string { + if o.OS == nil { + var z string + return z + } + return *o.OS +} + +// WithArchitecture set field Architecture to given value +func (o *ImportOptions) WithArchitecture(value string) *ImportOptions { + o.Architecture = &value + return o +} + +// GetArchitecture returns value of field Architecture +func (o *ImportOptions) GetArchitecture() string { + if o.Architecture == nil { + var z string + return z + } + return *o.Architecture +} + +// WithVariant set field Variant to given value +func (o *ImportOptions) WithVariant(value string) *ImportOptions { + o.Variant = &value + return o +} + +// GetVariant returns value of field Variant +func (o *ImportOptions) GetVariant() string { + if o.Variant == nil { + var z string + return z + } + return *o.Variant +} diff --git a/pkg/bindings/images/types_remove_options.go b/pkg/bindings/images/types_remove_options.go index 1fbe5f4ea..613a33183 100644 --- a/pkg/bindings/images/types_remove_options.go +++ b/pkg/bindings/images/types_remove_options.go @@ -46,3 +46,18 @@ func (o *RemoveOptions) GetForce() bool { } return *o.Force } + +// WithIgnore set field Ignore to given value +func (o *RemoveOptions) WithIgnore(value bool) *RemoveOptions { + o.Ignore = &value + return o +} + +// GetIgnore returns value of field Ignore +func (o *RemoveOptions) GetIgnore() bool { + if o.Ignore == nil { + var z bool + return z + } + return *o.Ignore +} diff --git a/pkg/bindings/play/play.go b/pkg/bindings/play/play.go index d4018b6b3..8058a8514 100644 --- a/pkg/bindings/play/play.go +++ b/pkg/bindings/play/play.go @@ -2,6 +2,7 @@ package play import ( "context" + "io" "net/http" "os" "strconv" @@ -14,20 +15,25 @@ import ( ) func Kube(ctx context.Context, path string, options *KubeOptions) (*entities.PlayKubeReport, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + + return KubeWithBody(ctx, f, options) +} + +func KubeWithBody(ctx context.Context, body io.Reader, options *KubeOptions) (*entities.PlayKubeReport, error) { var report entities.PlayKubeReport if options == nil { options = new(KubeOptions) } - conn, err := bindings.GetClient(ctx) - if err != nil { - return nil, err - } - f, err := os.Open(path) + conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - defer f.Close() params, err := options.ToParams() if err != nil { @@ -46,7 +52,7 @@ func Kube(ctx context.Context, path string, options *KubeOptions) (*entities.Pla return nil, err } - response, err := conn.DoRequest(ctx, f, http.MethodPost, "/play/kube", params, header) + response, err := conn.DoRequest(ctx, body, http.MethodPost, "/play/kube", params, header) if err != nil { return nil, err } @@ -60,12 +66,6 @@ func Kube(ctx context.Context, path string, options *KubeOptions) (*entities.Pla } func KubeDown(ctx context.Context, path string) (*entities.PlayKubeReport, error) { - var report entities.PlayKubeReport - conn, err := bindings.GetClient(ctx) - if err != nil { - return nil, err - } - f, err := os.Open(path) if err != nil { return nil, err @@ -75,7 +75,18 @@ func KubeDown(ctx context.Context, path string) (*entities.PlayKubeReport, error logrus.Warn(err) } }() - response, err := conn.DoRequest(ctx, f, http.MethodDelete, "/play/kube", nil, nil) + + return KubeDownWithBody(ctx, f) +} + +func KubeDownWithBody(ctx context.Context, body io.Reader) (*entities.PlayKubeReport, error) { + var report entities.PlayKubeReport + conn, err := bindings.GetClient(ctx) + if err != nil { + return nil, err + } + + response, err := conn.DoRequest(ctx, body, http.MethodDelete, "/play/kube", nil, nil) if err != nil { return nil, err } diff --git a/pkg/bindings/play/types.go b/pkg/bindings/play/types.go index ca639e46b..dbff4304b 100644 --- a/pkg/bindings/play/types.go +++ b/pkg/bindings/play/types.go @@ -7,6 +7,8 @@ import ( //go:generate go run ../generator/generator.go KubeOptions // KubeOptions are optional options for replaying kube YAML files type KubeOptions struct { + // Annotations - Annotations to add to Pods + Annotations map[string]string // Authfile - path to an authentication file. Authfile *string // CertDir - to a directory containing TLS certifications and keys. diff --git a/pkg/bindings/play/types_kube_options.go b/pkg/bindings/play/types_kube_options.go index 83a6f1566..d7a452ea2 100644 --- a/pkg/bindings/play/types_kube_options.go +++ b/pkg/bindings/play/types_kube_options.go @@ -18,6 +18,21 @@ func (o *KubeOptions) ToParams() (url.Values, error) { return util.ToParams(o) } +// WithAnnotations set field Annotations to given value +func (o *KubeOptions) WithAnnotations(value map[string]string) *KubeOptions { + o.Annotations = value + return o +} + +// GetAnnotations returns value of field Annotations +func (o *KubeOptions) GetAnnotations() map[string]string { + if o.Annotations == nil { + var z map[string]string + return z + } + return o.Annotations +} + // WithAuthfile set field Authfile to given value func (o *KubeOptions) WithAuthfile(value string) *KubeOptions { o.Authfile = &value diff --git a/pkg/bindings/test/attach_test.go b/pkg/bindings/test/attach_test.go index 670566882..dcebe0809 100644 --- a/pkg/bindings/test/attach_test.go +++ b/pkg/bindings/test/attach_test.go @@ -44,7 +44,8 @@ var _ = Describe("Podman containers attach", func() { timeout := uint(5) err := containers.Stop(bt.conn, id, new(containers.StopOptions).WithTimeout(timeout)) if err != nil { - GinkgoWriter.Write([]byte(err.Error())) + _, writeErr := GinkgoWriter.Write([]byte(err.Error())) + Expect(writeErr).ShouldNot(HaveOccurred()) } }() diff --git a/pkg/bindings/test/auth_test.go b/pkg/bindings/test/auth_test.go index b421f0797..c4c4b16d8 100644 --- a/pkg/bindings/test/auth_test.go +++ b/pkg/bindings/test/auth_test.go @@ -40,7 +40,8 @@ var _ = Describe("Podman images", func() { AfterEach(func() { s.Kill() bt.cleanup() - registry.Stop() + err := registry.Stop() + Expect(err).To(BeNil()) }) // Test using credentials. diff --git a/pkg/bindings/test/common_test.go b/pkg/bindings/test/common_test.go index f51e5f404..f2602967b 100644 --- a/pkg/bindings/test/common_test.go +++ b/pkg/bindings/test/common_test.go @@ -211,7 +211,7 @@ func (b *bindingTest) RunTopContainer(containerName *string, podName *string) (s } ctr, err := containers.CreateWithSpec(b.conn, s, nil) if err != nil { - return "", nil + return "", err } err = containers.Start(b.conn, ctr.ID, nil) if err != nil { diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go index 9411d8a5f..bf627fdba 100644 --- a/pkg/bindings/test/containers_test.go +++ b/pkg/bindings/test/containers_test.go @@ -322,7 +322,8 @@ var _ = Describe("Podman containers ", func() { // a container that has no healthcheck should be a 409 var name = "top" - bt.RunTopContainer(&name, nil) + _, err = bt.RunTopContainer(&name, nil) + Expect(err).To(BeNil()) _, err = containers.RunHealthCheck(bt.conn, name, nil) Expect(err).ToNot(BeNil()) code, _ = bindings.CheckResponseCode(err) |