@@ -40,6 +40,7 @@ import (
40
40
41
41
type Options struct {
42
42
ReconnectingPTYTimeout time.Duration
43
+ EnvironmentVariables map [string ]string
43
44
Logger slog.Logger
44
45
}
45
46
@@ -66,6 +67,7 @@ func New(dialer Dialer, options *Options) io.Closer {
66
67
logger : options .Logger ,
67
68
closeCancel : cancelFunc ,
68
69
closed : make (chan struct {}),
70
+ envVars : options .EnvironmentVariables ,
69
71
}
70
72
server .init (ctx )
71
73
return server
@@ -83,23 +85,21 @@ type agent struct {
83
85
closeMutex sync.Mutex
84
86
closed chan struct {}
85
87
86
- // Environment variables sent by Coder to inject for shell sessions.
87
- // These are atomic because values can change after reconnect.
88
- envVars atomic.Value
89
- ownerEmail atomic.String
90
- ownerUsername atomic.String
88
+ envVars map [string ]string
89
+ // metadata is atomic because values can change after reconnection.
90
+ metadata atomic.Value
91
91
startupScript atomic.Bool
92
92
sshServer * ssh.Server
93
93
}
94
94
95
95
func (a * agent ) run (ctx context.Context ) {
96
- var options Metadata
96
+ var metadata Metadata
97
97
var peerListener * peerbroker.Listener
98
98
var err error
99
99
// An exponential back-off occurs when the connection is failing to dial.
100
100
// This is to prevent server spam in case of a coderd outage.
101
101
for retrier := retry .New (50 * time .Millisecond , 10 * time .Second ); retrier .Wait (ctx ); {
102
- options , peerListener , err = a .dialer (ctx , a .logger )
102
+ metadata , peerListener , err = a .dialer (ctx , a .logger )
103
103
if err != nil {
104
104
if errors .Is (err , context .Canceled ) {
105
105
return
@@ -118,14 +118,12 @@ func (a *agent) run(ctx context.Context) {
118
118
return
119
119
default :
120
120
}
121
- a .envVars .Store (options .EnvironmentVariables )
122
- a .ownerEmail .Store (options .OwnerEmail )
123
- a .ownerUsername .Store (options .OwnerUsername )
121
+ a .metadata .Store (metadata )
124
122
125
123
if a .startupScript .CAS (false , true ) {
126
124
// The startup script has not ran yet!
127
125
go func () {
128
- err := a .runStartupScript (ctx , options .StartupScript )
126
+ err := a .runStartupScript (ctx , metadata .StartupScript )
129
127
if errors .Is (err , context .Canceled ) {
130
128
return
131
129
}
@@ -172,7 +170,7 @@ func (*agent) runStartupScript(ctx context.Context, script string) error {
172
170
writer , err = gsyslog .NewLogger (gsyslog .LOG_INFO , "USER" , "coder-startup-script" )
173
171
if err != nil {
174
172
// If the syslog isn't supported or cannot be created, use a text file in temp.
175
- writer , err = os .CreateTemp ("" , "coder-startup-script.txt" )
173
+ writer , err = os .CreateTemp ("" , "coder-startup-script-* .txt" )
176
174
if err != nil {
177
175
return xerrors .Errorf ("open startup script log file: %w" , err )
178
176
}
@@ -319,6 +317,15 @@ func (a *agent) createCommand(ctx context.Context, rawCommand string, env []stri
319
317
return nil , xerrors .Errorf ("get user shell: %w" , err )
320
318
}
321
319
320
+ rawMetadata := a .metadata .Load ()
321
+ if rawMetadata == nil {
322
+ return nil , xerrors .Errorf ("no metadata was provided: %w" , err )
323
+ }
324
+ metadata , valid := rawMetadata .(Metadata )
325
+ if ! valid {
326
+ return nil , xerrors .Errorf ("metadata is the wrong type: %T" , metadata )
327
+ }
328
+
322
329
// gliderlabs/ssh returns a command slice of zero
323
330
// when a shell is requested.
324
331
command := rawCommand
@@ -344,22 +351,23 @@ func (a *agent) createCommand(ctx context.Context, rawCommand string, env []stri
344
351
cmd .Env = append (cmd .Env , fmt .Sprintf (`GIT_SSH_COMMAND=%s gitssh --` , executablePath ))
345
352
// These prevent the user from having to specify _anything_ to successfully commit.
346
353
// Both author and committer must be set!
347
- cmd .Env = append (cmd .Env , fmt .Sprintf (`GIT_AUTHOR_EMAIL=%s` , a . ownerEmail . Load () ))
348
- cmd .Env = append (cmd .Env , fmt .Sprintf (`GIT_COMMITTER_EMAIL=%s` , a . ownerEmail . Load () ))
349
- cmd .Env = append (cmd .Env , fmt .Sprintf (`GIT_AUTHOR_NAME=%s` , a . ownerUsername . Load () ))
350
- cmd .Env = append (cmd .Env , fmt .Sprintf (`GIT_COMMITTER_NAME=%s` , a . ownerUsername . Load () ))
354
+ cmd .Env = append (cmd .Env , fmt .Sprintf (`GIT_AUTHOR_EMAIL=%s` , metadata . OwnerEmail ))
355
+ cmd .Env = append (cmd .Env , fmt .Sprintf (`GIT_COMMITTER_EMAIL=%s` , metadata . OwnerEmail ))
356
+ cmd .Env = append (cmd .Env , fmt .Sprintf (`GIT_AUTHOR_NAME=%s` , metadata . OwnerUsername ))
357
+ cmd .Env = append (cmd .Env , fmt .Sprintf (`GIT_COMMITTER_NAME=%s` , metadata . OwnerUsername ))
351
358
352
359
// Load environment variables passed via the agent.
353
360
// These should override all variables we manually specify.
354
- envVars := a . envVars . Load ()
355
- if envVars != nil {
356
- envVarMap , ok := envVars .( map [ string ] string )
357
- if ok {
358
- for key , value := range envVarMap {
359
- cmd . Env = append ( cmd . Env , fmt . Sprintf ( "%s=%s" , key , value ))
360
- }
361
- }
361
+ for key , value := range metadata . EnvironmentVariables {
362
+ cmd . Env = append ( cmd . Env , fmt . Sprintf ( "%s=%s" , key , value ))
363
+ }
364
+
365
+ // Agent-level environment variables should take over all!
366
+ // This is used for setting agent-specific variables like "CODER_AGENT_TOKEN".
367
+ for key , value := range a . envVars {
368
+ cmd . Env = append ( cmd . Env , fmt . Sprintf ( "%s=%s" , key , value ))
362
369
}
370
+
363
371
return cmd , nil
364
372
}
365
373
0 commit comments