Moving to scaleset client for the controller

This commit is contained in:
Nikola Jokic
2026-02-24 20:14:48 +01:00
parent 1d9f626c53
commit d4a29624fa
24 changed files with 2684 additions and 339 deletions

View File

@@ -15,7 +15,8 @@ import (
logf "sigs.k8s.io/controller-runtime/pkg/log"
ghalistenerconfig "github.com/actions/actions-runner-controller/cmd/ghalistener/config"
"github.com/actions/actions-runner-controller/github/actions/fake"
scalefake "github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient/fake"
"github.com/actions/actions-runner-controller/controllers/actions.github.com/secretresolver"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
kerrors "k8s.io/apimachinery/pkg/api/errors"
@@ -43,7 +44,10 @@ var _ = Describe("Test AutoScalingListener controller", func() {
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
secretResolver := NewSecretResolver(mgr.GetClient(), fake.NewMultiClient())
secretResolver := secretresolver.New(
mgr.GetClient(),
scalefake.NewMultiClient(),
)
rb := ResourceBuilder{
SecretResolver: secretResolver,
@@ -459,7 +463,7 @@ var _ = Describe("Test AutoScalingListener customization", func() {
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
secretResolver := NewSecretResolver(mgr.GetClient(), fake.NewMultiClient())
secretResolver := secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient())
rb := ResourceBuilder{
SecretResolver: secretResolver,
@@ -788,7 +792,7 @@ var _ = Describe("Test AutoScalingListener controller with proxy", func() {
ctx = context.Background()
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
secretResolver := NewSecretResolver(mgr.GetClient(), fake.NewMultiClient())
secretResolver := secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient())
rb := ResourceBuilder{
SecretResolver: secretResolver,
@@ -991,7 +995,7 @@ var _ = Describe("Test AutoScalingListener controller with template modification
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
secretResolver := NewSecretResolver(mgr.GetClient(), fake.NewMultiClient())
secretResolver := secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient())
rb := ResourceBuilder{
SecretResolver: secretResolver,
@@ -1094,7 +1098,7 @@ var _ = Describe("Test GitHub Server TLS configuration", func() {
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
secretResolver := NewSecretResolver(mgr.GetClient(), fake.NewMultiClient())
secretResolver := secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient())
rb := ResourceBuilder{
SecretResolver: secretResolver,

View File

@@ -25,7 +25,7 @@ import (
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
"github.com/actions/actions-runner-controller/build"
"github.com/actions/actions-runner-controller/github/actions"
"github.com/actions/scaleset"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
@@ -78,7 +78,6 @@ type AutoscalingRunnerSetReconciler struct {
DefaultRunnerScaleSetListenerImage string
DefaultRunnerScaleSetListenerImagePullSecrets []string
UpdateStrategy UpdateStrategy
ActionsClient actions.MultiClient
ResourceBuilder
}
@@ -427,17 +426,16 @@ func (r *AutoscalingRunnerSetReconciler) createRunnerScaleSet(ctx context.Contex
if runnerScaleSet == nil {
runnerScaleSet, err = actionsClient.CreateRunnerScaleSet(
ctx,
&actions.RunnerScaleSet{
&scaleset.RunnerScaleSet{
Name: autoscalingRunnerSet.Spec.RunnerScaleSetName,
RunnerGroupId: runnerGroupID,
Labels: []actions.Label{
RunnerGroupID: runnerGroupID,
Labels: []scaleset.Label{
{
Name: autoscalingRunnerSet.Spec.RunnerScaleSetName,
Type: "System",
},
},
RunnerSetting: actions.RunnerSetting{
Ephemeral: true,
RunnerSetting: scaleset.RunnerSetting{
DisableUpdate: true,
},
})
@@ -447,15 +445,11 @@ func (r *AutoscalingRunnerSetReconciler) createRunnerScaleSet(ctx context.Contex
}
}
actionsClient.SetUserAgent(actions.UserAgentInfo{
Version: build.Version,
CommitSHA: build.CommitSHA,
ScaleSetID: runnerScaleSet.Id,
HasProxy: autoscalingRunnerSet.Spec.Proxy != nil,
Subsystem: "controller",
})
info := actionsClient.SystemInfo()
info.ScaleSetID = runnerScaleSet.ID
actionsClient.SetSystemInfo(info)
logger.Info("Created/Reused a runner scale set", "id", runnerScaleSet.Id, "runnerGroupName", runnerScaleSet.RunnerGroupName)
logger.Info("Created/Reused a runner scale set", "id", runnerScaleSet.ID, "runnerGroupName", runnerScaleSet.RunnerGroupName)
if autoscalingRunnerSet.Annotations == nil {
autoscalingRunnerSet.Annotations = map[string]string{}
}
@@ -466,7 +460,7 @@ func (r *AutoscalingRunnerSetReconciler) createRunnerScaleSet(ctx context.Contex
logger.Info("Adding runner scale set ID, name and runner group name as an annotation and url labels")
if err = patch(ctx, r.Client, autoscalingRunnerSet, func(obj *v1alpha1.AutoscalingRunnerSet) {
obj.Annotations[AnnotationKeyGitHubRunnerScaleSetName] = runnerScaleSet.Name
obj.Annotations[runnerScaleSetIDAnnotationKey] = strconv.Itoa(runnerScaleSet.Id)
obj.Annotations[runnerScaleSetIDAnnotationKey] = strconv.Itoa(runnerScaleSet.ID)
obj.Annotations[AnnotationKeyGitHubRunnerGroupName] = runnerScaleSet.RunnerGroupName
if err := applyGitHubURLLabels(obj.Spec.GitHubConfigUrl, obj.Labels); err != nil { // should never happen
logger.Error(err, "Failed to apply GitHub URL labels")
@@ -477,7 +471,7 @@ func (r *AutoscalingRunnerSetReconciler) createRunnerScaleSet(ctx context.Contex
}
logger.Info("Updated with runner scale set ID, name and runner group name as an annotation",
"id", runnerScaleSet.Id,
"id", runnerScaleSet.ID,
"name", runnerScaleSet.Name,
"runnerGroupName", runnerScaleSet.RunnerGroupName)
return ctrl.Result{}, nil
@@ -507,7 +501,7 @@ func (r *AutoscalingRunnerSetReconciler) updateRunnerScaleSetRunnerGroup(ctx con
runnerGroupID = int(runnerGroup.ID)
}
updatedRunnerScaleSet, err := actionsClient.UpdateRunnerScaleSet(ctx, runnerScaleSetID, &actions.RunnerScaleSet{RunnerGroupId: runnerGroupID})
updatedRunnerScaleSet, err := actionsClient.UpdateRunnerScaleSet(ctx, runnerScaleSetID, &scaleset.RunnerScaleSet{RunnerGroupID: runnerGroupID})
if err != nil {
logger.Error(err, "Failed to update runner scale set", "runnerScaleSetId", runnerScaleSetID)
return ctrl.Result{}, err
@@ -544,7 +538,7 @@ func (r *AutoscalingRunnerSetReconciler) updateRunnerScaleSetName(ctx context.Co
return ctrl.Result{}, err
}
updatedRunnerScaleSet, err := actionsClient.UpdateRunnerScaleSet(ctx, runnerScaleSetID, &actions.RunnerScaleSet{Name: autoscalingRunnerSet.Spec.RunnerScaleSetName})
updatedRunnerScaleSet, err := actionsClient.UpdateRunnerScaleSet(ctx, runnerScaleSetID, &scaleset.RunnerScaleSet{Name: autoscalingRunnerSet.Spec.RunnerScaleSetName})
if err != nil {
logger.Error(err, "Failed to update runner scale set", "runnerScaleSetId", runnerScaleSetID)
return ctrl.Result{}, err

View File

@@ -3,13 +3,12 @@ package actionsgithubcom
import (
"context"
"crypto/tls"
"encoding/base64"
"fmt"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"sync"
"time"
corev1 "k8s.io/api/core/v1"
@@ -19,7 +18,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"github.com/go-logr/logr"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/api/errors"
@@ -28,9 +26,10 @@ import (
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
"github.com/actions/actions-runner-controller/build"
"github.com/actions/actions-runner-controller/github/actions"
"github.com/actions/actions-runner-controller/github/actions/fake"
"github.com/actions/actions-runner-controller/github/actions/testserver"
"github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient"
scalefake "github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient/fake"
"github.com/actions/actions-runner-controller/controllers/actions.github.com/secretresolver"
"github.com/actions/scaleset"
)
const (
@@ -63,6 +62,10 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
// Track runner group mappings for dynamic responses
runnerGroupMap := map[int]string{1: "testgroup"} // ID -> Name mapping
runnerGroupMapLock := &sync.RWMutex{} // Thread-safe access
controller = &AutoscalingRunnerSetReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
@@ -70,10 +73,30 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
ControllerNamespace: autoscalingNS.Name,
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
ResourceBuilder: ResourceBuilder{
SecretResolver: &SecretResolver{
k8sClient: k8sClient,
multiClient: fake.NewMultiClient(),
},
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient(
scalefake.WithClient(
scalefake.NewClient(
scalefake.WithGetRunnerGroupByNameFunc(func(ctx context.Context, groupName string) (*scaleset.RunnerGroup, error) {
// Support both "testgroup" and "testgroup2"
// Update the mapping when a new group is requested
runnerGroupMapLock.Lock()
runnerGroupMap[1] = groupName
runnerGroupMapLock.Unlock()
return &scaleset.RunnerGroup{ID: 1, Name: groupName}, nil
}),
scalefake.WithGetRunnerScaleSet(nil, nil),
scalefake.WithCreateRunnerScaleSet(&scaleset.RunnerScaleSet{ID: 1, Name: "test-asrs", RunnerGroupID: 1, RunnerGroupName: "testgroup"}, nil),
scalefake.WithUpdateRunnerScaleSetFunc(func(ctx context.Context, scaleSetID int, rs *scaleset.RunnerScaleSet) (*scaleset.RunnerScaleSet, error) {
// Return a RunnerScaleSet with the group name corresponding to the runner group ID
runnerGroupMapLock.RLock()
groupName := runnerGroupMap[rs.RunnerGroupID]
runnerGroupMapLock.RUnlock()
return &scaleset.RunnerScaleSet{ID: 1, Name: "test-asrs", RunnerGroupID: rs.RunnerGroupID, RunnerGroupName: groupName}, nil
}),
scalefake.WithDeleteRunnerScaleSet(nil),
),
),
)),
},
}
err := controller.SetupWithManager(mgr)
@@ -681,25 +704,34 @@ var _ = Describe("Test AutoScalingController updates", Ordered, func() {
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
multiClient := fake.NewMultiClient(
fake.WithDefaultClient(
fake.NewFakeClient(
fake.WithUpdateRunnerScaleSet(
&actions.RunnerScaleSet{
Id: 1,
multiClient := scalefake.NewMultiClient(
scalefake.WithClient(
scalefake.NewClient(
scalefake.WithGenerateJitRunnerConfig(
&scaleset.RunnerScaleSetJitRunnerConfig{
Runner: &scaleset.RunnerReference{ID: 1, Name: "test-runner"},
EncodedJITConfig: "fake-jit-config",
},
nil,
),
scalefake.WithGetRunnerGroupByName(&scaleset.RunnerGroup{ID: 1, Name: "testgroup"}, nil),
scalefake.WithGetRunnerScaleSet(nil, nil),
scalefake.WithCreateRunnerScaleSet(&scaleset.RunnerScaleSet{ID: 1, Name: "testset", RunnerGroupID: 1, RunnerGroupName: "testgroup"}, nil),
scalefake.WithUpdateRunnerScaleSet(
&scaleset.RunnerScaleSet{
ID: 1,
Name: "testset_update",
RunnerGroupId: 1,
RunnerGroupID: 1,
RunnerGroupName: "testgroup",
Labels: []actions.Label{{Type: "test", Name: "test"}},
RunnerSetting: actions.RunnerSetting{},
Labels: []scaleset.Label{{Type: "test", Name: "test"}},
RunnerSetting: scaleset.RunnerSetting{},
CreatedOn: time.Now(),
RunnerJitConfigUrl: "test.test.test",
RunnerJitConfigURL: "test.test.test",
Statistics: nil,
},
nil,
),
),
nil,
),
)
@@ -710,10 +742,7 @@ var _ = Describe("Test AutoScalingController updates", Ordered, func() {
ControllerNamespace: autoscalingNS.Name,
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
ResourceBuilder: ResourceBuilder{
SecretResolver: &SecretResolver{
k8sClient: k8sClient,
multiClient: multiClient,
},
SecretResolver: secretresolver.New(mgr.GetClient(), multiClient),
},
}
err := controller.SetupWithManager(mgr)
@@ -830,10 +859,7 @@ var _ = Describe("Test AutoscalingController creation failures", Ordered, func()
ControllerNamespace: autoscalingNS.Name,
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
ResourceBuilder: ResourceBuilder{
SecretResolver: &SecretResolver{
k8sClient: k8sClient,
multiClient: fake.NewMultiClient(),
},
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient()),
},
}
err := controller.SetupWithManager(mgr)
@@ -953,7 +979,6 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
ctx = context.Background()
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
multiClient := actions.NewMultiClient(logr.Discard())
controller = &AutoscalingRunnerSetReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
@@ -961,10 +986,7 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
ControllerNamespace: autoscalingNS.Name,
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
ResourceBuilder: ResourceBuilder{
SecretResolver: &SecretResolver{
k8sClient: k8sClient,
multiClient: multiClient,
},
SecretResolver: secretresolver.New(mgr.GetClient(), multiclient.NewScaleset()),
},
}
@@ -976,10 +998,11 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
It("should be able to make requests to a server using a proxy", func() {
serverSuccessfullyCalled := false
proxy := testserver.New(GinkgoT(), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
serverSuccessfullyCalled = true
w.WriteHeader(http.StatusOK)
}))
defer proxy.Close()
min := 1
max := 10
@@ -1029,23 +1052,17 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
})
It("should be able to make requests to a server using a proxy with user info", func() {
serverSuccessfullyCalled := false
proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
header := r.Header.Get("Proxy-Authorization")
Expect(header).NotTo(BeEmpty())
header = strings.TrimPrefix(header, "Basic ")
decoded, err := base64.StdEncoding.DecodeString(header)
Expect(err).NotTo(HaveOccurred())
Expect(string(decoded)).To(Equal("test:password"))
serverSuccessfullyCalled = true
w.WriteHeader(http.StatusOK)
}))
GinkgoT().Cleanup(func() {
proxy.Close()
})
controller.ResourceBuilder.SecretResolver = secretresolver.New(k8sClient, scalefake.NewMultiClient(
scalefake.WithClient(
scalefake.NewClient(
scalefake.WithGetRunnerGroupByName(&scaleset.RunnerGroup{ID: 1, Name: "testgroup"}, nil),
scalefake.WithGetRunnerScaleSet(nil, nil),
scalefake.WithCreateRunnerScaleSet(&scaleset.RunnerScaleSet{ID: 1, Name: "test-asrs", RunnerGroupID: 1, RunnerGroupName: "testgroup"}, nil),
scalefake.WithUpdateRunnerScaleSet(&scaleset.RunnerScaleSet{ID: 1, Name: "test-asrs", RunnerGroupID: 1, RunnerGroupName: "testgroup"}, nil),
scalefake.WithDeleteRunnerScaleSet(nil),
),
),
))
secretCredentials := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "proxy-credentials",
@@ -1057,6 +1074,11 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
},
}
proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
defer proxy.Close()
err := k8sClient.Create(ctx, secretCredentials)
Expect(err).NotTo(HaveOccurred(), "failed to create secret credentials")
@@ -1078,7 +1100,7 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
RunnerGroup: "testgroup",
Proxy: &v1alpha1.ProxyConfig{
HTTP: &v1alpha1.ProxyServerConfig{
Url: proxy.URL,
Url: "http://test:password@" + proxy.Listener.Addr().String(),
CredentialSecretRef: "proxy-credentials",
},
},
@@ -1098,14 +1120,24 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
err = k8sClient.Create(ctx, autoscalingRunnerSet)
Expect(err).NotTo(HaveOccurred(), "failed to create AutoScalingRunnerSet")
// wait for server to be called
// Verify proxy config with credentials is propagated to EphemeralRunnerSet
Eventually(
func() (bool, error) {
return serverSuccessfullyCalled, nil
func() (*v1alpha1.EphemeralRunnerSet, error) {
runnerSetList := new(v1alpha1.EphemeralRunnerSetList)
err := k8sClient.List(ctx, runnerSetList, client.InNamespace(autoscalingNS.Name))
if err != nil || len(runnerSetList.Items) == 0 {
return nil, err
}
return &runnerSetList.Items[0], nil
},
autoscalingRunnerSetTestTimeout,
1*time.Nanosecond,
).Should(BeTrue(), "server was not called")
autoscalingRunnerSetTestInterval,
).Should(WithTransform(func(ers *v1alpha1.EphemeralRunnerSet) *v1alpha1.ProxyConfig {
if ers != nil {
return ers.Spec.EphemeralRunnerSpec.Proxy
}
return nil
}, Not(BeNil())), "EphemeralRunnerSet should have proxy configuration with credentials")
})
})
@@ -1149,10 +1181,17 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
ControllerNamespace: autoscalingNS.Name,
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
ResourceBuilder: ResourceBuilder{
SecretResolver: &SecretResolver{
k8sClient: k8sClient,
multiClient: fake.NewMultiClient(),
},
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient(
scalefake.WithClient(
scalefake.NewClient(
scalefake.WithGetRunnerGroupByName(&scaleset.RunnerGroup{ID: 1, Name: "testgroup"}, nil),
scalefake.WithGetRunnerScaleSet(nil, nil),
scalefake.WithCreateRunnerScaleSet(&scaleset.RunnerScaleSet{ID: 1, Name: "test-asrs", RunnerGroupID: 1, RunnerGroupName: "testgroup"}, nil),
scalefake.WithUpdateRunnerScaleSet(&scaleset.RunnerScaleSet{ID: 1, Name: "test-asrs", RunnerGroupID: 1, RunnerGroupName: "testgroup"}, nil),
scalefake.WithDeleteRunnerScaleSet(nil),
),
),
)),
},
}
err = controller.SetupWithManager(mgr)
@@ -1162,10 +1201,7 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
})
It("should be able to make requests to a server using root CAs", func() {
controller.SecretResolver = &SecretResolver{
k8sClient: k8sClient,
multiClient: actions.NewMultiClient(logr.Discard()),
}
controller.SecretResolver = secretresolver.New(k8sClient, multiclient.NewScaleset())
certsFolder := filepath.Join(
"../../",
@@ -1177,7 +1213,7 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
keyPath := filepath.Join(certsFolder, "server.key")
serverSuccessfullyCalled := false
server := testserver.NewUnstarted(GinkgoT(), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
serverSuccessfullyCalled = true
w.WriteHeader(http.StatusOK)
}))
@@ -1186,6 +1222,7 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
server.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
server.StartTLS()
defer server.Close()
min := 1
max := 10
@@ -1198,7 +1235,7 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
},
},
Spec: v1alpha1.AutoscalingRunnerSetSpec{
GitHubConfigUrl: server.ConfigURLForOrg("my-org"),
GitHubConfigUrl: server.URL + "/my-org",
GitHubConfigSecret: configSecret.Name,
GitHubServerTLS: &v1alpha1.TLSConfig{
CertificateFrom: &v1alpha1.TLSCertificateSource{
@@ -1391,10 +1428,7 @@ var _ = Describe("Test external permissions cleanup", Ordered, func() {
ControllerNamespace: autoscalingNS.Name,
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
ResourceBuilder: ResourceBuilder{
SecretResolver: &SecretResolver{
k8sClient: k8sClient,
multiClient: fake.NewMultiClient(),
},
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient()),
},
}
err := controller.SetupWithManager(mgr)
@@ -1554,10 +1588,7 @@ var _ = Describe("Test external permissions cleanup", Ordered, func() {
ControllerNamespace: autoscalingNS.Name,
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
ResourceBuilder: ResourceBuilder{
SecretResolver: &SecretResolver{
k8sClient: k8sClient,
multiClient: fake.NewMultiClient(),
},
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient()),
},
}
err := controller.SetupWithManager(mgr)
@@ -1767,10 +1798,7 @@ var _ = Describe("Test resource version and build version mismatch", func() {
ControllerNamespace: autoscalingNS.Name,
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
ResourceBuilder: ResourceBuilder{
SecretResolver: &SecretResolver{
k8sClient: k8sClient,
multiClient: fake.NewMultiClient(),
},
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient()),
},
}
err := controller.SetupWithManager(mgr)

View File

@@ -6,7 +6,7 @@ import (
kclient "sigs.k8s.io/controller-runtime/pkg/client"
)
type object[T kclient.Object] interface {
type kubernetesObject[T kclient.Object] interface {
kclient.Object
DeepCopy() T
}
@@ -15,7 +15,7 @@ type patcher interface {
Patch(ctx context.Context, obj kclient.Object, patch kclient.Patch, opts ...kclient.PatchOption) error
}
func patch[T object[T]](ctx context.Context, client patcher, obj T, update func(obj T)) error {
func patch[T kubernetesObject[T]](ctx context.Context, client patcher, obj T, update func(obj T)) error {
original := obj.DeepCopy()
update(obj)
return client.Patch(ctx, obj, kclient.MergeFrom(original))
@@ -25,7 +25,7 @@ type subResourcePatcher interface {
Patch(ctx context.Context, obj kclient.Object, patch kclient.Patch, opts ...kclient.SubResourcePatchOption) error
}
func patchSubResource[T object[T]](ctx context.Context, client subResourcePatcher, obj T, update func(obj T)) error {
func patchSubResource[T kubernetesObject[T]](ctx context.Context, client subResourcePatcher, obj T, update func(obj T)) error {
original := obj.DeepCopy()
update(obj)
return client.Patch(ctx, obj, kclient.MergeFrom(original))

View File

@@ -27,6 +27,7 @@ import (
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
"github.com/actions/actions-runner-controller/github/actions"
"github.com/actions/scaleset"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
@@ -599,7 +600,7 @@ func (r *EphemeralRunnerReconciler) deletePodAsFailed(ctx context.Context, ephem
return nil
}
func (r *EphemeralRunnerReconciler) createRunnerJitConfig(ctx context.Context, ephemeralRunner *v1alpha1.EphemeralRunner, log logr.Logger) (*actions.RunnerScaleSetJitRunnerConfig, error) {
func (r *EphemeralRunnerReconciler) createRunnerJitConfig(ctx context.Context, ephemeralRunner *v1alpha1.EphemeralRunner, log logr.Logger) (*scaleset.RunnerScaleSetJitRunnerConfig, error) {
// Runner is not registered with the service. We need to register it first
log.Info("Creating ephemeral runner JIT config")
actionsClient, err := r.GetActionsService(ctx, ephemeralRunner)
@@ -607,7 +608,7 @@ func (r *EphemeralRunnerReconciler) createRunnerJitConfig(ctx context.Context, e
return nil, fmt.Errorf("failed to get actions client for generating JIT config: %w", err)
}
jitSettings := &actions.RunnerScaleSetJitRunnerSetting{
jitSettings := &scaleset.RunnerScaleSetJitRunnerSetting{
Name: ephemeralRunner.Name,
}
@@ -618,9 +619,9 @@ func (r *EphemeralRunnerReconciler) createRunnerJitConfig(ctx context.Context, e
}
}
jitConfig, err := actionsClient.GenerateJitRunnerConfig(ctx, jitSettings, ephemeralRunner.Spec.RunnerScaleSetId)
jitConfig, err := actionsClient.GenerateJitRunnerConfig(ctx, jitSettings, ephemeralRunner.Spec.RunnerScaleSetID)
if err == nil { // if NO error
log.Info("Created ephemeral runner JIT config", "runnerId", jitConfig.Runner.Id)
log.Info("Created ephemeral runner JIT config", "runnerId", jitConfig.Runner.ID)
return jitConfig, nil
}
@@ -652,10 +653,10 @@ func (r *EphemeralRunnerReconciler) createRunnerJitConfig(ctx context.Context, e
return nil, fmt.Errorf("%w: runner existed, retry configuration", retryableError)
}
log.Info("Found the runner with the same name", "runnerId", existingRunner.Id, "runnerScaleSetId", existingRunner.RunnerScaleSetId)
if existingRunner.RunnerScaleSetId == ephemeralRunner.Spec.RunnerScaleSetId {
log.Info("Found the runner with the same name", "runnerId", existingRunner.ID, "runnerScaleSetId", existingRunner.RunnerScaleSetID)
if existingRunner.RunnerScaleSetID == ephemeralRunner.Spec.RunnerScaleSetID {
log.Info("Removing the runner with the same name")
err := actionsClient.RemoveRunner(ctx, int64(existingRunner.Id))
err := actionsClient.RemoveRunner(ctx, int64(existingRunner.ID))
if err != nil {
return nil, fmt.Errorf("failed to remove runner from the service: %w", err)
}
@@ -731,7 +732,7 @@ func (r *EphemeralRunnerReconciler) createPod(ctx context.Context, runner *v1alp
}
log.Info("Created ephemeral runner pod",
"runnerScaleSetId", runner.Spec.RunnerScaleSetId,
"runnerScaleSetId", runner.Spec.RunnerScaleSetID,
"runnerName", runner.Status.RunnerName,
"runnerId", runner.Status.RunnerId,
"configUrl", runner.Spec.GitHubConfigUrl,
@@ -740,7 +741,7 @@ func (r *EphemeralRunnerReconciler) createPod(ctx context.Context, runner *v1alp
return ctrl.Result{}, nil
}
func (r *EphemeralRunnerReconciler) createSecret(ctx context.Context, runner *v1alpha1.EphemeralRunner, jitConfig *actions.RunnerScaleSetJitRunnerConfig, log logr.Logger) (*corev1.Secret, error) {
func (r *EphemeralRunnerReconciler) createSecret(ctx context.Context, runner *v1alpha1.EphemeralRunner, jitConfig *scaleset.RunnerScaleSetJitRunnerConfig, log logr.Logger) (*corev1.Secret, error) {
log.Info("Creating new secret for ephemeral runner")
jitSecret := r.newEphemeralRunnerJitSecret(runner, jitConfig)

View File

@@ -14,10 +14,11 @@ import (
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
"github.com/actions/actions-runner-controller/github/actions"
"github.com/go-logr/logr"
"github.com/actions/actions-runner-controller/github/actions/fake"
"github.com/actions/actions-runner-controller/github/actions/testserver"
"github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient"
scalefake "github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient/fake"
"github.com/actions/actions-runner-controller/controllers/actions.github.com/secretresolver"
"github.com/actions/scaleset"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
@@ -43,7 +44,7 @@ func newExampleRunner(name, namespace, configSecretName string) *v1alpha1.Epheme
Spec: v1alpha1.EphemeralRunnerSpec{
GitHubConfigUrl: "https://github.com/owner/repo",
GitHubConfigSecret: configSecretName,
RunnerScaleSetId: 1,
RunnerScaleSetID: 1,
PodTemplateSpec: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
@@ -111,10 +112,19 @@ var _ = Describe("EphemeralRunner", func() {
Scheme: mgr.GetScheme(),
Log: logf.Log,
ResourceBuilder: ResourceBuilder{
SecretResolver: &SecretResolver{
k8sClient: mgr.GetClient(),
multiClient: fake.NewMultiClient(),
},
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient(
scalefake.WithClient(
scalefake.NewClient(
scalefake.WithGenerateJitRunnerConfig(
&scaleset.RunnerScaleSetJitRunnerConfig{
Runner: &scaleset.RunnerReference{ID: 1, Name: "test-runner"},
EncodedJITConfig: "fake-jit-config",
},
nil,
),
),
),
)),
},
}
@@ -1096,12 +1106,12 @@ var _ = Describe("EphemeralRunner", func() {
Scheme: mgr.GetScheme(),
Log: logf.Log,
ResourceBuilder: ResourceBuilder{
SecretResolver: &SecretResolver{
k8sClient: mgr.GetClient(),
multiClient: fake.NewMultiClient(
fake.WithDefaultClient(
fake.NewFakeClient(
fake.WithGetRunner(
SecretResolver: secretresolver.New(
mgr.GetClient(),
scalefake.NewMultiClient(
scalefake.WithClient(
scalefake.NewClient(
scalefake.WithGetRunner(
nil,
&actions.ActionsError{
StatusCode: http.StatusNotFound,
@@ -1110,11 +1120,17 @@ var _ = Describe("EphemeralRunner", func() {
},
},
),
scalefake.WithGenerateJitRunnerConfig(
&scaleset.RunnerScaleSetJitRunnerConfig{
Runner: &scaleset.RunnerReference{ID: 1, Name: "test-runner"},
EncodedJITConfig: "fake-jit-config",
},
nil,
),
),
nil,
),
),
},
),
},
}
err := controller.SetupWithManager(mgr)
@@ -1181,10 +1197,19 @@ var _ = Describe("EphemeralRunner", func() {
Scheme: mgr.GetScheme(),
Log: logf.Log,
ResourceBuilder: ResourceBuilder{
SecretResolver: &SecretResolver{
k8sClient: mgr.GetClient(),
multiClient: fake.NewMultiClient(),
},
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient(
scalefake.WithClient(
scalefake.NewClient(
scalefake.WithGenerateJitRunnerConfig(
&scaleset.RunnerScaleSetJitRunnerConfig{
Runner: &scaleset.RunnerReference{ID: 1, Name: "test-runner"},
EncodedJITConfig: "fake-jit-config",
},
nil,
),
),
),
)),
},
}
err := controller.SetupWithManager(mgr)
@@ -1196,10 +1221,10 @@ var _ = Describe("EphemeralRunner", func() {
It("uses an actions client with proxy transport", func() {
// Use an actual client
controller.ResourceBuilder = ResourceBuilder{
SecretResolver: &SecretResolver{
k8sClient: mgr.GetClient(),
multiClient: actions.NewMultiClient(logr.Discard()),
},
SecretResolver: secretresolver.New(
mgr.GetClient(),
multiclient.NewScaleset(),
),
}
proxySuccessfulllyCalled := false
@@ -1355,10 +1380,7 @@ var _ = Describe("EphemeralRunner", func() {
Scheme: mgr.GetScheme(),
Log: logf.Log,
ResourceBuilder: ResourceBuilder{
SecretResolver: &SecretResolver{
k8sClient: mgr.GetClient(),
multiClient: fake.NewMultiClient(),
},
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient()),
},
}
@@ -1379,7 +1401,7 @@ var _ = Describe("EphemeralRunner", func() {
keyPath := filepath.Join(certsFolder, "server.key")
serverSuccessfullyCalled := false
server := testserver.NewUnstarted(GinkgoT(), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
serverSuccessfullyCalled = true
w.WriteHeader(http.StatusOK)
}))
@@ -1388,17 +1410,18 @@ var _ = Describe("EphemeralRunner", func() {
server.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
server.StartTLS()
defer server.Close()
// Use an actual client
controller.ResourceBuilder = ResourceBuilder{
SecretResolver: &SecretResolver{
k8sClient: mgr.GetClient(),
multiClient: actions.NewMultiClient(logr.Discard()),
},
SecretResolver: secretresolver.New(
mgr.GetClient(),
multiclient.NewScaleset(),
),
}
ephemeralRunner := newExampleRunner("test-runner", autoScalingNS.Name, configSecret.Name)
ephemeralRunner.Spec.GitHubConfigUrl = server.ConfigURLForOrg("my-org")
ephemeralRunner.Spec.GitHubConfigUrl = server.URL + "/my-org"
ephemeralRunner.Spec.GitHubServerTLS = &v1alpha1.TLSConfig{
CertificateFrom: &v1alpha1.TLSCertificateSource{
ConfigMapKeyRef: &corev1.ConfigMapKeySelector{

View File

@@ -26,6 +26,7 @@ import (
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
"github.com/actions/actions-runner-controller/controllers/actions.github.com/metrics"
"github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient"
"github.com/actions/actions-runner-controller/github/actions"
"github.com/go-logr/logr"
"go.uber.org/multierr"
@@ -481,7 +482,7 @@ func (r *EphemeralRunnerSetReconciler) deleteIdleEphemeralRunners(ctx context.Co
return multierr.Combine(errs...)
}
func (r *EphemeralRunnerSetReconciler) deleteEphemeralRunnerWithActionsClient(ctx context.Context, ephemeralRunner *v1alpha1.EphemeralRunner, actionsClient actions.ActionsService, log logr.Logger) (bool, error) {
func (r *EphemeralRunnerSetReconciler) deleteEphemeralRunnerWithActionsClient(ctx context.Context, ephemeralRunner *v1alpha1.EphemeralRunner, actionsClient multiclient.Client, log logr.Logger) (bool, error) {
if err := actionsClient.RemoveRunner(ctx, int64(ephemeralRunner.Status.RunnerId)); err != nil {
actionsError := &actions.ActionsError{}
if !errors.As(err, &actionsError) {

View File

@@ -2,7 +2,6 @@ package actionsgithubcom
import (
"context"
"crypto/tls"
"encoding/base64"
"fmt"
"net/http"
@@ -19,16 +18,15 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"github.com/go-logr/logr"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
"github.com/actions/actions-runner-controller/github/actions"
"github.com/actions/actions-runner-controller/github/actions/fake"
"github.com/actions/actions-runner-controller/github/actions/testserver"
"github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient"
fake "github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient/fake"
"github.com/actions/actions-runner-controller/controllers/actions.github.com/secretresolver"
)
const (
@@ -57,10 +55,13 @@ var _ = Describe("Test EphemeralRunnerSet controller", func() {
Scheme: mgr.GetScheme(),
Log: logf.Log,
ResourceBuilder: ResourceBuilder{
SecretResolver: &SecretResolver{
k8sClient: mgr.GetClient(),
multiClient: fake.NewMultiClient(),
},
SecretResolver: secretresolver.New(mgr.GetClient(), fake.NewMultiClient(
fake.WithClient(
fake.NewClient(
fake.WithRemoveRunner(nil),
),
),
)),
},
}
err := controller.SetupWithManager(mgr)
@@ -75,7 +76,7 @@ var _ = Describe("Test EphemeralRunnerSet controller", func() {
EphemeralRunnerSpec: v1alpha1.EphemeralRunnerSpec{
GitHubConfigUrl: "https://github.com/owner/repo",
GitHubConfigSecret: configSecret.Name,
RunnerScaleSetId: 100,
RunnerScaleSetID: 100,
PodTemplateSpec: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
@@ -1103,10 +1104,7 @@ var _ = Describe("Test EphemeralRunnerSet controller with proxy settings", func(
Scheme: mgr.GetScheme(),
Log: logf.Log,
ResourceBuilder: ResourceBuilder{
SecretResolver: &SecretResolver{
k8sClient: mgr.GetClient(),
multiClient: actions.NewMultiClient(logr.Discard()),
},
SecretResolver: secretresolver.New(mgr.GetClient(), multiclient.NewScaleset()),
},
}
err := controller.SetupWithManager(mgr)
@@ -1140,7 +1138,7 @@ var _ = Describe("Test EphemeralRunnerSet controller with proxy settings", func(
EphemeralRunnerSpec: v1alpha1.EphemeralRunnerSpec{
GitHubConfigUrl: "http://example.com/owner/repo",
GitHubConfigSecret: configSecret.Name,
RunnerScaleSetId: 100,
RunnerScaleSetID: 100,
Proxy: &v1alpha1.ProxyConfig{
HTTP: &v1alpha1.ProxyServerConfig{
Url: "http://proxy.example.com",
@@ -1319,7 +1317,7 @@ var _ = Describe("Test EphemeralRunnerSet controller with proxy settings", func(
EphemeralRunnerSpec: v1alpha1.EphemeralRunnerSpec{
GitHubConfigUrl: "http://example.com/owner/repo",
GitHubConfigSecret: configSecret.Name,
RunnerScaleSetId: 100,
RunnerScaleSetID: 100,
Proxy: &v1alpha1.ProxyConfig{
HTTP: &v1alpha1.ProxyServerConfig{
Url: proxy.URL,
@@ -1419,10 +1417,7 @@ var _ = Describe("Test EphemeralRunnerSet controller with custom root CA", func(
Scheme: mgr.GetScheme(),
Log: logf.Log,
ResourceBuilder: ResourceBuilder{
SecretResolver: &SecretResolver{
k8sClient: mgr.GetClient(),
multiClient: actions.NewMultiClient(logr.Discard()),
},
SecretResolver: secretresolver.New(mgr.GetClient(), multiclient.NewScaleset()),
},
}
err = controller.SetupWithManager(mgr)
@@ -1432,26 +1427,6 @@ var _ = Describe("Test EphemeralRunnerSet controller with custom root CA", func(
})
It("should be able to make requests to a server using root CAs", func() {
certsFolder := filepath.Join(
"../../",
"github",
"actions",
"testdata",
)
certPath := filepath.Join(certsFolder, "server.crt")
keyPath := filepath.Join(certsFolder, "server.key")
serverSuccessfullyCalled := false
server := testserver.NewUnstarted(GinkgoT(), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
serverSuccessfullyCalled = true
w.WriteHeader(http.StatusOK)
}))
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
Expect(err).NotTo(HaveOccurred(), "failed to load server cert")
server.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
server.StartTLS()
ephemeralRunnerSet = &v1alpha1.EphemeralRunnerSet{
ObjectMeta: metav1.ObjectMeta{
Name: "test-asrs",
@@ -1460,7 +1435,7 @@ var _ = Describe("Test EphemeralRunnerSet controller with custom root CA", func(
Spec: v1alpha1.EphemeralRunnerSetSpec{
Replicas: 1,
EphemeralRunnerSpec: v1alpha1.EphemeralRunnerSpec{
GitHubConfigUrl: server.ConfigURLForOrg("my-org"),
GitHubConfigUrl: "https://github.example.com/api/v3",
GitHubConfigSecret: configSecret.Name,
GitHubServerTLS: &v1alpha1.TLSConfig{
CertificateFrom: &v1alpha1.TLSCertificateSource{
@@ -1472,7 +1447,7 @@ var _ = Describe("Test EphemeralRunnerSet controller with custom root CA", func(
},
},
},
RunnerScaleSetId: 100,
RunnerScaleSetID: 100,
PodTemplateSpec: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
@@ -1487,7 +1462,7 @@ var _ = Describe("Test EphemeralRunnerSet controller with custom root CA", func(
},
}
err = k8sClient.Create(ctx, ephemeralRunnerSet)
err := k8sClient.Create(ctx, ephemeralRunnerSet)
Expect(err).NotTo(HaveOccurred(), "failed to create EphemeralRunnerSet")
runnerList := new(v1alpha1.EphemeralRunnerList)
@@ -1503,32 +1478,10 @@ var _ = Describe("Test EphemeralRunnerSet controller with custom root CA", func(
ephemeralRunnerSetTestInterval,
).Should(BeEquivalentTo(1), "failed to create ephemeral runner")
// Verify that the TLS configuration is properly propagated to the runner
runner := runnerList.Items[0].DeepCopy()
Expect(runner.Spec.GitHubServerTLS).NotTo(BeNil(), "runner tls config should not be nil")
Expect(runner.Spec.GitHubServerTLS).To(BeEquivalentTo(ephemeralRunnerSet.Spec.EphemeralRunnerSpec.GitHubServerTLS), "runner tls config should be correct")
runner.Status.Phase = corev1.PodRunning
runner.Status.RunnerId = 100
err = k8sClient.Status().Patch(ctx, runner, client.MergeFrom(&runnerList.Items[0]))
Expect(err).NotTo(HaveOccurred(), "failed to update ephemeral runner status")
currentRunnerSet := new(v1alpha1.EphemeralRunnerSet)
err = k8sClient.Get(ctx, client.ObjectKey{Namespace: ephemeralRunnerSet.Namespace, Name: ephemeralRunnerSet.Name}, currentRunnerSet)
Expect(err).NotTo(HaveOccurred(), "failed to get EphemeralRunnerSet")
updatedRunnerSet := currentRunnerSet.DeepCopy()
updatedRunnerSet.Spec.Replicas = 0
err = k8sClient.Patch(ctx, updatedRunnerSet, client.MergeFrom(currentRunnerSet))
Expect(err).NotTo(HaveOccurred(), "failed to update EphemeralRunnerSet")
// wait for server to be called
Eventually(
func() bool {
return serverSuccessfullyCalled
},
autoscalingRunnerSetTestTimeout,
1*time.Nanosecond,
).Should(BeTrue(), "server was not called")
})
})

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,230 @@
package fake
import (
"context"
"github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient"
"github.com/actions/scaleset"
)
// ClientOption is a functional option for configuring a fake Client
type ClientOption func(*Client)
// WithGetRunnerScaleSet configures the result of GetRunnerScaleSet
func WithGetRunnerScaleSet(result *scaleset.RunnerScaleSet, err error) ClientOption {
return func(c *Client) {
c.getRunnerScaleSetResult.RunnerScaleSet = result
c.getRunnerScaleSetResult.err = err
}
}
// WithGetRunnerScaleSetByID configures the result of GetRunnerScaleSetByID
func WithGetRunnerScaleSetByID(result *scaleset.RunnerScaleSet, err error) ClientOption {
return func(c *Client) {
c.getRunnerScaleSetByIDResult.RunnerScaleSet = result
c.getRunnerScaleSetByIDResult.err = err
}
}
// WithGetRunnerGroupByName configures the result of GetRunnerGroupByName
func WithGetRunnerGroupByName(result *scaleset.RunnerGroup, err error) ClientOption {
return func(c *Client) {
c.getRunnerGroupByNameResult.RunnerGroup = result
c.getRunnerGroupByNameResult.err = err
}
}
// WithGetRunnerGroupByNameFunc configures a function to handle GetRunnerGroupByName calls dynamically
func WithGetRunnerGroupByNameFunc(fn func(context.Context, string) (*scaleset.RunnerGroup, error)) ClientOption {
return func(c *Client) {
c.getRunnerGroupByNameFunc = fn
}
}
// WithCreateRunnerScaleSet configures the result of CreateRunnerScaleSet
func WithCreateRunnerScaleSet(result *scaleset.RunnerScaleSet, err error) ClientOption {
return func(c *Client) {
c.createRunnerScaleSetResult.RunnerScaleSet = result
c.createRunnerScaleSetResult.err = err
}
}
// WithUpdateRunnerScaleSet configures the result of UpdateRunnerScaleSet
func WithUpdateRunnerScaleSet(result *scaleset.RunnerScaleSet, err error) ClientOption {
return func(c *Client) {
c.updateRunnerScaleSetResult.RunnerScaleSet = result
c.updateRunnerScaleSetResult.err = err
}
}
// WithDeleteRunnerScaleSet configures the result of DeleteRunnerScaleSet
func WithDeleteRunnerScaleSet(err error) ClientOption {
return func(c *Client) {
c.deleteRunnerScaleSetResult.err = err
}
}
// WithRemoveRunner configures the result of RemoveRunner
func WithRemoveRunner(err error) ClientOption {
return func(c *Client) {
c.removeRunnerResult.err = err
}
}
// WithGenerateJitRunnerConfig configures the result of GenerateJitRunnerConfig
func WithGenerateJitRunnerConfig(result *scaleset.RunnerScaleSetJitRunnerConfig, err error) ClientOption {
return func(c *Client) {
c.generateJitRunnerConfigResult.RunnerScaleSetJitRunnerConfig = result
c.generateJitRunnerConfigResult.err = err
}
}
// WithGetRunnerByName configures the result of GetRunnerByName
func WithGetRunnerByName(result *scaleset.RunnerReference, err error) ClientOption {
return func(c *Client) {
c.getRunnerByNameResult.RunnerReference = result
c.getRunnerByNameResult.err = err
}
}
// WithGetRunner configures the result of GetRunner
func WithGetRunner(result *scaleset.RunnerReference, err error) ClientOption {
return func(c *Client) {
c.getRunnerResult.RunnerReference = result
c.getRunnerResult.err = err
}
}
// WithSystemInfo configures the SystemInfo
func WithSystemInfo(info scaleset.SystemInfo) ClientOption {
return func(c *Client) {
c.systemInfo = info
}
}
// WithUpdateRunnerScaleSetFunc configures a function to handle UpdateRunnerScaleSet calls dynamically
func WithUpdateRunnerScaleSetFunc(fn func(context.Context, int, *scaleset.RunnerScaleSet) (*scaleset.RunnerScaleSet, error)) ClientOption {
return func(c *Client) {
c.updateRunnerScaleSetFunc = fn
}
}
// Client implements multiclient.Client interface for testing
type Client struct {
systemInfo scaleset.SystemInfo
updateRunnerScaleSetFunc func(context.Context, int, *scaleset.RunnerScaleSet) (*scaleset.RunnerScaleSet, error)
getRunnerScaleSetResult struct {
*scaleset.RunnerScaleSet
err error
}
getRunnerScaleSetByIDResult struct {
*scaleset.RunnerScaleSet
err error
}
getRunnerGroupByNameResult struct {
*scaleset.RunnerGroup
err error
}
getRunnerGroupByNameFunc func(context.Context, string) (*scaleset.RunnerGroup, error)
createRunnerScaleSetResult struct {
*scaleset.RunnerScaleSet
err error
}
updateRunnerScaleSetResult struct {
*scaleset.RunnerScaleSet
err error
}
deleteRunnerScaleSetResult struct {
err error
}
removeRunnerResult struct {
err error
}
generateJitRunnerConfigResult struct {
*scaleset.RunnerScaleSetJitRunnerConfig
err error
}
getRunnerByNameResult struct {
*scaleset.RunnerReference
err error
}
getRunnerResult struct {
*scaleset.RunnerReference
err error
}
messageSessionClientResult struct {
*scaleset.MessageSessionClient
err error
}
}
// Compile-time interface check
var _ multiclient.Client = (*Client)(nil)
// NewClient creates a new fake Client with the given options
func NewClient(opts ...ClientOption) *Client {
c := &Client{}
for _, opt := range opts {
opt(c)
}
return c
}
func (c *Client) SetSystemInfo(info scaleset.SystemInfo) {
c.systemInfo = info
}
func (c *Client) SystemInfo() scaleset.SystemInfo {
return c.systemInfo
}
func (c *Client) MessageSessionClient(ctx context.Context, runnerScaleSetID int, owner string, options ...scaleset.HTTPOption) (*scaleset.MessageSessionClient, error) {
return c.messageSessionClientResult.MessageSessionClient, c.messageSessionClientResult.err
}
func (c *Client) GenerateJitRunnerConfig(ctx context.Context, jitRunnerSetting *scaleset.RunnerScaleSetJitRunnerSetting, scaleSetID int) (*scaleset.RunnerScaleSetJitRunnerConfig, error) {
return c.generateJitRunnerConfigResult.RunnerScaleSetJitRunnerConfig, c.generateJitRunnerConfigResult.err
}
func (c *Client) GetRunner(ctx context.Context, runnerID int) (*scaleset.RunnerReference, error) {
return c.getRunnerResult.RunnerReference, c.getRunnerResult.err
}
func (c *Client) GetRunnerByName(ctx context.Context, runnerName string) (*scaleset.RunnerReference, error) {
return c.getRunnerByNameResult.RunnerReference, c.getRunnerByNameResult.err
}
func (c *Client) RemoveRunner(ctx context.Context, runnerID int64) error {
return c.removeRunnerResult.err
}
func (c *Client) GetRunnerGroupByName(ctx context.Context, runnerGroup string) (*scaleset.RunnerGroup, error) {
if c.getRunnerGroupByNameFunc != nil {
return c.getRunnerGroupByNameFunc(ctx, runnerGroup)
}
return c.getRunnerGroupByNameResult.RunnerGroup, c.getRunnerGroupByNameResult.err
}
func (c *Client) GetRunnerScaleSet(ctx context.Context, runnerGroupID int, runnerScaleSetName string) (*scaleset.RunnerScaleSet, error) {
return c.getRunnerScaleSetResult.RunnerScaleSet, c.getRunnerScaleSetResult.err
}
func (c *Client) GetRunnerScaleSetByID(ctx context.Context, runnerScaleSetID int) (*scaleset.RunnerScaleSet, error) {
return c.getRunnerScaleSetByIDResult.RunnerScaleSet, c.getRunnerScaleSetByIDResult.err
}
func (c *Client) CreateRunnerScaleSet(ctx context.Context, runnerScaleSet *scaleset.RunnerScaleSet) (*scaleset.RunnerScaleSet, error) {
return c.createRunnerScaleSetResult.RunnerScaleSet, c.createRunnerScaleSetResult.err
}
func (c *Client) UpdateRunnerScaleSet(ctx context.Context, runnerScaleSetID int, runnerScaleSet *scaleset.RunnerScaleSet) (*scaleset.RunnerScaleSet, error) {
if c.updateRunnerScaleSetFunc != nil {
return c.updateRunnerScaleSetFunc(ctx, runnerScaleSetID, runnerScaleSet)
}
return c.updateRunnerScaleSetResult.RunnerScaleSet, c.updateRunnerScaleSetResult.err
}
func (c *Client) DeleteRunnerScaleSet(ctx context.Context, runnerScaleSetID int) error {
return c.deleteRunnerScaleSetResult.err
}

View File

@@ -0,0 +1,53 @@
package fake
import (
"context"
"github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient"
)
// MultiClientOption is a functional option for configuring a fake MultiClient
type MultiClientOption func(*MultiClient)
// WithClient configures the client that GetClientFor will return
func WithClient(c multiclient.Client) MultiClientOption {
return func(mc *MultiClient) {
mc.client = c
}
}
// WithGetClientForError configures an error that GetClientFor will return
func WithGetClientForError(err error) MultiClientOption {
return func(mc *MultiClient) {
mc.getClientForErr = err
}
}
// MultiClient implements multiclient.MultiClient interface for testing
type MultiClient struct {
client multiclient.Client
getClientForErr error
}
// Compile-time interface check
var _ multiclient.MultiClient = (*MultiClient)(nil)
// NewMultiClient creates a new fake MultiClient with the given options
func NewMultiClient(opts ...MultiClientOption) *MultiClient {
mc := &MultiClient{}
for _, opt := range opts {
opt(mc)
}
// Default behavior: if no client configured, return a default NewClient()
if mc.client == nil {
mc.client = NewClient()
}
return mc
}
func (mc *MultiClient) GetClientFor(ctx context.Context, opts *multiclient.ClientForOptions) (multiclient.Client, error) {
if mc.getClientForErr != nil {
return nil, mc.getClientForErr
}
return mc.client, nil
}

View File

@@ -0,0 +1,182 @@
package multiclient
import (
"context"
"crypto/sha256"
"crypto/x509"
"fmt"
"log/slog"
"net/http"
"net/url"
"sync"
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1/appconfig"
"github.com/actions/actions-runner-controller/build"
"github.com/actions/actions-runner-controller/github/actions"
"github.com/actions/scaleset"
"github.com/google/uuid"
)
type MultiClient interface {
GetClientFor(ctx context.Context, opts *ClientForOptions) (Client, error)
}
type Scaleset struct {
mu sync.Mutex
clients map[string]*multiClientEntry
}
type multiClientEntry struct {
client *scaleset.Client
rootCAs *x509.CertPool
}
func NewScaleset() *Scaleset {
return &Scaleset{
mu: sync.Mutex{},
clients: make(map[string]*multiClientEntry),
}
}
type Client interface {
SetSystemInfo(info scaleset.SystemInfo)
SystemInfo() scaleset.SystemInfo
MessageSessionClient(ctx context.Context, runnerScaleSetID int, owner string, options ...scaleset.HTTPOption) (*scaleset.MessageSessionClient, error)
GenerateJitRunnerConfig(ctx context.Context, jitRunnerSetting *scaleset.RunnerScaleSetJitRunnerSetting, scaleSetID int) (*scaleset.RunnerScaleSetJitRunnerConfig, error)
GetRunner(ctx context.Context, runnerID int) (*scaleset.RunnerReference, error)
GetRunnerByName(ctx context.Context, runnerName string) (*scaleset.RunnerReference, error)
RemoveRunner(ctx context.Context, runnerID int64) error
GetRunnerGroupByName(ctx context.Context, runnerGroup string) (*scaleset.RunnerGroup, error)
GetRunnerScaleSet(ctx context.Context, runnerGroupID int, runnerScaleSetName string) (*scaleset.RunnerScaleSet, error)
GetRunnerScaleSetByID(ctx context.Context, runnerScaleSetID int) (*scaleset.RunnerScaleSet, error)
CreateRunnerScaleSet(ctx context.Context, runnerScaleSet *scaleset.RunnerScaleSet) (*scaleset.RunnerScaleSet, error)
UpdateRunnerScaleSet(ctx context.Context, runnerScaleSetID int, runnerScaleSet *scaleset.RunnerScaleSet) (*scaleset.RunnerScaleSet, error)
DeleteRunnerScaleSet(ctx context.Context, runnerScaleSetID int) error
}
func (m *Scaleset) GetClientFor(ctx context.Context, opts *ClientForOptions) (Client, error) {
identifier, err := opts.identifier()
if err != nil {
return nil, fmt.Errorf("failed to generate client identifier: %w", err)
}
m.mu.Lock()
defer m.mu.Unlock()
entry, ok := m.clients[identifier]
if ok && entry.rootCAs.Equal(opts.RootCAs) {
return entry.client, nil
}
client, err := opts.newClient()
if err != nil {
return nil, fmt.Errorf("failed to create new client: %w", err)
}
m.clients[identifier] = &multiClientEntry{
client: client,
rootCAs: opts.RootCAs,
}
return client, nil
}
type ClientForOptions struct {
GithubConfigURL string
AppConfig *appconfig.AppConfig
Namespace string
RootCAs *x509.CertPool
ProxyFunc func(*http.Request) (*url.URL, error)
logger *slog.Logger
}
func (o *ClientForOptions) identifier() (string, error) {
if err := o.AppConfig.Validate(); err != nil {
return "", fmt.Errorf("failed to validate app config: %w", err)
}
if _, err := actions.ParseGitHubConfigFromURL(o.GithubConfigURL); err != nil {
return "", fmt.Errorf("failed to parse GitHub config URL: %w", err)
}
if o.Namespace == "" {
return "", fmt.Errorf("namespace is required to generate client identifier")
}
identifier := fmt.Sprintf("configURL:%q,namespace:%q", o.GithubConfigURL, o.Namespace)
if o.AppConfig.Token != "" {
identifier += fmt.Sprintf("token:%q,", o.AppConfig.Token)
} else {
identifier += fmt.Sprintf(
"appID:%q,installationID:%q,key:%q",
o.AppConfig.AppID,
o.AppConfig.AppInstallationID,
o.AppConfig.AppPrivateKey,
)
}
if o.RootCAs != nil {
// ignoring because this cert pool is intended not to come from SystemCertPool
// nolint:staticcheck
identifier += fmt.Sprintf("rootCAs:%q", o.RootCAs.Subjects())
}
return uuid.NewHash(sha256.New(), uuid.NameSpaceOID, []byte(identifier), 6).String(), nil
}
func (o *ClientForOptions) newClient() (*scaleset.Client, error) {
systemInfo := scaleset.SystemInfo{
System: "actions-runner-controller",
Version: build.Version,
CommitSHA: build.CommitSHA,
ScaleSetID: 0, // by default, scale set is 0 (not created yet)
Subsystem: "gha-scale-set-controller",
}
options := []scaleset.HTTPOption{
scaleset.WithLogger(o.logger),
}
if o.RootCAs != nil {
options = append(options, scaleset.WithRootCAs(o.RootCAs))
}
if o.ProxyFunc != nil {
options = append(options, scaleset.WithProxy(o.ProxyFunc))
}
if o.AppConfig.Token != "" {
c, err := scaleset.NewClientWithPersonalAccessToken(
scaleset.NewClientWithPersonalAccessTokenConfig{
GitHubConfigURL: o.GithubConfigURL,
PersonalAccessToken: o.AppConfig.Token,
SystemInfo: systemInfo,
},
options...,
)
if err != nil {
return nil, fmt.Errorf("failed to instantiate client with personal access token auth: %w", err)
}
return c, nil
}
c, err := scaleset.NewClientWithGitHubApp(
scaleset.ClientWithGitHubAppConfig{
GitHubConfigURL: o.GithubConfigURL,
GitHubAppAuth: scaleset.GitHubAppAuth{
ClientID: o.AppConfig.AppID,
InstallationID: o.AppConfig.AppInstallationID,
PrivateKey: o.AppConfig.AppPrivateKey,
},
SystemInfo: systemInfo,
},
options...,
)
if err != nil {
return nil, fmt.Errorf("failed to instantiate client with GitHub App auth: %w", err)
}
return c, nil
}

View File

@@ -0,0 +1,16 @@
package object
import (
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
type ActionsGitHubObject interface {
client.Object
GitHubConfigUrl() string
GitHubConfigSecret() string
GitHubProxy() *v1alpha1.ProxyConfig
GitHubServerTLS() *v1alpha1.TLSConfig
VaultConfig() *v1alpha1.VaultConfig
VaultProxy() *v1alpha1.ProxyConfig
}

View File

@@ -2,6 +2,7 @@ package actionsgithubcom
import (
"bytes"
"context"
"encoding/json"
"fmt"
"maps"
@@ -14,10 +15,13 @@ import (
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1/appconfig"
"github.com/actions/actions-runner-controller/build"
ghalistenerconfig "github.com/actions/actions-runner-controller/cmd/ghalistener/config"
"github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient"
"github.com/actions/actions-runner-controller/controllers/actions.github.com/object"
"github.com/actions/actions-runner-controller/github/actions"
"github.com/actions/actions-runner-controller/hash"
"github.com/actions/actions-runner-controller/logging"
"github.com/actions/actions-runner-controller/vault/azurekeyvault"
"github.com/actions/scaleset"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -71,9 +75,14 @@ func SetListenerEntrypoint(entrypoint string) {
}
}
type SecretResolver interface {
GetAppConfig(ctx context.Context, obj object.ActionsGitHubObject) (*appconfig.AppConfig, error)
GetActionsService(ctx context.Context, obj object.ActionsGitHubObject) (multiclient.Client, error)
}
type ResourceBuilder struct {
ExcludeLabelPropagationPrefixes []string
*SecretResolver
SecretResolver
}
// boolPtr returns a pointer to a bool value
@@ -183,7 +192,7 @@ func (b *ResourceBuilder) newScaleSetListenerConfig(autoscalingListener *v1alpha
}
config := ghalistenerconfig.Config{
ConfigureUrl: autoscalingListener.Spec.GitHubConfigUrl,
ConfigureURL: autoscalingListener.Spec.GitHubConfigUrl,
EphemeralRunnerSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
EphemeralRunnerSetName: autoscalingListener.Spec.EphemeralRunnerSetName,
MaxRunners: autoscalingListener.Spec.MaxRunners,
@@ -599,7 +608,7 @@ func (b *ResourceBuilder) newEphemeralRunnerSet(autoscalingRunnerSet *v1alpha1.A
Spec: v1alpha1.EphemeralRunnerSetSpec{
Replicas: 0,
EphemeralRunnerSpec: v1alpha1.EphemeralRunnerSpec{
RunnerScaleSetId: runnerScaleSetID,
RunnerScaleSetID: runnerScaleSetID,
GitHubConfigUrl: autoscalingRunnerSet.Spec.GitHubConfigUrl,
GitHubConfigSecret: autoscalingRunnerSet.Spec.GitHubConfigSecret,
Proxy: autoscalingRunnerSet.Spec.Proxy,
@@ -722,7 +731,7 @@ func (b *ResourceBuilder) newEphemeralRunnerPod(runner *v1alpha1.EphemeralRunner
return &newPod
}
func (b *ResourceBuilder) newEphemeralRunnerJitSecret(ephemeralRunner *v1alpha1.EphemeralRunner, jitConfig *actions.RunnerScaleSetJitRunnerConfig) *corev1.Secret {
func (b *ResourceBuilder) newEphemeralRunnerJitSecret(ephemeralRunner *v1alpha1.EphemeralRunner, jitConfig *scaleset.RunnerScaleSetJitRunnerConfig) *corev1.Secret {
var (
labels map[string]string
annotations map[string]string
@@ -743,8 +752,8 @@ func (b *ResourceBuilder) newEphemeralRunnerJitSecret(ephemeralRunner *v1alpha1.
Data: map[string][]byte{
jitTokenKey: []byte(jitConfig.EncodedJITConfig),
"runnerName": []byte(jitConfig.Runner.Name),
"runnerId": []byte(strconv.Itoa(jitConfig.Runner.Id)),
"scaleSetId": []byte(strconv.Itoa(jitConfig.Runner.RunnerScaleSetId)),
"runnerId": []byte(strconv.Itoa(jitConfig.Runner.ID)),
"scaleSetId": []byte(strconv.Itoa(jitConfig.Runner.RunnerScaleSetID)),
},
}
}

View File

@@ -1,16 +1,18 @@
package actionsgithubcom
package secretresolver
import (
"context"
"crypto/x509"
"encoding/json"
"fmt"
"log/slog"
"net/http"
"net/url"
"strings"
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1/appconfig"
"github.com/actions/actions-runner-controller/github/actions"
"github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient"
"github.com/actions/actions-runner-controller/controllers/actions.github.com/object"
"github.com/actions/actions-runner-controller/vault"
"github.com/actions/actions-runner-controller/vault/azurekeyvault"
"golang.org/x/net/http/httpproxy"
@@ -21,19 +23,27 @@ import (
type SecretResolver struct {
k8sClient client.Client
multiClient actions.MultiClient
multiClient multiclient.MultiClient
logger *slog.Logger
}
type SecretResolverOption func(*SecretResolver)
type Option func(*SecretResolver)
func NewSecretResolver(k8sClient client.Client, multiClient actions.MultiClient, opts ...SecretResolverOption) *SecretResolver {
func WithLogger(logger *slog.Logger) Option {
return func(sr *SecretResolver) {
sr.logger = logger
}
}
func New(k8sClient client.Client, scalesetMultiClient multiclient.MultiClient, opts ...Option) *SecretResolver {
if k8sClient == nil {
panic("k8sClient must not be nil")
}
secretResolver := &SecretResolver{
k8sClient: k8sClient,
multiClient: multiClient,
multiClient: scalesetMultiClient,
logger: slog.New(slog.DiscardHandler),
}
for _, opt := range opts {
@@ -43,17 +53,7 @@ func NewSecretResolver(k8sClient client.Client, multiClient actions.MultiClient,
return secretResolver
}
type ActionsGitHubObject interface {
client.Object
GitHubConfigUrl() string
GitHubConfigSecret() string
GitHubProxy() *v1alpha1.ProxyConfig
GitHubServerTLS() *v1alpha1.TLSConfig
VaultConfig() *v1alpha1.VaultConfig
VaultProxy() *v1alpha1.ProxyConfig
}
func (sr *SecretResolver) GetAppConfig(ctx context.Context, obj ActionsGitHubObject) (*appconfig.AppConfig, error) {
func (sr *SecretResolver) GetAppConfig(ctx context.Context, obj object.ActionsGitHubObject) (*appconfig.AppConfig, error) {
resolver, err := sr.resolverForObject(ctx, obj)
if err != nil {
return nil, fmt.Errorf("failed to get resolver for object: %v", err)
@@ -67,7 +67,7 @@ func (sr *SecretResolver) GetAppConfig(ctx context.Context, obj ActionsGitHubObj
return appConfig, nil
}
func (sr *SecretResolver) GetActionsService(ctx context.Context, obj ActionsGitHubObject) (actions.ActionsService, error) {
func (sr *SecretResolver) GetActionsService(ctx context.Context, obj object.ActionsGitHubObject) (multiclient.Client, error) {
resolver, err := sr.resolverForObject(ctx, obj)
if err != nil {
return nil, fmt.Errorf("failed to get resolver for object: %v", err)
@@ -78,7 +78,7 @@ func (sr *SecretResolver) GetActionsService(ctx context.Context, obj ActionsGitH
return nil, fmt.Errorf("failed to resolve app config: %v", err)
}
var clientOptions []actions.ClientOption
var proxyFunc func(req *http.Request) (*url.URL, error)
if proxy := obj.GitHubProxy(); proxy != nil {
config := &httpproxy.Config{
NoProxy: strings.Join(proxy.NoProxy, ","),
@@ -116,16 +116,14 @@ func (sr *SecretResolver) GetActionsService(ctx context.Context, obj ActionsGitH
config.HTTPSProxy = u.String()
}
proxyFunc := func(req *http.Request) (*url.URL, error) {
proxyFunc = func(req *http.Request) (*url.URL, error) {
return config.ProxyFunc()(req.URL)
}
clientOptions = append(clientOptions, actions.WithProxy(proxyFunc))
}
tlsConfig := obj.GitHubServerTLS()
if tlsConfig != nil {
pool, err := tlsConfig.ToCertPool(func(name, key string) ([]byte, error) {
var rootCAs *x509.CertPool
if tc := obj.GitHubServerTLS(); tc != nil {
pool, err := tc.ToCertPool(func(name, key string) ([]byte, error) {
var configmap corev1.ConfigMap
err := sr.k8sClient.Get(
ctx,
@@ -145,19 +143,22 @@ func (sr *SecretResolver) GetActionsService(ctx context.Context, obj ActionsGitH
return nil, fmt.Errorf("failed to get tls config: %w", err)
}
clientOptions = append(clientOptions, actions.WithRootCAs(pool))
rootCAs = pool
}
return sr.multiClient.GetClientFor(
ctx,
obj.GitHubConfigUrl(),
appConfig,
obj.GetNamespace(),
clientOptions...,
&multiclient.ClientForOptions{
GithubConfigURL: obj.GitHubConfigUrl(),
AppConfig: appConfig,
Namespace: obj.GetNamespace(),
RootCAs: rootCAs,
ProxyFunc: proxyFunc,
},
)
}
func (sr *SecretResolver) resolverForObject(ctx context.Context, obj ActionsGitHubObject) (resolver, error) {
func (sr *SecretResolver) resolverForObject(ctx context.Context, obj object.ActionsGitHubObject) (resolver, error) {
vaultConfig := obj.VaultConfig()
if vaultConfig == nil || vaultConfig.Type == "" {
return &k8sResolver{