summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.autocopr/README5
-rw-r--r--README.md10
-rw-r--r--RELEASE_NOTES.md2
-rw-r--r--build_osx.md2
-rw-r--r--cmd/podman/containers/prune.go4
-rw-r--r--cmd/podman/images/prune.go4
-rw-r--r--cmd/podman/manifest/add.go12
-rw-r--r--cmd/podman/manifest/annotate.go16
-rw-r--r--cmd/podman/manifest/create.go3
-rw-r--r--cmd/podman/manifest/inspect.go5
-rw-r--r--cmd/podman/manifest/remove.go17
-rw-r--r--cmd/podman/manifest/rm.go4
-rw-r--r--cmd/podman/networks/list.go14
-rw-r--r--cmd/podman/networks/prune.go4
-rw-r--r--cni/README.md2
-rw-r--r--contrib/podmanimage/stable/Dockerfile4
-rw-r--r--contrib/podmanimage/testing/Dockerfile4
-rw-r--r--contrib/podmanimage/upstream/Dockerfile6
-rw-r--r--contrib/snapcraft/snap/snapcraft.yaml2
-rw-r--r--contrib/systemd/README.md8
-rw-r--r--docs/source/Tutorials.rst14
-rw-r--r--docs/source/markdown/podman-build.1.md4
-rw-r--r--docs/source/markdown/podman-create.1.md6
-rw-r--r--docs/source/markdown/podman-run.1.md8
-rw-r--r--docs/source/markdown/podman.1.md2
-rw-r--r--docs/tutorials/mac_client.md4
-rw-r--r--docs/tutorials/podman_tutorial.md4
-rw-r--r--docs/tutorials/podman_tutorial_cn.md4
-rw-r--r--docs/tutorials/remote_client.md4
-rw-r--r--pkg/api/handlers/compat/images.go54
-rw-r--r--pkg/api/handlers/libpod/images.go43
-rw-r--r--pkg/api/handlers/libpod/manifests.go265
-rw-r--r--pkg/api/handlers/types.go40
-rw-r--r--pkg/api/handlers/utils/handler.go4
-rw-r--r--pkg/api/handlers/utils/images.go40
-rw-r--r--pkg/api/server/register_images.go2
-rw-r--r--pkg/api/server/register_manifest.go177
-rw-r--r--pkg/api/server/register_swagger.go4
-rw-r--r--pkg/api/server/server.go2
-rw-r--r--pkg/auth/auth.go10
-rw-r--r--pkg/auth/auth_test.go12
-rw-r--r--pkg/bindings/connection.go85
-rw-r--r--pkg/bindings/containers/attach.go8
-rw-r--r--pkg/bindings/errors.go2
-rw-r--r--pkg/bindings/images/build.go6
-rw-r--r--pkg/bindings/internal/util/util.go8
-rw-r--r--pkg/bindings/manifests/manifests.go144
-rw-r--r--pkg/bindings/manifests/types.go22
-rw-r--r--pkg/bindings/manifests/types_modify_options.go168
-rw-r--r--pkg/bindings/test/manifests_test.go124
-rw-r--r--pkg/domain/entities/engine_image.go8
-rw-r--r--pkg/domain/entities/images.go2
-rw-r--r--pkg/domain/entities/manifest.go86
-rw-r--r--pkg/domain/infra/abi/images_list.go11
-rw-r--r--pkg/domain/infra/abi/manifest.go102
-rw-r--r--pkg/domain/infra/runtime_abi.go2
-rw-r--r--pkg/domain/infra/tunnel/manifest.go30
-rw-r--r--pkg/machine/ignition.go85
-rw-r--r--pkg/specgenutil/util.go15
-rw-r--r--podman.spec.rpkg (renamed from .autocopr/podman.spec)167
-rw-r--r--rootless.md6
-rw-r--r--test/apiv2/15-manifest.at19
-rwxr-xr-xtest/apiv2/test-apiv26
-rw-r--r--test/e2e/libpod_suite_remote_test.go4
-rw-r--r--test/e2e/manifest_test.go103
-rw-r--r--test/system/030-run.bats7
-rw-r--r--test/system/070-build.bats2
67 files changed, 1306 insertions, 747 deletions
diff --git a/.autocopr/README b/.autocopr/README
deleted file mode 100644
index 791fe8262..000000000
--- a/.autocopr/README
+++ /dev/null
@@ -1,5 +0,0 @@
-Enable COPR:
-sudo dnf copr enable rhcontainerbot/podman-next
-
-Install podman:
-sudo dnf install podman -y
diff --git a/README.md b/README.md
index c2a7130f1..20a5401cc 100644
--- a/README.md
+++ b/README.md
@@ -60,10 +60,10 @@ Rootless Podman runs locked-down containers with no privileges that the user run
Some of these restrictions can be lifted (via `--privileged`, for example), but rootless containers will never have more privileges than the user that launched them.
If you run Podman as your user and mount in `/etc/passwd` from the host, you still won't be able to change it, since your user doesn't have permission to do so.
-Almost all normal Podman functionality is available, though there are some [shortcomings](https://github.com/containers/podman/blob/master/rootless.md).
-Any recent Podman release should be able to run rootless without any additional configuration, though your operating system may require some additional configuration detailed in the [install guide](https://github.com/containers/podman/blob/master/install.md).
+Almost all normal Podman functionality is available, though there are some [shortcomings](https://github.com/containers/podman/blob/main/rootless.md).
+Any recent Podman release should be able to run rootless without any additional configuration, though your operating system may require some additional configuration detailed in the [install guide](https://github.com/containers/podman/blob/main/install.md).
-A little configuration by an administrator is required before rootless Podman can be used, the necessary setup is documented [here](https://github.com/containers/podman/blob/master/docs/tutorials/rootless_tutorial.md).
+A little configuration by an administrator is required before rootless Podman can be used, the necessary setup is documented [here](https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md).
## Out of scope
@@ -110,10 +110,10 @@ includes tables showing Docker commands and their Podman equivalent commands.
**[Tutorials](docs/tutorials)**
Tutorials on using Podman.
-**[Remote Client](https://github.com/containers/podman/blob/master/docs/tutorials/remote_client.md)**
+**[Remote Client](https://github.com/containers/podman/blob/main/docs/tutorials/remote_client.md)**
A brief how-to on using the Podman remote-client.
-**[Basic Setup and Use of Podman in a Rootless environment](https://github.com/containers/podman/blob/master/docs/tutorials/rootless_tutorial.md)**
+**[Basic Setup and Use of Podman in a Rootless environment](https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md)**
A tutorial showing the setup and configuration necessary to run Rootless Podman.
**[Release Notes](RELEASE_NOTES.md)**
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 60d433953..04bd196be 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -2026,7 +2026,7 @@
- Updated Buildah to v1.7, picking up a number of bugfixes
- Updated containers/image library to v1.5, picking up a number of bugfixes and performance improvements to pushing images
- Updated containers/storage library to v1.10, picking up a number of bugfixes
-- Work on the remote Podman client for interacting with Podman remotely over Varlink is progressing steadily, and many image and pod commands are supported - please see the [Readme](https://github.com/containers/podman/blob/master/remote_client.md) for details
+- Work on the remote Podman client for interacting with Podman remotely over Varlink is progressing steadily, and many image and pod commands are supported - please see the [Readme](https://github.com/containers/podman/blob/main/remote_client.md) for details
- Added path masking to mounts with the `:z` and `:Z` options, preventing users from accidentally performing an SELinux relabel of their entire home directory
- The `podman container runlabel` command will not pull an image if it does not contain the requested label
- Many commands' usage information now includes examples
diff --git a/build_osx.md b/build_osx.md
index d5a292542..631fcf9c3 100644
--- a/build_osx.md
+++ b/build_osx.md
@@ -52,4 +52,4 @@ $ man podman
## Using the client
To learn how to use the Podman client, refer its
-[tutorial](https://github.com/containers/podman/blob/master/docs/tutorials/remote_client.md).
+[tutorial](https://github.com/containers/podman/blob/main/docs/tutorials/remote_client.md).
diff --git a/cmd/podman/containers/prune.go b/cmd/podman/containers/prune.go
index e13b9e7f6..f58e37fd1 100644
--- a/cmd/podman/containers/prune.go
+++ b/cmd/podman/containers/prune.go
@@ -9,11 +9,11 @@ import (
"github.com/containers/common/pkg/completion"
"github.com/containers/podman/v3/cmd/podman/common"
+ "github.com/containers/podman/v3/cmd/podman/parse"
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/cmd/podman/utils"
"github.com/containers/podman/v3/cmd/podman/validate"
"github.com/containers/podman/v3/pkg/domain/entities"
- "github.com/containers/podman/v3/pkg/specgenutil"
"github.com/spf13/cobra"
)
@@ -64,7 +64,7 @@ func prune(cmd *cobra.Command, args []string) error {
}
}
- pruneOptions.Filters, err = specgenutil.ParseFilters(filter)
+ pruneOptions.Filters, err = parse.FilterArgumentsIntoFilters(filter)
if err != nil {
return err
}
diff --git a/cmd/podman/images/prune.go b/cmd/podman/images/prune.go
index fc7451c41..e4c320a55 100644
--- a/cmd/podman/images/prune.go
+++ b/cmd/podman/images/prune.go
@@ -8,11 +8,11 @@ import (
"github.com/containers/common/pkg/completion"
"github.com/containers/podman/v3/cmd/podman/common"
+ "github.com/containers/podman/v3/cmd/podman/parse"
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/cmd/podman/utils"
"github.com/containers/podman/v3/cmd/podman/validate"
"github.com/containers/podman/v3/pkg/domain/entities"
- "github.com/containers/podman/v3/pkg/specgenutil"
"github.com/spf13/cobra"
)
@@ -66,7 +66,7 @@ func prune(cmd *cobra.Command, args []string) error {
return nil
}
}
- filterMap, err := specgenutil.ParseFilters(filter)
+ filterMap, err := parse.FilterArgumentsIntoFilters(filter)
if err != nil {
return err
}
diff --git a/cmd/podman/manifest/add.go b/cmd/podman/manifest/add.go
index 9d219601c..d09533967 100644
--- a/cmd/podman/manifest/add.go
+++ b/cmd/podman/manifest/add.go
@@ -26,14 +26,14 @@ type manifestAddOptsWrapper struct {
var (
manifestAddOpts = manifestAddOptsWrapper{}
addCmd = &cobra.Command{
- Use: "add [options] LIST LIST",
+ Use: "add [options] LIST IMAGE [IMAGE...]",
Short: "Add images to a manifest list or image index",
Long: "Adds an image to a manifest list or image index.",
RunE: add,
+ Args: cobra.MinimumNArgs(2),
ValidArgsFunction: common.AutocompleteImages,
Example: `podman manifest add mylist:v1.11 image:v1.11-amd64
podman manifest add mylist:v1.11 transport:imageName`,
- Args: cobra.ExactArgs(2),
}
)
@@ -93,10 +93,6 @@ func add(cmd *cobra.Command, args []string) error {
return err
}
- // FIXME: (@vrothberg) this interface confuses me a lot. Why are they
- // not two arguments?
- manifestAddOpts.Images = []string{args[1], args[0]}
-
if manifestAddOpts.CredentialsCLI != "" {
creds, err := util.ParseRegistryCreds(manifestAddOpts.CredentialsCLI)
if err != nil {
@@ -114,10 +110,10 @@ func add(cmd *cobra.Command, args []string) error {
manifestAddOpts.SkipTLSVerify = types.NewOptionalBool(!manifestAddOpts.TLSVerifyCLI)
}
- listID, err := registry.ImageEngine().ManifestAdd(context.Background(), manifestAddOpts.ManifestAddOptions)
+ listID, err := registry.ImageEngine().ManifestAdd(context.Background(), args[0], args[1:], manifestAddOpts.ManifestAddOptions)
if err != nil {
return err
}
- fmt.Printf("%s\n", listID)
+ fmt.Println(listID)
return nil
}
diff --git a/cmd/podman/manifest/annotate.go b/cmd/podman/manifest/annotate.go
index d806ce9e6..a032a1fe5 100644
--- a/cmd/podman/manifest/annotate.go
+++ b/cmd/podman/manifest/annotate.go
@@ -1,14 +1,12 @@
package manifest
import (
- "context"
"fmt"
"github.com/containers/common/pkg/completion"
"github.com/containers/podman/v3/cmd/podman/common"
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -20,8 +18,8 @@ var (
Short: "Add or update information about an entry in a manifest list or image index",
Long: "Adds or updates information about an entry in a manifest list or image index.",
RunE: annotate,
- Example: `podman manifest annotate --annotation left=right mylist:v1.11 image:v1.11-amd64`,
Args: cobra.ExactArgs(2),
+ Example: `podman manifest annotate --annotation left=right mylist:v1.11 image:v1.11-amd64`,
ValidArgsFunction: common.AutocompleteImages,
}
)
@@ -63,18 +61,10 @@ func init() {
}
func annotate(cmd *cobra.Command, args []string) error {
- listImageSpec := args[0]
- instanceSpec := args[1]
- if listImageSpec == "" {
- return errors.Errorf(`invalid image name "%s"`, listImageSpec)
- }
- if instanceSpec == "" {
- return errors.Errorf(`invalid image digest "%s"`, instanceSpec)
- }
- updatedListID, err := registry.ImageEngine().ManifestAnnotate(context.Background(), args, manifestAnnotateOpts)
+ id, err := registry.ImageEngine().ManifestAnnotate(registry.Context(), args[0], args[1], manifestAnnotateOpts)
if err != nil {
return err
}
- fmt.Printf("%s\n", updatedListID)
+ fmt.Println(id)
return nil
}
diff --git a/cmd/podman/manifest/create.go b/cmd/podman/manifest/create.go
index 95c9f89b0..acef53045 100644
--- a/cmd/podman/manifest/create.go
+++ b/cmd/podman/manifest/create.go
@@ -1,7 +1,6 @@
package manifest
import (
- "context"
"fmt"
"github.com/containers/podman/v3/cmd/podman/common"
@@ -36,7 +35,7 @@ func init() {
}
func create(cmd *cobra.Command, args []string) error {
- imageID, err := registry.ImageEngine().ManifestCreate(context.Background(), args[:1], args[1:], manifestCreateOpts)
+ imageID, err := registry.ImageEngine().ManifestCreate(registry.Context(), args[0], args[1:], manifestCreateOpts)
if err != nil {
return err
}
diff --git a/cmd/podman/manifest/inspect.go b/cmd/podman/manifest/inspect.go
index d444f9066..ac8ee3dc4 100644
--- a/cmd/podman/manifest/inspect.go
+++ b/cmd/podman/manifest/inspect.go
@@ -1,7 +1,6 @@
package manifest
import (
- "context"
"fmt"
"github.com/containers/podman/v3/cmd/podman/common"
@@ -29,10 +28,10 @@ func init() {
}
func inspect(cmd *cobra.Command, args []string) error {
- buf, err := registry.ImageEngine().ManifestInspect(context.Background(), args[0])
+ buf, err := registry.ImageEngine().ManifestInspect(registry.Context(), args[0])
if err != nil {
return err
}
- fmt.Printf("%s\n", buf)
+ fmt.Println(string(buf))
return nil
}
diff --git a/cmd/podman/manifest/remove.go b/cmd/podman/manifest/remove.go
index c44c0991e..4716af201 100644
--- a/cmd/podman/manifest/remove.go
+++ b/cmd/podman/manifest/remove.go
@@ -1,7 +1,6 @@
package manifest
import (
- "context"
"fmt"
"github.com/containers/podman/v3/cmd/podman/common"
@@ -16,9 +15,9 @@ var (
Short: "Remove an entry from a manifest list or image index",
Long: "Removes an image from a manifest list or image index.",
RunE: remove,
+ Args: cobra.ExactArgs(2),
ValidArgsFunction: common.AutocompleteImages,
Example: `podman manifest remove mylist:v1.11 sha256:15352d97781ffdf357bf3459c037be3efac4133dc9070c2dce7eca7c05c3e736`,
- Args: cobra.ExactArgs(2),
}
)
@@ -30,18 +29,10 @@ func init() {
}
func remove(cmd *cobra.Command, args []string) error {
- listImageSpec := args[0]
- instanceSpec := args[1]
- if listImageSpec == "" {
- return errors.Errorf(`invalid image name "%s"`, listImageSpec)
- }
- if instanceSpec == "" {
- return errors.Errorf(`invalid image digest "%s"`, instanceSpec)
- }
- updatedListID, err := registry.ImageEngine().ManifestRemove(context.Background(), args)
+ updatedListID, err := registry.ImageEngine().ManifestRemoveDigest(registry.Context(), args[0], args[1])
if err != nil {
- return errors.Wrapf(err, "error removing from manifest list %s", listImageSpec)
+ return errors.Wrapf(err, "error removing from manifest list %s", args[0])
}
- fmt.Printf("%s\n", updatedListID)
+ fmt.Println(updatedListID)
return nil
}
diff --git a/cmd/podman/manifest/rm.go b/cmd/podman/manifest/rm.go
index 5e78197ed..b4a52653b 100644
--- a/cmd/podman/manifest/rm.go
+++ b/cmd/podman/manifest/rm.go
@@ -12,13 +12,13 @@ import (
var (
rmCmd = &cobra.Command{
- Use: "rm LIST",
+ Use: "rm LIST [LIST...]",
Short: "Remove manifest list or image index from local storage",
Long: "Remove manifest list or image index from local storage.",
RunE: rm,
+ Args: cobra.MinimumNArgs(1),
ValidArgsFunction: common.AutocompleteImages,
Example: `podman manifest rm mylist:v1.11`,
- Args: cobra.ExactArgs(1),
}
)
diff --git a/cmd/podman/networks/list.go b/cmd/podman/networks/list.go
index b84c9f5d3..be61d72d8 100644
--- a/cmd/podman/networks/list.go
+++ b/cmd/podman/networks/list.go
@@ -10,10 +10,10 @@ import (
"github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/report"
"github.com/containers/podman/v3/cmd/podman/common"
+ "github.com/containers/podman/v3/cmd/podman/parse"
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/cmd/podman/validate"
"github.com/containers/podman/v3/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
@@ -61,14 +61,12 @@ func init() {
}
func networkList(cmd *cobra.Command, args []string) error {
- networkListOptions.Filters = make(map[string][]string)
- for _, f := range filters {
- split := strings.SplitN(f, "=", 2)
- if len(split) == 1 {
- return errors.Errorf("invalid filter %q", f)
- }
- networkListOptions.Filters[split[0]] = append(networkListOptions.Filters[split[0]], split[1])
+ var err error
+ networkListOptions.Filters, err = parse.FilterArgumentsIntoFilters(filters)
+ if err != nil {
+ return err
}
+
responses, err := registry.ContainerEngine().NetworkList(registry.Context(), networkListOptions)
if err != nil {
return err
diff --git a/cmd/podman/networks/prune.go b/cmd/podman/networks/prune.go
index 311d098cd..e113cd359 100644
--- a/cmd/podman/networks/prune.go
+++ b/cmd/podman/networks/prune.go
@@ -7,11 +7,11 @@ import (
"strings"
"github.com/containers/podman/v3/cmd/podman/common"
+ "github.com/containers/podman/v3/cmd/podman/parse"
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/cmd/podman/utils"
"github.com/containers/podman/v3/cmd/podman/validate"
"github.com/containers/podman/v3/pkg/domain/entities"
- "github.com/containers/podman/v3/pkg/specgenutil"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
@@ -68,7 +68,7 @@ func networkPrune(cmd *cobra.Command, _ []string) error {
return nil
}
}
- networkPruneOptions.Filters, err = specgenutil.ParseFilters(filter)
+ networkPruneOptions.Filters, err = parse.FilterArgumentsIntoFilters(filter)
if err != nil {
return err
}
diff --git a/cni/README.md b/cni/README.md
index 12c890ce8..8bcd4327a 100644
--- a/cni/README.md
+++ b/cni/README.md
@@ -11,7 +11,7 @@ For example a basic network configuration can be achieved with:
```bash
sudo mkdir -p /etc/cni/net.d
-curl -qsSL https://raw.githubusercontent.com/containers/libpod/master/cni/87-podman-bridge.conflist | sudo tee /etc/cni/net.d/87-podman-bridge.conflist
+curl -qsSL https://raw.githubusercontent.com/containers/podman/main/cni/87-podman-bridge.conflist | sudo tee /etc/cni/net.d/87-podman-bridge.conflist
```
Dependent upon your CNI configuration, you will need to install as a minimum the `port` and `bridge` [CNI plugins](https://github.com/containernetworking/plugins) into `/opt/cni/bin` (or the directory specified by `cni_plugin_dir` in containers.conf). Please refer to the [CNI](https://github.com/containernetworking) project page in GitHub for more information.
diff --git a/contrib/podmanimage/stable/Dockerfile b/contrib/podmanimage/stable/Dockerfile
index b0b5bb33b..a81c6fd58 100644
--- a/contrib/podmanimage/stable/Dockerfile
+++ b/contrib/podmanimage/stable/Dockerfile
@@ -19,8 +19,8 @@ RUN useradd podman; \
echo podman:10000:5000 > /etc/subuid; \
echo podman:10000:5000 > /etc/subgid;
-ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/containers.conf /etc/containers/containers.conf
-ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/podman-containers.conf /home/podman/.config/containers/containers.conf
+ADD https://raw.githubusercontent.com/containers/podman/main/contrib/podmanimage/stable/containers.conf /etc/containers/containers.conf
+ADD https://raw.githubusercontent.com/containers/podman/main/contrib/podmanimage/stable/podman-containers.conf /home/podman/.config/containers/containers.conf
RUN mkdir -p /home/podman/.local/share/containers; chown podman:podman -R /home/podman
diff --git a/contrib/podmanimage/testing/Dockerfile b/contrib/podmanimage/testing/Dockerfile
index e7228ea42..0b3e077a6 100644
--- a/contrib/podmanimage/testing/Dockerfile
+++ b/contrib/podmanimage/testing/Dockerfile
@@ -19,8 +19,8 @@ RUN useradd podman; \
echo podman:10000:5000 > /etc/subuid; \
echo podman:10000:5000 > /etc/subgid;
-ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/containers.conf /etc/containers/containers.conf
-ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/podman-containers.conf /home/podman/.config/containers/containers.conf
+ADD https://raw.githubusercontent.com/containers/podman/main/contrib/podmanimage/stable/containers.conf /etc/containers/containers.conf
+ADD https://raw.githubusercontent.com/containers/podman/main/contrib/podmanimage/stable/podman-containers.conf /home/podman/.config/containers/containers.conf
RUN mkdir -p /home/podman/.local/share/containers; chown podman:podman -R /home/podman
diff --git a/contrib/podmanimage/upstream/Dockerfile b/contrib/podmanimage/upstream/Dockerfile
index 864227f89..4b44c74c2 100644
--- a/contrib/podmanimage/upstream/Dockerfile
+++ b/contrib/podmanimage/upstream/Dockerfile
@@ -58,7 +58,7 @@ RUN yum -y update; rpm --restore shadow-utils 2>/dev/null; yum -y install --exc
mkdir -p /usr/libexec/cni; \
\cp -fR bin/* /usr/libexec/cni; \
mkdir -p /etc/cni/net.d; \
- curl -qsSL https://raw.githubusercontent.com/containers/libpod/master/cni/87-podman-bridge.conflist | tee /etc/cni/net.d/99-loopback.conf; \
+ curl -qsSL https://raw.githubusercontent.com/containers/podman/main/cni/87-podman-bridge.conflist | tee /etc/cni/net.d/99-loopback.conf; \
mkdir -p /usr/share/containers; \
rm -rf /root/podman/*; \
yum -y remove git golang go-md2man make; \
@@ -68,8 +68,8 @@ RUN useradd podman; \
echo podman:10000:5000 > /etc/subuid; \
echo podman:10000:5000 > /etc/subgid;
-ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/containers.conf /etc/containers/containers.conf
-ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/podman-containers.conf /home/podman/.config/containers/containers.conf
+ADD https://raw.githubusercontent.com/containers/podman/main/contrib/podmanimage/stable/containers.conf /etc/containers/containers.conf
+ADD https://raw.githubusercontent.com/containers/podman/main/contrib/podmanimage/stable/podman-containers.conf /home/podman/.config/containers/containers.conf
RUN mkdir -p /home/podman/.local/share/containers; chown podman:podman -R /home/podman
diff --git a/contrib/snapcraft/snap/snapcraft.yaml b/contrib/snapcraft/snap/snapcraft.yaml
index e3948e01b..57589b00e 100644
--- a/contrib/snapcraft/snap/snapcraft.yaml
+++ b/contrib/snapcraft/snap/snapcraft.yaml
@@ -17,7 +17,7 @@ parts:
source: https://github.com/containers/podman/archive/v0.11.1.1.tar.gz
go-importpath: github.com/containers/podman
build-packages:
- # https://github.com/containers/podman/blob/master/install.md#build-and-run-dependencies
+ # https://github.com/containers/podman/blob/main/install.md#build-and-run-dependencies
- btrfs-tools
- git
- golang-go
diff --git a/contrib/systemd/README.md b/contrib/systemd/README.md
index 480596915..af5ae0218 100644
--- a/contrib/systemd/README.md
+++ b/contrib/systemd/README.md
@@ -11,9 +11,9 @@
Assuming the status messages show no errors, the libpod service is ready to respond to the APIv2 on the unix domain socket `/run/podman/podman.sock`
### podman.service
-You can refer to [this example](https://github.com/containers/podman/blob/master/contrib/systemd/system/podman.service) for a sample podman.service file.
+You can refer to [this example](https://github.com/containers/podman/blob/main/contrib/systemd/system/podman.service) for a sample podman.service file.
### podman.socket
-You can refer to [this example](https://github.com/containers/podman/blob/master/contrib/systemd/system/podman.socket) for a sample podman.socket file.
+You can refer to [this example](https://github.com/containers/podman/blob/main/contrib/systemd/system/podman.socket) for a sample podman.socket file.
## user (podman service run as given user aka "rootless")
@@ -26,7 +26,7 @@ You can refer to [this example](https://github.com/containers/podman/blob/master
Assuming the status messages show no errors, the libpod service is ready to respond to the APIv2 on the unix domain socket `/run/user/$(id -u)/podman/podman.sock`
### podman.service
-You can refer to [this example](https://github.com/containers/podman/blob/master/contrib/systemd/user/podman.service) for a rootless podman.service file.
+You can refer to [this example](https://github.com/containers/podman/blob/main/contrib/systemd/user/podman.service) for a rootless podman.service file.
### podman.socket
-You can refer to [this example](https://github.com/containers/podman/blob/master/contrib/systemd/user/podman.socket) for a rootless podman.socket file.
+You can refer to [this example](https://github.com/containers/podman/blob/main/contrib/systemd/user/podman.socket) for a rootless podman.socket file.
diff --git a/docs/source/Tutorials.rst b/docs/source/Tutorials.rst
index cb9ab644d..34a029484 100644
--- a/docs/source/Tutorials.rst
+++ b/docs/source/Tutorials.rst
@@ -4,11 +4,11 @@ Tutorials
=========
Here are a number of useful tutorials to get you up and running with Podman. If you are familiar with the Docker `Container Engine`_ the command in Podman_ should be quite familiar. If you are brand new to containers, take a look at our `Introduction`.
-* `Basic Setup and Use of Podman <https://github.com/containers/podman/blob/master/docs/tutorials/podman_tutorial.md>`_: Learn how to setup Podman and perform some basic commands with the utility.
-* `Basic Setup and Use of Podman in a Rootless environment <https://github.com/containers/podman/blob/master/docs/tutorials/rootless_tutorial.md>`_: The steps required to setup rootless Podman are enumerated.
-* `Podman Mac/Windows tutorial <https://github.com/containers/podman/blob/master/docs/tutorials/mac_win_client.md>`_: Special setup for running the Podman remote client on a Mac or Windows PC and connecting to Podman running on a Linux VM are documented.
-* `How to sign and distribute container images using Podman <https://github.com/containers/podman/blob/master/docs/tutorials/image_signing.md>`_: Learn how to setup and use image signing with Podman.
-* `Podman remote-client tutorial <https://github.com/containers/podman/blob/master/docs/tutorials/remote_client.md>`_: A brief how-to on using the Podman remote-client.
-* `How to use libpod for custom/derivative projects <https://github.com/containers/podman/blob/master/docs/tutorials/podman-derivative-api.md>`_: How the libpod API can be used within your own project.
+* `Basic Setup and Use of Podman <https://github.com/containers/podman/blob/main/docs/tutorials/podman_tutorial.md>`_: Learn how to setup Podman and perform some basic commands with the utility.
+* `Basic Setup and Use of Podman in a Rootless environment <https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md>`_: The steps required to setup rootless Podman are enumerated.
+* `Podman Mac/Windows tutorial <https://github.com/containers/podman/blob/main/docs/tutorials/mac_win_client.md>`_: Special setup for running the Podman remote client on a Mac or Windows PC and connecting to Podman running on a Linux VM are documented.
+* `How to sign and distribute container images using Podman <https://github.com/containers/podman/blob/main/docs/tutorials/image_signing.md>`_: Learn how to setup and use image signing with Podman.
+* `Podman remote-client tutorial <https://github.com/containers/podman/blob/main/docs/tutorials/remote_client.md>`_: A brief how-to on using the Podman remote-client.
+* `How to use libpod for custom/derivative projects <https://github.com/containers/podman/blob/main/docs/tutorials/podman-derivative-api.md>`_: How the libpod API can be used within your own project.
* `How to use Podman's Go RESTful bindings <https://github.com/containers/podman/tree/main/pkg/bindings>`_: An introduction to using our RESTful Golang bindings in an external application.
-* `Common network setups <https://github.com/containers/podman/blob/master/docs/tutorials/basic_networking.md>`_: A basic guide to common network setups for Podman.
+* `Common network setups <https://github.com/containers/podman/blob/main/docs/tutorials/basic_networking.md>`_: A basic guide to common network setups for Podman.
diff --git a/docs/source/markdown/podman-build.1.md b/docs/source/markdown/podman-build.1.md
index 55549c99a..474597938 100644
--- a/docs/source/markdown/podman-build.1.md
+++ b/docs/source/markdown/podman-build.1.md
@@ -149,7 +149,7 @@ microseconds.
On some systems, changing the CPU limits may not be allowed for non-root
users. For more details, see
-https://github.com/containers/podman/blob/master/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error
+https://github.com/containers/podman/blob/main/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error
#### **--cpu-quota**=*limit*
@@ -162,7 +162,7 @@ ends (controllable via **--cpu-period**).
On some systems, changing the CPU limits may not be allowed for non-root
users. For more details, see
-https://github.com/containers/podman/blob/master/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error
+https://github.com/containers/podman/blob/main/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error
#### **--cpu-shares**, **-c**=*shares*
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index dd79a8d74..62028de40 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -160,7 +160,7 @@ microseconds.
On some systems, changing the CPU limits may not be allowed for non-root
users. For more details, see
-https://github.com/containers/podman/blob/master/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error
+https://github.com/containers/podman/blob/main/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error
#### **--cpu-quota**=*limit*
@@ -173,7 +173,7 @@ ends (controllable via **--cpu-period**).
On some systems, changing the CPU limits may not be allowed for non-root
users. For more details, see
-https://github.com/containers/podman/blob/master/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error
+https://github.com/containers/podman/blob/main/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error
#### **--cpu-rt-period**=*microseconds*
@@ -239,7 +239,7 @@ for **--cpu-period** and **--cpu-quota**, so you may only set either
On some systems, changing the CPU limits may not be allowed for non-root
users. For more details, see
-https://github.com/containers/podman/blob/master/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error
+https://github.com/containers/podman/blob/main/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error
#### **--cpuset-cpus**=*cpus*
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index 80652fcdf..efd60b46d 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -178,7 +178,7 @@ microseconds.
On some systems, changing the CPU limits may not be allowed for non-root
users. For more details, see
-https://github.com/containers/podman/blob/master/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error
+https://github.com/containers/podman/blob/main/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error
#### **--cpu-quota**=*limit*
@@ -191,7 +191,7 @@ ends (controllable via **--cpu-period**).
On some systems, changing the CPU limits may not be allowed for non-root
users. For more details, see
-https://github.com/containers/podman/blob/master/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error
+https://github.com/containers/podman/blob/main/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error
#### **--cpu-rt-period**=*microseconds*
@@ -255,7 +255,7 @@ for **--cpu-period** and **--cpu-quota**, so you may only set either
On some systems, changing the CPU limits may not be allowed for non-root
users. For more details, see
-https://github.com/containers/podman/blob/master/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error
+https://github.com/containers/podman/blob/main/troubleshooting.md#26-running-containers-with-cpu-limits-fails-with-a-permissions-error
#### **--cpuset-cpus**=*number*
@@ -377,7 +377,7 @@ You need to specify multi option commands in the form of a json string.
Set environment variables.
-This option allows arbitrary environment variables that are available for the process to be launched inside of the container. If an environment variable is specified without a value, Podman will check the host environment for a value and set the variable only if it is set on the host. If an environment variable ending in __*__ is specified, Podman will search the host environment for variables starting with the prefix and will add those variables to the container. If an environment variable with a trailing ***** is specified, then a value must be supplied.
+This option allows arbitrary environment variables that are available for the process to be launched inside of the container. If an environment variable is specified without a value, Podman will check the host environment for a value and set the variable only if it is set on the host. If an environment variable ending in __*__ is specified, Podman will search the host environment for variables starting with the prefix and will add those variables to the container. If an environment variable with a trailing __*__ is specified, then a value must be supplied.
See [**Environment**](#environment) note below for precedence and examples.
diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md
index 9f85ebda3..c1a22c0c1 100644
--- a/docs/source/markdown/podman.1.md
+++ b/docs/source/markdown/podman.1.md
@@ -402,7 +402,7 @@ The Overlay file system (OverlayFS) is not supported with kernels prior to 5.12.
The Network File System (NFS) and other distributed file systems (for example: Lustre, Spectrum Scale, the General Parallel File System (GPFS)) are not supported when running in rootless mode as these file systems do not understand user namespace. However, rootless Podman can make use of an NFS Homedir by modifying the `$HOME/.config/containers/storage.conf` to have the `graphroot` option point to a directory stored on local (Non NFS) storage.
-For more information, please refer to the [Podman Troubleshooting Page](https://github.com/containers/podman/blob/master/troubleshooting.md).
+For more information, please refer to the [Podman Troubleshooting Page](https://github.com/containers/podman/blob/main/troubleshooting.md).
## SEE ALSO
**[containers-mounts.conf(5)](https://github.com/containers/common/blob/main/docs/containers-mounts.conf.5.md)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**, **[containers-registries.conf(5)](https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md)**, **[containers-storage.conf(5)](https://github.com/containers/storage/blob/main/docs/containers-storage.conf.5.md)**, **[buildah(1)](https://github.com/containers/buildah/blob/main/docs/buildah.1.md)**, **oci-hooks(5)**, **[containers-policy.json(5)](https://github.com/containers/image/blob/main/docs/containers-policy.json.5.md)**, **[crun(1)](https://github.com/containers/crun/blob/main/crun.1.md)**, **[runc(8)](https://github.com/opencontainers/runc/blob/master/man/runc.8.md)**, **[subuid(5)](https://www.unix.com/man-page/linux/5/subuid)**, **[subgid(5)](https://www.unix.com/man-page/linux/5/subgid)**, **[slirp4netns(1)](https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md)**, **[conmon(8)](https://github.com/containers/conmon/blob/main/docs/conmon.8.md)**
diff --git a/docs/tutorials/mac_client.md b/docs/tutorials/mac_client.md
index 19522e74d..e73522da9 100644
--- a/docs/tutorials/mac_client.md
+++ b/docs/tutorials/mac_client.md
@@ -1,2 +1,2 @@
-# [Podman Mac Client tutorial](https://github.com/containers/podman/blob/master/docs/tutorials/mac_win_client.md)
-This tutorial has moved! You can find out how to set up Podman on macOS (as well as Windows) [here](https://github.com/containers/podman/blob/master/docs/tutorials/mac_win_client.md)
+# [Podman Mac Client tutorial](https://github.com/containers/podman/blob/main/docs/tutorials/mac_win_client.md)
+This tutorial has moved! You can find out how to set up Podman on macOS (as well as Windows) [here](https://github.com/containers/podman/blob/main/docs/tutorials/mac_win_client.md)
diff --git a/docs/tutorials/podman_tutorial.md b/docs/tutorials/podman_tutorial.md
index 92d0c41b1..83f1e5e1e 100644
--- a/docs/tutorials/podman_tutorial.md
+++ b/docs/tutorials/podman_tutorial.md
@@ -5,7 +5,7 @@ Podman is a utility provided as part of the libpod library. It can be used to c
containers. The following tutorial will teach you how to set up Podman and perform some basic
commands with Podman.
-If you are running on a Mac or Windows PC, you should instead follow the [Mac and Windows tutorial](https://github.com/containers/podman/blob/master/docs/tutorials/mac_win_client.md)
+If you are running on a Mac or Windows PC, you should instead follow the [Mac and Windows tutorial](https://github.com/containers/podman/blob/main/docs/tutorials/mac_win_client.md)
to set up the remote Podman client.
**NOTE**: the code samples are intended to be run as a non-root user, and use `sudo` where
@@ -13,7 +13,7 @@ root escalation is required.
## Installing Podman
-For installing or building Podman, please see the [installation instructions](https://github.com/containers/podman/blob/master/install.md).
+For installing or building Podman, please see the [installation instructions](https://github.com/containers/podman/blob/main/install.md).
## Familiarizing yourself with Podman
diff --git a/docs/tutorials/podman_tutorial_cn.md b/docs/tutorials/podman_tutorial_cn.md
index 5290c4076..36e83e16f 100644
--- a/docs/tutorials/podman_tutorial_cn.md
+++ b/docs/tutorials/podman_tutorial_cn.md
@@ -7,14 +7,14 @@ Podman是由libpod库æ供一个实用的程åºï¼Œå¯ä»¥è¢«ç”¨äºŽåˆ›å»ºå’Œç®¡ç
下é¢çš„教程会教你如何å¯åŠ¨ Podman 并使用 Podman 执行一些基本的命令。
如果你正在使用 Mac 或者 Windows
-,你应该先查看[Mac å’Œ Windows 使用说明](https://github.com/containers/podman/blob/master/docs/tutorials/mac_win_client.md)æ¥è®¾ç½® Podman
+,你应该先查看[Mac å’Œ Windows 使用说明](https://github.com/containers/podman/blob/main/docs/tutorials/mac_win_client.md)æ¥è®¾ç½® Podman
远程客户端。
**注æ„**ï¼šç¤ºä¾‹ä¸­æ‰€æœ‰å‘½ä»¤çš†ä»¥éž root 的用户è¿è¡Œï¼Œå¿…è¦çš„时候通过 `sudo` 命令æ¥èŽ·å– root æƒé™ã€‚
## 安装Podman
-安装或者编译 Podman ,请å‚ç…§[安装说明](https://github.com/containers/podman/blob/master/install.md)。
+安装或者编译 Podman ,请å‚ç…§[安装说明](https://github.com/containers/podman/blob/main/install.md)。
## 熟悉podman
diff --git a/docs/tutorials/remote_client.md b/docs/tutorials/remote_client.md
index 0370b0329..ef7160f6c 100644
--- a/docs/tutorials/remote_client.md
+++ b/docs/tutorials/remote_client.md
@@ -5,7 +5,7 @@ The purpose of the Podman remote-client is to allow users to interact with a Pod
The remote client takes advantage of a client-server model. You need Podman installed on a Linux machine or VM that also has the SSH daemon running. On the local operating system, when you execute a Podman command, Podman connects to the server via SSH. It then connects to the Podman service by using systemd socket activation, and hitting our [Rest API](https://docs.podman.io/en/latest/_static/api.html). The Podman commands are executed on the server. From the client's point of view, it seems like Podman runs locally.
-This tutorial is for running Podman remotely on Linux. If you are using a Mac or a Windows PC, please follow the [Mac and Windows tutorial](https://github.com/containers/podman/blob/master/docs/tutorials/mac_win_client.md)
+This tutorial is for running Podman remotely on Linux. If you are using a Mac or a Windows PC, please follow the [Mac and Windows tutorial](https://github.com/containers/podman/blob/main/docs/tutorials/mac_win_client.md)
## Obtaining and installing Podman
@@ -113,4 +113,4 @@ You can use the Podman remote clients to manage your containers running on a Lin
See the [Troubleshooting](../../troubleshooting.md) document if you run into issues.
## History
-Adapted from the [Mac and Windows tutorial](https://github.com/containers/podman/blob/master/docs/tutorials/mac_win_client.md)
+Adapted from the [Mac and Windows tutorial](https://github.com/containers/podman/blob/main/docs/tutorials/mac_win_client.md)
diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go
index c1cc99da4..f08a7ee41 100644
--- a/pkg/api/handlers/compat/images.go
+++ b/pkg/api/handlers/compat/images.go
@@ -11,6 +11,7 @@ import (
"github.com/containers/buildah"
"github.com/containers/common/libimage"
"github.com/containers/common/pkg/config"
+ "github.com/containers/common/pkg/filters"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v3/libpod"
@@ -404,25 +405,52 @@ func GetImage(w http.ResponseWriter, r *http.Request) {
}
func GetImages(w http.ResponseWriter, r *http.Request) {
- images, err := utils.GetImages(w, r)
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Failed get images"))
+ decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
+ runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
+ query := struct {
+ All bool
+ Digests bool
+ Filter string // Docker 1.24 compatibility
+ }{
+ // This is where you can override the golang default value for one of fields
+ }
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+ if _, found := r.URL.Query()["digests"]; found && query.Digests {
+ utils.UnSupportedParameter("digests")
return
}
- summaries := make([]*entities.ImageSummary, 0, len(images))
- for _, img := range images {
- // If the image is a manifest list, extract as much as we can.
- if isML, _ := img.IsManifestList(r.Context()); isML {
- continue
+ filterList, err := filters.FiltersFromRequest(r)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
+ return
+ }
+ if !utils.IsLibpodRequest(r) {
+ if len(query.Filter) > 0 { // Docker 1.24 compatibility
+ filterList = append(filterList, "reference="+query.Filter)
}
+ filterList = append(filterList, "manifest=false")
+ }
- is, err := handlers.ImageToImageSummary(img)
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Failed transform image summaries"))
- return
+ imageEngine := abi.ImageEngine{Libpod: runtime}
+
+ listOptions := entities.ImageListOptions{All: query.All, Filter: filterList}
+ summaries, err := imageEngine.List(r.Context(), listOptions)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
+ return
+ }
+
+ if !utils.IsLibpodRequest(r) {
+ // docker adds sha256: in front of the ID
+ for _, s := range summaries {
+ s.ID = "sha256:" + s.ID
}
- summaries = append(summaries, is)
}
utils.WriteResponse(w, http.StatusOK, summaries)
}
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index 6e23845f0..d3dbf3023 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -12,7 +12,6 @@ import (
"github.com/containers/buildah"
"github.com/containers/common/libimage"
- "github.com/containers/common/pkg/filters"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v3/libpod"
@@ -103,48 +102,6 @@ func GetImage(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusOK, inspect)
}
-func GetImages(w http.ResponseWriter, r *http.Request) {
- decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
- runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
- query := struct {
- All bool
- Digests bool
- Filter string // Docker 1.24 compatibility
- }{
- // This is where you can override the golang default value for one of fields
- }
-
- if err := decoder.Decode(&query, r.URL.Query()); err != nil {
- utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
- errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
- return
- }
- if _, found := r.URL.Query()["digests"]; found && query.Digests {
- utils.UnSupportedParameter("digests")
- return
- }
-
- filterList, err := filters.FiltersFromRequest(r)
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
- return
- }
- if !utils.IsLibpodRequest(r) && len(query.Filter) > 0 { // Docker 1.24 compatibility
- filterList = append(filterList, "reference="+query.Filter)
- }
-
- imageEngine := abi.ImageEngine{Libpod: runtime}
-
- listOptions := entities.ImageListOptions{All: query.All, Filter: filterList}
- summaries, err := imageEngine.List(r.Context(), listOptions)
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
- return
- }
-
- utils.WriteResponse(w, http.StatusOK, summaries)
-}
-
func PruneImages(w http.ResponseWriter, r *http.Request) {
var (
err error
diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go
index eb0b6827f..ef0839d1f 100644
--- a/pkg/api/handlers/libpod/manifests.go
+++ b/pkg/api/handlers/libpod/manifests.go
@@ -3,7 +3,11 @@ package libpod
import (
"context"
"encoding/json"
+ "fmt"
+ "io/ioutil"
"net/http"
+ "net/url"
+ "strings"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/manifest"
@@ -15,6 +19,8 @@ import (
"github.com/containers/podman/v3/pkg/auth"
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/containers/podman/v3/pkg/domain/infra/abi"
+ "github.com/containers/podman/v3/pkg/errorhandling"
+ "github.com/gorilla/mux"
"github.com/gorilla/schema"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
@@ -24,40 +30,93 @@ func ManifestCreate(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
query := struct {
- Name []string `schema:"name"`
- Image []string `schema:"image"`
- All bool `schema:"all"`
+ Name string `schema:"name"`
+ Images []string `schema:"images"`
+ All bool `schema:"all"`
}{
// Add defaults here once needed.
}
+
+ // Support 3.x API calls, alias image to images
+ if image, ok := r.URL.Query()["image"]; ok {
+ query.Images = image
+ }
+
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
return
}
- // TODO: (jhonce) When c/image is refactored the roadmap calls for this check to be pushed into that library.
- for _, n := range query.Name {
- if _, err := reference.ParseNormalizedNamed(n); err != nil {
+ // Support 4.x API calls, map query parameter to path
+ if name, ok := mux.Vars(r)["name"]; ok {
+ n, err := url.QueryUnescape(name)
+ if err != nil {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
- errors.Wrapf(err, "invalid image name %s", n))
+ errors.Wrapf(err, "failed to parse name parameter %q", name))
return
}
+ query.Name = n
+ }
+
+ if _, err := reference.ParseNormalizedNamed(query.Name); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "invalid image name %s", query.Name))
+ return
}
imageEngine := abi.ImageEngine{Libpod: runtime}
createOptions := entities.ManifestCreateOptions{All: query.All}
- manID, err := imageEngine.ManifestCreate(r.Context(), query.Name, query.Image, createOptions)
+ manID, err := imageEngine.ManifestCreate(r.Context(), query.Name, query.Images, createOptions)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+
+ status := http.StatusOK
+ if _, err := utils.SupportedVersion(r, "< 4.0.0"); err == utils.ErrVersionNotSupported {
+ status = http.StatusCreated
+ }
+
+ buffer, err := ioutil.ReadAll(r.Body)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+
+ // Treat \r\n as empty body
+ if len(buffer) < 3 {
+ utils.WriteResponse(w, status, handlers.IDResponse{ID: manID})
+ return
+ }
+
+ body := new(entities.ManifestModifyOptions)
+ if err := json.Unmarshal(buffer, body); err != nil {
+ utils.InternalServerError(w, errors.Wrap(err, "Decode()"))
+ return
+ }
+
+ // gather all images for manifest list
+ var images []string
+ if len(query.Images) > 0 {
+ images = append(query.Images)
+ }
+ if len(body.Images) > 0 {
+ images = append(body.Images)
+ }
+
+ id, err := imageEngine.ManifestAdd(r.Context(), query.Name, images, body.ManifestAddOptions)
if err != nil {
utils.InternalServerError(w, err)
return
}
- utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: manID})
+
+ utils.WriteResponse(w, status, handlers.IDResponse{ID: id})
}
-// ExistsManifest check if a manifest list exists
-func ExistsManifest(w http.ResponseWriter, r *http.Request) {
+// ManifestExists return true if manifest list exists.
+func ManifestExists(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
name := utils.GetName(r)
@@ -94,10 +153,18 @@ func ManifestInspect(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusOK, schema2List)
}
+// ManifestAdd remove digest from manifest list
+//
+// Deprecated: As of 4.0.0 use ManifestModify instead
func ManifestAdd(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
- var addOptions entities.ManifestAddOptions
- if err := json.NewDecoder(r.Body).Decode(&addOptions); err != nil {
+
+ // Wrapper to support 3.x with 4.x libpod
+ query := struct {
+ entities.ManifestAddOptions
+ Images []string
+ }{}
+ if err := json.NewDecoder(r.Body).Decode(&query); err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
return
}
@@ -108,15 +175,8 @@ func ManifestAdd(w http.ResponseWriter, r *http.Request) {
return
}
- // FIXME: we really need to clean up the manifest API. Swagger states
- // the arguments were strings not string slices. The use of string
- // slices, mixing lists and images is incredibly confusing.
- if len(addOptions.Images) == 1 {
- addOptions.Images = append(addOptions.Images, name)
- }
-
imageEngine := abi.ImageEngine{Libpod: runtime}
- newID, err := imageEngine.ManifestAdd(r.Context(), addOptions)
+ newID, err := imageEngine.ManifestAdd(r.Context(), name, query.Images, query.ManifestAddOptions)
if err != nil {
utils.InternalServerError(w, err)
return
@@ -124,7 +184,10 @@ func ManifestAdd(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: newID})
}
-func ManifestRemove(w http.ResponseWriter, r *http.Request) {
+// ManifestRemoveDigest remove digest from manifest list
+//
+// Deprecated: As of 4.0.0 use ManifestModify instead
+func ManifestRemoveDigest(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
query := struct {
@@ -155,7 +218,10 @@ func ManifestRemove(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: manifestList.ID()})
}
-func ManifestPush(w http.ResponseWriter, r *http.Request) {
+// ManifestPushV3 push image to registry
+//
+// Deprecated: As of 4.0.0 use ManifestPush instead
+func ManifestPushV3(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
query := struct {
@@ -207,3 +273,156 @@ func ManifestPush(w http.ResponseWriter, r *http.Request) {
}
utils.WriteResponse(w, http.StatusOK, digest)
}
+
+// ManifestPush push image to registry
+//
+// As of 4.0.0
+func ManifestPush(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
+ decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
+
+ query := struct {
+ All bool `schema:"all"`
+ TLSVerify bool `schema:"tlsVerify"`
+ }{
+ // Add defaults here once needed.
+ }
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+
+ destination := utils.GetVar(r, "destination")
+ if err := utils.IsRegistryReference(destination); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err)
+ return
+ }
+
+ authconf, authfile, err := auth.GetCredentials(r)
+ if err != nil {
+ utils.Error(w, "failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "failed to parse registry header for %s", r.URL.String()))
+ return
+ }
+ defer auth.RemoveAuthfile(authfile)
+ var username, password string
+ if authconf != nil {
+ username = authconf.Username
+ password = authconf.Password
+ }
+ options := entities.ImagePushOptions{
+ Authfile: authfile,
+ Username: username,
+ Password: password,
+ All: query.All,
+ }
+ if sys := runtime.SystemContext(); sys != nil {
+ options.CertDir = sys.DockerCertPath
+ }
+ if _, found := r.URL.Query()["tlsVerify"]; found {
+ options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
+ }
+
+ imageEngine := abi.ImageEngine{Libpod: runtime}
+ source := utils.GetName(r)
+ digest, err := imageEngine.ManifestPush(context.Background(), source, destination, options)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "error pushing image %q", destination))
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: digest})
+}
+
+// ManifestModify efficiently updates the named manifest list
+func ManifestModify(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
+ imageEngine := abi.ImageEngine{Libpod: runtime}
+
+ body := new(entities.ManifestModifyOptions)
+ if err := json.NewDecoder(r.Body).Decode(body); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
+ return
+ }
+
+ name := utils.GetName(r)
+ if _, err := runtime.LibimageRuntime().LookupManifestList(name); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusNotFound, err)
+ return
+ }
+
+ var report entities.ManifestModifyReport
+ switch {
+ case strings.EqualFold("update", body.Operation):
+ id, err := imageEngine.ManifestAdd(r.Context(), name, body.Images, body.ManifestAddOptions)
+ if err != nil {
+ report.Errors = append(report.Errors, err)
+ break
+ }
+ report = entities.ManifestModifyReport{
+ ID: id,
+ Images: body.Images,
+ }
+ case strings.EqualFold("remove", body.Operation):
+ for _, image := range body.Images {
+ id, err := imageEngine.ManifestRemoveDigest(r.Context(), name, image)
+ if err != nil {
+ report.Errors = append(report.Errors, err)
+ continue
+ }
+ report.ID = id
+ report.Images = append(report.Images, image)
+ }
+ case strings.EqualFold("annotate", body.Operation):
+ options := entities.ManifestAnnotateOptions{
+ Annotation: body.Annotation,
+ Arch: body.Arch,
+ Features: body.Features,
+ OS: body.OS,
+ OSFeatures: body.OSFeatures,
+ OSVersion: body.OSVersion,
+ Variant: body.Variant,
+ }
+ for _, image := range body.Images {
+ id, err := imageEngine.ManifestAnnotate(r.Context(), name, image, options)
+ if err != nil {
+ report.Errors = append(report.Errors, err)
+ continue
+ }
+ report.ID = id
+ report.Images = append(report.Images, image)
+ }
+ default:
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ fmt.Errorf("illegal operation %q for %q", body.Operation, r.URL.String()))
+ return
+ }
+
+ statusCode := http.StatusOK
+ switch {
+ case len(report.Errors) > 0 && len(report.Images) > 0:
+ statusCode = http.StatusConflict
+ case len(report.Errors) > 0:
+ statusCode = http.StatusInternalServerError
+ }
+ utils.WriteResponse(w, statusCode, report)
+}
+
+// ManifestDelete removes a manifest list from storage
+func ManifestDelete(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
+ imageEngine := abi.ImageEngine{Libpod: runtime}
+
+ name := utils.GetName(r)
+ if _, err := runtime.LibimageRuntime().LookupManifestList(name); err != nil {
+ utils.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound, err)
+ return
+ }
+
+ results, errs := imageEngine.ManifestRm(r.Context(), []string{name})
+ errsString := errorhandling.ErrorsToStrings(errs)
+ report := handlers.LibpodImagesRemoveReport{
+ ImageRemoveReport: *results,
+ Errors: errsString,
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
+}
diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go
index 1733476cc..d3a592bdf 100644
--- a/pkg/api/handlers/types.go
+++ b/pkg/api/handlers/types.go
@@ -184,46 +184,6 @@ type ExecStartConfig struct {
Width uint16 `json:"w"`
}
-func ImageToImageSummary(l *libimage.Image) (*entities.ImageSummary, error) {
- options := &libimage.InspectOptions{WithParent: true, WithSize: true}
- imageData, err := l.Inspect(context.TODO(), options)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to obtain summary for image %s", l.ID())
- }
-
- containers, err := l.Containers()
- if err != nil {
- return nil, errors.Wrapf(err, "failed to obtain Containers for image %s", l.ID())
- }
- containerCount := len(containers)
-
- isDangling, err := l.IsDangling(context.TODO())
- if err != nil {
- return nil, errors.Wrapf(err, "failed to check if image %s is dangling", l.ID())
- }
-
- is := entities.ImageSummary{
- // docker adds sha256: in front of the ID
- ID: "sha256:" + l.ID(),
- ParentId: imageData.Parent,
- RepoTags: imageData.RepoTags,
- RepoDigests: imageData.RepoDigests,
- Created: l.Created().Unix(),
- Size: imageData.Size,
- SharedSize: 0,
- VirtualSize: imageData.VirtualSize,
- Labels: imageData.Labels,
- Containers: containerCount,
- ReadOnly: l.IsReadOnly(),
- Dangling: isDangling,
- Names: l.Names(),
- Digest: string(imageData.Digest),
- ConfigDigest: "", // TODO: libpod/image didn't set it but libimage should
- History: imageData.NamesHistory,
- }
- return &is, nil
-}
-
func ImageDataToImageInspect(ctx context.Context, l *libimage.Image) (*ImageInspect, error) {
options := &libimage.InspectOptions{WithParent: true, WithSize: true}
info, err := l.Inspect(context.Background(), options)
diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go
index 96b7a957c..ee83755a1 100644
--- a/pkg/api/handlers/utils/handler.go
+++ b/pkg/api/handlers/utils/handler.go
@@ -174,7 +174,7 @@ func FilterMapToString(filters map[string][]string) (string, error) {
return string(f), nil
}
-func getVar(r *http.Request, k string) string {
+func GetVar(r *http.Request, k string) string {
val := mux.Vars(r)[k]
safeVal, err := url.PathUnescape(val)
if err != nil {
@@ -186,5 +186,5 @@ func getVar(r *http.Request, k string) string {
// GetName extracts the name from the mux
func GetName(r *http.Request) string {
- return getVar(r, "name")
+ return GetVar(r, "name")
}
diff --git a/pkg/api/handlers/utils/images.go b/pkg/api/handlers/utils/images.go
index 3f3f48193..639de9915 100644
--- a/pkg/api/handlers/utils/images.go
+++ b/pkg/api/handlers/utils/images.go
@@ -6,7 +6,6 @@ import (
"strings"
"github.com/containers/common/libimage"
- "github.com/containers/common/pkg/filters"
"github.com/containers/image/v5/docker"
storageTransport "github.com/containers/image/v5/storage"
"github.com/containers/image/v5/transports/alltransports"
@@ -16,7 +15,6 @@ import (
"github.com/containers/podman/v3/pkg/util"
"github.com/containers/storage"
"github.com/docker/distribution/reference"
- "github.com/gorilla/schema"
"github.com/pkg/errors"
)
@@ -91,44 +89,6 @@ func ParseStorageReference(name string) (types.ImageReference, error) {
return imageRef, nil
}
-// GetImages is a common function used to get images for libpod and other compatibility
-// mechanisms
-func GetImages(w http.ResponseWriter, r *http.Request) ([]*libimage.Image, error) {
- decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
- runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
- query := struct {
- All bool
- Digests bool
- Filter string // Docker 1.24 compatibility
- }{
- // This is where you can override the golang default value for one of fields
- }
-
- if err := decoder.Decode(&query, r.URL.Query()); err != nil {
- return nil, err
- }
- if _, found := r.URL.Query()["digests"]; found && query.Digests {
- UnSupportedParameter("digests")
- }
-
- filterList, err := filters.FiltersFromRequest(r)
- if err != nil {
- return nil, err
- }
- if !IsLibpodRequest(r) && len(query.Filter) > 0 { // Docker 1.24 compatibility
- filterList = append(filterList, "reference="+query.Filter)
- }
-
- if !query.All {
- // Filter intermediate images unless we want to list *all*.
- // NOTE: it's a positive filter, so `intermediate=false` means
- // to display non-intermediate images.
- filterList = append(filterList, "intermediate=false")
- }
- listOptions := &libimage.ListImagesOptions{Filters: filterList}
- return runtime.LibimageRuntime().ListImages(r.Context(), nil, listOptions)
-}
-
func GetImage(r *http.Request, name string) (*libimage.Image, error) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
image, _, err := runtime.LibimageRuntime().LookupImage(name, nil)
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index d9cda8579..df48253e2 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -840,7 +840,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// $ref: "#/responses/LibpodImageSummaryResponse"
// 500:
// $ref: '#/responses/InternalError'
- r.Handle(VersionedPath("/libpod/images/json"), s.APIHandler(libpod.GetImages)).Methods(http.MethodGet)
+ r.Handle(VersionedPath("/libpod/images/json"), s.APIHandler(compat.GetImages)).Methods(http.MethodGet)
// swagger:operation POST /libpod/images/load libpod ImageLoadLibpod
// ---
// tags:
diff --git a/pkg/api/server/register_manifest.go b/pkg/api/server/register_manifest.go
index 010d8a79e..8cd3d8b22 100644
--- a/pkg/api/server/register_manifest.go
+++ b/pkg/api/server/register_manifest.go
@@ -8,7 +8,9 @@ import (
)
func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
- // swagger:operation POST /libpod/manifests/create manifests ManifestCreateLibpod
+ v3 := r.PathPrefix("/v{version:[0-3][0-9A-Za-z.-]*}/libpod/manifests").Subrouter()
+ v4 := r.PathPrefix("/v{version:[4-9][0-9A-Za-z.-]*}/libpod/manifests").Subrouter()
+ // swagger:operation POST /libpod/manifests manifests ManifestCreateLibpod
// ---
// summary: Create
// description: Create a manifest list
@@ -18,18 +20,30 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// - in: query
// name: name
// type: string
- // description: manifest list name
+ // description: manifest list or index name to create
// required: true
// - in: query
- // name: image
+ // name: images
// type: string
- // description: name of the image
+ // required: true
+ // description: |
+ // One or more names of an image or a manifest list. Repeat parameter as needed.
+ //
+ // Support for multiple images, as of version 4.0.0
+ // Alias of `image` is support for compatibility with < 4.0.0
+ // Response status code is 200 with < 4.0.0 for compatibility
// - in: query
// name: all
// type: boolean
// description: add all contents if given list
+ // - in: body
+ // name: options
+ // description: options for new manifest
+ // required: false
+ // schema:
+ // $ref: "#/definitions/ManifestModifyOptions"
// responses:
- // 200:
+ // 201:
// schema:
// $ref: "#/definitions/IDResponse"
// 400:
@@ -38,17 +52,21 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// $ref: "#/responses/NoSuchImage"
// 500:
// $ref: "#/responses/InternalError"
- r.Handle(VersionedPath("/libpod/manifests/create"), s.APIHandler(libpod.ManifestCreate)).Methods(http.MethodPost)
+ v3.Handle("/create", s.APIHandler(libpod.ManifestCreate)).Methods(http.MethodPost)
+ v4.Handle("/{name:.*}", s.APIHandler(libpod.ManifestCreate)).Methods(http.MethodPost)
// swagger:operation GET /libpod/manifests/{name}/exists manifests ManifestExistsLibpod
// ---
// summary: Exists
- // description: Check if manifest list exists
+ // description: |
+ // Check if manifest list exists
+ //
+ // Note: There is no contract that the manifest list will exist for a follow-on operation
// parameters:
// - in: path
// name: name
// type: string
// required: true
- // description: the name of the manifest list
+ // description: the name or ID of the manifest list
// produces:
// - application/json
// responses:
@@ -58,11 +76,12 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// $ref: '#/responses/NoSuchManifest'
// 500:
// $ref: '#/responses/InternalError'
- r.Handle(VersionedPath("/libpod/manifests/{name}/exists"), s.APIHandler(libpod.ExistsManifest)).Methods(http.MethodGet)
+ v3.Handle("/{name:.*}/exists", s.APIHandler(libpod.ManifestExists)).Methods(http.MethodGet)
+ v4.Handle("/{name:.*}/exists", s.APIHandler(libpod.ManifestExists)).Methods(http.MethodGet)
// swagger:operation GET /libpod/manifests/{name}/json manifests ManifestInspectLibpod
// ---
// summary: Inspect
- // description: Display a manifest list
+ // description: Display attributes of given manifest list
// produces:
// - application/json
// parameters:
@@ -70,7 +89,7 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// name: name
// type: string
// required: true
- // description: the name or ID of the manifest
+ // description: the name or ID of the manifest list
// responses:
// 200:
// $ref: "#/responses/InspectManifest"
@@ -78,11 +97,53 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// $ref: "#/responses/NoSuchManifest"
// 500:
// $ref: "#/responses/InternalError"
- r.Handle(VersionedPath("/libpod/manifests/{name:.*}/json"), s.APIHandler(libpod.ManifestInspect)).Methods(http.MethodGet)
+ v3.Handle("/{name:.*}/json", s.APIHandler(libpod.ManifestInspect)).Methods(http.MethodGet)
+ v4.Handle("/{name:.*}/json", s.APIHandler(libpod.ManifestInspect)).Methods(http.MethodGet)
+ // swagger:operation PUT /libpod/manifests/{name} manifests ManifestModifyLibpod
+ // ---
+ // summary: Modify manifest list
+ // description: |
+ // Add/Remove an image(s) to a manifest list
+ //
+ // Note: operations are not atomic when multiple Images are provided.
+ //
+ // As of v4.0.0
+ // produces:
+ // - application/json
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name or ID of the manifest
+ // - in: body
+ // name: options
+ // description: options for mutating a manifest
+ // required: true
+ // schema:
+ // $ref: "#/definitions/ManifestModifyOptions"
+ // responses:
+ // 200:
+ // schema:
+ // $ref: "#/definitions/ManifestModifyReport"
+ // 404:
+ // $ref: "#/responses/NoSuchManifest"
+ // 400:
+ // $ref: "#/responses/BadParamError"
+ // 409:
+ // description: Operation had partial success, both Images and Errors may have members
+ // schema:
+ // $ref: "#/definitions/ManifestModifyReport"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ v4.Handle("/{name:.*}", s.APIHandler(libpod.ManifestModify)).Methods(http.MethodPut)
// swagger:operation POST /libpod/manifests/{name}/add manifests ManifestAddLibpod
// ---
// summary: Add image
- // description: Add an image to a manifest list
+ // description: |
+ // Add an image to a manifest list
+ //
+ // Deprecated: As of 4.0.0 use ManifestModifyLibpod instead
// produces:
// - application/json
// parameters:
@@ -95,7 +156,7 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// name: options
// description: options for creating a manifest
// schema:
- // $ref: "#/definitions/ManifestAddOpts"
+ // $ref: "#/definitions/ManifestAddOptions"
// responses:
// 200:
// schema:
@@ -106,11 +167,14 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// $ref: "#/responses/BadParamError"
// 500:
// $ref: "#/responses/InternalError"
- r.Handle(VersionedPath("/libpod/manifests/{name:.*}/add"), s.APIHandler(libpod.ManifestAdd)).Methods(http.MethodPost)
- // swagger:operation DELETE /libpod/manifests/{name} manifests ManifestDeleteLibpod
+ v3.Handle("/{name:.*}/add", s.APIHandler(libpod.ManifestAdd)).Methods(http.MethodPost)
+ // swagger:operation DELETE /libpod/manifests/{name} manifests ManifestDeleteV3Libpod
// ---
- // summary: Remove
- // description: Remove an image from a manifest list
+ // summary: Remove image from a manifest list
+ // description: |
+ // Remove an image from a manifest list
+ //
+ // Deprecated: As of 4.0.0 use ManifestModifyLibpod instead
// produces:
// - application/json
// parameters:
@@ -133,11 +197,37 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// $ref: "#/responses/NoSuchManifest"
// 500:
// $ref: "#/responses/InternalError"
- r.Handle(VersionedPath("/libpod/manifests/{name:.*}"), s.APIHandler(libpod.ManifestRemove)).Methods(http.MethodDelete)
- // swagger:operation POST /libpod/manifests/{name}/push manifests ManifestPushLibpod
+ v3.Handle("/{name:.*}", s.APIHandler(libpod.ManifestRemoveDigest)).Methods(http.MethodDelete)
+ // swagger:operation DELETE /libpod/manifests/{name} manifests ManifestDeleteLibpod
// ---
- // summary: Push
- // description: Push a manifest list or image index to a registry
+ // summary: Delete manifest list
+ // description: |
+ // Delete named manifest list
+ //
+ // As of v4.0.0
+ // produces:
+ // - application/json
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: The name or ID of the list to be deleted
+ // responses:
+ // 200:
+ // $ref: "#/responses/DocsLibpodImagesRemoveResponse"
+ // 404:
+ // $ref: "#/responses/NoSuchManifest"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ v4.Handle("/{name:.*}", s.APIHandler(libpod.ManifestDelete)).Methods(http.MethodDelete)
+ // swagger:operation POST /libpod/manifests/{name}/push manifests ManifestPushV3Libpod
+ // ---
+ // summary: Push manifest to registry
+ // description: |
+ // Push a manifest list or image index to a registry
+ //
+ // Deprecated: As of 4.0.0 use ManifestPushLibpod instead
// produces:
// - application/json
// parameters:
@@ -165,6 +255,47 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// $ref: "#/responses/NoSuchManifest"
// 500:
// $ref: "#/responses/InternalError"
- r.Handle(VersionedPath("/libpod/manifests/{name}/push"), s.APIHandler(libpod.ManifestPush)).Methods(http.MethodPost)
+ v3.Handle("/{name}/push", s.APIHandler(libpod.ManifestPushV3)).Methods(http.MethodPost)
+ // swagger:operation POST /libpod/manifests/{name}/registry/{destination} manifests ManifestPushLibpod
+ // ---
+ // summary: Push manifest list to registry
+ // description: |
+ // Push a manifest list or image index to the named registry
+ //
+ // As of v4.0.0
+ // produces:
+ // - application/json
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name or ID of the manifest list
+ // - in: path
+ // name: destination
+ // type: string
+ // required: true
+ // description: the registry for the manifest list
+ // - in: query
+ // name: all
+ // description: push all images
+ // type: boolean
+ // default: false
+ // - in: query
+ // name: tlsVerify
+ // type: boolean
+ // default: false
+ // description: skip TLS verification for registries
+ // responses:
+ // 200:
+ // schema:
+ // $ref: "#/definitions/IDResponse"
+ // 400:
+ // $ref: "#/responses/BadParamError"
+ // 404:
+ // $ref: "#/responses/NoSuchManifest"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ v4.Handle("/{name:.*}/registry/{destination:.*}", s.APIHandler(libpod.ManifestPush)).Methods(http.MethodPost)
return nil
}
diff --git a/pkg/api/server/register_swagger.go b/pkg/api/server/register_swagger.go
index dca1df14b..48af7713f 100644
--- a/pkg/api/server/register_swagger.go
+++ b/pkg/api/server/register_swagger.go
@@ -7,8 +7,8 @@ import (
"github.com/gorilla/mux"
)
-// RegisterSwaggerHandlers maps the swagger endpoint for the server
-func (s *APIServer) RegisterSwaggerHandlers(r *mux.Router) error {
+// registerSwaggerHandlers maps the swagger endpoint for the server
+func (s *APIServer) registerSwaggerHandlers(r *mux.Router) error {
// This handler does _*NOT*_ provide an UI rather just a swagger spec that an UI could render
r.HandleFunc(VersionedPath("/libpod/swagger"), s.APIHandler(libpod.ServeSwagger)).Methods(http.MethodGet)
return nil
diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go
index 8c5c7aeeb..65b7e2474 100644
--- a/pkg/api/server/server.go
+++ b/pkg/api/server/server.go
@@ -151,7 +151,7 @@ func newServer(runtime *libpod.Runtime, listener *net.Listener, opts entities.Se
server.registerPluginsHandlers,
server.registerPodsHandlers,
server.registerSecretHandlers,
- server.RegisterSwaggerHandlers,
+ server.registerSwaggerHandlers,
server.registerSwarmHandlers,
server.registerSystemHandlers,
server.registerVersionHandlers,
diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go
index f423c011d..419225007 100644
--- a/pkg/auth/auth.go
+++ b/pkg/auth/auth.go
@@ -140,7 +140,7 @@ func getAuthCredentials(headers []string) (*types.DockerAuthConfig, map[string]t
// MakeXRegistryConfigHeader returns a map with the "X-Registry-Config" header set, which can
// conveniently be used in the http stack.
-func MakeXRegistryConfigHeader(sys *types.SystemContext, username, password string) (map[string]string, error) {
+func MakeXRegistryConfigHeader(sys *types.SystemContext, username, password string) (http.Header, error) {
if sys == nil {
sys = &types.SystemContext{}
}
@@ -163,18 +163,18 @@ func MakeXRegistryConfigHeader(sys *types.SystemContext, username, password stri
if err != nil {
return nil, err
}
- return map[string]string{xRegistryConfigHeader: content}, nil
+ return http.Header{xRegistryConfigHeader: []string{content}}, nil
}
// MakeXRegistryAuthHeader returns a map with the "X-Registry-Auth" header set, which can
// conveniently be used in the http stack.
-func MakeXRegistryAuthHeader(sys *types.SystemContext, username, password string) (map[string]string, error) {
+func MakeXRegistryAuthHeader(sys *types.SystemContext, username, password string) (http.Header, error) {
if username != "" {
content, err := encodeSingleAuthConfig(types.DockerAuthConfig{Username: username, Password: password})
if err != nil {
return nil, err
}
- return map[string]string{xRegistryAuthHeader: content}, nil
+ return http.Header{xRegistryAuthHeader: []string{content}}, nil
}
if sys == nil {
@@ -188,7 +188,7 @@ func MakeXRegistryAuthHeader(sys *types.SystemContext, username, password string
if err != nil {
return nil, err
}
- return map[string]string{xRegistryAuthHeader: content}, nil
+ return http.Header{xRegistryAuthHeader: []string{content}}, nil
}
// RemoveAuthfile is a convenience function that is meant to be called in a
diff --git a/pkg/auth/auth_test.go b/pkg/auth/auth_test.go
index f7e6e4ef6..2c79f0b7c 100644
--- a/pkg/auth/auth_test.go
+++ b/pkg/auth/auth_test.go
@@ -85,8 +85,8 @@ func TestMakeXRegistryConfigHeaderGetCredentialsRoundtrip(t *testing.T) {
require.NoError(t, err)
req, err := http.NewRequest(http.MethodPost, "/", nil)
require.NoError(t, err, tc.name)
- for k, v := range headers {
- req.Header.Set(k, v)
+ for _, v := range headers.Values(xRegistryConfigHeader) {
+ req.Header.Add(xRegistryConfigHeader, v)
}
override, resPath, err := GetCredentials(req)
@@ -137,8 +137,8 @@ func TestMakeXRegistryAuthHeaderGetCredentialsRoundtrip(t *testing.T) {
require.NoError(t, err)
req, err := http.NewRequest(http.MethodPost, "/", nil)
require.NoError(t, err, tc.name)
- for k, v := range headers {
- req.Header.Set(k, v)
+ for _, v := range headers.Values(xRegistryAuthHeader) {
+ req.Header.Set(xRegistryAuthHeader, v)
}
override, resPath, err := GetCredentials(req)
@@ -219,7 +219,7 @@ func TestMakeXRegistryConfigHeader(t *testing.T) {
require.Len(t, res, 1, tc.name)
header, ok := res[xRegistryConfigHeader]
require.True(t, ok, tc.name)
- decodedHeader, err := base64.URLEncoding.DecodeString(header)
+ decodedHeader, err := base64.URLEncoding.DecodeString(header[0])
require.NoError(t, err, tc.name)
// Don't test for a specific JSON representation, just for the expected contents.
expected := map[string]interface{}{}
@@ -282,7 +282,7 @@ func TestMakeXRegistryAuthHeader(t *testing.T) {
require.Len(t, res, 1, tc.name)
header, ok := res[xRegistryAuthHeader]
require.True(t, ok, tc.name)
- decodedHeader, err := base64.URLEncoding.DecodeString(header)
+ decodedHeader, err := base64.URLEncoding.DecodeString(header[0])
require.NoError(t, err, tc.name)
// Don't test for a specific JSON representation, just for the expected contents.
expected := map[string]interface{}{}
diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go
index b2e949f67..332aa97c8 100644
--- a/pkg/bindings/connection.go
+++ b/pkg/bindings/connection.go
@@ -15,7 +15,6 @@ import (
"github.com/blang/semver"
"github.com/containers/podman/v3/pkg/terminal"
"github.com/containers/podman/v3/version"
- jsoniter "github.com/json-iterator/go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh"
@@ -35,16 +34,24 @@ type Connection struct {
type valueKey string
const (
- clientKey = valueKey("Client")
+ clientKey = valueKey("Client")
+ versionKey = valueKey("ServiceVersion")
)
// GetClient from context build by NewConnection()
func GetClient(ctx context.Context) (*Connection, error) {
- c, ok := ctx.Value(clientKey).(*Connection)
- if !ok {
- return nil, errors.Errorf("ClientKey not set in context")
+ if c, ok := ctx.Value(clientKey).(*Connection); ok {
+ return c, nil
}
- return c, nil
+ return nil, errors.Errorf("%s not set in context", clientKey)
+}
+
+// ServiceVersion from context build by NewConnection()
+func ServiceVersion(ctx context.Context) *semver.Version {
+ if v, ok := ctx.Value(versionKey).(*semver.Version); ok {
+ return v
+ }
+ return new(semver.Version)
}
// JoinURL elements with '/'
@@ -52,6 +59,7 @@ func JoinURL(elements ...string) string {
return "/" + strings.Join(elements, "/")
}
+// NewConnection creates a new service connection without an identity
func NewConnection(ctx context.Context, uri string) (context.Context, error) {
return NewConnectionWithIdentity(ctx, uri, "")
}
@@ -116,9 +124,11 @@ func NewConnectionWithIdentity(ctx context.Context, uri string, identity string)
}
ctx = context.WithValue(ctx, clientKey, &connection)
- if err := pingNewConnection(ctx); err != nil {
+ serviceVersion, err := pingNewConnection(ctx)
+ if err != nil {
return nil, errors.Wrap(err, "unable to connect to Podman socket")
}
+ ctx = context.WithValue(ctx, versionKey, serviceVersion)
return ctx, nil
}
@@ -139,15 +149,15 @@ func tcpClient(_url *url.URL) Connection {
// pingNewConnection pings to make sure the RESTFUL service is up
// and running. it should only be used when initializing a connection
-func pingNewConnection(ctx context.Context) error {
+func pingNewConnection(ctx context.Context) (*semver.Version, error) {
client, err := GetClient(ctx)
if err != nil {
- return err
+ return nil, err
}
// the ping endpoint sits at / in this case
response, err := client.DoRequest(ctx, nil, http.MethodGet, "/_ping", nil, nil)
if err != nil {
- return err
+ return nil, err
}
defer response.Body.Close()
@@ -155,23 +165,23 @@ func pingNewConnection(ctx context.Context) error {
versionHdr := response.Header.Get("Libpod-API-Version")
if versionHdr == "" {
logrus.Info("Service did not provide Libpod-API-Version Header")
- return nil
+ return new(semver.Version), nil
}
versionSrv, err := semver.ParseTolerant(versionHdr)
if err != nil {
- return err
+ return nil, err
}
switch version.APIVersion[version.Libpod][version.MinimalAPI].Compare(versionSrv) {
case -1, 0:
// Server's job when Client version is equal or older
- return nil
+ return &versionSrv, nil
case 1:
- return errors.Errorf("server API version is too old. Client %q server %q",
+ return nil, errors.Errorf("server API version is too old. Client %q server %q",
version.APIVersion[version.Libpod][version.MinimalAPI].String(), versionSrv.String())
}
}
- return errors.Errorf("ping response was %d", response.StatusCode)
+ return nil, errors.Errorf("ping response was %d", response.StatusCode)
}
func sshClient(_url *url.URL, secure bool, passPhrase string, identity string) (Connection, error) {
@@ -306,26 +316,29 @@ func unixClient(_url *url.URL) Connection {
}
// DoRequest assembles the http request and returns the response
-func (c *Connection) DoRequest(ctx context.Context, httpBody io.Reader, httpMethod, endpoint string, queryParams url.Values, header map[string]string, pathValues ...string) (*APIResponse, error) {
+func (c *Connection) DoRequest(ctx context.Context, httpBody io.Reader, httpMethod, endpoint string, queryParams url.Values, headers http.Header, pathValues ...string) (*APIResponse, error) {
var (
err error
response *http.Response
)
- params := make([]interface{}, len(pathValues)+3)
+ params := make([]interface{}, len(pathValues)+1)
+
+ if v := headers.Values("API-Version"); len(v) > 0 {
+ params[0] = v[0]
+ } else {
+ // Including the semver suffices breaks older services... so do not include them
+ v := version.APIVersion[version.Libpod][version.CurrentAPI]
+ params[0] = fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
+ }
- // Including the semver suffices breaks older services... so do not include them
- v := version.APIVersion[version.Libpod][version.CurrentAPI]
- params[0] = v.Major
- params[1] = v.Minor
- params[2] = v.Patch
for i, pv := range pathValues {
// url.URL lacks the semantics for escaping embedded path parameters... so we manually
// escape each one and assume the caller included the correct formatting in "endpoint"
- params[i+3] = url.PathEscape(pv)
+ params[i+1] = url.PathEscape(pv)
}
- uri := fmt.Sprintf("http://d/v%d.%d.%d/libpod"+endpoint, params...)
+ uri := fmt.Sprintf("http://d/v%s/libpod"+endpoint, params...)
logrus.Debugf("DoRequest Method: %s URI: %v", httpMethod, uri)
req, err := http.NewRequestWithContext(ctx, httpMethod, uri, httpBody)
@@ -335,9 +348,17 @@ func (c *Connection) DoRequest(ctx context.Context, httpBody io.Reader, httpMeth
if len(queryParams) > 0 {
req.URL.RawQuery = queryParams.Encode()
}
- for key, val := range header {
- req.Header.Set(key, val)
+
+ for key, val := range headers {
+ if key == "API-Version" {
+ continue
+ }
+
+ for _, v := range val {
+ req.Header.Add(key, v)
+ }
}
+
// Give the Do three chances in the case of a comm/service hiccup
for i := 1; i <= 3; i++ {
response, err = c.Client.Do(req) // nolint
@@ -349,7 +370,7 @@ func (c *Connection) DoRequest(ctx context.Context, httpBody io.Reader, httpMeth
return &APIResponse{response, req}, err
}
-// Get raw Transport.DialContext from client
+// GetDialer returns raw Transport.DialContext from client
func (c *Connection) GetDialer(ctx context.Context) (net.Conn, error) {
client := c.Client
transport := client.Transport.(*http.Transport)
@@ -360,16 +381,6 @@ func (c *Connection) GetDialer(ctx context.Context) (net.Conn, error) {
return nil, errors.New("Unable to get dial context")
}
-// FiltersToString converts our typical filter format of a
-// map[string][]string to a query/html safe string.
-func FiltersToString(filters map[string][]string) (string, error) {
- lowerCaseKeys := make(map[string][]string)
- for k, v := range filters {
- lowerCaseKeys[strings.ToLower(k)] = v
- }
- return jsoniter.MarshalToString(lowerCaseKeys)
-}
-
// IsInformational returns true if the response code is 1xx
func (h *APIResponse) IsInformational() bool {
return h.Response.StatusCode/100 == 1
diff --git a/pkg/bindings/containers/attach.go b/pkg/bindings/containers/attach.go
index baa3f182e..c02265cd8 100644
--- a/pkg/bindings/containers/attach.go
+++ b/pkg/bindings/containers/attach.go
@@ -108,9 +108,9 @@ func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Wri
}()
}
- headers := make(map[string]string)
- headers["Connection"] = "Upgrade"
- headers["Upgrade"] = "tcp"
+ headers := make(http.Header)
+ headers.Add("Connection", "Upgrade")
+ headers.Add("Upgrade", "tcp")
var socket net.Conn
socketSet := false
@@ -157,7 +157,7 @@ func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Wri
}
stdoutChan := make(chan error)
- stdinChan := make(chan error, 1) //stdin channel should not block
+ stdinChan := make(chan error, 1) // stdin channel should not block
if isSet.stdin {
go func() {
diff --git a/pkg/bindings/errors.go b/pkg/bindings/errors.go
index ec837b39c..be184b916 100644
--- a/pkg/bindings/errors.go
+++ b/pkg/bindings/errors.go
@@ -25,7 +25,7 @@ func (h APIResponse) Process(unmarshalInto interface{}) error {
return h.ProcessWithError(unmarshalInto, &errorhandling.ErrorModel{})
}
-// Process drains the response body, and processes the HTTP status code
+// ProcessWithError drains the response body, and processes the HTTP status code
// Note: Closing the response.Body is left to the caller
func (h APIResponse) ProcessWithError(unmarshalInto interface{}, unmarshalErrorInto interface{}) error {
data, err := ioutil.ReadAll(h.Response.Body)
diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go
index 6b5159f52..9880c73e4 100644
--- a/pkg/bindings/images/build.go
+++ b/pkg/bindings/images/build.go
@@ -300,7 +300,7 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
}
var (
- headers map[string]string
+ headers http.Header
err error
)
if options.SystemContext != nil && options.SystemContext.DockerAuthConfig != nil {
@@ -421,7 +421,7 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
return nil, err
}
- //add tmp file to context dir
+ // add tmp file to context dir
tarContent = append(tarContent, tmpSecretFile.Name())
modifiedSrc := fmt.Sprintf("src=%s", filepath.Base(tmpSecretFile.Name()))
@@ -634,7 +634,7 @@ func nTar(excludes []string, sources ...string) (io.ReadCloser, error) {
if lerr := tw.WriteHeader(hdr); lerr != nil {
return lerr
}
- } //skip other than file,folder and symlinks
+ } // skip other than file,folder and symlinks
return nil
})
merr = multierror.Append(merr, err)
diff --git a/pkg/bindings/internal/util/util.go b/pkg/bindings/internal/util/util.go
index bcf6959f2..f8f99d6c1 100644
--- a/pkg/bindings/internal/util/util.go
+++ b/pkg/bindings/internal/util/util.go
@@ -104,3 +104,11 @@ func ToParams(o interface{}) (url.Values, error) {
}
return params, nil
}
+
+func MapToArrayString(data map[string]string) []string {
+ l := make([]string, 0)
+ for k, v := range data {
+ l = append(l, k+"="+v)
+ }
+ return l
+}
diff --git a/pkg/bindings/manifests/manifests.go b/pkg/bindings/manifests/manifests.go
index af74eb406..50e324efa 100644
--- a/pkg/bindings/manifests/manifests.go
+++ b/pkg/bindings/manifests/manifests.go
@@ -3,15 +3,18 @@ package manifests
import (
"context"
"errors"
+ "fmt"
"net/http"
"net/url"
"strconv"
"strings"
+ "github.com/blang/semver"
"github.com/containers/image/v5/manifest"
"github.com/containers/podman/v3/pkg/api/handlers"
"github.com/containers/podman/v3/pkg/bindings"
"github.com/containers/podman/v3/pkg/bindings/images"
+ "github.com/containers/podman/v3/version"
jsoniter "github.com/json-iterator/go"
)
@@ -19,7 +22,7 @@ import (
// the new manifest can also be specified. The all boolean specifies to add all entries
// of a list if the name provided is a manifest list. The ID of the new manifest list
// is returned as a string.
-func Create(ctx context.Context, names, images []string, options *CreateOptions) (string, error) {
+func Create(ctx context.Context, name string, images []string, options *CreateOptions) (string, error) {
var idr handlers.IDResponse
if options == nil {
options = new(CreateOptions)
@@ -28,21 +31,19 @@ func Create(ctx context.Context, names, images []string, options *CreateOptions)
if err != nil {
return "", err
}
- if len(names) < 1 {
+ if len(name) < 1 {
return "", errors.New("creating a manifest requires at least one name argument")
}
params, err := options.ToParams()
if err != nil {
return "", err
}
- for _, name := range names {
- params.Add("name", name)
- }
+
for _, i := range images {
- params.Add("image", i)
+ params.Add("images", i)
}
- response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/manifests/create", params, nil)
+ response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/manifests/%s", params, nil, name)
if err != nil {
return "", err
}
@@ -67,70 +68,96 @@ func Exists(ctx context.Context, name string, options *ExistsOptions) (bool, err
}
// Inspect returns a manifest list for a given name.
-func Inspect(ctx context.Context, name string, options *InspectOptions) (*manifest.Schema2List, error) {
- var list manifest.Schema2List
- if options == nil {
- options = new(InspectOptions)
- }
- _ = options
+func Inspect(ctx context.Context, name string, _ *InspectOptions) (*manifest.Schema2List, error) {
conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
+
response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/manifests/%s/json", nil, nil, name)
if err != nil {
return nil, err
}
defer response.Body.Close()
+ var list manifest.Schema2List
return &list, response.Process(&list)
}
// Add adds a manifest to a given manifest list. Additional options for the manifest
// can also be specified. The ID of the new manifest list is returned as a string
func Add(ctx context.Context, name string, options *AddOptions) (string, error) {
- var idr handlers.IDResponse
if options == nil {
options = new(AddOptions)
}
+
+ if bindings.ServiceVersion(ctx).GTE(semver.MustParse("4.0.0")) {
+ optionsv4 := ModifyOptions{
+ All: options.All,
+ Annotations: options.Annotation,
+ Arch: options.Arch,
+ Features: options.Features,
+ Images: options.Images,
+ OS: options.OS,
+ OSFeatures: nil,
+ OSVersion: options.OSVersion,
+ Variant: options.Variant,
+ }
+ optionsv4.WithOperation("update")
+ return Modify(ctx, name, options.Images, &optionsv4)
+ }
+
+ // API Version < 4.0.0
conn, err := bindings.GetClient(ctx)
if err != nil {
return "", err
}
- optionsString, err := jsoniter.MarshalToString(options)
+ opts, err := jsoniter.MarshalToString(options)
if err != nil {
return "", err
}
- stringReader := strings.NewReader(optionsString)
- response, err := conn.DoRequest(ctx, stringReader, http.MethodPost, "/manifests/%s/add", nil, nil, name)
+ reader := strings.NewReader(opts)
+
+ headers := make(http.Header)
+ v := version.APIVersion[version.Libpod][version.MinimalAPI]
+ headers.Add("API-Version",
+ fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch))
+ response, err := conn.DoRequest(ctx, reader, http.MethodPost, "/manifests/%s/add", nil, headers, name)
if err != nil {
return "", err
}
defer response.Body.Close()
+ var idr handlers.IDResponse
return idr.ID, response.Process(&idr)
}
// Remove deletes a manifest entry from a manifest list. Both name and the digest to be
// removed are mandatory inputs. The ID of the new manifest list is returned as a string.
-func Remove(ctx context.Context, name, digest string, options *RemoveOptions) (string, error) {
- var idr handlers.IDResponse
- if options == nil {
- options = new(RemoveOptions)
+func Remove(ctx context.Context, name, digest string, _ *RemoveOptions) (string, error) {
+ if bindings.ServiceVersion(ctx).GTE(semver.MustParse("4.0.0")) {
+ optionsv4 := new(ModifyOptions).WithOperation("remove")
+ return Modify(ctx, name, []string{digest}, optionsv4)
}
- _ = options
+
+ // API Version < 4.0.0
conn, err := bindings.GetClient(ctx)
if err != nil {
return "", err
}
+
+ headers := http.Header{}
+ headers.Add("API-Version", "3.4.0")
+
params := url.Values{}
params.Set("digest", digest)
- response, err := conn.DoRequest(ctx, nil, http.MethodDelete, "/manifests/%s", params, nil, name)
+ response, err := conn.DoRequest(ctx, nil, http.MethodDelete, "/manifests/%s", params, headers, name)
if err != nil {
return "", err
}
defer response.Body.Close()
+ var idr handlers.IDResponse
return idr.ID, response.Process(&idr)
}
@@ -151,19 +178,26 @@ func Push(ctx context.Context, name, destination string, options *images.PushOpt
if err != nil {
return "", err
}
+
params, err := options.ToParams()
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
+ // ToParams() and change the key and flip the bool
if options.SkipTLSVerify != nil {
params.Del("SkipTLSVerify")
params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify()))
}
- params.Set("image", name)
- params.Set("destination", destination)
- response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/manifests/%s/push", params, nil, name)
+
+ var response *bindings.APIResponse
+ if bindings.ServiceVersion(ctx).GTE(semver.MustParse("4.0.0")) {
+ response, err = conn.DoRequest(ctx, nil, http.MethodPost, "/manifests/%s/registry/%s", params, nil, name, destination)
+ } else {
+ params.Set("image", name)
+ params.Set("destination", destination)
+ response, err = conn.DoRequest(ctx, nil, http.MethodPost, "/manifests/%s/push", params, nil, name)
+ }
if err != nil {
return "", err
}
@@ -172,25 +206,37 @@ func Push(ctx context.Context, name, destination string, options *images.PushOpt
return idr.ID, err
}
-// There is NO annotate endpoint. this binding could never work
-// Annotate updates the image configuration of a given manifest list
-//func Annotate(ctx context.Context, name, digest string, options image.ManifestAnnotateOpts) (string, error) {
-// var idr handlers.IDResponse
-// conn, err := bindings.GetClient(ctx)
-// if err != nil {
-// return "", err
-// }
-// params := url.Values{}
-// params.Set("digest", digest)
-// optionsString, err := jsoniter.MarshalToString(options)
-// if err != nil {
-// return "", err
-// }
-// stringReader := strings.NewReader(optionsString)
-// response, err := conn.DoRequest(ctx, stringReader, http.MethodPost, "/manifests/%s/annotate", params, name)
-// if err != nil {
-// return "", err
-// }
-// defer response.Body.Close()
-// return idr.ID, response.Process(&idr)
-//}
+// Modify modifies the given manifest list using options and the optional list of images
+func Modify(ctx context.Context, name string, images []string, options *ModifyOptions) (string, error) {
+ if options == nil || *options.Operation == "" {
+ return "", errors.New(`the field ModifyOptions.Operation must be set to either "update" or "remove"`)
+ }
+ options.WithImages(images)
+
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return "", err
+ }
+ opts, err := jsoniter.MarshalToString(options)
+ if err != nil {
+ return "", err
+ }
+ reader := strings.NewReader(opts)
+
+ response, err := conn.DoRequest(ctx, reader, http.MethodPut, "/manifests/%s", nil, nil, name)
+ if err != nil {
+ return "", err
+ }
+ defer response.Body.Close()
+
+ var idr handlers.IDResponse
+ return idr.ID, response.Process(&idr)
+}
+
+// Annotate modifies the given manifest list using options and the optional list of images
+//
+// As of 4.0.0
+func Annotate(ctx context.Context, name string, images []string, options *ModifyOptions) (string, error) {
+ options.WithOperation("annotate")
+ return Modify(ctx, name, images, options)
+}
diff --git a/pkg/bindings/manifests/types.go b/pkg/bindings/manifests/types.go
index fde90a865..5ff28ee30 100644
--- a/pkg/bindings/manifests/types.go
+++ b/pkg/bindings/manifests/types.go
@@ -18,7 +18,7 @@ type ExistsOptions struct {
}
//go:generate go run ../generator/generator.go AddOptions
-// AddOptions are optional options for adding manifests
+// AddOptions are optional options for adding manifest lists
type AddOptions struct {
All *bool
Annotation map[string]string
@@ -31,6 +31,24 @@ type AddOptions struct {
}
//go:generate go run ../generator/generator.go RemoveOptions
-// RemoveOptions are optional options for removing manifests
+// RemoveOptions are optional options for removing manifest lists
type RemoveOptions struct {
}
+
+//go:generate go run ../generator/generator.go ModifyOptions
+// ModifyOptions are optional options for modifying manifest lists
+type ModifyOptions struct {
+ // Operation values are "update", "remove" and "annotate". This allows the service to
+ // efficiently perform each update on a manifest list.
+ Operation *string
+ All *bool // All when true, operate on all images in a manifest list that may be included in Images
+ Annotations map[string]string // Annotations to add to manifest list
+ Arch *string // Arch overrides the architecture for the image
+ Features []string // Feature list for the image
+ Images []string // Images is an optional list of images to add/remove to/from manifest list depending on operation
+ OS *string // OS overrides the operating system for the image
+ OSFeatures []string // OS features for the image
+ OSVersion *string // OSVersion overrides the operating system for the image
+ Variant *string // Variant overrides the operating system variant for the image
+
+}
diff --git a/pkg/bindings/manifests/types_modify_options.go b/pkg/bindings/manifests/types_modify_options.go
new file mode 100644
index 000000000..ee5d94dbf
--- /dev/null
+++ b/pkg/bindings/manifests/types_modify_options.go
@@ -0,0 +1,168 @@
+// Code generated by go generate; DO NOT EDIT.
+package manifests
+
+import (
+ "net/url"
+
+ "github.com/containers/podman/v3/pkg/bindings/internal/util"
+)
+
+// Changed returns true if named field has been set
+func (o *ModifyOptions) Changed(fieldName string) bool {
+ return util.Changed(o, fieldName)
+}
+
+// ToParams formats struct fields to be passed to API service
+func (o *ModifyOptions) ToParams() (url.Values, error) {
+ return util.ToParams(o)
+}
+
+// WithOperation set field Operation to given value
+func (o *ModifyOptions) WithOperation(value string) *ModifyOptions {
+ o.Operation = &value
+ return o
+}
+
+// GetOperation returns value of field Operation
+func (o *ModifyOptions) GetOperation() string {
+ if o.Operation == nil {
+ var z string
+ return z
+ }
+ return *o.Operation
+}
+
+// WithAll set all when true, operate on all images in a manifest list that may be included in Images
+func (o *ModifyOptions) WithAll(value bool) *ModifyOptions {
+ o.All = &value
+ return o
+}
+
+// GetAll returns value of all when true, operate on all images in a manifest list that may be included in Images
+func (o *ModifyOptions) GetAll() bool {
+ if o.All == nil {
+ var z bool
+ return z
+ }
+ return *o.All
+}
+
+// WithAnnotations set annotations to add to manifest list
+func (o *ModifyOptions) WithAnnotations(value map[string]string) *ModifyOptions {
+ o.Annotations = value
+ return o
+}
+
+// GetAnnotations returns value of annotations to add to manifest list
+func (o *ModifyOptions) GetAnnotations() map[string]string {
+ if o.Annotations == nil {
+ var z map[string]string
+ return z
+ }
+ return o.Annotations
+}
+
+// WithArch set arch overrides the architecture for the image
+func (o *ModifyOptions) WithArch(value string) *ModifyOptions {
+ o.Arch = &value
+ return o
+}
+
+// GetArch returns value of arch overrides the architecture for the image
+func (o *ModifyOptions) GetArch() string {
+ if o.Arch == nil {
+ var z string
+ return z
+ }
+ return *o.Arch
+}
+
+// WithFeatures set feature list for the image
+func (o *ModifyOptions) WithFeatures(value []string) *ModifyOptions {
+ o.Features = value
+ return o
+}
+
+// GetFeatures returns value of feature list for the image
+func (o *ModifyOptions) GetFeatures() []string {
+ if o.Features == nil {
+ var z []string
+ return z
+ }
+ return o.Features
+}
+
+// WithImages set images is an optional list of images to add/remove to/from manifest list depending on operation
+func (o *ModifyOptions) WithImages(value []string) *ModifyOptions {
+ o.Images = value
+ return o
+}
+
+// GetImages returns value of images is an optional list of images to add/remove to/from manifest list depending on operation
+func (o *ModifyOptions) GetImages() []string {
+ if o.Images == nil {
+ var z []string
+ return z
+ }
+ return o.Images
+}
+
+// WithOS set oS overrides the operating system for the image
+func (o *ModifyOptions) WithOS(value string) *ModifyOptions {
+ o.OS = &value
+ return o
+}
+
+// GetOS returns value of oS overrides the operating system for the image
+func (o *ModifyOptions) GetOS() string {
+ if o.OS == nil {
+ var z string
+ return z
+ }
+ return *o.OS
+}
+
+// WithOSFeatures set oS features for the image
+func (o *ModifyOptions) WithOSFeatures(value []string) *ModifyOptions {
+ o.OSFeatures = value
+ return o
+}
+
+// GetOSFeatures returns value of oS features for the image
+func (o *ModifyOptions) GetOSFeatures() []string {
+ if o.OSFeatures == nil {
+ var z []string
+ return z
+ }
+ return o.OSFeatures
+}
+
+// WithOSVersion set oSVersion overrides the operating system for the image
+func (o *ModifyOptions) WithOSVersion(value string) *ModifyOptions {
+ o.OSVersion = &value
+ return o
+}
+
+// GetOSVersion returns value of oSVersion overrides the operating system for the image
+func (o *ModifyOptions) GetOSVersion() string {
+ if o.OSVersion == nil {
+ var z string
+ return z
+ }
+ return *o.OSVersion
+}
+
+// WithVariant set variant overrides the operating system variant for the image
+func (o *ModifyOptions) WithVariant(value string) *ModifyOptions {
+ o.Variant = &value
+ return o
+}
+
+// GetVariant returns value of variant overrides the operating system variant for the image
+func (o *ModifyOptions) GetVariant() string {
+ if o.Variant == nil {
+ var z string
+ return z
+ }
+ return *o.Variant
+}
diff --git a/pkg/bindings/test/manifests_test.go b/pkg/bindings/test/manifests_test.go
index e65632057..280006d15 100644
--- a/pkg/bindings/test/manifests_test.go
+++ b/pkg/bindings/test/manifests_test.go
@@ -12,7 +12,7 @@ import (
"github.com/onsi/gomega/gexec"
)
-var _ = Describe("Podman containers ", func() {
+var _ = Describe("podman manifest", func() {
var (
bt *bindingTest
s *gexec.Session
@@ -24,7 +24,8 @@ var _ = Describe("Podman containers ", func() {
s = bt.startAPIService()
time.Sleep(1 * time.Second)
err := bt.NewConnection()
- Expect(err).To(BeNil())
+ Expect(err).ToNot(HaveOccurred())
+
})
AfterEach(func() {
@@ -32,17 +33,19 @@ var _ = Describe("Podman containers ", func() {
bt.cleanup()
})
- It("create manifest", func() {
+ It("create", func() {
// create manifest list without images
- id, err := manifests.Create(bt.conn, []string{"quay.io/libpod/foobar:latest"}, []string{}, nil)
- Expect(err).To(BeNil())
+ id, err := manifests.Create(bt.conn, "quay.io/libpod/foobar:latest", []string{}, nil)
+ Expect(err).ToNot(HaveOccurred(), err)
list, err := manifests.Inspect(bt.conn, id, nil)
- Expect(err).To(BeNil())
+ Expect(err).ToNot(HaveOccurred())
+
Expect(len(list.Manifests)).To(BeZero())
// creating a duplicate should fail as a 500
- _, err = manifests.Create(bt.conn, []string{"quay.io/libpod/foobar:latest"}, []string{}, nil)
- Expect(err).ToNot(BeNil())
+ _, err = manifests.Create(bt.conn, "quay.io/libpod/foobar:latest", nil, nil)
+ Expect(err).To(HaveOccurred())
+
code, _ := bindings.CheckResponseCode(err)
Expect(code).To(BeNumerically("==", http.StatusInternalServerError))
@@ -50,96 +53,113 @@ var _ = Describe("Podman containers ", func() {
Expect(len(errs)).To(BeZero())
// create manifest list with images
- id, err = manifests.Create(bt.conn, []string{"quay.io/libpod/foobar:latest"}, []string{alpine.name}, nil)
- Expect(err).To(BeNil())
+ id, err = manifests.Create(bt.conn, "quay.io/libpod/foobar:latest", []string{alpine.name}, nil)
+ Expect(err).ToNot(HaveOccurred())
+
list, err = manifests.Inspect(bt.conn, id, nil)
- Expect(err).To(BeNil())
+ Expect(err).ToNot(HaveOccurred())
+
Expect(len(list.Manifests)).To(BeNumerically("==", 1))
})
- It("inspect bogus manifest", func() {
+ It("inspect", func() {
_, err := manifests.Inspect(bt.conn, "larry", nil)
- Expect(err).ToNot(BeNil())
+ Expect(err).To(HaveOccurred())
+
code, _ := bindings.CheckResponseCode(err)
Expect(code).To(BeNumerically("==", http.StatusNotFound))
})
- It("add manifest", func() {
+ It("add", func() {
// add to bogus should 404
_, err := manifests.Add(bt.conn, "foobar", nil)
- Expect(err).ToNot(BeNil())
+ Expect(err).To(HaveOccurred())
+
code, _ := bindings.CheckResponseCode(err)
- Expect(code).To(BeNumerically("==", http.StatusNotFound))
+ Expect(code).To(BeNumerically("==", http.StatusNotFound), err.Error())
+
+ id, err := manifests.Create(bt.conn, "quay.io/libpod/foobar:latest", []string{}, nil)
+ Expect(err).ToNot(HaveOccurred())
- id, err := manifests.Create(bt.conn, []string{"quay.io/libpod/foobar:latest"}, []string{}, nil)
- Expect(err).To(BeNil())
options := new(manifests.AddOptions).WithImages([]string{alpine.name})
_, err = manifests.Add(bt.conn, id, options)
- Expect(err).To(BeNil())
+ Expect(err).ToNot(HaveOccurred())
+
list, err := manifests.Inspect(bt.conn, id, nil)
- Expect(err).To(BeNil())
+ Expect(err).ToNot(HaveOccurred())
+
Expect(len(list.Manifests)).To(BeNumerically("==", 1))
// add bogus name to existing list should fail
options.WithImages([]string{"larry"})
_, err = manifests.Add(bt.conn, id, options)
- Expect(err).ToNot(BeNil())
+ Expect(err).To(HaveOccurred())
+
code, _ = bindings.CheckResponseCode(err)
Expect(code).To(BeNumerically("==", http.StatusInternalServerError))
})
- It("remove manifest", func() {
+ It("remove digest", func() {
// removal on bogus manifest list should be 404
_, err := manifests.Remove(bt.conn, "larry", "1234", nil)
- Expect(err).ToNot(BeNil())
+ Expect(err).To(HaveOccurred())
+
code, _ := bindings.CheckResponseCode(err)
Expect(code).To(BeNumerically("==", http.StatusNotFound))
- id, err := manifests.Create(bt.conn, []string{"quay.io/libpod/foobar:latest"}, []string{alpine.name}, nil)
- Expect(err).To(BeNil())
+ id, err := manifests.Create(bt.conn, "quay.io/libpod/foobar:latest", []string{alpine.name}, nil)
+ Expect(err).ToNot(HaveOccurred())
+
data, err := manifests.Inspect(bt.conn, id, nil)
- Expect(err).To(BeNil())
+ Expect(err).ToNot(HaveOccurred())
+
Expect(len(data.Manifests)).To(BeNumerically("==", 1))
// removal on a good manifest list with a bad digest should be 400
_, err = manifests.Remove(bt.conn, id, "!234", nil)
- Expect(err).ToNot(BeNil())
+ Expect(err).To(HaveOccurred())
+
code, _ = bindings.CheckResponseCode(err)
Expect(code).To(BeNumerically("==", http.StatusBadRequest))
digest := data.Manifests[0].Digest.String()
_, err = manifests.Remove(bt.conn, id, digest, nil)
- Expect(err).To(BeNil())
+ Expect(err).ToNot(HaveOccurred())
// removal on good manifest with good digest should work
data, err = manifests.Inspect(bt.conn, id, nil)
- Expect(err).To(BeNil())
- Expect(len(data.Manifests)).To(BeZero())
+ Expect(err).ToNot(HaveOccurred())
+
+ Expect(data.Manifests).Should(BeEmpty())
})
- // There is NO annotate endpoint, this could never work.:w
-
- //It("annotate manifest", func() {
- // id, err := manifests.Create(bt.conn, []string{"quay.io/libpod/foobar:latest"}, []string{}, nil)
- // Expect(err).To(BeNil())
- // opts := image.ManifestAddOpts{Images: []string{"docker.io/library/alpine:latest"}}
- //
- // _, err = manifests.Add(bt.conn, id, opts)
- // Expect(err).To(BeNil())
- // data, err := manifests.Inspect(bt.conn, id)
- // Expect(err).To(BeNil())
- // Expect(len(data.Manifests)).To(BeNumerically("==", 1))
- // digest := data.Manifests[0].Digest.String()
- // annoOpts := image.ManifestAnnotateOpts{OS: "foo"}
- // _, err = manifests.Annotate(bt.conn, id, digest, annoOpts)
- // Expect(err).To(BeNil())
- // list, err := manifests.Inspect(bt.conn, id)
- // Expect(err).To(BeNil())
- // Expect(len(list.Manifests)).To(BeNumerically("==", 1))
- // Expect(list.Manifests[0].Platform.OS).To(Equal("foo"))
- //})
+ It("annotate", func() {
+ id, err := manifests.Create(bt.conn, "quay.io/libpod/foobar:latest", []string{}, nil)
+ Expect(err).ToNot(HaveOccurred())
+
+ opts := manifests.AddOptions{Images: []string{"quay.io/libpod/alpine:latest"}}
+
+ _, err = manifests.Add(bt.conn, id, &opts)
+ Expect(err).ToNot(HaveOccurred())
+
+ data, err := manifests.Inspect(bt.conn, id, nil)
+ Expect(err).ToNot(HaveOccurred())
+
+ Expect(len(data.Manifests)).To(BeNumerically("==", 1))
+
+ digest := data.Manifests[0].Digest.String()
+ annoOpts := new(manifests.ModifyOptions).WithOS("foo")
+ _, err = manifests.Annotate(bt.conn, id, []string{digest}, annoOpts)
+ Expect(err).ToNot(HaveOccurred())
+
+ list, err := manifests.Inspect(bt.conn, id, nil)
+ Expect(err).ToNot(HaveOccurred())
+
+ Expect(len(list.Manifests)).To(BeNumerically("==", 1))
+ Expect(list.Manifests[0].Platform.OS).To(Equal("foo"))
+ })
It("push manifest", func() {
- Skip("TODO")
+ Skip("TODO: implement test for manifest push to registry")
})
})
diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go
index bec505163..bf9fcfd27 100644
--- a/pkg/domain/entities/engine_image.go
+++ b/pkg/domain/entities/engine_image.go
@@ -31,12 +31,12 @@ type ImageEngine interface {
Tree(ctx context.Context, nameOrID string, options ImageTreeOptions) (*ImageTreeReport, error)
Unmount(ctx context.Context, images []string, options ImageUnmountOptions) ([]*ImageUnmountReport, error)
Untag(ctx context.Context, nameOrID string, tags []string, options ImageUntagOptions) error
- ManifestCreate(ctx context.Context, names, images []string, opts ManifestCreateOptions) (string, error)
+ ManifestCreate(ctx context.Context, name string, images []string, opts ManifestCreateOptions) (string, error)
ManifestExists(ctx context.Context, name string) (*BoolReport, error)
ManifestInspect(ctx context.Context, name string) ([]byte, error)
- ManifestAdd(ctx context.Context, opts ManifestAddOptions) (string, error)
- ManifestAnnotate(ctx context.Context, names []string, opts ManifestAnnotateOptions) (string, error)
- ManifestRemove(ctx context.Context, names []string) (string, error)
+ ManifestAdd(ctx context.Context, listName string, imageNames []string, opts ManifestAddOptions) (string, error)
+ ManifestAnnotate(ctx context.Context, names, image string, opts ManifestAnnotateOptions) (string, error)
+ ManifestRemoveDigest(ctx context.Context, names, image string) (string, error)
ManifestRm(ctx context.Context, names []string) (*ImageRemoveReport, []error)
ManifestPush(ctx context.Context, name, destination string, imagePushOpts ImagePushOptions) (string, error)
Sign(ctx context.Context, names []string, options SignOptions) (*SignReport, error)
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index 62e7f67c8..bc41d7844 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -94,7 +94,7 @@ type ImageRemoveOptions struct {
LookupManifest bool
}
-// ImageRemoveResponse is the response for removing one or more image(s) from storage
+// ImageRemoveReport is the response for removing one or more image(s) from storage
// and images what was untagged vs actually removed.
type ImageRemoveReport struct {
// Deleted images.
diff --git a/pkg/domain/entities/manifest.go b/pkg/domain/entities/manifest.go
index 3f89e4d30..81f3e837b 100644
--- a/pkg/domain/entities/manifest.go
+++ b/pkg/domain/entities/manifest.go
@@ -2,35 +2,79 @@ package entities
import "github.com/containers/image/v5/types"
-// TODO: add comments to *all* types and fields.
-
+// ManifestCreateOptions provides model for creating manifest
type ManifestCreateOptions struct {
All bool `schema:"all"`
}
-// swagger:model ManifestAddOpts
+// ManifestAddOptions provides model for adding digests to manifest list
+//
+// swagger:model
type ManifestAddOptions struct {
- All bool `json:"all" schema:"all"`
- Annotation []string `json:"annotation" schema:"annotation"`
- Arch string `json:"arch" schema:"arch"`
- Authfile string `json:"-" schema:"-"`
- CertDir string `json:"-" schema:"-"`
- Features []string `json:"features" schema:"features"`
- Images []string `json:"images" schema:"images"`
- OS string `json:"os" schema:"os"`
- OSVersion string `json:"os_version" schema:"os_version"`
- Password string `json:"-" schema:"-"`
+ ManifestAnnotateOptions
+ // True when operating on a list to include all images
+ All bool `json:"all" schema:"all"`
+ // authfile to use when pushing manifest list
+ Authfile string `json:"-" schema:"-"`
+ // Home directory for certificates when pushing a manifest list
+ CertDir string `json:"-" schema:"-"`
+ // Password to authenticate to registry when pushing manifest list
+ Password string `json:"-" schema:"-"`
+ // Should TLS registry certificate be verified?
SkipTLSVerify types.OptionalBool `json:"-" schema:"-"`
- Username string `json:"-" schema:"-"`
- Variant string `json:"variant" schema:"variant"`
+ // Username to authenticate to registry when pushing manifest list
+ Username string `json:"-" schema:"-"`
+ // Images is an optional list of images to add to manifest list
+ Images []string `json:"images" schema:"images"`
}
+// ManifestAnnotateOptions provides model for annotating manifest list
type ManifestAnnotateOptions struct {
- Annotation []string `json:"annotation"`
- Arch string `json:"arch" schema:"arch"`
- Features []string `json:"features" schema:"features"`
- OS string `json:"os" schema:"os"`
+ // Annotation to add to manifest list
+ Annotation []string `json:"annotation" schema:"annotation"`
+ // Arch overrides the architecture for the image
+ Arch string `json:"arch" schema:"arch"`
+ // Feature list for the image
+ Features []string `json:"features" schema:"features"`
+ // OS overrides the operating system for the image
+ OS string `json:"os" schema:"os"`
+ // OS features for the image
OSFeatures []string `json:"os_features" schema:"os_features"`
- OSVersion string `json:"os_version" schema:"os_version"`
- Variant string `json:"variant" schema:"variant"`
+ // OSVersion overrides the operating system for the image
+ OSVersion string `json:"os_version" schema:"os_version"`
+ // Variant for the image
+ Variant string `json:"variant" schema:"variant"`
+}
+
+// ManifestModifyOptions provides the model for mutating a manifest
+//
+// swagger 2.0 does not support oneOf for schema validation.
+//
+// Operation "update" uses all fields.
+// Operation "remove" uses fields: Operation and Images
+// Operation "annotate" uses fields: Operation and Annotations
+//
+// swagger:model
+type ManifestModifyOptions struct {
+ Operation string `json:"operation" schema:"operation"` // Valid values: update, remove, annotate
+ ManifestAddOptions
+ ManifestRemoveOptions
+}
+
+// ManifestRemoveOptions provides the model for removing digests from a manifest
+//
+// swagger:model
+type ManifestRemoveOptions struct {
+}
+
+// ManifestModifyReport provides the model for removed digests and changed manifest
+//
+// swagger:model
+type ManifestModifyReport struct {
+ // Manifest List ID
+ ID string `json:"Id"`
+ // Images to removed from manifest list, otherwise not provided.
+ Images []string `json:"images,omitempty" schema:"images"`
+ // Errors associated with operation
+ Errors []error `json:"errors,omitempty"`
}
diff --git a/pkg/domain/infra/abi/images_list.go b/pkg/domain/infra/abi/images_list.go
index 2ec4ad244..47b47b065 100644
--- a/pkg/domain/infra/abi/images_list.go
+++ b/pkg/domain/infra/abi/images_list.go
@@ -26,9 +26,9 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions)
summaries := []*entities.ImageSummary{}
for _, img := range images {
- digests := make([]string, len(img.Digests()))
- for j, d := range img.Digests() {
- digests[j] = string(d)
+ repoDigests, err := img.RepoDigests()
+ if err != nil {
+ return nil, errors.Wrapf(err, "getting repoDigests from image %q", img.ID())
}
isDangling, err := img.IsDangling(ctx)
if err != nil {
@@ -37,11 +37,12 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions)
e := entities.ImageSummary{
ID: img.ID(),
- // ConfigDigest: string(img.ConfigDigest),
+ // TODO: libpod/image didn't set it but libimage should
+ // ConfigDigest: string(img.ConfigDigest),
Created: img.Created().Unix(),
Dangling: isDangling,
Digest: string(img.Digest()),
- RepoDigests: digests,
+ RepoDigests: repoDigests,
History: img.NamesHistory(),
Names: img.Names(),
ReadOnly: img.IsReadOnly(),
diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go
index d1bd5e2e4..d8733130b 100644
--- a/pkg/domain/infra/abi/manifest.go
+++ b/pkg/domain/infra/abi/manifest.go
@@ -22,13 +22,10 @@ import (
)
// ManifestCreate implements logic for creating manifest lists via ImageEngine
-func (ir *ImageEngine) ManifestCreate(ctx context.Context, names []string, images []string, opts entities.ManifestCreateOptions) (string, error) {
- // FIXME: change the interface of manifest create `names []string` ->
- // `name string`.
- if len(names) == 0 {
+func (ir *ImageEngine) ManifestCreate(ctx context.Context, name string, images []string, opts entities.ManifestCreateOptions) (string, error) {
+ if len(name) == 0 {
return "", errors.New("no name specified for creating a manifest list")
}
- name := names[0]
manifestList, err := ir.Libpod.LibimageRuntime().CreateManifestList(name)
if err != nil {
@@ -175,18 +172,12 @@ func (ir *ImageEngine) remoteManifestInspect(ctx context.Context, name string) (
}
// ManifestAdd adds images to the manifest list
-func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAddOptions) (string, error) {
- // FIXME: the name options below are *mandatory* arguments and should
- // be reflected as such in the signature.
-
- if len(opts.Images) < 2 {
- return "", errors.New("manifest add requires two images")
+func (ir *ImageEngine) ManifestAdd(ctx context.Context, name string, images []string, opts entities.ManifestAddOptions) (string, error) {
+ if len(images) < 1 {
+ return "", errors.New("manifest add requires at least one image")
}
- imageName := opts.Images[0]
- listName := opts.Images[1]
-
- manifestList, err := ir.Libpod.LibimageRuntime().LookupManifestList(listName)
+ manifestList, err := ir.Libpod.LibimageRuntime().LookupManifestList(name)
if err != nil {
return "", err
}
@@ -200,53 +191,46 @@ func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAd
Password: opts.Password,
}
- instanceDigest, err := manifestList.Add(ctx, imageName, addOptions)
- if err != nil {
- return "", err
- }
+ for _, image := range images {
+ instanceDigest, err := manifestList.Add(ctx, image, addOptions)
+ if err != nil {
+ return "", err
+ }
- annotateOptions := &libimage.ManifestListAnnotateOptions{
- Architecture: opts.Arch,
- Features: opts.Features,
- OS: opts.OS,
- OSVersion: opts.OSVersion,
- Variant: opts.Variant,
- }
- if len(opts.Annotation) != 0 {
- annotations := make(map[string]string)
- for _, annotationSpec := range opts.Annotation {
- spec := strings.SplitN(annotationSpec, "=", 2)
- if len(spec) != 2 {
- return "", errors.Errorf("no value given for annotation %q", spec[0])
+ annotateOptions := &libimage.ManifestListAnnotateOptions{
+ Architecture: opts.Arch,
+ Features: opts.Features,
+ OS: opts.OS,
+ OSVersion: opts.OSVersion,
+ Variant: opts.Variant,
+ }
+ if len(opts.Annotation) != 0 {
+ annotations := make(map[string]string)
+ for _, annotationSpec := range opts.Annotation {
+ spec := strings.SplitN(annotationSpec, "=", 2)
+ if len(spec) != 2 {
+ return "", errors.Errorf("no value given for annotation %q", spec[0])
+ }
+ annotations[spec[0]] = spec[1]
}
- annotations[spec[0]] = spec[1]
+ annotateOptions.Annotations = annotations
}
- annotateOptions.Annotations = annotations
- }
- if err := manifestList.AnnotateInstance(instanceDigest, annotateOptions); err != nil {
- return "", err
+ if err := manifestList.AnnotateInstance(instanceDigest, annotateOptions); err != nil {
+ return "", err
+ }
}
-
return manifestList.ID(), nil
}
// ManifestAnnotate updates an entry of the manifest list
-func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, names []string, opts entities.ManifestAnnotateOptions) (string, error) {
- // FIXME: the `names` are *mandatory* arguments and should be
- // reflected as such in the signature.
-
- if len(names) < 2 {
- return "", errors.New("manifest annotate requires two names")
- }
-
- listName := names[0]
- instanceDigest, err := digest.Parse(names[1])
+func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, name, image string, opts entities.ManifestAnnotateOptions) (string, error) {
+ instanceDigest, err := digest.Parse(image)
if err != nil {
- return "", errors.Errorf(`invalid image digest "%s": %v`, names[1], err)
+ return "", errors.Errorf(`invalid image digest "%s": %v`, image, err)
}
- manifestList, err := ir.Libpod.LibimageRuntime().LookupManifestList(listName)
+ manifestList, err := ir.Libpod.LibimageRuntime().LookupManifestList(name)
if err != nil {
return "", err
}
@@ -277,22 +261,14 @@ func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, names []string, opt
return manifestList.ID(), nil
}
-// ManifestRemove removes specified digest from the specified manifest list
-func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (string, error) {
- // FIXME: the `names` are *mandatory* arguments and should be
- // reflected as such in the signature.
-
- if len(names) < 2 {
- return "", errors.New("manifest remove requires two names")
- }
-
- listName := names[0]
- instanceDigest, err := digest.Parse(names[1])
+// ManifestRemoveDigest removes specified digest from the specified manifest list
+func (ir *ImageEngine) ManifestRemoveDigest(ctx context.Context, name, image string) (string, error) {
+ instanceDigest, err := digest.Parse(image)
if err != nil {
- return "", errors.Errorf(`invalid image digest "%s": %v`, names[1], err)
+ return "", errors.Errorf(`invalid image digest "%s": %v`, image, err)
}
- manifestList, err := ir.Libpod.LibimageRuntime().LookupManifestList(listName)
+ manifestList, err := ir.Libpod.LibimageRuntime().LookupManifestList(name)
if err != nil {
return "", err
}
diff --git a/pkg/domain/infra/runtime_abi.go b/pkg/domain/infra/runtime_abi.go
index 177e9cff4..ec7de2743 100644
--- a/pkg/domain/infra/runtime_abi.go
+++ b/pkg/domain/infra/runtime_abi.go
@@ -26,7 +26,7 @@ func NewContainerEngine(facts *entities.PodmanConfig) (entities.ContainerEngine,
return nil, fmt.Errorf("runtime mode '%v' is not supported", facts.EngineMode)
}
-// NewContainerEngine factory provides a libpod runtime for image-related operations
+// NewImageEngine factory provides a libpod runtime for image-related operations
func NewImageEngine(facts *entities.PodmanConfig) (entities.ImageEngine, error) {
switch facts.EngineMode {
case entities.ABIMode:
diff --git a/pkg/domain/infra/tunnel/manifest.go b/pkg/domain/infra/tunnel/manifest.go
index 62634f561..f89c59bc7 100644
--- a/pkg/domain/infra/tunnel/manifest.go
+++ b/pkg/domain/infra/tunnel/manifest.go
@@ -7,16 +7,16 @@ import (
"strings"
"github.com/containers/image/v5/types"
- images "github.com/containers/podman/v3/pkg/bindings/images"
+ "github.com/containers/podman/v3/pkg/bindings/images"
"github.com/containers/podman/v3/pkg/bindings/manifests"
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/pkg/errors"
)
// ManifestCreate implements manifest create via ImageEngine
-func (ir *ImageEngine) ManifestCreate(ctx context.Context, names, images []string, opts entities.ManifestCreateOptions) (string, error) {
+func (ir *ImageEngine) ManifestCreate(ctx context.Context, name string, images []string, opts entities.ManifestCreateOptions) (string, error) {
options := new(manifests.CreateOptions).WithAll(opts.All)
- imageID, err := manifests.Create(ir.ClientCtx, names, images, options)
+ imageID, err := manifests.Create(ir.ClientCtx, name, images, options)
if err != nil {
return imageID, errors.Wrapf(err, "error creating manifest")
}
@@ -33,7 +33,7 @@ func (ir *ImageEngine) ManifestExists(ctx context.Context, name string) (*entiti
}
// ManifestInspect returns contents of manifest list with given name
-func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte, error) {
+func (ir *ImageEngine) ManifestInspect(_ context.Context, name string) ([]byte, error) {
list, err := manifests.Inspect(ir.ClientCtx, name, nil)
if err != nil {
return nil, errors.Wrapf(err, "error getting content of manifest list or image %s", name)
@@ -47,9 +47,9 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte
}
// ManifestAdd adds images to the manifest list
-func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAddOptions) (string, error) {
+func (ir *ImageEngine) ManifestAdd(_ context.Context, name string, imageNames []string, opts entities.ManifestAddOptions) (string, error) {
options := new(manifests.AddOptions).WithAll(opts.All).WithArch(opts.Arch).WithVariant(opts.Variant)
- options.WithFeatures(opts.Features).WithImages(opts.Images).WithOS(opts.OS).WithOSVersion(opts.OSVersion)
+ options.WithFeatures(opts.Features).WithImages(imageNames).WithOS(opts.OS).WithOSVersion(opts.OSVersion)
if len(opts.Annotation) != 0 {
annotations := make(map[string]string)
for _, annotationSpec := range opts.Annotation {
@@ -62,25 +62,25 @@ func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAd
options.WithAnnotation(annotations)
}
- listID, err := manifests.Add(ir.ClientCtx, opts.Images[1], options)
+ id, err := manifests.Add(ir.ClientCtx, name, options)
if err != nil {
- return listID, errors.Wrapf(err, "error adding to manifest list %s", opts.Images[1])
+ return id, errors.Wrapf(err, "error adding to manifest list %s", name)
}
- return listID, nil
+ return id, nil
}
// ManifestAnnotate updates an entry of the manifest list
-func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, names []string, opts entities.ManifestAnnotateOptions) (string, error) {
+func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, name, images string, opts entities.ManifestAnnotateOptions) (string, error) {
return "", errors.New("not implemented")
}
-// ManifestRemove removes the digest from manifest list
-func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (string, error) {
- updatedListID, err := manifests.Remove(ir.ClientCtx, names[0], names[1], nil)
+// ManifestRemoveDigest removes the digest from manifest list
+func (ir *ImageEngine) ManifestRemoveDigest(ctx context.Context, name string, image string) (string, error) {
+ updatedListID, err := manifests.Remove(ir.ClientCtx, name, image, nil)
if err != nil {
- return updatedListID, errors.Wrapf(err, "error removing from manifest %s", names[0])
+ return updatedListID, errors.Wrapf(err, "error removing from manifest %s", name)
}
- return fmt.Sprintf("%s :%s\n", updatedListID, names[1]), nil
+ return fmt.Sprintf("%s :%s\n", updatedListID, image), nil
}
// ManifestRm removes the specified manifest list from storage
diff --git a/pkg/machine/ignition.go b/pkg/machine/ignition.go
index ac2cf71cf..09228553c 100644
--- a/pkg/machine/ignition.go
+++ b/pkg/machine/ignition.go
@@ -1,3 +1,4 @@
+//go:build amd64 || arm64
// +build amd64 arm64
package machine
@@ -423,61 +424,67 @@ func getCerts(certsDir string, isDir bool) []File {
files []File
)
- certs, err := ioutil.ReadDir(certsDir)
if isDir {
- if err == nil {
- for _, cert := range certs {
- b, err := ioutil.ReadFile(filepath.Join(certsDir, cert.Name()))
+ err := filepath.Walk(certsDir, func(path string, info os.FileInfo, err error) error {
+ if err == nil && !info.IsDir() {
+ certPath, err := filepath.Rel(certsDir, path)
if err != nil {
- logrus.Warnf("Unable to read cert file %s", err.Error())
- continue
+ logrus.Warnf("%s", err)
+ return nil
+ }
+
+ file, err := prepareCertFile(filepath.Join(certsDir, certPath), certPath)
+ if err == nil {
+ files = append(files, file)
}
- files = append(files, File{
- Node: Node{
- Group: getNodeGrp("root"),
- Path: filepath.Join("/etc/containers/certs.d/", cert.Name()),
- User: getNodeUsr("root"),
- },
- FileEmbedded1: FileEmbedded1{
- Append: nil,
- Contents: Resource{
- Source: encodeDataURLPtr(string(b)),
- },
- Mode: intToPtr(0644),
- },
- })
}
- } else {
+
+ return nil
+ })
+ if err != nil {
if !os.IsNotExist(err) {
logrus.Warnf("Unable to copy certs via ignition, error while reading certs from %s: %s", certsDir, err.Error())
}
}
} else {
fileName := filepath.Base(certsDir)
- b, err := ioutil.ReadFile(certsDir)
- if err != nil {
- logrus.Warnf("Unable to read cert file %s", err.Error())
- return files
+ file, err := prepareCertFile(certsDir, fileName)
+ if err == nil {
+ files = append(files, file)
}
- files = append(files, File{
- Node: Node{
- Group: getNodeGrp("root"),
- Path: filepath.Join("/etc/containers/certs.d/", fileName),
- User: getNodeUsr("root"),
- },
- FileEmbedded1: FileEmbedded1{
- Append: nil,
- Contents: Resource{
- Source: encodeDataURLPtr(string(b)),
- },
- Mode: intToPtr(0644),
- },
- })
}
return files
}
+func prepareCertFile(path string, name string) (File, error) {
+ b, err := ioutil.ReadFile(path)
+ if err != nil {
+ logrus.Warnf("Unable to read cert file %s", err.Error())
+ return File{}, err
+ }
+
+ targetPath := filepath.Join("/etc/containers/certs.d", name)
+
+ logrus.Debugf("Copying cert file from '%s' to '%s'.", path, targetPath)
+
+ file := File{
+ Node: Node{
+ Group: getNodeGrp("root"),
+ Path: targetPath,
+ User: getNodeUsr("root"),
+ },
+ FileEmbedded1: FileEmbedded1{
+ Append: nil,
+ Contents: Resource{
+ Source: encodeDataURLPtr(string(b)),
+ },
+ Mode: intToPtr(0644),
+ },
+ }
+ return file, nil
+}
+
func getProxyVariables() string {
proxyOpts := ""
for _, variable := range config.ProxyEnv {
diff --git a/pkg/specgenutil/util.go b/pkg/specgenutil/util.go
index 9389a98a5..0a980a576 100644
--- a/pkg/specgenutil/util.go
+++ b/pkg/specgenutil/util.go
@@ -38,21 +38,6 @@ func ReadPodIDFiles(files []string) ([]string, error) {
return ids, nil
}
-// ParseFilters transforms one filter format to another and validates input
-func ParseFilters(filter []string) (map[string][]string, error) {
- // TODO Remove once filter refactor is finished and url.Values done.
- filters := map[string][]string{}
- for _, f := range filter {
- t := strings.SplitN(f, "=", 2)
- filters = make(map[string][]string)
- if len(t) < 2 {
- return map[string][]string{}, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
- }
- filters[t[0]] = append(filters[t[0]], t[1])
- }
- return filters, nil
-}
-
// CreateExpose parses user-provided exposed port definitions and converts them
// into SpecGen format.
// TODO: The SpecGen format should really handle ranges more sanely - we could
diff --git a/.autocopr/podman.spec b/podman.spec.rpkg
index 45f6c14d8..cff5eb3d9 100644
--- a/.autocopr/podman.spec
+++ b/podman.spec.rpkg
@@ -1,4 +1,9 @@
-%global with_debug 0
+# For automatic rebuilds in COPR
+
+# The following tag is to get correct syntax highlighting for this file in vim text editor
+# vim: syntax=spec
+
+%global with_debug 1
%if 0%{?with_debug}
%global _find_debuginfo_dwz_opts %{nil}
@@ -7,6 +12,10 @@
%global debug_package %{nil}
%endif
+%if ! 0%{?gobuild:1}
+%define gobuild(o:) GO111MODULE=off go build -buildmode pie -compiler gc -tags="rpm_crashtraceback ${BUILDTAGS:-}" -ldflags "${LDFLAGS:-} -B 0x$(head -c20 /dev/urandom|od -An -tx1|tr -d ' \\n') -extldflags '-Wl,-z,relro -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld '" -a -v -x %{?**};
+%endif
+
%global provider github
%global provider_tld com
%global project containers
@@ -15,68 +24,37 @@
%global import_path %{provider}.%{provider_tld}/%{project}/%{repo}
%global git0 https://%{import_path}
-Name: podman
-Epoch: 100
-Version: 4
-%define build_datestamp %{lua: print(os.date("%Y%m%d"))}
-%define build_timestamp %{lua: print(os.date("%H%M%S"))}
-Release: %{build_datestamp}.%{build_timestamp}
+# git_dir_name returns repository name derived from remote Git repository URL
+Name: {{{ git_dir_name }}}
+
+Epoch: 101
+
+# git_dir_version returns version based on commit and tag history of the Git project
+Version: {{{ git_dir_version }}}
+
+# This can be useful later for adding downstream patches
+Release: 1%{?dist}
+
+# Basic description of the package
Summary: Manage Pods, Containers and Container Images
-License: ASL 2.0
-URL: https://%{name}.io/
-Source0: %{git0}/archive/main.tar.gz
-Provides: %{name}-manpages = %{epoch}:%{version}-%{release}
-%if 0%{?fedora} && ! 0%{?rhel}
-BuildRequires: btrfs-progs-devel
-%endif
-BuildRequires: gcc
-BuildRequires: golang >= 1.16.6
-BuildRequires: glib2-devel
-BuildRequires: glibc-devel
-BuildRequires: glibc-static
-BuildRequires: git-core
-BuildRequires: golang-github-cpuguy83-md2man
-BuildRequires: go-rpm-macros
-BuildRequires: gpgme-devel
-BuildRequires: libassuan-devel
-BuildRequires: libgpg-error-devel
-BuildRequires: libseccomp-devel
-BuildRequires: libselinux-devel
-%if 0%{?fedora} >= 35
-BuildRequires: shadow-utils-subid-devel
-%endif
-BuildRequires: pkgconfig
-BuildRequires: make
-BuildRequires: ostree-devel
-BuildRequires: systemd
-BuildRequires: systemd-devel
-Requires: conmon >= 2:2.0.30-2
-%if 0%{?fedora}
-Requires: containers-common >= 4:1-21
-%else
-Requires: containers-common >= 2:1-13
-%endif
-Requires: containernetworking-plugins >= 1.0.0-15.1
-Requires: iptables
-Requires: nftables
-Recommends: %{name}-plugins
-Recommends: catatonit
-Suggests: qemu-user-static
-%description
-%{name} (Pod Manager) is a fully featured container engine that is a simple
-daemonless tool. %{name} provides a Docker-CLI comparable command line that
-eases the transition from other container engines and allows the management of
-pods, containers and images. Simply put: alias docker=%{name}.
-Most %{name} commands can be run as a regular user, without requiring
-additional privileges.
+# License. We assume GPLv2+ here.
+License: ASL 2.0
-%{name} uses Buildah(1) internally to create container images.
-Both tools share image (not container) storage, hence each can use or
-manipulate images (but not containers) created by the other.
+# Home page of the project. Can also point to the public Git repository page.
+URL: https://github.com/containers/podman
-%{summary}
-%{repo} Simple management tool for pods, containers and images
+# Detailed information about the source Git repository and the source commit
+# for the created rpm package
+VCS: {{{ git_dir_vcs }}}
+
+# git_dir_pack macro places the repository content (the source files) into a tarball
+# and returns its filename. The tarball will be used to build the rpm.
+Source: {{{ git_dir_pack }}}
+
+# More detailed description of the package
+%description
+This is a hello world package.
%package docker
Summary: Emulate Docker CLI using %{name}
@@ -124,12 +102,55 @@ run %{name}-remote in production.
manage pods, containers and container images. %{name}-remote supports ssh
connections as well.
+# The following four sections already describe the rpm build process itself.
+# prep will extract the tarball defined as Source above and descend into it.
%prep
-%autosetup -n %{name}-main
+{{{ git_dir_setup_macro }}}
+# This will invoke `make` command in the directory with the extracted sources.
%build
-make all docker-docs
+%set_build_flags
+export GO111MODULE=off
+export GOPATH=$(pwd)/_build:$(pwd)
+export CGO_CFLAGS=$CFLAGS
+# These extra flags present in $CFLAGS have been skipped for now as they break the build
+CGO_CFLAGS=$(echo $CGO_CFLAGS | sed 's/-flto=auto//g')
+CGO_CFLAGS=$(echo $CGO_CFLAGS | sed 's/-Wp,D_GLIBCXX_ASSERTIONS//g')
+CGO_CFLAGS=$(echo $CGO_CFLAGS | sed 's/-specs=\/usr\/lib\/rpm\/redhat\/redhat-annobin-cc1//g')
+
+%ifarch x86_64
+export CGO_CFLAGS+=" -m64 -mtune=generic -fcf-protection=full"
+%endif
+mkdir _build
+pushd _build
+mkdir -p src/%{provider}.%{provider_tld}/%{project}
+ln -s ../../../../ src/%{import_path}
+popd
+ln -s vendor src
+
+# build date. FIXME: Makefile uses '/v2/libpod', that doesn't work here?
+LDFLAGS="-X %{import_path}/libpod/define.buildInfo=$(date +%s)"
+
+# build rootlessport first
+%gobuild -o bin/rootlessport %%{import_path}/cmd/rootlessport
+
+# build %%{name}
+export BUILDTAGS="seccomp exclude_graphdriver_devicemapper $(hack/btrfs_installed_tag.sh) $(hack/btrfs_tag.sh) $(hack/libdm_tag.sh) $(hack/selinux_tag.sh) $(hack/systemd_tag.sh)"
+%if 0%{?fedora} >= 35
+export BUILDTAGS+=" $(hack/libsubid_tag.sh)"
+%endif
+
+%gobuild -o bin/%{name} %{import_path}/cmd/%{name}
+
+# build %%{name}-remote
+export BUILDTAGS+=" exclude_graphdriver_btrfs btrfs_noversion remote"
+%gobuild -o bin/%{name}-remote %{import_path}/cmd/%{name}
+
+make docs docker-docs
+
+# This will copy the files generated by the `make` command above into
+# the installable rpm package.
%install
PODMAN_VERSION=%{version} %{__make} DESTDIR=%{buildroot} PREFIX=%{_prefix} ETCDIR=%{buildroot}%{_sysconfdir} \
install.bin-nobuild \
@@ -140,23 +161,18 @@ PODMAN_VERSION=%{version} %{__make} DESTDIR=%{buildroot} PREFIX=%{_prefix} ETCDI
install.docker-docs-nobuild \
install.remote-nobuild \
-mv pkg/hooks/README.md pkg/hooks/README-hooks.md
+install -d -p %{buildroot}/%{_datadir}/%{name}/test/system
+cp -pav test/system %{buildroot}/%{_datadir}/%{name}/test/
+mv pkg/hooks/README.md pkg/hooks/README-hooks.md
# do not include docker and podman-remote man pages in main package
for file in `find %{buildroot}%{_mandir}/man[15] -type f | sed "s,%{buildroot},," | grep -v -e remote -e docker`; do
echo "$file*" >> podman.file-list
done
-# install tests
-install -d -p %{buildroot}/%{_datadir}/%{name}/test/system
-cp -pav test/system %{buildroot}/%{_datadir}/%{name}/test/
-
-%check
-
-#define license tag if not already defined
-%{!?_licensedir:%global license %doc}
-
+# This lists all the files that are included in the rpm package and that
+# are going to be installed into target system where the rpm is installed.
%files -f %{name}.file-list
%license LICENSE
%doc README.md CONTRIBUTING.md pkg/hooks/README-hooks.md install.md transfer.md
@@ -201,10 +217,7 @@ cp -pav test/system %{buildroot}/%{_datadir}/%{name}/test/
%license LICENSE
%{_datadir}/%{name}/test
-%triggerpostun -- %{name} <= 3.2
-rm -f %{_sharedstatedir}/containers/storage/libpod/defaultCNINetExists
-exit 0
-
+# Finally, changes from the latest release of your application are generated from
+# your project's Git history. It will be empty until you make first annotated Git tag.
%changelog
-* Fri Dec 03 2021 Lokesh Mandvekar <lsm5@fedoraproject.org> - %{version}-%{release}
-- auto copr build
+{{{ git_dir_changelog }}}
diff --git a/rootless.md b/rootless.md
index bee5d337b..39c961d2a 100644
--- a/rootless.md
+++ b/rootless.md
@@ -25,11 +25,11 @@ can easily fail
* When a container root process like YUM attempts to create a file owned by a different UID, NFS Server/GPFS denies the creation.
* Does not work with homedirs mounted with noexec/nodev
* User can setup storage to point to other directories they can write to that are not mounted noexec/nodev
-* Can not use overlayfs driver, but does support fuse-overlayfs
- * Ubuntu supports non root overlay, but no other Linux distros do.
+* Support for using native overlayfs as an unprivileged user is only available for Podman version >= 3.1 on a Linux kernel version >= 5.12, otherwise the slower _fuse-overlayfs_ may be used.
+ * A few Linux distributions (e.g. Ubuntu) have supported even older Podman and Linux kernel versions by modifying the normal Linux kernel behaviour.
* Only other supported driver is VFS.
* Cannot use ping out of the box.
- * [(Can be fixed by setting sysctl on host)](https://github.com/containers/podman/blob/master/troubleshooting.md#5-rootless-containers-cannot-ping-hosts)
+ * [(Can be fixed by setting sysctl on host)](https://github.com/containers/podman/blob/main/troubleshooting.md#5-rootless-containers-cannot-ping-hosts)
* Requires new shadow-utils (not found in older (RHEL7/Centos7 distros) Should be fixed in RHEL7.7 release)
* A few commands do not work.
* mount/unmount (on fuse-overlay)
diff --git a/test/apiv2/15-manifest.at b/test/apiv2/15-manifest.at
new file mode 100644
index 000000000..0dd7026fa
--- /dev/null
+++ b/test/apiv2/15-manifest.at
@@ -0,0 +1,19 @@
+# -*- sh -*-
+#
+# Tests for manifest list endpoints
+
+t POST /v3.4.0/libpod/manifests/create?name=abc 200 \
+ .Id~[0-9a-f]\\{64\\}
+id_abc=$(jq -r '.Id' <<<"$output")
+
+t POST /v4.0.0/libpod/manifests/xyz 201 \
+ .Id~[0-9a-f]\\{64\\}
+echo xyz $output
+id_xyz=$(jq -r '.Id' <<<"$output")
+
+t GET /v3.4.0/libpod/manifests/$id_abc/exists 204
+t GET /v4.0.0/libpod/manifests/$id_xyz/exists 204
+
+# /v3.x cannot delete a manifest list
+t DELETE /v4.0.0/libpod/manifests/$id_abc 200
+t DELETE /v4.0.0/libpod/manifests/$id_xyz 200
diff --git a/test/apiv2/test-apiv2 b/test/apiv2/test-apiv2
index 391095539..19e8c12d0 100755
--- a/test/apiv2/test-apiv2
+++ b/test/apiv2/test-apiv2
@@ -364,7 +364,9 @@ function start_service() {
echo $WORKDIR
# Some tests use shortnames; force registry override to work around
# docker.io throttling.
- env CONTAINERS_REGISTRIES_CONF=$TESTS_DIR/../registries.conf $PODMAN_BIN \
+# FIXME esm revisit pulling expected images re: shortnames caused tests to fail
+# env CONTAINERS_REGISTRIES_CONF=$TESTS_DIR/../registries.conf
+ $PODMAN_BIN \
--root $WORKDIR/server_root --syslog=true \
system service \
--time 15 \
@@ -497,7 +499,7 @@ function wait_for_port() {
############
function podman() {
echo "\$ $PODMAN_BIN $*" >>$WORKDIR/output.log
- env CONTAINERS_REGISTRIES_CONF=$TESTS_DIR/../registries.conf \
+# env CONTAINERS_REGISTRIES_CONF=$TESTS_DIR/../registries.conf \
$PODMAN_BIN --root $WORKDIR/server_root "$@" >>$WORKDIR/output.log 2>&1
}
diff --git a/test/e2e/libpod_suite_remote_test.go b/test/e2e/libpod_suite_remote_test.go
index 4644e3748..53e1fc8f2 100644
--- a/test/e2e/libpod_suite_remote_test.go
+++ b/test/e2e/libpod_suite_remote_test.go
@@ -80,7 +80,7 @@ func (p *PodmanTestIntegration) StartRemoteService() {
args := []string{}
if _, found := os.LookupEnv("DEBUG_SERVICE"); found {
- args = append(args, "--log-level", "debug")
+ args = append(args, "--log-level", "trace")
}
remoteSocket := p.RemoteSocket
args = append(args, "system", "service", "--time", "0", remoteSocket)
@@ -151,7 +151,7 @@ func (p *PodmanTestIntegration) StopRemoteService() {
}
}
-//MakeOptions assembles all the podman main options
+// MakeOptions assembles all the podman main options
func getRemoteOptions(p *PodmanTestIntegration, args []string) []string {
podmanOptions := strings.Split(fmt.Sprintf("--root %s --runroot %s --runtime %s --conmon %s --cni-config-dir %s --cgroup-manager %s",
p.Root, p.RunRoot, p.OCIRuntime, p.ConmonBinary, p.CNIConfigDir, p.CgroupManager), " ")
diff --git a/test/e2e/manifest_test.go b/test/e2e/manifest_test.go
index 5978214ff..b86d5ecbe 100644
--- a/test/e2e/manifest_test.go
+++ b/test/e2e/manifest_test.go
@@ -44,19 +44,19 @@ var _ = Describe("Podman manifest", func() {
processTestResult(f)
})
- It("podman manifest create", func() {
+ It("create w/o image", func() {
session := podmanTest.Podman([]string{"manifest", "create", "foo"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
})
- It("podman manifest create", func() {
+ It("create w/ image", func() {
session := podmanTest.Podman([]string{"manifest", "create", "foo", imageList})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
})
- It("podman manifest inspect", func() {
+ It("inspect", func() {
session := podmanTest.Podman([]string{"manifest", "inspect", BB})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
@@ -71,29 +71,27 @@ var _ = Describe("Podman manifest", func() {
Expect(session).Should(Exit(0))
})
- It("podman manifest add", func() {
+ It("add w/ inspect", func() {
session := podmanTest.Podman([]string{"manifest", "create", "foo"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- session = podmanTest.Podman([]string{"manifest", "add", "--arch=arm64", "foo", imageListInstance})
- session.WaitWithDefaultTimeout()
- Expect(session).Should(Exit(0))
- })
+ id := strings.TrimSpace(string(session.Out.Contents()))
- It("podman manifest add one", func() {
- session := podmanTest.Podman([]string{"manifest", "create", "foo"})
+ session = podmanTest.Podman([]string{"manifest", "inspect", id})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
+
session = podmanTest.Podman([]string{"manifest", "add", "--arch=arm64", "foo", imageListInstance})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
+
session = podmanTest.Podman([]string{"manifest", "inspect", "foo"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
Expect(session.OutputToString()).To(ContainSubstring(imageListARM64InstanceDigest))
})
- It("podman manifest tag", func() {
+ It("tag", func() {
session := podmanTest.Podman([]string{"manifest", "create", "foobar"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
@@ -112,7 +110,7 @@ var _ = Describe("Podman manifest", func() {
Expect(session2.OutputToString()).To(Equal(session.OutputToString()))
})
- It("podman manifest add --all", func() {
+ It(" add --all", func() {
session := podmanTest.Podman([]string{"manifest", "create", "foo"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
@@ -122,14 +120,17 @@ var _ = Describe("Podman manifest", func() {
session = podmanTest.Podman([]string{"manifest", "inspect", "foo"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- Expect(session.OutputToString()).To(ContainSubstring(imageListAMD64InstanceDigest))
- Expect(session.OutputToString()).To(ContainSubstring(imageListARMInstanceDigest))
- Expect(session.OutputToString()).To(ContainSubstring(imageListARM64InstanceDigest))
- Expect(session.OutputToString()).To(ContainSubstring(imageListPPC64LEInstanceDigest))
- Expect(session.OutputToString()).To(ContainSubstring(imageListS390XInstanceDigest))
+ Expect(session.OutputToString()).To(
+ And(
+ ContainSubstring(imageListAMD64InstanceDigest),
+ ContainSubstring(imageListARMInstanceDigest),
+ ContainSubstring(imageListARM64InstanceDigest),
+ ContainSubstring(imageListPPC64LEInstanceDigest),
+ ContainSubstring(imageListS390XInstanceDigest),
+ ))
})
- It("podman manifest add --os", func() {
+ It("add --os", func() {
session := podmanTest.Podman([]string{"manifest", "create", "foo"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
@@ -142,7 +143,7 @@ var _ = Describe("Podman manifest", func() {
Expect(session.OutputToString()).To(ContainSubstring(`"os": "bar"`))
})
- It("podman manifest annotate", func() {
+ It("annotate", func() {
SkipIfRemote("Not supporting annotate on remote connections")
session := podmanTest.Podman([]string{"manifest", "create", "foo"})
session.WaitWithDefaultTimeout()
@@ -159,7 +160,7 @@ var _ = Describe("Podman manifest", func() {
Expect(session.OutputToString()).To(ContainSubstring(`"architecture": "bar"`))
})
- It("podman manifest remove", func() {
+ It("remove digest", func() {
session := podmanTest.Podman([]string{"manifest", "create", "foo"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
@@ -176,14 +177,18 @@ var _ = Describe("Podman manifest", func() {
session = podmanTest.Podman([]string{"manifest", "inspect", "foo"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- Expect(session.OutputToString()).To(ContainSubstring(imageListAMD64InstanceDigest))
- Expect(session.OutputToString()).To(ContainSubstring(imageListARMInstanceDigest))
- Expect(session.OutputToString()).To(ContainSubstring(imageListPPC64LEInstanceDigest))
- Expect(session.OutputToString()).To(ContainSubstring(imageListS390XInstanceDigest))
- Expect(session.OutputToString()).To(Not(ContainSubstring(imageListARM64InstanceDigest)))
+ Expect(session.OutputToString()).To(
+ And(
+ ContainSubstring(imageListAMD64InstanceDigest),
+ ContainSubstring(imageListARMInstanceDigest),
+ ContainSubstring(imageListPPC64LEInstanceDigest),
+ ContainSubstring(imageListS390XInstanceDigest),
+ Not(
+ ContainSubstring(imageListARM64InstanceDigest)),
+ ))
})
- It("podman manifest remove not-found", func() {
+ It("remove not-found", func() {
session := podmanTest.Podman([]string{"manifest", "create", "foo"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
@@ -199,7 +204,7 @@ var _ = Describe("Podman manifest", func() {
Expect(session).Should(Exit(0))
})
- It("podman manifest push", func() {
+ It("push --all", func() {
SkipIfRemote("manifest push to dir not supported in remote mode")
session := podmanTest.Podman([]string{"manifest", "create", "foo"})
session.WaitWithDefaultTimeout()
@@ -216,20 +221,24 @@ var _ = Describe("Podman manifest", func() {
session = podmanTest.Podman([]string{"manifest", "push", "--all", "foo", "dir:" + dest})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
+
files, err := filepath.Glob(dest + string(os.PathSeparator) + "*")
- Expect(err).To(BeNil())
+ Expect(err).ShouldNot(HaveOccurred())
check := SystemExec("sha256sum", files)
check.WaitWithDefaultTimeout()
Expect(check).Should(Exit(0))
prefix := "sha256:"
- Expect(check.OutputToString()).To(ContainSubstring(strings.TrimPrefix(imageListAMD64InstanceDigest, prefix)))
- Expect(check.OutputToString()).To(ContainSubstring(strings.TrimPrefix(imageListARMInstanceDigest, prefix)))
- Expect(check.OutputToString()).To(ContainSubstring(strings.TrimPrefix(imageListPPC64LEInstanceDigest, prefix)))
- Expect(check.OutputToString()).To(ContainSubstring(strings.TrimPrefix(imageListS390XInstanceDigest, prefix)))
- Expect(check.OutputToString()).To(ContainSubstring(strings.TrimPrefix(imageListARM64InstanceDigest, prefix)))
+ Expect(check.OutputToString()).To(
+ And(
+ ContainSubstring(strings.TrimPrefix(imageListAMD64InstanceDigest, prefix)),
+ ContainSubstring(strings.TrimPrefix(imageListARMInstanceDigest, prefix)),
+ ContainSubstring(strings.TrimPrefix(imageListPPC64LEInstanceDigest, prefix)),
+ ContainSubstring(strings.TrimPrefix(imageListS390XInstanceDigest, prefix)),
+ ContainSubstring(strings.TrimPrefix(imageListARM64InstanceDigest, prefix)),
+ ))
})
- It("podman push --all", func() {
+ It("push", func() {
SkipIfRemote("manifest push to dir not supported in remote mode")
session := podmanTest.Podman([]string{"manifest", "create", "foo"})
session.WaitWithDefaultTimeout()
@@ -251,15 +260,19 @@ var _ = Describe("Podman manifest", func() {
check := SystemExec("sha256sum", files)
check.WaitWithDefaultTimeout()
Expect(check).Should(Exit(0))
+
prefix := "sha256:"
- Expect(check.OutputToString()).To(ContainSubstring(strings.TrimPrefix(imageListAMD64InstanceDigest, prefix)))
- Expect(check.OutputToString()).To(ContainSubstring(strings.TrimPrefix(imageListARMInstanceDigest, prefix)))
- Expect(check.OutputToString()).To(ContainSubstring(strings.TrimPrefix(imageListPPC64LEInstanceDigest, prefix)))
- Expect(check.OutputToString()).To(ContainSubstring(strings.TrimPrefix(imageListS390XInstanceDigest, prefix)))
- Expect(check.OutputToString()).To(ContainSubstring(strings.TrimPrefix(imageListARM64InstanceDigest, prefix)))
+ Expect(check.OutputToString()).To(
+ And(
+ ContainSubstring(strings.TrimPrefix(imageListAMD64InstanceDigest, prefix)),
+ ContainSubstring(strings.TrimPrefix(imageListARMInstanceDigest, prefix)),
+ ContainSubstring(strings.TrimPrefix(imageListPPC64LEInstanceDigest, prefix)),
+ ContainSubstring(strings.TrimPrefix(imageListS390XInstanceDigest, prefix)),
+ ContainSubstring(strings.TrimPrefix(imageListARM64InstanceDigest, prefix)),
+ ))
})
- It("podman manifest push --rm", func() {
+ It("push --rm", func() {
SkipIfRemote("remote does not support --rm")
session := podmanTest.Podman([]string{"manifest", "create", "foo"})
session.WaitWithDefaultTimeout()
@@ -285,7 +298,7 @@ var _ = Describe("Podman manifest", func() {
Expect(session).To(ExitWithError())
})
- It("podman manifest exists", func() {
+ It("exists", func() {
manifestList := "manifest-list"
session := podmanTest.Podman([]string{"manifest", "create", manifestList})
session.WaitWithDefaultTimeout()
@@ -300,7 +313,7 @@ var _ = Describe("Podman manifest", func() {
Expect(session).Should(Exit(1))
})
- It("podman manifest rm should not remove referenced images", func() {
+ It("rm should not remove referenced images", func() {
manifestList := "manifestlist"
imageName := "quay.io/libpod/busybox"
@@ -320,11 +333,9 @@ var _ = Describe("Podman manifest", func() {
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- //image should still show up
- session = podmanTest.Podman([]string{"images"})
+ // image should still show up
+ session = podmanTest.Podman([]string{"image", "exists", imageName})
session.WaitWithDefaultTimeout()
- Expect(session.OutputToString()).To(ContainSubstring(imageName))
Expect(session).Should(Exit(0))
})
-
})
diff --git a/test/system/030-run.bats b/test/system/030-run.bats
index afcda3d3c..2d5ecab39 100644
--- a/test/system/030-run.bats
+++ b/test/system/030-run.bats
@@ -818,4 +818,11 @@ EOF
is "$output" ".*The --kernel-memory flag is no longer supported. This flag is a noop." "warn on use of --kernel-memory"
}
+
+# rhbz#1902979 : podman run fails to update /etc/hosts when --uidmap is provided
+@test "podman run update /etc/hosts" {
+ HOST=$(random_string 25)
+ run_podman run --uidmap 0:10001:10002 --rm --hostname ${HOST} $IMAGE grep ${HOST} /etc/hosts
+ is "${lines[0]}" ".*${HOST}.*"
+}
# vim: filetype=sh
diff --git a/test/system/070-build.bats b/test/system/070-build.bats
index 5e920506d..d5f7365e8 100644
--- a/test/system/070-build.bats
+++ b/test/system/070-build.bats
@@ -194,7 +194,7 @@ EOF
cat >$tmpdir/Dockerfile <<EOF
FROM $IMAGE
-ADD https://github.com/containers/podman/blob/master/README.md /tmp/
+ADD https://github.com/containers/podman/blob/main/README.md /tmp/
EOF
run_podman build -t add_url $tmpdir
run_podman run --rm add_url stat /tmp/README.md