From a9951e6b620c623763911bf982c31699b516ca0a Mon Sep 17 00:00:00 2001 From: Christopher Homberger Date: Fri, 12 Sep 2025 14:17:35 +0200 Subject: [PATCH] Fix respect Server Decision to use v2 broker flow --- protocol/connection.go | 18 ++++- protocol/task_agent.go | 55 +++++++++++++- runnerconfiguration/add.go | 3 +- .../compat/actions_runner_compat.go | 74 +++++++++++++------ 4 files changed, 123 insertions(+), 27 deletions(-) diff --git a/protocol/connection.go b/protocol/connection.go index bd226bd..d76dac9 100644 --- a/protocol/connection.go +++ b/protocol/connection.go @@ -357,16 +357,32 @@ func (vssConnection *VssConnection) CreateSessionExt(ctx context.Context, server con := &AgentMessageConnection{VssConnection: vssConnection, TaskAgentSession: session} con.Status = statusOnline + con.ServerV2URL = serverV2URL return con, nil } func (vssConnection *VssConnection) CreateSession(ctx context.Context) (*AgentMessageConnection, error) { - return vssConnection.CreateSessionExt(ctx, vssConnection.TaskAgent.ServerV2URL) + useV2Flow, hasUseV2Flow := vssConnection.TaskAgent.Properties.LookupBool("UseV2Flow") + serverV2URL, hasServerV2URL := vssConnection.TaskAgent.Properties.LookupString("ServerUrlV2") + if !useV2Flow || !hasUseV2Flow || !hasServerV2URL { + serverV2URL = "" + } else { + serverV2URL = strings.TrimSuffix(serverV2URL, "/") + } + return vssConnection.CreateSessionExt(ctx, serverV2URL) } func (vssConnection *VssConnection) LoadSession(ctx context.Context, session *TaskAgentSession) (*AgentMessageConnection, error) { con := &AgentMessageConnection{VssConnection: vssConnection, TaskAgentSession: session} con.Status = statusOnline + useV2Flow, hasUseV2Flow := vssConnection.TaskAgent.Properties.LookupBool("UseV2Flow") + serverV2URL, hasServerV2URL := vssConnection.TaskAgent.Properties.LookupString("ServerUrlV2") + if !useV2Flow || !hasUseV2Flow || !hasServerV2URL { + serverV2URL = "" + } else { + serverV2URL = strings.TrimSuffix(serverV2URL, "/") + } + con.ServerV2URL = serverV2URL return con, nil } diff --git a/protocol/task_agent.go b/protocol/task_agent.go index 0b2d8b2..395c83a 100644 --- a/protocol/task_agent.go +++ b/protocol/task_agent.go @@ -8,6 +8,7 @@ import ( "io" "net/http" "net/url" + "strings" "time" "github.com/golang-jwt/jwt" @@ -39,6 +40,57 @@ type AgentLabel struct { Type string } +type PropertyValue struct { + Type string `json:"$type"` + Value interface{} `json:"$value"` +} + +func (v *PropertyValue) UnmarshalJSON(data []byte) error { + var b bool + if json.Unmarshal(data, &b) == nil { + v.Type = "System.Boolean" + v.Value = b + return nil + } + var raw string + if json.Unmarshal(data, &raw) == nil { + v.Type = "System.String" + v.Value = raw + return nil + } + type PropertyValueRaw PropertyValue + // Best Effort, drop errors + _ = json.Unmarshal(data, (*PropertyValueRaw)(v)) + return nil +} + +type PropertiesCollection map[string]PropertyValue + +func (c *PropertiesCollection) Lookup(name, ty string) (interface{}, bool) { + for k, v := range *c { + if strings.EqualFold(k, name) && strings.EqualFold(v.Type, ty) { + return v.Value, true + } + } + return nil, false +} + +func (c *PropertiesCollection) LookupBool(name string) (value, ok bool) { + if v, ok := c.Lookup(name, "System.Boolean"); ok && v != nil { + b, isBool := v.(bool) + return b, isBool + } + return false, false +} + +func (c *PropertiesCollection) LookupString(name string) (string, bool) { + if v, ok := c.Lookup(name, "System.String"); ok && v != nil { + b, isString := v.(string) + return b, isString + } + return "", false +} + type TaskAgent struct { Authorization TaskAgentAuthorization Labels []AgentLabel @@ -53,8 +105,7 @@ type TaskAgent struct { CreatedOn string Ephemeral bool `json:",omitempty"` DisableUpdate bool `json:",omitempty"` - // Just a convenient way to store the URL, not part of the spec - ServerV2URL string `json:",omitempty"` + Properties PropertiesCollection } type TaskAgents struct { diff --git a/runnerconfiguration/add.go b/runnerconfiguration/add.go index 3558d5d..f07c585 100644 --- a/runnerconfiguration/add.go +++ b/runnerconfiguration/add.go @@ -81,7 +81,7 @@ func (config *ConfigureRunner) Configure( } if res.UseV2FLow { vssConnection = &protocol.VssConnection{ - AuthHeader: "RemoteAuth " + config.Token, + AuthHeader: "Bearer " + res.Token, Trace: config.Trace, Client: c, } @@ -309,7 +309,6 @@ func registerOrReplaceRunnerV2(taskAgent *protocol.TaskAgent, config *ConfigureR taskAgent.Name = runnerResp.Name taskAgent.Authorization.AuthorizationURL = runnerResp.Authorization.AuthorizationURL taskAgent.Authorization.ClientID = runnerResp.Authorization.ClientId - taskAgent.ServerV2URL = runnerResp.Authorization.ServerURL return nil } diff --git a/runnerconfiguration/compat/actions_runner_compat.go b/runnerconfiguration/compat/actions_runner_compat.go index a5aeb76..6eb9a2c 100644 --- a/runnerconfiguration/compat/actions_runner_compat.go +++ b/runnerconfiguration/compat/actions_runner_compat.go @@ -25,17 +25,18 @@ type DotnetRsaParameters struct { } type DotnetAgent struct { - AgentID string `json:"AgentId"` - AgentName string `json:"AgentName"` - DisableUpdate string `json:"DisableUpdate"` - Ephemeral string `json:"Ephemeral"` - PoolID string `json:"PoolId"` - PoolName string `json:"PoolName,omitempty"` - ServerURL string `json:"ServerUrl"` - WorkFolder string `json:"WorkFolder"` - GitHubURL string `json:"GitHubUrl"` - UseV2Flow bool `json:"UseV2Flow"` - ServerURLV2 string `json:"ServerUrlV2"` + AgentID string `json:"AgentId"` + AgentName string `json:"AgentName"` + DisableUpdate string `json:"DisableUpdate"` + Ephemeral string `json:"Ephemeral"` + PoolID string `json:"PoolId"` + PoolName string `json:"PoolName,omitempty"` + ServerURL string `json:"ServerUrl"` + WorkFolder string `json:"WorkFolder"` + GitHubURL string `json:"GitHubUrl"` + UseV2Flow bool `json:"UseV2Flow"` + ServerURLV2 string `json:"ServerUrlV2"` + UseRunnerAdminFlow bool `json:"UseRunnerAdminFlow,omitempty"` } type DotnetCredentials struct { @@ -138,10 +139,32 @@ func ToRunnerInstance(fileAccess ConfigFileAccess) (*runnerconfiguration.RunnerI } ephemeral, _ := strconv.ParseBool(agent.Ephemeral) disableUpdate, _ := strconv.ParseBool(agent.DisableUpdate) + + props := protocol.PropertiesCollection{} + if agent.ServerURL != "" { + props["ServerUrl"] = protocol.PropertyValue{ + Type: "System.String", + Value: agent.ServerURL, + } + } + if agent.ServerURLV2 != "" { + props["ServerUrlV2"] = protocol.PropertyValue{ + Type: "System.String", + Value: agent.ServerURLV2, + } + } + if agent.UseV2Flow { + props["UseV2Flow"] = protocol.PropertyValue{ + Type: "System.Boolean", + Value: agent.UseV2Flow, + } + } + return &runnerconfiguration.RunnerInstance{ PoolID: poolID, Auth: &protocol.GitHubAuthResult{ TenantURL: agent.ServerURL, + UseV2FLow: agent.UseRunnerAdminFlow, }, PKey: FromRsaParameters(rsaParameters), Agent: &protocol.TaskAgent{ @@ -155,7 +178,7 @@ func ToRunnerInstance(fileAccess ConfigFileAccess) (*runnerconfiguration.RunnerI }, DisableUpdate: disableUpdate, Version: "3.0.0", - ServerV2URL: agent.ServerURLV2, + Properties: props, }, WorkFolder: agent.WorkFolder, RegistrationURL: agent.GitHubURL, @@ -163,17 +186,24 @@ func ToRunnerInstance(fileAccess ConfigFileAccess) (*runnerconfiguration.RunnerI } func FromRunnerInstance(instance *runnerconfiguration.RunnerInstance, fileAccess ConfigFileAccess) error { + useV2Flow, _ := instance.Agent.Properties.LookupBool("UseV2Flow") + serverURL, ok := instance.Agent.Properties.LookupString("ServerUrl") + if serverURL == "" || !ok { + serverURL = instance.Auth.TenantURL + } + serverV2URL, _ := instance.Agent.Properties.LookupString("ServerUrlV2") agent := &DotnetAgent{ - AgentID: fmt.Sprint(instance.Agent.ID), - AgentName: instance.Agent.Name, - Ephemeral: fmt.Sprint(instance.Agent.Ephemeral), - DisableUpdate: fmt.Sprint(instance.Agent.DisableUpdate), - PoolID: fmt.Sprint(instance.PoolID), - ServerURL: instance.Auth.TenantURL, - WorkFolder: instance.WorkFolder, - GitHubURL: instance.RegistrationURL, - UseV2Flow: instance.Auth.UseV2FLow, - ServerURLV2: instance.Agent.ServerV2URL, + AgentID: fmt.Sprint(instance.Agent.ID), + AgentName: instance.Agent.Name, + Ephemeral: fmt.Sprint(instance.Agent.Ephemeral), + DisableUpdate: fmt.Sprint(instance.Agent.DisableUpdate), + PoolID: fmt.Sprint(instance.PoolID), + ServerURL: serverURL, + WorkFolder: instance.WorkFolder, + GitHubURL: instance.RegistrationURL, + UseV2Flow: useV2Flow, + ServerURLV2: serverV2URL, + UseRunnerAdminFlow: instance.Auth.UseV2FLow, } if agent.WorkFolder == "" { agent.WorkFolder = "_work"