Skip to content

Commit 42df434

Browse files
nervghalexey-igrychev
authored andcommitted
fix(host-cleanup): translate and handle docker prune error
Signed-off-by: Alexandr Zaytsev <alexandr.zaytsev@flant.com>
1 parent 6733b2f commit 42df434

File tree

6 files changed

+89
-20
lines changed

6 files changed

+89
-20
lines changed

pkg/container_backend/docker_server_backend.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -394,16 +394,26 @@ func (backend *DockerServerBackend) PostManifest(ctx context.Context, ref string
394394

395395
func (backend *DockerServerBackend) PruneImages(ctx context.Context, options prune.Options) (prune.Report, error) {
396396
report, err := docker.ImagesPrune(ctx, docker.ImagesPruneOptions(options))
397-
if err != nil {
397+
398+
switch {
399+
case docker.IsErrPruneRunning(err):
400+
return prune.Report{}, errors.Join(ErrPruneIsAlreadyRunning, err)
401+
case err != nil:
398402
return prune.Report{}, err
399403
}
404+
400405
return prune.Report(report), err
401406
}
402407

403408
func (backend *DockerServerBackend) PruneVolumes(ctx context.Context, options prune.Options) (prune.Report, error) {
404409
report, err := docker.VolumesPrune(ctx, docker.VolumesPruneOptions(options))
405-
if err != nil {
410+
411+
switch {
412+
case docker.IsErrPruneRunning(err):
413+
return prune.Report{}, errors.Join(ErrPruneIsAlreadyRunning, err)
414+
case err != nil:
406415
return prune.Report{}, err
407416
}
417+
408418
return prune.Report(report), err
409419
}

pkg/container_backend/errors.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ var (
1111
ErrCannotRemovePausedContainer = errors.New("cannot remove paused container")
1212
ErrCannotRemoveRunningContainer = errors.New("cannot remove running container")
1313
ErrImageUsedByContainer = types.ErrImageUsedByContainer
14+
ErrPruneIsAlreadyRunning = errors.New("a prune operation is already running")
1415
)

pkg/docker/errors.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,14 @@ func IsErrContainerRunning(err error) bool {
3131
}
3232
return strings.HasSuffix(cause.Error(), "container is running: stop the container before removing or force remove")
3333
}
34+
35+
// IsErrPruneRunning returns true if err is "a prune operation is already running" error
36+
// https://github.com/moby/moby/blob/25.0/volume/service/service.go#L212
37+
// https://github.com/moby/moby/blob/25.0/daemon/images/image_prune.go#L38
38+
func IsErrPruneRunning(err error) bool {
39+
if err == nil {
40+
return false
41+
}
42+
cause := errors.Cause(err)
43+
return strings.HasSuffix(cause.Error(), "a prune operation is already running")
44+
}

pkg/docker/errors_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,15 @@ var _ = Describe("docker errors", func() {
3131
Expect(IsErrContainerRunning(err0)).To(BeFalse())
3232
})
3333
})
34+
Describe("IsErrPruneRunning", func() {
35+
It("should return true if err is \"prune running\" error", func() {
36+
causeErr := errors.New("a prune operation is already running")
37+
err0 := errorsPkg.WithMessage(causeErr, "some text")
38+
Expect(IsErrPruneRunning(err0)).To(BeTrue())
39+
})
40+
It("should return false otherwise", func() {
41+
err0 := errors.New("some err")
42+
Expect(IsErrPruneRunning(err0)).To(BeFalse())
43+
})
44+
})
3445
})

pkg/host_cleaning/local_backend_cleaner.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -477,14 +477,15 @@ func (cleaner *LocalBackendCleaner) pruneImages(ctx context.Context, options Run
477477

478478
report, err := cleaner.backend.PruneImages(ctx, prune.Options{Filters: filters})
479479
switch {
480-
case errors.Is(container_backend.ErrImageUsedByContainer, err):
481-
logboek.Context(ctx).Info().LogF("Ignore image pruning: %s\n", err.Error())
480+
case errors.Is(err, container_backend.ErrImageUsedByContainer),
481+
errors.Is(err, container_backend.ErrPruneIsAlreadyRunning):
482+
logboek.Context(ctx).Info().LogF("NOTE: Ignore image pruning: %s\n", err.Error())
482483
return newCleanupReport(), nil
483484
case err != nil:
484485
return newCleanupReport(), err
485-
default:
486-
return mapPruneReportToCleanupReport(report), err
487486
}
487+
488+
return mapPruneReportToCleanupReport(report), err
488489
}
489490

490491
// pruneVolumes removes all anonymous volumes not used by at least one container
@@ -496,6 +497,15 @@ func (cleaner *LocalBackendCleaner) pruneVolumes(ctx context.Context, options Ru
496497
}
497498

498499
report, err := cleaner.backend.PruneVolumes(ctx, prune.Options{})
500+
501+
switch {
502+
case errors.Is(err, container_backend.ErrPruneIsAlreadyRunning):
503+
logboek.Context(ctx).Info().LogF("NOTE: Ignore volume pruning: %s\n", err.Error())
504+
return newCleanupReport(), nil
505+
case err != nil:
506+
return newCleanupReport(), err
507+
}
508+
499509
return mapPruneReportToCleanupReport(report), err
500510
}
501511

pkg/host_cleaning/local_backend_cleaner_test.go

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -121,16 +121,6 @@ var _ = Describe("LocalBackendCleaner", func() {
121121
Expect(err).To(Succeed())
122122
Expect(report).To(Equal(mapImageListToCleanupReport(list)))
123123
})
124-
It("should return err=nil and full report if opts.DryRun=false calling backend.PruneImages() which returns pruneErr=nil", func() {
125-
pruneReport := prune.Report{
126-
ItemsDeleted: []string{"one"},
127-
}
128-
backend.EXPECT().PruneImages(ctx, prune.Options{Filters: filters}).Return(pruneReport, nil)
129-
130-
report, err := cleaner.pruneImages(ctx, RunGCOptions{})
131-
Expect(err).To(Succeed())
132-
Expect(report).To(Equal(mapPruneReportToCleanupReport(pruneReport)))
133-
})
134124
It("should return err=some_err and empty report if opts.DryRun=false calling backend.PruneImages() which returns pruneErr=err", func() {
135125
err0 := errors.New("some_err")
136126
backend.EXPECT().PruneImages(ctx, prune.Options{Filters: filters}).Return(prune.Report{}, err0)
@@ -146,22 +136,58 @@ var _ = Describe("LocalBackendCleaner", func() {
146136
Expect(err).To(Succeed())
147137
Expect(report).To(Equal(newCleanupReport()))
148138
})
139+
It("should return err=nil and empty report if opts.DryRun=false calling backend.PruneImages() which returns pruneErr=ErrPruneIsAlreadyRunning", func() {
140+
backend.EXPECT().PruneImages(ctx, prune.Options{Filters: filters}).Return(prune.Report{}, container_backend.ErrPruneIsAlreadyRunning)
141+
142+
report, err := cleaner.pruneImages(ctx, RunGCOptions{})
143+
Expect(err).To(Succeed())
144+
Expect(report).To(Equal(newCleanupReport()))
145+
})
146+
It("should return err=nil and full report if opts.DryRun=false calling backend.PruneImages() which returns pruneErr=nil", func() {
147+
pruneReport := prune.Report{
148+
ItemsDeleted: []string{"one"},
149+
}
150+
backend.EXPECT().PruneImages(ctx, prune.Options{Filters: filters}).Return(pruneReport, nil)
151+
152+
report, err := cleaner.pruneImages(ctx, RunGCOptions{})
153+
Expect(err).To(Succeed())
154+
Expect(report).To(Equal(mapPruneReportToCleanupReport(pruneReport)))
155+
})
149156
})
150157

151158
Describe("pruneVolumes", func() {
152-
It("should return err if opts.DryRun=true", func() {
153-
_, err := cleaner.pruneVolumes(ctx, RunGCOptions{
159+
It("should return err=errOptionDryRunNotSupported and empty report if opts.DryRun=true", func() {
160+
report, err := cleaner.pruneVolumes(ctx, RunGCOptions{
154161
DryRun: true,
155162
})
156163
Expect(errors.Is(err, errOptionDryRunNotSupported)).To(BeTrue())
164+
Expect(report).To(Equal(newCleanupReport()))
157165
})
158-
It("should call backend.PruneVolumes() if opts.DryRun=false", func() {
159-
backend.EXPECT().PruneVolumes(ctx, prune.Options{}).Return(prune.Report{}, nil)
166+
It("should return err=nil and empty report if opts.DryRun=false calling backend.PruneVolumes() which returns returns pruneErr=ErrPruneIsAlreadyRunning", func() {
167+
backend.EXPECT().PruneVolumes(ctx, prune.Options{}).Return(prune.Report{}, container_backend.ErrPruneIsAlreadyRunning)
160168

161169
report, err := cleaner.pruneVolumes(ctx, RunGCOptions{})
162170
Expect(err).To(Succeed())
163171
Expect(report).To(Equal(newCleanupReport()))
164172
})
173+
It("should return err=some_err and empty report if opts.DryRun=false calling backend.PruneVolumes() which returns returns pruneErr=err", func() {
174+
err0 := errors.New("some_err")
175+
backend.EXPECT().PruneVolumes(ctx, prune.Options{}).Return(prune.Report{}, err0)
176+
177+
report, err := cleaner.pruneVolumes(ctx, RunGCOptions{})
178+
Expect(err).To(Equal(err0))
179+
Expect(report).To(Equal(newCleanupReport()))
180+
})
181+
It("should return err=nil and full report if opts.DryRun=false calling backend.PruneVolumes() which returns pruneErr=nil", func() {
182+
pruneReport := prune.Report{
183+
ItemsDeleted: []string{"one"},
184+
}
185+
backend.EXPECT().PruneVolumes(ctx, prune.Options{}).Return(pruneReport, nil)
186+
187+
report, err := cleaner.pruneVolumes(ctx, RunGCOptions{})
188+
Expect(err).To(Succeed())
189+
Expect(report).To(Equal(mapPruneReportToCleanupReport(pruneReport)))
190+
})
165191
})
166192

167193
Describe("safeCleanupWerfContainers", func() {

0 commit comments

Comments
 (0)