Skip to content

Commit 6f20a64

Browse files
authored
chore: add multi-org flag to develop.sh (#13923)
1 parent f975701 commit 6f20a64

File tree

6 files changed

+268
-32
lines changed

6 files changed

+268
-32
lines changed

codersdk/organizations.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,24 @@ func (c *Client) ProvisionerDaemons(ctx context.Context) ([]ProvisionerDaemon, e
290290
return daemons, json.NewDecoder(res.Body).Decode(&daemons)
291291
}
292292

293+
func (c *Client) OrganizationProvisionerDaemons(ctx context.Context, organizationID uuid.UUID) ([]ProvisionerDaemon, error) {
294+
res, err := c.Request(ctx, http.MethodGet,
295+
fmt.Sprintf("/api/v2/organizations/%s/provisionerdaemons", organizationID.String()),
296+
nil,
297+
)
298+
if err != nil {
299+
return nil, xerrors.Errorf("execute request: %w", err)
300+
}
301+
defer res.Body.Close()
302+
303+
if res.StatusCode != http.StatusOK {
304+
return nil, ReadBodyAsError(res)
305+
}
306+
307+
var daemons []ProvisionerDaemon
308+
return daemons, json.NewDecoder(res.Body).Decode(&daemons)
309+
}
310+
293311
// CreateTemplateVersion processes source-code and optionally associates the version with a template.
294312
// Executing without a template is useful for validating source-code.
295313
func (c *Client) CreateTemplateVersion(ctx context.Context, organizationID uuid.UUID, req CreateTemplateVersionRequest) (TemplateVersion, error) {

docs/cli/provisionerd_start.md

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

enterprise/cli/provisionerdaemons.go

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ package cli
44

55
import (
66
"context"
7+
"errors"
78
"fmt"
9+
"net/http"
810
"os"
911
"regexp"
1012
"time"
@@ -76,6 +78,7 @@ func (r *RootCmd) provisionerDaemonStart() *serpent.Command {
7678
prometheusEnable bool
7779
prometheusAddress string
7880
)
81+
orgContext := agpl.NewOrganizationContext()
7982
client := new(codersdk.Client)
8083
cmd := &serpent.Command{
8184
Use: "start",
@@ -95,6 +98,35 @@ func (r *RootCmd) provisionerDaemonStart() *serpent.Command {
9598
interruptCtx, interruptCancel := inv.SignalNotifyContext(ctx, agpl.InterruptSignals...)
9699
defer interruptCancel()
97100

101+
// This can fail to get the current organization
102+
// if the client is not authenticated as a user,
103+
// like when only PSK is provided.
104+
// This will be cleaner once PSK is replaced
105+
// with org scoped authentication tokens.
106+
org, err := orgContext.Selected(inv, client)
107+
if err != nil {
108+
var cErr *codersdk.Error
109+
if !errors.As(err, &cErr) || cErr.StatusCode() != http.StatusUnauthorized {
110+
return xerrors.Errorf("current organization: %w", err)
111+
}
112+
113+
if preSharedKey == "" {
114+
return xerrors.New("must provide a pre-shared key when not authenticated as a user")
115+
}
116+
117+
org = codersdk.Organization{ID: uuid.Nil}
118+
if orgContext.FlagSelect != "" {
119+
// If we are using PSK, we can't fetch the organization
120+
// to validate org name so we need the user to provide
121+
// a valid organization ID.
122+
orgID, err := uuid.Parse(orgContext.FlagSelect)
123+
if err != nil {
124+
return xerrors.New("must provide an org ID when not authenticated as a user and organization is specified")
125+
}
126+
org = codersdk.Organization{ID: orgID}
127+
}
128+
}
129+
98130
tags, err := agpl.ParseProvisionerTags(rawTags)
99131
if err != nil {
100132
return err
@@ -198,16 +230,16 @@ func (r *RootCmd) provisionerDaemonStart() *serpent.Command {
198230
connector := provisionerd.LocalProvisioners{
199231
string(database.ProvisionerTypeTerraform): proto.NewDRPCProvisionerClient(terraformClient),
200232
}
201-
id := uuid.New()
202233
srv := provisionerd.New(func(ctx context.Context) (provisionerdproto.DRPCProvisionerDaemonClient, error) {
203234
return client.ServeProvisionerDaemon(ctx, codersdk.ServeProvisionerDaemonRequest{
204-
ID: id,
235+
ID: uuid.New(),
205236
Name: name,
206237
Provisioners: []codersdk.ProvisionerType{
207238
codersdk.ProvisionerTypeTerraform,
208239
},
209240
Tags: tags,
210241
PreSharedKey: preSharedKey,
242+
Organization: org.ID,
211243
})
212244
}, &provisionerd.Options{
213245
Logger: logger,
@@ -348,6 +380,7 @@ func (r *RootCmd) provisionerDaemonStart() *serpent.Command {
348380
Default: "127.0.0.1:2112",
349381
},
350382
}
383+
orgContext.AttachOptions(cmd)
351384

352385
return cmd
353386
}

enterprise/cli/provisionerdaemons_test.go

Lines changed: 162 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -27,36 +27,134 @@ import (
2727
func TestProvisionerDaemon_PSK(t *testing.T) {
2828
t.Parallel()
2929

30-
client, _ := coderdenttest.New(t, &coderdenttest.Options{
31-
ProvisionerDaemonPSK: "provisionersftw",
32-
LicenseOptions: &coderdenttest.LicenseOptions{
33-
Features: license.Features{
34-
codersdk.FeatureExternalProvisionerDaemons: 1,
30+
t.Run("OK", func(t *testing.T) {
31+
t.Parallel()
32+
33+
client, _ := coderdenttest.New(t, &coderdenttest.Options{
34+
ProvisionerDaemonPSK: "provisionersftw",
35+
LicenseOptions: &coderdenttest.LicenseOptions{
36+
Features: license.Features{
37+
codersdk.FeatureExternalProvisionerDaemons: 1,
38+
},
3539
},
36-
},
40+
})
41+
inv, conf := newCLI(t, "provisionerd", "start", "--psk=provisionersftw", "--name=matt-daemon")
42+
err := conf.URL().Write(client.URL.String())
43+
require.NoError(t, err)
44+
pty := ptytest.New(t).Attach(inv)
45+
ctx, cancel := context.WithTimeout(inv.Context(), testutil.WaitLong)
46+
defer cancel()
47+
clitest.Start(t, inv)
48+
pty.ExpectNoMatchBefore(ctx, "check entitlement", "starting provisioner daemon")
49+
pty.ExpectMatchContext(ctx, "matt-daemon")
50+
51+
var daemons []codersdk.ProvisionerDaemon
52+
require.Eventually(t, func() bool {
53+
daemons, err = client.ProvisionerDaemons(ctx)
54+
if err != nil {
55+
return false
56+
}
57+
return len(daemons) == 1
58+
}, testutil.WaitShort, testutil.IntervalSlow)
59+
require.Equal(t, "matt-daemon", daemons[0].Name)
60+
require.Equal(t, provisionersdk.ScopeOrganization, daemons[0].Tags[provisionersdk.TagScope])
61+
require.Equal(t, buildinfo.Version(), daemons[0].Version)
62+
require.Equal(t, proto.CurrentVersion.String(), daemons[0].APIVersion)
63+
})
64+
65+
t.Run("AnotherOrg", func(t *testing.T) {
66+
t.Parallel()
67+
client, _ := coderdenttest.New(t, &coderdenttest.Options{
68+
ProvisionerDaemonPSK: "provisionersftw",
69+
LicenseOptions: &coderdenttest.LicenseOptions{
70+
Features: license.Features{
71+
codersdk.FeatureExternalProvisionerDaemons: 1,
72+
},
73+
},
74+
})
75+
anotherOrg := coderdtest.CreateOrganization(t, client, coderdtest.CreateOrganizationOptions{})
76+
inv, conf := newCLI(t, "provisionerd", "start", "--psk=provisionersftw", "--name", "org-daemon", "--org", anotherOrg.ID.String())
77+
err := conf.URL().Write(client.URL.String())
78+
require.NoError(t, err)
79+
pty := ptytest.New(t).Attach(inv)
80+
ctx, cancel := context.WithTimeout(inv.Context(), testutil.WaitLong)
81+
defer cancel()
82+
clitest.Start(t, inv)
83+
pty.ExpectMatchContext(ctx, "starting provisioner daemon")
84+
85+
var daemons []codersdk.ProvisionerDaemon
86+
require.Eventually(t, func() bool {
87+
daemons, err = client.OrganizationProvisionerDaemons(ctx, anotherOrg.ID)
88+
if err != nil {
89+
return false
90+
}
91+
return len(daemons) == 1
92+
}, testutil.WaitShort, testutil.IntervalSlow)
93+
assert.Equal(t, "org-daemon", daemons[0].Name)
94+
assert.Equal(t, provisionersdk.ScopeOrganization, daemons[0].Tags[provisionersdk.TagScope])
95+
assert.Equal(t, buildinfo.Version(), daemons[0].Version)
96+
assert.Equal(t, proto.CurrentVersion.String(), daemons[0].APIVersion)
97+
})
98+
99+
t.Run("AnotherOrgByNameWithUser", func(t *testing.T) {
100+
t.Parallel()
101+
client, _ := coderdenttest.New(t, &coderdenttest.Options{
102+
ProvisionerDaemonPSK: "provisionersftw",
103+
LicenseOptions: &coderdenttest.LicenseOptions{
104+
Features: license.Features{
105+
codersdk.FeatureExternalProvisionerDaemons: 1,
106+
},
107+
},
108+
})
109+
anotherOrg := coderdtest.CreateOrganization(t, client, coderdtest.CreateOrganizationOptions{})
110+
anotherClient, _ := coderdtest.CreateAnotherUser(t, client, anotherOrg.ID, rbac.RoleTemplateAdmin())
111+
inv, conf := newCLI(t, "provisionerd", "start", "--psk=provisionersftw", "--name", "org-daemon", "--org", anotherOrg.Name)
112+
clitest.SetupConfig(t, anotherClient, conf)
113+
pty := ptytest.New(t).Attach(inv)
114+
ctx, cancel := context.WithTimeout(inv.Context(), testutil.WaitLong)
115+
defer cancel()
116+
clitest.Start(t, inv)
117+
pty.ExpectMatchContext(ctx, "starting provisioner daemon")
118+
})
119+
120+
t.Run("AnotherOrgByNameNoUser", func(t *testing.T) {
121+
t.Parallel()
122+
client, _ := coderdenttest.New(t, &coderdenttest.Options{
123+
ProvisionerDaemonPSK: "provisionersftw",
124+
LicenseOptions: &coderdenttest.LicenseOptions{
125+
Features: license.Features{
126+
codersdk.FeatureExternalProvisionerDaemons: 1,
127+
},
128+
},
129+
})
130+
anotherOrg := coderdtest.CreateOrganization(t, client, coderdtest.CreateOrganizationOptions{})
131+
inv, conf := newCLI(t, "provisionerd", "start", "--psk=provisionersftw", "--name", "org-daemon", "--org", anotherOrg.Name)
132+
err := conf.URL().Write(client.URL.String())
133+
require.NoError(t, err)
134+
ctx, cancel := context.WithTimeout(inv.Context(), testutil.WaitLong)
135+
defer cancel()
136+
err = inv.WithContext(ctx).Run()
137+
require.ErrorContains(t, err, "must provide an org ID when not authenticated as a user and organization is specified")
138+
})
139+
140+
t.Run("NoUserNoPSK", func(t *testing.T) {
141+
t.Parallel()
142+
client, _ := coderdenttest.New(t, &coderdenttest.Options{
143+
ProvisionerDaemonPSK: "provisionersftw",
144+
LicenseOptions: &coderdenttest.LicenseOptions{
145+
Features: license.Features{
146+
codersdk.FeatureExternalProvisionerDaemons: 1,
147+
},
148+
},
149+
})
150+
inv, conf := newCLI(t, "provisionerd", "start", "--name", "org-daemon")
151+
err := conf.URL().Write(client.URL.String())
152+
require.NoError(t, err)
153+
ctx, cancel := context.WithTimeout(inv.Context(), testutil.WaitLong)
154+
defer cancel()
155+
err = inv.WithContext(ctx).Run()
156+
require.ErrorContains(t, err, "must provide a pre-shared key when not authenticated as a user")
37157
})
38-
inv, conf := newCLI(t, "provisionerd", "start", "--psk=provisionersftw", "--name=matt-daemon")
39-
err := conf.URL().Write(client.URL.String())
40-
require.NoError(t, err)
41-
pty := ptytest.New(t).Attach(inv)
42-
ctx, cancel := context.WithTimeout(inv.Context(), testutil.WaitLong)
43-
defer cancel()
44-
clitest.Start(t, inv)
45-
pty.ExpectNoMatchBefore(ctx, "check entitlement", "starting provisioner daemon")
46-
pty.ExpectMatchContext(ctx, "matt-daemon")
47-
48-
var daemons []codersdk.ProvisionerDaemon
49-
require.Eventually(t, func() bool {
50-
daemons, err = client.ProvisionerDaemons(ctx)
51-
if err != nil {
52-
return false
53-
}
54-
return len(daemons) == 1
55-
}, testutil.WaitShort, testutil.IntervalSlow)
56-
require.Equal(t, "matt-daemon", daemons[0].Name)
57-
require.Equal(t, provisionersdk.ScopeOrganization, daemons[0].Tags[provisionersdk.TagScope])
58-
require.Equal(t, buildinfo.Version(), daemons[0].Version)
59-
require.Equal(t, proto.CurrentVersion.String(), daemons[0].APIVersion)
60158
}
61159

62160
func TestProvisionerDaemon_SessionToken(t *testing.T) {
@@ -166,6 +264,42 @@ func TestProvisionerDaemon_SessionToken(t *testing.T) {
166264
assert.Equal(t, proto.CurrentVersion.String(), daemons[0].APIVersion)
167265
})
168266

267+
t.Run("ScopeUserAnotherOrg", func(t *testing.T) {
268+
t.Parallel()
269+
client, _ := coderdenttest.New(t, &coderdenttest.Options{
270+
ProvisionerDaemonPSK: "provisionersftw",
271+
LicenseOptions: &coderdenttest.LicenseOptions{
272+
Features: license.Features{
273+
codersdk.FeatureExternalProvisionerDaemons: 1,
274+
},
275+
},
276+
})
277+
anotherOrg := coderdtest.CreateOrganization(t, client, coderdtest.CreateOrganizationOptions{})
278+
anotherClient, anotherUser := coderdtest.CreateAnotherUser(t, client, anotherOrg.ID, rbac.RoleTemplateAdmin())
279+
inv, conf := newCLI(t, "provisionerd", "start", "--tag", "scope=user", "--name", "org-daemon", "--org", anotherOrg.ID.String())
280+
clitest.SetupConfig(t, anotherClient, conf)
281+
pty := ptytest.New(t).Attach(inv)
282+
ctx, cancel := context.WithTimeout(inv.Context(), testutil.WaitLong)
283+
defer cancel()
284+
clitest.Start(t, inv)
285+
pty.ExpectMatchContext(ctx, "starting provisioner daemon")
286+
287+
var daemons []codersdk.ProvisionerDaemon
288+
var err error
289+
require.Eventually(t, func() bool {
290+
daemons, err = client.OrganizationProvisionerDaemons(ctx, anotherOrg.ID)
291+
if err != nil {
292+
return false
293+
}
294+
return len(daemons) == 1
295+
}, testutil.WaitShort, testutil.IntervalSlow)
296+
assert.Equal(t, "org-daemon", daemons[0].Name)
297+
assert.Equal(t, provisionersdk.ScopeUser, daemons[0].Tags[provisionersdk.TagScope])
298+
assert.Equal(t, anotherUser.ID.String(), daemons[0].Tags[provisionersdk.TagOwner])
299+
assert.Equal(t, buildinfo.Version(), daemons[0].Version)
300+
assert.Equal(t, proto.CurrentVersion.String(), daemons[0].APIVersion)
301+
})
302+
169303
t.Run("PrometheusEnabled", func(t *testing.T) {
170304
t.Parallel()
171305

enterprise/cli/testdata/coder_provisionerd_start_--help.golden

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ USAGE:
66
Run a provisioner daemon
77

88
OPTIONS:
9+
-O, --org string, $CODER_ORGANIZATION
10+
Select which organization (uuid or name) to use.
11+
912
-c, --cache-dir string, $CODER_CACHE_DIRECTORY (default: [cache dir])
1013
Directory to store cached data.
1114

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