summaryrefslogtreecommitdiff
path: root/cmd/podman/shared/create.go
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/podman/shared/create.go')
-rw-r--r--cmd/podman/shared/create.go212
1 files changed, 141 insertions, 71 deletions
diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go
index 0814eeba3..5fa8d6c0b 100644
--- a/cmd/podman/shared/create.go
+++ b/cmd/podman/shared/create.go
@@ -18,12 +18,15 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
ann "github.com/containers/libpod/pkg/annotations"
+ "github.com/containers/libpod/pkg/autoupdate"
+ envLib "github.com/containers/libpod/pkg/env"
"github.com/containers/libpod/pkg/errorhandling"
"github.com/containers/libpod/pkg/inspect"
ns "github.com/containers/libpod/pkg/namespaces"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/seccomp"
cc "github.com/containers/libpod/pkg/spec"
+ systemdGen "github.com/containers/libpod/pkg/systemd/generate"
"github.com/containers/libpod/pkg/util"
"github.com/docker/go-connections/nat"
"github.com/docker/go-units"
@@ -68,6 +71,7 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.
}
imageName := ""
+ rawImageName := ""
var imageData *inspect.ImageData = nil
// Set the storage if there is no rootfs specified
@@ -77,9 +81,8 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.
writer = os.Stderr
}
- name := ""
if len(c.InputArgs) != 0 {
- name = c.InputArgs[0]
+ rawImageName = c.InputArgs[0]
} else {
return nil, nil, errors.Errorf("error, image name not provided")
}
@@ -96,11 +99,11 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.
ArchitectureChoice: overrideArch,
}
- newImage, err := runtime.ImageRuntime().New(ctx, name, rtc.SignaturePolicyPath, c.String("authfile"), writer, &dockerRegistryOptions, image.SigningOptions{}, nil, pullType)
+ newImage, err := runtime.ImageRuntime().New(ctx, rawImageName, rtc.Engine.SignaturePolicyPath, c.String("authfile"), writer, &dockerRegistryOptions, image.SigningOptions{}, nil, pullType)
if err != nil {
return nil, nil, err
}
- imageData, err = newImage.Inspect(ctx)
+ imageData, err = newImage.InspectNoSize(ctx)
if err != nil {
return nil, nil, err
}
@@ -173,11 +176,32 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.
}
}
- createConfig, err := ParseCreateOpts(ctx, c, runtime, imageName, imageData)
+ createConfig, err := ParseCreateOpts(ctx, c, runtime, imageName, rawImageName, imageData)
if err != nil {
return nil, nil, err
}
+ // (VR): Ideally we perform the checks _before_ pulling the image but that
+ // would require some bigger code refactoring of `ParseCreateOpts` and the
+ // logic here. But as the creation code will be consolidated in the future
+ // and given auto updates are experimental, we can live with that for now.
+ // In the end, the user may only need to correct the policy or the raw image
+ // name.
+ autoUpdatePolicy, autoUpdatePolicySpecified := createConfig.Labels[autoupdate.Label]
+ if autoUpdatePolicySpecified {
+ if _, err := autoupdate.LookupPolicy(autoUpdatePolicy); err != nil {
+ return nil, nil, err
+ }
+ // Now we need to make sure we're having a fully-qualified image reference.
+ if rootfs != "" {
+ return nil, nil, errors.Errorf("auto updates do not work with --rootfs")
+ }
+ // Make sure the input image is a docker.
+ if err := autoupdate.ValidateImageReference(rawImageName); err != nil {
+ return nil, nil, err
+ }
+ }
+
// Because parseCreateOpts does derive anything from the image, we add health check
// at this point. The rest is done by WithOptions.
createConfig.HealthCheck = healthCheck
@@ -269,7 +293,7 @@ func configurePod(c *GenericCLIResults, runtime *libpod.Runtime, namespaces map[
// Parses CLI options related to container creation into a config which can be
// parsed into an OCI runtime spec
-func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.Runtime, imageName string, data *inspect.ImageData) (*cc.CreateConfig, error) {
+func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.Runtime, imageName string, rawImageName string, data *inspect.ImageData) (*cc.CreateConfig, error) {
var (
inputCommand, command []string
memoryLimit, memoryReservation, memorySwap, memoryKernel int64
@@ -473,19 +497,55 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
}
// ENVIRONMENT VARIABLES
- env := EnvVariablesFromData(data)
+ //
+ // Precedence order (higher index wins):
+ // 1) env-host, 2) image data, 3) env-file, 4) env
+ env := map[string]string{
+ "container": "podman",
+ }
+
+ // First transform the os env into a map. We need it for the labels later in
+ // any case.
+ osEnv, err := envLib.ParseSlice(os.Environ())
+ if err != nil {
+ return nil, errors.Wrap(err, "error parsing host environment variables")
+ }
+
+ // Start with env-host
+
if c.Bool("env-host") {
- for _, e := range os.Environ() {
- pair := strings.SplitN(e, "=", 2)
- if _, ok := env[pair[0]]; !ok {
- if len(pair) > 1 {
- env[pair[0]] = pair[1]
- }
+ env = envLib.Join(env, osEnv)
+ }
+
+ // Image data overrides any previous variables
+ if data != nil {
+ configEnv, err := envLib.ParseSlice(data.Config.Env)
+ if err != nil {
+ return nil, errors.Wrap(err, "error passing image environment variables")
+ }
+ env = envLib.Join(env, configEnv)
+ }
+
+ // env-file overrides any previous variables
+ if c.IsSet("env-file") {
+ for _, f := range c.StringSlice("env-file") {
+ fileEnv, err := envLib.ParseFile(f)
+ if err != nil {
+ return nil, err
}
+ // File env is overridden by env.
+ env = envLib.Join(env, fileEnv)
}
}
- if err := parse.ReadKVStrings(env, c.StringSlice("env-file"), c.StringArray("env")); err != nil {
- return nil, errors.Wrapf(err, "unable to process environment variables")
+
+ // env overrides any previous variables
+ cmdlineEnv := c.StringSlice("env")
+ if len(cmdlineEnv) > 0 {
+ parsedEnv, err := envLib.ParseSlice(cmdlineEnv)
+ if err != nil {
+ return nil, err
+ }
+ env = envLib.Join(env, parsedEnv)
}
// LABEL VARIABLES
@@ -501,6 +561,10 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
}
}
+ if systemdUnit, exists := osEnv[systemdGen.EnvVariable]; exists {
+ labels[systemdGen.EnvVariable] = systemdUnit
+ }
+
// ANNOTATIONS
annotations := make(map[string]string)
@@ -572,7 +636,6 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
if err != nil {
return nil, errors.Wrapf(err, "unable to translate --shm-size")
}
-
// Verify the additional hosts are in correct format
for _, host := range c.StringSlice("add-host") {
if _, err := parse.ValidateExtraHost(host); err != nil {
@@ -580,24 +643,35 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
}
}
- // Check for . and dns-search domains
- if util.StringInSlice(".", c.StringSlice("dns-search")) && len(c.StringSlice("dns-search")) > 1 {
- return nil, errors.Errorf("cannot pass additional search domains when also specifying '.'")
+ var (
+ dnsSearches []string
+ dnsServers []string
+ dnsOptions []string
+ )
+ if c.Changed("dns-search") {
+ dnsSearches = c.StringSlice("dns-search")
+ // Check for explicit dns-search domain of ''
+ if len(dnsSearches) == 0 {
+ return nil, errors.Errorf("'' is not a valid domain")
+ }
+ // Validate domains are good
+ for _, dom := range dnsSearches {
+ if dom == "." {
+ if len(dnsSearches) > 1 {
+ return nil, errors.Errorf("cannot pass additional search domains when also specifying '.'")
+ }
+ continue
+ }
+ if _, err := parse.ValidateDomain(dom); err != nil {
+ return nil, err
+ }
+ }
}
-
- // Check for explicit dns-search domain of ''
- if c.Changed("dns-search") && len(c.StringSlice("dns-search")) == 0 {
- return nil, errors.Errorf("'' is not a valid domain")
+ if c.IsSet("dns") {
+ dnsServers = append(dnsServers, c.StringSlice("dns")...)
}
-
- // Validate domains are good
- for _, dom := range c.StringSlice("dns-search") {
- if dom == "." {
- continue
- }
- if _, err := parse.ValidateDomain(dom); err != nil {
- return nil, err
- }
+ if c.IsSet("dns-opt") {
+ dnsOptions = c.StringSlice("dns-opt")
}
var ImageVolumes map[string]struct{}
@@ -643,7 +717,7 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
pidsLimit := c.Int64("pids-limit")
if c.String("cgroups") == "disabled" && !c.Changed("pids-limit") {
- pidsLimit = 0
+ pidsLimit = -1
}
pid := &cc.PidConfig{
@@ -673,11 +747,10 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
HostAdd: c.StringSlice("add-host"),
Hostname: c.String("hostname"),
}
-
net := &cc.NetworkConfig{
- DNSOpt: c.StringSlice("dns-opt"),
- DNSSearch: c.StringSlice("dns-search"),
- DNSServers: c.StringSlice("dns"),
+ DNSOpt: dnsOptions,
+ DNSSearch: dnsSearches,
+ DNSServers: dnsServers,
HTTPProxy: c.Bool("http-proxy"),
MacAddress: c.String("mac-address"),
Network: c.String("network"),
@@ -688,9 +761,12 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
PortBindings: portBindings,
}
- sysctl, err := validateSysctl(c.StringSlice("sysctl"))
- if err != nil {
- return nil, errors.Wrapf(err, "invalid value for sysctl")
+ sysctl := map[string]string{}
+ if c.Changed("sysctl") {
+ sysctl, err = util.ValidateSysctls(c.StringSlice("sysctl"))
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid value for sysctl")
+ }
}
secConfig := &cc.SecurityConfig{
@@ -702,8 +778,10 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
Sysctl: sysctl,
}
- if err := secConfig.SetSecurityOpts(runtime, c.StringArray("security-opt")); err != nil {
- return nil, err
+ if c.Changed("security-opt") {
+ if err := secConfig.SetSecurityOpts(runtime, c.StringArray("security-opt")); err != nil {
+ return nil, err
+ }
}
// SECCOMP
@@ -717,6 +795,19 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
} else {
secConfig.SeccompPolicy = policy
}
+ rtc, err := runtime.GetConfig()
+ if err != nil {
+ return nil, err
+ }
+ volumes := rtc.Containers.Volumes
+ if c.Changed("volume") {
+ volumes = append(volumes, c.StringSlice("volume")...)
+ }
+
+ devices := rtc.Containers.Devices
+ if c.Changed("device") {
+ devices = append(devices, c.StringSlice("device")...)
+ }
config := &cc.CreateConfig{
Annotations: annotations,
@@ -727,15 +818,16 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
Command: command,
UserCommand: userCommand,
Detach: c.Bool("detach"),
- Devices: c.StringSlice("device"),
+ Devices: devices,
Entrypoint: entrypoint,
Env: env,
// ExposedPorts: ports,
- Init: c.Bool("init"),
- InitPath: c.String("init-path"),
- Image: imageName,
- ImageID: imageID,
- Interactive: c.Bool("interactive"),
+ Init: c.Bool("init"),
+ InitPath: c.String("init-path"),
+ Image: imageName,
+ RawImageName: rawImageName,
+ ImageID: imageID,
+ Interactive: c.Bool("interactive"),
// IP6Address: c.String("ipv6"), // Not implemented yet - needs CNI support for static v6
Labels: labels,
// LinkLocalIP: c.StringSlice("link-local-ip"), // Not implemented yet
@@ -781,7 +873,7 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
Tmpfs: c.StringArray("tmpfs"),
Tty: tty,
MountsFlag: c.StringArray("mount"),
- Volumes: c.StringArray("volume"),
+ Volumes: volumes,
WorkDir: workDir,
Rootfs: rootfs,
VolumesFrom: c.StringSlice("volumes-from"),
@@ -822,28 +914,6 @@ func CreateContainerFromCreateConfig(r *libpod.Runtime, createConfig *cc.CreateC
return ctr, nil
}
-var defaultEnvVariables = map[string]string{
- "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
- "TERM": "xterm",
-}
-
-// EnvVariablesFromData gets sets the default environment variables
-// for containers, and reads the variables from the image data, if present.
-func EnvVariablesFromData(data *inspect.ImageData) map[string]string {
- env := defaultEnvVariables
- if data != nil {
- for _, e := range data.Config.Env {
- split := strings.SplitN(e, "=", 2)
- if len(split) > 1 {
- env[split[0]] = split[1]
- } else {
- env[split[0]] = ""
- }
- }
- }
- return env
-}
-
func makeHealthCheckFromCli(c *GenericCLIResults) (*manifest.Schema2HealthConfig, error) {
inCommand := c.String("healthcheck-command")
inInterval := c.String("healthcheck-interval")