Document containers.conf settings for remote connections

Currently we don't document which end of the podman-remote client server
operations uses the containers.conf.  This PR begins documenting this
and then testing to make sure the defaults follow the rules.

Fixes: https://github.com/containers/podman/issues/7657

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
Daniel J Walsh
2020-11-18 16:51:33 -05:00
parent a18365c908
commit 9770947818
12 changed files with 421 additions and 164 deletions

View File

@ -84,7 +84,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) {
cgroupsFlagName := "cgroups" cgroupsFlagName := "cgroups"
createFlags.StringVar( createFlags.StringVar(
&cf.CGroupsMode, &cf.CGroupsMode,
cgroupsFlagName, containerConfig.Cgroups(), cgroupsFlagName, cgroupConfig(),
`control container cgroup configuration ("enabled"|"disabled"|"no-conmon"|"split")`, `control container cgroup configuration ("enabled"|"disabled"|"no-conmon"|"split")`,
) )
_ = cmd.RegisterFlagCompletionFunc(cgroupsFlagName, AutocompleteCgroupMode) _ = cmd.RegisterFlagCompletionFunc(cgroupsFlagName, AutocompleteCgroupMode)
@ -180,7 +180,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) {
deviceFlagName := "device" deviceFlagName := "device"
createFlags.StringSliceVar( createFlags.StringSliceVar(
&cf.Devices, &cf.Devices,
deviceFlagName, containerConfig.Devices(), deviceFlagName, devices(),
fmt.Sprintf("Add a host device to the container"), fmt.Sprintf("Add a host device to the container"),
) )
_ = cmd.RegisterFlagCompletionFunc(deviceFlagName, completion.AutocompleteDefault) _ = cmd.RegisterFlagCompletionFunc(deviceFlagName, completion.AutocompleteDefault)
@ -238,7 +238,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) {
envFlagName := "env" envFlagName := "env"
createFlags.StringArrayP( createFlags.StringArrayP(
envFlagName, "e", containerConfig.Env(), envFlagName, "e", env(),
"Set environment variables in container", "Set environment variables in container",
) )
_ = cmd.RegisterFlagCompletionFunc(envFlagName, completion.AutocompleteNone) _ = cmd.RegisterFlagCompletionFunc(envFlagName, completion.AutocompleteNone)
@ -357,7 +357,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) {
initPathFlagName := "init-path" initPathFlagName := "init-path"
createFlags.StringVar( createFlags.StringVar(
&cf.InitPath, &cf.InitPath,
initPathFlagName, containerConfig.InitPath(), initPathFlagName, initPath(),
// Do not use the Value field for setting the default value to determine user input (i.e., non-empty string) // Do not use the Value field for setting the default value to determine user input (i.e., non-empty string)
fmt.Sprintf("Path to the container-init binary"), fmt.Sprintf("Path to the container-init binary"),
) )
@ -508,7 +508,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) {
pidsLimitFlagName := "pids-limit" pidsLimitFlagName := "pids-limit"
createFlags.Int64( createFlags.Int64(
pidsLimitFlagName, containerConfig.PidsLimit(), pidsLimitFlagName, pidsLimit(),
"Tune container pids limit (set 0 for unlimited, -1 for server defaults)", "Tune container pids limit (set 0 for unlimited, -1 for server defaults)",
) )
_ = cmd.RegisterFlagCompletionFunc(pidsLimitFlagName, completion.AutocompleteNone) _ = cmd.RegisterFlagCompletionFunc(pidsLimitFlagName, completion.AutocompleteNone)
@ -543,7 +543,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) {
pullFlagName := "pull" pullFlagName := "pull"
createFlags.StringVar( createFlags.StringVar(
&cf.Pull, &cf.Pull,
pullFlagName, containerConfig.Engine.PullPolicy, pullFlagName, policy(),
`Pull image before creating ("always"|"missing"|"never")`, `Pull image before creating ("always"|"missing"|"never")`,
) )
_ = cmd.RegisterFlagCompletionFunc(pullFlagName, AutocompletePullOption) _ = cmd.RegisterFlagCompletionFunc(pullFlagName, AutocompletePullOption)
@ -606,7 +606,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) {
shmSizeFlagName := "shm-size" shmSizeFlagName := "shm-size"
createFlags.String( createFlags.String(
shmSizeFlagName, containerConfig.ShmSize(), shmSizeFlagName, shmSize(),
"Size of /dev/shm "+sizeWithUnitFormat, "Size of /dev/shm "+sizeWithUnitFormat,
) )
_ = cmd.RegisterFlagCompletionFunc(shmSizeFlagName, completion.AutocompleteNone) _ = cmd.RegisterFlagCompletionFunc(shmSizeFlagName, completion.AutocompleteNone)
@ -715,7 +715,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) {
ulimitFlagName := "ulimit" ulimitFlagName := "ulimit"
createFlags.StringSliceVar( createFlags.StringSliceVar(
&cf.Ulimit, &cf.Ulimit,
ulimitFlagName, containerConfig.Ulimits(), ulimitFlagName, ulimits(),
"Ulimit options", "Ulimit options",
) )
_ = cmd.RegisterFlagCompletionFunc(ulimitFlagName, completion.AutocompleteNone) _ = cmd.RegisterFlagCompletionFunc(ulimitFlagName, completion.AutocompleteNone)
@ -753,7 +753,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) {
volumeFlagName := "volume" volumeFlagName := "volume"
createFlags.StringArrayVarP( createFlags.StringArrayVarP(
&cf.Volume, &cf.Volume,
volumeFlagName, "v", containerConfig.Volumes(), volumeFlagName, "v", volumes(),
"Bind mount a volume into the container", "Bind mount a volume into the container",
) )
_ = cmd.RegisterFlagCompletionFunc(volumeFlagName, AutocompleteVolumeFlag) _ = cmd.RegisterFlagCompletionFunc(volumeFlagName, AutocompleteVolumeFlag)

View File

@ -6,6 +6,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/api/handlers"
"github.com/containers/podman/v2/pkg/cgroups" "github.com/containers/podman/v2/pkg/cgroups"
"github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/domain/entities"
@ -440,3 +441,66 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup
cmd = append(cmd, cc.Config.Cmd...) cmd = append(cmd, cc.Config.Cmd...)
return &cliOpts, cmd, nil return &cliOpts, cmd, nil
} }
func ulimits() []string {
if !registry.IsRemote() {
return containerConfig.Ulimits()
}
return nil
}
func cgroupConfig() string {
if !registry.IsRemote() {
return containerConfig.Cgroups()
}
return ""
}
func devices() []string {
if !registry.IsRemote() {
return containerConfig.Devices()
}
return nil
}
func env() []string {
if !registry.IsRemote() {
return containerConfig.Env()
}
return nil
}
func initPath() string {
if !registry.IsRemote() {
return containerConfig.InitPath()
}
return ""
}
func pidsLimit() int64 {
if !registry.IsRemote() {
return containerConfig.PidsLimit()
}
return -1
}
func policy() string {
if !registry.IsRemote() {
return containerConfig.Engine.PullPolicy
}
return ""
}
func shmSize() string {
if !registry.IsRemote() {
return containerConfig.ShmSize()
}
return ""
}
func volumes() []string {
if !registry.IsRemote() {
return containerConfig.Volumes()
}
return nil
}

View File

@ -10,7 +10,6 @@ import (
"github.com/containers/podman/v2/pkg/util" "github.com/containers/podman/v2/pkg/util"
spec "github.com/opencontainers/runtime-spec/specs-go" spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus"
) )
const ( const (
@ -45,7 +44,7 @@ func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bo
} }
// Next --volumes flag. // Next --volumes flag.
volumeMounts, volumeVolumes, overlayVolumes, err := getVolumeMounts(volumeFlag) volumeMounts, volumeVolumes, overlayVolumes, err := specgen.GenVolumeMounts(volumeFlag)
if err != nil { if err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
@ -594,105 +593,6 @@ func getImageVolume(args []string) (*specgen.ImageVolume, error) {
return newVolume, nil return newVolume, nil
} }
func getVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*specgen.NamedVolume, map[string]*specgen.OverlayVolume, error) {
mounts := make(map[string]spec.Mount)
volumes := make(map[string]*specgen.NamedVolume)
overlayVolumes := make(map[string]*specgen.OverlayVolume)
volumeFormatErr := errors.Errorf("incorrect volume format, should be [host-dir:]ctr-dir[:option]")
for _, vol := range volumeFlag {
var (
options []string
src string
dest string
err error
)
splitVol := strings.Split(vol, ":")
if len(splitVol) > 3 {
return nil, nil, nil, errors.Wrapf(volumeFormatErr, vol)
}
src = splitVol[0]
if len(splitVol) == 1 {
// This is an anonymous named volume. Only thing given
// is destination.
// Name/source will be blank, and populated by libpod.
src = ""
dest = splitVol[0]
} else if len(splitVol) > 1 {
dest = splitVol[1]
}
if len(splitVol) > 2 {
if options, err = parse.ValidateVolumeOpts(strings.Split(splitVol[2], ",")); err != nil {
return nil, nil, nil, err
}
}
// Do not check source dir for anonymous volumes
if len(splitVol) > 1 {
if err := parse.ValidateVolumeHostDir(src); err != nil {
return nil, nil, nil, err
}
}
if err := parse.ValidateVolumeCtrDir(dest); err != nil {
return nil, nil, nil, err
}
cleanDest := filepath.Clean(dest)
if strings.HasPrefix(src, "/") || strings.HasPrefix(src, ".") {
// This is not a named volume
overlayFlag := false
for _, o := range options {
if o == "O" {
overlayFlag = true
if len(options) > 1 {
return nil, nil, nil, errors.New("can't use 'O' with other options")
}
}
}
if overlayFlag {
// This is a overlay volume
newOverlayVol := new(specgen.OverlayVolume)
newOverlayVol.Destination = cleanDest
newOverlayVol.Source = src
if _, ok := overlayVolumes[newOverlayVol.Destination]; ok {
return nil, nil, nil, errors.Wrapf(errDuplicateDest, newOverlayVol.Destination)
}
overlayVolumes[newOverlayVol.Destination] = newOverlayVol
} else {
newMount := spec.Mount{
Destination: cleanDest,
Type: string(TypeBind),
Source: src,
Options: options,
}
if _, ok := mounts[newMount.Destination]; ok {
return nil, nil, nil, errors.Wrapf(errDuplicateDest, newMount.Destination)
}
mounts[newMount.Destination] = newMount
}
} else {
// This is a named volume
newNamedVol := new(specgen.NamedVolume)
newNamedVol.Name = src
newNamedVol.Dest = cleanDest
newNamedVol.Options = options
if _, ok := volumes[newNamedVol.Dest]; ok {
return nil, nil, nil, errors.Wrapf(errDuplicateDest, newNamedVol.Dest)
}
volumes[newNamedVol.Dest] = newNamedVol
}
logrus.Debugf("User mount %s:%s options %v", src, dest, options)
}
return mounts, volumes, overlayVolumes, nil
}
// GetTmpfsMounts creates spec.Mount structs for user-requested tmpfs mounts // GetTmpfsMounts creates spec.Mount structs for user-requested tmpfs mounts
func getTmpfsMounts(tmpfsFlag []string) (map[string]spec.Mount, error) { func getTmpfsMounts(tmpfsFlag []string) (map[string]spec.Mount, error) {
m := make(map[string]spec.Mount) m := make(map[string]spec.Mount)

View File

@ -18,6 +18,10 @@ any point.
The initial status of the container created with **podman create** is 'created'. The initial status of the container created with **podman create** is 'created'.
Default settings for flags are defined in `containers.conf`. Most settings for
remote connections use the server's containers.conf, except when documented in
man pages.
## OPTIONS ## OPTIONS
#### **--add-host**=*host* #### **--add-host**=*host*
@ -817,6 +821,7 @@ Signal to stop a container. Default is SIGTERM.
#### **--stop-timeout**=*seconds* #### **--stop-timeout**=*seconds*
Timeout (in seconds) to stop a container. Default is 10. Timeout (in seconds) to stop a container. Default is 10.
Remote connections use local containers.conf for defaults
#### **--subgidname**=*name* #### **--subgidname**=*name*
@ -893,10 +898,12 @@ standard input.
#### **--tz**=*timezone* #### **--tz**=*timezone*
Set timezone in container. This flag takes area-based timezones, GMT time, as well as `local`, which sets the timezone in the container to match the host machine. See `/usr/share/zoneinfo/` for valid timezones. Set timezone in container. This flag takes area-based timezones, GMT time, as well as `local`, which sets the timezone in the container to match the host machine. See `/usr/share/zoneinfo/` for valid timezones.
Remote connections use local containers.conf for defaults
#### **--umask**=*umask* #### **--umask**=*umask*
Set the umask inside the container. Defaults to `0022`. Set the umask inside the container. Defaults to `0022`.
Remote connections use local containers.conf for defaults
#### **--uidmap**=*container_uid:host_uid:amount* #### **--uidmap**=*container_uid:host_uid:amount*

View File

@ -33,6 +33,10 @@ is located at _/run/.containerenv_.
When running from a user defined network namespace, the _/etc/netns/NSNAME/resolv.conf_ When running from a user defined network namespace, the _/etc/netns/NSNAME/resolv.conf_
will be used if it exists, otherwise _/etc/resolv.conf_ will be used. will be used if it exists, otherwise _/etc/resolv.conf_ will be used.
Default settings are defined in `containers.conf`. Most settings for remote
connections use the servers containers.conf, except when documented in man
pages.
## OPTIONS ## OPTIONS
#### **--add-host**=_host_:_ip_ #### **--add-host**=_host_:_ip_
@ -857,6 +861,7 @@ Signal to stop a container. Default is **SIGTERM**.
#### **--stop-timeout**=*seconds* #### **--stop-timeout**=*seconds*
Timeout to stop a container. Default is **10**. Timeout to stop a container. Default is **10**.
Remote connections use local containers.conf for defaults
#### **--subgidname**=*name* #### **--subgidname**=*name*
@ -952,10 +957,12 @@ standard input.
#### **--tz**=*timezone* #### **--tz**=*timezone*
Set timezone in container. This flag takes area-based timezones, GMT time, as well as `local`, which sets the timezone in the container to match the host machine. See `/usr/share/zoneinfo/` for valid timezones. Set timezone in container. This flag takes area-based timezones, GMT time, as well as `local`, which sets the timezone in the container to match the host machine. See `/usr/share/zoneinfo/` for valid timezones.
Remote connections use local containers.conf for defaults
#### **--umask**=*umask* #### **--umask**=*umask*
Set the umask inside the container. Defaults to `0022`. Set the umask inside the container. Defaults to `0022`.
Remote connections use local containers.conf for defaults
#### **--uidmap**=*container_uid*:*host_uid*:*amount* #### **--uidmap**=*container_uid*:*host_uid*:*amount*

View File

@ -17,6 +17,10 @@ Podman uses Buildah(1) internally to create container images. Both tools share i
(not container) storage, hence each can use or manipulate images (but not containers) (not container) storage, hence each can use or manipulate images (but not containers)
created by the other. created by the other.
Default settings for flags are defined in `containers.conf`. Most settings for
Remote connections use the server's containers.conf, except when documented in
man pages.
**podman [GLOBAL OPTIONS]** **podman [GLOBAL OPTIONS]**
## GLOBAL OPTIONS ## GLOBAL OPTIONS
@ -33,6 +37,7 @@ Path of the configuration directory for CNI networks. (Default: `/etc/cni/net.d
#### **--connection**, **-c** #### **--connection**, **-c**
Connection to use for remote podman (Default connection is configured in `containers.conf`) Connection to use for remote podman (Default connection is configured in `containers.conf`)
Remote connections use local containers.conf for default.
#### **--conmon** #### **--conmon**
Path of the conmon binary (Default path is configured in `containers.conf`) Path of the conmon binary (Default path is configured in `containers.conf`)
@ -71,6 +76,7 @@ Identity value resolution precedence:
- command line value - command line value
- environment variable `CONTAINER_SSHKEY`, if `CONTAINER_HOST` is found - environment variable `CONTAINER_SSHKEY`, if `CONTAINER_HOST` is found
- `containers.conf` - `containers.conf`
Remote connections use local containers.conf for default.
#### **--log-level**=*level* #### **--log-level**=*level*
@ -86,6 +92,7 @@ Path to the command binary to use for setting up a network. It is currently onl
#### **--remote**, **-r** #### **--remote**, **-r**
Access Podman service will be remote Access Podman service will be remote
Remote connections use local containers.conf for default.
#### **--url**=*value* #### **--url**=*value*
URL to access Podman service (default from `containers.conf`, rootless `unix://run/user/$UID/podman/podman.sock` or as root `unix://run/podman/podman.sock`). URL to access Podman service (default from `containers.conf`, rootless `unix://run/user/$UID/podman/podman.sock` or as root `unix://run/podman/podman.sock`).
@ -104,6 +111,7 @@ URL value resolution precedence:
- environment variable `CONTAINER_HOST` - environment variable `CONTAINER_HOST`
- `containers.conf` - `containers.conf`
- `unix://run/podman/podman.sock` - `unix://run/podman/podman.sock`
Remote connections use local containers.conf for default.
#### **--root**=*value* #### **--root**=*value*

View File

@ -111,7 +111,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
return nil, errors.Wrap(err, "invalid config provided") return nil, errors.Wrap(err, "invalid config provided")
} }
finalMounts, finalVolumes, err := finalizeMounts(ctx, s, rt, rtc, newImage) finalMounts, finalVolumes, finalOverlays, err := finalizeMounts(ctx, s, rt, rtc, newImage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -121,7 +121,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
return nil, err return nil, err
} }
opts, err := createContainerOptions(ctx, rt, s, pod, finalVolumes, newImage, command) opts, err := createContainerOptions(ctx, rt, s, pod, finalVolumes, finalOverlays, newImage, command)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -144,7 +144,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
return rt.NewContainer(ctx, runtimeSpec, options...) return rt.NewContainer(ctx, runtimeSpec, options...)
} }
func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator, pod *libpod.Pod, volumes []*specgen.NamedVolume, img *image.Image, command []string) ([]libpod.CtrCreateOption, error) { func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator, pod *libpod.Pod, volumes []*specgen.NamedVolume, overlays []*specgen.OverlayVolume, img *image.Image, command []string) ([]libpod.CtrCreateOption, error) {
var options []libpod.CtrCreateOption var options []libpod.CtrCreateOption
var err error var err error
@ -224,7 +224,7 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
for _, volume := range volumes { for _, volume := range volumes {
destinations = append(destinations, volume.Dest) destinations = append(destinations, volume.Dest)
} }
for _, overlayVolume := range s.OverlayVolumes { for _, overlayVolume := range overlays {
destinations = append(destinations, overlayVolume.Destination) destinations = append(destinations, overlayVolume.Destination)
} }
for _, imageVolume := range s.ImageVolumes { for _, imageVolume := range s.ImageVolumes {
@ -244,9 +244,9 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
options = append(options, libpod.WithNamedVolumes(vols)) options = append(options, libpod.WithNamedVolumes(vols))
} }
if len(s.OverlayVolumes) != 0 { if len(overlays) != 0 {
var vols []*libpod.ContainerOverlayVolume var vols []*libpod.ContainerOverlayVolume
for _, v := range s.OverlayVolumes { for _, v := range overlays {
vols = append(vols, &libpod.ContainerOverlayVolume{ vols = append(vols, &libpod.ContainerOverlayVolume{
Dest: v.Destination, Dest: v.Destination,
Source: v.Source, Source: v.Source,

View File

@ -33,17 +33,17 @@ var (
) )
// Produce final mounts and named volumes for a container // Produce final mounts and named volumes for a container
func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, img *image.Image) ([]spec.Mount, []*specgen.NamedVolume, error) { func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, img *image.Image) ([]spec.Mount, []*specgen.NamedVolume, []*specgen.OverlayVolume, error) {
// Get image volumes // Get image volumes
baseMounts, baseVolumes, err := getImageVolumes(ctx, img, s) baseMounts, baseVolumes, err := getImageVolumes(ctx, img, s)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, nil, err
} }
// Get volumes-from mounts // Get volumes-from mounts
volFromMounts, volFromVolumes, err := getVolumesFrom(s.VolumesFrom, rt) volFromMounts, volFromVolumes, err := getVolumesFrom(s.VolumesFrom, rt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, nil, err
} }
// Supersede from --volumes-from. // Supersede from --volumes-from.
@ -57,19 +57,53 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru
// Need to make map forms of specgen mounts/volumes. // Need to make map forms of specgen mounts/volumes.
unifiedMounts := map[string]spec.Mount{} unifiedMounts := map[string]spec.Mount{}
unifiedVolumes := map[string]*specgen.NamedVolume{} unifiedVolumes := map[string]*specgen.NamedVolume{}
unifiedOverlays := map[string]*specgen.OverlayVolume{}
// Need to make map forms of specgen mounts/volumes.
commonMounts, commonVolumes, commonOverlayVolumes, err := specgen.GenVolumeMounts(rtc.Volumes())
if err != nil {
return nil, nil, nil, err
}
for _, m := range s.Mounts { for _, m := range s.Mounts {
if _, ok := unifiedMounts[m.Destination]; ok { if _, ok := unifiedMounts[m.Destination]; ok {
return nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified mounts - multiple mounts at %q", m.Destination) return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified mounts - multiple mounts at %q", m.Destination)
} }
unifiedMounts[m.Destination] = m unifiedMounts[m.Destination] = m
} }
for _, m := range commonMounts {
if _, ok := unifiedMounts[m.Destination]; !ok {
unifiedMounts[m.Destination] = m
}
}
for _, v := range s.Volumes { for _, v := range s.Volumes {
if _, ok := unifiedVolumes[v.Dest]; ok { if _, ok := unifiedVolumes[v.Dest]; ok {
return nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified volumes - multiple volumes at %q", v.Dest) return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified volumes - multiple volumes at %q", v.Dest)
} }
unifiedVolumes[v.Dest] = v unifiedVolumes[v.Dest] = v
} }
for _, v := range commonVolumes {
if _, ok := unifiedVolumes[v.Dest]; !ok {
unifiedVolumes[v.Dest] = v
}
}
for _, v := range s.OverlayVolumes {
if _, ok := unifiedOverlays[v.Destination]; ok {
return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified volumes - multiple volumes at %q", v.Destination)
}
unifiedOverlays[v.Destination] = v
}
for _, v := range commonOverlayVolumes {
if _, ok := unifiedOverlays[v.Destination]; ok {
unifiedOverlays[v.Destination] = v
}
}
// If requested, add container init binary // If requested, add container init binary
if s.Init { if s.Init {
initPath := s.InitPath initPath := s.InitPath
@ -78,10 +112,10 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru
} }
initMount, err := addContainerInitBinary(s, initPath) initMount, err := addContainerInitBinary(s, initPath)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, nil, err
} }
if _, ok := unifiedMounts[initMount.Destination]; ok { if _, ok := unifiedMounts[initMount.Destination]; ok {
return nil, nil, errors.Wrapf(errDuplicateDest, "conflict with mount added by --init to %q", initMount.Destination) return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict with mount added by --init to %q", initMount.Destination)
} }
unifiedMounts[initMount.Destination] = initMount unifiedMounts[initMount.Destination] = initMount
} }
@ -115,12 +149,12 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru
// Check for conflicts between named volumes and mounts // Check for conflicts between named volumes and mounts
for dest := range baseMounts { for dest := range baseMounts {
if _, ok := baseVolumes[dest]; ok { if _, ok := baseVolumes[dest]; ok {
return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
} }
} }
for dest := range baseVolumes { for dest := range baseVolumes {
if _, ok := baseMounts[dest]; ok { if _, ok := baseMounts[dest]; ok {
return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
} }
} }
// Final step: maps to arrays // Final step: maps to arrays
@ -129,7 +163,7 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru
if mount.Type == TypeBind { if mount.Type == TypeBind {
absSrc, err := filepath.Abs(mount.Source) absSrc, err := filepath.Abs(mount.Source)
if err != nil { if err != nil {
return nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source) return nil, nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source)
} }
mount.Source = absSrc mount.Source = absSrc
} }
@ -140,7 +174,12 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru
finalVolumes = append(finalVolumes, volume) finalVolumes = append(finalVolumes, volume)
} }
return finalMounts, finalVolumes, nil finalOverlays := make([]*specgen.OverlayVolume, 0, len(unifiedOverlays))
for _, volume := range unifiedOverlays {
finalOverlays = append(finalOverlays, volume)
}
return finalMounts, finalVolumes, finalOverlays, nil
} }
// Get image volumes from the given image // Get image volumes from the given image

View File

@ -1,13 +1,13 @@
package specgen package specgen
import ( import (
"errors"
"net" "net"
"syscall" "syscall"
"github.com/containers/image/v5/manifest" "github.com/containers/image/v5/manifest"
"github.com/containers/storage" "github.com/containers/storage"
spec "github.com/opencontainers/runtime-spec/specs-go" spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
) )
// LogConfig describes the logging characteristics for a container // LogConfig describes the logging characteristics for a container
@ -459,42 +459,6 @@ type SpecGenerator struct {
ContainerHealthCheckConfig ContainerHealthCheckConfig
} }
// NamedVolume holds information about a named volume that will be mounted into
// the container.
type NamedVolume struct {
// Name is the name of the named volume to be mounted. May be empty.
// If empty, a new named volume with a pseudorandomly generated name
// will be mounted at the given destination.
Name string
// Destination to mount the named volume within the container. Must be
// an absolute path. Path will be created if it does not exist.
Dest string
// Options are options that the named volume will be mounted with.
Options []string
}
// OverlayVolume holds information about a overlay volume that will be mounted into
// the container.
type OverlayVolume struct {
// Destination is the absolute path where the mount will be placed in the container.
Destination string `json:"destination"`
// Source specifies the source path of the mount.
Source string `json:"source,omitempty"`
}
// ImageVolume is a volume based on a container image. The container image is
// first mounted on the host and is then bind-mounted into the container. An
// ImageVolume is always mounted read only.
type ImageVolume struct {
// Source is the source of the image volume. The image can be referred
// to by name and by ID.
Source string
// Destination is the absolute path of the mount in the container.
Destination string
// ReadWrite sets the volume writable.
ReadWrite bool
}
// PortMapping is one or more ports that will be mapped into the container. // PortMapping is one or more ports that will be mapped into the container.
type PortMapping struct { type PortMapping struct {
// HostIP is the IP that we will bind to on the host. // HostIP is the IP that we will bind to on the host.

149
pkg/specgen/volumes.go Normal file
View File

@ -0,0 +1,149 @@
package specgen
import (
"path/filepath"
"strings"
"github.com/containers/buildah/pkg/parse"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// NamedVolume holds information about a named volume that will be mounted into
// the container.
type NamedVolume struct {
// Name is the name of the named volume to be mounted. May be empty.
// If empty, a new named volume with a pseudorandomly generated name
// will be mounted at the given destination.
Name string
// Destination to mount the named volume within the container. Must be
// an absolute path. Path will be created if it does not exist.
Dest string
// Options are options that the named volume will be mounted with.
Options []string
}
// OverlayVolume holds information about a overlay volume that will be mounted into
// the container.
type OverlayVolume struct {
// Destination is the absolute path where the mount will be placed in the container.
Destination string `json:"destination"`
// Source specifies the source path of the mount.
Source string `json:"source,omitempty"`
}
// ImageVolume is a volume based on a container image. The container image is
// first mounted on the host and is then bind-mounted into the container. An
// ImageVolume is always mounted read only.
type ImageVolume struct {
// Source is the source of the image volume. The image can be referred
// to by name and by ID.
Source string
// Destination is the absolute path of the mount in the container.
Destination string
// ReadWrite sets the volume writable.
ReadWrite bool
}
// GenVolumeMounts parses user input into mounts, volumes and overlay volumes
func GenVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*NamedVolume, map[string]*OverlayVolume, error) {
errDuplicateDest := errors.Errorf("duplicate mount destination")
mounts := make(map[string]spec.Mount)
volumes := make(map[string]*NamedVolume)
overlayVolumes := make(map[string]*OverlayVolume)
volumeFormatErr := errors.Errorf("incorrect volume format, should be [host-dir:]ctr-dir[:option]")
for _, vol := range volumeFlag {
var (
options []string
src string
dest string
err error
)
splitVol := strings.Split(vol, ":")
if len(splitVol) > 3 {
return nil, nil, nil, errors.Wrapf(volumeFormatErr, vol)
}
src = splitVol[0]
if len(splitVol) == 1 {
// This is an anonymous named volume. Only thing given
// is destination.
// Name/source will be blank, and populated by libpod.
src = ""
dest = splitVol[0]
} else if len(splitVol) > 1 {
dest = splitVol[1]
}
if len(splitVol) > 2 {
if options, err = parse.ValidateVolumeOpts(strings.Split(splitVol[2], ",")); err != nil {
return nil, nil, nil, err
}
}
// Do not check source dir for anonymous volumes
if len(splitVol) > 1 {
if err := parse.ValidateVolumeHostDir(src); err != nil {
return nil, nil, nil, err
}
}
if err := parse.ValidateVolumeCtrDir(dest); err != nil {
return nil, nil, nil, err
}
cleanDest := filepath.Clean(dest)
if strings.HasPrefix(src, "/") || strings.HasPrefix(src, ".") {
// This is not a named volume
overlayFlag := false
for _, o := range options {
if o == "O" {
overlayFlag = true
if len(options) > 1 {
return nil, nil, nil, errors.New("can't use 'O' with other options")
}
}
}
if overlayFlag {
// This is a overlay volume
newOverlayVol := new(OverlayVolume)
newOverlayVol.Destination = cleanDest
newOverlayVol.Source = src
if _, ok := overlayVolumes[newOverlayVol.Destination]; ok {
return nil, nil, nil, errors.Wrapf(errDuplicateDest, newOverlayVol.Destination)
}
overlayVolumes[newOverlayVol.Destination] = newOverlayVol
} else {
newMount := spec.Mount{
Destination: cleanDest,
Type: "bind",
Source: src,
Options: options,
}
if _, ok := mounts[newMount.Destination]; ok {
return nil, nil, nil, errors.Wrapf(errDuplicateDest, newMount.Destination)
}
mounts[newMount.Destination] = newMount
}
} else {
// This is a named volume
newNamedVol := new(NamedVolume)
newNamedVol.Name = src
newNamedVol.Dest = cleanDest
newNamedVol.Options = options
if _, ok := volumes[newNamedVol.Dest]; ok {
return nil, nil, nil, errors.Wrapf(errDuplicateDest, newNamedVol.Dest)
}
volumes[newNamedVol.Dest] = newNamedVol
}
logrus.Debugf("User mount %s:%s options %v", src, dest, options)
}
return mounts, volumes, overlayVolumes, nil
}

View File

@ -0,0 +1,51 @@
[containers]
# A list of ulimits to be set in containers by default, specified as
# "<ulimit name>=<soft limit>:<hard limit>", for example:
# "nofile=1024:2048"
# See setrlimit(2) for a list of resource names.
# Any limit not specified here will be inherited from the process launching the
# container engine.
# Ulimits has limits for non privileged container engines.
#
default_ulimits = [
"nofile=100:100",
]
# Environment variable list for the conmon process; used for passing necessary
# environment variables to conmon or the runtime.
#
env = [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"foo=bar1",
]
# container engines use container separation using MAC(SELinux) labeling.
# Flag is ignored on label disabled systems.
#
label = false
# Size of /dev/shm. Specified as <number><unit>.
# Unit is optional, values:
# b (bytes), k (kilobytes), m (megabytes), or g (gigabytes).
# If the unit is omitted, the system uses bytes.
#
shm_size = "202k"
# List of devices. Specified as
# "<device-on-host>:<device-on-container>:<permissions>", for example:
# "/dev/sdc:/dev/xvdc:rwm".
# If it is empty or commented out, only the default devices will be used
#
devices = []
default_sysctls = [
"net.ipv4.ping_group_range=0 0",
]
dns_searches=[ "barfoo.com", ]
dns_servers=[ "4.3.2.1", ]
tz = "America/New_York"
umask = "0022"

View File

@ -177,6 +177,9 @@ var _ = Describe("Podman run", func() {
} }
os.Setenv("CONTAINERS_CONF", conffile) os.Setenv("CONTAINERS_CONF", conffile)
if IsRemote() {
podmanTest.RestartRemoteService()
}
result := podmanTest.Podman([]string{"run", ALPINE, "ls", tempdir}) result := podmanTest.Podman([]string{"run", ALPINE, "ls", tempdir})
result.WaitWithDefaultTimeout() result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0)) Expect(result.ExitCode()).To(Equal(0))
@ -224,6 +227,17 @@ var _ = Describe("Podman run", func() {
Expect(session.LineInOuputStartsWith("search")).To(BeFalse()) Expect(session.LineInOuputStartsWith("search")).To(BeFalse())
}) })
It("podman run use containers.conf search domain", func() {
session := podmanTest.Podman([]string{"run", ALPINE, "cat", "/etc/resolv.conf"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.LineInOuputStartsWith("search")).To(BeTrue())
Expect(session.OutputToString()).To(ContainSubstring("foobar.com"))
Expect(session.OutputToString()).To(ContainSubstring("1.2.3.4"))
Expect(session.OutputToString()).To(ContainSubstring("debug"))
})
It("podman run containers.conf timezone", func() { It("podman run containers.conf timezone", func() {
//containers.conf timezone set to Pacific/Honolulu //containers.conf timezone set to Pacific/Honolulu
session := podmanTest.Podman([]string{"run", ALPINE, "date", "+'%H %Z'"}) session := podmanTest.Podman([]string{"run", ALPINE, "date", "+'%H %Z'"})
@ -231,6 +245,7 @@ var _ = Describe("Podman run", func() {
Expect(session.ExitCode()).To(Equal(0)) Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("HST")) Expect(session.OutputToString()).To(ContainSubstring("HST"))
}) })
It("podman run containers.conf umask", func() { It("podman run containers.conf umask", func() {
//containers.conf umask set to 0002 //containers.conf umask set to 0002
if !strings.Contains(podmanTest.OCIRuntime, "crun") { if !strings.Contains(podmanTest.OCIRuntime, "crun") {
@ -243,4 +258,57 @@ var _ = Describe("Podman run", func() {
Expect(session.OutputToString()).To(Equal("0002")) Expect(session.OutputToString()).To(Equal("0002"))
}) })
It("podman-remote test localcontainers.conf versus remote containers.conf", func() {
if !IsRemote() {
Skip("this test is only for remote")
}
os.Setenv("CONTAINERS_CONF", "config/containers-remote.conf")
// Configuration that comes from remote server
// env
session := podmanTest.Podman([]string{"run", ALPINE, "printenv", "foo"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(Equal("bar"))
// dns-search, server, options
session = podmanTest.Podman([]string{"run", ALPINE, "cat", "/etc/resolv.conf"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.LineInOuputStartsWith("search")).To(BeTrue())
Expect(session.OutputToString()).To(ContainSubstring("foobar.com"))
Expect(session.OutputToString()).To(ContainSubstring("1.2.3.4"))
Expect(session.OutputToString()).To(ContainSubstring("debug"))
// sysctls
session = podmanTest.Podman([]string{"run", "--rm", ALPINE, "cat", "/proc/sys/net/ipv4/ping_group_range"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("1000"))
// shm-size
session = podmanTest.Podman([]string{"run", ALPINE, "grep", "shm", "/proc/self/mounts"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("size=200k"))
// ulimits
session = podmanTest.Podman([]string{"run", "--rm", fedoraMinimal, "ulimit", "-n"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("500"))
// Configuration that comes from remote client
// Timezone
session = podmanTest.Podman([]string{"run", ALPINE, "date", "+'%H %Z'"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("EST"))
// Umask
session = podmanTest.Podman([]string{"run", "--rm", ALPINE, "sh", "-c", "umask"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(Equal("0022"))
})
}) })