diff --git a/internal/templates/templates.go b/internal/templates/templates.go index 3962117bf..8acc22844 100644 --- a/internal/templates/templates.go +++ b/internal/templates/templates.go @@ -24,6 +24,18 @@ type WrapperContext struct { MetadataURL string } +// InstallContext wraps the vendored InstallRunnerParams with agent-specific +// fields so templates can render agent values directly without requiring +// the runner to fetch metadata and parse it with jq at runtime. +type InstallContext struct { + cloudconfig.InstallRunnerParams + AgentMode bool + AgentDownloadURL string + AgentURL string + AgentToken string + AgentShell string // "true" or "false", used verbatim in TOML config +} + func GetTemplateContent(osType commonParams.OSType, forge params.EndpointType) ([]byte, error) { switch forge { case params.GithubEndpointType, params.GiteaEndpointType: @@ -53,7 +65,7 @@ func GetTemplateContent(osType commonParams.OSType, forge params.EndpointType) ( return data, nil } -func RenderRunnerInstallScript(tpl string, context cloudconfig.InstallRunnerParams) ([]byte, error) { +func RenderRunnerInstallScript(tpl string, context any) ([]byte, error) { t, err := template.New("").Parse(tpl) if err != nil { return nil, fmt.Errorf("failed to parse template: %w", err) diff --git a/internal/templates/userdata/gitea_linux_userdata.tmpl b/internal/templates/userdata/gitea_linux_userdata.tmpl index 4ef629199..a7adefbe8 100644 --- a/internal/templates/userdata/gitea_linux_userdata.tmpl +++ b/internal/templates/userdata/gitea_linux_userdata.tmpl @@ -55,31 +55,18 @@ function fail() { exit 1 } -INSTANCE_METADATA=$(curl \ - --retry 5 \ - --retry-delay 5 \ - --retry-connrefused \ - --fail -s \ - -H 'Accept: application/json' \ - -H "Authorization: Bearer ${BEARER_TOKEN}" \ - "$METADATA_URL/runner-metadata" 2>&1) || fail "failed to get instance metadata: $INSTANCE_METADATA" - -AGENT_MODE=$(echo "$INSTANCE_METADATA" | jq -r '.agent_mode // empty') +{{- if .AgentMode }} +AGENT_MODE="true" +{{- else }} +AGENT_MODE="" +{{- end }} + if [ "$AGENT_MODE" == "true" ]; then sendStatus "Agent mode is enabled; setting up agent" - DOWNLOAD_URL=$(echo "$INSTANCE_METADATA" | jq -r '.agent_tools.download_url // empty') - if [ -z "$DOWNLOAD_URL" ]; then - fail "agent mode is enabled, but no agent tools found in metadata" - fi - AGENT_URL=$(echo "$INSTANCE_METADATA" | jq -r '.metadata_access.agent_url // empty') - if [ -z "$AGENT_URL" ]; then - fail "agent mode is enabled, but agent_url was not found in metadata" - fi - AGENT_TOKEN=$(echo "$INSTANCE_METADATA" | jq -r '.agent_token // empty') - if [ -z "$AGENT_TOKEN" ]; then - fail "agent mode is enabled, but agent_token was not found in metadata" - fi - AGENT_SHELL=$(echo "$INSTANCE_METADATA" | jq -r '.agent_shell_enabled // false') + DOWNLOAD_URL="{{ .AgentDownloadURL }}" + AGENT_URL="{{ .AgentURL }}" + AGENT_TOKEN="{{ .AgentToken }}" + AGENT_SHELL={{ .AgentShell }} sendStatus "Downloading agent from $DOWNLOAD_URL" sudo curl --retry 5 \ --retry-delay 5 \ diff --git a/internal/templates/userdata/gitea_windows_userdata.tmpl b/internal/templates/userdata/gitea_windows_userdata.tmpl index 61454b30e..2ef13f7c7 100644 --- a/internal/templates/userdata/gitea_windows_userdata.tmpl +++ b/internal/templates/userdata/gitea_windows_userdata.tmpl @@ -382,45 +382,24 @@ if (!($CallbackURL -match "^(.*)/status(/)?$")) { $CallbackURL = "$CallbackURL/status" } $GHRunnerGroup = "{{.GitHubRunnerGroup}}" -try { - $instanceMetadata = (wget -UseBasicParsing -Headers @{"Accept"="application/json"; "Authorization"="Bearer $Token"} -Uri {{.MetadataURL}}/runner-metadata) - $metadata = ConvertFrom-Json $instanceMetadata.Content -} catch { - Invoke-GarmFailure -Message "failed to get runner metadata: $_" -CallbackURL "{{.CallbackURL}}" | Out-Null -} - function Get-IsAgentMode { - return ($metadata.agent_mode -eq $true) + return {{ if .AgentMode }}$true{{ else }}$false{{ end }} } function Get-AgentURL { - $url = $metadata.metadata_access.agent_url - if (!$url) { - Throw("missing agent URL") - } - return $url + return "{{ .AgentURL }}" } function Get-AgentToken { - $token = $metadata.agent_token - if (!$token) { - Throw("missing agent Token") - } - return $token + return "{{ .AgentToken }}" } function Get-AgentDownloadURL { - $url = $metadata.agent_tools.download_url - if (!$url) { - Throw("missing agent download URL") - } - return $url + return "{{ .AgentDownloadURL }}" } function Get-AgentShellEnabled { - $shellEnabled = $metadata.agent_shell_enabled - if ($shellEnabled) {return "true"} - return "false" + return "{{ .AgentShell }}" } function Install-GarmAgent { diff --git a/internal/templates/userdata/github_linux_userdata.tmpl b/internal/templates/userdata/github_linux_userdata.tmpl index d5955c41a..609623cff 100644 --- a/internal/templates/userdata/github_linux_userdata.tmpl +++ b/internal/templates/userdata/github_linux_userdata.tmpl @@ -64,31 +64,18 @@ function fail() { exit 1 } -INSTANCE_METADATA=$(curl \ - --retry 5 \ - --retry-delay 5 \ - --retry-connrefused \ - --fail -s \ - -H 'Accept: application/json' \ - -H "Authorization: Bearer ${BEARER_TOKEN}" \ - "$METADATA_URL/runner-metadata" 2>&1) || fail "failed to get instance metadata: $INSTANCE_METADATA" - -AGENT_MODE=$(echo "$INSTANCE_METADATA" | jq -r '.agent_mode // empty') +{{- if .AgentMode }} +AGENT_MODE="true" +{{- else }} +AGENT_MODE="" +{{- end }} + if [ "$AGENT_MODE" == "true" ]; then sendStatus "Agent mode is enabled; setting up agent" - DOWNLOAD_URL=$(echo "$INSTANCE_METADATA" | jq -r '.agent_tools.download_url // empty') - if [ -z "$DOWNLOAD_URL" ]; then - fail "agent mode is enabled, but no agent tools found in metadata" - fi - AGENT_URL=$(echo "$INSTANCE_METADATA" | jq -r '.metadata_access.agent_url // empty') - if [ -z "$AGENT_URL" ]; then - fail "agent mode is enabled, but agent_url was not found in metadata" - fi - AGENT_TOKEN=$(echo "$INSTANCE_METADATA" | jq -r '.agent_token // empty') - if [ -z "$AGENT_TOKEN" ]; then - fail "agent mode is enabled, but agent_token was not found in metadata" - fi - AGENT_SHELL=$(echo "$INSTANCE_METADATA" | jq -r '.agent_shell_enabled // false') + DOWNLOAD_URL="{{ .AgentDownloadURL }}" + AGENT_URL="{{ .AgentURL }}" + AGENT_TOKEN="{{ .AgentToken }}" + AGENT_SHELL={{ .AgentShell }} sendStatus "Downloading agent from $DOWNLOAD_URL" sudo curl --retry 5 \ --retry-delay 5 \ diff --git a/internal/templates/userdata/github_windows_userdata.tmpl b/internal/templates/userdata/github_windows_userdata.tmpl index c31888f8f..dd8aa3107 100644 --- a/internal/templates/userdata/github_windows_userdata.tmpl +++ b/internal/templates/userdata/github_windows_userdata.tmpl @@ -382,45 +382,24 @@ if (!($CallbackURL -match "^(.*)/status(/)?$")) { $CallbackURL = "$CallbackURL/status" } $GHRunnerGroup = "{{.GitHubRunnerGroup}}" -try { - $instanceMetadata = (wget -UseBasicParsing -Headers @{"Accept"="application/json"; "Authorization"="Bearer $Token"} -Uri {{.MetadataURL}}/runner-metadata) - $metadata = ConvertFrom-Json $instanceMetadata.Content -} catch { - Invoke-GarmFailure -Message "failed to get runner metadata: $_" -CallbackURL "{{.CallbackURL}}" | Out-Null -} - function Get-IsAgentMode { - return ($metadata.agent_mode -eq $true) + return {{ if .AgentMode }}$true{{ else }}$false{{ end }} } function Get-AgentURL { - $url = $metadata.metadata_access.agent_url - if (!$url) { - Throw("missing agent URL") - } - return $url + return "{{ .AgentURL }}" } function Get-AgentToken { - $token = $metadata.agent_token - if (!$token) { - Throw("missing agent Token") - } - return $token + return "{{ .AgentToken }}" } function Get-AgentDownloadURL { - $url = $metadata.agent_tools.download_url - if (!$url) { - Throw("missing agent download URL") - } - return $url + return "{{ .AgentDownloadURL }}" } function Get-AgentShellEnabled { - $shellEnabled = $metadata.agent_shell_enabled - if ($shellEnabled) {return "true"} - return "false" + return "{{ .AgentShell }}" } function Install-GarmAgent { diff --git a/runner/metadata.go b/runner/metadata.go index 66f306c1d..0e47c810a 100644 --- a/runner/metadata.go +++ b/runner/metadata.go @@ -377,6 +377,7 @@ func (r *Runner) GetRunnerInstallScript(ctx context.Context) ([]byte, error) { var templateID uint var specs cloudconfig.CloudConfigSpec var extraSpecs json.RawMessage + var enableShell bool switch { case instance.PoolID != "": pool, err := r.store.GetPoolByID(r.ctx, instance.PoolID) @@ -389,6 +390,7 @@ func (r *Runner) GetRunnerInstallScript(ctx context.Context) ([]byte, error) { } extraSpecs = pool.ExtraSpecs templateID = pool.TemplateID + enableShell = pool.EnableShell case instance.ScaleSetID > 0: scaleSet, err := r.store.GetScaleSetByID(r.ctx, instance.ScaleSetID) if err != nil { @@ -400,6 +402,7 @@ func (r *Runner) GetRunnerInstallScript(ctx context.Context) ([]byte, error) { } extraSpecs = scaleSet.ExtraSpecs templateID = scaleSet.TemplateID + enableShell = scaleSet.EnableShell default: return nil, fmt.Errorf("instance is not part of a pool or scale set") } @@ -425,9 +428,33 @@ func (r *Runner) GetRunnerInstallScript(ctx context.Context) ([]byte, error) { } } + // Build agent context for the template so templates don't need + // to fetch metadata and parse it with jq at runtime. + tplCtx := templates.InstallContext{ + InstallRunnerParams: installCtx, + } + if entity.AgentMode { + tplCtx.AgentMode = true + agentTool := r.getInstanceAgentTool(instance) + if agentTool != nil { + tplCtx.AgentDownloadURL = agentTool.DownloadURL + agentToken, err := r.GetAgentJWTToken(r.ctx, instance.Name) + if err != nil { + return nil, fmt.Errorf("failed to get agent token: %w", err) + } + tplCtx.AgentToken = agentToken + tplCtx.AgentURL = cache.ControllerInfo().AgentURL + tplCtx.AgentShell = fmt.Sprintf("%t", enableShell) + } else { + slog.WarnContext(ctx, "agent mode enabled but no tools available", + "runner_name", instance.Name) + tplCtx.AgentMode = false + } + } + var tplBytes []byte if len(specs.RunnerInstallTemplate) > 0 { - installCtx.ExtraContext = specs.ExtraContext + tplCtx.ExtraContext = specs.ExtraContext tplBytes = specs.RunnerInstallTemplate } else { template, err := r.store.GetTemplate(r.ctx, templateID) @@ -437,7 +464,7 @@ func (r *Runner) GetRunnerInstallScript(ctx context.Context) ([]byte, error) { tplBytes = template.Data } - installScript, err := templates.RenderRunnerInstallScript(string(tplBytes), installCtx) + installScript, err := templates.RenderRunnerInstallScript(string(tplBytes), tplCtx) if err != nil { return nil, fmt.Errorf("failed to get runner install script: %w", err) }