Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitdfeafa8

Browse files
authored
feat: show a warning when an organization has no provisioners (#14136)
1 parentefbd625 commitdfeafa8

File tree

5 files changed

+113
-11
lines changed

5 files changed

+113
-11
lines changed

‎docs/admin/provisioners.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ There are two exceptions:
6767
**Organization-scoped Provisioners** can pick up build jobs created by any user.
6868
These provisioners always have the implicit tags`scope=organization owner=""`.
6969

70+
```shell
71+
coder provisionerd start --org<organization_name>
72+
```
73+
74+
If you omit the`--org` argument, the provisioner will be assigned to the
75+
default organization.
76+
7077
```shell
7178
coder provisionerd start
7279
```

‎site/src/api/api.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,18 @@ class ApiMethods {
627627
returnresponse.data;
628628
};
629629

630+
/**
631+
*@param organization Can be the organization's ID or name
632+
*/
633+
getProvisionerDaemonsByOrganization=async(
634+
organization:string,
635+
):Promise<TypesGen.ProvisionerDaemon[]>=>{
636+
constresponse=awaitthis.axios.get<TypesGen.ProvisionerDaemon[]>(
637+
`/api/v2/organizations/${organization}/provisionerdaemons`,
638+
);
639+
returnresponse.data;
640+
};
641+
630642
getTemplate=async(templateId:string):Promise<TypesGen.Template>=>{
631643
constresponse=awaitthis.axios.get<TypesGen.Template>(
632644
`/api/v2/templates/${templateId}`,

‎site/src/api/queries/organizations.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,16 @@ export const organizations = () => {
107107
queryFn:()=>API.getOrganizations(),
108108
};
109109
};
110+
111+
exportconstgetProvisionerDaemonsKey=(organization:string)=>[
112+
"organization",
113+
organization,
114+
"provisionerDaemons",
115+
];
116+
117+
exportconstprovisionerDaemons=(organization:string)=>{
118+
return{
119+
queryKey:getProvisionerDaemonsKey(organization),
120+
queryFn:()=>API.getProvisionerDaemonsByOrganization(organization),
121+
};
122+
};

‎site/src/pages/CreateTemplatePage/CreateTemplateForm.stories.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import{action}from"@storybook/addon-actions";
22
importtype{Meta,StoryObj}from"@storybook/react";
3+
import{screen,userEvent}from"@storybook/test";
34
import{
5+
getProvisionerDaemonsKey,
6+
organizationsKey,
7+
}from"api/queries/organizations";
8+
import{
9+
MockDefaultOrganization,
10+
MockOrganization2,
411
MockTemplate,
512
MockTemplateExample,
613
MockTemplateVersionVariable1,
@@ -54,6 +61,31 @@ export const StarterTemplateWithOrgPicker: Story = {
5461
},
5562
};
5663

64+
exportconstStarterTemplateWithProvisionerWarning:Story={
65+
parameters:{
66+
queries:[
67+
{
68+
key:organizationsKey,
69+
data:[MockDefaultOrganization,MockOrganization2],
70+
},
71+
{
72+
key:getProvisionerDaemonsKey(MockOrganization2.id),
73+
data:[],
74+
},
75+
],
76+
},
77+
args:{
78+
...StarterTemplate.args,
79+
showOrganizationPicker:true,
80+
},
81+
play:async()=>{
82+
constorganizationPicker=screen.getByPlaceholderText("Organization name");
83+
awaituserEvent.click(organizationPicker);
84+
constorg2=awaitscreen.findByText(MockOrganization2.display_name);
85+
awaituserEvent.click(org2);
86+
},
87+
};
88+
5789
exportconstDuplicateTemplateWithVariables:Story={
5890
args:{
5991
copiedTemplate:MockTemplate,

‎site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
importLinkfrom"@mui/material/Link";
12
importTextFieldfrom"@mui/material/TextField";
23
import{useFormik}from"formik";
34
importcamelCasefrom"lodash/camelCase";
45
importcapitalizefrom"lodash/capitalize";
56
import{useState,typeFC}from"react";
7+
import{useQuery}from"react-query";
68
import{useSearchParams}from"react-router-dom";
79
import*asYupfrom"yup";
10+
import{provisionerDaemons}from"api/queries/organizations";
811
importtype{
912
Organization,
1013
ProvisionerJobLog,
@@ -14,6 +17,7 @@ import type {
1417
TemplateVersionVariable,
1518
VariableValue,
1619
}from"api/typesGenerated";
20+
import{Alert}from"components/Alert/Alert";
1721
import{
1822
HorizontalForm,
1923
FormSection,
@@ -23,6 +27,7 @@ import {
2327
import{IconField}from"components/IconField/IconField";
2428
import{OrganizationAutocomplete}from"components/OrganizationAutocomplete/OrganizationAutocomplete";
2529
import{SelectedTemplate}from"pages/CreateWorkspacePage/SelectedTemplate";
30+
import{docs}from"utils/docs";
2631
import{
2732
nameValidator,
2833
getFormHelpers,
@@ -210,6 +215,24 @@ export const CreateTemplateForm: FC<CreateTemplateFormProps> = (props) => {
210215
});
211216
constgetFieldHelpers=getFormHelpers<CreateTemplateFormData>(form,error);
212217

218+
constprovisionerDaemonsQuery=useQuery(
219+
selectedOrg
220+
?{
221+
...provisionerDaemons(selectedOrg.id),
222+
enabled:showOrganizationPicker,
223+
select:(provisioners)=>provisioners.length<1,
224+
}
225+
:{enabled:false},
226+
);
227+
228+
// TODO: Ideally, we would have a backend endpoint that could notify the
229+
// frontend that a provisioner has been connected, so that we could hide
230+
// this warning. In the meantime, **do not use this variable to disable
231+
// form submission**!! A user could easily see this warning, connect a
232+
// provisioner, and then not refresh the page. Even if they submit without
233+
// a provisioner, it'll just sit in the job queue until they connect one.
234+
constshowProvisionerWarning=provisionerDaemonsQuery.data;
235+
213236
return(
214237
<HorizontalFormonSubmit={form.handleSubmit}>
215238
{/* General info */}
@@ -232,17 +255,20 @@ export const CreateTemplateForm: FC<CreateTemplateFormProps> = (props) => {
232255
)}
233256

234257
{showOrganizationPicker&&(
235-
<OrganizationAutocomplete
236-
{...getFieldHelpers("organization")}
237-
required
238-
label="Belongs to"
239-
value={selectedOrg}
240-
onChange={(newValue)=>{
241-
setSelectedOrg(newValue);
242-
voidform.setFieldValue("organization",newValue?.name||"");
243-
}}
244-
size="medium"
245-
/>
258+
<>
259+
{showProvisionerWarning&&<ProvisionerWarning/>}
260+
<OrganizationAutocomplete
261+
{...getFieldHelpers("organization")}
262+
required
263+
label="Belongs to"
264+
value={selectedOrg}
265+
onChange={(newValue)=>{
266+
setSelectedOrg(newValue);
267+
voidform.setFieldValue("organization",newValue?.name||"");
268+
}}
269+
size="medium"
270+
/>
271+
</>
246272
)}
247273

248274
{"copiedTemplate"inprops&&(
@@ -369,3 +395,15 @@ const fillNameAndDisplayWithFilename = async (
369395
form.setFieldValue("display_name",capitalize(name)),
370396
]);
371397
};
398+
399+
constProvisionerWarning:FC=()=>{
400+
return(
401+
<Alertseverity="warning"css={{marginBottom:16}}>
402+
This organization does not have any provisioners. Before you create a
403+
template, you&apos;ll need to configure a provisioner.{" "}
404+
<Linkhref={docs("/admin/provisioners#organization-scoped-provisioners")}>
405+
See our documentation.
406+
</Link>
407+
</Alert>
408+
);
409+
};

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp