Skip to content

Commit 60401e6

Browse files
authored
ci: run integration test against mainline and stable versions (#248)
- Adds `scripts/coderversion` to fetch stable and mainline versions - Adds `minVersion` constraint to integration tests - Runs integration tests against both stable and mainline versions - Adds separate integration test for fields introduced in v2.12.0
1 parent e6ba29a commit 60401e6

File tree

7 files changed

+228
-16
lines changed

7 files changed

+228
-16
lines changed

.github/workflows/test.yml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,21 @@ jobs:
3838
run: |
3939
go build -v .
4040
41-
- name: Run integration test
41+
- name: Run integration test (mainline)
4242
timeout-minutes: 10
4343
env:
4444
CODER_IMAGE: "ghcr.io/coder/coder"
45-
CODER_VERSION: "latest"
4645
run: |
47-
go test -v ./integration
46+
source <(go run ./scripts/coderversion)
47+
CODER_VERSION="${CODER_MAINLINE_VERSION}" go test -v ./integration
48+
49+
- name: Run integration test (stable)
50+
timeout-minutes: 10
51+
env:
52+
CODER_IMAGE: "ghcr.io/coder/coder"
53+
run: |
54+
source <(go run ./scripts/coderversion)
55+
CODER_VERSION="${CODER_STABLE_VERSION}" go test -v ./integration
4856
4957
# run acceptance tests in a matrix with Terraform core versions
5058
test:

go.mod

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,17 @@ require (
99
github.com/google/uuid v1.6.0
1010
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320
1111
github.com/hashicorp/terraform-plugin-sdk/v2 v2.20.0
12+
github.com/masterminds/semver v1.5.0
1213
github.com/mitchellh/mapstructure v1.5.0
1314
github.com/robfig/cron/v3 v3.0.1
1415
github.com/stretchr/testify v1.9.0
16+
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8
17+
golang.org/x/mod v0.18.0
1518
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
1619
)
1720

1821
require (
22+
github.com/Masterminds/semver v1.5.0 // indirect
1923
github.com/Microsoft/go-winio v0.5.2 // indirect
2024
github.com/agext/levenshtein v1.2.3 // indirect
2125
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
@@ -76,8 +80,6 @@ require (
7680
go.opentelemetry.io/otel/sdk v1.27.0 // indirect
7781
go.opentelemetry.io/otel/trace v1.27.0 // indirect
7882
golang.org/x/crypto v0.23.0 // indirect
79-
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect
80-
golang.org/x/mod v0.18.0 // indirect
8183
golang.org/x/net v0.25.0 // indirect
8284
golang.org/x/sys v0.20.0 // indirect
8385
golang.org/x/text v0.15.0 // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
22
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
33
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
4+
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
5+
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
46
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
57
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
68
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
@@ -147,6 +149,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
147149
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
148150
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
149151
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
152+
github.com/masterminds/semver v1.5.0 h1:hTxJTTY7tjvnWMrl08O6u3G6BLlKVwxSz01lVac9P8U=
153+
github.com/masterminds/semver v1.5.0/go.mod h1:s7KNT9fnd7edGzwwP7RBX4H0v/CYd5qdOLfkL1V75yg=
150154
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
151155
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
152156
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=

integration/integration_test.go

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,19 @@ func TestIntegration(t *testing.T) {
5757
require.NoError(t, err, "invalid value specified for timeout")
5858
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeoutMins)*time.Minute)
5959
t.Cleanup(cancel)
60+
ctrID := setup(ctx, t, t.Name(), coderImg, coderVersion)
6061

6162
for _, tt := range []struct {
6263
// Name of the folder under `integration/` containing a test template
6364
name string
65+
// Minimum coder version for which to run this test
66+
minVersion string
6467
// map of string to regex to be passed to assertOutput()
6568
expectedOutput map[string]string
6669
}{
6770
{
68-
name: "test-data-source",
71+
name: "test-data-source",
72+
minVersion: "v0.0.0",
6973
expectedOutput: map[string]string{
7074
"provisioner.arch": runtime.GOARCH,
7175
"provisioner.id": `[a-zA-Z0-9-]+`,
@@ -86,6 +90,31 @@ func TestIntegration(t *testing.T) {
8690
"workspace.template_name": `test-data-source`,
8791
"workspace.template_version": `.+`,
8892
"workspace.transition": `start`,
93+
},
94+
},
95+
{
96+
name: "workspace-owner",
97+
minVersion: "v2.12.0",
98+
expectedOutput: map[string]string{
99+
"provisioner.arch": runtime.GOARCH,
100+
"provisioner.id": `[a-zA-Z0-9-]+`,
101+
"provisioner.os": runtime.GOOS,
102+
"workspace.access_port": `\d+`,
103+
"workspace.access_url": `https?://\D+:\d+`,
104+
"workspace.id": `[a-zA-z0-9-]+`,
105+
"workspace.name": ``,
106+
"workspace.owner": `testing`,
107+
"workspace.owner_email": `testing@coder\.com`,
108+
"workspace.owner_groups": `\[\]`,
109+
"workspace.owner_id": `[a-zA-Z0-9]+`,
110+
"workspace.owner_name": `default`,
111+
"workspace.owner_oidc_access_token": `^$`, // TODO: need a test OIDC integration
112+
"workspace.owner_session_token": `[a-zA-Z0-9-]+`,
113+
"workspace.start_count": `1`,
114+
"workspace.template_id": `[a-zA-Z0-9-]+`,
115+
"workspace.template_name": `workspace-owner`,
116+
"workspace.template_version": `.+`,
117+
"workspace.transition": `start`,
89118
"workspace_owner.email": `testing@coder\.com`,
90119
"workspace_owner.full_name": `default`,
91120
"workspace_owner.groups": `\[\]`,
@@ -98,9 +127,13 @@ func TestIntegration(t *testing.T) {
98127
},
99128
},
100129
} {
130+
tt := tt
101131
t.Run(tt.name, func(t *testing.T) {
132+
t.Parallel()
133+
if coderVersion != "latest" && semver.Compare(coderVersion, tt.minVersion) < 0 {
134+
t.Skipf("skipping due to CODER_VERSION %q < minVersion %q", coderVersion, tt.minVersion)
135+
}
102136
// Given: we have an existing Coder deployment running locally
103-
ctrID := setup(ctx, t, tt.name, coderImg, coderVersion)
104137
// Import named template
105138

106139
// NOTE: Template create command was deprecated after this version

integration/test-data-source/main.tf

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,6 @@ locals {
3838
"workspace.template_name" : data.coder_workspace.me.template_name,
3939
"workspace.template_version" : data.coder_workspace.me.template_version,
4040
"workspace.transition" : data.coder_workspace.me.transition,
41-
"workspace_owner.email" : data.coder_workspace_owner.me.email,
42-
"workspace_owner.full_name" : data.coder_workspace_owner.me.full_name,
43-
"workspace_owner.groups" : jsonencode(data.coder_workspace_owner.me.groups),
44-
"workspace_owner.id" : data.coder_workspace_owner.me.id,
45-
"workspace_owner.name" : data.coder_workspace_owner.me.name,
46-
"workspace_owner.oidc_access_token" : data.coder_workspace_owner.me.oidc_access_token,
47-
"workspace_owner.session_token" : data.coder_workspace_owner.me.session_token,
48-
"workspace_owner.ssh_private_key" : data.coder_workspace_owner.me.ssh_private_key,
49-
"workspace_owner.ssh_public_key" : data.coder_workspace_owner.me.ssh_public_key,
5041
}
5142
}
5243

integration/workspace-owner/main.tf

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
terraform {
2+
required_providers {
3+
coder = {
4+
source = "coder/coder"
5+
}
6+
local = {
7+
source = "hashicorp/local"
8+
}
9+
}
10+
}
11+
12+
// TODO: test coder_external_auth and coder_git_auth
13+
// data coder_external_auth "me" {}
14+
// data coder_git_auth "me" {}
15+
data "coder_provisioner" "me" {}
16+
data "coder_workspace" "me" {}
17+
data "coder_workspace_owner" "me" {}
18+
19+
locals {
20+
# NOTE: these must all be strings in the output
21+
output = {
22+
"provisioner.arch" : data.coder_provisioner.me.arch,
23+
"provisioner.id" : data.coder_provisioner.me.id,
24+
"provisioner.os" : data.coder_provisioner.me.os,
25+
"workspace.access_port" : tostring(data.coder_workspace.me.access_port),
26+
"workspace.access_url" : data.coder_workspace.me.access_url,
27+
"workspace.id" : data.coder_workspace.me.id,
28+
"workspace.name" : data.coder_workspace.me.name,
29+
"workspace.owner" : data.coder_workspace.me.owner,
30+
"workspace.owner_email" : data.coder_workspace.me.owner_email,
31+
"workspace.owner_groups" : jsonencode(data.coder_workspace.me.owner_groups),
32+
"workspace.owner_id" : data.coder_workspace.me.owner_id,
33+
"workspace.owner_name" : data.coder_workspace.me.owner_name,
34+
"workspace.owner_oidc_access_token" : data.coder_workspace.me.owner_oidc_access_token,
35+
"workspace.owner_session_token" : data.coder_workspace.me.owner_session_token,
36+
"workspace.start_count" : tostring(data.coder_workspace.me.start_count),
37+
"workspace.template_id" : data.coder_workspace.me.template_id,
38+
"workspace.template_name" : data.coder_workspace.me.template_name,
39+
"workspace.template_version" : data.coder_workspace.me.template_version,
40+
"workspace.transition" : data.coder_workspace.me.transition,
41+
"workspace_owner.email" : data.coder_workspace_owner.me.email,
42+
"workspace_owner.full_name" : data.coder_workspace_owner.me.full_name,
43+
"workspace_owner.groups" : jsonencode(data.coder_workspace_owner.me.groups),
44+
"workspace_owner.id" : data.coder_workspace_owner.me.id,
45+
"workspace_owner.name" : data.coder_workspace_owner.me.name,
46+
"workspace_owner.oidc_access_token" : data.coder_workspace_owner.me.oidc_access_token,
47+
"workspace_owner.session_token" : data.coder_workspace_owner.me.session_token,
48+
"workspace_owner.ssh_private_key" : data.coder_workspace_owner.me.ssh_private_key,
49+
"workspace_owner.ssh_public_key" : data.coder_workspace_owner.me.ssh_public_key,
50+
}
51+
}
52+
53+
variable "output_path" {
54+
type = string
55+
}
56+
57+
resource "local_file" "output" {
58+
filename = var.output_path
59+
content = jsonencode(local.output)
60+
}
61+
62+
output "output" {
63+
value = local.output
64+
sensitive = true
65+
}

scripts/coderversion/main.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
"os"
8+
9+
"github.com/masterminds/semver"
10+
)
11+
12+
func main() {
13+
releases := fetchReleases()
14+
15+
mainlineVer := semver.MustParse("v0.0.0")
16+
for _, rel := range releases {
17+
if rel == "" {
18+
debug("ignoring untagged version %s\n", rel)
19+
continue
20+
}
21+
22+
ver, err := semver.NewVersion(rel)
23+
if err != nil {
24+
debug("skipping invalid version %s\n", rel)
25+
}
26+
27+
if ver.Compare(mainlineVer) > 0 {
28+
mainlineVer = ver
29+
continue
30+
}
31+
}
32+
33+
mainline := fmt.Sprintf("v%d.%d.%d", mainlineVer.Major(), mainlineVer.Minor(), mainlineVer.Patch())
34+
_, _ = fmt.Fprintf(os.Stdout, "CODER_MAINLINE_VERSION=%q\n", mainline)
35+
36+
expectedStableMinor := mainlineVer.Minor() - 1
37+
if expectedStableMinor < 0 {
38+
expectedStableMinor = 0
39+
}
40+
debug("expected stable minor: %d\n", expectedStableMinor)
41+
stableVer := semver.MustParse("v0.0.0")
42+
for _, rel := range releases {
43+
debug("check version %s\n", rel)
44+
if rel == "" {
45+
debug("ignoring untagged version %s\n", rel)
46+
continue
47+
}
48+
49+
ver, err := semver.NewVersion(rel)
50+
if err != nil {
51+
debug("skipping invalid version %s\n", rel)
52+
}
53+
54+
if ver.Minor() != expectedStableMinor {
55+
debug("skipping version %s\n", rel)
56+
continue
57+
}
58+
59+
if ver.Compare(stableVer) > 0 {
60+
stableVer = ver
61+
continue
62+
}
63+
}
64+
65+
stable := fmt.Sprintf("v%d.%d.%d", stableVer.Major(), stableVer.Minor(), stableVer.Patch())
66+
_, _ = fmt.Fprintf(os.Stdout, "CODER_STABLE_VERSION=%q\n", stable)
67+
}
68+
69+
type release struct {
70+
TagName string `json:"tag_name"`
71+
}
72+
73+
const releasesURL = "https://api.github.com/repos/coder/coder/releases"
74+
75+
// fetchReleases fetches the releases of coder/coder
76+
// this is done directly via JSON API to avoid pulling in the entire
77+
// github client
78+
func fetchReleases() []string {
79+
resp, err := http.Get(releasesURL)
80+
if err != nil {
81+
fatal("get releases: %w", err)
82+
}
83+
defer resp.Body.Close()
84+
85+
var releases []release
86+
if err := json.NewDecoder(resp.Body).Decode(&releases); err != nil {
87+
fatal("parse releases: %w", err)
88+
}
89+
90+
var ss []string
91+
for _, rel := range releases {
92+
if rel.TagName != "" {
93+
ss = append(ss, rel.TagName)
94+
95+
}
96+
}
97+
return ss
98+
}
99+
100+
func debug(format string, args ...any) {
101+
if _, ok := os.LookupEnv("VERBOSE"); ok {
102+
_, _ = fmt.Fprintf(os.Stderr, format, args...)
103+
}
104+
}
105+
106+
func fatal(format string, args ...any) {
107+
_, _ = fmt.Fprintf(os.Stderr, format, args...)
108+
os.Exit(1)
109+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy