From 977094781841f57c82b71e3ccc32dad952a8c0e5 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Wed, 18 Nov 2020 16:51:33 -0500 Subject: 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 --- pkg/specgen/generate/container_create.go | 12 +-- pkg/specgen/generate/storage.go | 61 ++++++++++--- pkg/specgen/specgen.go | 38 +------- pkg/specgen/volumes.go | 149 +++++++++++++++++++++++++++++++ 4 files changed, 206 insertions(+), 54 deletions(-) create mode 100644 pkg/specgen/volumes.go (limited to 'pkg') diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index c049e64cf..45a374216 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -111,7 +111,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener 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 { return nil, err } @@ -121,7 +121,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener 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 { return nil, err } @@ -144,7 +144,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener 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 err error @@ -224,7 +224,7 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen. for _, volume := range volumes { destinations = append(destinations, volume.Dest) } - for _, overlayVolume := range s.OverlayVolumes { + for _, overlayVolume := range overlays { destinations = append(destinations, overlayVolume.Destination) } 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)) } - if len(s.OverlayVolumes) != 0 { + if len(overlays) != 0 { var vols []*libpod.ContainerOverlayVolume - for _, v := range s.OverlayVolumes { + for _, v := range overlays { vols = append(vols, &libpod.ContainerOverlayVolume{ Dest: v.Destination, Source: v.Source, diff --git a/pkg/specgen/generate/storage.go b/pkg/specgen/generate/storage.go index b225f79ee..331a5c5bf 100644 --- a/pkg/specgen/generate/storage.go +++ b/pkg/specgen/generate/storage.go @@ -33,17 +33,17 @@ var ( ) // 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 baseMounts, baseVolumes, err := getImageVolumes(ctx, img, s) if err != nil { - return nil, nil, err + return nil, nil, nil, err } // Get volumes-from mounts volFromMounts, volFromVolumes, err := getVolumesFrom(s.VolumesFrom, rt) if err != nil { - return nil, nil, err + return nil, nil, nil, err } // 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. unifiedMounts := map[string]spec.Mount{} 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 { 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 } + + for _, m := range commonMounts { + if _, ok := unifiedMounts[m.Destination]; !ok { + unifiedMounts[m.Destination] = m + } + } + for _, v := range s.Volumes { 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 } + 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 s.Init { initPath := s.InitPath @@ -78,10 +112,10 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru } initMount, err := addContainerInitBinary(s, initPath) if err != nil { - return nil, nil, err + return nil, nil, nil, err } 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 } @@ -115,12 +149,12 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru // Check for conflicts between named volumes and mounts for dest := range baseMounts { 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 { 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 @@ -129,7 +163,7 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru if mount.Type == TypeBind { absSrc, err := filepath.Abs(mount.Source) 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 } @@ -140,7 +174,12 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru 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 diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index 0a9a16ea7..fad2406e5 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -1,13 +1,13 @@ package specgen import ( - "errors" "net" "syscall" "github.com/containers/image/v5/manifest" "github.com/containers/storage" spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" ) // LogConfig describes the logging characteristics for a container @@ -459,42 +459,6 @@ type SpecGenerator struct { 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. type PortMapping struct { // HostIP is the IP that we will bind to on the host. diff --git a/pkg/specgen/volumes.go b/pkg/specgen/volumes.go new file mode 100644 index 000000000..1178f9960 --- /dev/null +++ b/pkg/specgen/volumes.go @@ -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 +} -- cgit v1.2.3-54-g00ecf