2021-06-27 16:28:07 +09:00
package e2e
import (
"context"
"fmt"
2022-08-25 10:25:06 +09:00
"io"
"net/http"
2022-05-26 10:59:50 +09:00
"os"
2021-06-28 08:30:32 +09:00
"path/filepath"
2022-02-17 09:16:28 +09:00
"strconv"
2022-08-27 07:10:10 +00:00
"strings"
2021-06-27 16:28:07 +09:00
"time"
2022-12-28 01:38:34 +01:00
"github.com/actions/actions-runner-controller/testing"
2023-05-28 08:36:55 +01:00
"github.com/google/go-github/v52/github"
2021-06-28 08:30:32 +09:00
"github.com/onsi/gomega"
2022-11-01 20:30:10 +09:00
"github.com/stretchr/testify/require"
2022-08-25 10:25:06 +09:00
"golang.org/x/oauth2"
2021-06-28 08:30:32 +09:00
"sigs.k8s.io/yaml"
2021-06-27 16:28:07 +09:00
)
2022-07-01 21:32:05 +09:00
type DeployKind int
const (
RunnerSets DeployKind = iota
RunnerDeployments
)
2021-06-28 08:30:32 +09:00
var (
2022-08-25 04:41:55 +00:00
// See the below link for maintained versions of cert-manager
// https://cert-manager.io/docs/installation/supported-releases/
2022-07-07 11:27:49 +09:00
certManagerVersion = "v1.8.2"
2021-06-27 16:28:07 +09:00
2023-04-27 13:06:35 +09:00
arcStableImageRepo = "summerwind/actions-runner-controller"
arcStableImageTag = "v0.25.2"
2021-06-29 17:52:43 +09:00
testResultCMNamePrefix = "test-result-"
2022-08-27 07:07:56 +00:00
2024-08-20 12:22:06 +02:00
RunnerVersion = "2.319.1"
2024-07-26 13:52:44 -04:00
RunnerContainerHooksVersion = "0.6.1"
2021-06-28 08:30:32 +09:00
)
// If you're willing to run this test via VS Code "run test" or "debug test",
// almost certainly you'd want to make the default go test timeout from 30s to longer and enough value.
// Press Cmd + Shift + P, type "Workspace Settings" and open it, and type "go test timeout" and set e.g. 600s there.
// See https://github.com/golang/vscode-go/blob/master/docs/settings.md#gotesttimeout for more information.
//
// This tests ues testing.Logf extensively for debugging purpose.
// But messages logged via Logf shows up only when the test failed by default.
// To always enable logging, do not forget to pass `-test.v` to `go test`.
2021-11-09 09:04:19 +09:00
// If you're using VS Code, open `Workspace Settings` and search for `go test flags`, edit the `.vscode/settings.json` and put the below:
2022-08-25 10:25:06 +09:00
//
// "go.testFlags": ["-v"]
2021-06-28 08:30:32 +09:00
//
// This function requires a few environment variables to be set to provide some test data.
// If you're using VS Code and wanting to run this test locally,
// Browse "Workspace Settings" and search for "go test env file" and put e.g. "${workspaceFolder}/.test.env" there.
2021-06-29 17:52:43 +09:00
//
// Instead of relying on "stages" to make it possible to rerun individual tests like terratest,
// you use the "run subtest" feature provided by IDE like VS Code, IDEA, and GoLand.
// Our `testing` package automatically checks for the running test name and skips the cleanup tasks
// whenever the whole test failed, so that you can immediately start fixing issues and rerun inidividual tests.
// See the below link for how terratest handles this:
// https://terratest.gruntwork.io/docs/testing-best-practices/iterating-locally-using-test-stages/
2022-07-01 21:32:05 +09:00
//
// This functions leaves PVs undeleted. To delete PVs, run:
2022-08-25 10:25:06 +09:00
//
// kubectl get pv -ojson | jq -rMc '.items[] | select(.status.phase == "Available") | {name:.metadata.name, status:.status.phase} | .name' | xargs kubectl delete pv
2022-07-01 21:32:05 +09:00
//
// If you disk full after dozens of test runs, try:
2022-08-25 10:25:06 +09:00
//
// docker system prune
//
2022-07-01 21:32:05 +09:00
// and
2022-08-25 10:25:06 +09:00
//
// kind delete cluster --name teste2e
2022-07-01 21:32:05 +09:00
//
// The former tend to release 200MB-3GB and the latter can result in releasing like 100GB due to kind node contains loaded container images and
// (in case you use it) local provisioners disk image(which is implemented as a directory within the kind node).
2021-06-28 08:30:32 +09:00
func TestE2E ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "Skipped as -short is set" )
}
2021-06-27 16:28:07 +09:00
2022-07-06 08:57:21 +09:00
k8sMinorVer := os . Getenv ( "ARC_E2E_KUBE_VERSION" )
2022-07-01 21:32:05 +09:00
skipRunnerCleanUp := os . Getenv ( "ARC_E2E_SKIP_RUNNER_CLEANUP" ) != ""
retainCluster := os . Getenv ( "ARC_E2E_RETAIN_CLUSTER" ) != ""
skipTestIDCleanUp := os . Getenv ( "ARC_E2E_SKIP_TEST_ID_CLEANUP" ) != ""
2022-07-07 11:27:27 +09:00
skipArgoTunnelCleanUp := os . Getenv ( "ARC_E2E_SKIP_ARGO_TUNNEL_CLEAN_UP" ) != ""
2022-07-01 21:32:05 +09:00
2022-12-07 10:02:35 +00:00
vars := buildVars (
os . Getenv ( "ARC_E2E_IMAGE_REPO" ) ,
os . Getenv ( "UBUNTU_VERSION" ) ,
)
2022-07-07 20:48:07 +09:00
2024-03-18 12:46:30 +01:00
testedVersions := [ ] struct {
2022-08-25 10:25:06 +09:00
label string
controller , controllerVer string
chart , chartVer string
2023-03-28 04:18:07 +02:00
opt [ ] InstallARCOption
2022-08-25 10:25:06 +09:00
} {
{
label : "stable" ,
2023-04-27 13:06:35 +09:00
controller : arcStableImageRepo ,
controllerVer : arcStableImageTag ,
2023-01-13 07:15:05 +09:00
chart : "actions-runner-controller/actions-runner-controller" ,
2022-08-25 10:25:06 +09:00
// 0.20.2 accidentally added support for runner-status-update which isn't supported by ARC 0.25.2.
// With some chart values, the controller end up with crashlooping with `flag provided but not defined: -runner-status-update-hook`.
chartVer : "0.20.1" ,
} ,
{
label : "edge" ,
controller : vars . controllerImageRepo ,
controllerVer : vars . controllerImageTag ,
chart : "" ,
chartVer : "" ,
2023-03-28 04:18:07 +02:00
opt : [ ] InstallARCOption {
func ( ia * InstallARCConfig ) {
ia . GithubWebhookServerEnvName = "FOO"
ia . GithubWebhookServerEnvValue = "foo"
} ,
} ,
2022-08-25 10:25:06 +09:00
} ,
}
2022-07-07 20:48:07 +09:00
env := initTestEnv ( t , k8sMinorVer , vars )
if vt := os . Getenv ( "ARC_E2E_VERIFY_TIMEOUT" ) ; vt != "" {
var err error
env . VerifyTimeout , err = time . ParseDuration ( vt )
if err != nil {
t . Fatalf ( "Failed to parse duration %q: %v" , vt , err )
}
}
2022-08-25 04:43:03 +00:00
env . doDockerBuild = os . Getenv ( "ARC_E2E_DO_DOCKER_BUILD" ) != ""
2021-06-27 16:28:07 +09:00
2021-06-29 17:52:43 +09:00
t . Run ( "build and load images" , func ( t * testing . T ) {
env . buildAndLoadImages ( t )
} )
2021-06-27 16:28:07 +09:00
2022-07-07 20:48:07 +09:00
if t . Failed ( ) {
return
}
2021-06-29 17:52:43 +09:00
t . Run ( "install cert-manager" , func ( t * testing . T ) {
env . installCertManager ( t )
2021-06-27 16:28:07 +09:00
} )
2021-06-29 17:52:43 +09:00
if t . Failed ( ) {
return
}
2022-07-01 21:32:05 +09:00
t . Run ( "RunnerSets" , func ( t * testing . T ) {
2022-07-10 02:56:45 +00:00
if os . Getenv ( "ARC_E2E_SKIP_RUNNERSETS" ) != "" {
t . Skip ( "RunnerSets test has been skipped due to ARC_E2E_SKIP_RUNNERSETS" )
}
2024-03-18 12:46:30 +01:00
var testID string
2021-06-27 16:28:07 +09:00
2022-07-01 21:32:05 +09:00
t . Run ( "get or generate test ID" , func ( t * testing . T ) {
testID = env . GetOrGenerateTestID ( t )
} )
2021-06-27 16:28:07 +09:00
2022-07-01 21:32:05 +09:00
if ! skipTestIDCleanUp {
t . Cleanup ( func ( ) {
env . DeleteTestID ( t )
} )
}
2021-06-27 16:28:07 +09:00
2022-08-24 10:42:45 +09:00
if t . Failed ( ) {
return
}
2022-07-07 11:27:27 +09:00
t . Run ( "install argo-tunnel" , func ( t * testing . T ) {
env . installArgoTunnel ( t )
} )
if ! skipArgoTunnelCleanUp {
t . Cleanup ( func ( ) {
env . uninstallArgoTunnel ( t )
} )
}
2022-08-24 10:42:45 +09:00
if t . Failed ( ) {
return
}
2022-08-25 10:25:06 +09:00
for i , v := range testedVersions {
t . Run ( "install actions-runner-controller " + v . label , func ( t * testing . T ) {
t . Logf ( "Using controller %s:%s and chart %s:%s" , v . controller , v . controllerVer , v . chart , v . chartVer )
2023-03-28 04:18:07 +02:00
env . installActionsRunnerController ( t , v . controller , v . controllerVer , testID , v . chart , v . chartVer , v . opt ... )
2022-07-01 21:32:05 +09:00
} )
2021-06-27 16:28:07 +09:00
2022-08-25 10:25:06 +09:00
if t . Failed ( ) {
return
}
2022-08-24 10:42:45 +09:00
2022-08-25 10:25:06 +09:00
if i > 0 {
continue
}
2021-06-27 16:28:07 +09:00
2022-08-25 10:25:06 +09:00
t . Run ( "deploy runners" , func ( t * testing . T ) {
env . deploy ( t , RunnerSets , testID )
} )
if ! skipRunnerCleanUp {
t . Cleanup ( func ( ) {
env . undeploy ( t , RunnerSets , testID )
} )
}
if t . Failed ( ) {
return
}
2022-07-01 21:32:05 +09:00
}
2021-06-27 16:28:07 +09:00
2022-08-27 07:08:56 +00:00
t . Run ( "Install workflow" , func ( t * testing . T ) {
env . installActionsWorkflow ( t , RunnerSets , testID )
} )
if t . Failed ( ) {
return
}
2022-08-26 01:28:00 +00:00
ctx , cancel := context . WithCancel ( context . Background ( ) )
go func ( ) {
2023-01-13 07:15:37 +09:00
var cancelled bool
defer func ( ) {
if ! cancelled {
t . Logf ( "Stopping the continuous rolling-update of runners due to error(s)" )
}
cancel ( )
} ( )
2022-08-26 01:28:00 +00:00
for i := 1 ; ; i ++ {
2023-01-13 07:15:37 +09:00
if t . Failed ( ) {
cancelled = true
return
}
2022-08-26 01:28:00 +00:00
select {
case _ , ok := <- ctx . Done ( ) :
if ! ok {
t . Logf ( "Stopping the continuous rolling-update of runners" )
}
2023-01-13 07:15:37 +09:00
cancelled = true
2022-08-26 01:28:00 +00:00
default :
2022-08-27 07:11:17 +00:00
time . Sleep ( 60 * time . Second )
2022-08-26 01:28:00 +00:00
t . Run ( fmt . Sprintf ( "update runners attempt %d" , i ) , func ( t * testing . T ) {
env . deploy ( t , RunnerSets , testID , fmt . Sprintf ( "ROLLING_UPDATE_PHASE=%d" , i ) )
} )
}
}
} ( )
t . Cleanup ( func ( ) {
cancel ( )
} )
2022-07-01 21:32:05 +09:00
t . Run ( "Verify workflow run result" , func ( t * testing . T ) {
2023-01-13 07:15:37 +09:00
env . verifyActionsWorkflowRun ( t , ctx , testID )
2022-07-01 21:32:05 +09:00
} )
2021-06-27 16:28:07 +09:00
} )
2022-07-01 21:32:05 +09:00
t . Run ( "RunnerDeployments" , func ( t * testing . T ) {
2022-08-26 01:48:54 +00:00
if os . Getenv ( "ARC_E2E_SKIP_RUNNERDEPLOYMENT" ) != "" {
t . Skip ( "RunnerSets test has been skipped due to ARC_E2E_SKIP_RUNNERSETS" )
}
2024-03-18 12:46:30 +01:00
var testID string
2021-06-29 17:52:43 +09:00
2022-07-01 21:32:05 +09:00
t . Run ( "get or generate test ID" , func ( t * testing . T ) {
testID = env . GetOrGenerateTestID ( t )
} )
2021-06-28 08:30:32 +09:00
2022-07-01 21:32:05 +09:00
if ! skipTestIDCleanUp {
t . Cleanup ( func ( ) {
env . DeleteTestID ( t )
} )
}
2021-06-27 16:28:07 +09:00
2022-08-24 10:42:45 +09:00
if t . Failed ( ) {
return
}
2022-07-07 11:27:27 +09:00
t . Run ( "install argo-tunnel" , func ( t * testing . T ) {
env . installArgoTunnel ( t )
} )
if ! skipArgoTunnelCleanUp {
t . Cleanup ( func ( ) {
env . uninstallArgoTunnel ( t )
} )
}
2022-08-24 10:42:45 +09:00
if t . Failed ( ) {
return
}
2022-08-25 10:25:06 +09:00
for i , v := range testedVersions {
t . Run ( "install actions-runner-controller " + v . label , func ( t * testing . T ) {
t . Logf ( "Using controller %s:%s and chart %s:%s" , v . controller , v . controllerVer , v . chart , v . chartVer )
2023-03-28 04:18:07 +02:00
env . installActionsRunnerController ( t , v . controller , v . controllerVer , testID , v . chart , v . chartVer , v . opt ... )
2022-07-01 21:32:05 +09:00
} )
2022-08-25 10:25:06 +09:00
if t . Failed ( ) {
return
}
2022-08-24 10:42:45 +09:00
2022-08-25 10:25:06 +09:00
if i > 0 {
continue
}
2021-06-29 17:52:43 +09:00
2022-08-25 10:25:06 +09:00
t . Run ( "deploy runners" , func ( t * testing . T ) {
env . deploy ( t , RunnerDeployments , testID )
} )
if ! skipRunnerCleanUp {
t . Cleanup ( func ( ) {
env . undeploy ( t , RunnerDeployments , testID )
} )
}
if t . Failed ( ) {
return
}
2022-07-01 21:32:05 +09:00
}
2022-08-27 07:08:56 +00:00
t . Run ( "Install workflow" , func ( t * testing . T ) {
env . installActionsWorkflow ( t , RunnerDeployments , testID )
} )
if t . Failed ( ) {
return
}
2022-08-26 01:28:00 +00:00
ctx , cancel := context . WithCancel ( context . Background ( ) )
go func ( ) {
2023-01-13 07:15:37 +09:00
var cancelled bool
defer func ( ) {
if ! cancelled {
t . Logf ( "Stopping the continuous rolling-update of runners due to error(s)" )
}
cancel ( )
} ( )
2022-08-26 01:28:00 +00:00
for i := 1 ; ; i ++ {
2023-01-13 07:15:37 +09:00
if t . Failed ( ) {
cancelled = true
return
}
2022-08-26 01:28:00 +00:00
select {
case _ , ok := <- ctx . Done ( ) :
if ! ok {
t . Logf ( "Stopping the continuous rolling-update of runners" )
}
2023-01-13 07:15:37 +09:00
cancelled = true
2022-11-03 20:55:36 +09:00
return
2022-08-26 01:28:00 +00:00
default :
time . Sleep ( 10 * time . Second )
t . Run ( fmt . Sprintf ( "update runners - attempt %d" , i ) , func ( t * testing . T ) {
env . deploy ( t , RunnerDeployments , testID , fmt . Sprintf ( "ROLLING_UPDATE_PHASE=%d" , i ) )
} )
2022-11-01 20:30:10 +09:00
t . Run ( fmt . Sprintf ( "set deletiontimestamps on runner pods - attempt %d" , i ) , func ( t * testing . T ) {
env . setDeletionTimestampsOnRunningPods ( t , RunnerDeployments )
} )
2022-08-26 01:28:00 +00:00
}
}
} ( )
t . Cleanup ( func ( ) {
cancel ( )
} )
2022-07-01 21:32:05 +09:00
t . Run ( "Verify workflow run result" , func ( t * testing . T ) {
2023-01-13 07:15:37 +09:00
env . verifyActionsWorkflowRun ( t , ctx , testID )
2022-07-01 21:32:05 +09:00
} )
2021-06-28 08:30:32 +09:00
} )
2022-03-12 12:10:04 +00:00
2022-07-01 21:32:05 +09:00
if retainCluster {
2022-05-26 10:59:50 +09:00
t . FailNow ( )
}
2021-06-29 17:52:43 +09:00
}
2021-06-28 08:30:32 +09:00
2021-06-29 17:52:43 +09:00
type env struct {
* testing . Env
2021-06-28 08:30:32 +09:00
2022-07-07 20:48:07 +09:00
Kind * testing . Kind
2022-03-12 12:10:04 +00:00
// Uses GITHUB_APP_ID, GITHUB_APP_INSTALLATION_ID, and GITHUB_APP_PRIVATE_KEY
// to let ARC authenticate as a GitHub App
useApp bool
2021-06-29 17:52:43 +09:00
2022-07-01 21:32:05 +09:00
testName string
repoToCommit string
appID , appInstallationID , appPrivateKeyFile string
githubToken , testRepo , testOrg , testOrgRepo string
githubTokenWebhook string
2023-02-28 15:19:58 -08:00
createSecretsUsingHelm string
2022-07-01 21:32:05 +09:00
testEnterprise string
testEphemeral string
scaleDownDelaySecondsAfterScaleOut int64
minReplicas int64
dockerdWithinRunnerContainer bool
2022-08-24 10:42:45 +09:00
rootlessDocker bool
2022-08-25 04:43:03 +00:00
doDockerBuild bool
2022-08-24 10:42:45 +09:00
containerMode string
2022-08-25 04:44:22 +00:00
runnerServiceAccuontName string
2022-11-01 20:30:10 +09:00
runnerGracefulStopTimeout string
runnerTerminationGracePeriodSeconds string
2022-08-25 04:44:22 +00:00
runnerNamespace string
2022-11-04 01:46:58 +00:00
logFormat string
2022-07-07 20:48:07 +09:00
remoteKubeconfig string
2023-03-27 18:42:20 -05:00
admissionWebhooksTimeout string
2022-07-07 20:48:07 +09:00
imagePullSecretName string
2022-07-10 06:13:53 +00:00
imagePullPolicy string
2023-04-27 13:06:35 +09:00
dindSidecarRepositoryAndTag string
2023-03-28 01:43:33 +02:00
watchNamespace string
2022-07-07 20:48:07 +09:00
vars vars
VerifyTimeout time . Duration
2021-06-29 17:52:43 +09:00
}
2022-07-07 20:48:07 +09:00
type vars struct {
controllerImageRepo , controllerImageTag string
2022-08-24 10:42:45 +09:00
runnerImageRepo string
runnerDindImageRepo string
runnerRootlessDindImageRepo string
2022-07-07 20:48:07 +09:00
2023-04-27 13:06:35 +09:00
dindSidecarImageRepo , dindSidecarImageTag string
2022-07-07 20:48:07 +09:00
prebuildImages [ ] testing . ContainerImage
builds [ ] testing . DockerBuild
commonScriptEnv [ ] string
}
2022-12-07 10:02:35 +00:00
func buildVars ( repo , ubuntuVer string ) vars {
2022-07-07 20:48:07 +09:00
if repo == "" {
repo = "actionsrunnercontrollere2e"
}
var (
2022-08-24 10:42:45 +09:00
controllerImageRepo = repo + "/actions-runner-controller"
controllerImageTag = "e2e"
controllerImage = testing . Img ( controllerImageRepo , controllerImageTag )
runnerImageRepo = repo + "/actions-runner"
runnerDindImageRepo = repo + "/actions-runner-dind"
runnerRootlessDindImageRepo = repo + "/actions-runner-rootless-dind"
runnerImageTag = "e2e"
runnerImage = testing . Img ( runnerImageRepo , runnerImageTag )
runnerDindImage = testing . Img ( runnerDindImageRepo , runnerImageTag )
runnerRootlessDindImage = testing . Img ( runnerRootlessDindImageRepo , runnerImageTag )
2023-04-27 13:06:35 +09:00
dindSidecarImageRepo = "docker"
2023-11-27 04:25:19 +01:00
dindSidecarImageTag = "24.0.7-dind"
2023-04-27 13:06:35 +09:00
dindSidecarImage = testing . Img ( dindSidecarImageRepo , dindSidecarImageTag )
2022-07-07 20:48:07 +09:00
)
var vs vars
vs . controllerImageRepo , vs . controllerImageTag = controllerImageRepo , controllerImageTag
vs . runnerDindImageRepo = runnerDindImageRepo
2022-08-24 10:42:45 +09:00
vs . runnerRootlessDindImageRepo = runnerRootlessDindImageRepo
2022-07-07 20:48:07 +09:00
vs . runnerImageRepo = runnerImageRepo
2023-04-27 13:06:35 +09:00
vs . dindSidecarImageRepo = dindSidecarImageRepo
vs . dindSidecarImageTag = dindSidecarImageTag
2022-07-07 20:48:07 +09:00
// vs.controllerImage, vs.controllerImageTag
vs . prebuildImages = [ ] testing . ContainerImage {
controllerImage ,
runnerImage ,
runnerDindImage ,
2022-08-24 10:42:45 +09:00
runnerRootlessDindImage ,
2023-04-27 13:06:35 +09:00
dindSidecarImage ,
2022-07-07 20:48:07 +09:00
}
vs . builds = [ ] testing . DockerBuild {
{
Dockerfile : "../../Dockerfile" ,
Args : [ ] testing . BuildArg { } ,
Image : controllerImage ,
EnableBuildX : true ,
} ,
{
2022-12-07 10:02:35 +00:00
Dockerfile : fmt . Sprintf ( "../../runner/actions-runner.ubuntu-%s.dockerfile" , ubuntuVer ) ,
2022-07-07 20:48:07 +09:00
Args : [ ] testing . BuildArg {
{
Name : "RUNNER_VERSION" ,
2022-08-27 07:07:56 +00:00
Value : RunnerVersion ,
2022-07-07 20:48:07 +09:00
} ,
2023-10-16 22:45:25 +09:00
{
Name : "RUNNER_CONTAINER_HOOKS_VERSION" ,
Value : RunnerContainerHooksVersion ,
} ,
2022-07-07 20:48:07 +09:00
} ,
Image : runnerImage ,
EnableBuildX : true ,
} ,
{
2022-12-07 10:02:35 +00:00
Dockerfile : fmt . Sprintf ( "../../runner/actions-runner-dind.ubuntu-%s.dockerfile" , ubuntuVer ) ,
2022-07-07 20:48:07 +09:00
Args : [ ] testing . BuildArg {
{
Name : "RUNNER_VERSION" ,
2022-08-27 07:07:56 +00:00
Value : RunnerVersion ,
2022-07-07 20:48:07 +09:00
} ,
2023-10-16 22:45:25 +09:00
{
Name : "RUNNER_CONTAINER_HOOKS_VERSION" ,
Value : RunnerContainerHooksVersion ,
} ,
2022-07-07 20:48:07 +09:00
} ,
Image : runnerDindImage ,
EnableBuildX : true ,
} ,
2022-08-24 10:42:45 +09:00
{
2022-12-07 10:02:35 +00:00
Dockerfile : fmt . Sprintf ( "../../runner/actions-runner-dind-rootless.ubuntu-%s.dockerfile" , ubuntuVer ) ,
2022-08-24 10:42:45 +09:00
Args : [ ] testing . BuildArg {
{
Name : "RUNNER_VERSION" ,
2022-08-27 07:07:56 +00:00
Value : RunnerVersion ,
2022-08-24 10:42:45 +09:00
} ,
2023-10-16 22:45:25 +09:00
{
Name : "RUNNER_CONTAINER_HOOKS_VERSION" ,
Value : RunnerContainerHooksVersion ,
} ,
2022-08-24 10:42:45 +09:00
} ,
Image : runnerRootlessDindImage ,
EnableBuildX : true ,
} ,
2022-07-07 20:48:07 +09:00
}
vs . commonScriptEnv = [ ] string {
"SYNC_PERIOD=" + "30s" ,
"RUNNER_TAG=" + runnerImageTag ,
}
return vs
}
func initTestEnv ( t * testing . T , k8sMinorVer string , vars vars ) * env {
2021-06-29 17:52:43 +09:00
t . Helper ( )
2022-07-07 20:48:07 +09:00
testingEnv := testing . Start ( t , k8sMinorVer )
2021-06-29 17:52:43 +09:00
e := & env { Env : testingEnv }
2022-07-01 21:32:05 +09:00
testName := t . Name ( )
2021-06-28 08:30:32 +09:00
2022-02-19 09:24:12 +00:00
t . Logf ( "Initializing test with name %s" , testName )
2021-06-28 08:30:32 +09:00
2022-02-19 09:24:12 +00:00
e . testName = testName
2021-06-29 17:52:43 +09:00
e . githubToken = testing . Getenv ( t , "GITHUB_TOKEN" )
2022-03-12 12:10:04 +00:00
e . appID = testing . Getenv ( t , "GITHUB_APP_ID" )
e . appInstallationID = testing . Getenv ( t , "GITHUB_APP_INSTALLATION_ID" )
e . appPrivateKeyFile = testing . Getenv ( t , "GITHUB_APP_PRIVATE_KEY_FILE" )
2022-02-17 09:16:28 +09:00
e . githubTokenWebhook = testing . Getenv ( t , "WEBHOOK_GITHUB_TOKEN" )
2023-02-28 15:19:58 -08:00
e . createSecretsUsingHelm = testing . Getenv ( t , "CREATE_SECRETS_USING_HELM" )
2022-02-17 09:16:28 +09:00
e . repoToCommit = testing . Getenv ( t , "TEST_COMMIT_REPO" )
e . testRepo = testing . Getenv ( t , "TEST_REPO" , "" )
e . testOrg = testing . Getenv ( t , "TEST_ORG" , "" )
e . testOrgRepo = testing . Getenv ( t , "TEST_ORG_REPO" , "" )
2022-03-13 13:11:26 +00:00
e . testEnterprise = testing . Getenv ( t , "TEST_ENTERPRISE" , "" )
2022-03-13 07:22:04 +00:00
e . testEphemeral = testing . Getenv ( t , "TEST_EPHEMERAL" , "" )
2022-08-25 04:44:22 +00:00
e . runnerServiceAccuontName = testing . Getenv ( t , "TEST_RUNNER_SERVICE_ACCOUNT_NAME" , "" )
2022-11-01 20:30:10 +09:00
e . runnerTerminationGracePeriodSeconds = testing . Getenv ( t , "TEST_RUNNER_TERMINATION_GRACE_PERIOD_SECONDS" , "30" )
e . runnerGracefulStopTimeout = testing . Getenv ( t , "TEST_RUNNER_GRACEFUL_STOP_TIMEOUT" , "15" )
2022-08-25 04:44:22 +00:00
e . runnerNamespace = testing . Getenv ( t , "TEST_RUNNER_NAMESPACE" , "default" )
2022-11-04 01:46:58 +00:00
e . logFormat = testing . Getenv ( t , "ARC_E2E_LOG_FORMAT" , "" )
2022-07-07 20:48:07 +09:00
e . remoteKubeconfig = testing . Getenv ( t , "ARC_E2E_REMOTE_KUBECONFIG" , "" )
2023-03-27 18:42:20 -05:00
e . admissionWebhooksTimeout = testing . Getenv ( t , "ARC_E2E_ADMISSION_WEBHOOKS_TIMEOUT" , "" )
2022-07-07 20:48:07 +09:00
e . imagePullSecretName = testing . Getenv ( t , "ARC_E2E_IMAGE_PULL_SECRET_NAME" , "" )
2023-04-27 13:06:35 +09:00
// This should be the default for Ubuntu 20.04 based runner images
e . dindSidecarRepositoryAndTag = vars . dindSidecarImageRepo + ":" + vars . dindSidecarImageTag
2022-07-07 20:48:07 +09:00
e . vars = vars
2022-07-10 06:13:53 +00:00
if e . remoteKubeconfig != "" {
e . imagePullPolicy = "Always"
} else {
e . imagePullPolicy = "IfNotPresent"
}
2023-03-28 01:43:33 +02:00
e . watchNamespace = testing . Getenv ( t , "TEST_WATCH_NAMESPACE" , "" )
2022-07-07 20:48:07 +09:00
if e . remoteKubeconfig == "" {
2023-04-27 13:06:35 +09:00
images := [ ] testing . ContainerImage {
testing . Img ( vars . dindSidecarImageRepo , vars . dindSidecarImageTag ) ,
testing . Img ( "quay.io/brancz/kube-rbac-proxy" , "v0.10.0" ) ,
testing . Img ( "quay.io/jetstack/cert-manager-controller" , certManagerVersion ) ,
testing . Img ( "quay.io/jetstack/cert-manager-cainjector" , certManagerVersion ) ,
testing . Img ( "quay.io/jetstack/cert-manager-webhook" , certManagerVersion ) ,
// Otherwise kubelet would fail to pull images from DockerHub due too rate limit:
// Warning Failed 19s kubelet Failed to pull image "summerwind/actions-runner-controller:v0.25.2": rpc error: code = Unknown desc = failed to pull and unpack image "docker.io/summerwind/actions-runner-controller:v0.25.2": failed to copy: httpReadSeeker: failed open: unexpected status code https://registry-1.docker.io/v2/summerwind/actions-runner-controller/manifests/sha256:92faf7e9f7f09a6240cdb5eb82eaf448852bdddf2fb77d0a5669fd8e5062b97b: 429 Too Many Requests - Server message: toomanyrequests: You have reached your pull rate limit. You may increase the limit by authenticating and upgrading: https://www.docker.com/increase-rate-limit
testing . Img ( arcStableImageRepo , arcStableImageTag ) ,
}
2022-07-07 20:48:07 +09:00
e . Kind = testing . StartKind ( t , k8sMinorVer , testing . Preload ( images ... ) )
e . Env . Kubeconfig = e . Kind . Kubeconfig ( )
} else {
e . Env . Kubeconfig = e . remoteKubeconfig
// Kind automatically installs https://github.com/rancher/local-path-provisioner for PVs.
// But assuming the remote cluster isn't a kind Kubernetes cluster,
// we need to install any provisioner manually.
// Here, we install the local-path-provisioner on the remote cluster too,
// so that we won't suffer from E2E failures due to the provisioner difference.
e . KubectlApply ( t , "https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.22/deploy/local-path-storage.yaml" , testing . KubectlConfig { } )
}
2022-03-12 14:08:23 +00:00
2022-02-20 13:40:03 +00:00
e . scaleDownDelaySecondsAfterScaleOut , _ = strconv . ParseInt ( testing . Getenv ( t , "TEST_RUNNER_SCALE_DOWN_DELAY_SECONDS_AFTER_SCALE_OUT" , "10" ) , 10 , 32 )
e . minReplicas , _ = strconv . ParseInt ( testing . Getenv ( t , "TEST_RUNNER_MIN_REPLICAS" , "1" ) , 10 , 32 )
2021-06-28 08:30:32 +09:00
2022-02-20 04:37:15 +00:00
var err error
e . dockerdWithinRunnerContainer , err = strconv . ParseBool ( testing . Getenv ( t , "TEST_RUNNER_DOCKERD_WITHIN_RUNNER_CONTAINER" , "false" ) )
if err != nil {
panic ( fmt . Sprintf ( "unable to parse bool from TEST_RUNNER_DOCKERD_WITHIN_RUNNER_CONTAINER: %v" , err ) )
}
2022-08-24 10:42:45 +09:00
e . rootlessDocker , err = strconv . ParseBool ( testing . Getenv ( t , "TEST_RUNNER_ROOTLESS_DOCKER" , "false" ) )
if err != nil {
panic ( fmt . Sprintf ( "unable to parse bool from TEST_RUNNER_ROOTLESS_DOCKER: %v" , err ) )
}
e . containerMode = testing . Getenv ( t , "TEST_CONTAINER_MODE" , "" )
if err != nil {
panic ( fmt . Sprintf ( "unable to parse bool from TEST_CONTAINER_MODE: %v" , err ) )
}
2022-08-25 10:25:06 +09:00
if err := e . checkGitHubToken ( t , e . githubToken ) ; err != nil {
t . Fatal ( err )
}
if err := e . checkGitHubToken ( t , e . githubTokenWebhook ) ; err != nil {
t . Fatal ( err )
}
2021-06-29 17:52:43 +09:00
return e
}
2022-08-25 10:25:06 +09:00
func ( e * env ) checkGitHubToken ( t * testing . T , tok string ) error {
t . Helper ( )
ctx := context . Background ( )
transport := oauth2 . NewClient ( ctx , oauth2 . StaticTokenSource ( & oauth2 . Token { AccessToken : tok } ) ) . Transport
c := github . NewClient ( & http . Client { Transport : transport } )
aa , res , err := c . Octocat ( context . Background ( ) , "hello" )
if err != nil {
2022-08-26 01:48:36 +00:00
b , ioerr := io . ReadAll ( res . Body )
if ioerr != nil {
t . Logf ( "%v" , ioerr )
2022-08-25 10:25:06 +09:00
return err
}
t . Logf ( string ( b ) )
return err
}
t . Logf ( "%s" , aa )
2022-08-27 07:10:10 +00:00
if e . testEnterprise != "" {
if _ , res , err := c . Enterprise . CreateRegistrationToken ( ctx , e . testEnterprise ) ; err != nil {
b , ioerr := io . ReadAll ( res . Body )
if ioerr != nil {
t . Logf ( "%v" , ioerr )
return err
}
t . Logf ( string ( b ) )
return err
}
}
if e . testOrg != "" {
if _ , res , err := c . Actions . CreateOrganizationRegistrationToken ( ctx , e . testOrg ) ; err != nil {
b , ioerr := io . ReadAll ( res . Body )
if ioerr != nil {
t . Logf ( "%v" , ioerr )
return err
}
t . Logf ( string ( b ) )
return err
}
}
if e . testRepo != "" {
s := strings . Split ( e . testRepo , "/" )
owner , repo := s [ 0 ] , s [ 1 ]
if _ , res , err := c . Actions . CreateRegistrationToken ( ctx , owner , repo ) ; err != nil {
b , ioerr := io . ReadAll ( res . Body )
if ioerr != nil {
t . Logf ( "%v" , ioerr )
return err
}
t . Logf ( string ( b ) )
2022-08-25 10:25:06 +09:00
return err
}
}
return nil
}
2021-06-29 17:52:43 +09:00
func ( e * env ) buildAndLoadImages ( t * testing . T ) {
t . Helper ( )
2022-07-07 20:48:07 +09:00
e . DockerBuild ( t , e . vars . builds )
if e . remoteKubeconfig == "" {
e . KindLoadImages ( t , e . vars . prebuildImages )
} else {
// If it fails with `no basic auth credentials` here, you might have missed logging into the container registry beforehand.
// For ECR, run something like:
// aws ecr get-login-password | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com
// Also note that the authenticated session can be expired in a day or so(probably depends on your AWS config),
// so you might better write a script to do docker login before running the E2E test.
e . DockerPush ( t , e . vars . prebuildImages )
}
}
func ( e * env ) KindLoadImages ( t * testing . T , prebuildImages [ ] testing . ContainerImage ) {
t . Helper ( )
ctx , cancel := context . WithTimeout ( context . Background ( ) , 300 * time . Second )
defer cancel ( )
if err := e . Kind . LoadImages ( ctx , prebuildImages ) ; err != nil {
t . Fatal ( err )
}
2021-06-29 17:52:43 +09:00
}
func ( e * env ) installCertManager ( t * testing . T ) {
t . Helper ( )
applyCfg := testing . KubectlConfig { NoValidate : true }
e . KubectlApply ( t , fmt . Sprintf ( "https://github.com/jetstack/cert-manager/releases/download/%s/cert-manager.yaml" , certManagerVersion ) , applyCfg )
waitCfg := testing . KubectlConfig {
Namespace : "cert-manager" ,
Timeout : 90 * time . Second ,
2021-06-27 16:28:07 +09:00
}
2021-06-29 17:52:43 +09:00
e . KubectlWaitUntilDeployAvailable ( t , "cert-manager-cainjector" , waitCfg )
e . KubectlWaitUntilDeployAvailable ( t , "cert-manager-webhook" , waitCfg . WithTimeout ( 60 * time . Second ) )
e . KubectlWaitUntilDeployAvailable ( t , "cert-manager" , waitCfg . WithTimeout ( 60 * time . Second ) )
}
2021-06-28 08:30:32 +09:00
2023-03-28 04:18:07 +02:00
type InstallARCConfig struct {
GithubWebhookServerEnvName , GithubWebhookServerEnvValue string
}
type InstallARCOption func ( * InstallARCConfig )
func ( e * env ) installActionsRunnerController ( t * testing . T , repo , tag , testID , chart , chartVer string , opts ... InstallARCOption ) {
2021-06-29 17:52:43 +09:00
t . Helper ( )
2021-06-28 08:30:32 +09:00
2023-03-28 04:18:07 +02:00
var c InstallARCConfig
for _ , opt := range opts {
opt ( & c )
}
2021-06-29 17:52:43 +09:00
e . createControllerNamespaceAndServiceAccount ( t )
2021-06-28 08:30:32 +09:00
2021-06-29 17:52:43 +09:00
scriptEnv := [ ] string {
2022-07-07 20:48:07 +09:00
"KUBECONFIG=" + e . Kubeconfig ,
2021-06-29 17:52:43 +09:00
"ACCEPTANCE_TEST_DEPLOYMENT_TOOL=" + "helm" ,
2022-08-25 10:25:06 +09:00
"CHART=" + chart ,
"CHART_VERSION=" + chartVer ,
2021-06-28 08:30:32 +09:00
}
2021-06-29 17:52:43 +09:00
varEnv := [ ] string {
2022-02-17 09:16:28 +09:00
"WEBHOOK_GITHUB_TOKEN=" + e . githubTokenWebhook ,
2023-02-28 15:19:58 -08:00
"CREATE_SECRETS_USING_HELM=" + e . createSecretsUsingHelm ,
2022-07-01 21:32:05 +09:00
"TEST_ID=" + testID ,
"NAME=" + repo ,
"VERSION=" + tag ,
2023-03-27 18:42:20 -05:00
"ADMISSION_WEBHOOKS_TIMEOUT=" + e . admissionWebhooksTimeout ,
2022-07-07 20:48:07 +09:00
"IMAGE_PULL_SECRET=" + e . imagePullSecretName ,
2022-07-10 06:13:53 +00:00
"IMAGE_PULL_POLICY=" + e . imagePullPolicy ,
2023-04-27 13:06:35 +09:00
"DIND_SIDECAR_REPOSITORY_AND_TAG=" + e . dindSidecarRepositoryAndTag ,
2023-03-28 01:43:33 +02:00
"WATCH_NAMESPACE=" + e . watchNamespace ,
2021-06-29 14:34:27 +09:00
}
2022-03-12 12:10:04 +00:00
if e . useApp {
varEnv = append ( varEnv ,
"ACCEPTANCE_TEST_SECRET_TYPE=app" ,
"APP_ID=" + e . appID ,
"APP_INSTALLATION_ID=" + e . appInstallationID ,
"APP_PRIVATE_KEY_FILE=" + e . appPrivateKeyFile ,
)
} else {
varEnv = append ( varEnv ,
"ACCEPTANCE_TEST_SECRET_TYPE=token" ,
"GITHUB_TOKEN=" + e . githubToken ,
)
}
2022-11-04 01:46:58 +00:00
if e . logFormat != "" {
varEnv = append ( varEnv ,
"LOG_FORMAT=" + e . logFormat ,
)
}
2023-03-28 04:18:07 +02:00
varEnv = append ( varEnv ,
"GITHUB_WEBHOOK_SERVER_ENV_NAME=" + c . GithubWebhookServerEnvName ,
"GITHUB_WEBHOOK_SERVER_ENV_VALUE=" + c . GithubWebhookServerEnvValue ,
)
2022-07-01 21:32:05 +09:00
scriptEnv = append ( scriptEnv , varEnv ... )
2022-07-07 20:48:07 +09:00
scriptEnv = append ( scriptEnv , e . vars . commonScriptEnv ... )
2022-07-01 21:32:05 +09:00
e . RunScript ( t , "../../acceptance/deploy.sh" , testing . ScriptConfig { Dir : "../.." , Env : scriptEnv } )
}
2022-08-26 01:28:00 +00:00
func ( e * env ) deploy ( t * testing . T , kind DeployKind , testID string , env ... string ) {
2022-07-01 21:32:05 +09:00
t . Helper ( )
2022-08-26 01:28:00 +00:00
e . do ( t , "apply" , kind , testID , env ... )
2022-07-01 21:32:05 +09:00
}
func ( e * env ) undeploy ( t * testing . T , kind DeployKind , testID string ) {
t . Helper ( )
e . do ( t , "delete" , kind , testID )
}
2022-11-01 20:30:10 +09:00
func ( e * env ) setDeletionTimestampsOnRunningPods ( t * testing . T , deployKind DeployKind ) {
t . Helper ( )
var scope , kind , labelKind string
if e . testOrg != "" {
scope = "org"
} else if e . testEnterprise != "" {
scope = "enterprise"
} else {
scope = "repo"
}
if deployKind == RunnerDeployments {
kind = "runnerdeploy"
labelKind = "runner-deployment"
} else {
kind = "runnerset"
labelKind = "runnerset"
}
label := fmt . Sprintf ( "%s-name=%s-%s" , labelKind , scope , kind )
ctx := context . Background ( )
c := e . getKubectlConfig ( )
t . Logf ( "Finding pods with label %s" , label )
pods , err := e . Kubectl . FindPods ( ctx , label , c )
require . NoError ( t , err )
if len ( pods ) == 0 {
return
}
t . Logf ( "Setting deletionTimestamps on pods %s" , strings . Join ( pods , ", " ) )
err = e . Kubectl . DeletePods ( ctx , pods , c )
require . NoError ( t , err )
t . Logf ( "Deleted pods %s" , strings . Join ( pods , ", " ) )
}
2022-08-26 01:28:00 +00:00
func ( e * env ) do ( t * testing . T , op string , kind DeployKind , testID string , env ... string ) {
2022-07-01 21:32:05 +09:00
t . Helper ( )
e . createControllerNamespaceAndServiceAccount ( t )
scriptEnv := [ ] string {
2022-07-07 20:48:07 +09:00
"KUBECONFIG=" + e . Kubeconfig ,
2022-07-01 21:32:05 +09:00
"OP=" + op ,
2022-08-25 04:44:22 +00:00
"RUNNER_NAMESPACE=" + e . runnerNamespace ,
"RUNNER_SERVICE_ACCOUNT_NAME=" + e . runnerServiceAccuontName ,
2022-11-01 20:30:10 +09:00
"RUNNER_GRACEFUL_STOP_TIMEOUT=" + e . runnerGracefulStopTimeout ,
"RUNNER_TERMINATION_GRACE_PERIOD_SECONDS=" + e . runnerTerminationGracePeriodSeconds ,
2022-07-01 21:32:05 +09:00
}
2022-08-26 01:28:00 +00:00
scriptEnv = append ( scriptEnv , env ... )
2022-07-01 21:32:05 +09:00
switch kind {
case RunnerSets :
scriptEnv = append ( scriptEnv , "USE_RUNNERSET=1" )
case RunnerDeployments :
scriptEnv = append ( scriptEnv , "USE_RUNNERSET=false" )
default :
t . Fatalf ( "Invalid deploy kind %v" , kind )
}
varEnv := [ ] string {
"TEST_ENTERPRISE=" + e . testEnterprise ,
"TEST_REPO=" + e . testRepo ,
"TEST_ORG=" + e . testOrg ,
"TEST_ORG_REPO=" + e . testOrgRepo ,
"RUNNER_LABEL=" + e . runnerLabel ( testID ) ,
"TEST_EPHEMERAL=" + e . testEphemeral ,
fmt . Sprintf ( "RUNNER_SCALE_DOWN_DELAY_SECONDS_AFTER_SCALE_OUT=%d" , e . scaleDownDelaySecondsAfterScaleOut ) ,
fmt . Sprintf ( "REPO_RUNNER_MIN_REPLICAS=%d" , e . minReplicas ) ,
fmt . Sprintf ( "ORG_RUNNER_MIN_REPLICAS=%d" , e . minReplicas ) ,
fmt . Sprintf ( "ENTERPRISE_RUNNER_MIN_REPLICAS=%d" , e . minReplicas ) ,
2022-08-24 10:42:45 +09:00
"RUNNER_CONTAINER_MODE=" + e . containerMode ,
}
if e . dockerdWithinRunnerContainer && e . containerMode == "kubernetes" {
t . Fatalf ( "TEST_RUNNER_DOCKERD_WITHIN_RUNNER_CONTAINER cannot be set along with TEST_CONTAINER_MODE=kubernetes" )
t . FailNow ( )
2022-07-01 21:32:05 +09:00
}
2022-02-20 04:37:15 +00:00
if e . dockerdWithinRunnerContainer {
varEnv = append ( varEnv ,
"RUNNER_DOCKERD_WITHIN_RUNNER_CONTAINER=true" ,
)
2022-08-24 10:42:45 +09:00
if e . rootlessDocker {
varEnv = append ( varEnv ,
"RUNNER_NAME=" + e . vars . runnerRootlessDindImageRepo ,
)
} else {
varEnv = append ( varEnv ,
"RUNNER_NAME=" + e . vars . runnerDindImageRepo ,
)
}
2022-02-20 04:37:15 +00:00
} else {
varEnv = append ( varEnv ,
"RUNNER_DOCKERD_WITHIN_RUNNER_CONTAINER=false" ,
2022-07-07 20:48:07 +09:00
"RUNNER_NAME=" + e . vars . runnerImageRepo ,
2022-02-20 04:37:15 +00:00
)
}
2021-06-29 17:52:43 +09:00
scriptEnv = append ( scriptEnv , varEnv ... )
2022-07-07 20:48:07 +09:00
scriptEnv = append ( scriptEnv , e . vars . commonScriptEnv ... )
2021-06-29 17:52:43 +09:00
2022-07-01 21:32:05 +09:00
e . RunScript ( t , "../../acceptance/deploy_runners.sh" , testing . ScriptConfig { Dir : "../.." , Env : scriptEnv } )
}
2022-07-07 11:27:27 +09:00
func ( e * env ) installArgoTunnel ( t * testing . T ) {
e . doArgoTunnel ( t , "apply" )
}
func ( e * env ) uninstallArgoTunnel ( t * testing . T ) {
e . doArgoTunnel ( t , "delete" )
}
func ( e * env ) doArgoTunnel ( t * testing . T , op string ) {
t . Helper ( )
scriptEnv := [ ] string {
2022-07-07 20:48:07 +09:00
"KUBECONFIG=" + e . Kubeconfig ,
2022-07-07 11:27:27 +09:00
"OP=" + op ,
"TUNNEL_ID=" + os . Getenv ( "TUNNEL_ID" ) ,
"TUNNE_NAME=" + os . Getenv ( "TUNNEL_NAME" ) ,
"TUNNEL_HOSTNAME=" + os . Getenv ( "TUNNEL_HOSTNAME" ) ,
}
e . RunScript ( t , "../../acceptance/argotunnel.sh" , testing . ScriptConfig { Dir : "../.." , Env : scriptEnv } )
}
2022-07-01 21:32:05 +09:00
func ( e * env ) runnerLabel ( testID string ) string {
return "test-" + testID
2021-06-29 17:52:43 +09:00
}
func ( e * env ) createControllerNamespaceAndServiceAccount ( t * testing . T ) {
t . Helper ( )
e . KubectlEnsureNS ( t , "actions-runner-system" , testing . KubectlConfig { } )
e . KubectlEnsureClusterRoleBindingServiceAccount ( t , "default-admin" , "cluster-admin" , "default:default" , testing . KubectlConfig { } )
}
2022-07-01 21:32:05 +09:00
func ( e * env ) installActionsWorkflow ( t * testing . T , kind DeployKind , testID string ) {
2021-06-29 17:52:43 +09:00
t . Helper ( )
2022-08-25 04:43:03 +00:00
installActionsWorkflow ( t , e . testName + " " + testID , e . runnerLabel ( testID ) , testResultCMNamePrefix , e . repoToCommit , kind , e . testJobs ( testID ) , ! e . rootlessDocker , e . doDockerBuild )
2022-07-01 21:32:05 +09:00
}
func ( e * env ) testJobs ( testID string ) [ ] job {
return createTestJobs ( testID , testResultCMNamePrefix , 6 )
2021-06-29 17:52:43 +09:00
}
2023-01-13 07:15:37 +09:00
func ( e * env ) verifyActionsWorkflowRun ( t * testing . T , ctx context . Context , testID string ) {
2021-06-29 17:52:43 +09:00
t . Helper ( )
2023-01-13 07:15:37 +09:00
verifyActionsWorkflowRun ( t , ctx , e . Env , e . testJobs ( testID ) , e . verifyTimeout ( ) , e . getKubectlConfig ( ) )
2022-07-07 20:48:07 +09:00
}
func ( e * env ) verifyTimeout ( ) time . Duration {
if e . VerifyTimeout > 0 {
return e . VerifyTimeout
}
return 8 * 60 * time . Second
2021-06-29 17:52:43 +09:00
}
2022-11-01 20:30:10 +09:00
func ( e * env ) getKubectlConfig ( ) testing . KubectlConfig {
kubectlEnv := [ ] string {
"KUBECONFIG=" + e . Kubeconfig ,
}
cmCfg := testing . KubectlConfig {
Env : kubectlEnv ,
}
return cmCfg
}
2021-06-29 17:52:43 +09:00
type job struct {
name , testArg , configMapName string
}
func createTestJobs ( id , testResultCMNamePrefix string , numJobs int ) [ ] job {
2021-06-29 14:34:27 +09:00
var testJobs [ ] job
for i := 0 ; i < numJobs ; i ++ {
name := fmt . Sprintf ( "test%d" , i )
testArg := fmt . Sprintf ( "%s%d" , id , i )
configMapName := testResultCMNamePrefix + testArg
testJobs = append ( testJobs , job { name : name , testArg : testArg , configMapName : configMapName } )
}
2021-06-29 17:52:43 +09:00
return testJobs
}
2022-02-17 09:16:28 +09:00
const Branch = "main"
2022-08-24 10:42:45 +09:00
// useSudo also implies rootful docker and the use of buildx cache export/import
2022-08-25 04:43:03 +00:00
func installActionsWorkflow ( t * testing . T , testName , runnerLabel , testResultCMNamePrefix , testRepo string , kind DeployKind , testJobs [ ] job , useSudo , doDockerBuild bool ) {
2021-06-29 17:52:43 +09:00
t . Helper ( )
ctx , cancel := context . WithTimeout ( context . Background ( ) , 30 * time . Second )
defer cancel ( )
2022-02-19 09:24:12 +00:00
wfName := "E2E " + testName
2021-06-29 17:52:43 +09:00
wf := testing . Workflow {
Name : wfName ,
On : testing . On {
Push : & testing . Push {
2022-02-17 09:16:28 +09:00
Branches : [ ] string { Branch } ,
2021-06-28 08:30:32 +09:00
} ,
2021-06-29 17:52:43 +09:00
} ,
Jobs : map [ string ] testing . Job { } ,
}
2021-06-29 14:34:27 +09:00
2022-06-30 09:09:58 +09:00
kubernetesContainerMode := os . Getenv ( "TEST_CONTAINER_MODE" ) == "kubernetes"
var container string
if kubernetesContainerMode {
container = "golang:1.18"
}
2021-06-29 17:52:43 +09:00
for _ , j := range testJobs {
2022-06-30 09:09:58 +09:00
steps := [ ] testing . Step {
{
Uses : testing . ActionsCheckout ,
} ,
}
2022-08-24 10:42:45 +09:00
var sudo string
if useSudo {
sudo = "sudo "
}
2022-06-30 09:09:58 +09:00
if ! kubernetesContainerMode {
2022-07-01 21:32:05 +09:00
if kind == RunnerDeployments {
steps = append ( steps ,
testing . Step {
2022-08-24 10:42:45 +09:00
Run : sudo + "mkdir -p \"${RUNNER_TOOL_CACHE}\" \"${HOME}/.cache\"" ,
2022-07-01 21:32:05 +09:00
} ,
)
2022-08-24 10:42:45 +09:00
if useSudo {
steps = append ( steps ,
testing . Step {
// This might be the easiest way to handle permissions without use of securityContext
// https://stackoverflow.com/questions/50156124/kubernetes-nfs-persistent-volumes-permission-denied#comment107483717_53186320
Run : sudo + "mkdir -p \"/var/lib/docker\"" ,
} ,
)
}
2022-07-01 21:32:05 +09:00
}
2022-08-24 10:42:45 +09:00
if useSudo {
steps = append ( steps ,
2022-08-27 07:12:06 +00:00
testing . Step {
// This might be the easiest way to handle permissions without use of securityContext
// https://stackoverflow.com/questions/50156124/kubernetes-nfs-persistent-volumes-permission-denied#comment107483717_53186320
Run : sudo + "chmod 777 -R \"${RUNNER_TOOL_CACHE}\" \"${HOME}/.cache\"" ,
} ,
2022-08-24 10:42:45 +09:00
testing . Step {
Run : sudo + "chmod 777 -R \"/var/lib/docker\"" ,
} ,
2022-08-27 07:12:06 +00:00
testing . Step {
// This might be the easiest way to handle permissions without use of securityContext
// https://stackoverflow.com/questions/50156124/kubernetes-nfs-persistent-volumes-permission-denied#comment107483717_53186320
Run : "ls -lah \"${RUNNER_TOOL_CACHE}\" \"${HOME}/.cache\"" ,
} ,
testing . Step {
// This might be the easiest way to handle permissions without use of securityContext
// https://stackoverflow.com/questions/50156124/kubernetes-nfs-persistent-volumes-permission-denied#comment107483717_53186320
Run : "ls -lah \"/var/lib/docker\" || echo ls failed." ,
} ,
2022-08-24 10:42:45 +09:00
)
}
steps = append ( steps ,
2022-06-30 09:09:58 +09:00
testing . Step {
2022-05-16 09:26:48 +09:00
Uses : "actions/setup-go@v3" ,
With : & testing . With {
2024-06-17 10:36:23 +02:00
GoVersion : "1.22.4" ,
2022-05-16 09:26:48 +09:00
} ,
} ,
2022-06-30 09:09:58 +09:00
)
2023-03-28 21:40:10 -04:00
// Ensure both the alias and the full command work after
// https://github.com/actions/actions-runner-controller/pull/2326
steps = append ( steps ,
testing . Step {
Run : "docker-compose version" ,
} ,
testing . Step {
Run : "docker compose version" ,
} ,
)
2022-06-30 09:09:58 +09:00
}
steps = append ( steps ,
testing . Step {
Run : "go version" ,
} ,
testing . Step {
Run : "go build ." ,
} ,
)
2022-08-25 04:43:03 +00:00
if doDockerBuild {
if ! kubernetesContainerMode {
setupBuildXActionWith := & testing . With {
BuildkitdFlags : "--debug" ,
// As the consequence of setting `install: false`, it doesn't install buildx as an alias to `docker build`
// so we need to use `docker buildx build` in the next step
Install : false ,
}
var dockerBuildCache , dockerfile string
if useSudo {
// This needs to be set only when rootful docker mode.
// When rootless, we need to use the `docker` buildx driver, which doesn't support cache export
// so we end up with the below error on docker-build:
// error: cache export feature is currently not supported for docker driver. Please switch to a different driver (eg. "docker buildx create --use")
2022-11-01 20:30:10 +09:00
// See https://docs.docker.com/engine/reference/commandline/buildx_create/#docker-container-driver
// for the `docker-container` driver.
2022-08-25 04:43:03 +00:00
dockerBuildCache = "--cache-from=type=local,src=/home/runner/.cache/buildx " +
"--cache-to=type=local,dest=/home/runner/.cache/buildx-new,mode=max "
dockerfile = "Dockerfile"
2022-11-01 20:30:10 +09:00
// Note though, if the cache does not exist yet, the buildx build seem to write cache data to /home/runner/.cache/buildx,
// not buildx-new.
// I think the following message emitted by buildx in the end is relevant to this behaviour, but not 100% sure:
// WARNING: local cache import at /home/runner/.cache/buildx not found due to err: could not read /home/runner/.cache/buildx/index.json: open /home/runner/.cache/buildx/index.json: no such file or directory
2022-08-25 04:43:03 +00:00
} else {
2022-11-01 20:30:10 +09:00
// See https://docs.docker.com/engine/reference/commandline/buildx_create/#docker-driver
// for the `docker` driver.
2022-08-25 04:43:03 +00:00
setupBuildXActionWith . Driver = "docker"
dockerfile = "Dockerfile.nocache"
}
2023-03-27 22:29:16 -04:00
useCustomDockerContext := os . Getenv ( "ARC_E2E_USE_CUSTOM_DOCKER_CONTEXT" ) != ""
if useCustomDockerContext {
setupBuildXActionWith . Endpoint = "mycontext"
steps = append ( steps , testing . Step {
2022-08-25 04:43:03 +00:00
// https://github.com/docker/buildx/issues/413#issuecomment-710660155
// To prevent setup-buildx-action from failing with:
// error: could not create a builder instance with TLS data loaded from environment. Please use `docker context create <context-name>` to create a context for current environment and then create a builder instance with `docker buildx create <context-name>`
Run : "docker context create mycontext" ,
} ,
2023-03-27 22:29:16 -04:00
testing . Step {
Run : "docker context use mycontext" ,
} ,
)
}
steps = append ( steps ,
2022-08-25 04:43:03 +00:00
testing . Step {
Name : "Set up Docker Buildx" ,
Uses : "docker/setup-buildx-action@v1" ,
With : setupBuildXActionWith ,
2022-08-24 10:42:45 +09:00
} ,
testing . Step {
2023-04-27 13:06:35 +09:00
Run : "docker buildx build --platform=linux/amd64 -t test1 --load " +
2022-08-25 04:43:03 +00:00
dockerBuildCache +
fmt . Sprintf ( "-f %s ." , dockerfile ) ,
2022-08-24 10:42:45 +09:00
} ,
2023-04-27 13:06:35 +09:00
testing . Step {
Run : "docker run --rm test1" ,
} ,
testing . Step {
Uses : "addnab/docker-run-action@v3" ,
With : & testing . With {
Image : "test1" ,
Run : "hello" ,
Shell : "sh" ,
} ,
} ,
2022-08-24 10:42:45 +09:00
)
2022-11-01 20:30:10 +09:00
if useSudo {
steps = append ( steps ,
testing . Step {
// https://github.com/docker/build-push-action/blob/master/docs/advanced/cache.md#local-cache
// See https://github.com/moby/buildkit/issues/1896 for why this is needed
Run : "if -d /home/runner/.cache/buildx-new; then " + sudo + "rm -rf /home/runner/.cache/buildx && " + sudo + ` mv /home/runner/.cache/buildx-new /home/runner/.cache/buildx; else echo "/home/runner/.cache/buildx-new is not found. Perhaps you're running this on a stateleess runner?"; fi ` ,
} ,
testing . Step {
Run : "ls -lah /home/runner/.cache/*" ,
} ,
)
}
2022-08-24 10:42:45 +09:00
}
2022-11-01 20:30:10 +09:00
if useSudo {
if kind == RunnerDeployments {
steps = append ( steps ,
testing . Step {
// https://github.com/docker/build-push-action/blob/master/docs/advanced/cache.md#local-cache
// See https://github.com/moby/buildkit/issues/1896 for why this is needed
Run : sudo + "rm -rf /home/runner/.cache/buildx && mv /home/runner/.cache/buildx-new /home/runner/.cache/buildx" ,
} ,
testing . Step {
Run : sudo + "ls -lah /home/runner/.cache/*" ,
} ,
)
}
}
2022-06-30 09:09:58 +09:00
}
2022-08-25 04:43:03 +00:00
steps = append ( steps ,
testing . Step {
Uses : "azure/setup-kubectl@v1" ,
With : & testing . With {
2024-06-17 10:36:23 +02:00
Version : "v1.22.4" ,
2022-08-25 04:43:03 +00:00
} ,
} ,
testing . Step {
Run : fmt . Sprintf ( "./test.sh %s %s" , t . Name ( ) , j . testArg ) ,
} ,
)
2022-06-30 09:09:58 +09:00
wf . Jobs [ j . name ] = testing . Job {
RunsOn : runnerLabel ,
Container : container ,
Steps : steps ,
2021-06-28 08:30:32 +09:00
}
2021-06-29 17:52:43 +09:00
}
2021-06-28 08:30:32 +09:00
2021-06-29 17:52:43 +09:00
wfContent , err := yaml . Marshal ( wf )
if err != nil {
t . Fatal ( err )
}
2021-06-28 08:30:32 +09:00
2021-06-29 17:52:43 +09:00
script := [ ] byte ( fmt . Sprintf ( ` #!/usr/bin/env bash
2021-06-28 08:30:32 +09:00
set -vx
2021-06-29 14:34:27 +09:00
name=$1
id=$2
echo hello from $name
kubectl delete cm %s$id || true
kubectl create cm %s$id --from-literal=status=ok
` , testResultCMNamePrefix , testResultCMNamePrefix ) )
2021-06-28 08:30:32 +09:00
2021-06-29 17:52:43 +09:00
g := testing . GitRepo {
Dir : filepath . Join ( t . TempDir ( ) , "gitrepo" ) ,
Name : testRepo ,
CommitMessage : wfName ,
Contents : map [ string ] [ ] byte {
".github/workflows/workflow.yaml" : wfContent ,
"test.sh" : script ,
} ,
2022-02-17 09:16:28 +09:00
Branch : Branch ,
2021-06-29 17:52:43 +09:00
}
2021-06-28 08:30:32 +09:00
2021-06-29 17:52:43 +09:00
if err := g . Sync ( ctx ) ; err != nil {
t . Fatal ( err )
2021-06-28 08:30:32 +09:00
}
2021-06-29 17:52:43 +09:00
}
2021-06-28 08:30:32 +09:00
2023-01-13 07:15:37 +09:00
func verifyActionsWorkflowRun ( t * testing . T , ctx context . Context , env * testing . Env , testJobs [ ] job , timeout time . Duration , cmCfg testing . KubectlConfig ) {
2021-06-29 17:52:43 +09:00
t . Helper ( )
2021-06-29 14:34:27 +09:00
2021-06-29 17:52:43 +09:00
var expected [ ] string
2022-09-20 20:08:22 -04:00
for range testJobs {
2021-06-29 17:52:43 +09:00
expected = append ( expected , "ok" )
}
2021-06-29 14:34:27 +09:00
2023-01-13 07:15:37 +09:00
gomega . NewGomegaWithT ( t ) . Eventually ( ctx , func ( ) ( [ ] string , error ) {
2021-06-29 17:52:43 +09:00
var results [ ] string
2021-06-29 14:34:27 +09:00
2021-06-29 17:52:43 +09:00
var errs [ ] error
2021-06-29 14:34:27 +09:00
2021-06-29 17:52:43 +09:00
for i := range testJobs {
testResultCMName := testJobs [ i ] . configMapName
2021-06-29 14:34:27 +09:00
2021-06-29 17:52:43 +09:00
ctx , cancel := context . WithTimeout ( context . Background ( ) , 10 * time . Second )
defer cancel ( )
2021-06-29 14:34:27 +09:00
2021-06-29 17:52:43 +09:00
m , err := env . Kubectl . GetCMLiterals ( ctx , testResultCMName , cmCfg )
if err != nil {
errs = append ( errs , err )
} else {
result := m [ "status" ]
results = append ( results , result )
2021-06-29 14:34:27 +09:00
}
2021-06-29 17:52:43 +09:00
}
2021-06-28 08:30:32 +09:00
2021-06-29 17:52:43 +09:00
var err error
2021-06-28 08:30:32 +09:00
2021-06-29 17:52:43 +09:00
if len ( errs ) > 0 {
var msg string
2021-06-28 08:30:32 +09:00
2021-06-29 17:52:43 +09:00
for i , e := range errs {
msg += fmt . Sprintf ( "error%d: %v\n" , i , e )
}
2021-06-28 08:30:32 +09:00
2021-06-29 17:52:43 +09:00
err = fmt . Errorf ( "%d errors occurred: %s" , len ( errs ) , msg )
}
2021-06-28 08:30:32 +09:00
2021-06-29 17:52:43 +09:00
return results , err
2022-07-07 20:48:07 +09:00
} , timeout , 30 * time . Second ) . Should ( gomega . Equal ( expected ) )
2021-06-27 16:28:07 +09:00
}