-
Notifications
You must be signed in to change notification settings - Fork 772
Add a generic abstraction for filesystem based stores #3364
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,13 +32,14 @@ import ( | |
|
|
||
| "github.com/containerd/nerdctl/v2/pkg/api/types" | ||
| "github.com/containerd/nerdctl/v2/pkg/clientutil" | ||
| "github.com/containerd/nerdctl/v2/pkg/cmd/volume" | ||
| "github.com/containerd/nerdctl/v2/pkg/containerutil" | ||
| "github.com/containerd/nerdctl/v2/pkg/dnsutil/hostsstore" | ||
| "github.com/containerd/nerdctl/v2/pkg/idutil/containerwalker" | ||
| "github.com/containerd/nerdctl/v2/pkg/ipcutil" | ||
| "github.com/containerd/nerdctl/v2/pkg/labels" | ||
| "github.com/containerd/nerdctl/v2/pkg/mountutil/volumestore" | ||
| "github.com/containerd/nerdctl/v2/pkg/namestore" | ||
| "github.com/containerd/nerdctl/v2/pkg/store" | ||
| ) | ||
|
|
||
| var _ error = ErrContainerStatus{} | ||
|
|
@@ -128,18 +129,10 @@ func RemoveContainer(ctx context.Context, c containerd.Container, globalOptions | |
| return err | ||
| } | ||
| // Get volume store | ||
| volStore, err := volume.Store(globalOptions.Namespace, globalOptions.DataRoot, globalOptions.Address) | ||
| volStore, err := volumestore.New(dataStore, globalOptions.Namespace) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Calling volume.Store is useless in that case, as we already have dataStore. |
||
| if err != nil { | ||
| return err | ||
| } | ||
| // Note: technically, it is not strictly necessary to acquire an exclusive lock on the volume store here. | ||
|
apostasie marked this conversation as resolved.
|
||
| // Worst case scenario, we would fail removing anonymous volumes later on, which is a soft error, and which would | ||
| // only happen if we concurrently tried to remove the same container. | ||
| err = volStore.Lock() | ||
| if err != nil { | ||
| return err | ||
| } | ||
| defer volStore.Unlock() | ||
| // Decode IPC | ||
| ipc, err := ipcutil.DecodeIPCLabel(containerLabels[labels.IPC]) | ||
| if err != nil { | ||
|
|
@@ -212,24 +205,34 @@ func RemoveContainer(ctx context.Context, c containerd.Container, globalOptions | |
|
|
||
| // Enforce release name here in case the poststop hook name release fails - soft failure | ||
| if name != "" { | ||
| if err = nameStore.Release(name, id); err != nil { | ||
| // Double-releasing may happen with containers started with --rm, so, ignore NotFound errors | ||
| if err := nameStore.Release(name, id); err != nil && !errors.Is(err, store.ErrNotFound) { | ||
| log.G(ctx).WithError(err).Warnf("failed to release container name %s", name) | ||
| } | ||
| } | ||
|
|
||
| // De-allocate hosts file - soft failure | ||
| if err = hostsstore.DeallocHostsFile(dataStore, containerNamespace, id); err != nil { | ||
| hs, err := hostsstore.New(dataStore, containerNamespace) | ||
| if err != nil { | ||
| log.G(ctx).WithError(err).Warnf("failed to instantiate hostsstore for %q", containerNamespace) | ||
| } else if err = hs.DeallocHostsFile(id); err != nil { | ||
| // De-allocate hosts file - soft failure | ||
| log.G(ctx).WithError(err).Warnf("failed to remove hosts file for container %q", id) | ||
| } | ||
|
|
||
| // Volume removal is not handled by the poststop hook lifecycle because it depends on removeAnonVolumes option | ||
| // Note that the anonymous volume list has been obtained earlier, without locking the volume store. | ||
| // Technically, a concurrent operation MAY have deleted these anonymous volumes already at this point, which | ||
| // would make this operation here "soft fail". | ||
| // This is not a problem per-se, though we will output a warning in that case. | ||
| if anonVolumesJSON, ok := containerLabels[labels.AnonymousVolumes]; ok && removeAnonVolumes { | ||
| var anonVolumes []string | ||
| if err = json.Unmarshal([]byte(anonVolumesJSON), &anonVolumes); err != nil { | ||
| log.G(ctx).WithError(err).Warnf("failed to unmarshall anonvolume information for container %q", id) | ||
| } else { | ||
| var errs []error | ||
| _, errs, err = volStore.Remove(anonVolumes) | ||
| _, errs, err = volStore.Remove(func() ([]string, []error, error) { | ||
| return anonVolumes, nil, nil | ||
| }) | ||
| if err != nil || len(errs) > 0 { | ||
| log.G(ctx).WithError(err).Warnf("failed to remove anonymous volumes %v", anonVolumes) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -44,7 +44,7 @@ func Rename(ctx context.Context, client *containerd.Client, containerID, newCont | |
| if err != nil { | ||
| return err | ||
| } | ||
| hostst, err := hostsstore.NewStore(dataStore) | ||
| hostst, err := hostsstore.New(dataStore, options.GOptions.Namespace) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. HostsStore signature got changed to be per-namespace - align with other stores, and guarantees better isolation between namespaces and less "global" locking. |
||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
@@ -84,7 +84,7 @@ func renameContainer(ctx context.Context, container containerd.Container, newNam | |
| labels.Name: name, | ||
| } | ||
| namst.Rename(newName, id, name) | ||
| hostst.Update(ns, id, name) | ||
| hostst.Update(id, name) | ||
| container.SetLabels(ctx, lbls) | ||
| } | ||
| }() | ||
|
|
@@ -93,7 +93,7 @@ func renameContainer(ctx context.Context, container containerd.Container, newNam | |
| return err | ||
| } | ||
| if runtime.GOOS == "linux" { | ||
| if err = hostst.Update(ns, id, newName); err != nil { | ||
| if err = hostst.Update(id, newName); err != nil { | ||
| log.G(ctx).WithError(err).Warn("failed to update host networking definitions " + | ||
| "- if your container is using network 'none', this is expected - otherwise, please report this as a bug") | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -258,7 +258,7 @@ func generateMountOpts(ctx context.Context, client *containerd.Client, ensuredIm | |
|
|
||
| log.G(ctx).Debugf("creating anonymous volume %q, for \"VOLUME %s\"", | ||
| anonVolName, imgVolRaw) | ||
| anonVol, err := volStore.Create(anonVolName, []string{}) | ||
| anonVol, err := volStore.CreateWithoutLock(anonVolName, []string{}) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Create method now provides locking. |
||
| if err != nil { | ||
| return nil, nil, nil, err | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,10 +19,12 @@ package volume | |
| import ( | ||
| "context" | ||
| "fmt" | ||
| "strings" | ||
|
|
||
| containerd "github.com/containerd/containerd/v2/client" | ||
|
|
||
| "github.com/containerd/nerdctl/v2/pkg/api/types" | ||
| "github.com/containerd/nerdctl/v2/pkg/inspecttypes/native" | ||
| "github.com/containerd/nerdctl/v2/pkg/labels" | ||
| ) | ||
|
|
||
|
|
@@ -33,60 +35,50 @@ func Prune(ctx context.Context, client *containerd.Client, options types.VolumeP | |
| if err != nil { | ||
| return err | ||
| } | ||
| err = volStore.Lock() | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replacing all of that with a |
||
| if err != nil { | ||
| return err | ||
| } | ||
| defer volStore.Unlock() | ||
|
|
||
| // Get containers and see which volumes are used | ||
| containers, err := client.Containers(ctx) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| var toRemove []string // nolint: prealloc | ||
|
|
||
| usedVolumesList, err := usedVolumes(ctx, containers) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| var removeNames []string // nolint: prealloc | ||
|
|
||
| // Get the list of known volumes from the store | ||
| volumes, err := volStore.List(false) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| err = volStore.Prune(func(volumes []*native.Volume) ([]string, error) { | ||
| // Get containers and see which volumes are used | ||
| containers, err := client.Containers(ctx) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| // Iterate through the known volumes, making sure we do not remove in-use volumes | ||
| // but capture as well anon volumes (if --all was passed) | ||
| for _, volume := range volumes { | ||
| if _, ok := usedVolumesList[volume.Name]; ok { | ||
| continue | ||
| usedVolumesList, err := usedVolumes(ctx, containers) | ||
|
AkihiroSuda marked this conversation as resolved.
|
||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| if !options.All { | ||
| if volume.Labels == nil { | ||
|
|
||
| for _, volume := range volumes { | ||
| if _, ok := usedVolumesList[volume.Name]; ok { | ||
| continue | ||
| } | ||
| val, ok := (*volume.Labels)[labels.AnonymousVolumes] | ||
| //skip the named volume and only remove the anonymous volume | ||
| if !ok || val != "" { | ||
| continue | ||
| if !options.All { | ||
| if volume.Labels == nil { | ||
| continue | ||
| } | ||
| val, ok := (*volume.Labels)[labels.AnonymousVolumes] | ||
| // skip the named volume and only remove the anonymous volume | ||
| if !ok || val != "" { | ||
| continue | ||
| } | ||
| } | ||
| toRemove = append(toRemove, volume.Name) | ||
| } | ||
| removeNames = append(removeNames, volume.Name) | ||
| } | ||
|
|
||
| // Remove the volumes from that list | ||
| removedNames, _, err := volStore.Remove(removeNames) | ||
| return toRemove, nil | ||
| }) | ||
|
|
||
| if err != nil { | ||
| return err | ||
| } | ||
| if len(removedNames) > 0 { | ||
|
|
||
| if len(toRemove) > 0 { | ||
| fmt.Fprintln(options.Stdout, "Deleted Volumes:") | ||
| for _, name := range removedNames { | ||
| fmt.Fprintln(options.Stdout, name) | ||
| } | ||
| fmt.Fprintln(options.Stdout, strings.Join(toRemove, "\n")) | ||
| fmt.Fprintln(options.Stdout, "") | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is now a
Existsmethod, much cheaper thanGet