aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/common/create.go2
-rw-r--r--contrib/helloimage/Containerfile13
-rw-r--r--contrib/helloimage/README.md28
-rwxr-xr-xcontrib/helloimage/podman_hello_world.bash23
-rw-r--r--contrib/helloimage/podman_hello_world.c26
-rw-r--r--go.mod2
-rw-r--r--go.sum3
-rw-r--r--libpod/container_internal_linux.go134
-rw-r--r--libpod/networking_linux.go48
-rw-r--r--libpod/runtime_ctr.go5
-rw-r--r--pkg/api/handlers/libpod/networks.go66
-rw-r--r--pkg/api/server/register_networks.go2
-rw-r--r--pkg/bindings/images/build.go2
-rw-r--r--pkg/bindings/test/networks_test.go4
-rw-r--r--pkg/checkpoint/checkpoint_restore.go7
-rw-r--r--pkg/machine/fcos.go57
-rw-r--r--test/apiv2/35-networks.at5
-rwxr-xr-xtest/apiv2/test-apiv210
-rw-r--r--test/e2e/checkpoint_test.go4
-rw-r--r--test/e2e/common_test.go14
-rw-r--r--test/e2e/network_connect_disconnect_test.go31
-rw-r--r--test/e2e/network_create_test.go22
-rw-r--r--test/e2e/network_test.go57
-rw-r--r--test/e2e/run_networking_test.go91
-rw-r--r--test/system/070-build.bats28
-rw-r--r--troubleshooting.md231
-rw-r--r--vendor/github.com/containers/common/libimage/copier.go7
-rw-r--r--vendor/github.com/containers/common/libimage/manifests/manifests.go12
-rw-r--r--vendor/github.com/containers/common/libnetwork/cni/cni_conversion.go29
-rw-r--r--vendor/github.com/containers/common/libnetwork/cni/config.go2
-rw-r--r--vendor/github.com/containers/common/libnetwork/internal/util/validate.go6
-rw-r--r--vendor/github.com/containers/common/libnetwork/netavark/config.go9
-rw-r--r--vendor/github.com/containers/common/libnetwork/netavark/network.go4
-rw-r--r--vendor/github.com/containers/common/pkg/auth/auth.go2
-rw-r--r--vendor/github.com/containers/common/version/version.go2
-rw-r--r--vendor/github.com/coreos/stream-metadata-go/release/release.go112
-rw-r--r--vendor/github.com/coreos/stream-metadata-go/release/rhcos/rhcos.go14
-rw-r--r--vendor/github.com/coreos/stream-metadata-go/release/translate.go196
-rw-r--r--vendor/modules.txt4
39 files changed, 1184 insertions, 130 deletions
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go
index 5fefbacdf..1121806d5 100644
--- a/cmd/podman/common/create.go
+++ b/cmd/podman/common/create.go
@@ -36,7 +36,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
createFlags.StringSliceVar(
&cf.Annotation,
annotationFlagName, []string{},
- "Add annotations to container (key:value)",
+ "Add annotations to container (key=value)",
)
_ = cmd.RegisterFlagCompletionFunc(annotationFlagName, completion.AutocompleteNone)
diff --git a/contrib/helloimage/Containerfile b/contrib/helloimage/Containerfile
index bea71cae0..0cbf6d9a0 100644
--- a/contrib/helloimage/Containerfile
+++ b/contrib/helloimage/Containerfile
@@ -1,8 +1,11 @@
-FROM registry.access.redhat.com/ubi8-micro:latest
+FROM docker.io/alpine as builder
+RUN apk add gcc libc-dev
+ADD podman_hello_world.c .
+RUN gcc -O2 -static -o podman_hello_world podman_hello_world.c
+FROM scratch
LABEL maintainer="Podman Maintainers"
LABEL artist="Máirín Ní Ḋuḃṫaiġ, Twitter:@mairin"
-WORKDIR /tmp
-
-COPY podman_hello_world.bash .
-ENTRYPOINT ./podman_hello_world.bash
+USER 1000
+COPY --from=builder podman_hello_world /usr/local/bin/podman_hello_world
+CMD ["/usr/local/bin/podman_hello_world"]
diff --git a/contrib/helloimage/README.md b/contrib/helloimage/README.md
index 93edcc527..ca69f87b4 100644
--- a/contrib/helloimage/README.md
+++ b/contrib/helloimage/README.md
@@ -19,7 +19,7 @@ Using this image is helpful to:
The contents of this directory contain:
* ./Containerfile
- * ./podman_hello_world.bash
+ * ./podman_hello_world.c
## Sample Usage
@@ -28,7 +28,7 @@ To simply run the image:
```
podman run quay.io/podman/hello
-! ... Hello Podman World ...!
+!... Hello Podman World ...!
.--"--.
/ - - \
@@ -49,7 +49,29 @@ To build the image yourself, copy the files from this directory into
a local directory and issue these commands:
```
-chmod 755 ./podman_hello_world.bash
podman build -t myhello .
podman run myhello
```
+
+## Potential Issues:
+
+The image runs as a rootless user with the UID set to `1000`.
+If the /etc/subuid and /etch/subgid values are not set appropriately to run as a
+rootless user on the host, an error like this might be raised:
+
+```
+Copying blob acab339ca1e8 done
+ERRO[0002] Error while applying layer: ApplyLayer exit status 1 stdout: stderr: potentially insufficient UIDs or GIDs available in user namespace (requested 0:12 for /var/spool/mail): Check /etc/subuid and /etc/subgid: lchown /var/spool/mail: invalid argument
+Error: writing blob: adding layer with blob "sha256:ee0cde9de8a68f171a8c03b0e9954abf18576947e2f3187e84d8c31ccd8f6a09": ApplyLayer exit status 1 stdout: stderr: potentially insufficient UIDs or GIDs available in user namespace (requested 0:12 for /var/spool/mail): Check /etc/subuid and /etc/subgid: lchown /var/spool/mail: invalid argument
+```
+
+Please refer to this [blog post](https://www.redhat.com/sysadmin/rootless-podman) for further configuration information.
+
+## THANKS!
+
+Many Thanks to @afbjorklund for a great discussion during the
+first revision of this container image that resulted in moving
+from using bash to using C, and the ensuing changes to the
+Containerfile.
+
+Also many thanks to @mairin for the awesome ASCII art!
diff --git a/contrib/helloimage/podman_hello_world.bash b/contrib/helloimage/podman_hello_world.bash
deleted file mode 100755
index c28141174..000000000
--- a/contrib/helloimage/podman_hello_world.bash
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/sh
-###
-# ASCII art by the incomparable Máirín Duffy,
-# duffy@redhat.com, Twitter: @mairin
-# January 2022
-###
-echo " "
-echo "! ... Hello Podman World ...!"
-echo " "
-echo " .--\"--. "
-echo " / - - \\ "
-echo " / (O) (O) \\ "
-echo " ~~~| -=(,Y,)=- | "
-echo " .---. /\` \\ |~~ "
-echo " ~/ o o \\~~~~.----. ~~ "
-echo " | =(X)= |~ / (O (O) \\ "
-echo " ~~~~~~~ ~| =(Y_)=- | "
-echo " ~~~~ ~~~| U |~~ "
-echo ""
-echo "Project: https://github.com/containers/podman"
-echo "Website: https://podman.io"
-echo "Documents: https://docs.podman.io"
-echo "Twitter: @Podman_io"
diff --git a/contrib/helloimage/podman_hello_world.c b/contrib/helloimage/podman_hello_world.c
new file mode 100644
index 000000000..ee574130d
--- /dev/null
+++ b/contrib/helloimage/podman_hello_world.c
@@ -0,0 +1,26 @@
+//###
+// ASCII art by the incomparable Máirín Duffy,
+// duffy@redhat.com, Twitter: @mairin
+// January 2022
+//###
+
+#include <stdio.h>
+int main() {
+ puts("\
+!... Hello Podman World ...!\n\
+\n\
+ .--\"--. \n\
+ / - - \\ \n\
+ / (O) (O) \\ \n\
+ ~~~| -=(,Y,)=- | \n\
+ .---. /` \\ |~~ \n\
+ ~/ o o \\~~~~.----. ~~ \n\
+ | =(X)= |~ / (O (O) \\ \n\
+ ~~~~~~~ ~| =(Y_)=- | \n\
+ ~~~~ ~~~| U |~~ \n\
+\n\
+Project: https://github.com/containers/podman\n\
+Website: https://podman.io\n\
+Documents: https://docs.podman.io\n\
+Twitter: @Podman_io");
+}
diff --git a/go.mod b/go.mod
index 789c53f15..79880469b 100644
--- a/go.mod
+++ b/go.mod
@@ -12,7 +12,7 @@ require (
github.com/containernetworking/cni v1.0.1
github.com/containernetworking/plugins v1.0.1
github.com/containers/buildah v1.24.1
- github.com/containers/common v0.47.3
+ github.com/containers/common v0.47.4
github.com/containers/conmon v2.0.20+incompatible
github.com/containers/image/v5 v5.19.1
github.com/containers/ocicrypt v1.1.2
diff --git a/go.sum b/go.sum
index 15565d743..b291e068e 100644
--- a/go.sum
+++ b/go.sum
@@ -325,8 +325,9 @@ github.com/containernetworking/plugins v1.0.1 h1:wwCfYbTCj5FC0EJgyzyjTXmqysOiJE9
github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE=
github.com/containers/buildah v1.24.1 h1:PlvU0hbUsm1x4H9kPcsmqjViqDGnBpSZT3QtZ00RtgI=
github.com/containers/buildah v1.24.1/go.mod h1:sE7AaoPQYwAB7dleOOKOpzOO3bA8lRUvZRiZcn/RYi0=
-github.com/containers/common v0.47.3 h1:pRT7gkLrBSQe3075j5hoHYeeKpGTWBJHws+tS5xxfak=
github.com/containers/common v0.47.3/go.mod h1:/VAV4ibC27Lfyb9cxXM4uTYrJFa/7s+utNB052MJdzY=
+github.com/containers/common v0.47.4 h1:kS202Z/bTQIM/pwyuJ+lF8143Uli6AB9Q9OVR0xa9CM=
+github.com/containers/common v0.47.4/go.mod h1:HgX0mFXyB0Tbe2REEIp9x9CxET6iSzmHfwR6S/t2LZc=
github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/image/v5 v5.19.1 h1:g4/+XIuh1kRoRn2MfLDhfHhkNOIO9JtqhSyo55tjpfE=
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 95f1634a8..afa351c17 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -2045,19 +2045,8 @@ func (c *Container) generateResolvConf() (string, error) {
}
}
- ipv6 := false
- // If network status is set check for ipv6 and dns namesevers
netStatus := c.getNetworkStatus()
for _, status := range netStatus {
- for _, netInt := range status.Interfaces {
- for _, netAddress := range netInt.Subnets {
- // Note: only using To16() does not work since it also returns a valid ip for ipv4
- if netAddress.IPNet.IP.To4() == nil && netAddress.IPNet.IP.To16() != nil {
- ipv6 = true
- }
- }
- }
-
if status.DNSServerIPs != nil {
for _, nsIP := range status.DNSServerIPs {
networkNameServers = append(networkNameServers, nsIP.String())
@@ -2070,16 +2059,9 @@ func (c *Container) generateResolvConf() (string, error) {
}
}
- if c.config.NetMode.IsSlirp4netns() {
- ctrNetworkSlipOpts := []string{}
- if c.config.NetworkOptions != nil {
- ctrNetworkSlipOpts = append(ctrNetworkSlipOpts, c.config.NetworkOptions["slirp4netns"]...)
- }
- slirpOpts, err := parseSlirp4netnsNetworkOptions(c.runtime, ctrNetworkSlipOpts)
- if err != nil {
- return "", err
- }
- ipv6 = slirpOpts.enableIPv6
+ ipv6, err := c.checkForIPv6(netStatus)
+ if err != nil {
+ return "", err
}
// Ensure that the container's /etc/resolv.conf is compatible with its
@@ -2160,6 +2142,116 @@ func (c *Container) generateResolvConf() (string, error) {
return destPath, nil
}
+// Check if a container uses IPv6.
+func (c *Container) checkForIPv6(netStatus map[string]types.StatusBlock) (bool, error) {
+ for _, status := range netStatus {
+ for _, netInt := range status.Interfaces {
+ for _, netAddress := range netInt.Subnets {
+ // Note: only using To16() does not work since it also returns a valid ip for ipv4
+ if netAddress.IPNet.IP.To4() == nil && netAddress.IPNet.IP.To16() != nil {
+ return true, nil
+ }
+ }
+ }
+ }
+
+ if c.config.NetMode.IsSlirp4netns() {
+ ctrNetworkSlipOpts := []string{}
+ if c.config.NetworkOptions != nil {
+ ctrNetworkSlipOpts = append(ctrNetworkSlipOpts, c.config.NetworkOptions["slirp4netns"]...)
+ }
+ slirpOpts, err := parseSlirp4netnsNetworkOptions(c.runtime, ctrNetworkSlipOpts)
+ if err != nil {
+ return false, err
+ }
+ return slirpOpts.enableIPv6, nil
+ }
+
+ return false, nil
+}
+
+// Add a new nameserver to the container's resolv.conf, ensuring that it is the
+// first nameserver present.
+// Usable only with running containers.
+func (c *Container) addNameserver(ips []string) error {
+ // Take no action if container is not running.
+ if !c.ensureState(define.ContainerStateRunning, define.ContainerStateCreated) {
+ return nil
+ }
+
+ // Do we have a resolv.conf at all?
+ path, ok := c.state.BindMounts["/etc/resolv.conf"]
+ if !ok {
+ return nil
+ }
+
+ // Read in full contents, parse out existing nameservers
+ contents, err := ioutil.ReadFile(path)
+ if err != nil {
+ return err
+ }
+ ns := resolvconf.GetNameservers(contents)
+ options := resolvconf.GetOptions(contents)
+ search := resolvconf.GetSearchDomains(contents)
+
+ // We could verify that it doesn't already exist
+ // but extra nameservers shouldn't harm anything.
+ // Ensure we are the first entry in resolv.conf though, otherwise we
+ // might be after user-added servers.
+ ns = append(ips, ns...)
+
+ // We're rewriting the container's resolv.conf as part of this, but we
+ // hold the container lock, so there should be no risk of parallel
+ // modification.
+ if _, err := resolvconf.Build(path, ns, search, options); err != nil {
+ return errors.Wrapf(err, "error adding new nameserver to container %s resolv.conf", c.ID())
+ }
+
+ return nil
+}
+
+// Remove an entry from the existing resolv.conf of the container.
+// Usable only with running containers.
+func (c *Container) removeNameserver(ips []string) error {
+ // Take no action if container is not running.
+ if !c.ensureState(define.ContainerStateRunning, define.ContainerStateCreated) {
+ return nil
+ }
+
+ // Do we have a resolv.conf at all?
+ path, ok := c.state.BindMounts["/etc/resolv.conf"]
+ if !ok {
+ return nil
+ }
+
+ // Read in full contents, parse out existing nameservers
+ contents, err := ioutil.ReadFile(path)
+ if err != nil {
+ return err
+ }
+ ns := resolvconf.GetNameservers(contents)
+ options := resolvconf.GetOptions(contents)
+ search := resolvconf.GetSearchDomains(contents)
+
+ toRemove := make(map[string]bool)
+ for _, ip := range ips {
+ toRemove[ip] = true
+ }
+
+ newNS := make([]string, 0, len(ns))
+ for _, server := range ns {
+ if !toRemove[server] {
+ newNS = append(newNS, server)
+ }
+ }
+
+ if _, err := resolvconf.Build(path, newNS, search, options); err != nil {
+ return errors.Wrapf(err, "error removing nameservers from container %s resolv.conf", c.ID())
+ }
+
+ return nil
+}
+
// updateHosts updates the container's hosts file
func (c *Container) updateHosts(path string) error {
var hosts string
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index e55e9d114..19d5c7f76 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -1170,6 +1170,7 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro
}
// update network status if container is running
+ oldStatus, statusExist := networkStatus[netName]
delete(networkStatus, netName)
c.state.NetworkStatus = networkStatus
err = c.save()
@@ -1180,8 +1181,26 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro
// Reload ports when there are still connected networks, maybe we removed the network interface with the child ip.
// Reloading without connected networks does not make sense, so we can skip this step.
if rootless.IsRootless() && len(networkStatus) > 0 {
- return c.reloadRootlessRLKPortMapping()
+ if err := c.reloadRootlessRLKPortMapping(); err != nil {
+ return err
+ }
+ }
+
+ // Update resolv.conf if required
+ if statusExist {
+ stringIPs := make([]string, 0, len(oldStatus.DNSServerIPs))
+ for _, ip := range oldStatus.DNSServerIPs {
+ stringIPs = append(stringIPs, ip.String())
+ }
+ if len(stringIPs) == 0 {
+ return nil
+ }
+ logrus.Debugf("Removing DNS Servers %v from resolv.conf", stringIPs)
+ if err := c.removeNameserver(stringIPs); err != nil {
+ return err
+ }
}
+
return nil
}
@@ -1263,11 +1282,36 @@ func (c *Container) NetworkConnect(nameOrID, netName string, netOpts types.PerNe
if err != nil {
return err
}
+
// The first network needs a port reload to set the correct child ip for the rootlessport process.
// Adding a second network does not require a port reload because the child ip is still valid.
if rootless.IsRootless() && len(networks) == 0 {
- return c.reloadRootlessRLKPortMapping()
+ if err := c.reloadRootlessRLKPortMapping(); err != nil {
+ return err
+ }
}
+
+ ipv6, err := c.checkForIPv6(networkStatus)
+ if err != nil {
+ return err
+ }
+
+ // Update resolv.conf if required
+ stringIPs := make([]string, 0, len(results[netName].DNSServerIPs))
+ for _, ip := range results[netName].DNSServerIPs {
+ if (ip.To4() == nil) && !ipv6 {
+ continue
+ }
+ stringIPs = append(stringIPs, ip.String())
+ }
+ if len(stringIPs) == 0 {
+ return nil
+ }
+ logrus.Debugf("Adding DNS Servers %v to resolv.conf", stringIPs)
+ if err := c.addNameserver(stringIPs); err != nil {
+ return err
+ }
+
return nil
}
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 3799b463f..44364100e 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -192,6 +192,11 @@ func (r *Runtime) initContainerVariables(rSpec *spec.Spec, config *ContainerConf
}
// Reset the log path to point to the default
ctr.config.LogPath = ""
+ // Later in validate() the check is for nil. JSONDeepCopy sets it to an empty
+ // object. Resetting it to nil if it was nil before.
+ if config.StaticMAC == nil {
+ ctr.config.StaticMAC = nil
+ }
}
ctr.config.Spec = rSpec
diff --git a/pkg/api/handlers/libpod/networks.go b/pkg/api/handlers/libpod/networks.go
index 71d46ce70..16f499d4c 100644
--- a/pkg/api/handlers/libpod/networks.go
+++ b/pkg/api/handlers/libpod/networks.go
@@ -17,22 +17,37 @@ import (
)
func CreateNetwork(w http.ResponseWriter, r *http.Request) {
+ if v, err := utils.SupportedVersion(r, ">=4.0.0"); err != nil {
+ utils.BadRequest(w, "version", v.String(), err)
+ return
+ }
+
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
network := types.Network{}
if err := json.NewDecoder(r.Body).Decode(&network); err != nil {
- utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "decode body"))
+ utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "failed to decode request JSON payload"))
return
}
ic := abi.ContainerEngine{Libpod: runtime}
report, err := ic.NetworkCreate(r.Context(), network)
if err != nil {
- utils.InternalServerError(w, err)
+ if errors.Is(err, types.ErrNetworkExists) {
+ utils.Error(w, http.StatusConflict, err)
+ } else {
+ utils.InternalServerError(w, err)
+ }
return
}
utils.WriteResponse(w, http.StatusOK, report)
}
+
func ListNetworks(w http.ResponseWriter, r *http.Request) {
+ if v, err := utils.SupportedVersion(r, ">=4.0.0"); err != nil {
+ utils.BadRequest(w, "version", v.String(), err)
+ return
+ }
+
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
filterMap, err := util.PrepareFilters(r)
if err != nil {
@@ -54,6 +69,11 @@ func ListNetworks(w http.ResponseWriter, r *http.Request) {
}
func RemoveNetwork(w http.ResponseWriter, r *http.Request) {
+ if v, err := utils.SupportedVersion(r, ">=4.0.0"); err != nil {
+ utils.BadRequest(w, "version", v.String(), err)
+ return
+ }
+
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
query := struct {
@@ -87,21 +107,18 @@ func RemoveNetwork(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusOK, reports)
}
+// InspectNetwork reports on given network's details
func InspectNetwork(w http.ResponseWriter, r *http.Request) {
- runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
- decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
- query := struct {
- }{
- // override any golang type defaults
- }
- if err := decoder.Decode(&query, r.URL.Query()); err != nil {
- utils.Error(w, http.StatusInternalServerError,
- errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
+ if v, err := utils.SupportedVersion(r, ">=4.0.0"); err != nil {
+ utils.BadRequest(w, "version", v.String(), err)
return
}
+
+ runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
+ ic := abi.ContainerEngine{Libpod: runtime}
+
name := utils.GetName(r)
options := entities.InspectOptions{}
- ic := abi.ContainerEngine{Libpod: runtime}
reports, errs, err := ic.NetworkInspect(r.Context(), []string{name}, options)
// If the network cannot be found, we return a 404.
if len(errs) > 0 {
@@ -117,14 +134,19 @@ func InspectNetwork(w http.ResponseWriter, r *http.Request) {
// Connect adds a container to a network
func Connect(w http.ResponseWriter, r *http.Request) {
- runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
+ if v, err := utils.SupportedVersion(r, ">=4.0.0"); err != nil {
+ utils.BadRequest(w, "version", v.String(), err)
+ return
+ }
+ runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
var netConnect entities.NetworkConnectOptions
if err := json.NewDecoder(r.Body).Decode(&netConnect); err != nil {
- utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
+ utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "failed to decode request JSON payload"))
return
}
name := utils.GetName(r)
+
err := runtime.ConnectContainerToNetwork(netConnect.Container, name, netConnect.PerNetworkOptions)
if err != nil {
if errors.Cause(err) == define.ErrNoSuchCtr {
@@ -143,10 +165,15 @@ func Connect(w http.ResponseWriter, r *http.Request) {
// ExistsNetwork check if a network exists
func ExistsNetwork(w http.ResponseWriter, r *http.Request) {
- runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
- name := utils.GetName(r)
+ if v, err := utils.SupportedVersion(r, ">=4.0.0"); err != nil {
+ utils.BadRequest(w, "version", v.String(), err)
+ return
+ }
+ runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
ic := abi.ContainerEngine{Libpod: runtime}
+
+ name := utils.GetName(r)
report, err := ic.NetworkExists(r.Context(), name)
if err != nil {
utils.Error(w, http.StatusInternalServerError, err)
@@ -161,7 +188,13 @@ func ExistsNetwork(w http.ResponseWriter, r *http.Request) {
// Prune removes unused networks
func Prune(w http.ResponseWriter, r *http.Request) {
+ if v, err := utils.SupportedVersion(r, ">=4.0.0"); err != nil {
+ utils.BadRequest(w, "version", v.String(), err)
+ return
+ }
+
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
+ ic := abi.ContainerEngine{Libpod: runtime}
filterMap, err := util.PrepareFilters(r)
if err != nil {
@@ -172,7 +205,6 @@ func Prune(w http.ResponseWriter, r *http.Request) {
pruneOptions := entities.NetworkPruneOptions{
Filters: *filterMap,
}
- ic := abi.ContainerEngine{Libpod: runtime}
pruneReports, err := ic.NetworkPrune(r.Context(), pruneOptions)
if err != nil {
utils.Error(w, http.StatusInternalServerError, err)
diff --git a/pkg/api/server/register_networks.go b/pkg/api/server/register_networks.go
index baa1fe6fb..4466c938f 100644
--- a/pkg/api/server/register_networks.go
+++ b/pkg/api/server/register_networks.go
@@ -320,6 +320,8 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
// $ref: "#/responses/NetworkCreateReport"
// 400:
// $ref: "#/responses/BadParamError"
+ // 409:
+ // $ref: "#/responses/ConflictError"
// 500:
// $ref: "#/responses/InternalError"
r.HandleFunc(VersionedPath("/libpod/networks/create"), s.APIHandler(libpod.CreateNetwork)).Methods(http.MethodPost)
diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go
index a363f2c6e..c508cb767 100644
--- a/pkg/bindings/images/build.go
+++ b/pkg/bindings/images/build.go
@@ -352,11 +352,13 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
}
c = tmpFile.Name()
}
+ c = filepath.Clean(c)
cfDir := filepath.Dir(c)
if absDir, err := filepath.EvalSymlinks(cfDir); err == nil {
name := filepath.ToSlash(strings.TrimPrefix(c, cfDir+string(filepath.Separator)))
c = filepath.Join(absDir, name)
}
+
containerfile, err := filepath.Abs(c)
if err != nil {
logrus.Errorf("Cannot find absolute path of %v: %v", c, err)
diff --git a/pkg/bindings/test/networks_test.go b/pkg/bindings/test/networks_test.go
index ee2d6f472..137db71a3 100644
--- a/pkg/bindings/test/networks_test.go
+++ b/pkg/bindings/test/networks_test.go
@@ -80,7 +80,7 @@ var _ = Describe("Podman networks", func() {
// Valid filter params => network should be pruned now.
filters = map[string][]string{
- "until": {"5000000000"}, //June 11, 2128
+ "until": {"5000000000"}, // June 11, 2128
}
pruneResponse, err = network.Prune(connText, new(network.PruneOptions).WithFilters(filters))
Expect(err).To(BeNil())
@@ -105,7 +105,7 @@ var _ = Describe("Podman networks", func() {
_, err = network.Create(connText, &net)
Expect(err).ToNot(BeNil())
code, _ := bindings.CheckResponseCode(err)
- Expect(code).To(BeNumerically("==", http.StatusInternalServerError))
+ Expect(code).To(BeNumerically("==", http.StatusConflict))
})
It("inspect network", func() {
diff --git a/pkg/checkpoint/checkpoint_restore.go b/pkg/checkpoint/checkpoint_restore.go
index 1ebd6a455..270b5b6c4 100644
--- a/pkg/checkpoint/checkpoint_restore.go
+++ b/pkg/checkpoint/checkpoint_restore.go
@@ -140,6 +140,13 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt
return nil, errors.Errorf("pod %s does not share the network namespace", ctrConfig.Pod)
}
ctrConfig.NetNsCtr = infraContainer.ID()
+ for net, opts := range ctrConfig.Networks {
+ opts.StaticIPs = nil
+ opts.StaticMAC = nil
+ ctrConfig.Networks[net] = opts
+ }
+ ctrConfig.StaticIP = nil
+ ctrConfig.StaticMAC = nil
}
if ctrConfig.PIDNsCtr != "" {
diff --git a/pkg/machine/fcos.go b/pkg/machine/fcos.go
index 60ab471ee..4d3e2edf4 100644
--- a/pkg/machine/fcos.go
+++ b/pkg/machine/fcos.go
@@ -14,6 +14,7 @@ import (
"strings"
"github.com/coreos/stream-metadata-go/fedoracoreos"
+ "github.com/coreos/stream-metadata-go/release"
"github.com/coreos/stream-metadata-go/stream"
"github.com/pkg/errors"
@@ -28,6 +29,14 @@ var (
Format string = "qcow2.xz"
)
+const (
+ // Used for testing the latest podman in fcos
+ // special builds
+ podmanTesting = "podman-testing"
+ PodmanTestingHost = "fedorapeople.org"
+ PodmanTestingURL = "groups/podman/testing"
+)
+
type FcosDownload struct {
Download
}
@@ -111,14 +120,39 @@ func getFcosArch() string {
return arch
}
+// getStreamURL is a wrapper for the fcos.GetStream URL
+// so that we can inject a special stream and url for
+// testing podman before it merges into fcos builds
+func getStreamURL(streamType string) url2.URL {
+ // For the podmanTesting stream type, we point to
+ // a custom url on fedorapeople.org
+ if streamType == podmanTesting {
+ return url2.URL{
+ Scheme: "https",
+ Host: PodmanTestingHost,
+ Path: fmt.Sprintf("%s/%s.json", PodmanTestingURL, "podman4"),
+ }
+ }
+ return fedoracoreos.GetStreamURL(streamType)
+}
+
// This should get Exported and stay put as it will apply to all fcos downloads
// getFCOS parses fedoraCoreOS's stream and returns the image download URL and the release version
func getFCOSDownload(imageStream string) (*fcosDownloadInfo, error) {
var (
fcosstable stream.Stream
+ altMeta release.Release
streamType string
)
+
+ // This is being hard set to testing. Once podman4 is in the
+ // fcos trees, we should remove it and re-release at least on
+ // macs.
+ imageStream = "podman-testing"
+
switch imageStream {
+ case "podman-testing":
+ streamType = "podman-testing"
case "testing", "":
streamType = fedoracoreos.StreamTesting
case "next":
@@ -128,7 +162,7 @@ func getFCOSDownload(imageStream string) (*fcosDownloadInfo, error) {
default:
return nil, errors.Errorf("invalid stream %s: valid streams are `testing` and `stable`", imageStream)
}
- streamurl := fedoracoreos.GetStreamURL(streamType)
+ streamurl := getStreamURL(streamType)
resp, err := http.Get(streamurl.String())
if err != nil {
return nil, err
@@ -142,6 +176,27 @@ func getFCOSDownload(imageStream string) (*fcosDownloadInfo, error) {
logrus.Error(err)
}
}()
+ if imageStream == podmanTesting {
+ if err := json.Unmarshal(body, &altMeta); err != nil {
+ return nil, err
+ }
+
+ arches, ok := altMeta.Architectures[getFcosArch()]
+ if !ok {
+ return nil, fmt.Errorf("unable to pull VM image: no targetArch in stream")
+ }
+ qcow2, ok := arches.Media.Qemu.Artifacts["qcow2.xz"]
+ if !ok {
+ return nil, fmt.Errorf("unable to pull VM image: no qcow2.xz format in stream")
+ }
+ disk := qcow2.Disk
+
+ return &fcosDownloadInfo{
+ Location: disk.Location,
+ Sha256Sum: disk.Sha256,
+ CompressionType: "xz",
+ }, nil
+ }
if err := json.Unmarshal(body, &fcosstable); err != nil {
return nil, err
diff --git a/test/apiv2/35-networks.at b/test/apiv2/35-networks.at
index 0e2389bd5..3502b89e0 100644
--- a/test/apiv2/35-networks.at
+++ b/test/apiv2/35-networks.at
@@ -8,7 +8,10 @@ t GET networks/non-existing-network 404 \
t POST libpod/networks/create name='"network1"' 200 \
.name=network1 \
- .created~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* \
+ .created~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.*
+
+t POST /v3.4.0/libpod/networks/create name='"bad_version"' 400 \
+ .cause='given version is not supported'
# --data '{"name":"network2","subnets":[{"subnet":"10.10.254.0/24"}],"Labels":{"abc":"val"}}'
t POST libpod/networks/create name='"network2"' \
diff --git a/test/apiv2/test-apiv2 b/test/apiv2/test-apiv2
index 56280f04e..bd728e130 100755
--- a/test/apiv2/test-apiv2
+++ b/test/apiv2/test-apiv2
@@ -256,11 +256,11 @@ function t() {
# If given path begins with /, use it as-is; otherwise prepend /version/
local url=http://$HOST:$PORT
- if expr "$path" : "/" >/dev/null; then
- url="$url$path"
- else
- url="$url/v1.40/$path"
- fi
+ case "$path" in
+ /*) url="$url$path" ;;
+ libpod/*) url="$url/v4.0.0/$path" ;;
+ *) url="$url/v1.41/$path" ;;
+ esac
# Log every action we do
echo "-------------------------------------------------------------" >>$LOG
diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go
index 5f1e4b1d1..5abc672e9 100644
--- a/test/e2e/checkpoint_test.go
+++ b/test/e2e/checkpoint_test.go
@@ -1081,10 +1081,6 @@ var _ = Describe("Podman checkpoint", func() {
})
namespaceCombination := []string{
- "cgroup,ipc,net,uts,pid",
- "cgroup,ipc,net,uts",
- "cgroup,ipc,net",
- "cgroup,ipc",
"ipc,net,uts,pid",
"ipc,net,uts",
"ipc,net",
diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go
index f843a8984..b1cd76d27 100644
--- a/test/e2e/common_test.go
+++ b/test/e2e/common_test.go
@@ -771,15 +771,15 @@ func SkipIfNotActive(unit string, reason string) {
}
}
-func SkipIfNetavark(p *PodmanTestIntegration) {
- if p.NetworkBackend == Netavark {
- Skip("This test is not compatible with the netavark network backend")
+func SkipIfCNI(p *PodmanTestIntegration) {
+ if p.NetworkBackend == CNI {
+ Skip("this test is not compatible with the CNI network backend")
}
}
-func SkipUntilAardvark(p *PodmanTestIntegration) {
+func SkipIfNetavark(p *PodmanTestIntegration) {
if p.NetworkBackend == Netavark {
- Skip("Re-enable when aardvark is functional")
+ Skip("This test is not compatible with the netavark network backend")
}
}
@@ -1038,3 +1038,7 @@ func ncz(port int) bool {
}
return false
}
+
+func createNetworkName(name string) string {
+ return name + stringid.GenerateNonCryptoID()[:10]
+}
diff --git a/test/e2e/network_connect_disconnect_test.go b/test/e2e/network_connect_disconnect_test.go
index baef142ba..a0716c84d 100644
--- a/test/e2e/network_connect_disconnect_test.go
+++ b/test/e2e/network_connect_disconnect_test.go
@@ -2,6 +2,7 @@ package integration
import (
"os"
+ "strings"
. "github.com/containers/podman/v4/test/utils"
"github.com/containers/storage/pkg/stringid"
@@ -77,6 +78,11 @@ var _ = Describe("Podman network connect and disconnect", func() {
Expect(session).Should(Exit(0))
defer podmanTest.removeNetwork(netName)
+ gw := podmanTest.Podman([]string{"network", "inspect", netName, "--format", "{{(index .Subnets 0).Gateway}}"})
+ gw.WaitWithDefaultTimeout()
+ Expect(gw).Should(Exit(0))
+ ns := gw.OutputToString()
+
ctr := podmanTest.Podman([]string{"run", "-dt", "--name", "test", "--network", netName, ALPINE, "top"})
ctr.WaitWithDefaultTimeout()
Expect(ctr).Should(Exit(0))
@@ -85,6 +91,11 @@ var _ = Describe("Podman network connect and disconnect", func() {
exec.WaitWithDefaultTimeout()
Expect(exec).Should(Exit(0))
+ exec2 := podmanTest.Podman([]string{"exec", "-it", "test", "cat", "/etc/resolv.conf"})
+ exec2.WaitWithDefaultTimeout()
+ Expect(exec2).Should(Exit(0))
+ Expect(strings.Contains(exec2.OutputToString(), ns)).To(BeTrue())
+
dis := podmanTest.Podman([]string{"network", "disconnect", netName, "test"})
dis.WaitWithDefaultTimeout()
Expect(dis).Should(Exit(0))
@@ -98,6 +109,11 @@ var _ = Describe("Podman network connect and disconnect", func() {
exec = podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"})
exec.WaitWithDefaultTimeout()
Expect(exec).Should(ExitWithError())
+
+ exec3 := podmanTest.Podman([]string{"exec", "-it", "test", "cat", "/etc/resolv.conf"})
+ exec3.WaitWithDefaultTimeout()
+ Expect(exec3).Should(Exit(0))
+ Expect(strings.Contains(exec3.OutputToString(), ns)).To(BeFalse())
})
It("bad network name in connect should result in error", func() {
@@ -182,6 +198,16 @@ var _ = Describe("Podman network connect and disconnect", func() {
Expect(session).Should(Exit(0))
defer podmanTest.removeNetwork(newNetName)
+ gw := podmanTest.Podman([]string{"network", "inspect", newNetName, "--format", "{{(index .Subnets 0).Gateway}}"})
+ gw.WaitWithDefaultTimeout()
+ Expect(gw).Should(Exit(0))
+ ns := gw.OutputToString()
+
+ exec2 := podmanTest.Podman([]string{"exec", "-it", "test", "cat", "/etc/resolv.conf"})
+ exec2.WaitWithDefaultTimeout()
+ Expect(exec2).Should(Exit(0))
+ Expect(strings.Contains(exec2.OutputToString(), ns)).To(BeFalse())
+
ip := "10.11.100.99"
mac := "44:11:44:11:44:11"
connect := podmanTest.Podman([]string{"network", "connect", "--ip", ip, "--mac-address", mac, newNetName, "test"})
@@ -206,6 +232,11 @@ var _ = Describe("Podman network connect and disconnect", func() {
Expect(exec.OutputToString()).Should(ContainSubstring(ip))
Expect(exec.OutputToString()).Should(ContainSubstring(mac))
+ exec3 := podmanTest.Podman([]string{"exec", "-it", "test", "cat", "/etc/resolv.conf"})
+ exec3.WaitWithDefaultTimeout()
+ Expect(exec3).Should(Exit(0))
+ Expect(strings.Contains(exec3.OutputToString(), ns)).To(BeTrue())
+
// make sure no logrus errors are shown https://github.com/containers/podman/issues/9602
rm := podmanTest.Podman([]string{"rm", "--time=0", "-f", "test"})
rm.WaitWithDefaultTimeout()
diff --git a/test/e2e/network_create_test.go b/test/e2e/network_create_test.go
index 7589adaab..395759ee6 100644
--- a/test/e2e/network_create_test.go
+++ b/test/e2e/network_create_test.go
@@ -330,8 +330,8 @@ var _ = Describe("Podman network create", func() {
Expect(nc).To(ExitWithError())
})
- It("podman network create with internal should not have dnsname", func() {
- SkipUntilAardvark(podmanTest)
+ It("podman CNI network create with internal should not have dnsname", func() {
+ SkipIfNetavark(podmanTest)
net := "internal-test" + stringid.GenerateNonCryptoID()
nc := podmanTest.Podman([]string{"network", "create", "--internal", net})
nc.WaitWithDefaultTimeout()
@@ -348,6 +348,24 @@ var _ = Describe("Podman network create", func() {
Expect(nc.OutputToString()).ToNot(ContainSubstring("dnsname"))
})
+ It("podman Netavark network create with internal should have dnsname", func() {
+ SkipIfCNI(podmanTest)
+ net := "internal-test" + stringid.GenerateNonCryptoID()
+ nc := podmanTest.Podman([]string{"network", "create", "--internal", net})
+ nc.WaitWithDefaultTimeout()
+ defer podmanTest.removeNetwork(net)
+ Expect(nc).Should(Exit(0))
+ // Not performing this check on remote tests because it is a logrus error which does
+ // not come back via stderr on the remote client.
+ if !IsRemote() {
+ Expect(nc.ErrorToString()).To(BeEmpty())
+ }
+ nc = podmanTest.Podman([]string{"network", "inspect", net})
+ nc.WaitWithDefaultTimeout()
+ Expect(nc).Should(Exit(0))
+ Expect(nc.OutputToString()).To(ContainSubstring(`"dns_enabled": true`))
+ })
+
It("podman network create with invalid name", func() {
for _, name := range []string{"none", "host", "bridge", "private", "slirp4netns", "container", "ns"} {
nc := podmanTest.Podman([]string{"network", "create", name})
diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go
index bd30a1f5d..89a9005f5 100644
--- a/test/e2e/network_test.go
+++ b/test/e2e/network_test.go
@@ -466,10 +466,61 @@ var _ = Describe("Podman network", func() {
Expect(lines[1]).To(Equal(netName2))
})
- It("podman network with multiple aliases", func() {
- SkipUntilAardvark(podmanTest)
+ It("podman CNI network with multiple aliases", func() {
+ SkipIfNetavark(podmanTest)
+ var worked bool
+ netName := createNetworkName("aliasTest")
+ session := podmanTest.Podman([]string{"network", "create", netName})
+ session.WaitWithDefaultTimeout()
+ defer podmanTest.removeNetwork(netName)
+ Expect(session).Should(Exit(0))
+
+ interval := time.Duration(250 * time.Millisecond)
+ for i := 0; i < 6; i++ {
+ n := podmanTest.Podman([]string{"network", "exists", netName})
+ n.WaitWithDefaultTimeout()
+ worked = n.ExitCode() == 0
+ if worked {
+ break
+ }
+ time.Sleep(interval)
+ interval *= 2
+ }
+
+ top := podmanTest.Podman([]string{"run", "-dt", "--name=web", "--network=" + netName, "--network-alias=web1", "--network-alias=web2", nginx})
+ top.WaitWithDefaultTimeout()
+ Expect(top).Should(Exit(0))
+ interval = time.Duration(250 * time.Millisecond)
+ // Wait for the nginx service to be running
+ for i := 0; i < 6; i++ {
+ // Test curl against the container's name
+ c1 := podmanTest.Podman([]string{"run", "--dns-search", "dns.podman", "--network=" + netName, nginx, "curl", "web"})
+ c1.WaitWithDefaultTimeout()
+ worked = c1.ExitCode() == 0
+ if worked {
+ break
+ }
+ time.Sleep(interval)
+ interval *= 2
+ }
+ Expect(worked).To(BeTrue())
+
+ // Nginx is now running so no need to do a loop
+ // Test against the first alias
+ c2 := podmanTest.Podman([]string{"run", "--dns-search", "dns.podman", "--network=" + netName, nginx, "curl", "web1"})
+ c2.WaitWithDefaultTimeout()
+ Expect(c2).Should(Exit(0))
+
+ // Test against the second alias
+ c3 := podmanTest.Podman([]string{"run", "--dns-search", "dns.podman", "--network=" + netName, nginx, "curl", "web2"})
+ c3.WaitWithDefaultTimeout()
+ Expect(c3).Should(Exit(0))
+ })
+
+ It("podman Netavark network with multiple aliases", func() {
+ SkipIfCNI(podmanTest)
var worked bool
- netName := "aliasTest" + stringid.GenerateNonCryptoID()
+ netName := createNetworkName("aliasTest")
session := podmanTest.Podman([]string{"network", "create", netName})
session.WaitWithDefaultTimeout()
defer podmanTest.removeNetwork(netName)
diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go
index 4c056df10..aa1887f84 100644
--- a/test/e2e/run_networking_test.go
+++ b/test/e2e/run_networking_test.go
@@ -715,8 +715,8 @@ EXPOSE 2004-2005/tcp`, ALPINE)
Expect(run.OutputToString()).To(ContainSubstring(ipAddr))
})
- It("podman cni network works across user ns", func() {
- SkipUntilAardvark(podmanTest)
+ It("podman CNI network works across user ns", func() {
+ SkipIfNetavark(podmanTest)
netName := stringid.GenerateNonCryptoID()
create := podmanTest.Podman([]string{"network", "create", netName})
create.WaitWithDefaultTimeout()
@@ -740,6 +740,31 @@ EXPOSE 2004-2005/tcp`, ALPINE)
Expect(log.OutputToString()).To(Equal("podman"))
})
+ It("podman Netavark network works across user ns", func() {
+ SkipIfCNI(podmanTest)
+ netName := createNetworkName("")
+ create := podmanTest.Podman([]string{"network", "create", netName})
+ create.WaitWithDefaultTimeout()
+ Expect(create).Should(Exit(0))
+ defer podmanTest.removeNetwork(netName)
+
+ name := "nc-server"
+ run := podmanTest.Podman([]string{"run", "--log-driver", "k8s-file", "-d", "--name", name, "--net", netName, ALPINE, "nc", "-l", "-p", "9480"})
+ run.WaitWithDefaultTimeout()
+ Expect(run).Should(Exit(0))
+
+ // NOTE: we force the k8s-file log driver to make sure the
+ // tests are passing inside a container.
+ run = podmanTest.Podman([]string{"run", "--log-driver", "k8s-file", "--rm", "--net", netName, "--uidmap", "0:1:4096", ALPINE, "sh", "-c", fmt.Sprintf("echo podman | nc -w 1 %s.dns.podman 9480", name)})
+ run.WaitWithDefaultTimeout()
+ Expect(run).Should(Exit(0))
+
+ log := podmanTest.Podman([]string{"logs", name})
+ log.WaitWithDefaultTimeout()
+ Expect(log).Should(Exit(0))
+ Expect(log.OutputToString()).To(Equal("podman"))
+ })
+
It("podman run with new:pod and static-ip", func() {
netName := stringid.GenerateNonCryptoID()
ipAddr := "10.25.40.128"
@@ -814,14 +839,50 @@ EXPOSE 2004-2005/tcp`, ALPINE)
pingTest("--net=private")
})
- It("podman run check dnsname plugin", func() {
- SkipUntilAardvark(podmanTest)
+ It("podman run check dnsname plugin with CNI", func() {
+ SkipIfNetavark(podmanTest)
+ pod := "testpod"
+ session := podmanTest.Podman([]string{"pod", "create", "--name", pod})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+
+ net := createNetworkName("IntTest")
+ session = podmanTest.Podman([]string{"network", "create", net})
+ session.WaitWithDefaultTimeout()
+ defer podmanTest.removeNetwork(net)
+ Expect(session).Should(Exit(0))
+
+ pod2 := "testpod2"
+ session = podmanTest.Podman([]string{"pod", "create", "--network", net, "--name", pod2})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+
+ session = podmanTest.Podman([]string{"run", "--name", "con1", "--network", net, ALPINE, "nslookup", "con1"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+
+ session = podmanTest.Podman([]string{"run", "--name", "con2", "--pod", pod, "--network", net, ALPINE, "nslookup", "con2"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+
+ session = podmanTest.Podman([]string{"run", "--name", "con3", "--pod", pod2, ALPINE, "nslookup", "con1"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(1))
+ Expect(session.ErrorToString()).To(ContainSubstring("can't resolve 'con1'"))
+
+ session = podmanTest.Podman([]string{"run", "--name", "con4", "--network", net, ALPINE, "nslookup", pod2 + ".dns.podman"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ })
+
+ It("podman run check dnsname plugin with Netavark", func() {
+ SkipIfCNI(podmanTest)
pod := "testpod"
session := podmanTest.Podman([]string{"pod", "create", "--name", pod})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- net := "IntTest" + stringid.GenerateNonCryptoID()
+ net := createNetworkName("IntTest")
session = podmanTest.Podman([]string{"network", "create", net})
session.WaitWithDefaultTimeout()
defer podmanTest.removeNetwork(net)
@@ -850,9 +911,23 @@ EXPOSE 2004-2005/tcp`, ALPINE)
Expect(session).Should(Exit(0))
})
- It("podman run check dnsname adds dns search domain", func() {
- SkipUntilAardvark(podmanTest)
- net := "dnsname" + stringid.GenerateNonCryptoID()
+ It("podman run check dnsname adds dns search domain with CNI", func() {
+ SkipIfNetavark(podmanTest)
+ net := createNetworkName("dnsname")
+ session := podmanTest.Podman([]string{"network", "create", net})
+ session.WaitWithDefaultTimeout()
+ defer podmanTest.removeNetwork(net)
+ Expect(session).Should(Exit(0))
+
+ session = podmanTest.Podman([]string{"run", "--network", net, ALPINE, "cat", "/etc/resolv.conf"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.OutputToString()).To(ContainSubstring("search dns.podman"))
+ })
+
+ It("podman run check dnsname adds dns search domain with Netavark", func() {
+ SkipIfCNI(podmanTest)
+ net := createNetworkName("dnsname")
session := podmanTest.Podman([]string{"network", "create", net})
session.WaitWithDefaultTimeout()
defer podmanTest.removeNetwork(net)
diff --git a/test/system/070-build.bats b/test/system/070-build.bats
index d5f7365e8..a95acd986 100644
--- a/test/system/070-build.bats
+++ b/test/system/070-build.bats
@@ -88,12 +88,10 @@ EOF
containerfile=$PODMAN_TMPDIR/Containerfile
cat >$containerfile <<EOF
FROM $IMAGE
-RUN apk add nginx
RUN echo $rand_content > /$rand_filename
EOF
- # The 'apk' command can take a long time to fetch files; bump timeout
- PODMAN_TIMEOUT=240 run_podman build -t build_test -f - --format=docker $tmpdir < $containerfile
+ run_podman build -t build_test -f - --format=docker $tmpdir < $containerfile
is "$output" ".*COMMIT" "COMMIT seen in log"
run_podman run --rm build_test cat /$rand_filename
@@ -188,6 +186,30 @@ EOF
run_podman rmi -f build_test $iid
}
+@test "podman build test -f ./relative" {
+ rand_filename=$(random_string 20)
+ rand_content=$(random_string 50)
+
+ tmpdir=$PODMAN_TMPDIR/build-test
+ mkdir -p $tmpdir
+ mkdir -p $PODMAN_TMPDIR/reldir
+
+ containerfile=$PODMAN_TMPDIR/reldir/Containerfile
+ cat >$containerfile <<EOF
+FROM $IMAGE
+RUN echo $rand_content > /$rand_filename
+EOF
+
+ cd $PODMAN_TMPDIR
+ run_podman build -t build_test -f ./reldir/Containerfile --format=docker $tmpdir
+ is "$output" ".*COMMIT" "COMMIT seen in log"
+
+ run_podman run --rm build_test cat /$rand_filename
+ is "$output" "$rand_content" "reading generated file in image"
+
+ run_podman rmi -f build_test
+}
+
@test "podman build - URLs" {
tmpdir=$PODMAN_TMPDIR/build-test
mkdir -p $tmpdir
diff --git a/troubleshooting.md b/troubleshooting.md
index 82ca64305..6f2a96a56 100644
--- a/troubleshooting.md
+++ b/troubleshooting.md
@@ -916,6 +916,235 @@ After deleting a VM on macOS, the initialization of subsequent VMs fails.
After deleting a client VM on macOS via `podman machine stop` && `podman machine rm`, attempting to `podman machine init` a new client VM leads to an error with the 127.0.0.1:7777 port already bound.
-### Solution
+#### Solution
You will need to remove the hanging gv-proxy process bound to the port in question. For example, if the port mentioned in the error message is 127.0.0.1:7777, you can use the command `kill -9 $(lsof -i:7777)` in order to identify and remove the hanging process which prevents you from starting a new VM on that default port.
+
+### 32) The sshd process fails to run inside of the container.
+
+#### Symptom
+
+The sshd process running inside the container fails with the error
+"Error writing /proc/self/loginuid".
+
+### Solution
+
+If the `/proc/self/loginuid` file is already initialized then the
+`CAP_AUDIT_CONTROL` capability is required to override it.
+
+This happens when running Podman from a user session since the
+`/proc/self/loginuid` file is already initialized. The solution is to
+run Podman from a system service, either using the Podman service, and
+then using podman -remote to start the container or simply by running
+something like `systemd-run podman run ...`. In this case the
+container will only need `CAP_AUDIT_WRITE`.
+
+### 33) Container creates a file that is not owned by the user's regular UID
+
+After running a container with rootless Podman, the non-root user sees a numerical UID and GID instead of a username and groupname.
+
+#### Symptom
+
+When listing file permissions with `ls -l` on the host in a directory that was passed as `--volume /some/dir` to `podman run`,
+the UID and GID are displayed rather than the corresponding username and groupname. The UID and GID numbers displayed are
+from the user's subordinate UID and GID ranges on the host system.
+
+An example
+
+```Text
+$ mkdir dir1
+$ chmod 777 dir1
+$ podman run --rm -v ./dir1:/dir1:Z \
+ --user 2003:2003 \
+ docker.io/library/ubuntu bash -c "touch /dir1/a; chmod 600 /dir1/a"
+$ ls -l dir1/a
+-rw-------. 1 102002 102002 0 Jan 19 19:35 dir1/a
+$ less dir1/a
+less: dir1/a: Permission denied
+```
+
+#### Solution
+
+If you want to read or remove such a file, you can do so by entering a user namespace.
+Instead of running commands such as `less dir1/a` or `rm dir1/a`, you would need to
+prepend the command-line with `podman unshare`, i.e.,
+`podman unshare less dir1/a` or `podman unshare rm dir1/a`. To be able to use Bash
+features, such as variable expansion and globbing, you need to wrap the command with
+`bash -c`, e.g. `podman unshare bash -c 'ls $HOME/dir1/a*'`.
+
+Would it have been possible to run Podman in another way so that your regular
+user would have become the owner of the file? Yes, you can use the options
+__--uidmap__ and __--gidmap__ to change how UIDs and GIDs are mapped
+between the container and the host. Let's try it out.
+
+In the example above `ls -l` shows the UID 102002 and GID 102002. Set shell variables
+
+```Text
+$ uid_from_ls = 102002
+$ gid_from_ls = 102002
+```
+
+Set shell variables to the lowest subordinate UID and GID
+
+```Text
+$ lowest_subuid=$(podman info --format "{{ (index .Host.IDMappings.UIDMap 1).HostID }}")
+$ lowest_subgid=$(podman info --format "{{ (index .Host.IDMappings.GIDMap 1).HostID }}")
+```
+
+Compute the UID and GID inside the container that map to the owner of the created file on the host.
+
+```Text
+$ uid=$(( $uid_from_ls - $lowest_subuid + 1))
+$ gid=$(( $gid_from_ls - $lowest_subgid + 1))
+```
+(In the computation it was assumed that there is only one subuid range and one subgid range)
+
+```Text
+$ echo $uid
+2003
+$ echo $gid
+2003
+```
+
+The computation shows that the UID is _2003_ and the GID is _2003_ inside the container.
+This comes as no surprise as this is what was specified before with `--user=2003:2003`,
+but the same computation could be used whenever a username is specified
+or the __--user__ option is not used.
+
+Run the container again but now with UIDs and GIDs mapped
+
+```Text
+$ subuidSize=$(( $(podman info --format "{{ range .Host.IDMappings.UIDMap }}+{{.Size }}{{end }}" ) - 1 ))
+$ subgidSize=$(( $(podman info --format "{{ range .Host.IDMappings.GIDMap }}+{{.Size }}{{end }}" ) - 1 ))
+$ mkdir dir1
+$ chmod 777 dir1
+$ podman run --rm
+ -v ./dir1:/dir1:Z \
+ --user $uid:$gid \
+ --uidmap $uid:0:1 \
+ --uidmap 0:1:$uid \
+ --uidmap $(($uid+1)):$(($uid+1)):$(($subuidSize-$uid)) \
+ --gidmap $gid:0:1 \
+ --gidmap 0:1:$gid \
+ --gidmap $(($gid+1)):$(($gid+1)):$(($subgidSize-$gid)) \
+ docker.io/library/ubuntu bash -c "touch /dir1/a; chmod 600 /dir1/a"
+$ id -u
+tester
+$ id -g
+tester
+$ ls -l dir1/a
+-rw-------. 1 tester tester 0 Jan 19 20:31 dir1/a
+$
+```
+
+In this example the __--user__ option specified a rootless user in the container.
+As the rootless user could also have been specified in the container image, e.g.,
+
+```Text
+$ podman image inspect --format "user: {{.User}}" IMAGE
+user: hpc
+$
+```
+the same problem could also occur even without specifying __--user__.
+
+Another variant of the same problem could occur when using
+__--user=root:root__ (the default), but where the root user creates non-root owned files
+in some way (e.g by creating them themselves, or switching the effective UID to
+a rootless user and then creates files).
+
+### 34) Passed-in devices or files can't be accessed in rootless container (UID/GID mapping problem)
+
+As a non-root user you have access rights to devices, files and directories that you
+want to pass into a rootless container with `--device=...`, `--volume=...` or `--mount=..`.
+
+Podman by default maps a non-root user inside a container to one of the user's
+subordinate UIDs and subordinate GIDs on the host. When the container user tries to access a
+file, a "Permission denied" error could occur because the container user does not have the
+permissions of the regular user of the host.
+
+#### Symptom
+
+* Any access inside the container is rejected with "Permission denied"
+for files, directories or devices passed in to the container
+with `--device=..`,`--volume=..` or `--mount=..`, e.g.
+
+```Text
+$ mkdir dir1
+$ chmod 700 dir1
+$ podman run --rm -v ./dir1:/dir1:Z \
+ --user 2003:2003 \
+ docker.io/library/ubuntu ls /dir1
+ls: cannot open directory '/dir1': Permission denied
+```
+
+#### Solution
+
+We follow essentialy the same solution as in the previous
+troubleshooting tip:
+ "_Container creates a file that is not owned by the regular UID_"
+but for this problem the container UID and GID can't be as
+easily computed by mere addition and subtraction.
+
+In other words, it might be more challenging to find out the UID and
+the GID inside the container that we want to map to the regular
+user on the host.
+
+If the __--user__ option is used together with a numerical UID and GID
+to specify a rootless user, we already know the answer.
+
+If the __--user__ option is used together with a username and groupname,
+we could look up the UID and GID in the file _/etc/passwd_ of the container.
+
+If the container user is not set via __--user__ but instead from the
+container image, we could inspect the container image
+
+```Text
+$ podman image inspect --format "user: {{.User}}" IMAGE
+user: hpc
+$
+```
+
+and then look it up in _/etc/passwd_ of the container.
+
+If the problem occurs in a container that is started to run as root but later
+switches to an effictive UID of a rootless user, it might be less
+straightforward to find out the UID and the GID. Reading the
+_Containerfile_, _Dockerfile_ or the _/etc/passwd_ could give a clue.
+
+To run the container with the rootless container UID and GID mapped to the
+user's regular UID and GID on the host follow these steps:
+
+Set the _uid_ and _gid_ shell variables in a Bash shell to the UID and GID
+of the user that will be running inside the container, e.g.
+
+```Text
+$ uid=2003
+$ gid=2003
+```
+
+and run
+
+```Text
+$ mkdir dir1
+$ echo hello > dir1/file.txt
+$ chmod 700 dir1/file.txt
+$ subuidSize=$(( $(podman info --format "{{ range .Host.IDMappings.UIDMap }}+{{.Size }}{{end }}" ) - 1 ))
+$ subgidSize=$(( $(podman info --format "{{ range .Host.IDMappings.GIDMap }}+{{.Size }}{{end }}" ) - 1 ))
+$ podman run --rm \
+ -v ./dir1:/dir1:Z \
+ --user $uid:$gid \
+ --uidmap $uid:0:1 \
+ --uidmap 0:1:$uid \
+ --uidmap $(($uid+1)):$(($uid+1)):$(($subuidSize-$uid)) \
+ --gidmap $gid:0:1 \
+ --gidmap 0:1:$gid \
+ --gidmap $(($gid+1)):$(($gid+1)):$(($subgidSize-$gid)) \
+ docker.io/library/alpine cat /dir1/file.txt
+hello
+$
+```
+
+A side-note: Using [__--userns=keep-id__](https://docs.podman.io/en/latest/markdown/podman-run.1.html#userns-mode)
+can sometimes be an alternative solution, but it forces the regular
+user's host UID to be mapped to the same UID inside the container
+so it provides less flexibility than using __--uidmap__ and __--gidmap__.
diff --git a/vendor/github.com/containers/common/libimage/copier.go b/vendor/github.com/containers/common/libimage/copier.go
index 459989579..2a8f47f7f 100644
--- a/vendor/github.com/containers/common/libimage/copier.go
+++ b/vendor/github.com/containers/common/libimage/copier.go
@@ -7,6 +7,7 @@ import (
"strings"
"time"
+ "github.com/containers/common/libimage/manifests"
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/retry"
"github.com/containers/image/v5/copy"
@@ -26,8 +27,10 @@ const (
)
// LookupReferenceFunc return an image reference based on the specified one.
-// This can be used to pass custom blob caches to the copy operation.
-type LookupReferenceFunc func(ref types.ImageReference) (types.ImageReference, error)
+// The returned reference can return custom ImageSource or ImageDestination
+// objects which intercept or filter blobs, manifests, and signatures as
+// they are read and written.
+type LookupReferenceFunc = manifests.LookupReferenceFunc
// CopyOptions allow for customizing image-copy operations.
type CopyOptions struct {
diff --git a/vendor/github.com/containers/common/libimage/manifests/manifests.go b/vendor/github.com/containers/common/libimage/manifests/manifests.go
index 45223cc2f..ccff908c9 100644
--- a/vendor/github.com/containers/common/libimage/manifests/manifests.go
+++ b/vendor/github.com/containers/common/libimage/manifests/manifests.go
@@ -27,6 +27,12 @@ import (
const instancesData = "instances.json"
+// LookupReferenceFunc return an image reference based on the specified one.
+// The returned reference can return custom ImageSource or ImageDestination
+// objects which intercept or filter blobs, manifests, and signatures as
+// they are read and written.
+type LookupReferenceFunc func(ref types.ImageReference) (types.ImageReference, error)
+
// ErrListImageUnknown is returned when we attempt to create an image reference
// for a List that has not yet been saved to an image.
var ErrListImageUnknown = stderrors.New("unable to determine which image holds the manifest list")
@@ -57,6 +63,7 @@ type PushOptions struct {
SignBy string // fingerprint of GPG key to use to sign images
RemoveSignatures bool // true to discard signatures in images
ManifestType string // the format to use when saving the list - possible options are oci, v2s1, and v2s2
+ SourceFilter LookupReferenceFunc // filter the list source
}
// Create creates a new list containing information about the specified image,
@@ -221,6 +228,11 @@ func (l *list) Push(ctx context.Context, dest types.ImageReference, options Push
if err != nil {
return nil, "", err
}
+ if options.SourceFilter != nil {
+ if src, err = options.SourceFilter(src); err != nil {
+ return nil, "", err
+ }
+ }
copyOptions := &cp.Options{
ImageListSelection: options.ImageListSelection,
Instances: options.Instances,
diff --git a/vendor/github.com/containers/common/libnetwork/cni/cni_conversion.go b/vendor/github.com/containers/common/libnetwork/cni/cni_conversion.go
index dedb40ad3..5574b2b1c 100644
--- a/vendor/github.com/containers/common/libnetwork/cni/cni_conversion.go
+++ b/vendor/github.com/containers/common/libnetwork/cni/cni_conversion.go
@@ -222,14 +222,33 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ
err error
)
if len(network.Subnets) > 0 {
+ defIpv4Route := false
+ defIpv6Route := false
for _, subnet := range network.Subnets {
- route, err := newIPAMDefaultRoute(util.IsIPv6(subnet.Subnet.IP))
- if err != nil {
- return nil, "", err
- }
- routes = append(routes, route)
ipam := newIPAMLocalHostRange(subnet.Subnet, subnet.LeaseRange, subnet.Gateway)
ipamRanges = append(ipamRanges, []ipamLocalHostRangeConf{*ipam})
+
+ // only add default route for not internal networks
+ if !network.Internal {
+ ipv6 := util.IsIPv6(subnet.Subnet.IP)
+ if !ipv6 && defIpv4Route {
+ continue
+ }
+ if ipv6 && defIpv6Route {
+ continue
+ }
+
+ if ipv6 {
+ defIpv6Route = true
+ } else {
+ defIpv4Route = true
+ }
+ route, err := newIPAMDefaultRoute(ipv6)
+ if err != nil {
+ return nil, "", err
+ }
+ routes = append(routes, route)
+ }
}
ipamConf = newIPAMHostLocalConf(routes, ipamRanges)
} else {
diff --git a/vendor/github.com/containers/common/libnetwork/cni/config.go b/vendor/github.com/containers/common/libnetwork/cni/config.go
index b0aa19d94..b1f89400c 100644
--- a/vendor/github.com/containers/common/libnetwork/cni/config.go
+++ b/vendor/github.com/containers/common/libnetwork/cni/config.go
@@ -82,7 +82,7 @@ func (n *cniNetwork) networkCreate(newNetwork *types.Network, defaultNet bool) (
return nil, errors.Wrapf(types.ErrInvalidArg, "unsupported driver %s", newNetwork.Driver)
}
- err = internalutil.ValidateSubnets(newNetwork, usedNetworks)
+ err = internalutil.ValidateSubnets(newNetwork, !newNetwork.Internal, usedNetworks)
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/containers/common/libnetwork/internal/util/validate.go b/vendor/github.com/containers/common/libnetwork/internal/util/validate.go
index bfc5e2247..ac3934f8d 100644
--- a/vendor/github.com/containers/common/libnetwork/internal/util/validate.go
+++ b/vendor/github.com/containers/common/libnetwork/internal/util/validate.go
@@ -65,11 +65,11 @@ func ValidateSubnet(s *types.Subnet, addGateway bool, usedNetworks []*net.IPNet)
}
// ValidateSubnets will validate the subnets for this network.
-// It also sets the gateway if the gateway is empty and it sets
+// It also sets the gateway if the gateway is empty and addGateway is set to true
// IPv6Enabled to true if at least one subnet is ipv6.
-func ValidateSubnets(network *types.Network, usedNetworks []*net.IPNet) error {
+func ValidateSubnets(network *types.Network, addGateway bool, usedNetworks []*net.IPNet) error {
for i := range network.Subnets {
- err := ValidateSubnet(&network.Subnets[i], !network.Internal, usedNetworks)
+ err := ValidateSubnet(&network.Subnets[i], addGateway, usedNetworks)
if err != nil {
return err
}
diff --git a/vendor/github.com/containers/common/libnetwork/netavark/config.go b/vendor/github.com/containers/common/libnetwork/netavark/config.go
index 7de59f807..16b4e5c53 100644
--- a/vendor/github.com/containers/common/libnetwork/netavark/config.go
+++ b/vendor/github.com/containers/common/libnetwork/netavark/config.go
@@ -115,16 +115,13 @@ func (n *netavarkNetwork) networkCreate(newNetwork *types.Network, defaultNet bo
return nil, errors.Wrapf(types.ErrInvalidArg, "unsupported driver %s", newNetwork.Driver)
}
- err = internalutil.ValidateSubnets(newNetwork, usedNetworks)
+ // add gatway when not internal or dns enabled
+ addGateway := !newNetwork.Internal || newNetwork.DNSEnabled
+ err = internalutil.ValidateSubnets(newNetwork, addGateway, usedNetworks)
if err != nil {
return nil, err
}
- // FIXME: If we have a working solution for internal networks with dns this check should be removed.
- if newNetwork.DNSEnabled && newNetwork.Internal {
- return nil, errors.New("cannot set internal and dns enabled")
- }
-
newNetwork.Created = time.Now()
if !defaultNet {
diff --git a/vendor/github.com/containers/common/libnetwork/netavark/network.go b/vendor/github.com/containers/common/libnetwork/netavark/network.go
index 7122acf98..efea36fec 100644
--- a/vendor/github.com/containers/common/libnetwork/netavark/network.go
+++ b/vendor/github.com/containers/common/libnetwork/netavark/network.go
@@ -231,7 +231,9 @@ func parseNetwork(network *types.Network) error {
return errors.Errorf("invalid network ID %q", network.ID)
}
- return util.ValidateSubnets(network, nil)
+ // add gatway when not internal or dns enabled
+ addGateway := !network.Internal || network.DNSEnabled
+ return util.ValidateSubnets(network, addGateway, nil)
}
func (n *netavarkNetwork) createDefaultNetwork() (*types.Network, error) {
diff --git a/vendor/github.com/containers/common/pkg/auth/auth.go b/vendor/github.com/containers/common/pkg/auth/auth.go
index ff52b028e..af3c8f803 100644
--- a/vendor/github.com/containers/common/pkg/auth/auth.go
+++ b/vendor/github.com/containers/common/pkg/auth/auth.go
@@ -248,7 +248,7 @@ func getUserAndPass(opts *LoginOptions, password, userFromAuthFile string) (user
}
if password == "" {
fmt.Fprint(opts.Stdout, "Password: ")
- pass, err := terminal.ReadPassword(0)
+ pass, err := terminal.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
return "", "", errors.Wrap(err, "reading password")
}
diff --git a/vendor/github.com/containers/common/version/version.go b/vendor/github.com/containers/common/version/version.go
index 5ab8cd7f2..eac64b077 100644
--- a/vendor/github.com/containers/common/version/version.go
+++ b/vendor/github.com/containers/common/version/version.go
@@ -1,4 +1,4 @@
package version
// Version is the version of the build.
-const Version = "0.47.3"
+const Version = "0.47.4"
diff --git a/vendor/github.com/coreos/stream-metadata-go/release/release.go b/vendor/github.com/coreos/stream-metadata-go/release/release.go
new file mode 100644
index 000000000..84a032703
--- /dev/null
+++ b/vendor/github.com/coreos/stream-metadata-go/release/release.go
@@ -0,0 +1,112 @@
+// Package release contains APIs for interacting with a
+// particular "release". Avoid this unless you are sure
+// you need it. It's expected that CoreOS users interact
+// with streams instead.
+package release
+
+import (
+ relrhcos "github.com/coreos/stream-metadata-go/release/rhcos"
+)
+
+// Index models the release index:
+// https://github.com/coreos/fedora-coreos-tracker/tree/master/metadata/release-index
+type Index struct {
+ Note string `json:"note"` // used to note to users not to consume the release metadata index
+ Releases []IndexRelease `json:"releases"`
+ Metadata Metadata `json:"metadata"`
+ Stream string `json:"stream"`
+}
+
+// IndexRelease is a "release pointer" from a release index
+type IndexRelease struct {
+ Commits []IndexReleaseCommit `json:"commits"`
+ Version string `json:"version"`
+ MetadataURL string `json:"metadata"`
+}
+
+// IndexReleaseCommit describes an ostree commit plus architecture
+type IndexReleaseCommit struct {
+ Architecture string `json:"architecture"`
+ Checksum string `json:"checksum"`
+}
+
+// Release contains details from release.json
+type Release struct {
+ Release string `json:"release"`
+ Stream string `json:"stream"`
+ Metadata Metadata `json:"metadata"`
+ Architectures map[string]Arch `json:"architectures"`
+}
+
+// Metadata is common metadata that contains last-modified
+type Metadata struct {
+ LastModified string `json:"last-modified"`
+}
+
+// Arch release details
+type Arch struct {
+ Commit string `json:"commit"`
+ Media Media `json:"media"`
+ RHELCoreOSExtensions *relrhcos.Extensions `json:"rhel-coreos-extensions,omitempty"`
+}
+
+// Media contains release details for various platforms
+type Media struct {
+ Aliyun *PlatformBase `json:"aliyun"`
+ Aws *PlatformAws `json:"aws"`
+ Azure *PlatformBase `json:"azure"`
+ Digitalocean *PlatformBase `json:"digitalocean"`
+ Exoscale *PlatformBase `json:"exoscale"`
+ Gcp *PlatformGcp `json:"gcp"`
+ Ibmcloud *PlatformBase `json:"ibmcloud"`
+ Metal *PlatformBase `json:"metal"`
+ Openstack *PlatformBase `json:"openstack"`
+ Qemu *PlatformBase `json:"qemu"`
+ Vmware *PlatformBase `json:"vmware"`
+ Vultr *PlatformBase `json:"vultr"`
+}
+
+// PlatformBase with no cloud images
+type PlatformBase struct {
+ Artifacts map[string]ImageFormat `json:"artifacts"`
+}
+
+// PlatformAws contains AWS image information
+type PlatformAws struct {
+ PlatformBase
+ Images map[string]CloudImage `json:"images"`
+}
+
+// PlatformGcp GCP image detail
+type PlatformGcp struct {
+ PlatformBase
+ Image *GcpImage `json:"image"`
+}
+
+// ImageFormat contains all artifacts for a single OS image
+type ImageFormat struct {
+ Disk *Artifact `json:"disk,omitempty"`
+ Kernel *Artifact `json:"kernel,omitempty"`
+ Initramfs *Artifact `json:"initramfs,omitempty"`
+ Rootfs *Artifact `json:"rootfs,omitempty"`
+}
+
+// Artifact represents one image file, plus its metadata
+type Artifact struct {
+ Location string `json:"location"`
+ Signature string `json:"signature"`
+ Sha256 string `json:"sha256"`
+ UncompressedSha256 string `json:"uncompressed-sha256,omitempty"`
+}
+
+// CloudImage generic image detail
+type CloudImage struct {
+ Image string `json:"image"`
+}
+
+// GcpImage represents a GCP cloud image
+type GcpImage struct {
+ Project string `json:"project,omitempty"`
+ Family string `json:"family,omitempty"`
+ Name string `json:"name,omitempty"`
+}
diff --git a/vendor/github.com/coreos/stream-metadata-go/release/rhcos/rhcos.go b/vendor/github.com/coreos/stream-metadata-go/release/rhcos/rhcos.go
new file mode 100644
index 000000000..aeae2c8be
--- /dev/null
+++ b/vendor/github.com/coreos/stream-metadata-go/release/rhcos/rhcos.go
@@ -0,0 +1,14 @@
+package rhcos
+
+// Extensions is data specific to Red Hat Enterprise Linux CoreOS
+type Extensions struct {
+ AzureDisk *AzureDisk `json:"azure-disk,omitempty"`
+}
+
+// AzureDisk represents an Azure cloud image.
+type AzureDisk struct {
+ // URL to an image already stored in Azure infrastructure
+ // that can be copied into an image gallery. Avoid creating VMs directly
+ // from this URL as that may lead to performance limitations.
+ URL string `json:"url,omitempty"`
+}
diff --git a/vendor/github.com/coreos/stream-metadata-go/release/translate.go b/vendor/github.com/coreos/stream-metadata-go/release/translate.go
new file mode 100644
index 000000000..518c75eb9
--- /dev/null
+++ b/vendor/github.com/coreos/stream-metadata-go/release/translate.go
@@ -0,0 +1,196 @@
+package release
+
+import (
+ "github.com/coreos/stream-metadata-go/stream"
+ "github.com/coreos/stream-metadata-go/stream/rhcos"
+)
+
+func mapArtifact(ra *Artifact) *stream.Artifact {
+ if ra == nil {
+ return nil
+ }
+ return &stream.Artifact{
+ Location: ra.Location,
+ Signature: ra.Signature,
+ Sha256: ra.Sha256,
+ UncompressedSha256: ra.UncompressedSha256,
+ }
+}
+
+func mapFormats(m map[string]ImageFormat) map[string]stream.ImageFormat {
+ r := make(map[string]stream.ImageFormat)
+ for k, v := range m {
+ r[k] = stream.ImageFormat{
+ Disk: mapArtifact(v.Disk),
+ Kernel: mapArtifact(v.Kernel),
+ Initramfs: mapArtifact(v.Initramfs),
+ Rootfs: mapArtifact(v.Rootfs),
+ }
+ }
+ return r
+}
+
+// Convert a release architecture to a stream architecture
+func (releaseArch *Arch) toStreamArch(rel *Release) stream.Arch {
+ artifacts := make(map[string]stream.PlatformArtifacts)
+ cloudImages := stream.Images{}
+ var rhcosExt *rhcos.Extensions
+ relRHCOSExt := releaseArch.RHELCoreOSExtensions
+ if relRHCOSExt != nil {
+ rhcosExt = &rhcos.Extensions{}
+ }
+ if releaseArch.Media.Aws != nil {
+ artifacts["aws"] = stream.PlatformArtifacts{
+ Release: rel.Release,
+ Formats: mapFormats(releaseArch.Media.Aws.Artifacts),
+ }
+ awsAmis := stream.AwsImage{
+ Regions: make(map[string]stream.AwsRegionImage),
+ }
+ if releaseArch.Media.Aws.Images != nil {
+ for region, ami := range releaseArch.Media.Aws.Images {
+ ri := stream.AwsRegionImage{Release: rel.Release, Image: ami.Image}
+ awsAmis.Regions[region] = ri
+
+ }
+ cloudImages.Aws = &awsAmis
+ }
+ }
+
+ if releaseArch.Media.Azure != nil {
+ artifacts["azure"] = stream.PlatformArtifacts{
+ Release: rel.Release,
+ Formats: mapFormats(releaseArch.Media.Azure.Artifacts),
+ }
+
+ if relRHCOSExt != nil {
+ az := relRHCOSExt.AzureDisk
+ if az != nil {
+ rhcosExt.AzureDisk = &rhcos.AzureDisk{
+ Release: rel.Release,
+ URL: az.URL,
+ }
+ }
+ }
+ // In the future this is where we'd also add FCOS Marketplace data.
+ // See https://github.com/coreos/stream-metadata-go/issues/13
+ }
+
+ if releaseArch.Media.Aliyun != nil {
+ artifacts["aliyun"] = stream.PlatformArtifacts{
+ Release: rel.Release,
+ Formats: mapFormats(releaseArch.Media.Aliyun.Artifacts),
+ }
+ }
+
+ if releaseArch.Media.Exoscale != nil {
+ artifacts["exoscale"] = stream.PlatformArtifacts{
+ Release: rel.Release,
+ Formats: mapFormats(releaseArch.Media.Exoscale.Artifacts),
+ }
+ }
+
+ if releaseArch.Media.Vultr != nil {
+ artifacts["vultr"] = stream.PlatformArtifacts{
+ Release: rel.Release,
+ Formats: mapFormats(releaseArch.Media.Vultr.Artifacts),
+ }
+ }
+
+ if releaseArch.Media.Gcp != nil {
+ artifacts["gcp"] = stream.PlatformArtifacts{
+ Release: rel.Release,
+ Formats: mapFormats(releaseArch.Media.Gcp.Artifacts),
+ }
+
+ if releaseArch.Media.Gcp.Image != nil {
+ cloudImages.Gcp = &stream.GcpImage{
+ Name: releaseArch.Media.Gcp.Image.Name,
+ Family: releaseArch.Media.Gcp.Image.Family,
+ Project: releaseArch.Media.Gcp.Image.Project,
+ }
+ }
+ }
+
+ if releaseArch.Media.Digitalocean != nil {
+ artifacts["digitalocean"] = stream.PlatformArtifacts{
+ Release: rel.Release,
+ Formats: mapFormats(releaseArch.Media.Digitalocean.Artifacts),
+ }
+
+ /* We're producing artifacts but they're not yet available
+ in DigitalOcean as distribution images.
+ digitalOceanImage := stream.CloudImage{Image: fmt.Sprintf("fedora-coreos-%s", Stream)}
+ cloudImages.Digitalocean = &digitalOceanImage
+ */
+ }
+
+ if releaseArch.Media.Ibmcloud != nil {
+ artifacts["ibmcloud"] = stream.PlatformArtifacts{
+ Release: rel.Release,
+ Formats: mapFormats(releaseArch.Media.Ibmcloud.Artifacts),
+ }
+ }
+
+ // if releaseArch.Media.Packet != nil {
+ // packet := StreamMediaDetails{
+ // Release: rel.Release,
+ // Formats: releaseArch.Media.Packet.Artifacts,
+ // }
+ // artifacts.Packet = &packet
+
+ // packetImage := StreamCloudImage{Image: fmt.Sprintf("fedora_coreos_%s", rel.Stream)}
+ // cloudImages.Packet = &packetImage
+ // }
+
+ if releaseArch.Media.Openstack != nil {
+ artifacts["openstack"] = stream.PlatformArtifacts{
+ Release: rel.Release,
+ Formats: mapFormats(releaseArch.Media.Openstack.Artifacts),
+ }
+ }
+
+ if releaseArch.Media.Qemu != nil {
+ artifacts["qemu"] = stream.PlatformArtifacts{
+ Release: rel.Release,
+ Formats: mapFormats(releaseArch.Media.Qemu.Artifacts),
+ }
+ }
+
+ // if releaseArch.Media.Virtualbox != nil {
+ // virtualbox := StreamMediaDetails{
+ // Release: rel.Release,
+ // Formats: releaseArch.Media.Virtualbox.Artifacts,
+ // }
+ // artifacts.Virtualbox = &virtualbox
+ // }
+
+ if releaseArch.Media.Vmware != nil {
+ artifacts["vmware"] = stream.PlatformArtifacts{
+ Release: rel.Release,
+ Formats: mapFormats(releaseArch.Media.Vmware.Artifacts),
+ }
+ }
+
+ if releaseArch.Media.Metal != nil {
+ artifacts["metal"] = stream.PlatformArtifacts{
+ Release: rel.Release,
+ Formats: mapFormats(releaseArch.Media.Metal.Artifacts),
+ }
+ }
+
+ return stream.Arch{
+ Artifacts: artifacts,
+ Images: cloudImages,
+ RHELCoreOSExtensions: rhcosExt,
+ }
+}
+
+// ToStreamArchitectures converts a release to a stream
+func (rel *Release) ToStreamArchitectures() map[string]stream.Arch {
+ streamArch := make(map[string]stream.Arch)
+ for arch, releaseArch := range rel.Architectures {
+ streamArch[arch] = releaseArch.toStreamArch(rel)
+ }
+ return streamArch
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 909c2707e..f6042a041 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -109,7 +109,7 @@ github.com/containers/buildah/pkg/rusage
github.com/containers/buildah/pkg/sshagent
github.com/containers/buildah/pkg/util
github.com/containers/buildah/util
-# github.com/containers/common v0.47.3
+# github.com/containers/common v0.47.4
## explicit
github.com/containers/common/libimage
github.com/containers/common/libimage/manifests
@@ -291,6 +291,8 @@ github.com/coreos/go-systemd/v22/sdjournal
## explicit
github.com/coreos/stream-metadata-go/fedoracoreos
github.com/coreos/stream-metadata-go/fedoracoreos/internals
+github.com/coreos/stream-metadata-go/release
+github.com/coreos/stream-metadata-go/release/rhcos
github.com/coreos/stream-metadata-go/stream
github.com/coreos/stream-metadata-go/stream/rhcos
# github.com/cyphar/filepath-securejoin v0.2.3