aboutsummaryrefslogtreecommitdiff
path: root/pkg/bindings
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/bindings')
-rw-r--r--pkg/bindings/connection.go151
-rw-r--r--pkg/bindings/containers/checkpoint.go2
-rw-r--r--pkg/bindings/containers/exec.go2
-rw-r--r--pkg/bindings/containers/update.go31
-rw-r--r--pkg/bindings/generate/types.go2
-rw-r--r--pkg/bindings/generate/types_systemd_options.go15
-rw-r--r--pkg/bindings/images/build.go22
-rw-r--r--pkg/bindings/images/images.go4
-rw-r--r--pkg/bindings/images/pull.go17
-rw-r--r--pkg/bindings/images/push.go16
-rw-r--r--pkg/bindings/images/types.go16
-rw-r--r--pkg/bindings/images/types_pull_options.go16
-rw-r--r--pkg/bindings/images/types_push_options.go16
-rw-r--r--pkg/bindings/images/types_remove_options.go15
-rw-r--r--pkg/bindings/internal/util/util.go3
-rw-r--r--pkg/bindings/kube/kube.go14
-rw-r--r--pkg/bindings/kube/types.go2
-rw-r--r--pkg/bindings/manifests/manifests.go55
-rw-r--r--pkg/bindings/manifests/types.go7
-rw-r--r--pkg/bindings/manifests/types_create_options.go15
-rw-r--r--pkg/bindings/system/system.go5
-rw-r--r--pkg/bindings/test/images_test.go26
-rw-r--r--pkg/bindings/test/manifests_test.go23
-rw-r--r--pkg/bindings/test/types_test.go66
24 files changed, 365 insertions, 176 deletions
diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go
index b994a5857..6d7b052b7 100644
--- a/pkg/bindings/connection.go
+++ b/pkg/bindings/connection.go
@@ -14,11 +14,9 @@ import (
"time"
"github.com/blang/semver/v4"
- "github.com/containers/podman/v4/pkg/terminal"
+ "github.com/containers/common/pkg/ssh"
"github.com/containers/podman/v4/version"
"github.com/sirupsen/logrus"
- "golang.org/x/crypto/ssh"
- "golang.org/x/crypto/ssh/agent"
)
type APIResponse struct {
@@ -74,8 +72,7 @@ func NewConnection(ctx context.Context, uri string) (context.Context, error) {
// or ssh://<user>@<host>[:port]/run/podman/podman.sock?secure=True
func NewConnectionWithIdentity(ctx context.Context, uri string, identity string) (context.Context, error) {
var (
- err error
- secure bool
+ err error
)
if v, found := os.LookupEnv("CONTAINER_HOST"); found && uri == "" {
uri = v
@@ -85,11 +82,6 @@ func NewConnectionWithIdentity(ctx context.Context, uri string, identity string)
identity = v
}
- passPhrase := ""
- if v, found := os.LookupEnv("CONTAINER_PASSPHRASE"); found {
- passPhrase = v
- }
-
_url, err := url.Parse(uri)
if err != nil {
return nil, fmt.Errorf("value of CONTAINER_HOST is not a valid url: %s: %w", uri, err)
@@ -99,11 +91,26 @@ func NewConnectionWithIdentity(ctx context.Context, uri string, identity string)
var connection Connection
switch _url.Scheme {
case "ssh":
- secure, err = strconv.ParseBool(_url.Query().Get("secure"))
+ port, err := strconv.Atoi(_url.Port())
if err != nil {
- secure = false
+ return nil, err
}
- connection, err = sshClient(_url, secure, passPhrase, identity)
+ conn, err := ssh.Dial(&ssh.ConnectionDialOptions{
+ Host: uri,
+ Identity: identity,
+ User: _url.User,
+ Port: port,
+ }, "golang")
+ if err != nil {
+ return nil, err
+ }
+ connection = Connection{URI: _url}
+ connection.Client = &http.Client{
+ Transport: &http.Transport{
+ DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
+ return ssh.DialNet(conn, "unix", _url)
+ },
+ }}
case "unix":
if !strings.HasPrefix(uri, "unix:///") {
// autofix unix://path_element vs unix:///path_element
@@ -184,124 +191,6 @@ func pingNewConnection(ctx context.Context) (*semver.Version, error) {
return nil, fmt.Errorf("ping response was %d", response.StatusCode)
}
-func sshClient(_url *url.URL, secure bool, passPhrase string, identity string) (Connection, error) {
- // if you modify the authmethods or their conditionals, you will also need to make similar
- // changes in the client (currently cmd/podman/system/connection/add getUDS).
-
- var signers []ssh.Signer // order Signers are appended to this list determines which key is presented to server
-
- if len(identity) > 0 {
- s, err := terminal.PublicKey(identity, []byte(passPhrase))
- if err != nil {
- return Connection{}, fmt.Errorf("failed to parse identity %q: %w", identity, err)
- }
-
- signers = append(signers, s)
- logrus.Debugf("SSH Ident Key %q %s %s", identity, ssh.FingerprintSHA256(s.PublicKey()), s.PublicKey().Type())
- }
-
- if sock, found := os.LookupEnv("SSH_AUTH_SOCK"); found {
- logrus.Debugf("Found SSH_AUTH_SOCK %q, ssh-agent signer(s) enabled", sock)
-
- c, err := net.Dial("unix", sock)
- if err != nil {
- return Connection{}, err
- }
-
- agentSigners, err := agent.NewClient(c).Signers()
- if err != nil {
- return Connection{}, err
- }
- signers = append(signers, agentSigners...)
-
- if logrus.IsLevelEnabled(logrus.DebugLevel) {
- for _, s := range agentSigners {
- logrus.Debugf("SSH Agent Key %s %s", ssh.FingerprintSHA256(s.PublicKey()), s.PublicKey().Type())
- }
- }
- }
-
- var authMethods []ssh.AuthMethod
- if len(signers) > 0 {
- var dedup = make(map[string]ssh.Signer)
- // Dedup signers based on fingerprint, ssh-agent keys override CONTAINER_SSHKEY
- for _, s := range signers {
- fp := ssh.FingerprintSHA256(s.PublicKey())
- if _, found := dedup[fp]; found {
- logrus.Debugf("Dedup SSH Key %s %s", ssh.FingerprintSHA256(s.PublicKey()), s.PublicKey().Type())
- }
- dedup[fp] = s
- }
-
- var uniq []ssh.Signer
- for _, s := range dedup {
- uniq = append(uniq, s)
- }
- authMethods = append(authMethods, ssh.PublicKeysCallback(func() ([]ssh.Signer, error) {
- return uniq, nil
- }))
- }
-
- if pw, found := _url.User.Password(); found {
- authMethods = append(authMethods, ssh.Password(pw))
- }
-
- if len(authMethods) == 0 {
- callback := func() (string, error) {
- pass, err := terminal.ReadPassword("Login password:")
- return string(pass), err
- }
- authMethods = append(authMethods, ssh.PasswordCallback(callback))
- }
-
- port := _url.Port()
- if port == "" {
- port = "22"
- }
-
- callback := ssh.InsecureIgnoreHostKey()
- if secure {
- host := _url.Hostname()
- if port != "22" {
- host = fmt.Sprintf("[%s]:%s", host, port)
- }
- key := terminal.HostKey(host)
- if key != nil {
- callback = ssh.FixedHostKey(key)
- }
- }
-
- bastion, err := ssh.Dial("tcp",
- net.JoinHostPort(_url.Hostname(), port),
- &ssh.ClientConfig{
- User: _url.User.Username(),
- Auth: authMethods,
- HostKeyCallback: callback,
- HostKeyAlgorithms: []string{
- ssh.KeyAlgoRSA,
- ssh.KeyAlgoDSA,
- ssh.KeyAlgoECDSA256,
- ssh.KeyAlgoECDSA384,
- ssh.KeyAlgoECDSA521,
- ssh.KeyAlgoED25519,
- },
- Timeout: 5 * time.Second,
- },
- )
- if err != nil {
- return Connection{}, fmt.Errorf("connection to bastion host (%s) failed: %w", _url.String(), err)
- }
-
- connection := Connection{URI: _url}
- connection.Client = &http.Client{
- Transport: &http.Transport{
- DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
- return bastion.Dial("unix", _url.Path)
- },
- }}
- return connection, nil
-}
-
func unixClient(_url *url.URL) Connection {
connection := Connection{URI: _url}
connection.Client = &http.Client{
diff --git a/pkg/bindings/containers/checkpoint.go b/pkg/bindings/containers/checkpoint.go
index bcb944488..8c072f588 100644
--- a/pkg/bindings/containers/checkpoint.go
+++ b/pkg/bindings/containers/checkpoint.go
@@ -39,7 +39,7 @@ func Checkpoint(ctx context.Context, nameOrID string, options *CheckpointOptions
}
defer response.Body.Close()
- if !export {
+ if response.StatusCode != http.StatusOK || !export {
return &report, response.Process(&report)
}
diff --git a/pkg/bindings/containers/exec.go b/pkg/bindings/containers/exec.go
index 3d19fb812..c5c8760ed 100644
--- a/pkg/bindings/containers/exec.go
+++ b/pkg/bindings/containers/exec.go
@@ -33,7 +33,7 @@ func ExecCreate(ctx context.Context, nameOrID string, config *handlers.ExecCreat
requestJSON, err := json.Marshal(config)
if err != nil {
- return "", fmt.Errorf("error marshalling exec config to JSON: %w", err)
+ return "", fmt.Errorf("marshalling exec config to JSON: %w", err)
}
jsonReader := strings.NewReader(string(requestJSON))
diff --git a/pkg/bindings/containers/update.go b/pkg/bindings/containers/update.go
new file mode 100644
index 000000000..7cda7c306
--- /dev/null
+++ b/pkg/bindings/containers/update.go
@@ -0,0 +1,31 @@
+package containers
+
+import (
+ "context"
+ "net/http"
+ "strings"
+
+ "github.com/containers/podman/v4/pkg/bindings"
+ "github.com/containers/podman/v4/pkg/domain/entities"
+ jsoniter "github.com/json-iterator/go"
+)
+
+func Update(ctx context.Context, options *entities.ContainerUpdateOptions) (string, error) {
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return "", err
+ }
+
+ resources, err := jsoniter.MarshalToString(options.Specgen.ResourceLimits)
+ if err != nil {
+ return "", err
+ }
+ stringReader := strings.NewReader(resources)
+ response, err := conn.DoRequest(ctx, stringReader, http.MethodPost, "/containers/%s/update", nil, nil, options.NameOrID)
+ if err != nil {
+ return "", err
+ }
+ defer response.Body.Close()
+
+ return options.NameOrID, response.Process(nil)
+}
diff --git a/pkg/bindings/generate/types.go b/pkg/bindings/generate/types.go
index 25c398c8b..31b43897c 100644
--- a/pkg/bindings/generate/types.go
+++ b/pkg/bindings/generate/types.go
@@ -38,4 +38,6 @@ type SystemdOptions struct {
After *[]string
// Requires - systemd requires list for the container or pods
Requires *[]string
+ // AdditionalEnvVariables - Sets environment variables to a systemd unit file
+ AdditionalEnvVariables *[]string
}
diff --git a/pkg/bindings/generate/types_systemd_options.go b/pkg/bindings/generate/types_systemd_options.go
index 4d436945b..3aec33a54 100644
--- a/pkg/bindings/generate/types_systemd_options.go
+++ b/pkg/bindings/generate/types_systemd_options.go
@@ -226,3 +226,18 @@ func (o *SystemdOptions) GetRequires() []string {
}
return *o.Requires
}
+
+// WithAdditionalEnvVariables set field AdditionalEnvVariables to given value
+func (o *SystemdOptions) WithAdditionalEnvVariables(value []string) *SystemdOptions {
+ o.AdditionalEnvVariables = &value
+ return o
+}
+
+// GetAdditionalEnvVariables returns value of field AdditionalEnvVariables
+func (o *SystemdOptions) GetAdditionalEnvVariables() []string {
+ if o.AdditionalEnvVariables == nil {
+ var z []string
+ return z
+ }
+ return *o.AdditionalEnvVariables
+}
diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go
index 6883585e2..ef875c9eb 100644
--- a/pkg/bindings/images/build.go
+++ b/pkg/bindings/images/build.go
@@ -88,6 +88,13 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
}
params.Set("additionalbuildcontexts", string(additionalBuildContextMap))
}
+ if options.IDMappingOptions != nil {
+ idmappingsOptions, err := jsoniter.Marshal(options.IDMappingOptions)
+ if err != nil {
+ return nil, err
+ }
+ params.Set("idmappingoptions", string(idmappingsOptions))
+ }
if buildArgs := options.Args; len(buildArgs) > 0 {
bArgs, err := jsoniter.MarshalToString(buildArgs)
if err != nil {
@@ -224,6 +231,15 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
if len(options.Manifest) > 0 {
params.Set("manifest", options.Manifest)
}
+ if options.CacheFrom != nil {
+ params.Set("cachefrom", options.CacheFrom.String())
+ }
+ if options.CacheTo != nil {
+ params.Set("cacheto", options.CacheTo.String())
+ }
+ if int64(options.CacheTTL) != 0 {
+ params.Set("cachettl", options.CacheTTL.String())
+ }
if memSwap := options.CommonBuildOpts.MemorySwap; memSwap > 0 {
params.Set("memswap", strconv.Itoa(int(memSwap)))
}
@@ -574,7 +590,7 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
func nTar(excludes []string, sources ...string) (io.ReadCloser, error) {
pm, err := fileutils.NewPatternMatcher(excludes)
if err != nil {
- return nil, fmt.Errorf("error processing excludes list %v: %w", excludes, err)
+ return nil, fmt.Errorf("processing excludes list %v: %w", excludes, err)
}
if len(sources) == 0 {
@@ -623,7 +639,7 @@ func nTar(excludes []string, sources ...string) (io.ReadCloser, error) {
excluded, err := pm.Matches(name) //nolint:staticcheck
if err != nil {
- return fmt.Errorf("error checking if %q is excluded: %w", name, err)
+ return fmt.Errorf("checking if %q is excluded: %w", name, err)
}
if excluded {
// Note: filepath.SkipDir is not possible to use given .dockerignore semantics.
@@ -726,7 +742,7 @@ func parseDockerignore(root string) ([]string, error) {
var dockerIgnoreErr error
ignore, dockerIgnoreErr = ioutil.ReadFile(filepath.Join(root, ".dockerignore"))
if dockerIgnoreErr != nil && !os.IsNotExist(dockerIgnoreErr) {
- return nil, fmt.Errorf("error reading .containerignore: '%s': %w", root, err)
+ return nil, err
}
}
rawexcludes := strings.Split(string(ignore), "\n")
diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go
index bb7867c4e..ea7d445db 100644
--- a/pkg/bindings/images/images.go
+++ b/pkg/bindings/images/images.go
@@ -282,9 +282,9 @@ func Search(ctx context.Context, term string, options *SearchOptions) ([]entitie
}
params.Set("term", term)
- // Note: we have to verify if skipped is false.
+ // SkipTLSVerify is special. It's not being serialized by ToParams()
+ // because we need to flip the boolean.
if options.SkipTLSVerify != nil {
- params.Del("SkipTLSVerify")
params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify()))
}
diff --git a/pkg/bindings/images/pull.go b/pkg/bindings/images/pull.go
index 1a4aa3038..8caf45c0e 100644
--- a/pkg/bindings/images/pull.go
+++ b/pkg/bindings/images/pull.go
@@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"io"
- "io/ioutil"
"net/http"
"os"
"strconv"
@@ -36,9 +35,9 @@ func Pull(ctx context.Context, rawImage string, options *PullOptions) ([]string,
}
params.Set("reference", rawImage)
+ // SkipTLSVerify is special. It's not being serialized by ToParams()
+ // because we need to flip the boolean.
if options.SkipTLSVerify != nil {
- params.Del("SkipTLSVerify")
- // Note: we have to verify if skipped is false.
params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify()))
}
@@ -57,10 +56,14 @@ func Pull(ctx context.Context, rawImage string, options *PullOptions) ([]string,
return nil, response.Process(err)
}
- // Historically pull writes status to stderr
- stderr := io.Writer(os.Stderr)
+ var writer io.Writer
if options.GetQuiet() {
- stderr = ioutil.Discard
+ writer = io.Discard
+ } else if progressWriter := options.GetProgressWriter(); progressWriter != nil {
+ writer = progressWriter
+ } else {
+ // Historically push writes status to stderr
+ writer = os.Stderr
}
dec := json.NewDecoder(response.Body)
@@ -84,7 +87,7 @@ func Pull(ctx context.Context, rawImage string, options *PullOptions) ([]string,
switch {
case report.Stream != "":
- fmt.Fprint(stderr, report.Stream)
+ fmt.Fprint(writer, report.Stream)
case report.Error != "":
pullErrors = append(pullErrors, errors.New(report.Error))
case len(report.Images) > 0:
diff --git a/pkg/bindings/images/push.go b/pkg/bindings/images/push.go
index 8db3726e6..0e1309e91 100644
--- a/pkg/bindings/images/push.go
+++ b/pkg/bindings/images/push.go
@@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"io"
- "io/ioutil"
"net/http"
"os"
"strconv"
@@ -39,10 +38,9 @@ func Push(ctx context.Context, source string, destination string, options *PushO
if err != nil {
return err
}
- // SkipTLSVerify is special. We need to delete the param added by
- // toparams and change the key and flip the bool
+ // SkipTLSVerify is special. It's not being serialized by ToParams()
+ // because we need to flip the boolean.
if options.SkipTLSVerify != nil {
- params.Del("SkipTLSVerify")
params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify()))
}
params.Set("destination", destination)
@@ -58,10 +56,14 @@ func Push(ctx context.Context, source string, destination string, options *PushO
return response.Process(err)
}
- // Historically push writes status to stderr
- writer := io.Writer(os.Stderr)
+ var writer io.Writer
if options.GetQuiet() {
- writer = ioutil.Discard
+ writer = io.Discard
+ } else if progressWriter := options.GetProgressWriter(); progressWriter != nil {
+ writer = progressWriter
+ } else {
+ // Historically push writes status to stderr
+ writer = os.Stderr
}
dec := json.NewDecoder(response.Body)
diff --git a/pkg/bindings/images/types.go b/pkg/bindings/images/types.go
index 0664afc1b..f8630926e 100644
--- a/pkg/bindings/images/types.go
+++ b/pkg/bindings/images/types.go
@@ -1,6 +1,8 @@
package images
import (
+ "io"
+
buildahDefine "github.com/containers/buildah/define"
)
@@ -15,6 +17,8 @@ type RemoveOptions struct {
Ignore *bool
// Confirms if given name is a manifest list and removes it, otherwise returns error.
LookupManifest *bool
+ // Does not remove dangling parent images
+ NoPrune *bool
}
//go:generate go run ../generator/generator.go DiffOptions
@@ -129,8 +133,12 @@ type PushOptions struct {
Format *string
// Password for authenticating against the registry.
Password *string
+ // ProgressWriter is a writer where push progress are sent.
+ // Since API handler for image push is quiet by default, WithQuiet(false) is necessary for
+ // the writer to receive progress messages.
+ ProgressWriter *io.Writer `schema:"-"`
// SkipTLSVerify to skip HTTPS and certificate verification.
- SkipTLSVerify *bool
+ SkipTLSVerify *bool `schema:"-"`
// RemoveSignatures Discard any pre-existing signatures in the image.
RemoveSignatures *bool
// Username for authenticating against the registry.
@@ -150,7 +158,7 @@ type SearchOptions struct {
// Limit the number of results.
Limit *int
// SkipTLSVerify to skip HTTPS and certificate verification.
- SkipTLSVerify *bool
+ SkipTLSVerify *bool `schema:"-"`
// ListTags search the available tags of the repository
ListTags *bool
}
@@ -174,11 +182,13 @@ type PullOptions struct {
Policy *string
// Password for authenticating against the registry.
Password *string
+ // ProgressWriter is a writer where pull progress are sent.
+ ProgressWriter *io.Writer `schema:"-"`
// Quiet can be specified to suppress pull progress when pulling. Ignored
// for remote calls.
Quiet *bool
// SkipTLSVerify to skip HTTPS and certificate verification.
- SkipTLSVerify *bool
+ SkipTLSVerify *bool `schema:"-"`
// Username for authenticating against the registry.
Username *string
// Variant will overwrite the local variant for image pulls.
diff --git a/pkg/bindings/images/types_pull_options.go b/pkg/bindings/images/types_pull_options.go
index 4cd525185..c1a88fd9e 100644
--- a/pkg/bindings/images/types_pull_options.go
+++ b/pkg/bindings/images/types_pull_options.go
@@ -2,6 +2,7 @@
package images
import (
+ "io"
"net/url"
"github.com/containers/podman/v4/pkg/bindings/internal/util"
@@ -107,6 +108,21 @@ func (o *PullOptions) GetPassword() string {
return *o.Password
}
+// WithProgressWriter set field ProgressWriter to given value
+func (o *PullOptions) WithProgressWriter(value io.Writer) *PullOptions {
+ o.ProgressWriter = &value
+ return o
+}
+
+// GetProgressWriter returns value of field ProgressWriter
+func (o *PullOptions) GetProgressWriter() io.Writer {
+ if o.ProgressWriter == nil {
+ var z io.Writer
+ return z
+ }
+ return *o.ProgressWriter
+}
+
// WithQuiet set field Quiet to given value
func (o *PullOptions) WithQuiet(value bool) *PullOptions {
o.Quiet = &value
diff --git a/pkg/bindings/images/types_push_options.go b/pkg/bindings/images/types_push_options.go
index 1ae031824..817d873f8 100644
--- a/pkg/bindings/images/types_push_options.go
+++ b/pkg/bindings/images/types_push_options.go
@@ -2,6 +2,7 @@
package images
import (
+ "io"
"net/url"
"github.com/containers/podman/v4/pkg/bindings/internal/util"
@@ -107,6 +108,21 @@ func (o *PushOptions) GetPassword() string {
return *o.Password
}
+// WithProgressWriter set field ProgressWriter to given value
+func (o *PushOptions) WithProgressWriter(value io.Writer) *PushOptions {
+ o.ProgressWriter = &value
+ return o
+}
+
+// GetProgressWriter returns value of field ProgressWriter
+func (o *PushOptions) GetProgressWriter() io.Writer {
+ if o.ProgressWriter == nil {
+ var z io.Writer
+ return z
+ }
+ return *o.ProgressWriter
+}
+
// WithSkipTLSVerify set field SkipTLSVerify to given value
func (o *PushOptions) WithSkipTLSVerify(value bool) *PushOptions {
o.SkipTLSVerify = &value
diff --git a/pkg/bindings/images/types_remove_options.go b/pkg/bindings/images/types_remove_options.go
index 559ebcfd5..8972ac93c 100644
--- a/pkg/bindings/images/types_remove_options.go
+++ b/pkg/bindings/images/types_remove_options.go
@@ -76,3 +76,18 @@ func (o *RemoveOptions) GetLookupManifest() bool {
}
return *o.LookupManifest
}
+
+// WithNoPrune set field NoPrune to given value
+func (o *RemoveOptions) WithNoPrune(value bool) *RemoveOptions {
+ o.NoPrune = &value
+ return o
+}
+
+// GetNoPrune returns value of field NoPrune
+func (o *RemoveOptions) GetNoPrune() bool {
+ if o.NoPrune == nil {
+ var z bool
+ return z
+ }
+ return *o.NoPrune
+}
diff --git a/pkg/bindings/internal/util/util.go b/pkg/bindings/internal/util/util.go
index f8f99d6c1..52ce14738 100644
--- a/pkg/bindings/internal/util/util.go
+++ b/pkg/bindings/internal/util/util.go
@@ -74,6 +74,9 @@ func ToParams(o interface{}) (url.Values, error) {
}
paramName := fieldName
if pn, ok := sType.Field(i).Tag.Lookup("schema"); ok {
+ if pn == "-" {
+ continue
+ }
paramName = pn
}
switch {
diff --git a/pkg/bindings/kube/kube.go b/pkg/bindings/kube/kube.go
index b9cc0efa7..1b9f888ef 100644
--- a/pkg/bindings/kube/kube.go
+++ b/pkg/bindings/kube/kube.go
@@ -10,6 +10,7 @@ import (
"github.com/containers/image/v5/types"
"github.com/containers/podman/v4/pkg/auth"
"github.com/containers/podman/v4/pkg/bindings"
+ "github.com/containers/podman/v4/pkg/bindings/generate"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/sirupsen/logrus"
)
@@ -39,8 +40,10 @@ func PlayWithBody(ctx context.Context, body io.Reader, options *PlayOptions) (*e
if err != nil {
return nil, err
}
+ // SkipTLSVerify is special. It's not being serialized by ToParams()
+ // because we need to flip the boolean.
if options.SkipTLSVerify != nil {
- params.Set("tlsVerify", strconv.FormatBool(options.GetSkipTLSVerify()))
+ params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify()))
}
if options.Start != nil {
params.Set("start", strconv.FormatBool(options.GetStart()))
@@ -51,7 +54,7 @@ func PlayWithBody(ctx context.Context, body io.Reader, options *PlayOptions) (*e
return nil, err
}
- response, err := conn.DoRequest(ctx, body, http.MethodPost, "/kube/play", params, header)
+ response, err := conn.DoRequest(ctx, body, http.MethodPost, "/play/kube", params, header)
if err != nil {
return nil, err
}
@@ -85,7 +88,7 @@ func DownWithBody(ctx context.Context, body io.Reader) (*entities.KubePlayReport
return nil, err
}
- response, err := conn.DoRequest(ctx, body, http.MethodDelete, "/kube/play", nil, nil)
+ response, err := conn.DoRequest(ctx, body, http.MethodDelete, "/play/kube", nil, nil)
if err != nil {
return nil, err
}
@@ -94,3 +97,8 @@ func DownWithBody(ctx context.Context, body io.Reader) (*entities.KubePlayReport
}
return &report, nil
}
+
+// Kube generate Kubernetes YAML (v1 specification)
+func Generate(ctx context.Context, nameOrIDs []string, options generate.KubeOptions) (*entities.GenerateKubeReport, error) {
+ return generate.Kube(ctx, nameOrIDs, &options)
+}
diff --git a/pkg/bindings/kube/types.go b/pkg/bindings/kube/types.go
index 783d1912a..279a9f8f3 100644
--- a/pkg/bindings/kube/types.go
+++ b/pkg/bindings/kube/types.go
@@ -27,7 +27,7 @@ type PlayOptions struct {
SignaturePolicy *string
// SkipTLSVerify - skip https and certificate validation when
// contacting container registries.
- SkipTLSVerify *bool
+ SkipTLSVerify *bool `schema:"-"`
// SeccompProfileRoot - path to a directory containing seccomp
// profiles.
SeccompProfileRoot *string
diff --git a/pkg/bindings/manifests/manifests.go b/pkg/bindings/manifests/manifests.go
index 80153c4b4..752366937 100644
--- a/pkg/bindings/manifests/manifests.go
+++ b/pkg/bindings/manifests/manifests.go
@@ -2,10 +2,13 @@ package manifests
import (
"context"
+ "encoding/json"
"errors"
"fmt"
+ "io"
"io/ioutil"
"net/http"
+ "os"
"strconv"
"strings"
@@ -142,7 +145,6 @@ func Delete(ctx context.Context, name string) (*entities.ManifestRemoveReport, e
// the name will be used instead. If the optional all boolean is specified, all images specified
// in the list will be pushed as well.
func Push(ctx context.Context, name, destination string, options *images.PushOptions) (string, error) {
- var idr entities.IDResponse
if options == nil {
options = new(images.PushOptions)
}
@@ -163,10 +165,9 @@ func Push(ctx context.Context, name, destination string, options *images.PushOpt
if err != nil {
return "", err
}
- // SkipTLSVerify is special. We need to delete the param added by
- // ToParams() and change the key and flip the bool
+ // SkipTLSVerify is special. It's not being serialized by ToParams()
+ // because we need to flip the boolean.
if options.SkipTLSVerify != nil {
- params.Del("SkipTLSVerify")
params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify()))
}
@@ -176,7 +177,46 @@ func Push(ctx context.Context, name, destination string, options *images.PushOpt
}
defer response.Body.Close()
- return idr.ID, response.Process(&idr)
+ if !response.IsSuccess() {
+ return "", response.Process(err)
+ }
+
+ var writer io.Writer
+ if options.GetQuiet() {
+ writer = io.Discard
+ } else if progressWriter := options.GetProgressWriter(); progressWriter != nil {
+ writer = progressWriter
+ } else {
+ // Historically push writes status to stderr
+ writer = os.Stderr
+ }
+
+ dec := json.NewDecoder(response.Body)
+ for {
+ var report entities.ManifestPushReport
+ if err := dec.Decode(&report); err != nil {
+ return "", err
+ }
+
+ select {
+ case <-response.Request.Context().Done():
+ break
+ default:
+ // non-blocking select
+ }
+
+ switch {
+ case report.ID != "":
+ return report.ID, nil
+ case report.Stream != "":
+ fmt.Fprint(writer, report.Stream)
+ case report.Error != "":
+ // There can only be one error.
+ return "", errors.New(report.Error)
+ default:
+ return "", fmt.Errorf("failed to parse push results stream, unexpected input: %v", report)
+ }
+ }
}
// Modify modifies the given manifest list using options and the optional list of images
@@ -205,10 +245,9 @@ func Modify(ctx context.Context, name string, images []string, options *ModifyOp
if err != nil {
return "", err
}
- // SkipTLSVerify is special. We need to delete the param added by
- // ToParams() and change the key and flip the bool
+ // SkipTLSVerify is special. It's not being serialized by ToParams()
+ // because we need to flip the boolean.
if options.SkipTLSVerify != nil {
- params.Del("SkipTLSVerify")
params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify()))
}
diff --git a/pkg/bindings/manifests/types.go b/pkg/bindings/manifests/types.go
index e23ef798d..fec3f9d13 100644
--- a/pkg/bindings/manifests/types.go
+++ b/pkg/bindings/manifests/types.go
@@ -8,7 +8,8 @@ type InspectOptions struct {
//go:generate go run ../generator/generator.go CreateOptions
// CreateOptions are optional options for creating manifests
type CreateOptions struct {
- All *bool
+ All *bool
+ Amend *bool
}
//go:generate go run ../generator/generator.go ExistsOptions
@@ -31,7 +32,7 @@ type AddOptions struct {
Authfile *string
Password *string
Username *string
- SkipTLSVerify *bool
+ SkipTLSVerify *bool `schema:"-"`
}
//go:generate go run ../generator/generator.go RemoveOptions
@@ -59,5 +60,5 @@ type ModifyOptions struct {
Authfile *string
Password *string
Username *string
- SkipTLSVerify *bool
+ SkipTLSVerify *bool `schema:"-"`
}
diff --git a/pkg/bindings/manifests/types_create_options.go b/pkg/bindings/manifests/types_create_options.go
index 960332a82..09942c00a 100644
--- a/pkg/bindings/manifests/types_create_options.go
+++ b/pkg/bindings/manifests/types_create_options.go
@@ -31,3 +31,18 @@ func (o *CreateOptions) GetAll() bool {
}
return *o.All
}
+
+// WithAmend set field Amend to given value
+func (o *CreateOptions) WithAmend(value bool) *CreateOptions {
+ o.Amend = &value
+ return o
+}
+
+// GetAmend returns value of field Amend
+func (o *CreateOptions) GetAmend() bool {
+ if o.Amend == nil {
+ var z bool
+ return z
+ }
+ return *o.Amend
+}
diff --git a/pkg/bindings/system/system.go b/pkg/bindings/system/system.go
index dae80384b..733b2cb5c 100644
--- a/pkg/bindings/system/system.go
+++ b/pkg/bindings/system/system.go
@@ -36,8 +36,9 @@ func Events(ctx context.Context, eventChan chan entities.Event, cancelChan chan
if cancelChan != nil {
go func() {
<-cancelChan
- err = response.Body.Close()
- logrus.Errorf("Unable to close event response body: %v", err)
+ if err := response.Body.Close(); err != nil {
+ logrus.Errorf("Unable to close event response body: %v", err)
+ }
}()
}
diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go
index 8f76ce456..53c5a1e83 100644
--- a/pkg/bindings/test/images_test.go
+++ b/pkg/bindings/test/images_test.go
@@ -1,11 +1,14 @@
package bindings_test
import (
+ "bytes"
+ "fmt"
"net/http"
"os"
"path/filepath"
"time"
+ podmanRegistry "github.com/containers/podman/v4/hack/podman-registry-go"
"github.com/containers/podman/v4/pkg/bindings"
"github.com/containers/podman/v4/pkg/bindings/containers"
"github.com/containers/podman/v4/pkg/bindings/images"
@@ -362,9 +365,14 @@ var _ = Describe("Podman images", func() {
It("Image Pull", func() {
rawImage := "docker.io/library/busybox:latest"
- pulledImages, err := images.Pull(bt.conn, rawImage, nil)
+ var writer bytes.Buffer
+ pullOpts := new(images.PullOptions).WithProgressWriter(&writer)
+ pulledImages, err := images.Pull(bt.conn, rawImage, pullOpts)
Expect(err).NotTo(HaveOccurred())
Expect(len(pulledImages)).To(Equal(1))
+ output := writer.String()
+ Expect(output).To(ContainSubstring("Trying to pull "))
+ Expect(output).To(ContainSubstring("Getting image source signatures"))
exists, err := images.Exists(bt.conn, rawImage, nil)
Expect(err).NotTo(HaveOccurred())
@@ -379,6 +387,22 @@ var _ = Describe("Podman images", func() {
Expect(err).To(HaveOccurred())
})
+ It("Image Push", func() {
+ registry, err := podmanRegistry.Start()
+ Expect(err).To(BeNil())
+
+ var writer bytes.Buffer
+ pushOpts := new(images.PushOptions).WithUsername(registry.User).WithPassword(registry.Password).WithSkipTLSVerify(true).WithProgressWriter(&writer).WithQuiet(false)
+ err = images.Push(bt.conn, alpine.name, fmt.Sprintf("localhost:%s/test:latest", registry.Port), pushOpts)
+ Expect(err).ToNot(HaveOccurred())
+
+ output := writer.String()
+ Expect(output).To(ContainSubstring("Copying blob "))
+ Expect(output).To(ContainSubstring("Copying config "))
+ Expect(output).To(ContainSubstring("Writing manifest to image destination"))
+ Expect(output).To(ContainSubstring("Storing signatures"))
+ })
+
It("Build no options", func() {
results, err := images.Build(bt.conn, []string{"fixture/Containerfile"}, entities.BuildOptions{})
Expect(err).ToNot(HaveOccurred())
diff --git a/pkg/bindings/test/manifests_test.go b/pkg/bindings/test/manifests_test.go
index 6a34ef5a6..d6749f920 100644
--- a/pkg/bindings/test/manifests_test.go
+++ b/pkg/bindings/test/manifests_test.go
@@ -1,9 +1,12 @@
package bindings_test
import (
+ "bytes"
+ "fmt"
"net/http"
"time"
+ podmanRegistry "github.com/containers/podman/v4/hack/podman-registry-go"
"github.com/containers/podman/v4/pkg/bindings"
"github.com/containers/podman/v4/pkg/bindings/images"
"github.com/containers/podman/v4/pkg/bindings/manifests"
@@ -12,7 +15,7 @@ import (
"github.com/onsi/gomega/gexec"
)
-var _ = Describe("podman manifest", func() {
+var _ = Describe("Podman manifests", func() {
var (
bt *bindingTest
s *gexec.Session
@@ -172,7 +175,21 @@ var _ = Describe("podman manifest", func() {
Expect(list.Manifests[0].Platform.OS).To(Equal("foo"))
})
- It("push manifest", func() {
- Skip("TODO: implement test for manifest push to registry")
+ It("Manifest Push", func() {
+ registry, err := podmanRegistry.Start()
+ Expect(err).To(BeNil())
+
+ name := "quay.io/libpod/foobar:latest"
+ _, err = manifests.Create(bt.conn, name, []string{alpine.name}, nil)
+ Expect(err).ToNot(HaveOccurred())
+
+ var writer bytes.Buffer
+ pushOpts := new(images.PushOptions).WithUsername(registry.User).WithPassword(registry.Password).WithAll(true).WithSkipTLSVerify(true).WithProgressWriter(&writer).WithQuiet(false)
+ _, err = manifests.Push(bt.conn, name, fmt.Sprintf("localhost:%s/test:latest", registry.Port), pushOpts)
+ Expect(err).ToNot(HaveOccurred())
+
+ output := writer.String()
+ Expect(output).To(ContainSubstring("Writing manifest list to image destination"))
+ Expect(output).To(ContainSubstring("Storing list signatures"))
})
})
diff --git a/pkg/bindings/test/types_test.go b/pkg/bindings/test/types_test.go
new file mode 100644
index 000000000..bc98c8b7d
--- /dev/null
+++ b/pkg/bindings/test/types_test.go
@@ -0,0 +1,66 @@
+package bindings_test
+
+import (
+ "bytes"
+
+ "github.com/containers/podman/v4/pkg/bindings/images"
+ "github.com/containers/podman/v4/pkg/bindings/kube"
+ "github.com/containers/podman/v4/pkg/bindings/manifests"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Binding types", func() {
+ It("serialize image pull options", func() {
+ var writer bytes.Buffer
+ opts := new(images.PullOptions).WithOS("foo").WithProgressWriter(&writer).WithSkipTLSVerify(true)
+ params, err := opts.ToParams()
+ Expect(err).ToNot(HaveOccurred())
+ Expect(params.Get("os")).To(Equal("foo"))
+ Expect(params.Has("progresswriter")).To(BeFalse())
+ Expect(params.Has("skiptlsverify")).To(BeFalse())
+ })
+
+ It("serialize image push options", func() {
+ var writer bytes.Buffer
+ opts := new(images.PushOptions).WithAll(true).WithProgressWriter(&writer).WithSkipTLSVerify(true)
+ params, err := opts.ToParams()
+ Expect(err).ToNot(HaveOccurred())
+ Expect(params.Get("all")).To(Equal("true"))
+ Expect(params.Has("progresswriter")).To(BeFalse())
+ Expect(params.Has("skiptlsverify")).To(BeFalse())
+ })
+
+ It("serialize image search options", func() {
+ opts := new(images.SearchOptions).WithLimit(123).WithSkipTLSVerify(true)
+ params, err := opts.ToParams()
+ Expect(err).ToNot(HaveOccurred())
+ Expect(params.Get("limit")).To(Equal("123"))
+ Expect(params.Has("skiptlsverify")).To(BeFalse())
+ })
+
+ It("serialize manifest modify options", func() {
+ opts := new(manifests.ModifyOptions).WithOS("foo").WithSkipTLSVerify(true)
+ params, err := opts.ToParams()
+ Expect(err).ToNot(HaveOccurred())
+ Expect(params.Get("os")).To(Equal("foo"))
+ Expect(params.Has("skiptlsverify")).To(BeFalse())
+ })
+
+ It("serialize manifest add options", func() {
+ opts := new(manifests.AddOptions).WithAll(true).WithOS("foo").WithSkipTLSVerify(true)
+ params, err := opts.ToParams()
+ Expect(err).ToNot(HaveOccurred())
+ Expect(params.Get("all")).To(Equal("true"))
+ Expect(params.Get("os")).To(Equal("foo"))
+ Expect(params.Has("skiptlsverify")).To(BeFalse())
+ })
+
+ It("serialize kube play options", func() {
+ opts := new(kube.PlayOptions).WithQuiet(true).WithSkipTLSVerify(true)
+ params, err := opts.ToParams()
+ Expect(err).ToNot(HaveOccurred())
+ Expect(params.Get("quiet")).To(Equal("true"))
+ Expect(params.Has("skiptlsverify")).To(BeFalse())
+ })
+})