diff --git a/go.mod b/go.mod index 625aae19..b5e26e56 100644 --- a/go.mod +++ b/go.mod @@ -76,6 +76,8 @@ require ( go.opentelemetry.io/otel/sdk v1.27.0 // indirect go.opentelemetry.io/otel/trace v1.27.0 // indirect golang.org/x/crypto v0.23.0 // indirect + golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect + golang.org/x/mod v0.18.0 // indirect golang.org/x/net v0.25.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect diff --git a/go.sum b/go.sum index d07d5ff3..047612ff 100644 --- a/go.sum +++ b/go.sum @@ -252,9 +252,13 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/integration/integration_test.go b/integration/integration_test.go index cf58b99e..49b36456 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -21,6 +21,8 @@ import ( "github.com/docker/docker/pkg/stdcopy" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "golang.org/x/exp/slices" + "golang.org/x/mod/semver" ) // TestIntegration performs an integration test against an ephemeral Coder deployment. @@ -37,6 +39,16 @@ func TestIntegration(t *testing.T) { t.Skip("Skipping integration tests during tf acceptance tests") } + coderImg := os.Getenv("CODER_IMAGE") + if coderImg == "" { + coderImg = "ghcr.io/coder/coder" + } + + coderVersion := os.Getenv("CODER_VERSION") + if coderVersion == "" { + coderVersion = "latest" + } + timeoutStr := os.Getenv("TIMEOUT_MINS") if timeoutStr == "" { timeoutStr = "10" @@ -88,9 +100,17 @@ func TestIntegration(t *testing.T) { } { t.Run(tt.name, func(t *testing.T) { // Given: we have an existing Coder deployment running locally - ctrID := setup(ctx, t, tt.name) + ctrID := setup(ctx, t, tt.name, coderImg, coderVersion) // Import named template - _, rc := execContainer(ctx, t, ctrID, fmt.Sprintf(`coder templates push %s --directory /src/integration/%s --var output_path=/tmp/%s.json --yes`, tt.name, tt.name, tt.name)) + + // NOTE: Template create command was deprecated after this version + // ref: https://github.com/coder/coder/pull/11390 + templateCreateCmd := "push" + if semver.Compare(coderVersion, "v2.7.0") < 1 { + t.Logf("using now-deprecated templates create command for older coder version") + templateCreateCmd = "create" + } + _, rc := execContainer(ctx, t, ctrID, fmt.Sprintf(`coder templates %s %s --directory /src/integration/%s --var output_path=/tmp/%s.json --yes`, templateCreateCmd, tt.name, tt.name, tt.name)) require.Equal(t, 0, rc) // Create a workspace _, rc = execContainer(ctx, t, ctrID, fmt.Sprintf(`coder create %s -t %s --yes`, tt.name, tt.name)) @@ -105,7 +125,7 @@ func TestIntegration(t *testing.T) { } } -func setup(ctx context.Context, t *testing.T, name string) string { +func setup(ctx context.Context, t *testing.T, name, coderImg, coderVersion string) string { var ( // For this test to work, we pass in a custom terraformrc to use // the locally built version of the provider. @@ -118,16 +138,6 @@ func setup(ctx context.Context, t *testing.T, name string) string { localURL = "http://localhost:3000" ) - coderImg := os.Getenv("CODER_IMAGE") - if coderImg == "" { - coderImg = "ghcr.io/coder/coder" - } - - coderVersion := os.Getenv("CODER_VERSION") - if coderVersion == "" { - coderVersion = "latest" - } - t.Logf("using coder image %s:%s", coderImg, coderVersion) // Ensure the binary is built @@ -151,11 +161,7 @@ func setup(ctx context.Context, t *testing.T, name string) string { // Ensure the image is available locally. refStr := coderImg + ":" + coderVersion - t.Logf("ensuring image %q", refStr) - resp, err := cli.ImagePull(ctx, refStr, image.PullOptions{}) - require.NoError(t, err) - _, err = io.ReadAll(resp) - require.NoError(t, err) + ensureImage(ctx, t, cli, refStr) // Stand up a temporary Coder instance ctr, err := cli.ContainerCreate(ctx, &container.Config{ @@ -213,6 +219,25 @@ func setup(ctx context.Context, t *testing.T, name string) string { return ctr.ID } +func ensureImage(ctx context.Context, t *testing.T, cli *client.Client, ref string) { + t.Helper() + + t.Logf("ensuring image %q", ref) + images, err := cli.ImageList(ctx, image.ListOptions{}) + require.NoError(t, err, "list images") + for _, img := range images { + if slices.Contains(img.RepoTags, ref) { + t.Logf("image %q found locally, not pulling", ref) + return + } + } + t.Logf("image %s not found locally, attempting to pull", ref) + resp, err := cli.ImagePull(ctx, ref, image.PullOptions{}) + require.NoError(t, err) + _, err = io.ReadAll(resp) + require.NoError(t, err) +} + // execContainer executes the given command in the given container and returns // the output and the exit code of the command. func execContainer(ctx context.Context, t *testing.T, containerID, command string) (string, int) { @@ -249,7 +274,7 @@ func assertOutput(t *testing.T, expected, actual map[string]string) { for expectedKey, expectedValExpr := range expected { actualVal := actual[expectedKey] - assert.Regexp(t, expectedValExpr, actualVal) + assert.Regexp(t, expectedValExpr, actualVal, "output key %q does not have expected value", expectedKey) } for actualKey := range actual { _, ok := expected[actualKey]