Merge pull request #7861 from dmcgowan/cleanup-context

Add cleanup package for context management during cleanup
This commit is contained in:
Fu Wei
2023-01-05 13:18:31 +08:00
committed by GitHub
10 changed files with 166 additions and 68 deletions

52
pkg/cleanup/context.go Normal file
View File

@@ -0,0 +1,52 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package providing utilies to help cleanup
package cleanup
import (
"context"
"time"
)
type clearCancel struct {
context.Context
}
func (cc clearCancel) Deadline() (deadline time.Time, ok bool) {
return
}
func (cc clearCancel) Done() <-chan struct{} {
return nil
}
func (cc clearCancel) Err() error {
return nil
}
// Background creates a new context which clears out the parent errors
func Background(ctx context.Context) context.Context {
return clearCancel{ctx}
}
// Do runs the provided function with a context in which the
// errors are cleared out and will timeout after 10 seconds.
func Do(ctx context.Context, do func(context.Context)) {
ctx, cancel := context.WithTimeout(clearCancel{ctx}, 10*time.Second)
do(ctx)
cancel()
}

View File

@@ -0,0 +1,58 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cleanup
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
)
func TestBackground(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
var k struct{}
v := "incontext"
ctx = context.WithValue(ctx, k, v)
assert.Nil(t, contextError(ctx))
assert.Equal(t, ctx.Value(k), v)
cancel()
assert.Error(t, contextError(ctx))
assert.Equal(t, ctx.Value(k), v)
// cleanup context should no longer be canceled
ctx = Background(ctx)
assert.Nil(t, contextError(ctx))
assert.Equal(t, ctx.Value(k), v)
// cleanup contexts can be rewrapped in cancel context
ctx, cancel = context.WithCancel(ctx)
cancel()
assert.Error(t, contextError(ctx))
assert.Equal(t, ctx.Value(k), v)
}
func contextError(ctx context.Context) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
return nil
}
}

View File

@@ -35,6 +35,7 @@ import (
"github.com/containerd/containerd/labels"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/mount"
"github.com/containerd/containerd/pkg/cleanup"
"github.com/containerd/containerd/pkg/kmutex"
"github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/snapshots"
@@ -368,11 +369,11 @@ func (u *Unpacker) unpack(
select {
case <-ctx.Done():
abort(context.Background()) // Cleanup context
cleanup.Do(ctx, abort)
return ctx.Err()
case err := <-fetchErr:
if err != nil {
abort(ctx)
cleanup.Do(ctx, abort)
return err
}
case <-fetchC[i-fetchOffset]:
@@ -380,16 +381,16 @@ func (u *Unpacker) unpack(
diff, err := a.Apply(ctx, desc, mounts, unpack.ApplyOpts...)
if err != nil {
abort(ctx)
cleanup.Do(ctx, abort)
return fmt.Errorf("failed to extract layer %s: %w", diffIDs[i], err)
}
if diff.Digest != diffIDs[i] {
abort(ctx)
cleanup.Do(ctx, abort)
return fmt.Errorf("wrong diff id calculated on extraction %q", diffIDs[i])
}
if err = sn.Commit(ctx, chainID, key, opts...); err != nil {
abort(ctx)
cleanup.Do(ctx, abort)
if errdefs.IsAlreadyExists(err) {
return nil
}