@@ -40,6 +40,7 @@ import (
4040
4141type Options struct {
4242 ReconnectingPTYTimeout time.Duration
43+ EnvironmentVariables map [string ]string
4344 Logger slog.Logger
4445}
4546
@@ -66,6 +67,7 @@ func New(dialer Dialer, options *Options) io.Closer {
6667 logger : options .Logger ,
6768 closeCancel : cancelFunc ,
6869 closed : make (chan struct {}),
70+ envVars : options .EnvironmentVariables ,
6971 }
7072 server .init (ctx )
7173 return server
@@ -83,23 +85,21 @@ type agent struct {
8385 closeMutex sync.Mutex
8486 closed chan struct {}
8587
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
9191 startupScript atomic.Bool
9292 sshServer * ssh.Server
9393}
9494
9595func (a * agent ) run (ctx context.Context ) {
96- var options Metadata
96+ var metadata Metadata
9797 var peerListener * peerbroker.Listener
9898 var err error
9999 // An exponential back-off occurs when the connection is failing to dial.
100100 // This is to prevent server spam in case of a coderd outage.
101101 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 )
103103 if err != nil {
104104 if errors .Is (err , context .Canceled ) {
105105 return
@@ -118,14 +118,12 @@ func (a *agent) run(ctx context.Context) {
118118 return
119119 default :
120120 }
121- a .envVars .Store (options .EnvironmentVariables )
122- a .ownerEmail .Store (options .OwnerEmail )
123- a .ownerUsername .Store (options .OwnerUsername )
121+ a .metadata .Store (metadata )
124122
125123 if a .startupScript .CAS (false , true ) {
126124 // The startup script has not ran yet!
127125 go func () {
128- err := a .runStartupScript (ctx , options .StartupScript )
126+ err := a .runStartupScript (ctx , metadata .StartupScript )
129127 if errors .Is (err , context .Canceled ) {
130128 return
131129 }
@@ -172,7 +170,7 @@ func (*agent) runStartupScript(ctx context.Context, script string) error {
172170 writer , err = gsyslog .NewLogger (gsyslog .LOG_INFO , "USER" , "coder-startup-script" )
173171 if err != nil {
174172 // 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" )
176174 if err != nil {
177175 return xerrors .Errorf ("open startup script log file: %w" , err )
178176 }
@@ -319,6 +317,15 @@ func (a *agent) createCommand(ctx context.Context, rawCommand string, env []stri
319317 return nil , xerrors .Errorf ("get user shell: %w" , err )
320318 }
321319
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+
322329 // gliderlabs/ssh returns a command slice of zero
323330 // when a shell is requested.
324331 command := rawCommand
@@ -344,22 +351,23 @@ func (a *agent) createCommand(ctx context.Context, rawCommand string, env []stri
344351 cmd .Env = append (cmd .Env , fmt .Sprintf (`GIT_SSH_COMMAND=%s gitssh --` , executablePath ))
345352 // These prevent the user from having to specify _anything_ to successfully commit.
346353 // 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 ))
351358
352359 // Load environment variables passed via the agent.
353360 // 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 ))
362369 }
370+
363371 return cmd , nil
364372}
365373
0 commit comments