summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/images/build.go34
-rw-r--r--cmd/podman/system/service_abi.go30
-rw-r--r--libpod/events/logfile.go4
-rw-r--r--libpod/runtime_img.go3
-rw-r--r--pkg/api/handlers/compat/images_build.go11
-rw-r--r--pkg/api/handlers/compat/version.go6
-rw-r--r--pkg/api/handlers/types.go4
-rw-r--r--pkg/api/handlers/types/types.go23
-rw-r--r--pkg/api/server/swagger.go2
-rw-r--r--pkg/bindings/images/build.go1
-rw-r--r--pkg/bindings/images/images.go6
-rw-r--r--pkg/bindings/images/rm.go4
-rw-r--r--pkg/bindings/images/types.go4
-rw-r--r--pkg/domain/entities/images.go41
-rw-r--r--pkg/domain/entities/system.go2
-rw-r--r--pkg/domain/entities/types.go4
-rw-r--r--pkg/domain/entities/types/auth.go22
-rw-r--r--pkg/domain/entities/types/types.go28
-rw-r--r--pkg/domain/entities/volumes.go96
-rw-r--r--pkg/domain/infra/abi/play.go28
-rw-r--r--pkg/domain/infra/abi/system.go2
-rw-r--r--pkg/domain/infra/runtime_libpod.go8
-rw-r--r--pkg/namespaces/namespaces.go10
-rw-r--r--pkg/rootless/rootless.go4
-rw-r--r--pkg/rootless/rootless_linux.c11
-rw-r--r--pkg/specgen/generate/container_create.go4
-rw-r--r--pkg/specgen/generate/kube/kube.go100
-rw-r--r--pkg/specgen/generate/kube/play_test.go359
-rw-r--r--pkg/specgen/specgen.go4
-rw-r--r--pkg/util/utils.go14
-rw-r--r--test/e2e/play_kube_test.go236
31 files changed, 943 insertions, 162 deletions
diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go
index da6d556b1..da7f5d862 100644
--- a/cmd/podman/images/build.go
+++ b/cmd/podman/images/build.go
@@ -8,7 +8,7 @@ import (
"strings"
"time"
- "github.com/containers/buildah/define"
+ buildahDefine "github.com/containers/buildah/define"
buildahCLI "github.com/containers/buildah/pkg/cli"
"github.com/containers/buildah/pkg/parse"
"github.com/containers/common/pkg/completion"
@@ -158,11 +158,11 @@ func buildFlags(cmd *cobra.Command) {
flags.SetNormalizeFunc(buildahCLI.AliasFlags)
if registry.IsRemote() {
flag = flags.Lookup("isolation")
- buildOpts.Isolation = define.OCI
- if err := flag.Value.Set(define.OCI); err != nil {
- logrus.Errorf("unable to set --isolation to %v: %v", define.OCI, err)
+ buildOpts.Isolation = buildahDefine.OCI
+ if err := flag.Value.Set(buildahDefine.OCI); err != nil {
+ logrus.Errorf("unable to set --isolation to %v: %v", buildahDefine.OCI, err)
}
- flag.DefValue = define.OCI
+ flag.DefValue = buildahDefine.OCI
_ = flags.MarkHidden("disable-content-trust")
_ = flags.MarkHidden("cache-from")
_ = flags.MarkHidden("sign-by")
@@ -195,7 +195,7 @@ func build(cmd *cobra.Command, args []string) error {
var contextDir string
if len(args) > 0 {
// The context directory could be a URL. Try to handle that.
- tempDir, subDir, err := define.TempDirForURL("", "buildah", args[0])
+ tempDir, subDir, err := buildahDefine.TempDirForURL("", "buildah", args[0])
if err != nil {
return errors.Wrapf(err, "error prepping temporary context directory")
}
@@ -318,16 +318,16 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
return nil, errors.Errorf("can only set one of 'pull' or 'pull-always' or 'pull-never'")
}
- pullPolicy := define.PullIfMissing
+ pullPolicy := buildahDefine.PullIfMissing
if c.Flags().Changed("pull") && flags.Pull {
- pullPolicy = define.PullAlways
+ pullPolicy = buildahDefine.PullAlways
}
if flags.PullAlways {
- pullPolicy = define.PullAlways
+ pullPolicy = buildahDefine.PullAlways
}
if flags.PullNever {
- pullPolicy = define.PullNever
+ pullPolicy = buildahDefine.PullNever
}
args := make(map[string]string)
@@ -402,9 +402,9 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
flags.Layers = false
}
- compression := define.Gzip
+ compression := buildahDefine.Gzip
if flags.DisableCompression {
- compression = define.Uncompressed
+ compression = buildahDefine.Uncompressed
}
isolation, err := parse.IsolationOption(flags.Isolation)
@@ -426,10 +426,10 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
format := ""
flags.Format = strings.ToLower(flags.Format)
switch {
- case strings.HasPrefix(flags.Format, define.OCI):
- format = define.OCIv1ImageManifest
- case strings.HasPrefix(flags.Format, define.DOCKER):
- format = define.Dockerv2ImageManifest
+ case strings.HasPrefix(flags.Format, buildahDefine.OCI):
+ format = buildahDefine.OCIv1ImageManifest
+ case strings.HasPrefix(flags.Format, buildahDefine.DOCKER):
+ format = buildahDefine.Dockerv2ImageManifest
default:
return nil, errors.Errorf("unrecognized image type %q", flags.Format)
}
@@ -457,7 +457,7 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
return nil, errors.Wrapf(err, "unable to obtain decrypt config")
}
- opts := define.BuildOptions{
+ opts := buildahDefine.BuildOptions{
AddCapabilities: flags.CapAdd,
AdditionalTags: tags,
Annotations: flags.Annotation,
diff --git a/cmd/podman/system/service_abi.go b/cmd/podman/system/service_abi.go
index 0e96e1fbb..9e8a9f9b4 100644
--- a/cmd/podman/system/service_abi.go
+++ b/cmd/podman/system/service_abi.go
@@ -24,16 +24,28 @@ func restService(opts entities.ServiceOptions, flags *pflag.FlagSet, cfg *entiti
)
if opts.URI != "" {
- fields := strings.Split(opts.URI, ":")
- if len(fields) == 1 {
- return errors.Errorf("%s is an invalid socket destination", opts.URI)
+ if os.Getenv("LISTEN_FDS") != "" {
+ // If it is activated by systemd, use the first LISTEN_FD (3)
+ // instead of opening the socket file.
+ f := os.NewFile(uintptr(3), "podman.sock")
+ l, err := net.FileListener(f)
+ if err != nil {
+ return err
+ }
+ listener = &l
+ } else {
+ fields := strings.Split(opts.URI, ":")
+ if len(fields) == 1 {
+ return errors.Errorf("%s is an invalid socket destination", opts.URI)
+ }
+ network := fields[0]
+ address := strings.Join(fields[1:], ":")
+ l, err := net.Listen(network, address)
+ if err != nil {
+ return errors.Wrapf(err, "unable to create socket")
+ }
+ listener = &l
}
- address := strings.Join(fields[1:], ":")
- l, err := net.Listen(fields[0], address)
- if err != nil {
- return errors.Wrapf(err, "unable to create socket")
- }
- listener = &l
}
// Close stdin, so shortnames will not prompt
diff --git a/libpod/events/logfile.go b/libpod/events/logfile.go
index 7a32ea311..0f00525e8 100644
--- a/libpod/events/logfile.go
+++ b/libpod/events/logfile.go
@@ -7,7 +7,7 @@ import (
"time"
"github.com/containers/podman/v3/pkg/util"
- "github.com/containers/storage"
+ "github.com/containers/storage/pkg/lockfile"
"github.com/pkg/errors"
)
@@ -20,7 +20,7 @@ type EventLogFile struct {
// Writes to the log file
func (e EventLogFile) Write(ee Event) error {
// We need to lock events file
- lock, err := storage.GetLockfile(e.options.LogFilePath + ".lock")
+ lock, err := lockfile.GetLockfile(e.options.LogFilePath + ".lock")
if err != nil {
return err
}
diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go
index 13ac42e7d..b427125db 100644
--- a/libpod/runtime_img.go
+++ b/libpod/runtime_img.go
@@ -9,6 +9,7 @@ import (
"net/url"
"os"
+ buildahDefine "github.com/containers/buildah/define"
"github.com/containers/buildah/imagebuildah"
"github.com/containers/image/v5/directory"
"github.com/containers/image/v5/docker/reference"
@@ -165,7 +166,7 @@ func (r *Runtime) newImageBuildCompleteEvent(idOrName string) {
}
// Build adds the runtime to the imagebuildah call
-func (r *Runtime) Build(ctx context.Context, options imagebuildah.BuildOptions, dockerfiles ...string) (string, reference.Canonical, error) {
+func (r *Runtime) Build(ctx context.Context, options buildahDefine.BuildOptions, dockerfiles ...string) (string, reference.Canonical, error) {
if options.Runtime == "" {
// Make sure that build containers use the same runtime as Podman (see #9365).
conf := util.DefaultContainerConfig()
diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go
index 36785a362..fd310711f 100644
--- a/pkg/api/handlers/compat/images_build.go
+++ b/pkg/api/handlers/compat/images_build.go
@@ -13,8 +13,7 @@ import (
"time"
"github.com/containers/buildah"
- "github.com/containers/buildah/define"
- "github.com/containers/buildah/imagebuildah"
+ buildahDefine "github.com/containers/buildah/define"
"github.com/containers/buildah/util"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v3/libpod"
@@ -277,13 +276,13 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
jobs = query.Jobs
}
- pullPolicy := define.PullIfMissing
+ pullPolicy := buildahDefine.PullIfMissing
if utils.IsLibpodRequest(r) {
- pullPolicy = define.PolicyMap[query.PullPolicy]
+ pullPolicy = buildahDefine.PolicyMap[query.PullPolicy]
} else {
if _, found := r.URL.Query()["pull"]; found {
if query.Pull {
- pullPolicy = define.PullAlways
+ pullPolicy = buildahDefine.PullAlways
}
}
}
@@ -315,7 +314,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
return
}
- buildOptions := imagebuildah.BuildOptions{
+ buildOptions := buildahDefine.BuildOptions{
AddCapabilities: addCaps,
AdditionalTags: additionalTags,
Annotations: annotations,
diff --git a/pkg/api/handlers/compat/version.go b/pkg/api/handlers/compat/version.go
index fae147440..f1cd77a9a 100644
--- a/pkg/api/handlers/compat/version.go
+++ b/pkg/api/handlers/compat/version.go
@@ -10,8 +10,8 @@ import (
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/pkg/api/handlers/utils"
"github.com/containers/podman/v3/pkg/domain/entities"
+ "github.com/containers/podman/v3/pkg/domain/entities/types"
"github.com/containers/podman/v3/version"
- docker "github.com/docker/docker/api/types"
"github.com/pkg/errors"
)
@@ -32,7 +32,7 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) {
return
}
- components := []docker.ComponentVersion{{
+ components := []types.ComponentVersion{{
Name: "Podman Engine",
Version: versionInfo.Version,
Details: map[string]string{
@@ -52,7 +52,7 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) {
minVersion := version.APIVersion[version.Compat][version.MinimalAPI]
utils.WriteResponse(w, http.StatusOK, entities.ComponentVersion{
- Version: docker.Version{
+ Version: types.Version{
Platform: struct {
Name string
}{
diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go
index f5eaf6f6d..736203171 100644
--- a/pkg/api/handlers/types.go
+++ b/pkg/api/handlers/types.go
@@ -28,10 +28,6 @@ type ContainerConfig struct {
dockerContainer.Config
}
-type LibpodImagesLoadReport struct {
- ID string `json:"id"`
-}
-
type LibpodImagesPullReport struct {
entities.ImagePullReport
}
diff --git a/pkg/api/handlers/types/types.go b/pkg/api/handlers/types/types.go
new file mode 100644
index 000000000..71165364f
--- /dev/null
+++ b/pkg/api/handlers/types/types.go
@@ -0,0 +1,23 @@
+package types
+
+import (
+ "github.com/containers/podman/v3/pkg/domain/entities"
+)
+
+// LibpodImagesRemoveReport is the return type for image removal via the rest
+// api.
+type LibpodImagesRemoveReport struct {
+ entities.ImageRemoveReport
+ // Image removal requires is to return data and an error.
+ Errors []string
+}
+
+// HistoryResponse provides details on image layers
+type HistoryResponse struct {
+ ID string `json:"Id"`
+ Created int64
+ CreatedBy string
+ Tags []string
+ Size int64
+ Comment string
+}
diff --git a/pkg/api/server/swagger.go b/pkg/api/server/swagger.go
index 12fd083bb..d282edf23 100644
--- a/pkg/api/server/swagger.go
+++ b/pkg/api/server/swagger.go
@@ -205,7 +205,7 @@ type swagHealthCheckRunResponse struct {
type swagVersion struct {
// in:body
Body struct {
- entities.SystemVersionReport
+ entities.ComponentVersion
}
}
diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go
index 17095b84b..c79d79136 100644
--- a/pkg/bindings/images/build.go
+++ b/pkg/bindings/images/build.go
@@ -174,7 +174,6 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
if len(platform) > 0 {
params.Set("platform", platform)
}
-
params.Set("pullpolicy", options.PullPolicy.String())
if options.Quiet {
diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go
index 1be2bdfdd..8680d6baa 100644
--- a/pkg/bindings/images/images.go
+++ b/pkg/bindings/images/images.go
@@ -8,7 +8,7 @@ import (
"net/url"
"strconv"
- "github.com/containers/podman/v3/pkg/api/handlers"
+ "github.com/containers/podman/v3/pkg/api/handlers/types"
"github.com/containers/podman/v3/pkg/auth"
"github.com/containers/podman/v3/pkg/bindings"
"github.com/containers/podman/v3/pkg/domain/entities"
@@ -96,12 +96,12 @@ func Tree(ctx context.Context, nameOrID string, options *TreeOptions) (*entities
}
// History returns the parent layers of an image.
-func History(ctx context.Context, nameOrID string, options *HistoryOptions) ([]*handlers.HistoryResponse, error) {
+func History(ctx context.Context, nameOrID string, options *HistoryOptions) ([]*types.HistoryResponse, error) {
if options == nil {
options = new(HistoryOptions)
}
_ = options
- var history []*handlers.HistoryResponse
+ var history []*types.HistoryResponse
conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
diff --git a/pkg/bindings/images/rm.go b/pkg/bindings/images/rm.go
index beecce7bf..e45e583f4 100644
--- a/pkg/bindings/images/rm.go
+++ b/pkg/bindings/images/rm.go
@@ -4,7 +4,7 @@ import (
"context"
"net/http"
- "github.com/containers/podman/v3/pkg/api/handlers"
+ "github.com/containers/podman/v3/pkg/api/handlers/types"
"github.com/containers/podman/v3/pkg/bindings"
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/containers/podman/v3/pkg/errorhandling"
@@ -19,7 +19,7 @@ func Remove(ctx context.Context, images []string, options *RemoveOptions) (*enti
// FIXME - bindings tests are missing for this endpoint. Once the CI is
// re-enabled for bindings, we need to add them. At the time of writing,
// the tests don't compile.
- var report handlers.LibpodImagesRemoveReport
+ var report types.LibpodImagesRemoveReport
conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, []error{err}
diff --git a/pkg/bindings/images/types.go b/pkg/bindings/images/types.go
index 7bf70c82b..1f3e46729 100644
--- a/pkg/bindings/images/types.go
+++ b/pkg/bindings/images/types.go
@@ -1,7 +1,7 @@
package images
import (
- "github.com/containers/buildah/imagebuildah"
+ buildahDefine "github.com/containers/buildah/define"
)
//go:generate go run ../generator/generator.go RemoveOptions
@@ -162,7 +162,7 @@ type PullOptions struct {
//BuildOptions are optional options for building images
type BuildOptions struct {
- imagebuildah.BuildOptions
+ buildahDefine.BuildOptions
}
//go:generate go run ../generator/generator.go ExistsOptions
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index 7999d8209..3cc46ed0a 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -8,33 +8,32 @@ import (
"github.com/containers/image/v5/types"
"github.com/containers/podman/v3/pkg/inspect"
"github.com/containers/podman/v3/pkg/trust"
- docker "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/opencontainers/go-digest"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
)
type Image struct {
- ID string `json:"Id"`
- RepoTags []string `json:",omitempty"`
- RepoDigests []string `json:",omitempty"`
- Parent string `json:",omitempty"`
- Comment string `json:",omitempty"`
- Created string `json:",omitempty"`
- Container string `json:",omitempty"`
- ContainerConfig *container.Config `json:",omitempty"`
- DockerVersion string `json:",omitempty"`
- Author string `json:",omitempty"`
- Config *container.Config `json:",omitempty"`
- Architecture string `json:",omitempty"`
- Variant string `json:",omitempty"`
- Os string `json:",omitempty"`
- OsVersion string `json:",omitempty"`
- Size int64 `json:",omitempty"`
- VirtualSize int64 `json:",omitempty"`
- GraphDriver docker.GraphDriverData `json:",omitempty"`
- RootFS docker.RootFS `json:",omitempty"`
- Metadata docker.ImageMetadata `json:",omitempty"`
+ ID string `json:"Id"`
+ RepoTags []string `json:",omitempty"`
+ RepoDigests []string `json:",omitempty"`
+ Parent string `json:",omitempty"`
+ Comment string `json:",omitempty"`
+ Created string `json:",omitempty"`
+ Container string `json:",omitempty"`
+ ContainerConfig *container.Config `json:",omitempty"`
+ DockerVersion string `json:",omitempty"`
+ Author string `json:",omitempty"`
+ Config *container.Config `json:",omitempty"`
+ Architecture string `json:",omitempty"`
+ Variant string `json:",omitempty"`
+ Os string `json:",omitempty"`
+ OsVersion string `json:",omitempty"`
+ Size int64 `json:",omitempty"`
+ VirtualSize int64 `json:",omitempty"`
+ GraphDriver string `json:",omitempty"`
+ RootFS string `json:",omitempty"`
+ Metadata string `json:",omitempty"`
// Podman extensions
Digest digest.Digest `json:",omitempty"`
diff --git a/pkg/domain/entities/system.go b/pkg/domain/entities/system.go
index 4b8383613..1a671d59e 100644
--- a/pkg/domain/entities/system.go
+++ b/pkg/domain/entities/system.go
@@ -5,7 +5,7 @@ import (
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/pkg/domain/entities/reports"
- "github.com/docker/docker/api/types"
+ "github.com/containers/podman/v3/pkg/domain/entities/types"
"github.com/spf13/cobra"
)
diff --git a/pkg/domain/entities/types.go b/pkg/domain/entities/types.go
index 9efc64c93..02e374111 100644
--- a/pkg/domain/entities/types.go
+++ b/pkg/domain/entities/types.go
@@ -3,7 +3,7 @@ package entities
import (
"net"
- "github.com/containers/buildah/imagebuildah"
+ buildahDefine "github.com/containers/buildah/define"
"github.com/containers/podman/v3/libpod/events"
"github.com/containers/podman/v3/pkg/specgen"
"github.com/containers/storage/pkg/archive"
@@ -91,7 +91,7 @@ type ContainerCreateResponse struct {
// BuildOptions describe the options for building container images.
type BuildOptions struct {
- imagebuildah.BuildOptions
+ buildahDefine.BuildOptions
}
// BuildReport is the image-build report.
diff --git a/pkg/domain/entities/types/auth.go b/pkg/domain/entities/types/auth.go
new file mode 100644
index 000000000..ddf15bb18
--- /dev/null
+++ b/pkg/domain/entities/types/auth.go
@@ -0,0 +1,22 @@
+package types // import "github.com/docker/docker/api/types"
+
+// AuthConfig contains authorization information for connecting to a Registry
+type AuthConfig struct {
+ Username string `json:"username,omitempty"`
+ Password string `json:"password,omitempty"`
+ Auth string `json:"auth,omitempty"`
+
+ // Email is an optional value associated with the username.
+ // This field is deprecated and will be removed in a later
+ // version of docker.
+ Email string `json:"email,omitempty"`
+
+ ServerAddress string `json:"serveraddress,omitempty"`
+
+ // IdentityToken is used to authenticate the user and get
+ // an access token for the registry.
+ IdentityToken string `json:"identitytoken,omitempty"`
+
+ // RegistryToken is a bearer token to be sent to a registry
+ RegistryToken string `json:"registrytoken,omitempty"`
+}
diff --git a/pkg/domain/entities/types/types.go b/pkg/domain/entities/types/types.go
new file mode 100644
index 000000000..77834c0cb
--- /dev/null
+++ b/pkg/domain/entities/types/types.go
@@ -0,0 +1,28 @@
+package types // import "github.com/docker/docker/api/types"
+
+// ComponentVersion describes the version information for a specific component.
+type ComponentVersion struct {
+ Name string
+ Version string
+ Details map[string]string `json:",omitempty"`
+}
+
+// Version contains response of Engine API:
+// GET "/version"
+type Version struct {
+ Platform struct{ Name string } `json:",omitempty"`
+ Components []ComponentVersion `json:",omitempty"`
+
+ // The following fields are deprecated, they relate to the Engine component and are kept for backwards compatibility
+
+ Version string
+ APIVersion string `json:"ApiVersion"`
+ MinAPIVersion string `json:"MinAPIVersion,omitempty"`
+ GitCommit string
+ GoVersion string
+ Os string
+ Arch string
+ KernelVersion string `json:",omitempty"`
+ Experimental bool `json:",omitempty"`
+ BuildTime string `json:",omitempty"`
+}
diff --git a/pkg/domain/entities/volumes.go b/pkg/domain/entities/volumes.go
index beb2a75ac..55a6a1b14 100644
--- a/pkg/domain/entities/volumes.go
+++ b/pkg/domain/entities/volumes.go
@@ -4,10 +4,72 @@ import (
"net/url"
"github.com/containers/podman/v3/libpod/define"
- docker_api_types "github.com/docker/docker/api/types"
- docker_api_types_volume "github.com/docker/docker/api/types/volume"
)
+// Volume volume
+// swagger:model Volume
+type volume struct {
+
+ // Date/Time the volume was created.
+ CreatedAt string `json:"CreatedAt,omitempty"`
+
+ // Name of the volume driver used by the volume.
+ // Required: true
+ Driver string `json:"Driver"`
+
+ // User-defined key/value metadata.
+ // Required: true
+ Labels map[string]string `json:"Labels"`
+
+ // Mount path of the volume on the host.
+ // Required: true
+ Mountpoint string `json:"Mountpoint"`
+
+ // Name of the volume.
+ // Required: true
+ Name string `json:"Name"`
+
+ // The driver specific options used when creating the volume.
+ //
+ // Required: true
+ Options map[string]string `json:"Options"`
+
+ // The level at which the volume exists. Either `global` for cluster-wide,
+ // or `local` for machine level.
+ //
+ // Required: true
+ Scope string `json:"Scope"`
+
+ // Low-level details about the volume, provided by the volume driver.
+ // Details are returned as a map with key/value pairs:
+ // `{"key":"value","key2":"value2"}`.
+ //
+ // The `Status` field is optional, and is omitted if the volume driver
+ // does not support this feature.
+ //
+ Status map[string]interface{} `json:"Status,omitempty"`
+
+ // usage data
+ UsageData *VolumeUsageData `json:"UsageData,omitempty"`
+}
+
+type VolumeUsageData struct {
+
+ // The number of containers referencing this volume. This field
+ // is set to `-1` if the reference-count is not available.
+ //
+ // Required: true
+ RefCount int64 `json:"RefCount"`
+
+ // Amount of disk space used by the volume (in bytes). This information
+ // is only available for volumes created with the `"local"` volume
+ // driver. For volumes created with other volume drivers, this field
+ // is set to `-1` ("not available")
+ //
+ // Required: true
+ Size int64 `json:"Size"`
+}
+
// swagger:model VolumeCreate
type VolumeCreateOptions struct {
// New volume's name. Can be left blank
@@ -113,14 +175,14 @@ type SwagVolumeListResponse struct {
*/
// swagger:model DockerVolumeCreate
-type DockerVolumeCreate docker_api_types_volume.VolumeCreateBody
+type DockerVolumeCreate VolumeCreateBody
// This response definition is used for both the create and inspect endpoints
// swagger:response DockerVolumeInfoResponse
type SwagDockerVolumeInfoResponse struct {
// in:body
Body struct {
- docker_api_types.Volume
+ volume
}
}
@@ -129,6 +191,30 @@ type SwagDockerVolumeInfoResponse struct {
type SwagDockerVolumePruneResponse struct {
// in:body
Body struct {
- docker_api_types.VolumesPruneReport
+ // docker_api_types.VolumesPruneReport
}
}
+
+// VolumeCreateBody Volume configuration
+// swagger:model VolumeCreateBody
+type VolumeCreateBody struct {
+
+ // Name of the volume driver to use.
+ // Required: true
+ Driver string `json:"Driver"`
+
+ // A mapping of driver options and values. These options are
+ // passed directly to the driver and are driver specific.
+ //
+ // Required: true
+ DriverOpts map[string]string `json:"DriverOpts"`
+
+ // User-defined key/value metadata.
+ // Required: true
+ Labels map[string]string `json:"Labels"`
+
+ // The new volume's name. If not specified, Docker generates a name.
+ //
+ // Required: true
+ Name string `json:"Name"`
+}
diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go
index 7d87fc83a..3b5c141d7 100644
--- a/pkg/domain/infra/abi/play.go
+++ b/pkg/domain/infra/abi/play.go
@@ -9,6 +9,7 @@ import (
"os"
"strings"
+ "github.com/containers/common/pkg/secrets"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v3/libpod"
"github.com/containers/podman/v3/libpod/define"
@@ -135,6 +136,12 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
report entities.PlayKubeReport
)
+ // Create the secret manager before hand
+ secretsManager, err := secrets.NewManager(ic.Libpod.GetSecretsStorageDir())
+ if err != nil {
+ return nil, err
+ }
+
// check for name collision between pod and container
if podName == "" {
return nil, errors.Errorf("pod does not have a name")
@@ -261,16 +268,17 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
}
specgenOpts := kube.CtrSpecGenOptions{
- Container: container,
- Image: newImage,
- Volumes: volumes,
- PodID: pod.ID(),
- PodName: podName,
- PodInfraID: podInfraID,
- ConfigMaps: configMaps,
- SeccompPaths: seccompPaths,
- RestartPolicy: ctrRestartPolicy,
- NetNSIsHost: p.NetNS.IsHost(),
+ Container: container,
+ Image: newImage,
+ Volumes: volumes,
+ PodID: pod.ID(),
+ PodName: podName,
+ PodInfraID: podInfraID,
+ ConfigMaps: configMaps,
+ SeccompPaths: seccompPaths,
+ RestartPolicy: ctrRestartPolicy,
+ NetNSIsHost: p.NetNS.IsHost(),
+ SecretsManager: secretsManager,
}
specGen, err := kube.ToSpecGen(ctx, &specgenOpts)
if err != nil {
diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go
index 9f7c8919b..a3e753384 100644
--- a/pkg/domain/infra/abi/system.go
+++ b/pkg/domain/infra/abi/system.go
@@ -67,7 +67,7 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command)
if os.Geteuid() == 0 {
ownsCgroup, err := cgroups.UserOwnsCurrentSystemdCgroup()
if err != nil {
- logrus.Warnf("Failed to detect the owner for the current cgroup: %v", err)
+ logrus.Infof("Failed to detect the owner for the current cgroup: %v", err)
}
if !ownsCgroup {
conf, err := ic.Config(context.Background())
diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go
index 8b6581c7b..b0d9dc797 100644
--- a/pkg/domain/infra/runtime_libpod.go
+++ b/pkg/domain/infra/runtime_libpod.go
@@ -15,8 +15,8 @@ import (
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/containers/podman/v3/pkg/namespaces"
"github.com/containers/podman/v3/pkg/rootless"
- "github.com/containers/storage"
"github.com/containers/storage/pkg/idtools"
+ "github.com/containers/storage/types"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
flag "github.com/spf13/pflag"
@@ -100,7 +100,7 @@ func GetRuntimeNoStore(ctx context.Context, fs *flag.FlagSet, cfg *entities.Podm
func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpod.Runtime, error) {
options := []libpod.RuntimeOption{}
- storageOpts := storage.StoreOptions{}
+ storageOpts := types.StoreOptions{}
cfg := opts.config
storageSet := false
@@ -237,8 +237,8 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo
}
// ParseIDMapping takes idmappings and subuid and subgid maps and returns a storage mapping
-func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []string, subUIDMap, subGIDMap string) (*storage.IDMappingOptions, error) {
- options := storage.IDMappingOptions{
+func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []string, subUIDMap, subGIDMap string) (*types.IDMappingOptions, error) {
+ options := types.IDMappingOptions{
HostUIDMapping: true,
HostGIDMapping: true,
}
diff --git a/pkg/namespaces/namespaces.go b/pkg/namespaces/namespaces.go
index c35f68e02..a7736aee0 100644
--- a/pkg/namespaces/namespaces.go
+++ b/pkg/namespaces/namespaces.go
@@ -5,7 +5,7 @@ import (
"strconv"
"strings"
- "github.com/containers/storage"
+ "github.com/containers/storage/types"
)
const (
@@ -109,12 +109,12 @@ func (n UsernsMode) IsDefaultValue() bool {
// GetAutoOptions returns a AutoUserNsOptions with the settings to setup automatically
// a user namespace.
-func (n UsernsMode) GetAutoOptions() (*storage.AutoUserNsOptions, error) {
+func (n UsernsMode) GetAutoOptions() (*types.AutoUserNsOptions, error) {
parts := strings.SplitN(string(n), ":", 2)
if parts[0] != "auto" {
return nil, fmt.Errorf("wrong user namespace mode")
}
- options := storage.AutoUserNsOptions{}
+ options := types.AutoUserNsOptions{}
if len(parts) == 1 {
return &options, nil
}
@@ -131,13 +131,13 @@ func (n UsernsMode) GetAutoOptions() (*storage.AutoUserNsOptions, error) {
}
options.Size = uint32(s)
case "uidmapping":
- mapping, err := storage.ParseIDMapping([]string{v[1]}, nil, "", "")
+ mapping, err := types.ParseIDMapping([]string{v[1]}, nil, "", "")
if err != nil {
return nil, err
}
options.AdditionalUIDMappings = append(options.AdditionalUIDMappings, mapping.UIDMap...)
case "gidmapping":
- mapping, err := storage.ParseIDMapping(nil, []string{v[1]}, "", "")
+ mapping, err := types.ParseIDMapping(nil, []string{v[1]}, "", "")
if err != nil {
return nil, err
}
diff --git a/pkg/rootless/rootless.go b/pkg/rootless/rootless.go
index b5538efc3..0b9d719a9 100644
--- a/pkg/rootless/rootless.go
+++ b/pkg/rootless/rootless.go
@@ -5,7 +5,7 @@ import (
"sort"
"sync"
- "github.com/containers/storage"
+ "github.com/containers/storage/pkg/lockfile"
"github.com/opencontainers/runc/libcontainer/user"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
@@ -25,7 +25,7 @@ func TryJoinPauseProcess(pausePidPath string) (bool, int, error) {
}
// It could not join the pause process, let's lock the file before trying to delete it.
- pidFileLock, err := storage.GetLockfile(pausePidPath)
+ pidFileLock, err := lockfile.GetLockfile(pausePidPath)
if err != nil {
// The file was deleted by another process.
if os.IsNotExist(err) {
diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c
index d93e4c10c..7a2bf0377 100644
--- a/pkg/rootless/rootless_linux.c
+++ b/pkg/rootless/rootless_linux.c
@@ -233,9 +233,8 @@ int
is_fd_inherited(int fd)
{
if (open_files_set == NULL || fd > open_files_max_fd || fd < 0)
- {
return 0;
- }
+
return FD_ISSET(fd % FD_SETSIZE, &(open_files_set[fd / FD_SETSIZE])) ? 1 : 0;
}
@@ -633,9 +632,10 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path)
close (user_ns);
close (mnt_ns);
- for (f = 3; f < open_files_max_fd; f++)
- if (open_files_set == NULL || FD_ISSET (f % FD_SETSIZE, &(open_files_set[f / FD_SETSIZE])))
+ for (f = 3; f <= open_files_max_fd; f++)
+ if (is_fd_inherited (f))
close (f);
+
return pid;
}
@@ -813,13 +813,14 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re
if (do_socket_activation)
{
long num_fds;
+
num_fds = strtol (listen_fds, NULL, 10);
if (num_fds != LONG_MIN && num_fds != LONG_MAX)
{
int f;
for (f = 3; f < num_fds + 3; f++)
- if (open_files_set == NULL || FD_ISSET (f % FD_SETSIZE, &(open_files_set[f / FD_SETSIZE])))
+ if (is_fd_inherited (f))
close (f);
}
unsetenv ("LISTEN_PID");
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index 03697b353..1d724ffb0 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -11,7 +11,7 @@ import (
"github.com/containers/podman/v3/libpod/image"
"github.com/containers/podman/v3/pkg/specgen"
"github.com/containers/podman/v3/pkg/util"
- "github.com/containers/storage"
+ "github.com/containers/storage/types"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -367,7 +367,7 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
return options, nil
}
-func CreateExitCommandArgs(storageConfig storage.StoreOptions, config *config.Config, syslog, rm, exec bool) ([]string, error) {
+func CreateExitCommandArgs(storageConfig types.StoreOptions, config *config.Config, syslog, rm, exec bool) ([]string, error) {
// We need a cleanup process for containers in the current model.
// But we can't assume that the caller is Podman - it could be another
// user of the API.
diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go
index d61c8bd19..31ed3fd7c 100644
--- a/pkg/specgen/generate/kube/kube.go
+++ b/pkg/specgen/generate/kube/kube.go
@@ -2,11 +2,13 @@ package kube
import (
"context"
+ "encoding/json"
"fmt"
"net"
"strings"
"github.com/containers/common/pkg/parse"
+ "github.com/containers/common/pkg/secrets"
"github.com/containers/podman/v3/libpod/image"
ann "github.com/containers/podman/v3/pkg/annotations"
"github.com/containers/podman/v3/pkg/specgen"
@@ -94,6 +96,8 @@ type CtrSpecGenOptions struct {
RestartPolicy string
// NetNSIsHost tells the container to use the host netns
NetNSIsHost bool
+ // SecretManager to access the secrets
+ SecretsManager *secrets.SecretsManager
}
func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGenerator, error) {
@@ -210,12 +214,18 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener
}
for _, env := range opts.Container.Env {
- value := envVarValue(env, opts.ConfigMaps)
+ value, err := envVarValue(env, opts)
+ if err != nil {
+ return nil, err
+ }
envs[env.Name] = value
}
for _, envFrom := range opts.Container.EnvFrom {
- cmEnvs := envVarsFromConfigMap(envFrom, opts.ConfigMaps)
+ cmEnvs, err := envVarsFrom(envFrom, opts)
+ if err != nil {
+ return nil, err
+ }
for k, v := range cmEnvs {
envs[k] = v
@@ -325,40 +335,96 @@ func quantityToInt64(quantity *resource.Quantity) (int64, error) {
return 0, errors.Errorf("Quantity cannot be represented as int64: %v", quantity)
}
-// envVarsFromConfigMap returns all key-value pairs as env vars from a configMap that matches the envFrom setting of a container
-func envVarsFromConfigMap(envFrom v1.EnvFromSource, configMaps []v1.ConfigMap) map[string]string {
+// read a k8s secret in JSON format from the secret manager
+func k8sSecretFromSecretManager(name string, secretsManager *secrets.SecretsManager) (map[string][]byte, error) {
+ _, jsonSecret, err := secretsManager.LookupSecretData(name)
+ if err != nil {
+ return nil, err
+ }
+
+ var secrets map[string][]byte
+ if err := json.Unmarshal(jsonSecret, &secrets); err != nil {
+ return nil, errors.Errorf("Secret %v is not valid JSON: %v", name, err)
+ }
+ return secrets, nil
+}
+
+// envVarsFrom returns all key-value pairs as env vars from a configMap or secret that matches the envFrom setting of a container
+func envVarsFrom(envFrom v1.EnvFromSource, opts *CtrSpecGenOptions) (map[string]string, error) {
envs := map[string]string{}
if envFrom.ConfigMapRef != nil {
- cmName := envFrom.ConfigMapRef.Name
+ cmRef := envFrom.ConfigMapRef
+ err := errors.Errorf("Configmap %v not found", cmRef.Name)
- for _, c := range configMaps {
- if cmName == c.Name {
+ for _, c := range opts.ConfigMaps {
+ if cmRef.Name == c.Name {
envs = c.Data
+ err = nil
break
}
}
+
+ if err != nil && (cmRef.Optional == nil || !*cmRef.Optional) {
+ return nil, err
+ }
}
- return envs
+ if envFrom.SecretRef != nil {
+ secRef := envFrom.SecretRef
+ secret, err := k8sSecretFromSecretManager(secRef.Name, opts.SecretsManager)
+ if err == nil {
+ for k, v := range secret {
+ envs[k] = string(v)
+ }
+ } else if secRef.Optional == nil || !*secRef.Optional {
+ return nil, err
+ }
+ }
+
+ return envs, nil
}
// envVarValue returns the environment variable value configured within the container's env setting.
-// It gets the value from a configMap if specified, otherwise returns env.Value
-func envVarValue(env v1.EnvVar, configMaps []v1.ConfigMap) string {
- for _, c := range configMaps {
- if env.ValueFrom != nil {
- if env.ValueFrom.ConfigMapKeyRef != nil {
- if env.ValueFrom.ConfigMapKeyRef.Name == c.Name {
- if value, ok := c.Data[env.ValueFrom.ConfigMapKeyRef.Key]; ok {
- return value
+// It gets the value from a configMap or secret if specified, otherwise returns env.Value
+func envVarValue(env v1.EnvVar, opts *CtrSpecGenOptions) (string, error) {
+ if env.ValueFrom != nil {
+ if env.ValueFrom.ConfigMapKeyRef != nil {
+ cmKeyRef := env.ValueFrom.ConfigMapKeyRef
+ err := errors.Errorf("Cannot set env %v: configmap %v not found", env.Name, cmKeyRef.Name)
+
+ for _, c := range opts.ConfigMaps {
+ if cmKeyRef.Name == c.Name {
+ if value, ok := c.Data[cmKeyRef.Key]; ok {
+ return value, nil
}
+ err = errors.Errorf("Cannot set env %v: key %s not found in configmap %v", env.Name, cmKeyRef.Key, cmKeyRef.Name)
+ break
+ }
+ }
+ if cmKeyRef.Optional == nil || !*cmKeyRef.Optional {
+ return "", err
+ }
+ return "", nil
+ }
+
+ if env.ValueFrom.SecretKeyRef != nil {
+ secKeyRef := env.ValueFrom.SecretKeyRef
+ secret, err := k8sSecretFromSecretManager(secKeyRef.Name, opts.SecretsManager)
+ if err == nil {
+ if val, ok := secret[secKeyRef.Key]; ok {
+ return string(val), nil
}
+ err = errors.Errorf("Secret %v has not %v key", secKeyRef.Name, secKeyRef.Key)
+ }
+ if secKeyRef.Optional == nil || !*secKeyRef.Optional {
+ return "", errors.Errorf("Cannot set env %v: %v", env.Name, err)
}
+ return "", nil
}
}
- return env.Value
+ return env.Value, nil
}
// getPodPorts converts a slice of kube container descriptions to an
diff --git a/pkg/specgen/generate/kube/play_test.go b/pkg/specgen/generate/kube/play_test.go
index 148540e9f..f714826f0 100644
--- a/pkg/specgen/generate/kube/play_test.go
+++ b/pkg/specgen/generate/kube/play_test.go
@@ -1,19 +1,49 @@
package kube
import (
+ "encoding/json"
+ "io/ioutil"
+ "os"
"testing"
+ "github.com/containers/common/pkg/secrets"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
-func TestEnvVarsFromConfigMap(t *testing.T) {
+func createSecrets(t *testing.T, d string) *secrets.SecretsManager {
+ secretsManager, err := secrets.NewManager(d)
+ assert.NoError(t, err)
+
+ driver := "file"
+ driverOpts := map[string]string{
+ "path": d,
+ }
+
+ for _, s := range k8sSecrets {
+ data, err := json.Marshal(s.Data)
+ assert.NoError(t, err)
+
+ _, err = secretsManager.Store(s.ObjectMeta.Name, data, driver, driverOpts)
+ assert.NoError(t, err)
+ }
+
+ return secretsManager
+}
+
+func TestEnvVarsFrom(t *testing.T) {
+ d, err := ioutil.TempDir("", "secrets")
+ assert.NoError(t, err)
+ defer os.RemoveAll(d)
+ secretsManager := createSecrets(t, d)
+
tests := []struct {
- name string
- envFrom v1.EnvFromSource
- configMapList []v1.ConfigMap
- expected map[string]string
+ name string
+ envFrom v1.EnvFromSource
+ options CtrSpecGenOptions
+ succeed bool
+ expected map[string]string
}{
{
"ConfigMapExists",
@@ -24,7 +54,10 @@ func TestEnvVarsFromConfigMap(t *testing.T) {
},
},
},
- configMapList,
+ CtrSpecGenOptions{
+ ConfigMaps: configMapList,
+ },
+ true,
map[string]string{
"myvar": "foo",
},
@@ -38,7 +71,26 @@ func TestEnvVarsFromConfigMap(t *testing.T) {
},
},
},
- configMapList,
+ CtrSpecGenOptions{
+ ConfigMaps: configMapList,
+ },
+ false,
+ nil,
+ },
+ {
+ "OptionalConfigMapDoesNotExist",
+ v1.EnvFromSource{
+ ConfigMapRef: &v1.ConfigMapEnvSource{
+ LocalObjectReference: v1.LocalObjectReference{
+ Name: "doesnotexist",
+ },
+ Optional: &optional,
+ },
+ },
+ CtrSpecGenOptions{
+ ConfigMaps: configMapList,
+ },
+ true,
map[string]string{},
},
{
@@ -50,7 +102,74 @@ func TestEnvVarsFromConfigMap(t *testing.T) {
},
},
},
- []v1.ConfigMap{},
+ CtrSpecGenOptions{
+ ConfigMaps: []v1.ConfigMap{},
+ },
+ false,
+ nil,
+ },
+ {
+ "OptionalEmptyConfigMapList",
+ v1.EnvFromSource{
+ ConfigMapRef: &v1.ConfigMapEnvSource{
+ LocalObjectReference: v1.LocalObjectReference{
+ Name: "foo",
+ },
+ Optional: &optional,
+ },
+ },
+ CtrSpecGenOptions{
+ ConfigMaps: []v1.ConfigMap{},
+ },
+ true,
+ map[string]string{},
+ },
+ {
+ "SecretExists",
+ v1.EnvFromSource{
+ SecretRef: &v1.SecretEnvSource{
+ LocalObjectReference: v1.LocalObjectReference{
+ Name: "foo",
+ },
+ },
+ },
+ CtrSpecGenOptions{
+ SecretsManager: secretsManager,
+ },
+ true,
+ map[string]string{
+ "myvar": "foo",
+ },
+ },
+ {
+ "SecretDoesNotExist",
+ v1.EnvFromSource{
+ SecretRef: &v1.SecretEnvSource{
+ LocalObjectReference: v1.LocalObjectReference{
+ Name: "doesnotexist",
+ },
+ },
+ },
+ CtrSpecGenOptions{
+ SecretsManager: secretsManager,
+ },
+ false,
+ nil,
+ },
+ {
+ "OptionalSecretDoesNotExist",
+ v1.EnvFromSource{
+ SecretRef: &v1.SecretEnvSource{
+ LocalObjectReference: v1.LocalObjectReference{
+ Name: "doesnotexist",
+ },
+ Optional: &optional,
+ },
+ },
+ CtrSpecGenOptions{
+ SecretsManager: secretsManager,
+ },
+ true,
map[string]string{},
},
}
@@ -58,18 +177,25 @@ func TestEnvVarsFromConfigMap(t *testing.T) {
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
- result := envVarsFromConfigMap(test.envFrom, test.configMapList)
+ result, err := envVarsFrom(test.envFrom, &test.options)
+ assert.Equal(t, err == nil, test.succeed)
assert.Equal(t, test.expected, result)
})
}
}
func TestEnvVarValue(t *testing.T) {
+ d, err := ioutil.TempDir("", "secrets")
+ assert.NoError(t, err)
+ defer os.RemoveAll(d)
+ secretsManager := createSecrets(t, d)
+
tests := []struct {
- name string
- envVar v1.EnvVar
- configMapList []v1.ConfigMap
- expected string
+ name string
+ envVar v1.EnvVar
+ options CtrSpecGenOptions
+ succeed bool
+ expected string
}{
{
"ConfigMapExists",
@@ -84,7 +210,10 @@ func TestEnvVarValue(t *testing.T) {
},
},
},
- configMapList,
+ CtrSpecGenOptions{
+ ConfigMaps: configMapList,
+ },
+ true,
"foo",
},
{
@@ -100,7 +229,30 @@ func TestEnvVarValue(t *testing.T) {
},
},
},
- configMapList,
+ CtrSpecGenOptions{
+ ConfigMaps: configMapList,
+ },
+ false,
+ "",
+ },
+ {
+ "OptionalContainerKeyDoesNotExistInConfigMap",
+ v1.EnvVar{
+ Name: "FOO",
+ ValueFrom: &v1.EnvVarSource{
+ ConfigMapKeyRef: &v1.ConfigMapKeySelector{
+ LocalObjectReference: v1.LocalObjectReference{
+ Name: "foo",
+ },
+ Key: "doesnotexist",
+ Optional: &optional,
+ },
+ },
+ },
+ CtrSpecGenOptions{
+ ConfigMaps: configMapList,
+ },
+ true,
"",
},
{
@@ -116,7 +268,30 @@ func TestEnvVarValue(t *testing.T) {
},
},
},
- configMapList,
+ CtrSpecGenOptions{
+ ConfigMaps: configMapList,
+ },
+ false,
+ "",
+ },
+ {
+ "OptionalConfigMapDoesNotExist",
+ v1.EnvVar{
+ Name: "FOO",
+ ValueFrom: &v1.EnvVarSource{
+ ConfigMapKeyRef: &v1.ConfigMapKeySelector{
+ LocalObjectReference: v1.LocalObjectReference{
+ Name: "doesnotexist",
+ },
+ Key: "myvar",
+ Optional: &optional,
+ },
+ },
+ },
+ CtrSpecGenOptions{
+ ConfigMaps: configMapList,
+ },
+ true,
"",
},
{
@@ -132,7 +307,127 @@ func TestEnvVarValue(t *testing.T) {
},
},
},
- []v1.ConfigMap{},
+ CtrSpecGenOptions{
+ ConfigMaps: []v1.ConfigMap{},
+ },
+ false,
+ "",
+ },
+ {
+ "OptionalEmptyConfigMapList",
+ v1.EnvVar{
+ Name: "FOO",
+ ValueFrom: &v1.EnvVarSource{
+ ConfigMapKeyRef: &v1.ConfigMapKeySelector{
+ LocalObjectReference: v1.LocalObjectReference{
+ Name: "foo",
+ },
+ Key: "myvar",
+ Optional: &optional,
+ },
+ },
+ },
+ CtrSpecGenOptions{
+ ConfigMaps: []v1.ConfigMap{},
+ },
+ true,
+ "",
+ },
+ {
+ "SecretExists",
+ v1.EnvVar{
+ Name: "FOO",
+ ValueFrom: &v1.EnvVarSource{
+ SecretKeyRef: &v1.SecretKeySelector{
+ LocalObjectReference: v1.LocalObjectReference{
+ Name: "foo",
+ },
+ Key: "myvar",
+ },
+ },
+ },
+ CtrSpecGenOptions{
+ SecretsManager: secretsManager,
+ },
+ true,
+ "foo",
+ },
+ {
+ "ContainerKeyDoesNotExistInSecret",
+ v1.EnvVar{
+ Name: "FOO",
+ ValueFrom: &v1.EnvVarSource{
+ SecretKeyRef: &v1.SecretKeySelector{
+ LocalObjectReference: v1.LocalObjectReference{
+ Name: "foo",
+ },
+ Key: "doesnotexist",
+ },
+ },
+ },
+ CtrSpecGenOptions{
+ SecretsManager: secretsManager,
+ },
+ false,
+ "",
+ },
+ {
+ "OptionalContainerKeyDoesNotExistInSecret",
+ v1.EnvVar{
+ Name: "FOO",
+ ValueFrom: &v1.EnvVarSource{
+ SecretKeyRef: &v1.SecretKeySelector{
+ LocalObjectReference: v1.LocalObjectReference{
+ Name: "foo",
+ },
+ Key: "doesnotexist",
+ Optional: &optional,
+ },
+ },
+ },
+ CtrSpecGenOptions{
+ SecretsManager: secretsManager,
+ },
+ true,
+ "",
+ },
+ {
+ "SecretDoesNotExist",
+ v1.EnvVar{
+ Name: "FOO",
+ ValueFrom: &v1.EnvVarSource{
+ SecretKeyRef: &v1.SecretKeySelector{
+ LocalObjectReference: v1.LocalObjectReference{
+ Name: "doesnotexist",
+ },
+ Key: "myvar",
+ },
+ },
+ },
+ CtrSpecGenOptions{
+ SecretsManager: secretsManager,
+ },
+ false,
+ "",
+ },
+ {
+ "OptionalSecretDoesNotExist",
+ v1.EnvVar{
+ Name: "FOO",
+ ValueFrom: &v1.EnvVarSource{
+ SecretKeyRef: &v1.SecretKeySelector{
+ LocalObjectReference: v1.LocalObjectReference{
+ Name: "doesnotexist",
+ },
+ Key: "myvar",
+ Optional: &optional,
+ },
+ },
+ },
+ CtrSpecGenOptions{
+ SecretsManager: secretsManager,
+ },
+ true,
"",
},
}
@@ -140,7 +435,8 @@ func TestEnvVarValue(t *testing.T) {
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
- result := envVarValue(test.envVar, test.configMapList)
+ result, err := envVarValue(test.envVar, &test.options)
+ assert.Equal(t, err == nil, test.succeed)
assert.Equal(t, test.expected, result)
})
}
@@ -170,3 +466,30 @@ var configMapList = []v1.ConfigMap{
},
},
}
+
+var optional = true
+
+var k8sSecrets = []v1.Secret{
+ {
+ TypeMeta: v12.TypeMeta{
+ Kind: "Secret",
+ },
+ ObjectMeta: v12.ObjectMeta{
+ Name: "bar",
+ },
+ Data: map[string][]byte{
+ "myvar": []byte("bar"),
+ },
+ },
+ {
+ TypeMeta: v12.TypeMeta{
+ Kind: "Secret",
+ },
+ ObjectMeta: v12.ObjectMeta{
+ Name: "foo",
+ },
+ Data: map[string][]byte{
+ "myvar": []byte("foo"),
+ },
+ },
+}
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index 732579bf0..c10dc5ef5 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -5,7 +5,7 @@ import (
"syscall"
"github.com/containers/image/v5/manifest"
- "github.com/containers/storage"
+ "github.com/containers/storage/types"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
)
@@ -302,7 +302,7 @@ type ContainerSecurityConfig struct {
// IDMappings are UID and GID mappings that will be used by user
// namespaces.
// Required if UserNS is private.
- IDMappings *storage.IDMappingOptions `json:"idmappings,omitempty"`
+ IDMappings *types.IDMappingOptions `json:"idmappings,omitempty"`
// ReadOnlyFilesystem indicates that everything will be mounted
// as read-only
ReadOnlyFilesystem bool `json:"read_only_filesystem,omitempty"`
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index a4c8f3a64..bbaf72981 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -20,8 +20,8 @@ import (
"github.com/containers/podman/v3/pkg/namespaces"
"github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/podman/v3/pkg/signal"
- "github.com/containers/storage"
"github.com/containers/storage/pkg/idtools"
+ stypes "github.com/containers/storage/types"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
@@ -344,8 +344,8 @@ func ParseSignal(rawSignal string) (syscall.Signal, error) {
}
// GetKeepIDMapping returns the mappings and the user to use when keep-id is used
-func GetKeepIDMapping() (*storage.IDMappingOptions, int, int, error) {
- options := storage.IDMappingOptions{
+func GetKeepIDMapping() (*stypes.IDMappingOptions, int, int, error) {
+ options := stypes.IDMappingOptions{
HostUIDMapping: true,
HostGIDMapping: true,
}
@@ -395,8 +395,8 @@ func GetKeepIDMapping() (*storage.IDMappingOptions, int, int, error) {
}
// ParseIDMapping takes idmappings and subuid and subgid maps and returns a storage mapping
-func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []string, subUIDMap, subGIDMap string) (*storage.IDMappingOptions, error) {
- options := storage.IDMappingOptions{
+func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []string, subUIDMap, subGIDMap string) (*stypes.IDMappingOptions, error) {
+ options := stypes.IDMappingOptions{
HostUIDMapping: true,
HostGIDMapping: true,
}
@@ -479,7 +479,7 @@ type tomlConfig struct {
} `toml:"storage"`
}
-func getTomlStorage(storeOptions *storage.StoreOptions) *tomlConfig {
+func getTomlStorage(storeOptions *stypes.StoreOptions) *tomlConfig {
config := new(tomlConfig)
config.Storage.Driver = storeOptions.GraphDriverName
@@ -496,7 +496,7 @@ func getTomlStorage(storeOptions *storage.StoreOptions) *tomlConfig {
}
// WriteStorageConfigFile writes the configuration to a file
-func WriteStorageConfigFile(storageOpts *storage.StoreOptions, storageConf string) error {
+func WriteStorageConfigFile(storageOpts *stypes.StoreOptions, storageConf string) error {
if err := os.MkdirAll(filepath.Dir(storageConf), 0755); err != nil {
return err
}
diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go
index a4c738f17..93c8426a7 100644
--- a/test/e2e/play_kube_test.go
+++ b/test/e2e/play_kube_test.go
@@ -141,7 +141,16 @@ spec:
configMapKeyRef:
name: {{ .RefName }}
key: {{ .RefKey }}
- {{ else }}
+ optional: {{ .Optional }}
+ {{ end }}
+ {{ if (eq .ValueFrom "secret") }}
+ valueFrom:
+ secretKeyRef:
+ name: {{ .RefName }}
+ key: {{ .RefKey }}
+ optional: {{ .Optional }}
+ {{ end }}
+ {{ if (eq .ValueFrom "") }}
value: {{ .Value }}
{{ end }}
{{ end }}
@@ -151,6 +160,12 @@ spec:
{{ if (eq .From "configmap") }}
- configMapRef:
name: {{ .Name }}
+ optional: {{ .Optional }}
+ {{ end }}
+ {{ if (eq .From "secret") }}
+ - secretRef:
+ name: {{ .Name }}
+ optional: {{ .Optional }}
{{ end }}
{{ end }}
{{ end }}
@@ -340,6 +355,8 @@ var (
seccompPwdEPERM = []byte(`{"defaultAction":"SCMP_ACT_ALLOW","syscalls":[{"name":"getcwd","action":"SCMP_ACT_ERRNO"}]}`)
// CPU Period in ms
defaultCPUPeriod = 100
+ // Default secret in JSON. Note that the values ("foo" and "bar") are base64 encoded.
+ defaultSecret = []byte(`{"FOO":"Zm9v","BAR":"YmFy"}`)
)
func writeYaml(content string, fileName string) error {
@@ -407,6 +424,16 @@ func generateMultiDocKubeYaml(kubeObjects []string, pathname string) error {
return writeYaml(multiKube, pathname)
}
+func createSecret(podmanTest *PodmanTestIntegration, name string, value []byte) {
+ secretFilePath := filepath.Join(podmanTest.TempDir, "secret")
+ err := ioutil.WriteFile(secretFilePath, value, 0755)
+ Expect(err).To(BeNil())
+
+ secret := podmanTest.Podman([]string{"secret", "create", name, secretFilePath})
+ secret.WaitWithDefaultTimeout()
+ Expect(secret.ExitCode()).To(Equal(0))
+}
+
// ConfigMap describes the options a kube yaml can be configured at configmap level
type ConfigMap struct {
Name string
@@ -746,7 +773,7 @@ func withVolumeMount(mountPath string, readonly bool) ctrOption {
}
}
-func withEnv(name, value, valueFrom, refName, refKey string) ctrOption {
+func withEnv(name, value, valueFrom, refName, refKey string, optional bool) ctrOption {
return func(c *Ctr) {
e := Env{
Name: name,
@@ -754,17 +781,19 @@ func withEnv(name, value, valueFrom, refName, refKey string) ctrOption {
ValueFrom: valueFrom,
RefName: refName,
RefKey: refKey,
+ Optional: optional,
}
c.Env = append(c.Env, e)
}
}
-func withEnvFrom(name, from string) ctrOption {
+func withEnvFrom(name, from string, optional bool) ctrOption {
return func(c *Ctr) {
e := EnvFrom{
- Name: name,
- From: from,
+ Name: name,
+ From: from,
+ Optional: optional,
}
c.EnvFrom = append(c.EnvFrom, e)
@@ -822,11 +851,13 @@ type Env struct {
ValueFrom string
RefName string
RefKey string
+ Optional bool
}
type EnvFrom struct {
- Name string
- From string
+ Name string
+ From string
+ Optional bool
}
func milliCPUToQuota(milliCPU string) int {
@@ -1062,7 +1093,7 @@ var _ = Describe("Podman play kube", func() {
err := generateKubeYaml("configmap", cm, cmYamlPathname)
Expect(err).To(BeNil())
- pod := getPod(withCtr(getCtr(withEnv("FOO", "", "configmap", "foo", "FOO"))))
+ pod := getPod(withCtr(getCtr(withEnv("FOO", "", "configmap", "foo", "FOO", false))))
err = generateKubeYaml("pod", pod, kubeYaml)
Expect(err).To(BeNil())
@@ -1076,6 +1107,68 @@ var _ = Describe("Podman play kube", func() {
Expect(inspect.OutputToString()).To(ContainSubstring(`FOO=foo`))
})
+ It("podman play kube test required env value from configmap with missing key", func() {
+ SkipIfRemote("configmap list is not supported as a param")
+ cmYamlPathname := filepath.Join(podmanTest.TempDir, "foo-cm.yaml")
+ cm := getConfigMap(withConfigMapName("foo"), withConfigMapData("FOO", "foo"))
+ err := generateKubeYaml("configmap", cm, cmYamlPathname)
+ Expect(err).To(BeNil())
+
+ pod := getPod(withCtr(getCtr(withEnv("FOO", "", "configmap", "foo", "MISSING_KEY", false))))
+ err = generateKubeYaml("pod", pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml, "--configmap", cmYamlPathname})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Not(Equal(0)))
+ })
+
+ It("podman play kube test required env value from missing configmap", func() {
+ pod := getPod(withCtr(getCtr(withEnv("FOO", "", "configmap", "missing_cm", "FOO", false))))
+ err = generateKubeYaml("pod", pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Not(Equal(0)))
+ })
+
+ It("podman play kube test optional env value from configmap with missing key", func() {
+ SkipIfRemote("configmap list is not supported as a param")
+ cmYamlPathname := filepath.Join(podmanTest.TempDir, "foo-cm.yaml")
+ cm := getConfigMap(withConfigMapName("foo"), withConfigMapData("FOO", "foo"))
+ err := generateKubeYaml("configmap", cm, cmYamlPathname)
+ Expect(err).To(BeNil())
+
+ pod := getPod(withCtr(getCtr(withEnv("FOO", "", "configmap", "foo", "MISSING_KEY", true))))
+ err = generateKubeYaml("pod", pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml, "--configmap", cmYamlPathname})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Equal(0))
+
+ inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ range .Config.Env }}[{{ . }}]{{end}}'"})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect.ExitCode()).To(Equal(0))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`[FOO=]`))
+ })
+
+ It("podman play kube test optional env value from missing configmap", func() {
+ pod := getPod(withCtr(getCtr(withEnv("FOO", "", "configmap", "missing_cm", "FOO", true))))
+ err = generateKubeYaml("pod", pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Equal(0))
+
+ inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ range .Config.Env }}[{{ . }}]{{end}}'"})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect.ExitCode()).To(Equal(0))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`[FOO=]`))
+ })
+
It("podman play kube test get all key-value pairs from configmap as envs", func() {
SkipIfRemote("configmap list is not supported as a param")
cmYamlPathname := filepath.Join(podmanTest.TempDir, "foo-cm.yaml")
@@ -1083,7 +1176,7 @@ var _ = Describe("Podman play kube", func() {
err := generateKubeYaml("configmap", cm, cmYamlPathname)
Expect(err).To(BeNil())
- pod := getPod(withCtr(getCtr(withEnvFrom("foo", "configmap"))))
+ pod := getPod(withCtr(getCtr(withEnvFrom("foo", "configmap", false))))
err = generateKubeYaml("pod", pod, kubeYaml)
Expect(err).To(BeNil())
@@ -1098,6 +1191,131 @@ var _ = Describe("Podman play kube", func() {
Expect(inspect.OutputToString()).To(ContainSubstring(`FOO2=foo2`))
})
+ It("podman play kube test get all key-value pairs from required configmap as envs", func() {
+ pod := getPod(withCtr(getCtr(withEnvFrom("missing_cm", "configmap", false))))
+ err = generateKubeYaml("pod", pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Not(Equal(0)))
+ })
+
+ It("podman play kube test get all key-value pairs from optional configmap as envs", func() {
+ pod := getPod(withCtr(getCtr(withEnvFrom("missing_cm", "configmap", true))))
+ err = generateKubeYaml("pod", pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Equal(0))
+ })
+
+ It("podman play kube test env value from secret", func() {
+ createSecret(podmanTest, "foo", defaultSecret)
+ pod := getPod(withCtr(getCtr(withEnv("FOO", "", "secret", "foo", "FOO", false))))
+ err = generateKubeYaml("pod", pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Equal(0))
+
+ inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Env }}'"})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect.ExitCode()).To(Equal(0))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`FOO=foo`))
+ })
+
+ It("podman play kube test required env value from missing secret", func() {
+ pod := getPod(withCtr(getCtr(withEnv("FOO", "", "secret", "foo", "FOO", false))))
+ err = generateKubeYaml("pod", pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Not(Equal(0)))
+ })
+
+ It("podman play kube test required env value from secret with missing key", func() {
+ createSecret(podmanTest, "foo", defaultSecret)
+ pod := getPod(withCtr(getCtr(withEnv("FOO", "", "secret", "foo", "MISSING", false))))
+ err = generateKubeYaml("pod", pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Not(Equal(0)))
+ })
+
+ It("podman play kube test optional env value from missing secret", func() {
+ pod := getPod(withCtr(getCtr(withEnv("FOO", "", "secret", "foo", "FOO", true))))
+ err = generateKubeYaml("pod", pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Equal(0))
+
+ inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ range .Config.Env }}[{{ . }}]{{end}}'"})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect.ExitCode()).To(Equal(0))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`[FOO=]`))
+ })
+
+ It("podman play kube test optional env value from secret with missing key", func() {
+ createSecret(podmanTest, "foo", defaultSecret)
+ pod := getPod(withCtr(getCtr(withEnv("FOO", "", "secret", "foo", "MISSING", true))))
+ err = generateKubeYaml("pod", pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Equal(0))
+
+ inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ range .Config.Env }}[{{ . }}]{{end}}'"})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect.ExitCode()).To(Equal(0))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`[FOO=]`))
+ })
+
+ It("podman play kube test get all key-value pairs from secret as envs", func() {
+ createSecret(podmanTest, "foo", defaultSecret)
+ pod := getPod(withCtr(getCtr(withEnvFrom("foo", "secret", false))))
+ err = generateKubeYaml("pod", pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Equal(0))
+
+ inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Env }}'"})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect.ExitCode()).To(Equal(0))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`FOO=foo`))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`BAR=bar`))
+ })
+
+ It("podman play kube test get all key-value pairs from required secret as envs", func() {
+ pod := getPod(withCtr(getCtr(withEnvFrom("missing_secret", "secret", false))))
+ err = generateKubeYaml("pod", pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Not(Equal(0)))
+ })
+
+ It("podman play kube test get all key-value pairs from optional secret as envs", func() {
+ pod := getPod(withCtr(getCtr(withEnvFrom("missing_secret", "secret", true))))
+ err = generateKubeYaml("pod", pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Equal(0))
+ })
+
It("podman play kube test hostname", func() {
pod := getPod()
err := generateKubeYaml("pod", pod, kubeYaml)