Skip to content

Commit

Permalink
Merge pull request #16243 from alexlarsson/volume-create-ignore
Browse files Browse the repository at this point in the history
Add podman volume create --ignore
  • Loading branch information
openshift-merge-robot authored Oct 26, 2022
2 parents 6dd0c83 + b7f05ce commit 47bcd10
Show file tree
Hide file tree
Showing 11 changed files with 80 additions and 18 deletions.
11 changes: 9 additions & 2 deletions cmd/podman/volumes/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ var (
var (
createOpts = entities.VolumeCreateOptions{}
opts = struct {
Label []string
Opts []string
Label []string
Opts []string
Ignore bool
}{}
)

Expand All @@ -53,6 +54,9 @@ func init() {
optFlagName := "opt"
flags.StringArrayVarP(&opts.Opts, optFlagName, "o", []string{}, "Set driver specific options (default [])")
_ = createCommand.RegisterFlagCompletionFunc(optFlagName, completion.AutocompleteNone)

ignoreFlagName := "ignore"
flags.BoolVar(&opts.Ignore, ignoreFlagName, false, "Don't fail if volume already exists")
}

func create(cmd *cobra.Command, args []string) error {
Expand All @@ -62,6 +66,9 @@ func create(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
createOpts.Name = args[0]
}

createOpts.IgnoreIfExists = opts.Ignore

createOpts.Label, err = parse.GetAllLabels([]string{}, opts.Label)
if err != nil {
return fmt.Errorf("unable to process labels: %w", err)
Expand Down
4 changes: 4 additions & 0 deletions docs/source/markdown/podman-volume-create.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ Such plugins must be defined in the **volume_plugins** section of the **[contain

Print usage statement

#### **--ignore**

Don't fail if the named volume already exists, instead just print the name. Note that the new options are not applied to the existing volume.

#### **--label**, **-l**=*label*

Set metadata for a volume (e.g., --label mykey=value).
Expand Down
11 changes: 11 additions & 0 deletions libpod/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -1551,6 +1551,17 @@ func WithCreateWorkingDir() CtrCreateOption {

// Volume Creation Options

func WithVolumeIgnoreIfExist() VolumeCreateOption {
return func(volume *Volume) error {
if volume.valid {
return define.ErrVolumeFinalized
}
volume.ignoreIfExists = true

return nil
}
}

// WithVolumeName sets the name of the volume.
func WithVolumeName(name string) VolumeCreateOption {
return func(volume *Volume) error {
Expand Down
21 changes: 15 additions & 6 deletions libpod/runtime_volume_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,14 @@ func (r *Runtime) newVolume(ctx context.Context, noCreatePluginVolume bool, opti
volume.config.CreatedTime = time.Now()

// Check if volume with given name exists.
exists, err := r.state.HasVolume(volume.config.Name)
if err != nil {
return nil, fmt.Errorf("checking if volume with name %s exists: %w", volume.config.Name, err)
}
if exists {
return nil, fmt.Errorf("volume with name %s already exists: %w", volume.config.Name, define.ErrVolumeExists)
if !volume.ignoreIfExists {
exists, err := r.state.HasVolume(volume.config.Name)
if err != nil {
return nil, fmt.Errorf("checking if volume with name %s exists: %w", volume.config.Name, err)
}
if exists {
return nil, fmt.Errorf("volume with name %s already exists: %w", volume.config.Name, define.ErrVolumeExists)
}
}

// Plugin can be nil if driver is local, but that's OK - superfluous
Expand Down Expand Up @@ -209,6 +211,13 @@ func (r *Runtime) newVolume(ctx context.Context, noCreatePluginVolume bool, opti

// Add the volume to state
if err := r.state.AddVolume(volume); err != nil {
if volume.ignoreIfExists && errors.Is(err, define.ErrVolumeExists) {
existingVolume, err := r.state.Volume(volume.config.Name)
if err != nil {
return nil, fmt.Errorf("reading volume from state: %w", err)
}
return existingVolume, nil
}
return nil, fmt.Errorf("adding volume to state: %w", err)
}
defer volume.newVolumeEvent(events.Create)
Expand Down
9 changes: 5 additions & 4 deletions libpod/volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ type Volume struct {
config *VolumeConfig
state *VolumeState

valid bool
plugin *plugin.VolumePlugin
runtime *Runtime
lock lock.Locker
ignoreIfExists bool
valid bool
plugin *plugin.VolumePlugin
runtime *Runtime
lock lock.Locker
}

// VolumeConfig holds the volume's immutable configuration.
Expand Down
5 changes: 5 additions & 0 deletions pkg/api/handlers/libpod/volumes.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) {
}
volumeOptions = append(volumeOptions, parsedOptions...)
}

if input.IgnoreIfExists {
volumeOptions = append(volumeOptions, libpod.WithVolumeIgnoreIfExist())
}

vol, err := runtime.NewVolume(r.Context(), volumeOptions...)
if err != nil {
utils.InternalServerError(w, err)
Expand Down
2 changes: 2 additions & 0 deletions pkg/domain/entities/volumes.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type VolumeCreateOptions struct {
Labels map[string]string `schema:"labels"`
// Mapping of driver options and values.
Options map[string]string `schema:"opts"`
// Ignore existing volumes
IgnoreIfExists bool `schema:"ignoreIfExist"`
}

type VolumeConfigResponse struct {
Expand Down
5 changes: 5 additions & 0 deletions pkg/domain/infra/abi/volumes.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.Volum
}
volumeOptions = append(volumeOptions, parsedOptions...)
}

if opts.IgnoreIfExists {
volumeOptions = append(volumeOptions, libpod.WithVolumeIgnoreIfExist())
}

vol, err := ic.Libpod.NewVolume(ctx, volumeOptions...)
if err != nil {
return nil, err
Expand Down
5 changes: 1 addition & 4 deletions pkg/systemd/quadlet/quadlet.go
Original file line number Diff line number Diff line change
Expand Up @@ -669,11 +669,9 @@ func ConvertVolume(volume *parser.UnitFile, name string) (*parser.UnitFile, erro
// Need the containers filesystem mounted to start podman
service.Add(UnitGroup, "RequiresMountsFor", "%t/containers")

execCond := fmt.Sprintf("/usr/bin/bash -c \"! /usr/bin/podman volume exists %s\"", volumeName)

labels := volume.LookupAllKeyVal(VolumeGroup, "Label")

podman := NewPodmanCmdline("volume", "create")
podman := NewPodmanCmdline("volume", "create", "--ignore")

var opts strings.Builder
opts.WriteString("o=")
Expand Down Expand Up @@ -706,7 +704,6 @@ func ConvertVolume(volume *parser.UnitFile, name string) (*parser.UnitFile, erro
service.Setv(ServiceGroup,
"Type", "oneshot",
"RemainAfterExit", "yes",
"ExecCondition", execCond,

// The default syslog identifier is the exec basename (podman) which isn't very useful here
"SyslogIdentifier", "%N")
Expand Down
3 changes: 1 addition & 2 deletions test/e2e/quadlet/basic.volume
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
## assert-key-is Unit RequiresMountsFor "%t/containers"
## assert-key-is Service Type oneshot
## assert-key-is Service RemainAfterExit yes
## assert-key-is Service ExecCondition '/usr/bin/bash -c "! /usr/bin/podman volume exists systemd-basic"'
## assert-key-is Service ExecStart "/usr/bin/podman volume create systemd-basic"
## assert-key-is Service ExecStart "/usr/bin/podman volume create --ignore systemd-basic"
## assert-key-is Service SyslogIdentifier "%N"

[Volume]
22 changes: 22 additions & 0 deletions test/e2e/volume_create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,28 @@ var _ = Describe("Podman volume create", func() {
Expect(check.OutputToStringArray()).To(HaveLen(1))
})

It("podman create volume with existing name fails", func() {
session := podmanTest.Podman([]string{"volume", "create", "myvol"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))

session = podmanTest.Podman([]string{"volume", "create", "myvol"})
session.WaitWithDefaultTimeout()
Expect(session).To(ExitWithError())
})

It("podman create volume --ignore", func() {
session := podmanTest.Podman([]string{"volume", "create", "myvol"})
session.WaitWithDefaultTimeout()
volName := session.OutputToString()
Expect(session).Should(Exit(0))

session = podmanTest.Podman([]string{"volume", "create", "--ignore", "myvol"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
Expect(session.OutputToString()).To(Equal(volName))
})

It("podman create and export volume", func() {
if podmanTest.RemoteTest {
Skip("Volume export check does not work with a remote client")
Expand Down

0 comments on commit 47bcd10

Please sign in to comment.