Skip to content

Commit 48fc97a

Browse files
committed
Add support for standalone provisioner daemons with token authentication
1 parent 97052e0 commit 48fc97a

20 files changed

+384
-30
lines changed

cli/provisionercreate.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package cli
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/coder/coder/codersdk"
7+
"github.com/spf13/cobra"
8+
"golang.org/x/xerrors"
9+
)
10+
11+
func provisionerCreate() *cobra.Command {
12+
root := &cobra.Command{
13+
Use: "create [name]",
14+
Short: "Create a provisioner daemon instance",
15+
Args: cobra.ExactArgs(1),
16+
RunE: func(cmd *cobra.Command, args []string) error {
17+
client, err := createClient(cmd)
18+
if err != nil {
19+
return err
20+
}
21+
22+
provisionerName := args[0]
23+
24+
provisionerDaemon, err := client.CreateProvisionerDaemon(cmd.Context(), codersdk.CreateProvisionerDaemonRequest{
25+
Name: provisionerName,
26+
})
27+
if err != nil {
28+
return err
29+
}
30+
31+
if provisionerDaemon.AuthToken == nil {
32+
return xerrors.New("provisioner daemon was created without an auth token")
33+
}
34+
tokenArg := provisionerDaemon.AuthToken.String()
35+
36+
_, _ = fmt.Fprintln(cmd.ErrOrStderr(), `A new provisioner daemon has been registered.
37+
38+
Start the provisioner daemon with the following command:
39+
40+
coder provisioners run --token `+tokenArg)
41+
42+
return nil
43+
},
44+
}
45+
return root
46+
}

cli/provisionerrun.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package cli
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/signal"
7+
"path/filepath"
8+
9+
"cdr.dev/slog"
10+
"cdr.dev/slog/sloggers/sloghuman"
11+
"github.com/coder/coder/cli/cliflag"
12+
"github.com/coder/coder/cli/cliui"
13+
"github.com/spf13/cobra"
14+
"golang.org/x/xerrors"
15+
)
16+
17+
func provisionerRun() *cobra.Command {
18+
var (
19+
cacheDir string
20+
verbose bool
21+
)
22+
root := &cobra.Command{
23+
Use: "run",
24+
Short: "Run a standalone Coder provisioner",
25+
RunE: func(cmd *cobra.Command, args []string) error {
26+
ctx := cmd.Context()
27+
notifyCtx, notifyStop := signal.NotifyContext(ctx, interruptSignals...)
28+
defer notifyStop()
29+
30+
logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr()))
31+
if verbose {
32+
logger = logger.Leveled(slog.LevelDebug)
33+
}
34+
35+
client, err := createClient(cmd)
36+
if err != nil {
37+
return err
38+
}
39+
40+
errCh := make(chan error, 1)
41+
provisionerDaemon, err := newProvisionerDaemon(ctx, client.ListenProvisionerDaemon, logger, cacheDir, errCh, false)
42+
if err != nil {
43+
return xerrors.Errorf("create provisioner daemon: %w", err)
44+
}
45+
46+
var exitErr error
47+
select {
48+
case <-notifyCtx.Done():
49+
exitErr = notifyCtx.Err()
50+
_, _ = fmt.Fprintln(cmd.OutOrStdout(), cliui.Styles.Bold.Render(
51+
"Interrupt caught, gracefully exiting. Use ctrl+\\ to force quit",
52+
))
53+
case exitErr = <-errCh:
54+
}
55+
56+
err = provisionerDaemon.Close()
57+
if err != nil {
58+
cmd.PrintErrf("Close provisioner daemon: %s\n", err)
59+
return err
60+
}
61+
62+
return exitErr
63+
},
64+
}
65+
defaultCacheDir := filepath.Join(os.TempDir(), "coder-cache")
66+
if dir := os.Getenv("CACHE_DIRECTORY"); dir != "" {
67+
// For compatibility with systemd.
68+
defaultCacheDir = dir
69+
}
70+
cliflag.StringVarP(root.Flags(), &cacheDir, "cache-dir", "", "CODER_CACHE_DIRECTORY", defaultCacheDir, "Specifies a directory to cache binaries for provision operations. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd.")
71+
cliflag.BoolVarP(root.Flags(), &verbose, "verbose", "v", "CODER_VERBOSE", false, "Enables verbose logging.")
72+
return root
73+
}

cli/provisioners.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package cli
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
)
6+
7+
func provisioners() *cobra.Command {
8+
cmd := &cobra.Command{
9+
Use: "provisioners",
10+
Short: "Create, manage and run standalone provisioner daemons",
11+
Aliases: []string{"provisioner"},
12+
}
13+
cmd.AddCommand(
14+
provisionerRun(),
15+
provisionerCreate(),
16+
)
17+
18+
return cmd
19+
}

cli/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ func Root() *cobra.Command {
120120
logout(),
121121
parameters(),
122122
portForward(),
123+
provisioners(),
123124
publickey(),
124125
resetPassword(),
125126
schedules(),

cli/server.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ func server() *cobra.Command {
463463
}
464464
}()
465465
for i := 0; uint8(i) < provisionerDaemonCount; i++ {
466-
daemon, err := newProvisionerDaemon(ctx, coderAPI, logger, cacheDir, errCh, false)
466+
daemon, err := newProvisionerDaemon(ctx, coderAPI.ListenProvisionerDaemon, logger, cacheDir, errCh, false)
467467
if err != nil {
468468
return xerrors.Errorf("create provisioner daemon: %w", err)
469469
}
@@ -805,7 +805,7 @@ func shutdownWithTimeout(s interface{ Shutdown(context.Context) error }, timeout
805805
}
806806

807807
// nolint:revive
808-
func newProvisionerDaemon(ctx context.Context, coderAPI *coderd.API,
808+
func newProvisionerDaemon(ctx context.Context, dialer provisionerd.Dialer,
809809
logger slog.Logger, cacheDir string, errCh chan error, dev bool,
810810
) (srv *provisionerd.Server, err error) {
811811
ctx, cancel := context.WithCancel(ctx)
@@ -873,7 +873,7 @@ func newProvisionerDaemon(ctx context.Context, coderAPI *coderd.API,
873873
}()
874874
provisioners[string(database.ProvisionerTypeEcho)] = proto.NewDRPCProvisionerClient(provisionersdk.Conn(echoClient))
875875
}
876-
return provisionerd.New(coderAPI.ListenProvisionerDaemon, &provisionerd.Options{
876+
return provisionerd.New(dialer, &provisionerd.Options{
877877
Logger: logger,
878878
PollInterval: 500 * time.Millisecond,
879879
UpdateInterval: 500 * time.Millisecond,

coderd/coderd.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,13 @@ func New(options *Options) *API {
178178
r.Post("/", api.postFile)
179179
})
180180
r.Route("/provisionerdaemons", func(r chi.Router) {
181-
r.Use(
182-
apiKeyMiddleware,
183-
)
184-
r.Get("/", api.provisionerDaemons)
181+
r.Group(func(r chi.Router) {
182+
r.Use(apiKeyMiddleware)
183+
r.Get("/", api.provisionerDaemons)
184+
r.Post("/", api.postProvisionerDaemon)
185+
})
185186
r.Route("/me", func(r chi.Router) {
187+
r.Use(httpmw.ExtractProvisionerDaemon(options.Database))
186188
r.Get("/listen", api.provisionerDaemonsListen)
187189
})
188190
})

coderd/database/databasefake/databasefake.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,6 +1306,26 @@ func (q *fakeQuerier) GetProvisionerDaemonByID(_ context.Context, id uuid.UUID)
13061306
return database.ProvisionerDaemon{}, sql.ErrNoRows
13071307
}
13081308

1309+
func (q *fakeQuerier) GetProvisionerDaemonByAuthToken(_ context.Context, token uuid.NullUUID) (database.ProvisionerDaemon, error) {
1310+
if !token.Valid {
1311+
return database.ProvisionerDaemon{}, sql.ErrNoRows
1312+
}
1313+
1314+
q.mutex.RLock()
1315+
defer q.mutex.RUnlock()
1316+
1317+
for _, provisionerDaemon := range q.provisionerDaemons {
1318+
if !provisionerDaemon.AuthToken.Valid {
1319+
continue
1320+
}
1321+
if provisionerDaemon.AuthToken.UUID.String() != token.UUID.String() {
1322+
continue
1323+
}
1324+
return provisionerDaemon, nil
1325+
}
1326+
return database.ProvisionerDaemon{}, sql.ErrNoRows
1327+
}
1328+
13091329
func (q *fakeQuerier) GetProvisionerJobByID(_ context.Context, id uuid.UUID) (database.ProvisionerJob, error) {
13101330
q.mutex.RLock()
13111331
defer q.mutex.RUnlock()
@@ -1663,6 +1683,7 @@ func (q *fakeQuerier) InsertProvisionerDaemon(_ context.Context, arg database.In
16631683
CreatedAt: arg.CreatedAt,
16641684
Name: arg.Name,
16651685
Provisioners: arg.Provisioners,
1686+
AuthToken: arg.AuthToken,
16661687
}
16671688
q.provisionerDaemons = append(q.provisionerDaemons, daemon)
16681689
return daemon, nil

coderd/database/dump.sql

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ALTER TABLE provisioner_daemons DROP COLUMN IF EXISTS auth_token;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ALTER TABLE provisioner_daemons ADD COLUMN IF NOT EXISTS auth_token uuid;

coderd/database/models.go

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

coderd/database/querier.go

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

coderd/database/queries.sql.go

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

coderd/database/queries/provisionerdaemons.sql

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ INSERT INTO
1818
id,
1919
created_at,
2020
"name",
21-
provisioners
21+
provisioners,
22+
auth_token
2223
)
2324
VALUES
24-
($1, $2, $3, $4) RETURNING *;
25+
($1, $2, $3, $4, $5) RETURNING *;
2526

2627
-- name: UpdateProvisionerDaemonByID :exec
2728
UPDATE
@@ -31,3 +32,11 @@ SET
3132
provisioners = $3
3233
WHERE
3334
id = $1;
35+
36+
-- name: GetProvisionerDaemonByAuthToken :one
37+
SELECT
38+
*
39+
FROM
40+
provisioner_daemons
41+
WHERE
42+
auth_token = $1;

0 commit comments

Comments
 (0)