Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion internal/templates/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Expand Down
33 changes: 10 additions & 23 deletions internal/templates/userdata/gitea_linux_userdata.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
31 changes: 5 additions & 26 deletions internal/templates/userdata/gitea_windows_userdata.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
33 changes: 10 additions & 23 deletions internal/templates/userdata/github_linux_userdata.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
31 changes: 5 additions & 26 deletions internal/templates/userdata/github_windows_userdata.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
31 changes: 29 additions & 2 deletions runner/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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 {
Expand All @@ -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")
}
Expand All @@ -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)
Expand All @@ -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)
}
Expand Down
Loading