945 lines
31 KiB
Go
945 lines
31 KiB
Go
package actionsgithubcom
|
|
|
|
// import (
|
|
// "context"
|
|
// "crypto/tls"
|
|
// "encoding/base64"
|
|
// "fmt"
|
|
// "net/http"
|
|
// "net/http/httptest"
|
|
// "os"
|
|
// "path/filepath"
|
|
// "strings"
|
|
// "time"
|
|
//
|
|
// "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/onsi/ginkgo/v2"
|
|
// . "github.com/onsi/gomega"
|
|
// corev1 "k8s.io/api/core/v1"
|
|
// kerrors "k8s.io/apimachinery/pkg/api/errors"
|
|
// metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
// ctrl "sigs.k8s.io/controller-runtime"
|
|
// "sigs.k8s.io/controller-runtime/pkg/client"
|
|
// logf "sigs.k8s.io/controller-runtime/pkg/log"
|
|
// )
|
|
//
|
|
// const (
|
|
// timeout = time.Second * 10
|
|
// interval = time.Millisecond * 250
|
|
// runnerImage = "ghcr.io/actions/actions-runner:latest"
|
|
// )
|
|
//
|
|
// func newExampleRunner(name, namespace, configSecretName string) *v1alpha1.EphemeralRunner {
|
|
// return &v1alpha1.EphemeralRunner{
|
|
// ObjectMeta: metav1.ObjectMeta{
|
|
// Name: name,
|
|
// Namespace: namespace,
|
|
// },
|
|
// Spec: v1alpha1.EphemeralRunnerSpec{
|
|
// GitHubConfigUrl: "https://github.com/owner/repo",
|
|
// GitHubConfigSecret: configSecretName,
|
|
// RunnerScaleSetId: 1,
|
|
// PodTemplateSpec: corev1.PodTemplateSpec{
|
|
// Spec: corev1.PodSpec{
|
|
// Containers: []corev1.Container{
|
|
// {
|
|
// Name: EphemeralRunnerContainerName,
|
|
// Image: runnerImage,
|
|
// Command: []string{"/runner/run.sh"},
|
|
// VolumeMounts: []corev1.VolumeMount{
|
|
// {
|
|
// Name: "runner",
|
|
// MountPath: "/runner",
|
|
// },
|
|
// },
|
|
// },
|
|
// },
|
|
// InitContainers: []corev1.Container{
|
|
// {
|
|
// Name: "setup",
|
|
// Image: runnerImage,
|
|
// Command: []string{"sh", "-c", "cp -r /home/runner/* /runner/"},
|
|
// VolumeMounts: []corev1.VolumeMount{
|
|
// {
|
|
// Name: "runner",
|
|
// MountPath: "/runner",
|
|
// },
|
|
// },
|
|
// },
|
|
// },
|
|
// Volumes: []corev1.Volume{
|
|
// {
|
|
// Name: "runner",
|
|
// VolumeSource: corev1.VolumeSource{
|
|
// EmptyDir: &corev1.EmptyDirVolumeSource{},
|
|
// },
|
|
// },
|
|
// },
|
|
// },
|
|
// },
|
|
// },
|
|
// }
|
|
// }
|
|
//
|
|
// var _ = Describe("EphemeralRunner", func() {
|
|
// Describe("Resource manipulation", func() {
|
|
// var ctx context.Context
|
|
// var mgr ctrl.Manager
|
|
// var autoscalingNS *corev1.Namespace
|
|
// var configSecret *corev1.Secret
|
|
// var controller *EphemeralRunnerReconciler
|
|
// var ephemeralRunner *v1alpha1.EphemeralRunner
|
|
//
|
|
// BeforeEach(func() {
|
|
// ctx = context.Background()
|
|
// autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
|
// configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
|
//
|
|
// controller = &EphemeralRunnerReconciler{
|
|
// Client: mgr.GetClient(),
|
|
// Scheme: mgr.GetScheme(),
|
|
// Log: logf.Log,
|
|
// ActionsClient: fake.NewMultiClient(),
|
|
// }
|
|
//
|
|
// err := controller.SetupWithManager(mgr)
|
|
// Expect(err).To(BeNil(), "failed to setup controller")
|
|
//
|
|
// ephemeralRunner = newExampleRunner("test-runner", autoscalingNS.Name, configSecret.Name)
|
|
// err = k8sClient.Create(ctx, ephemeralRunner)
|
|
// Expect(err).To(BeNil(), "failed to create ephemeral runner")
|
|
//
|
|
// startManagers(GinkgoT(), mgr)
|
|
// })
|
|
//
|
|
// It("It should create/add all required resources for EphemeralRunner (finalizer, jit secret)", func() {
|
|
// created := new(v1alpha1.EphemeralRunner)
|
|
// // Check if finalizer is added
|
|
// Eventually(
|
|
// func() ([]string, error) {
|
|
// err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, created)
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
// if len(created.Finalizers) == 0 {
|
|
// return nil, nil
|
|
// }
|
|
//
|
|
// n := len(created.Finalizers) // avoid capacity mismatch
|
|
// return created.Finalizers[:n:n], nil
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo([]string{ephemeralRunnerActionsFinalizerName, ephemeralRunnerFinalizerName}))
|
|
//
|
|
// Eventually(
|
|
// func() (bool, error) {
|
|
// secret := new(corev1.Secret)
|
|
// if err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, secret); err != nil {
|
|
// return false, err
|
|
// }
|
|
//
|
|
// _, ok := secret.Data[jitTokenKey]
|
|
// return ok, nil
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo(true))
|
|
//
|
|
// Eventually(
|
|
// func() (string, error) {
|
|
// pod := new(corev1.Pod)
|
|
// if err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, pod); err != nil {
|
|
// return "", err
|
|
// }
|
|
//
|
|
// return pod.Name, nil
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo(ephemeralRunner.Name))
|
|
// })
|
|
//
|
|
// It("It should re-create pod on failure", func() {
|
|
// pod := new(corev1.Pod)
|
|
// Eventually(func() (bool, error) {
|
|
// if err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, pod); err != nil {
|
|
// return false, err
|
|
// }
|
|
// return true, nil
|
|
// }).Should(BeEquivalentTo(true))
|
|
//
|
|
// err := k8sClient.Delete(ctx, pod)
|
|
// Expect(err).To(BeNil(), "failed to delete pod")
|
|
//
|
|
// pod = new(corev1.Pod)
|
|
// Eventually(func() (bool, error) {
|
|
// if err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, pod); err != nil {
|
|
// return false, err
|
|
// }
|
|
// return true, nil
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo(true))
|
|
// })
|
|
//
|
|
// It("It should clean up resources when deleted", func() {
|
|
// // wait for pod to be created
|
|
// pod := new(corev1.Pod)
|
|
// Eventually(func() (bool, error) {
|
|
// if err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, pod); err != nil {
|
|
// return false, err
|
|
// }
|
|
// return true, nil
|
|
// }).Should(BeEquivalentTo(true))
|
|
//
|
|
// // create runner-linked pod
|
|
// runnerLinkedPod := &corev1.Pod{
|
|
// ObjectMeta: metav1.ObjectMeta{
|
|
// Name: "test-runner-linked-pod",
|
|
// Namespace: ephemeralRunner.Namespace,
|
|
// Labels: map[string]string{
|
|
// "runner-pod": ephemeralRunner.Name,
|
|
// },
|
|
// },
|
|
// Spec: corev1.PodSpec{
|
|
// Containers: []corev1.Container{
|
|
// {
|
|
// Name: "runner-linked-container",
|
|
// Image: "ubuntu:latest",
|
|
// },
|
|
// },
|
|
// },
|
|
// }
|
|
//
|
|
// err := k8sClient.Create(ctx, runnerLinkedPod)
|
|
// Expect(err).To(BeNil(), "failed to create runner linked pod")
|
|
// Eventually(
|
|
// func() (bool, error) {
|
|
// pod := new(corev1.Pod)
|
|
// if err := k8sClient.Get(ctx, client.ObjectKey{Name: runnerLinkedPod.Name, Namespace: runnerLinkedPod.Namespace}, pod); err != nil {
|
|
// return false, nil
|
|
// }
|
|
// return true, nil
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo(true))
|
|
//
|
|
// // create runner linked secret
|
|
// runnerLinkedSecret := &corev1.Secret{
|
|
// ObjectMeta: metav1.ObjectMeta{
|
|
// Name: "test-runner-linked-secret",
|
|
// Namespace: ephemeralRunner.Namespace,
|
|
// Labels: map[string]string{
|
|
// "runner-pod": ephemeralRunner.Name,
|
|
// },
|
|
// },
|
|
// Data: map[string][]byte{"test": []byte("test")},
|
|
// }
|
|
//
|
|
// err = k8sClient.Create(ctx, runnerLinkedSecret)
|
|
// Expect(err).To(BeNil(), "failed to create runner linked secret")
|
|
// Eventually(
|
|
// func() (bool, error) {
|
|
// secret := new(corev1.Secret)
|
|
// if err := k8sClient.Get(ctx, client.ObjectKey{Name: runnerLinkedSecret.Name, Namespace: runnerLinkedSecret.Namespace}, secret); err != nil {
|
|
// return false, nil
|
|
// }
|
|
// return true, nil
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo(true))
|
|
//
|
|
// err = k8sClient.Delete(ctx, ephemeralRunner)
|
|
// Expect(err).To(BeNil(), "failed to delete ephemeral runner")
|
|
//
|
|
// Eventually(
|
|
// func() (bool, error) {
|
|
// pod := new(corev1.Pod)
|
|
// err = k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, pod)
|
|
// if err == nil {
|
|
// return false, nil
|
|
// }
|
|
// return kerrors.IsNotFound(err), nil
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo(true))
|
|
//
|
|
// Eventually(
|
|
// func() (bool, error) {
|
|
// secret := new(corev1.Secret)
|
|
// err = k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, secret)
|
|
// if err == nil {
|
|
// return false, nil
|
|
// }
|
|
// return kerrors.IsNotFound(err), nil
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo(true))
|
|
//
|
|
// Eventually(
|
|
// func() (bool, error) {
|
|
// pod := new(corev1.Pod)
|
|
// err = k8sClient.Get(ctx, client.ObjectKey{Name: runnerLinkedPod.Name, Namespace: runnerLinkedPod.Namespace}, pod)
|
|
// if err == nil {
|
|
// return false, nil
|
|
// }
|
|
// return kerrors.IsNotFound(err), nil
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo(true))
|
|
//
|
|
// Eventually(
|
|
// func() (bool, error) {
|
|
// secret := new(corev1.Secret)
|
|
// err = k8sClient.Get(ctx, client.ObjectKey{Name: runnerLinkedSecret.Name, Namespace: runnerLinkedSecret.Namespace}, secret)
|
|
// if err == nil {
|
|
// return false, nil
|
|
// }
|
|
// return kerrors.IsNotFound(err), nil
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo(true))
|
|
//
|
|
// Eventually(
|
|
// func() (bool, error) {
|
|
// updated := new(v1alpha1.EphemeralRunner)
|
|
// err = k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, updated)
|
|
// if err == nil {
|
|
// return false, nil
|
|
// }
|
|
// return kerrors.IsNotFound(err), nil
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo(true))
|
|
// })
|
|
//
|
|
// It("It should eventually have runner id set", func() {
|
|
// Eventually(
|
|
// func() (int, error) {
|
|
// updatedEphemeralRunner := new(v1alpha1.EphemeralRunner)
|
|
// err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, updatedEphemeralRunner)
|
|
// if err != nil {
|
|
// return 0, err
|
|
// }
|
|
// return updatedEphemeralRunner.Status.RunnerId, nil
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeNumerically(">", 0))
|
|
// })
|
|
//
|
|
// It("It should patch the ephemeral runner non terminating status", func() {
|
|
// pod := new(corev1.Pod)
|
|
// Eventually(
|
|
// func() (bool, error) {
|
|
// err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, pod)
|
|
// if err != nil {
|
|
// return false, err
|
|
// }
|
|
// return true, nil
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo(true))
|
|
//
|
|
// for _, phase := range []corev1.PodPhase{corev1.PodRunning, corev1.PodPending} {
|
|
// podCopy := pod.DeepCopy()
|
|
// pod.Status.Phase = phase
|
|
// // set container state to force status update
|
|
// pod.Status.ContainerStatuses = append(pod.Status.ContainerStatuses, corev1.ContainerStatus{
|
|
// Name: EphemeralRunnerContainerName,
|
|
// State: corev1.ContainerState{},
|
|
// })
|
|
// err := k8sClient.Status().Patch(ctx, pod, client.MergeFrom(podCopy))
|
|
// Expect(err).To(BeNil(), "failed to patch pod status")
|
|
//
|
|
// Eventually(
|
|
// func() (corev1.PodPhase, error) {
|
|
// updated := new(v1alpha1.EphemeralRunner)
|
|
// err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, updated)
|
|
// if err != nil {
|
|
// return "", err
|
|
// }
|
|
// return updated.Status.Phase, nil
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo(phase))
|
|
// }
|
|
// })
|
|
//
|
|
// It("It should not update phase if container state does not exist", func() {
|
|
// pod := new(corev1.Pod)
|
|
// Eventually(
|
|
// func() (bool, error) {
|
|
// err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, pod)
|
|
// if err != nil {
|
|
// return false, err
|
|
// }
|
|
// return true, nil
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo(true))
|
|
//
|
|
// pod.Status.Phase = corev1.PodRunning
|
|
// err := k8sClient.Status().Update(ctx, pod)
|
|
// Expect(err).To(BeNil(), "failed to patch pod status")
|
|
//
|
|
// Consistently(
|
|
// func() (corev1.PodPhase, error) {
|
|
// updated := new(v1alpha1.EphemeralRunner)
|
|
// if err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, updated); err != nil {
|
|
// return corev1.PodUnknown, err
|
|
// }
|
|
// return updated.Status.Phase, nil
|
|
// },
|
|
// timeout,
|
|
// ).Should(BeEquivalentTo(""))
|
|
// })
|
|
//
|
|
// It("It should not re-create pod indefinitely", func() {
|
|
// updated := new(v1alpha1.EphemeralRunner)
|
|
// pod := new(corev1.Pod)
|
|
// Eventually(
|
|
// func() (bool, error) {
|
|
// err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, updated)
|
|
// if err != nil {
|
|
// return false, err
|
|
// }
|
|
//
|
|
// err = k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, pod)
|
|
// if err != nil {
|
|
// if kerrors.IsNotFound(err) && len(updated.Status.Failures) > 5 {
|
|
// return true, nil
|
|
// }
|
|
//
|
|
// return false, err
|
|
// }
|
|
//
|
|
// pod.Status.ContainerStatuses = append(pod.Status.ContainerStatuses, corev1.ContainerStatus{
|
|
// Name: EphemeralRunnerContainerName,
|
|
// State: corev1.ContainerState{
|
|
// Terminated: &corev1.ContainerStateTerminated{
|
|
// ExitCode: 1,
|
|
// },
|
|
// },
|
|
// })
|
|
// err = k8sClient.Status().Update(ctx, pod)
|
|
// Expect(err).To(BeNil(), "Failed to update pod status")
|
|
// return false, fmt.Errorf("pod haven't failed for 5 times.")
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo(true), "we should stop creating pod after 5 failures")
|
|
//
|
|
// // In case we still have pod created due to controller-runtime cache delay, mark the container as exited
|
|
// err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, pod)
|
|
// if err == nil {
|
|
// pod.Status.ContainerStatuses = append(pod.Status.ContainerStatuses, corev1.ContainerStatus{
|
|
// Name: EphemeralRunnerContainerName,
|
|
// State: corev1.ContainerState{
|
|
// Terminated: &corev1.ContainerStateTerminated{
|
|
// ExitCode: 1,
|
|
// },
|
|
// },
|
|
// })
|
|
// err := k8sClient.Status().Update(ctx, pod)
|
|
// Expect(err).To(BeNil(), "Failed to update pod status")
|
|
// }
|
|
//
|
|
// // EphemeralRunner should failed with reason TooManyPodFailures
|
|
// Eventually(func() (string, error) {
|
|
// err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, updated)
|
|
// if err != nil {
|
|
// return "", err
|
|
// }
|
|
// return updated.Status.Reason, nil
|
|
// }, timeout, interval).Should(BeEquivalentTo("TooManyPodFailures"), "Reason should be TooManyPodFailures")
|
|
//
|
|
// // EphemeralRunner should not have any pod
|
|
// Eventually(func() (bool, error) {
|
|
// err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, pod)
|
|
// if err == nil {
|
|
// return false, nil
|
|
// }
|
|
// return kerrors.IsNotFound(err), nil
|
|
// }, timeout, interval).Should(BeEquivalentTo(true))
|
|
// })
|
|
//
|
|
// It("It should re-create pod on eviction", func() {
|
|
// pod := new(corev1.Pod)
|
|
// Eventually(
|
|
// func() (bool, error) {
|
|
// err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, pod)
|
|
// if err != nil {
|
|
// return false, err
|
|
// }
|
|
// return true, nil
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo(true))
|
|
//
|
|
// pod.Status.Phase = corev1.PodFailed
|
|
// pod.Status.Reason = "Evicted"
|
|
// pod.Status.ContainerStatuses = append(pod.Status.ContainerStatuses, corev1.ContainerStatus{
|
|
// Name: EphemeralRunnerContainerName,
|
|
// State: corev1.ContainerState{},
|
|
// })
|
|
// err := k8sClient.Status().Update(ctx, pod)
|
|
// Expect(err).To(BeNil(), "failed to patch pod status")
|
|
//
|
|
// updated := new(v1alpha1.EphemeralRunner)
|
|
// Eventually(func() (bool, error) {
|
|
// err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, updated)
|
|
// if err != nil {
|
|
// return false, err
|
|
// }
|
|
// return len(updated.Status.Failures) == 1, nil
|
|
// }, timeout, interval).Should(BeEquivalentTo(true))
|
|
//
|
|
// // should re-create after failure
|
|
// Eventually(
|
|
// func() (bool, error) {
|
|
// pod := new(corev1.Pod)
|
|
// if err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, pod); err != nil {
|
|
// return false, err
|
|
// }
|
|
// return true, nil
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo(true))
|
|
// })
|
|
//
|
|
// It("It should re-create pod on exit status 0, but runner exists within the service", func() {
|
|
// pod := new(corev1.Pod)
|
|
// Eventually(
|
|
// func() (bool, error) {
|
|
// if err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, pod); err != nil {
|
|
// return false, err
|
|
// }
|
|
// return true, nil
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo(true))
|
|
//
|
|
// pod.Status.ContainerStatuses = append(pod.Status.ContainerStatuses, corev1.ContainerStatus{
|
|
// Name: EphemeralRunnerContainerName,
|
|
// State: corev1.ContainerState{
|
|
// Terminated: &corev1.ContainerStateTerminated{
|
|
// ExitCode: 0,
|
|
// },
|
|
// },
|
|
// })
|
|
// err := k8sClient.Status().Update(ctx, pod)
|
|
// Expect(err).To(BeNil(), "failed to update pod status")
|
|
//
|
|
// updated := new(v1alpha1.EphemeralRunner)
|
|
// Eventually(func() (bool, error) {
|
|
// err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, updated)
|
|
// if err != nil {
|
|
// return false, err
|
|
// }
|
|
// return len(updated.Status.Failures) == 1, nil
|
|
// }, timeout, interval).Should(BeEquivalentTo(true))
|
|
//
|
|
// // should re-create after failure
|
|
// Eventually(
|
|
// func() (bool, error) {
|
|
// pod := new(corev1.Pod)
|
|
// if err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, pod); err != nil {
|
|
// return false, err
|
|
// }
|
|
// return true, nil
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo(true))
|
|
// })
|
|
//
|
|
// It("It should not set the phase to succeeded without pod termination status", func() {
|
|
// pod := new(corev1.Pod)
|
|
// Eventually(
|
|
// func() (bool, error) {
|
|
// if err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, pod); err != nil {
|
|
// return false, err
|
|
// }
|
|
// return true, nil
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo(true))
|
|
//
|
|
// // first set phase to running
|
|
// pod.Status.ContainerStatuses = append(pod.Status.ContainerStatuses, corev1.ContainerStatus{
|
|
// Name: EphemeralRunnerContainerName,
|
|
// State: corev1.ContainerState{
|
|
// Running: &corev1.ContainerStateRunning{
|
|
// StartedAt: metav1.Now(),
|
|
// },
|
|
// },
|
|
// })
|
|
// pod.Status.Phase = corev1.PodRunning
|
|
// err := k8sClient.Status().Update(ctx, pod)
|
|
// Expect(err).To(BeNil())
|
|
//
|
|
// Eventually(
|
|
// func() (corev1.PodPhase, error) {
|
|
// updated := new(v1alpha1.EphemeralRunner)
|
|
// if err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, updated); err != nil {
|
|
// return "", err
|
|
// }
|
|
// return updated.Status.Phase, nil
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo(corev1.PodRunning))
|
|
//
|
|
// // set phase to succeeded
|
|
// pod.Status.Phase = corev1.PodSucceeded
|
|
// err = k8sClient.Status().Update(ctx, pod)
|
|
// Expect(err).To(BeNil())
|
|
//
|
|
// Consistently(
|
|
// func() (corev1.PodPhase, error) {
|
|
// updated := new(v1alpha1.EphemeralRunner)
|
|
// if err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, updated); err != nil {
|
|
// return "", err
|
|
// }
|
|
// return updated.Status.Phase, nil
|
|
// },
|
|
// timeout,
|
|
// ).Should(BeEquivalentTo(corev1.PodRunning))
|
|
// })
|
|
// })
|
|
//
|
|
// Describe("Checking the API", func() {
|
|
// var ctx context.Context
|
|
// var autoscalingNS *corev1.Namespace
|
|
// var configSecret *corev1.Secret
|
|
// var controller *EphemeralRunnerReconciler
|
|
// var mgr ctrl.Manager
|
|
//
|
|
// BeforeEach(func() {
|
|
// ctx = context.Background()
|
|
// autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
|
// configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
|
//
|
|
// controller = &EphemeralRunnerReconciler{
|
|
// Client: mgr.GetClient(),
|
|
// Scheme: mgr.GetScheme(),
|
|
// Log: logf.Log,
|
|
// ActionsClient: fake.NewMultiClient(
|
|
// fake.WithDefaultClient(
|
|
// fake.NewFakeClient(
|
|
// fake.WithGetRunner(
|
|
// nil,
|
|
// &actions.ActionsError{
|
|
// StatusCode: http.StatusNotFound,
|
|
// ExceptionName: "AgentNotFoundException",
|
|
// },
|
|
// ),
|
|
// ),
|
|
// nil,
|
|
// ),
|
|
// ),
|
|
// }
|
|
// err := controller.SetupWithManager(mgr)
|
|
// Expect(err).To(BeNil(), "failed to setup controller")
|
|
//
|
|
// startManagers(GinkgoT(), mgr)
|
|
// })
|
|
//
|
|
// It("It should set the Phase to Succeeded", func() {
|
|
// ephemeralRunner := newExampleRunner("test-runner", autoscalingNS.Name, configSecret.Name)
|
|
//
|
|
// err := k8sClient.Create(ctx, ephemeralRunner)
|
|
// Expect(err).To(BeNil())
|
|
//
|
|
// pod := new(corev1.Pod)
|
|
// Eventually(func() (bool, error) {
|
|
// if err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, pod); err != nil {
|
|
// return false, err
|
|
// }
|
|
// return true, nil
|
|
// }, timeout, interval).Should(BeEquivalentTo(true))
|
|
//
|
|
// pod.Status.ContainerStatuses = append(pod.Status.ContainerStatuses, corev1.ContainerStatus{
|
|
// Name: EphemeralRunnerContainerName,
|
|
// State: corev1.ContainerState{
|
|
// Terminated: &corev1.ContainerStateTerminated{
|
|
// ExitCode: 0,
|
|
// },
|
|
// },
|
|
// })
|
|
// err = k8sClient.Status().Update(ctx, pod)
|
|
// Expect(err).To(BeNil(), "failed to update pod status")
|
|
//
|
|
// updated := new(v1alpha1.EphemeralRunner)
|
|
// Eventually(func() (corev1.PodPhase, error) {
|
|
// err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, updated)
|
|
// if err != nil {
|
|
// return "", nil
|
|
// }
|
|
// return updated.Status.Phase, nil
|
|
// }, timeout, interval).Should(BeEquivalentTo(corev1.PodSucceeded))
|
|
// })
|
|
// })
|
|
//
|
|
// Describe("Pod proxy config", func() {
|
|
// var ctx context.Context
|
|
// var mgr ctrl.Manager
|
|
// var autoScalingNS *corev1.Namespace
|
|
// var configSecret *corev1.Secret
|
|
// var controller *EphemeralRunnerReconciler
|
|
//
|
|
// BeforeEach(func() {
|
|
// ctx = context.Background()
|
|
// autoScalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
|
// configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoScalingNS.Name)
|
|
//
|
|
// controller = &EphemeralRunnerReconciler{
|
|
// Client: mgr.GetClient(),
|
|
// Scheme: mgr.GetScheme(),
|
|
// Log: logf.Log,
|
|
// ActionsClient: fake.NewMultiClient(),
|
|
// }
|
|
// err := controller.SetupWithManager(mgr)
|
|
// Expect(err).To(BeNil(), "failed to setup controller")
|
|
//
|
|
// startManagers(GinkgoT(), mgr)
|
|
// })
|
|
//
|
|
// It("uses an actions client with proxy transport", func() {
|
|
// // Use an actual client
|
|
// controller.ActionsClient = actions.NewMultiClient("test", logr.Discard())
|
|
//
|
|
// proxySuccessfulllyCalled := 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"))
|
|
//
|
|
// proxySuccessfulllyCalled = true
|
|
// w.WriteHeader(http.StatusOK)
|
|
// }))
|
|
// GinkgoT().Cleanup(func() {
|
|
// proxy.Close()
|
|
// })
|
|
//
|
|
// secretCredentials := &corev1.Secret{
|
|
// ObjectMeta: metav1.ObjectMeta{
|
|
// Name: "proxy-credentials",
|
|
// Namespace: autoScalingNS.Name,
|
|
// },
|
|
// Data: map[string][]byte{
|
|
// "username": []byte("test"),
|
|
// "password": []byte("password"),
|
|
// },
|
|
// }
|
|
//
|
|
// err := k8sClient.Create(ctx, secretCredentials)
|
|
// Expect(err).NotTo(HaveOccurred(), "failed to create secret credentials")
|
|
//
|
|
// ephemeralRunner := newExampleRunner("test-runner", autoScalingNS.Name, configSecret.Name)
|
|
// ephemeralRunner.Spec.GitHubConfigUrl = "http://example.com/org/repo"
|
|
// ephemeralRunner.Spec.Proxy = &v1alpha1.ProxyConfig{
|
|
// HTTP: &v1alpha1.ProxyServerConfig{
|
|
// Url: proxy.URL,
|
|
// CredentialSecretRef: "proxy-credentials",
|
|
// },
|
|
// }
|
|
//
|
|
// err = k8sClient.Create(ctx, ephemeralRunner)
|
|
// Expect(err).To(BeNil(), "failed to create ephemeral runner")
|
|
//
|
|
// Eventually(
|
|
// func() bool {
|
|
// return proxySuccessfulllyCalled
|
|
// },
|
|
// 2*time.Second,
|
|
// interval,
|
|
// ).Should(BeEquivalentTo(true))
|
|
// })
|
|
//
|
|
// It("It should create EphemeralRunner with proxy environment variables using ProxySecretRef", func() {
|
|
// ephemeralRunner := newExampleRunner("test-runner", autoScalingNS.Name, configSecret.Name)
|
|
// ephemeralRunner.Spec.Proxy = &v1alpha1.ProxyConfig{
|
|
// HTTP: &v1alpha1.ProxyServerConfig{
|
|
// Url: "http://proxy.example.com:8080",
|
|
// },
|
|
// HTTPS: &v1alpha1.ProxyServerConfig{
|
|
// Url: "http://proxy.example.com:8080",
|
|
// },
|
|
// NoProxy: []string{"example.com"},
|
|
// }
|
|
// ephemeralRunner.Spec.ProxySecretRef = "proxy-secret"
|
|
// err := k8sClient.Create(ctx, ephemeralRunner)
|
|
// Expect(err).To(BeNil(), "failed to create ephemeral runner")
|
|
//
|
|
// pod := new(corev1.Pod)
|
|
// Eventually(
|
|
// func(g Gomega) {
|
|
// err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, pod)
|
|
// g.Expect(err).To(BeNil(), "failed to get ephemeral runner pod")
|
|
// },
|
|
// timeout,
|
|
// interval,
|
|
// ).Should(Succeed(), "failed to get ephemeral runner pod")
|
|
//
|
|
// Expect(pod.Spec.Containers[0].Env).To(ContainElement(corev1.EnvVar{
|
|
// Name: EnvVarHTTPProxy,
|
|
// ValueFrom: &corev1.EnvVarSource{
|
|
// SecretKeyRef: &corev1.SecretKeySelector{
|
|
// LocalObjectReference: corev1.LocalObjectReference{
|
|
// Name: ephemeralRunner.Spec.ProxySecretRef,
|
|
// },
|
|
// Key: "http_proxy",
|
|
// },
|
|
// },
|
|
// }))
|
|
//
|
|
// Expect(pod.Spec.Containers[0].Env).To(ContainElement(corev1.EnvVar{
|
|
// Name: EnvVarHTTPSProxy,
|
|
// ValueFrom: &corev1.EnvVarSource{
|
|
// SecretKeyRef: &corev1.SecretKeySelector{
|
|
// LocalObjectReference: corev1.LocalObjectReference{
|
|
// Name: ephemeralRunner.Spec.ProxySecretRef,
|
|
// },
|
|
// Key: "https_proxy",
|
|
// },
|
|
// },
|
|
// }))
|
|
//
|
|
// Expect(pod.Spec.Containers[0].Env).To(ContainElement(corev1.EnvVar{
|
|
// Name: EnvVarNoProxy,
|
|
// ValueFrom: &corev1.EnvVarSource{
|
|
// SecretKeyRef: &corev1.SecretKeySelector{
|
|
// LocalObjectReference: corev1.LocalObjectReference{
|
|
// Name: ephemeralRunner.Spec.ProxySecretRef,
|
|
// },
|
|
// Key: "no_proxy",
|
|
// },
|
|
// },
|
|
// }))
|
|
// })
|
|
// })
|
|
//
|
|
// Describe("TLS config", func() {
|
|
// var ctx context.Context
|
|
// var mgr ctrl.Manager
|
|
// var autoScalingNS *corev1.Namespace
|
|
// var configSecret *corev1.Secret
|
|
// var controller *EphemeralRunnerReconciler
|
|
// var rootCAConfigMap *corev1.ConfigMap
|
|
//
|
|
// BeforeEach(func() {
|
|
// ctx = context.Background()
|
|
// autoScalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
|
// configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoScalingNS.Name)
|
|
//
|
|
// cert, err := os.ReadFile(filepath.Join(
|
|
// "../../",
|
|
// "github",
|
|
// "actions",
|
|
// "testdata",
|
|
// "rootCA.crt",
|
|
// ))
|
|
// Expect(err).NotTo(HaveOccurred(), "failed to read root CA cert")
|
|
// rootCAConfigMap = &corev1.ConfigMap{
|
|
// ObjectMeta: metav1.ObjectMeta{
|
|
// Name: "root-ca-configmap",
|
|
// Namespace: autoScalingNS.Name,
|
|
// },
|
|
// Data: map[string]string{
|
|
// "rootCA.crt": string(cert),
|
|
// },
|
|
// }
|
|
// err = k8sClient.Create(ctx, rootCAConfigMap)
|
|
// Expect(err).NotTo(HaveOccurred(), "failed to create configmap with root CAs")
|
|
//
|
|
// controller = &EphemeralRunnerReconciler{
|
|
// Client: mgr.GetClient(),
|
|
// Scheme: mgr.GetScheme(),
|
|
// Log: logf.Log,
|
|
// ActionsClient: fake.NewMultiClient(),
|
|
// }
|
|
//
|
|
// err = controller.SetupWithManager(mgr)
|
|
// Expect(err).To(BeNil(), "failed to setup controller")
|
|
//
|
|
// startManagers(GinkgoT(), mgr)
|
|
// })
|
|
//
|
|
// 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()
|
|
//
|
|
// // Use an actual client
|
|
// controller.ActionsClient = actions.NewMultiClient("test", logr.Discard())
|
|
//
|
|
// ephemeralRunner := newExampleRunner("test-runner", autoScalingNS.Name, configSecret.Name)
|
|
// ephemeralRunner.Spec.GitHubConfigUrl = server.ConfigURLForOrg("my-org")
|
|
// ephemeralRunner.Spec.GitHubServerTLS = &v1alpha1.GitHubServerTLSConfig{
|
|
// CertificateFrom: &v1alpha1.TLSCertificateSource{
|
|
// ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
|
|
// LocalObjectReference: corev1.LocalObjectReference{
|
|
// Name: rootCAConfigMap.Name,
|
|
// },
|
|
// Key: "rootCA.crt",
|
|
// },
|
|
// },
|
|
// }
|
|
//
|
|
// err = k8sClient.Create(ctx, ephemeralRunner)
|
|
// Expect(err).To(BeNil(), "failed to create ephemeral runner")
|
|
//
|
|
// Eventually(
|
|
// func() bool {
|
|
// return serverSuccessfullyCalled
|
|
// },
|
|
// 2*time.Second,
|
|
// interval,
|
|
// ).Should(BeTrue(), "failed to contact server")
|
|
// })
|
|
// })
|
|
// })
|