mirror of
https://github.com/containers/podman.git
synced 2025-05-18 23:57:22 +08:00
Merge pull request #16243 from alexlarsson/volume-create-ignore
Add podman volume create --ignore
This commit is contained in:
@ -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)
|
||||
|
@ -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).
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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]
|
||||
|
@ -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")
|
||||
|
Reference in New Issue
Block a user