Merge pull request #16243 from alexlarsson/volume-create-ignore

Add podman volume create --ignore
This commit is contained in:
OpenShift Merge Robot 2022-10-26 15:00:51 -04:00 committed by GitHub
commit 47bcd10f61
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 80 additions and 18 deletions

View file

@ -30,8 +30,9 @@ var (
var (
createOpts = entities.VolumeCreateOptions{}
opts = struct {
Label []string
Opts []string
Label []string
Opts []string
Ignore bool
}{}
)
@ -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 {
@ -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)

View file

@ -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).

View file

@ -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 {

View file

@ -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
@ -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)

View file

@ -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.

View file

@ -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)

View file

@ -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 {

View file

@ -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

View file

@ -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=")
@ -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")

View file

@ -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]

View file

@ -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")