Skip to content

Commit a4fd505

Browse files
committed
Merge branch 'main' into licenseflow
2 parents 5ebd76b + b6703b1 commit a4fd505

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+1096
-379
lines changed

.vscode/settings.json

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"codersdk",
1818
"cronstrue",
1919
"databasefake",
20+
"dbtype",
2021
"DERP",
2122
"derphttp",
2223
"derpmap",

cli/deployment/config.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ func newConfig() *codersdk.DeploymentConfig {
143143
Name: "Cache Directory",
144144
Usage: "The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd.",
145145
Flag: "cache-dir",
146-
Default: defaultCacheDir(),
146+
Default: DefaultCacheDir(),
147147
},
148148
InMemoryDatabase: &codersdk.DeploymentConfigField[bool]{
149149
Name: "In Memory Database",
@@ -672,7 +672,7 @@ func formatEnv(key string) string {
672672
return "CODER_" + strings.ToUpper(strings.NewReplacer("-", "_", ".", "_").Replace(key))
673673
}
674674

675-
func defaultCacheDir() string {
675+
func DefaultCacheDir() string {
676676
defaultCacheDir, err := os.UserCacheDir()
677677
if err != nil {
678678
defaultCacheDir = os.TempDir()

cli/gitaskpass.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func gitAskpass() *cobra.Command {
2626
RunE: func(cmd *cobra.Command, args []string) error {
2727
ctx := cmd.Context()
2828

29-
ctx, stop := signal.NotifyContext(ctx, interruptSignals...)
29+
ctx, stop := signal.NotifyContext(ctx, InterruptSignals...)
3030
defer stop()
3131

3232
user, host, err := gitauth.ParseAskpass(args[0])

cli/gitssh.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func gitssh() *cobra.Command {
2929

3030
// Catch interrupt signals to ensure the temporary private
3131
// key file is cleaned up on most cases.
32-
ctx, stop := signal.NotifyContext(ctx, interruptSignals...)
32+
ctx, stop := signal.NotifyContext(ctx, InterruptSignals...)
3333
defer stop()
3434

3535
// Early check so errors are reported immediately.

cli/login_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func TestLogin(t *testing.T) {
7575
// accurately detect Windows ptys when they are not attached to a process:
7676
// https://github.com/mattn/go-isatty/issues/59
7777
doneChan := make(chan struct{})
78-
root, _ := clitest.New(t, "login", client.URL.String(), "--first-user-username", "testuser", "--first-user-email", "user@coder.com", "--first-user-password", "password")
78+
root, _ := clitest.New(t, "login", client.URL.String(), "--first-user-username", "testuser", "--first-user-email", "user@coder.com", "--first-user-password", "password", "--first-user-trial")
7979
pty := ptytest.New(t)
8080
root.SetIn(pty.Input())
8181
root.SetOut(pty.Output())

cli/server.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co
108108
//
109109
// To get out of a graceful shutdown, the user can send
110110
// SIGQUIT with ctrl+\ or SIGKILL with `kill -9`.
111-
notifyCtx, notifyStop := signal.NotifyContext(ctx, interruptSignals...)
111+
notifyCtx, notifyStop := signal.NotifyContext(ctx, InterruptSignals...)
112112
defer notifyStop()
113113

114114
// Clean up idle connections at the end, e.g.
@@ -946,7 +946,7 @@ func newProvisionerDaemon(
946946
return provisionerd.New(func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
947947
// This debounces calls to listen every second. Read the comment
948948
// in provisionerdserver.go to learn more!
949-
return coderAPI.ListenProvisionerDaemon(ctx, time.Second)
949+
return coderAPI.CreateInMemoryProvisionerDaemon(ctx, time.Second)
950950
}, &provisionerd.Options{
951951
Logger: logger,
952952
PollInterval: 500 * time.Millisecond,

cli/signal_unix.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"syscall"
88
)
99

10-
var interruptSignals = []os.Signal{
10+
var InterruptSignals = []os.Signal{
1111
os.Interrupt,
1212
syscall.SIGTERM,
1313
syscall.SIGHUP,

cli/signal_windows.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ import (
66
"os"
77
)
88

9-
var interruptSignals = []os.Signal{os.Interrupt}
9+
var InterruptSignals = []os.Signal{os.Interrupt}

cli/templatecreate.go

+31-9
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ import (
2424

2525
func templateCreate() *cobra.Command {
2626
var (
27-
directory string
28-
provisioner string
29-
parameterFile string
30-
defaultTTL time.Duration
27+
directory string
28+
provisioner string
29+
provisionerTags []string
30+
parameterFile string
31+
defaultTTL time.Duration
3132
)
3233
cmd := &cobra.Command{
3334
Use: "create [name]",
@@ -87,12 +88,18 @@ func templateCreate() *cobra.Command {
8788
}
8889
spin.Stop()
8990

91+
tags, err := ParseProvisionerTags(provisionerTags)
92+
if err != nil {
93+
return err
94+
}
95+
9096
job, _, err := createValidTemplateVersion(cmd, createValidTemplateVersionArgs{
91-
Client: client,
92-
Organization: organization,
93-
Provisioner: database.ProvisionerType(provisioner),
94-
FileID: resp.ID,
95-
ParameterFile: parameterFile,
97+
Client: client,
98+
Organization: organization,
99+
Provisioner: database.ProvisionerType(provisioner),
100+
FileID: resp.ID,
101+
ParameterFile: parameterFile,
102+
ProvisionerTags: tags,
96103
})
97104
if err != nil {
98105
return err
@@ -131,6 +138,7 @@ func templateCreate() *cobra.Command {
131138
cmd.Flags().StringVarP(&directory, "directory", "d", currentDirectory, "Specify the directory to create from")
132139
cmd.Flags().StringVarP(&provisioner, "test.provisioner", "", "terraform", "Customize the provisioner backend")
133140
cmd.Flags().StringVarP(&parameterFile, "parameter-file", "", "", "Specify a file path with parameter values.")
141+
cmd.Flags().StringArrayVarP(&provisionerTags, "provisioner-tag", "", []string{}, "Specify a set of tags to target provisioner daemons.")
134142
cmd.Flags().DurationVarP(&defaultTTL, "default-ttl", "", 24*time.Hour, "Specify a default TTL for workspaces created from this template.")
135143
// This is for testing!
136144
err := cmd.Flags().MarkHidden("test.provisioner")
@@ -154,6 +162,7 @@ type createValidTemplateVersionArgs struct {
154162
// before prompting the user. Set to false to always prompt for param
155163
// values.
156164
ReuseParameters bool
165+
ProvisionerTags map[string]string
157166
}
158167

159168
func createValidTemplateVersion(cmd *cobra.Command, args createValidTemplateVersionArgs, parameters ...codersdk.CreateParameterRequest) (*codersdk.TemplateVersion, []codersdk.CreateParameterRequest, error) {
@@ -165,6 +174,7 @@ func createValidTemplateVersion(cmd *cobra.Command, args createValidTemplateVers
165174
FileID: args.FileID,
166175
Provisioner: codersdk.ProvisionerType(args.Provisioner),
167176
ParameterValues: parameters,
177+
ProvisionerTags: args.ProvisionerTags,
168178
}
169179
if args.Template != nil {
170180
req.TemplateID = args.Template.ID
@@ -334,3 +344,15 @@ func prettyDirectoryPath(dir string) string {
334344
}
335345
return pretty
336346
}
347+
348+
func ParseProvisionerTags(rawTags []string) (map[string]string, error) {
349+
tags := map[string]string{}
350+
for _, rawTag := range rawTags {
351+
parts := strings.SplitN(rawTag, "=", 2)
352+
if len(parts) < 2 {
353+
return nil, xerrors.Errorf("invalid tag format for %q. must be key=value", rawTag)
354+
}
355+
tags[parts[0]] = parts[1]
356+
}
357+
return tags, nil
358+
}

cli/templatepush.go

+12-5
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ import (
1818

1919
func templatePush() *cobra.Command {
2020
var (
21-
directory string
22-
versionName string
23-
provisioner string
24-
parameterFile string
25-
alwaysPrompt bool
21+
directory string
22+
versionName string
23+
provisioner string
24+
parameterFile string
25+
alwaysPrompt bool
26+
provisionerTags []string
2627
)
2728

2829
cmd := &cobra.Command{
@@ -75,6 +76,11 @@ func templatePush() *cobra.Command {
7576
}
7677
spin.Stop()
7778

79+
tags, err := ParseProvisionerTags(provisionerTags)
80+
if err != nil {
81+
return err
82+
}
83+
7884
job, _, err := createValidTemplateVersion(cmd, createValidTemplateVersionArgs{
7985
Name: versionName,
8086
Client: client,
@@ -84,6 +90,7 @@ func templatePush() *cobra.Command {
8490
ParameterFile: parameterFile,
8591
Template: &template,
8692
ReuseParameters: !alwaysPrompt,
93+
ProvisionerTags: tags,
8794
})
8895
if err != nil {
8996
return err

coderd/autobuild/executor/lifecycle_executor.go

+1
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ func build(ctx context.Context, store database.Store, workspace database.Workspa
278278
Type: database.ProvisionerJobTypeWorkspaceBuild,
279279
StorageMethod: priorJob.StorageMethod,
280280
FileID: priorJob.FileID,
281+
Tags: priorJob.Tags,
281282
Input: input,
282283
})
283284
if err != nil {

coderd/coderd.go

+83-14
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"crypto/tls"
66
"crypto/x509"
7+
"encoding/json"
78
"fmt"
89
"io"
910
"net/http"
@@ -19,10 +20,13 @@ import (
1920
"github.com/go-chi/chi/v5/middleware"
2021
"github.com/google/uuid"
2122
"github.com/klauspost/compress/zstd"
23+
"github.com/moby/moby/pkg/namesgenerator"
2224
"github.com/prometheus/client_golang/prometheus"
2325
"go.opentelemetry.io/otel/trace"
2426
"golang.org/x/xerrors"
2527
"google.golang.org/api/idtoken"
28+
"storj.io/drpc/drpcmux"
29+
"storj.io/drpc/drpcserver"
2630
"tailscale.com/derp"
2731
"tailscale.com/derp/derphttp"
2832
"tailscale.com/tailcfg"
@@ -33,17 +37,20 @@ import (
3337
"github.com/coder/coder/coderd/audit"
3438
"github.com/coder/coder/coderd/awsidentity"
3539
"github.com/coder/coder/coderd/database"
40+
"github.com/coder/coder/coderd/database/dbtype"
3641
"github.com/coder/coder/coderd/gitauth"
3742
"github.com/coder/coder/coderd/gitsshkey"
3843
"github.com/coder/coder/coderd/httpapi"
3944
"github.com/coder/coder/coderd/httpmw"
4045
"github.com/coder/coder/coderd/metricscache"
46+
"github.com/coder/coder/coderd/provisionerdserver"
4147
"github.com/coder/coder/coderd/rbac"
4248
"github.com/coder/coder/coderd/telemetry"
4349
"github.com/coder/coder/coderd/tracing"
4450
"github.com/coder/coder/coderd/wsconncache"
4551
"github.com/coder/coder/codersdk"
4652
"github.com/coder/coder/provisionerd/proto"
53+
"github.com/coder/coder/provisionersdk"
4754
"github.com/coder/coder/site"
4855
"github.com/coder/coder/tailnet"
4956
)
@@ -324,13 +331,6 @@ func New(options *Options) *API {
324331
r.Get("/{fileID}", api.fileByID)
325332
r.Post("/", api.postFile)
326333
})
327-
328-
r.Route("/provisionerdaemons", func(r chi.Router) {
329-
r.Use(
330-
apiKeyMiddleware,
331-
)
332-
r.Get("/", api.provisionerDaemons)
333-
})
334334
r.Route("/organizations", func(r chi.Router) {
335335
r.Use(
336336
apiKeyMiddleware,
@@ -596,18 +596,20 @@ type API struct {
596596
// RootHandler serves "/"
597597
RootHandler chi.Router
598598

599-
metricsCache *metricscache.Cache
600-
siteHandler http.Handler
601-
websocketWaitMutex sync.Mutex
602-
websocketWaitGroup sync.WaitGroup
599+
metricsCache *metricscache.Cache
600+
siteHandler http.Handler
601+
602+
WebsocketWaitMutex sync.Mutex
603+
WebsocketWaitGroup sync.WaitGroup
604+
603605
workspaceAgentCache *wsconncache.Cache
604606
}
605607

606608
// Close waits for all WebSocket connections to drain before returning.
607609
func (api *API) Close() error {
608-
api.websocketWaitMutex.Lock()
609-
api.websocketWaitGroup.Wait()
610-
api.websocketWaitMutex.Unlock()
610+
api.WebsocketWaitMutex.Lock()
611+
api.WebsocketWaitGroup.Wait()
612+
api.WebsocketWaitMutex.Unlock()
611613

612614
api.metricsCache.Close()
613615
coordinator := api.TailnetCoordinator.Load()
@@ -636,3 +638,70 @@ func compressHandler(h http.Handler) http.Handler {
636638

637639
return cmp.Handler(h)
638640
}
641+
642+
// CreateInMemoryProvisionerDaemon is an in-memory connection to a provisionerd. Useful when starting coderd and provisionerd
643+
// in the same process.
644+
func (api *API) CreateInMemoryProvisionerDaemon(ctx context.Context, debounce time.Duration) (client proto.DRPCProvisionerDaemonClient, err error) {
645+
clientSession, serverSession := provisionersdk.TransportPipe()
646+
defer func() {
647+
if err != nil {
648+
_ = clientSession.Close()
649+
_ = serverSession.Close()
650+
}
651+
}()
652+
653+
name := namesgenerator.GetRandomName(1)
654+
daemon, err := api.Database.InsertProvisionerDaemon(ctx, database.InsertProvisionerDaemonParams{
655+
ID: uuid.New(),
656+
CreatedAt: database.Now(),
657+
Name: name,
658+
Provisioners: []database.ProvisionerType{database.ProvisionerTypeEcho, database.ProvisionerTypeTerraform},
659+
Tags: dbtype.StringMap{
660+
provisionerdserver.TagScope: provisionerdserver.ScopeOrganization,
661+
},
662+
})
663+
if err != nil {
664+
return nil, xerrors.Errorf("insert provisioner daemon %q: %w", name, err)
665+
}
666+
667+
tags, err := json.Marshal(daemon.Tags)
668+
if err != nil {
669+
return nil, xerrors.Errorf("marshal tags: %w", err)
670+
}
671+
672+
mux := drpcmux.New()
673+
err = proto.DRPCRegisterProvisionerDaemon(mux, &provisionerdserver.Server{
674+
AccessURL: api.AccessURL,
675+
ID: daemon.ID,
676+
Database: api.Database,
677+
Pubsub: api.Pubsub,
678+
Provisioners: daemon.Provisioners,
679+
Telemetry: api.Telemetry,
680+
Tags: tags,
681+
QuotaCommitter: &api.QuotaCommitter,
682+
AcquireJobDebounce: debounce,
683+
Logger: api.Logger.Named(fmt.Sprintf("provisionerd-%s", daemon.Name)),
684+
})
685+
if err != nil {
686+
return nil, err
687+
}
688+
server := drpcserver.NewWithOptions(mux, drpcserver.Options{
689+
Log: func(err error) {
690+
if xerrors.Is(err, io.EOF) {
691+
return
692+
}
693+
api.Logger.Debug(ctx, "drpc server error", slog.Error(err))
694+
},
695+
})
696+
go func() {
697+
err := server.Serve(ctx, serverSession)
698+
if err != nil && !xerrors.Is(err, io.EOF) {
699+
api.Logger.Debug(ctx, "provisioner daemon disconnected", slog.Error(err))
700+
}
701+
// close the sessions so we don't leak goroutines serving them.
702+
_ = clientSession.Close()
703+
_ = serverSession.Close()
704+
}()
705+
706+
return proto.NewDRPCProvisionerDaemonClient(provisionersdk.Conn(clientSession)), nil
707+
}

0 commit comments

Comments
 (0)