From b6fafa04a28a35c91b107c7d8d29d4f217fb94ec Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Thu, 16 Nov 2023 13:17:59 +0900 Subject: [PATCH] test: retry containerd initialization (#5597) Signed-off-by: knqyf263 --- pkg/fanal/test/integration/containerd_test.go | 132 +++++++++--------- 1 file changed, 65 insertions(+), 67 deletions(-) diff --git a/pkg/fanal/test/integration/containerd_test.go b/pkg/fanal/test/integration/containerd_test.go index 45339fd9a347..9ca993ec0627 100644 --- a/pkg/fanal/test/integration/containerd_test.go +++ b/pkg/fanal/test/integration/containerd_test.go @@ -6,6 +6,7 @@ import ( "compress/gzip" "context" "encoding/json" + "errors" "fmt" "os" "path/filepath" @@ -14,7 +15,7 @@ import ( "testing" "time" - "github.com/aquasecurity/trivy/pkg/fanal/analyzer" + "github.com/samber/lo" "github.com/containerd/containerd" "github.com/containerd/containerd/images" @@ -23,9 +24,10 @@ import ( v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - testcontainers "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" + "github.com/aquasecurity/trivy/pkg/fanal/analyzer" "github.com/aquasecurity/trivy/pkg/fanal/applier" "github.com/aquasecurity/trivy/pkg/fanal/artifact" aimage "github.com/aquasecurity/trivy/pkg/fanal/artifact/image" @@ -34,13 +36,12 @@ import ( "github.com/aquasecurity/trivy/pkg/fanal/types" ) -func configureTestDataPaths(t *testing.T, namespace string) (string, string) { +func setupContainerd(t *testing.T, ctx context.Context, namespace string) *containerd.Client { t.Helper() - tmpDir, err := os.MkdirTemp("/tmp", "fanal") - require.NoError(t, err) + tmpDir := t.TempDir() containerdDir := filepath.Join(tmpDir, "containerd") - err = os.MkdirAll(containerdDir, os.ModePerm) + err := os.MkdirAll(containerdDir, os.ModePerm) require.NoError(t, err) socketPath := filepath.Join(containerdDir, "containerd.sock") @@ -49,10 +50,29 @@ func configureTestDataPaths(t *testing.T, namespace string) (string, string) { t.Setenv("CONTAINERD_ADDRESS", socketPath) t.Setenv("CONTAINERD_NAMESPACE", namespace) - return tmpDir, socketPath + startContainerd(t, ctx, tmpDir) + + // Retry up to 3 times until containerd is ready + var client *containerd.Client + iteration, _, err := lo.AttemptWhileWithDelay(3, 1*time.Second, func(int, time.Duration) (error, bool) { + client, err = containerd.New(socketPath) + if err != nil { + if !errors.Is(err, os.ErrPermission) { + return err, false // unexpected error + } + return err, true + } + t.Cleanup(func() { + assert.NoError(t, client.Close()) + }) + return nil, false + }) + require.NoErrorf(t, err, "attempted %d times ", iteration) + + return client } -func startContainerd(t *testing.T, ctx context.Context, hostPath string) testcontainers.Container { +func startContainerd(t *testing.T, ctx context.Context, hostPath string) { t.Helper() t.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true") req := testcontainers.ContainerRequest{ @@ -84,18 +104,19 @@ func startContainerd(t *testing.T, ctx context.Context, hostPath string) testcon }) require.NoError(t, err) - return containerdC + t.Cleanup(func() { + assert.NoError(t, containerdC.Terminate(ctx)) + }) } // Each of these tests imports an image and tags it with the name found in the // `imageName` field. Then, the containerd store is searched by the reference // provided in the `searchName` field. func TestContainerd_SearchLocalStoreByNameOrDigest(t *testing.T) { - type testInstance struct { - name string - imageName string - searchName string - expectErr bool + // Each architecture needs different images and test cases. + // Currently only amd64 architecture is supported + if runtime.GOARCH != "amd64" { + t.Skip("'Containerd' test only supports amd64 architecture") } digest := "sha256:f12582b2f2190f350e3904462c1c23aaf366b4f76705e97b199f9bbded1d816a" @@ -103,7 +124,12 @@ func TestContainerd_SearchLocalStoreByNameOrDigest(t *testing.T) { tag := "world" importedImageOriginalName := "ghcr.io/aquasecurity/trivy-test-images:alpine-310" - tests := []testInstance{ + tests := []struct { + name string + imageName string + searchName string + wantErr bool + }{ { name: "familiarName:tag", imageName: fmt.Sprintf("%s:%s", basename, tag), @@ -128,13 +154,13 @@ func TestContainerd_SearchLocalStoreByNameOrDigest(t *testing.T) { name: "other-registry.io/library/name:wrongTag should fail", imageName: fmt.Sprintf("other-registry.io/library/%s:%s", basename, tag), searchName: fmt.Sprintf("other-registry.io/library/%s:badtag", basename), - expectErr: true, + wantErr: true, }, { name: "other-registry.io/library/wrongName:tag should fail", imageName: fmt.Sprintf("other-registry.io/library/%s:%s", basename, tag), searchName: fmt.Sprintf("other-registry.io/library/badname:%s", tag), - expectErr: true, + wantErr: true, }, { name: "digest should succeed", @@ -145,7 +171,7 @@ func TestContainerd_SearchLocalStoreByNameOrDigest(t *testing.T) { name: "wrong digest should fail", imageName: "", searchName: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - expectErr: true, + wantErr: true, }, { name: "name@digest", @@ -171,32 +197,19 @@ func TestContainerd_SearchLocalStoreByNameOrDigest(t *testing.T) { name: "wrongName@digest should fail", imageName: fmt.Sprintf("%s:%s", basename, tag), searchName: fmt.Sprintf("badname@%s", digest), - expectErr: true, + wantErr: true, }, { name: "compound/wrongName@digest should fail", imageName: fmt.Sprintf("compound/%s:%s", basename, tag), searchName: fmt.Sprintf("compound/badname@%s", digest), - expectErr: true, + wantErr: true, }, } - // Each architecture needs different images and test cases. - // Currently only amd64 architecture is supported - if runtime.GOARCH != "amd64" { - t.Skip("'Containerd' test only supports amd64 architecture") - } namespace := "default" ctx := namespaces.WithNamespace(context.Background(), namespace) - tmpDir, socketPath := configureTestDataPaths(t, namespace) - defer os.RemoveAll(tmpDir) - - containerdC := startContainerd(t, ctx, tmpDir) - defer containerdC.Terminate(ctx) - - client, err := containerd.New(socketPath) - require.NoError(t, err) - defer client.Close() + client := setupContainerd(t, ctx, namespace) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -240,7 +253,7 @@ func TestContainerd_SearchLocalStoreByNameOrDigest(t *testing.T) { img, cleanup, err := image.NewContainerImage(ctx, tt.searchName, types.ImageOptions{ImageSources: types.ImageSources{types.ContainerdImageSource}}) defer cleanup() - if tt.expectErr { + if tt.wantErr { require.Error(t, err) return } @@ -272,7 +285,12 @@ func TestContainerd_LocalImage_Alternative_Namespace(t *testing.T) { } func localImageTestWithNamespace(t *testing.T, namespace string) { - t.Helper() + // Each architecture needs different images and test cases. + // Currently only amd64 architecture is supported + if runtime.GOARCH != "amd64" { + t.Skip("'Containerd' test only supports amd64 architecture") + } + tests := []struct { name string imageName string @@ -644,22 +662,10 @@ func localImageTestWithNamespace(t *testing.T, namespace string) { }, }, } - // Each architecture needs different images and test cases. - // Currently only amd64 architecture is supported - if runtime.GOARCH != "amd64" { - t.Skip("'Containerd' test only supports amd64 architecture") - } - ctx := namespaces.WithNamespace(context.Background(), namespace) - - tmpDir, socketPath := configureTestDataPaths(t, namespace) - defer os.RemoveAll(tmpDir) - - containerdC := startContainerd(t, ctx, tmpDir) - defer containerdC.Terminate(ctx) - client, err := containerd.New(socketPath) - require.NoError(t, err) - defer client.Close() + t.Helper() + ctx := namespaces.WithNamespace(context.Background(), namespace) + client := setupContainerd(t, ctx, namespace) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -731,6 +737,12 @@ func localImageTestWithNamespace(t *testing.T, namespace string) { } func TestContainerd_PullImage(t *testing.T) { + // Each architecture needs different images and test cases. + // Currently only amd64 architecture is supported + if runtime.GOARCH != "amd64" { + t.Skip("'Containerd' test only supports amd64 architecture") + } + tests := []struct { name string imageName string @@ -786,23 +798,9 @@ func TestContainerd_PullImage(t *testing.T) { }, } - // Each architecture needs different images and test cases. - // Currently only amd64 architecture is supported - if runtime.GOARCH != "amd64" { - t.Skip("'Containerd' test only supports amd64 architecture") - } - namespace := "default" ctx := namespaces.WithNamespace(context.Background(), namespace) - - tmpDir, socketPath := configureTestDataPaths(t, namespace) - - containerdC := startContainerd(t, ctx, tmpDir) - defer containerdC.Terminate(ctx) - - cli, err := containerd.New(socketPath) - require.NoError(t, err) - defer cli.Close() + client := setupContainerd(t, ctx, namespace) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -815,7 +813,7 @@ func TestContainerd_PullImage(t *testing.T) { c.Close() }() - _, err = cli.Pull(ctx, tt.imageName) + _, err = client.Pull(ctx, tt.imageName) require.NoError(t, err) // Enable only containerd