Skip to content

Commit 7959543

Browse files
committed
Merge branch 'main' of https://github.com/coder/coder into bq/enable-options-to-dormant
2 parents 0b99e06 + 879c61c commit 7959543

22 files changed

+231
-101
lines changed

.github/workflows/pr-deploy.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ jobs:
253253
run: |
254254
set -euo pipefail
255255
mkdir -p ~/.kube
256-
echo "${{ secrets.PR_DEPLOYMENTS_KUBECONFIG }}" > ~/.kube/config
256+
echo "${{ secrets.PR_DEPLOYMENTS_KUBECONFIG_BASE64 }}" | base64 --decode > ~/.kube/config
257257
chmod 644 ~/.kube/config
258258
export KUBECONFIG=~/.kube/config
259259

cli/templatelist.go

+2-8
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
)
1212

1313
func (r *RootCmd) templateList() *serpent.Command {
14-
orgContext := NewOrganizationContext()
1514
formatter := cliui.NewOutputFormatter(
1615
cliui.TableFormat([]templateTableRow{}, []string{"name", "organization name", "last updated", "used by"}),
1716
cliui.JSONFormat(),
@@ -26,17 +25,13 @@ func (r *RootCmd) templateList() *serpent.Command {
2625
r.InitClient(client),
2726
),
2827
Handler: func(inv *serpent.Invocation) error {
29-
organization, err := orgContext.Selected(inv, client)
30-
if err != nil {
31-
return err
32-
}
33-
templates, err := client.TemplatesByOrganization(inv.Context(), organization.ID)
28+
templates, err := client.Templates(inv.Context(), codersdk.TemplateFilter{})
3429
if err != nil {
3530
return err
3631
}
3732

3833
if len(templates) == 0 {
39-
_, _ = fmt.Fprintf(inv.Stderr, "%s No templates found in %s! Create one:\n\n", Caret, color.HiWhiteString(organization.Name))
34+
_, _ = fmt.Fprintf(inv.Stderr, "%s No templates found! Create one:\n\n", Caret)
4035
_, _ = fmt.Fprintln(inv.Stderr, color.HiMagentaString(" $ coder templates push <directory>\n"))
4136
return nil
4237
}
@@ -53,6 +48,5 @@ func (r *RootCmd) templateList() *serpent.Command {
5348
}
5449

5550
formatter.AttachOptions(&cmd.Options)
56-
orgContext.AttachOptions(cmd)
5751
return cmd
5852
}

cli/templatelist_test.go

+36-5
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,6 @@ func TestTemplateList(t *testing.T) {
8888
client := coderdtest.New(t, &coderdtest.Options{})
8989
owner := coderdtest.CreateFirstUser(t, client)
9090

91-
org, err := client.Organization(context.Background(), owner.OrganizationID)
92-
require.NoError(t, err)
93-
9491
templateAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleTemplateAdmin())
9592

9693
inv, root := clitest.New(t, "templates", "list")
@@ -110,8 +107,42 @@ func TestTemplateList(t *testing.T) {
110107

111108
require.NoError(t, <-errC)
112109

113-
pty.ExpectMatch("No templates found in")
114-
pty.ExpectMatch(org.Name)
110+
pty.ExpectMatch("No templates found")
115111
pty.ExpectMatch("Create one:")
116112
})
113+
114+
t.Run("MultiOrg", func(t *testing.T) {
115+
t.Parallel()
116+
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
117+
owner := coderdtest.CreateFirstUser(t, client)
118+
119+
// Template in the first organization
120+
firstVersion := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil)
121+
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, firstVersion.ID)
122+
_ = coderdtest.CreateTemplate(t, client, owner.OrganizationID, firstVersion.ID)
123+
124+
secondOrg := coderdtest.CreateOrganization(t, client, coderdtest.CreateOrganizationOptions{
125+
IncludeProvisionerDaemon: true,
126+
})
127+
secondVersion := coderdtest.CreateTemplateVersion(t, client, secondOrg.ID, nil)
128+
_ = coderdtest.CreateTemplate(t, client, secondOrg.ID, secondVersion.ID)
129+
130+
// Create a site wide template admin
131+
templateAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleTemplateAdmin())
132+
133+
inv, root := clitest.New(t, "templates", "list", "--output=json")
134+
clitest.SetupConfig(t, templateAdmin, root)
135+
136+
ctx, cancelFunc := context.WithTimeout(context.Background(), testutil.WaitLong)
137+
defer cancelFunc()
138+
139+
out := bytes.NewBuffer(nil)
140+
inv.Stdout = out
141+
err := inv.WithContext(ctx).Run()
142+
require.NoError(t, err)
143+
144+
var templates []codersdk.Template
145+
require.NoError(t, json.Unmarshal(out.Bytes(), &templates))
146+
require.Len(t, templates, 2)
147+
})
117148
}

cli/templates.go

-4
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@ func (r *RootCmd) templates() *serpent.Command {
1717
Use: "templates",
1818
Short: "Manage templates",
1919
Long: "Templates are written in standard Terraform and describe the infrastructure for workspaces\n" + FormatExamples(
20-
Example{
21-
Description: "Make changes to your template, and plan the changes",
22-
Command: "coder templates plan my-template",
23-
},
2420
Example{
2521
Description: "Create or push an update to the template. Your developers can update their workspaces",
2622
Command: "coder templates push my-template",

cli/testdata/coder_templates_--help.golden

-4
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@ USAGE:
99

1010
Templates are written in standard Terraform and describe the infrastructure
1111
for workspaces
12-
- Make changes to your template, and plan the changes:
13-
14-
$ coder templates plan my-template
15-
1612
- Create or push an update to the template. Your developers can update their
1713
workspaces:
1814

cli/testdata/coder_templates_list_--help.golden

-3
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@ USAGE:
88
Aliases: ls
99

1010
OPTIONS:
11-
-O, --org string, $CODER_ORGANIZATION
12-
Select which organization (uuid or name) to use.
13-
1411
-c, --column string-array (default: name,organization name,last updated,used by)
1512
Columns to display in table output. Available columns: name, created
1613
at, last updated, organization id, organization name, provisioner,

docs/cli/templates.md

-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/cli/templates_list.md

-9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/workspaces.md

+2-4
Original file line numberDiff line numberDiff line change
@@ -151,13 +151,11 @@ manually updated the workspace.
151151
## Updating workspaces
152152

153153
After updating the default version of the template that a workspace was created
154-
from, you can update the workspace.
154+
from, you can update the workspace. Coder will start the workspace with said
155+
version.
155156

156157
![Updating a workspace](./images/workspace-update.png)
157158

158-
If the workspace is running, Coder stops it, updates it, then starts the
159-
workspace again.
160-
161159
On the command line:
162160

163161
```shell

site/e2e/parameters.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { RichParameter } from "./provisionerGenerated";
22

33
// Rich parameters
44

5-
const emptyParameter: RichParameter = {
5+
export const emptyParameter: RichParameter = {
66
name: "",
77
description: "",
88
type: "",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { test, expect } from "@playwright/test";
2+
import { username } from "../../constants";
3+
import {
4+
createTemplate,
5+
createWorkspace,
6+
echoResponsesWithParameters,
7+
} from "../../helpers";
8+
import { emptyParameter } from "../../parameters";
9+
import type { RichParameter } from "../../provisionerGenerated";
10+
11+
test("create workspace in auto mode", async ({ page }) => {
12+
const richParameters: RichParameter[] = [
13+
{ ...emptyParameter, name: "repo", type: "string" },
14+
];
15+
const template = await createTemplate(
16+
page,
17+
echoResponsesWithParameters(richParameters),
18+
);
19+
const name = "test-workspace";
20+
await page.goto(
21+
`/templates/${template}/workspace?mode=auto&param.repo=example&name=${name}`,
22+
{
23+
waitUntil: "domcontentloaded",
24+
},
25+
);
26+
await expect(page).toHaveTitle(`${username}/${name} - Coder`);
27+
});
28+
29+
test("use an existing workspace that matches the `match` parameter instead of creating a new one", async ({
30+
page,
31+
}) => {
32+
const richParameters: RichParameter[] = [
33+
{ ...emptyParameter, name: "repo", type: "string" },
34+
];
35+
const template = await createTemplate(
36+
page,
37+
echoResponsesWithParameters(richParameters),
38+
);
39+
const prevWorkspace = await createWorkspace(page, template);
40+
await page.goto(
41+
`/templates/${template}/workspace?mode=auto&param.repo=example&name=new-name&match=name:${prevWorkspace}`,
42+
{
43+
waitUntil: "domcontentloaded",
44+
},
45+
);
46+
await expect(page).toHaveTitle(`${username}/${prevWorkspace} - Coder`);
47+
});
48+
49+
test("show error if `match` parameter is invalid", async ({ page }) => {
50+
const richParameters: RichParameter[] = [
51+
{ ...emptyParameter, name: "repo", type: "string" },
52+
];
53+
const template = await createTemplate(
54+
page,
55+
echoResponsesWithParameters(richParameters),
56+
);
57+
const prevWorkspace = await createWorkspace(page, template);
58+
await page.goto(
59+
`/templates/${template}/workspace?mode=auto&param.repo=example&name=new-name&match=not-valid-query:${prevWorkspace}`,
60+
{
61+
waitUntil: "domcontentloaded",
62+
},
63+
);
64+
await expect(page.getByText("Invalid match value")).toBeVisible();
65+
});

site/e2e/tests/createWorkspace.spec.ts renamed to site/e2e/tests/workspaces/createWorkspace.spec.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import {
77
openTerminalWindow,
88
requireTerraformProvisioner,
99
verifyParameters,
10-
} from "../helpers";
11-
import { beforeCoderTest } from "../hooks";
10+
} from "../../helpers";
11+
import { beforeCoderTest } from "../../hooks";
1212
import {
1313
secondParameter,
1414
fourthParameter,
@@ -18,8 +18,8 @@ import {
1818
seventhParameter,
1919
sixthParameter,
2020
randParamName,
21-
} from "../parameters";
22-
import type { RichParameter } from "../provisionerGenerated";
21+
} from "../../parameters";
22+
import type { RichParameter } from "../../provisionerGenerated";
2323

2424
test.beforeEach(({ page }) => beforeCoderTest(page));
2525

site/e2e/tests/restartWorkspace.spec.ts renamed to site/e2e/tests/workspaces/restartWorkspace.spec.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import {
55
createWorkspace,
66
echoResponsesWithParameters,
77
verifyParameters,
8-
} from "../helpers";
9-
import { beforeCoderTest } from "../hooks";
10-
import { firstBuildOption, secondBuildOption } from "../parameters";
11-
import type { RichParameter } from "../provisionerGenerated";
8+
} from "../../helpers";
9+
import { beforeCoderTest } from "../../hooks";
10+
import { firstBuildOption, secondBuildOption } from "../../parameters";
11+
import type { RichParameter } from "../../provisionerGenerated";
1212

1313
test.beforeEach(({ page }) => beforeCoderTest(page));
1414

site/e2e/tests/startWorkspace.spec.ts renamed to site/e2e/tests/workspaces/startWorkspace.spec.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import {
66
echoResponsesWithParameters,
77
stopWorkspace,
88
verifyParameters,
9-
} from "../helpers";
10-
import { beforeCoderTest } from "../hooks";
11-
import { firstBuildOption, secondBuildOption } from "../parameters";
12-
import type { RichParameter } from "../provisionerGenerated";
9+
} from "../../helpers";
10+
import { beforeCoderTest } from "../../hooks";
11+
import { firstBuildOption, secondBuildOption } from "../../parameters";
12+
import type { RichParameter } from "../../provisionerGenerated";
1313

1414
test.beforeEach(({ page }) => beforeCoderTest(page));
1515

site/e2e/tests/updateWorkspace.spec.ts renamed to site/e2e/tests/workspaces/updateWorkspace.spec.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@ import {
77
updateWorkspace,
88
updateWorkspaceParameters,
99
verifyParameters,
10-
} from "../helpers";
11-
import { beforeCoderTest } from "../hooks";
10+
} from "../../helpers";
11+
import { beforeCoderTest } from "../../hooks";
1212
import {
1313
fifthParameter,
1414
firstParameter,
1515
secondParameter,
1616
sixthParameter,
1717
secondBuildOption,
18-
} from "../parameters";
19-
import type { RichParameter } from "../provisionerGenerated";
18+
} from "../../parameters";
19+
import type { RichParameter } from "../../provisionerGenerated";
2020

2121
test.beforeEach(({ page }) => beforeCoderTest(page));
2222

site/src/api/errors.ts

+13
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ export const getValidationErrorMessage = (error: unknown): string => {
111111
};
112112

113113
export const getErrorDetail = (error: unknown): string | undefined => {
114+
if (error instanceof DetailedError) {
115+
return error.detail;
116+
}
117+
114118
if (error instanceof Error) {
115119
return "Please check the developer console for more details.";
116120
}
@@ -125,3 +129,12 @@ export const getErrorDetail = (error: unknown): string | undefined => {
125129

126130
return undefined;
127131
};
132+
133+
export class DetailedError extends Error {
134+
constructor(
135+
message: string,
136+
public detail?: string,
137+
) {
138+
super(message);
139+
}
140+
}

0 commit comments

Comments
 (0)