Skip to content

Commit 632f5a5

Browse files
committed
chore: move provisioner keys commands into slim build
1 parent b3a3671 commit 632f5a5

File tree

4 files changed

+374
-371
lines changed

4 files changed

+374
-371
lines changed

enterprise/cli/provisionerdaemons.go

Lines changed: 1 addition & 367 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,6 @@
1-
//go:build !slim
2-
31
package cli
42

5-
import (
6-
"context"
7-
"errors"
8-
"fmt"
9-
"net/http"
10-
"os"
11-
"regexp"
12-
"time"
13-
14-
"github.com/google/uuid"
15-
"github.com/prometheus/client_golang/prometheus"
16-
"github.com/prometheus/client_golang/prometheus/collectors"
17-
"github.com/prometheus/client_golang/prometheus/promhttp"
18-
"golang.org/x/xerrors"
19-
20-
"cdr.dev/slog"
21-
"cdr.dev/slog/sloggers/sloghuman"
22-
agpl "github.com/coder/coder/v2/cli"
23-
"github.com/coder/coder/v2/cli/clilog"
24-
"github.com/coder/coder/v2/cli/cliui"
25-
"github.com/coder/coder/v2/cli/cliutil"
26-
"github.com/coder/coder/v2/coderd/database"
27-
"github.com/coder/coder/v2/codersdk"
28-
"github.com/coder/coder/v2/codersdk/drpc"
29-
"github.com/coder/coder/v2/provisioner/terraform"
30-
"github.com/coder/coder/v2/provisionerd"
31-
provisionerdproto "github.com/coder/coder/v2/provisionerd/proto"
32-
"github.com/coder/coder/v2/provisionersdk"
33-
"github.com/coder/coder/v2/provisionersdk/proto"
34-
"github.com/coder/serpent"
35-
)
3+
import "github.com/coder/serpent"
364

375
func (r *RootCmd) provisionerDaemons() *serpent.Command {
386
cmd := &serpent.Command{
@@ -50,337 +18,3 @@ func (r *RootCmd) provisionerDaemons() *serpent.Command {
5018

5119
return cmd
5220
}
53-
54-
func validateProvisionerDaemonName(name string) error {
55-
if len(name) > 64 {
56-
return xerrors.Errorf("name cannot be greater than 64 characters in length")
57-
}
58-
if ok, err := regexp.MatchString(`^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$`, name); err != nil || !ok {
59-
return xerrors.Errorf("name %q is not a valid hostname", name)
60-
}
61-
return nil
62-
}
63-
64-
func (r *RootCmd) provisionerDaemonStart() *serpent.Command {
65-
var (
66-
cacheDir string
67-
logHuman string
68-
logJSON string
69-
logStackdriver string
70-
logFilter []string
71-
name string
72-
rawTags []string
73-
pollInterval time.Duration
74-
pollJitter time.Duration
75-
preSharedKey string
76-
verbose bool
77-
78-
prometheusEnable bool
79-
prometheusAddress string
80-
)
81-
orgContext := agpl.NewOrganizationContext()
82-
client := new(codersdk.Client)
83-
cmd := &serpent.Command{
84-
Use: "start",
85-
Short: "Run a provisioner daemon",
86-
Middleware: serpent.Chain(
87-
// disable checks and warnings because this command starts a daemon; it is
88-
// not meant for humans typing commands. Furthermore, the checks are
89-
// incompatible with PSK auth that this command uses
90-
r.InitClient(client),
91-
),
92-
Handler: func(inv *serpent.Invocation) error {
93-
ctx, cancel := context.WithCancel(inv.Context())
94-
defer cancel()
95-
96-
stopCtx, stopCancel := inv.SignalNotifyContext(ctx, agpl.StopSignalsNoInterrupt...)
97-
defer stopCancel()
98-
interruptCtx, interruptCancel := inv.SignalNotifyContext(ctx, agpl.InterruptSignals...)
99-
defer interruptCancel()
100-
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{MinimalOrganization: codersdk.MinimalOrganization{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{MinimalOrganization: codersdk.MinimalOrganization{ID: orgID}}
127-
}
128-
}
129-
130-
tags, err := agpl.ParseProvisionerTags(rawTags)
131-
if err != nil {
132-
return err
133-
}
134-
135-
if name == "" {
136-
name = cliutil.Hostname()
137-
}
138-
139-
if err := validateProvisionerDaemonName(name); err != nil {
140-
return err
141-
}
142-
143-
logOpts := []clilog.Option{
144-
clilog.WithFilter(logFilter...),
145-
clilog.WithHuman(logHuman),
146-
clilog.WithJSON(logJSON),
147-
clilog.WithStackdriver(logStackdriver),
148-
}
149-
if verbose {
150-
logOpts = append(logOpts, clilog.WithVerbose())
151-
}
152-
153-
logger, closeLogger, err := clilog.New(logOpts...).Build(inv)
154-
if err != nil {
155-
// Fall back to a basic logger
156-
logger = slog.Make(sloghuman.Sink(inv.Stderr))
157-
logger.Error(ctx, "failed to initialize logger", slog.Error(err))
158-
} else {
159-
defer closeLogger()
160-
}
161-
162-
if len(tags) == 0 {
163-
logger.Info(ctx, "note: untagged provisioners can only pick up jobs from untagged templates")
164-
}
165-
166-
// When authorizing with a PSK, we automatically scope the provisionerd
167-
// to organization. Scoping to user with PSK auth is not a valid configuration.
168-
if preSharedKey != "" {
169-
logger.Info(ctx, "psk auth automatically sets tag "+provisionersdk.TagScope+"="+provisionersdk.ScopeOrganization)
170-
tags[provisionersdk.TagScope] = provisionersdk.ScopeOrganization
171-
}
172-
173-
err = os.MkdirAll(cacheDir, 0o700)
174-
if err != nil {
175-
return xerrors.Errorf("mkdir %q: %w", cacheDir, err)
176-
}
177-
178-
tempDir, err := os.MkdirTemp("", "provisionerd")
179-
if err != nil {
180-
return err
181-
}
182-
183-
terraformClient, terraformServer := drpc.MemTransportPipe()
184-
go func() {
185-
<-ctx.Done()
186-
_ = terraformClient.Close()
187-
_ = terraformServer.Close()
188-
}()
189-
190-
errCh := make(chan error, 1)
191-
go func() {
192-
defer cancel()
193-
194-
err := terraform.Serve(ctx, &terraform.ServeOptions{
195-
ServeOptions: &provisionersdk.ServeOptions{
196-
Listener: terraformServer,
197-
Logger: logger.Named("terraform"),
198-
WorkDirectory: tempDir,
199-
},
200-
CachePath: cacheDir,
201-
})
202-
if err != nil && !xerrors.Is(err, context.Canceled) {
203-
select {
204-
case errCh <- err:
205-
default:
206-
}
207-
}
208-
}()
209-
210-
var metrics *provisionerd.Metrics
211-
if prometheusEnable {
212-
logger.Info(ctx, "starting Prometheus endpoint", slog.F("address", prometheusAddress))
213-
214-
prometheusRegistry := prometheus.NewRegistry()
215-
prometheusRegistry.MustRegister(collectors.NewGoCollector())
216-
prometheusRegistry.MustRegister(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}))
217-
218-
m := provisionerd.NewMetrics(prometheusRegistry)
219-
m.Runner.NumDaemons.Set(float64(1)) // Set numDaemons to 1 as this is standalone mode.
220-
metrics = &m
221-
222-
closeFunc := agpl.ServeHandler(ctx, logger, promhttp.InstrumentMetricHandler(
223-
prometheusRegistry, promhttp.HandlerFor(prometheusRegistry, promhttp.HandlerOpts{}),
224-
), prometheusAddress, "prometheus")
225-
defer closeFunc()
226-
}
227-
228-
logger.Info(ctx, "starting provisioner daemon", slog.F("tags", tags), slog.F("name", name))
229-
230-
connector := provisionerd.LocalProvisioners{
231-
string(database.ProvisionerTypeTerraform): proto.NewDRPCProvisionerClient(terraformClient),
232-
}
233-
srv := provisionerd.New(func(ctx context.Context) (provisionerdproto.DRPCProvisionerDaemonClient, error) {
234-
return client.ServeProvisionerDaemon(ctx, codersdk.ServeProvisionerDaemonRequest{
235-
ID: uuid.New(),
236-
Name: name,
237-
Provisioners: []codersdk.ProvisionerType{
238-
codersdk.ProvisionerTypeTerraform,
239-
},
240-
Tags: tags,
241-
PreSharedKey: preSharedKey,
242-
Organization: org.ID,
243-
})
244-
}, &provisionerd.Options{
245-
Logger: logger,
246-
UpdateInterval: 500 * time.Millisecond,
247-
Connector: connector,
248-
Metrics: metrics,
249-
})
250-
251-
waitForProvisionerJobs := false
252-
var exitErr error
253-
select {
254-
case <-stopCtx.Done():
255-
exitErr = stopCtx.Err()
256-
_, _ = fmt.Fprintln(inv.Stdout, cliui.Bold(
257-
"Stop caught, waiting for provisioner jobs to complete and gracefully exiting. Use ctrl+\\ to force quit",
258-
))
259-
waitForProvisionerJobs = true
260-
case <-interruptCtx.Done():
261-
exitErr = interruptCtx.Err()
262-
_, _ = fmt.Fprintln(inv.Stdout, cliui.Bold(
263-
"Interrupt caught, gracefully exiting. Use ctrl+\\ to force quit",
264-
))
265-
case exitErr = <-errCh:
266-
}
267-
if exitErr != nil && !xerrors.Is(exitErr, context.Canceled) {
268-
cliui.Errorf(inv.Stderr, "Unexpected error, shutting down server: %s\n", exitErr)
269-
}
270-
271-
err = srv.Shutdown(ctx, waitForProvisionerJobs)
272-
if err != nil {
273-
return xerrors.Errorf("shutdown: %w", err)
274-
}
275-
276-
// Shutdown does not call close. Must call it manually.
277-
err = srv.Close()
278-
if err != nil {
279-
return xerrors.Errorf("close server: %w", err)
280-
}
281-
282-
cancel()
283-
if xerrors.Is(exitErr, context.Canceled) {
284-
return nil
285-
}
286-
return exitErr
287-
},
288-
}
289-
290-
cmd.Options = serpent.OptionSet{
291-
{
292-
Flag: "cache-dir",
293-
FlagShorthand: "c",
294-
Env: "CODER_CACHE_DIRECTORY",
295-
Description: "Directory to store cached data.",
296-
Default: codersdk.DefaultCacheDir(),
297-
Value: serpent.StringOf(&cacheDir),
298-
},
299-
{
300-
Flag: "tag",
301-
FlagShorthand: "t",
302-
Env: "CODER_PROVISIONERD_TAGS",
303-
Description: "Tags to filter provisioner jobs by.",
304-
Value: serpent.StringArrayOf(&rawTags),
305-
},
306-
{
307-
Flag: "poll-interval",
308-
Env: "CODER_PROVISIONERD_POLL_INTERVAL",
309-
Default: time.Second.String(),
310-
Description: "Deprecated and ignored.",
311-
Value: serpent.DurationOf(&pollInterval),
312-
},
313-
{
314-
Flag: "poll-jitter",
315-
Env: "CODER_PROVISIONERD_POLL_JITTER",
316-
Description: "Deprecated and ignored.",
317-
Default: (100 * time.Millisecond).String(),
318-
Value: serpent.DurationOf(&pollJitter),
319-
},
320-
{
321-
Flag: "psk",
322-
Env: "CODER_PROVISIONER_DAEMON_PSK",
323-
Description: "Pre-shared key to authenticate with Coder server.",
324-
Value: serpent.StringOf(&preSharedKey),
325-
},
326-
{
327-
Flag: "name",
328-
Env: "CODER_PROVISIONER_DAEMON_NAME",
329-
Description: "Name of this provisioner daemon. Defaults to the current hostname without FQDN.",
330-
Value: serpent.StringOf(&name),
331-
Default: "",
332-
},
333-
{
334-
Flag: "verbose",
335-
Env: "CODER_PROVISIONER_DAEMON_VERBOSE",
336-
Description: "Output debug-level logs.",
337-
Value: serpent.BoolOf(&verbose),
338-
Default: "false",
339-
},
340-
{
341-
Flag: "log-human",
342-
Env: "CODER_PROVISIONER_DAEMON_LOGGING_HUMAN",
343-
Description: "Output human-readable logs to a given file.",
344-
Value: serpent.StringOf(&logHuman),
345-
Default: "/dev/stderr",
346-
},
347-
{
348-
Flag: "log-json",
349-
Env: "CODER_PROVISIONER_DAEMON_LOGGING_JSON",
350-
Description: "Output JSON logs to a given file.",
351-
Value: serpent.StringOf(&logJSON),
352-
Default: "",
353-
},
354-
{
355-
Flag: "log-stackdriver",
356-
Env: "CODER_PROVISIONER_DAEMON_LOGGING_STACKDRIVER",
357-
Description: "Output Stackdriver compatible logs to a given file.",
358-
Value: serpent.StringOf(&logStackdriver),
359-
Default: "",
360-
},
361-
{
362-
Flag: "log-filter",
363-
Env: "CODER_PROVISIONER_DAEMON_LOG_FILTER",
364-
Description: "Filter debug logs by matching against a given regex. Use .* to match all debug logs.",
365-
Value: serpent.StringArrayOf(&logFilter),
366-
Default: "",
367-
},
368-
{
369-
Flag: "prometheus-enable",
370-
Env: "CODER_PROMETHEUS_ENABLE",
371-
Description: "Serve prometheus metrics on the address defined by prometheus address.",
372-
Value: serpent.BoolOf(&prometheusEnable),
373-
Default: "false",
374-
},
375-
{
376-
Flag: "prometheus-address",
377-
Env: "CODER_PROMETHEUS_ADDRESS",
378-
Description: "The bind address to serve prometheus metrics.",
379-
Value: serpent.StringOf(&prometheusAddress),
380-
Default: "127.0.0.1:2112",
381-
},
382-
}
383-
orgContext.AttachOptions(cmd)
384-
385-
return cmd
386-
}

0 commit comments

Comments
 (0)