Skip to content

Moving to scaleset client for the controller#4390

Open
nikola-jokic wants to merge 2 commits intomasterfrom
nikola-jokic/controller-scaleset-client
Open

Moving to scaleset client for the controller#4390
nikola-jokic wants to merge 2 commits intomasterfrom
nikola-jokic/controller-scaleset-client

Conversation

@nikola-jokic
Copy link
Collaborator

No description provided.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 2, 2026

Hello! Thank you for your contribution.

Please review our contribution guidelines to understand the project's testing and code conventions.

@nikola-jokic nikola-jokic marked this pull request as ready for review March 10, 2026 00:58
@nikola-jokic nikola-jokic requested a review from mumoshu as a code owner March 10, 2026 00:58
Copilot AI review requested due to automatic review settings March 10, 2026 00:58
@nikola-jokic nikola-jokic requested review from a team, rentziass and toast-gear as code owners March 10, 2026 00:58
@nikola-jokic nikola-jokic added the gha-runner-scale-set Related to the gha-runner-scale-set mode label Mar 10, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates the actions.github.com controllers from the legacy github/actions client to the newer github.com/actions/scaleset client by introducing a scaleset-backed multi-client and updating secret resolution, controller logic, and tests accordingly.

Changes:

  • Add a new controllers/actions.github.com/multiclient scaleset-backed client cache and update controllers to use it via secretresolver.
  • Refactor secret resolution into controllers/actions.github.com/secretresolver and extract the shared ActionsGitHubObject interface into controllers/actions.github.com/object.
  • Update dependency github.com/actions/scaleset to v0.2.0 and adjust controller/resource builder code + tests for renamed fields/types.

Reviewed changes

Copilot reviewed 25 out of 26 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
main.go Wire up the new scaleset multiclient + secretresolver and switch comma-slice parsing to strings.SplitSeq.
logging/logger.go Minor formatting cleanup of zap options definition.
logger/logger.go New helper for creating *slog.Logger from level/format.
go.mod / go.sum Bump github.com/actions/scaleset to v0.2.0.
controllers/actions.github.com/secretresolver/secret_resolver.go New secretresolver package; builds proxy + custom RootCAs options and requests a multiclient per config.
controllers/actions.github.com/multiclient/multi_client.go New scaleset multiclient implementation with client caching and client construction.
controllers/actions.github.com/multiclient/fake/* New fake multiclient/client implementations for tests.
controllers/actions.github.com/object/object.go New shared ActionsGitHubObject interface extracted from old resolver.
controllers/actions.github.com/resourcebuilder.go Update types/fields to scaleset equivalents and switch SecretResolver to an interface.
controllers/actions.github.com/_controller.go Switch JIT config + client usage types to scaleset-backed interfaces.
controllers/actions.github.com/*_test.go Update tests to use new secretresolver + multiclient fakes; adjust TLS/proxy tests accordingly.
cmd/ghalistener/* Rename ConfigureUrlConfigureURL and update usages/tests; reuse new slog logger helper.
apis/actions.github.com/v1alpha1/ephemeralrunner_types.go Rename RunnerScaleSetIdRunnerScaleSetID.
.mockery.yaml / controllers/actions.github.com/mocks_test.go Update mockery config and add generated mocks for controller package.
Comments suppressed due to low confidence (4)

controllers/actions.github.com/secretresolver/secret_resolver.go:60

  • This returns an error using %v, which prevents unwrapping with errors.Is/As. Use %w when wrapping the underlying error so callers can detect specific failure causes.
    controllers/actions.github.com/secretresolver/secret_resolver.go:28
  • SecretResolver now has a logger field and WithLogger(...) option, but the logger is never read/used anywhere in this type, so the option currently has no effect. Either use sr.logger for resolver/client creation logging (and/or plumb it into the underlying scaleset client options) or remove the field/option to avoid misleading API surface.
    controllers/actions.github.com/secretresolver/secret_resolver.go:79
  • This returns an error using %v, which prevents unwrapping with errors.Is/As. Use %w when wrapping the underlying error so callers can detect specific failure causes.
    controllers/actions.github.com/secretresolver/secret_resolver.go:42
  • New(...) validates k8sClient but not scalesetMultiClient. If scalesetMultiClient is nil, GetActionsService will panic later on first use. Consider validating it in New (panic or return error) to fail fast with a clearer message.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 139 to 141
func (c *Config) Logger() (*slog.Logger, error) {
var lvl slog.Level
switch strings.ToLower(c.LogLevel) {
case "debug":
lvl = slog.LevelDebug
case "info":
lvl = slog.LevelInfo
case "warn":
lvl = slog.LevelWarn
case "error":
lvl = slog.LevelError
default:
return nil, fmt.Errorf("invalid log level: %s", c.LogLevel)
}

var logger *slog.Logger
switch c.LogFormat {
case "json":
logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
AddSource: true,
Level: lvl,
}))
case "text":
logger = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
AddSource: true,
Level: lvl,
}))
default:
return nil, fmt.Errorf("invalid log format: %s", c.LogFormat)
}

return logger.With("app", appName), nil
return logger.New(c.LogLevel, c.LogFormat)
}
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Config.Logger() used to add an app attribute (via logger.With("app", appName)), but now returns the base logger without that context. If log consumers rely on the app field, this is a behavior regression. Consider returning logger.New(...).With("app", appName) (or equivalent) to preserve the prior structured log field.

Copilot uses AI. Check for mistakes.
Comment on lines +72 to +74
entry, ok := m.clients[identifier]
if ok && entry.rootCAs.Equal(opts.RootCAs) {
return entry.client, nil
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

entry.rootCAs.Equal(opts.RootCAs) will panic when a cached entry has rootCAs == nil (the common case when no custom CA is configured). Handle nils explicitly (e.g., treat both nil as equal; otherwise compare non-nil pools) before calling Equal.

Copilot uses AI. Check for mistakes.
Comment on lines +110 to +114
identifier := fmt.Sprintf("configURL:%q,namespace:%q", o.GithubConfigURL, o.Namespace)

if o.AppConfig.Token != "" {
identifier += fmt.Sprintf("token:%q,", o.AppConfig.Token)
} else {
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The identifier string is missing a delimiter after the namespace (fmt.Sprintf("configURL:%q,namespace:%q", ...)). Appending token:... / appID:... immediately after can create ambiguous identifiers and result in cache collisions. Add a trailing separator (e.g., a comma) after the namespace segment (and/or before appending auth fields).

Copilot uses AI. Check for mistakes.
Comment on lines +100 to +103
func (o *ClientForOptions) identifier() (string, error) {
if err := o.AppConfig.Validate(); err != nil {
return "", fmt.Errorf("failed to validate app config: %w", err)
}
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

identifier() assumes o.AppConfig is non-nil and calls o.AppConfig.Validate(). If a caller passes nil (or constructs ClientForOptions without AppConfig), this will panic. Consider validating o.AppConfig != nil and returning a descriptive error instead.

Copilot uses AI. Check for mistakes.
Comment on lines +90 to +96
type ClientForOptions struct {
GithubConfigURL string
AppConfig *appconfig.AppConfig
Namespace string
RootCAs *x509.CertPool
ProxyFunc func(*http.Request) (*url.URL, error)

Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ClientForOptions includes ProxyFunc, but proxy configuration is not included in identifier(). If two objects share config URL/namespace/creds but differ in proxy settings, the cached client can be reused with the wrong proxy transport. Include proxy configuration (or a stable hash) in the cache key/identifier.

Copilot uses AI. Check for mistakes.
Comment on lines +141 to +143
options := []scaleset.HTTPOption{
scaleset.WithLogger(o.logger),
}
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

newClient() always passes scaleset.WithLogger(o.logger), but ClientForOptions.logger is unexported and never set by callers, so it will always be nil. If scaleset.WithLogger doesn’t tolerate nil this can panic; even if it does, it silently disables HTTP client logging. Consider defaulting to slog.New(slog.DiscardHandler) when o.logger is nil and/or provide an exported option to set the logger.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gha-runner-scale-set Related to the gha-runner-scale-set mode

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants