diff --git a/.github/workflows/smoke-test-s390x.yaml b/.github/workflows/smoke-test-s390x.yaml new file mode 100644 index 000000000..41495913e --- /dev/null +++ b/.github/workflows/smoke-test-s390x.yaml @@ -0,0 +1,11 @@ +name: s390x Runner Smoke Test +on: workflow_dispatch +jobs: + smoke-test: + runs-on: [self-hosted, S390X] + steps: + - name: Check architecture + run: | + echo "Architecture: $(uname -m)" + cat /etc/os-release | grep PRETTY_NAME + echo "Runner is alive on $(arch)" diff --git a/cmd/mapt/cmd/aws/hosts/fedora.go b/cmd/mapt/cmd/aws/hosts/fedora.go index 69c55f2c0..e02518d76 100644 --- a/cmd/mapt/cmd/aws/hosts/fedora.go +++ b/cmd/mapt/cmd/aws/hosts/fedora.go @@ -56,7 +56,7 @@ func getFedoraCreate() *cobra.Command { Debug: viper.IsSet(params.Debug), DebugLevel: viper.GetUint(params.DebugLevel), CirrusPWArgs: params.CirrusPersistentWorkerArgs(), - GHRunnerArgs: params.GithubRunnerArgs(), + GHRunnerArgs: params.GithubRunnerArgs(params.LinuxGithubArch()), GLRunnerArgs: params.GitLabRunnerArgs(params.LinuxGitLabArch()), Tags: viper.GetStringMapString(params.Tags), }, diff --git a/cmd/mapt/cmd/aws/hosts/mac.go b/cmd/mapt/cmd/aws/hosts/mac.go index b82aae28a..00b77de47 100644 --- a/cmd/mapt/cmd/aws/hosts/mac.go +++ b/cmd/mapt/cmd/aws/hosts/mac.go @@ -47,7 +47,7 @@ func getMacRequest() *cobra.Command { ResultsOutput: viper.GetString(params.ConnectionDetailsOutput), Debug: viper.IsSet(params.Debug), DebugLevel: viper.GetUint(params.DebugLevel), - GHRunnerArgs: params.GithubRunnerArgs(), + GHRunnerArgs: params.GithubRunnerArgs(params.LinuxGithubArch()), GLRunnerArgs: params.GitLabRunnerArgs(params.LinuxGitLabArch()), Tags: viper.GetStringMapString(params.Tags), }, diff --git a/cmd/mapt/cmd/aws/hosts/rhel.go b/cmd/mapt/cmd/aws/hosts/rhel.go index 6308913c7..e02056135 100644 --- a/cmd/mapt/cmd/aws/hosts/rhel.go +++ b/cmd/mapt/cmd/aws/hosts/rhel.go @@ -52,7 +52,7 @@ func getRHELCreate() *cobra.Command { Debug: viper.IsSet(params.Debug), DebugLevel: viper.GetUint(params.DebugLevel), CirrusPWArgs: params.CirrusPersistentWorkerArgs(), - GHRunnerArgs: params.GithubRunnerArgs(), + GHRunnerArgs: params.GithubRunnerArgs(params.LinuxGithubArch()), GLRunnerArgs: params.GitLabRunnerArgs(params.LinuxGitLabArch()), Tags: viper.GetStringMapString(params.Tags), }, diff --git a/cmd/mapt/cmd/aws/hosts/windows.go b/cmd/mapt/cmd/aws/hosts/windows.go index 00da29e9e..6cce4c08d 100644 --- a/cmd/mapt/cmd/aws/hosts/windows.go +++ b/cmd/mapt/cmd/aws/hosts/windows.go @@ -65,7 +65,7 @@ func getWindowsCreate() *cobra.Command { Debug: viper.IsSet(params.Debug), DebugLevel: viper.GetUint(params.DebugLevel), CirrusPWArgs: params.CirrusPersistentWorkerArgs(), - GHRunnerArgs: params.GithubRunnerArgs(), + GHRunnerArgs: params.GithubRunnerArgs(params.LinuxGithubArch()), GLRunnerArgs: params.GitLabRunnerArgs(params.LinuxGitLabArch()), Tags: viper.GetStringMapString(params.Tags), }, diff --git a/cmd/mapt/cmd/aws/services/mac-pool.go b/cmd/mapt/cmd/aws/services/mac-pool.go index 483965db1..6e39d796b 100644 --- a/cmd/mapt/cmd/aws/services/mac-pool.go +++ b/cmd/mapt/cmd/aws/services/mac-pool.go @@ -173,7 +173,7 @@ func request() *cobra.Command { Debug: viper.IsSet(params.Debug), DebugLevel: viper.GetUint(params.DebugLevel), CirrusPWArgs: params.CirrusPersistentWorkerArgs(), - GHRunnerArgs: params.GithubRunnerArgs(), + GHRunnerArgs: params.GithubRunnerArgs(params.LinuxGithubArch()), GLRunnerArgs: params.GitLabRunnerArgs(params.LinuxGitLabArch()), Tags: viper.GetStringMapString(params.Tags), }, diff --git a/cmd/mapt/cmd/azure/hosts/rhel.go b/cmd/mapt/cmd/azure/hosts/rhel.go index c6d26dead..73cd540dc 100644 --- a/cmd/mapt/cmd/azure/hosts/rhel.go +++ b/cmd/mapt/cmd/azure/hosts/rhel.go @@ -48,7 +48,7 @@ func getCreateRHEL() *cobra.Command { Debug: viper.IsSet(params.Debug), DebugLevel: viper.GetUint(params.DebugLevel), CirrusPWArgs: params.CirrusPersistentWorkerArgs(), - GHRunnerArgs: params.GithubRunnerArgs(), + GHRunnerArgs: params.GithubRunnerArgs(params.LinuxGithubArch()), GLRunnerArgs: params.GitLabRunnerArgs(params.LinuxGitLabArch()), Tags: viper.GetStringMapString(params.Tags), }, diff --git a/cmd/mapt/cmd/azure/hosts/windows.go b/cmd/mapt/cmd/azure/hosts/windows.go index e527a171d..a49cb9926 100644 --- a/cmd/mapt/cmd/azure/hosts/windows.go +++ b/cmd/mapt/cmd/azure/hosts/windows.go @@ -61,7 +61,7 @@ func getCreateWindowsDesktop() *cobra.Command { Debug: viper.IsSet(params.Debug), DebugLevel: viper.GetUint(params.DebugLevel), CirrusPWArgs: params.CirrusPersistentWorkerArgs(), - GHRunnerArgs: params.GithubRunnerArgs(), + GHRunnerArgs: params.GithubRunnerArgs(params.LinuxGithubArch()), GLRunnerArgs: params.GitLabRunnerArgs(params.LinuxGitLabArch()), Tags: viper.GetStringMapString(params.Tags), }, diff --git a/cmd/mapt/cmd/ibmcloud/hosts/ibm-power.go b/cmd/mapt/cmd/ibmcloud/hosts/ibm-power.go index b62855dec..9c1e7561b 100644 --- a/cmd/mapt/cmd/ibmcloud/hosts/ibm-power.go +++ b/cmd/mapt/cmd/ibmcloud/hosts/ibm-power.go @@ -2,6 +2,7 @@ package hosts import ( "github.com/redhat-developer/mapt/cmd/mapt/cmd/params" + "github.com/redhat-developer/mapt/pkg/integrations/github" "github.com/redhat-developer/mapt/pkg/integrations/gitlab" maptContext "github.com/redhat-developer/mapt/pkg/manager/context" ibmpower "github.com/redhat-developer/mapt/pkg/provider/ibmcloud/action/ibm-power" @@ -52,7 +53,7 @@ func ibmPowerCreate() *cobra.Command { Debug: viper.IsSet(params.Debug), DebugLevel: viper.GetUint(params.DebugLevel), CirrusPWArgs: params.CirrusPersistentWorkerArgs(), - GHRunnerArgs: params.GithubRunnerArgs(), + GHRunnerArgs: params.GithubRunnerArgs(&github.Ppc64le), GLRunnerArgs: params.GitLabRunnerArgs(&gitlab.Ppc64le), Tags: viper.GetStringMapString(params.Tags), }, diff --git a/cmd/mapt/cmd/ibmcloud/hosts/ibm-z.go b/cmd/mapt/cmd/ibmcloud/hosts/ibm-z.go index 31a0bebed..b5b372612 100644 --- a/cmd/mapt/cmd/ibmcloud/hosts/ibm-z.go +++ b/cmd/mapt/cmd/ibmcloud/hosts/ibm-z.go @@ -2,6 +2,7 @@ package hosts import ( "github.com/redhat-developer/mapt/cmd/mapt/cmd/params" + "github.com/redhat-developer/mapt/pkg/integrations/github" "github.com/redhat-developer/mapt/pkg/integrations/gitlab" maptContext "github.com/redhat-developer/mapt/pkg/manager/context" ibmz "github.com/redhat-developer/mapt/pkg/provider/ibmcloud/action/ibm-z" @@ -52,7 +53,7 @@ func ibmZCreate() *cobra.Command { Debug: viper.IsSet(params.Debug), DebugLevel: viper.GetUint(params.DebugLevel), CirrusPWArgs: params.CirrusPersistentWorkerArgs(), - GHRunnerArgs: params.GithubRunnerArgs(), + GHRunnerArgs: params.GithubRunnerArgs(&github.S390x), GLRunnerArgs: params.GitLabRunnerArgs(&gitlab.S390x), Tags: viper.GetStringMapString(params.Tags), }, diff --git a/cmd/mapt/cmd/params/params.go b/cmd/mapt/cmd/params/params.go index c0dfad656..ef6830240 100644 --- a/cmd/mapt/cmd/params/params.go +++ b/cmd/mapt/cmd/params/params.go @@ -1,6 +1,8 @@ package params import ( + "os" + "github.com/redhat-developer/mapt/pkg/integrations/cirrus" "github.com/redhat-developer/mapt/pkg/integrations/github" "github.com/redhat-developer/mapt/pkg/integrations/gitlab" @@ -155,8 +157,8 @@ const ( PIProcTypeDesc string = "PowerVS processor type (shared, dedicated, capped)" PIProcTypeDefault string = "shared" PISysType string = "pi-sys-type" - PISysTypeDesc string = "PowerVS system type (s922, s1022, e880, e980)" - PISysTypeDefault string = "s1022" + PISysTypeDesc string = "PowerVS system type (e.g. e1080, s1022, s1122)" + PISysTypeDefault string = "e1080" PIStorageType string = "pi-storage-type" PIStorageTypeDesc string = "PowerVS storage tier for instance and data volume (tier1, tier3)" PIStorageTypeDefault string = "tier1" @@ -282,18 +284,38 @@ func AddGHActionsFlags(fs *pflag.FlagSet) { fs.StringSlice(ghActionsRunnerLabels, nil, GHActionsRunnerLabelsDesc) } -func GithubRunnerArgs() *github.GithubRunnerArgs { - if viper.IsSet(ghActionsRunnerToken) { - return &github.GithubRunnerArgs{ - Token: viper.GetString(ghActionsRunnerToken), - RepoURL: viper.GetString(ghActionsRunnerRepo), - Labels: viper.GetStringSlice(ghActionsRunnerLabels), - Platform: &github.Linux, - Arch: linuxArchAsGithubActionsArch( - viper.GetString(LinuxArch)), +func GithubRunnerArgs(arch *github.Arch) *github.GithubRunnerArgs { + token := viper.GetString(ghActionsRunnerToken) + repoURL := viper.GetString(ghActionsRunnerRepo) + pat := os.Getenv("GITHUB_TOKEN") + + if token == "" && pat == "" { + return nil + } + + if repoURL == "" { + logging.Error("--ghactions-runner-repo is required for GitHub Actions runner setup") + return nil + } + + if token == "" { + logging.Info("no --ghactions-runner-token provided, auto-generating from GITHUB_TOKEN") + var err error + token, err = github.GenerateRegistrationToken(pat, repoURL) + if err != nil { + logging.Errorf("failed to auto-generate runner registration token: %v", err) + return nil } + logging.Info("runner registration token generated successfully") + } + + return &github.GithubRunnerArgs{ + Token: token, + RepoURL: repoURL, + Labels: viper.GetStringSlice(ghActionsRunnerLabels), + Platform: &github.Linux, + Arch: arch, } - return nil } func AddCirrusFlags(fs *pflag.FlagSet) { @@ -357,10 +379,18 @@ func linuxArchAsCirrusArch(arch string) *cirrus.Arch { return &cirrus.Arm64 } +func LinuxGithubArch() *github.Arch { + return linuxArchAsGithubActionsArch(viper.GetString(LinuxArch)) +} + func linuxArchAsGithubActionsArch(arch string) *github.Arch { switch arch { case "x86_64": return &github.Amd64 + case "ppc64le": + return &github.Ppc64le + case "s390x": + return &github.S390x } return &github.Arm64 } diff --git a/pkg/integrations/github/api.go b/pkg/integrations/github/api.go new file mode 100644 index 000000000..a592cfc60 --- /dev/null +++ b/pkg/integrations/github/api.go @@ -0,0 +1,68 @@ +package github + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + "time" +) + +type registrationTokenResponse struct { + Token string `json:"token"` + ExpiresAt string `json:"expires_at"` +} + +// GenerateRegistrationToken calls the GitHub API to create a short-lived +// runner registration token for the given repository. +// pat is a Personal Access Token with repo admin scope. +// repoURL is in the form "owner/repo" or "https://github.com/owner/repo". +func GenerateRegistrationToken(pat, repoURL string) (string, error) { + ownerRepo := repoURL + ownerRepo = strings.TrimPrefix(ownerRepo, "https://github.com/") + ownerRepo = strings.TrimPrefix(ownerRepo, "http://github.com/") + ownerRepo = strings.TrimSuffix(ownerRepo, "/") + + parts := strings.Split(ownerRepo, "/") + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + return "", fmt.Errorf("invalid repo format %q, expected owner/repo", repoURL) + } + + url := fmt.Sprintf("https://api.github.com/repos/%s/%s/actions/runners/registration-token", parts[0], parts[1]) + + req, err := http.NewRequest(http.MethodPost, url, nil) + if err != nil { + return "", fmt.Errorf("creating request: %w", err) + } + req.Header.Set("Authorization", "token "+pat) + req.Header.Set("Accept", "application/vnd.github+json") + req.Header.Set("X-GitHub-Api-Version", "2022-11-28") + + client := &http.Client{Timeout: 30 * time.Second} + resp, err := client.Do(req) + if err != nil { + return "", fmt.Errorf("calling GitHub API: %w", err) + } + defer func() { _ = resp.Body.Close() }() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", fmt.Errorf("reading response: %w", err) + } + + if resp.StatusCode != http.StatusCreated { + return "", fmt.Errorf("GitHub API returned %d: %s (ensure GITHUB_TOKEN has admin scope on the repo)", resp.StatusCode, string(body)) + } + + var tokenResp registrationTokenResponse + if err := json.Unmarshal(body, &tokenResp); err != nil { + return "", fmt.Errorf("parsing response: %w", err) + } + + if tokenResp.Token == "" { + return "", fmt.Errorf("empty token in GitHub API response") + } + + return tokenResp.Token, nil +} diff --git a/pkg/integrations/github/ghrunner.go b/pkg/integrations/github/ghrunner.go index ecb772b03..90ce87651 100644 --- a/pkg/integrations/github/ghrunner.go +++ b/pkg/integrations/github/ghrunner.go @@ -14,6 +14,9 @@ var runnerVersion = "2.317.0" // 1 is version, 2 is platform: (win, linux, osx), 3 is arch: (arm64, x64, arm) const runnerBaseURL = "https://github.com/actions/runner/releases/download/v%[1]s/actions-runner-%[2]s-%[3]s-%[1]s" +const runnerImageRepo = "https://github.com/aipcc-cicd/action-runner-image-pz.git" +const runnerImageRepoVersion = "v2.0.0" + //go:embed snippet-darwin.sh var snippetDarwin []byte @@ -23,12 +26,23 @@ var snippetLinux []byte //go:embed snippet-windows.ps1 var snippetWindows []byte +//go:embed snippet-linux-ppc64le.sh +var snippetLinuxPpc64le []byte + +//go:embed snippet-linux-s390x.sh +var snippetLinuxS390x []byte + var snippets map[Platform][]byte = map[Platform][]byte{ Darwin: snippetDarwin, Linux: snippetLinux, Windows: snippetWindows, } +var archSnippets map[Arch][]byte = map[Arch][]byte{ + Ppc64le: snippetLinuxPpc64le, + S390x: snippetLinuxS390x, +} + var runnerArgs *GithubRunnerArgs func Init(args *GithubRunnerArgs) { @@ -40,17 +54,23 @@ func (args *GithubRunnerArgs) GetUserDataValues() *integrations.UserDataValues { return nil } return &integrations.UserDataValues{ - Name: args.Name, - Token: args.Token, - Labels: getLabels(), - RepoURL: args.RepoURL, - CliURL: downloadURL(), + Name: args.Name, + Token: args.Token, + Labels: getLabels(), + RepoURL: args.RepoURL, + CliURL: downloadURL(), + RunnerImageRepo: runnerImageRepo, + RunnerImageRepoVersion: runnerImageRepoVersion, } } func (args *GithubRunnerArgs) GetSetupScriptTemplate() string { - templateConfig := string(snippets[*runnerArgs.Platform][:]) - return templateConfig + if *runnerArgs.Platform == Linux && runnerArgs.Arch != nil { + if archSnippet, ok := archSnippets[*runnerArgs.Arch]; ok { + return string(archSnippet[:]) + } + } + return string(snippets[*runnerArgs.Platform][:]) } func GetRunnerArgs() *GithubRunnerArgs { diff --git a/pkg/integrations/github/snippet-linux-ppc64le.sh b/pkg/integrations/github/snippet-linux-ppc64le.sh new file mode 100644 index 000000000..9670a1b78 --- /dev/null +++ b/pkg/integrations/github/snippet-linux-ppc64le.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set -euo pipefail + +dnf install -y git-core + +git clone --branch "{{ .RunnerImageRepoVersion }}" --depth=1 "{{ .RunnerImageRepo }}" /opt/action-runner-image-pz + +cd /opt/action-runner-image-pz +bash -c '. scripts/vm.sh rhel 9 minimal --skip-snap-lxd' || true + +# The upstream configure-system.sh runs chmod -R 777 /usr/share which breaks +# sshd privilege separation (/usr/share/empty.sshd must be root-owned, not +# world-writable). Also fix PAM duplicates from configure-limits.sh. +chmod 755 /usr/share/empty.sshd 2>/dev/null || true +chown root:root /usr/share/empty.sshd 2>/dev/null || true +for f in /etc/pam.d/system-auth /etc/pam.d/password-auth; do + if [ -f "$f" ]; then + awk '!seen[$0]++' "$f" > "${f}.tmp" && mv "${f}.tmp" "$f" + fi +done +systemctl restart sshd 2>/dev/null || true + +if [ ! -f /opt/runner-cache/config.sh ]; then + echo "Runner binary not found after build" >&2 + exit 1 +fi + +id -u runner &>/dev/null || useradd -m -s /bin/bash runner +chown -R runner:runner /opt/runner-cache + +sudo -u runner bash -c ' + cd /opt/runner-cache + + ./config.sh \ + --unattended \ + --disableupdate \ + --ephemeral \ + --name "{{ .Name }}" \ + --labels "{{ .Labels }}" \ + --url "{{ .RepoURL }}" \ + --token "{{ .Token }}" + + nohup ./run.sh > /tmp/gh-runner.log 2>&1 & +' diff --git a/pkg/integrations/github/snippet-linux-s390x.sh b/pkg/integrations/github/snippet-linux-s390x.sh new file mode 100644 index 000000000..e1e638494 --- /dev/null +++ b/pkg/integrations/github/snippet-linux-s390x.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -euo pipefail + +apt-get update -y && apt-get install -y software-properties-common + +git clone --branch "{{ .RunnerImageRepoVersion }}" --depth=1 "{{ .RunnerImageRepo }}" /opt/action-runner-image-pz + +cd /opt/action-runner-image-pz +# Allow build to continue past flaky upstream test failures +bash -c '. scripts/vm.sh ubuntu 22.04 minimal --skip-snap-lxd' || true + +if [ ! -f /opt/runner-cache/config.sh ]; then + echo "Runner binary not found after build — check build logs" >&2 + exit 1 +fi + +id -u runner &>/dev/null || useradd -m -s /bin/bash runner +chown -R runner:runner /opt/runner-cache + +sudo -u runner bash -c ' + cd /opt/runner-cache + + ./config.sh \ + --unattended \ + --disableupdate \ + --ephemeral \ + --name "{{ .Name }}" \ + --labels "{{ .Labels }}" \ + --url "{{ .RepoURL }}" \ + --token "{{ .Token }}" + + nohup ./run.sh > /tmp/gh-runner.log 2>&1 & +' diff --git a/pkg/integrations/github/types.go b/pkg/integrations/github/types.go index ccc8974d3..2d85f2665 100644 --- a/pkg/integrations/github/types.go +++ b/pkg/integrations/github/types.go @@ -8,9 +8,11 @@ var ( Linux Platform = "linux" Darwin Platform = "osx" - Arm64 Arch = "arm64" - Amd64 Arch = "x64" - Arm Arch = "arm" + Arm64 Arch = "arm64" + Amd64 Arch = "x64" + Arm Arch = "arm" + Ppc64le Arch = "ppc64le" + S390x Arch = "s390x" ) type GithubRunnerArgs struct { diff --git a/pkg/integrations/integrations.go b/pkg/integrations/integrations.go index 7ec6153de..ee1df5db3 100644 --- a/pkg/integrations/integrations.go +++ b/pkg/integrations/integrations.go @@ -17,6 +17,8 @@ type UserDataValues struct { Unsecure bool Concurrent int LogToJournald bool + RunnerImageRepo string + RunnerImageRepoVersion string } type IntegrationConfig interface { diff --git a/pkg/manager/context/context.go b/pkg/manager/context/context.go index 4454b578e..f98066bce 100644 --- a/pkg/manager/context/context.go +++ b/pkg/manager/context/context.go @@ -183,7 +183,7 @@ func addCommonTags(c *Context) { func manageIntegration(c *Context, ca *ContextArgs) error { if ca.GHRunnerArgs != nil { - ca.GHRunnerArgs.Name = c.RunID() + ca.GHRunnerArgs.Name = c.ProjectName() github.Init(ca.GHRunnerArgs) } if ca.CirrusPWArgs != nil { diff --git a/pkg/provider/ibmcloud/action/ibm-power/cloud-config b/pkg/provider/ibmcloud/action/ibm-power/cloud-config index 2053274ed..f99ad8789 100644 --- a/pkg/provider/ibmcloud/action/ibm-power/cloud-config +++ b/pkg/provider/ibmcloud/action/ibm-power/cloud-config @@ -12,6 +12,9 @@ write_files: else systemctl start multipathd 2>/dev/null || true DATA_DEV="" + ATTEMPTS=0 + MAX_ATTEMPTS=90 + while [ $ATTEMPTS -lt $MAX_ATTEMPTS ]; do _attempts=0 while true; do DATA_DEV=$(lsblk -rnpo NAME,TYPE | awk '$2=="mpath"{mpath[$1]=1} $2=="part"{p=$1; sub(/p?[0-9]+$/,"",p); has_part[p]=1} END{for(d in mpath) if(!has_part[d]&&d!~/control/) print d}' | head -1) @@ -21,9 +24,12 @@ write_files: udevadm trigger --subsystem-match=block 2>/dev/null || true udevadm settle 2>/dev/null || true multipathd reconfigure 2>/dev/null || true + ATTEMPTS=$((ATTEMPTS + 1)) sleep 10 done - if [ -n "$DATA_DEV" ]; then + if [ -z "$DATA_DEV" ]; then + echo "WARNING: data volume not found after $((MAX_ATTEMPTS * 10))s, using root filesystem" >&2 + else udevadm settle 2>/dev/null || true sleep 10 mkfs.xfs -f -K "$DATA_DEV" @@ -86,6 +92,13 @@ write_files: content: | {{.GitLabRunnerScript}} {{- end}} +{{- if .GHActionsRunnerScript}} + - path: /opt/install-ghrunner.sh + permissions: '0700' + owner: root:root + content: | +{{.GHActionsRunnerScript}} +{{- end}} runcmd: - systemctl enable mount-data-home.service - dnf install -y git podman policycoreutils-python-utils @@ -104,3 +117,10 @@ runcmd: - mkdir -p /var/log/gitlab-runner - bash /opt/install-glrunner.sh {{- end}} +{{- if .GHActionsRunnerScript}} + - | + export COS_KEY_ID="{{ .COSAccessKeyID }}" + export COS_SECRET="{{ .COSSecretAccessKey }}" + export COS_ENDPOINT="{{ .COSEndpoint }}" + bash /opt/install-ghrunner.sh +{{- end}} diff --git a/pkg/provider/ibmcloud/action/ibm-power/ibm-power.go b/pkg/provider/ibmcloud/action/ibm-power/ibm-power.go index c0c65c1c5..a1e758f0b 100644 --- a/pkg/provider/ibmcloud/action/ibm-power/ibm-power.go +++ b/pkg/provider/ibmcloud/action/ibm-power/ibm-power.go @@ -4,6 +4,7 @@ import ( _ "embed" "encoding/base64" "fmt" + "os" "strings" "github.com/mapt-oss/pulumi-ibmcloud/sdk/go/ibmcloud" @@ -11,6 +12,7 @@ import ( "github.com/pulumi/pulumi/sdk/v3/go/auto" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/redhat-developer/mapt/pkg/integrations" + "github.com/redhat-developer/mapt/pkg/integrations/github" "github.com/redhat-developer/mapt/pkg/integrations/gitlab" "github.com/redhat-developer/mapt/pkg/integrations/otelcol" "github.com/redhat-developer/mapt/pkg/manager" @@ -29,9 +31,13 @@ import ( var CloudConfig []byte type userDataValues struct { - Gateway string - OtelColScript string - GitLabRunnerScript string + Gateway string + OtelColScript string + GitLabRunnerScript string + GHActionsRunnerScript string + COSAccessKeyID string + COSSecretAccessKey string + COSEndpoint string } const ( @@ -140,7 +146,7 @@ func New(ctx *mc.ContextArgs, args *PWArgs) error { } sr, err := manager.UpStack(r.mCtx, cs) if err != nil { - return fmt.Errorf("stack creation failed: %w", err) + return err } return manageResults(mCtx, sr, prefix, r.vpcPublicSubnetID != "") } @@ -183,6 +189,15 @@ func (r *pwRequest) deploy(ctx *pulumi.Context) error { } hasOtel := otelSet == 3 + ghRunnerScript := "" + if ghRunnerArgs := github.GetRunnerArgs(); ghRunnerArgs != nil { + s, err := integrations.GetIntegrationSnippetAsCloudInitWritableFile(ghRunnerArgs, defaultUser) + if err != nil { + return err + } + ghRunnerScript = *s + } + var piUserDataInput pulumi.StringPtrInput glRunnerArgs := gitlab.GetRunnerArgs() if glRunnerArgs != nil { @@ -192,6 +207,7 @@ func (r *pwRequest) deploy(ctx *pulumi.Context) error { } gateway := subnetInfo.Gateway localArgs := *glRunnerArgs + localGHScript := ghRunnerScript localArgs.LogToJournald = hasOtel piUserDataInput = authToken.ApplyT(func(token string) (*string, error) { localArgs.AuthToken = token @@ -203,7 +219,7 @@ func (r *pwRequest) deploy(ctx *pulumi.Context) error { if hasOtel { otelArgs = r.otelArgs(true) } - ud, err := piUserData(gateway, otelArgs, *glSnippet) + ud, err := piUserData(gateway, otelArgs, *glSnippet, localGHScript) if err != nil { return nil, err } @@ -214,7 +230,7 @@ func (r *pwRequest) deploy(ctx *pulumi.Context) error { if hasOtel { otelArgs = r.otelArgs(false) } - ud, err := piUserData(subnetInfo.Gateway, otelArgs, "") + ud, err := piUserData(subnetInfo.Gateway, otelArgs, "", ghRunnerScript) if err != nil { return fmt.Errorf("failed to render user data: %w", err) } @@ -461,7 +477,7 @@ func (r *pwRequest) otelArgs(monitorGitLabRunner bool) *otelcol.OtelcolArgs { // piUserData renders the cloud-config template and returns it base64-encoded // for use as PiUserData on a PowerVS instance. -func piUserData(gateway string, otelArgs *otelcol.OtelcolArgs, glRunnerScript string) (string, error) { +func piUserData(gateway string, otelArgs *otelcol.OtelcolArgs, glRunnerScript, ghRunnerScript string) (string, error) { otelScript := "" if otelArgs != nil { s, err := otelcol.GetSnippetAsCloudInitWritableFile(otelArgs) @@ -472,9 +488,13 @@ func piUserData(gateway string, otelArgs *otelcol.OtelcolArgs, glRunnerScript st } script, err := file.Template( userDataValues{ - Gateway: gateway, - OtelColScript: otelScript, - GitLabRunnerScript: glRunnerScript, + Gateway: gateway, + OtelColScript: otelScript, + GitLabRunnerScript: glRunnerScript, + GHActionsRunnerScript: ghRunnerScript, + COSAccessKeyID: os.Getenv("IBMCLOUD_COS_ACCESS_KEY_ID"), + COSSecretAccessKey: os.Getenv("IBMCLOUD_COS_SECRET_ACCESS_KEY"), + COSEndpoint: os.Getenv("IBMCLOUD_COS_ENDPOINT"), }, string(CloudConfig)) if err != nil { diff --git a/pkg/provider/ibmcloud/action/ibm-power/ibm-power_test.go b/pkg/provider/ibmcloud/action/ibm-power/ibm-power_test.go index 12fee0b4d..d877bf8ec 100644 --- a/pkg/provider/ibmcloud/action/ibm-power/ibm-power_test.go +++ b/pkg/provider/ibmcloud/action/ibm-power/ibm-power_test.go @@ -9,7 +9,7 @@ import ( ) func TestPiUserData_noRunner(t *testing.T) { - out, err := piUserData("10.0.0.1", nil, "") + out, err := piUserData("10.0.0.1", nil, "", "") if err != nil { t.Fatalf("piUserData returned error: %v", err) } @@ -31,7 +31,7 @@ func TestPiUserData_noRunner(t *testing.T) { func TestPiUserData_withRunner(t *testing.T) { script := " #!/bin/bash\n echo hello" - out, err := piUserData("10.0.0.1", nil, script) + out, err := piUserData("10.0.0.1", nil, script, "") if err != nil { t.Fatalf("piUserData returned error: %v", err) } @@ -63,7 +63,7 @@ func TestPiUserData_withOtelAndRunner(t *testing.T) { SecurePath: "/var/log/secure", MonitorGitLabRunner: true, } - out, err := piUserData("10.0.0.1", args, script) + out, err := piUserData("10.0.0.1", args, script, "") if err != nil { t.Fatalf("piUserData returned error: %v", err) } diff --git a/pkg/provider/ibmcloud/action/ibm-z/cloud-config b/pkg/provider/ibmcloud/action/ibm-z/cloud-config index a1546c5d2..5f0bf1588 100644 --- a/pkg/provider/ibmcloud/action/ibm-z/cloud-config +++ b/pkg/provider/ibmcloud/action/ibm-z/cloud-config @@ -1,5 +1,5 @@ #cloud-config -{{- if or .OtelColScript .GitLabRunnerScript}} +{{- if or .OtelColScript .GitLabRunnerScript .GHActionsRunnerScript}} write_files: {{- if .OtelColScript}} - path: /opt/install-otelcol.sh @@ -33,6 +33,13 @@ write_files: content: | {{.GitLabRunnerScript}} {{- end}} +{{- if .GHActionsRunnerScript}} + - path: /opt/install-ghrunner.sh + permissions: '0700' + owner: root:root + content: | +{{.GHActionsRunnerScript}} +{{- end}} {{- end}} runcmd: - apt-get update -y @@ -44,3 +51,6 @@ runcmd: - mkdir -p /var/log/gitlab-runner - bash /opt/install-glrunner.sh {{- end}} +{{- if .GHActionsRunnerScript}} + - bash /opt/install-ghrunner.sh +{{- end}} diff --git a/pkg/provider/ibmcloud/action/ibm-z/ibm-z.go b/pkg/provider/ibmcloud/action/ibm-z/ibm-z.go index c2719d94d..1a4892b61 100644 --- a/pkg/provider/ibmcloud/action/ibm-z/ibm-z.go +++ b/pkg/provider/ibmcloud/action/ibm-z/ibm-z.go @@ -12,6 +12,7 @@ import ( "github.com/pulumi/pulumi/sdk/v3/go/auto" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/redhat-developer/mapt/pkg/integrations" + "github.com/redhat-developer/mapt/pkg/integrations/github" "github.com/redhat-developer/mapt/pkg/integrations/gitlab" "github.com/redhat-developer/mapt/pkg/integrations/otelcol" "github.com/redhat-developer/mapt/pkg/manager" @@ -31,8 +32,9 @@ import ( var CloudConfig []byte type userDataValues struct { - OtelColScript string - GitLabRunnerScript string + OtelColScript string + GitLabRunnerScript string + GHActionsRunnerScript string } const ( @@ -370,8 +372,19 @@ func (r *zRequest) buildUserDataInput() (pulumi.StringPtrInput, error) { return nil, fmt.Errorf("partial otel configuration: --otel-app-code, --otel-auth-token, and --otel-index must all be set together") } hasOtel := otelSet == 3 + + ghRunnerScript := "" + if ghRunnerArgs := github.GetRunnerArgs(); ghRunnerArgs != nil { + s, err := integrations.GetIntegrationSnippetAsCloudInitWritableFile(ghRunnerArgs, defaultUser) + if err != nil { + return nil, err + } + ghRunnerScript = *s + } + if r.glAuthToken != nil { localArgs := *r.glRunnerArgsCopy + localGHScript := ghRunnerScript localArgs.LogToJournald = hasOtel return r.glAuthToken.ApplyT(func(token string) (*string, error) { localArgs.AuthToken = token @@ -383,15 +396,19 @@ func (r *zRequest) buildUserDataInput() (pulumi.StringPtrInput, error) { if hasOtel { otelArgs = r.otelArgs(true) } - ud, err := izUserData(otelArgs, *glSnippet) + ud, err := izUserData(otelArgs, *glSnippet, localGHScript) if err != nil { return nil, err } return &ud, nil }).(pulumi.StringPtrOutput), nil } - if hasOtel { - ud, err := izUserData(r.otelArgs(false), "") + if hasOtel || ghRunnerScript != "" { + var otelArgs *otelcol.OtelcolArgs + if hasOtel { + otelArgs = r.otelArgs(false) + } + ud, err := izUserData(otelArgs, "", ghRunnerScript) if err != nil { return nil, fmt.Errorf("failed to render user data: %w", err) } @@ -414,7 +431,7 @@ func (r *zRequest) otelArgs(monitorGitLabRunner bool) *otelcol.OtelcolArgs { } } -func izUserData(otelArgs *otelcol.OtelcolArgs, glRunnerScript string) (string, error) { +func izUserData(otelArgs *otelcol.OtelcolArgs, glRunnerScript, ghRunnerScript string) (string, error) { otelScript := "" if otelArgs != nil { s, err := otelcol.GetSnippetAsCloudInitWritableFile(otelArgs) @@ -425,8 +442,9 @@ func izUserData(otelArgs *otelcol.OtelcolArgs, glRunnerScript string) (string, e } script, err := file.Template( userDataValues{ - OtelColScript: otelScript, - GitLabRunnerScript: glRunnerScript, + OtelColScript: otelScript, + GitLabRunnerScript: glRunnerScript, + GHActionsRunnerScript: ghRunnerScript, }, string(CloudConfig)) if err != nil { diff --git a/pkg/provider/ibmcloud/action/ibm-z/ibm-z_test.go b/pkg/provider/ibmcloud/action/ibm-z/ibm-z_test.go index b952fdebc..c2b84be7f 100644 --- a/pkg/provider/ibmcloud/action/ibm-z/ibm-z_test.go +++ b/pkg/provider/ibmcloud/action/ibm-z/ibm-z_test.go @@ -38,7 +38,7 @@ func decodeIzOutput(t *testing.T, out string) string { } func TestIzUserData_noRunner(t *testing.T) { - out, err := izUserData(nil, "") + out, err := izUserData(nil, "", "") if err != nil { t.Fatalf("izUserData returned error: %v", err) } @@ -56,7 +56,7 @@ func TestIzUserData_noRunner(t *testing.T) { func TestIzUserData_withRunner(t *testing.T) { script := " #!/bin/bash\n echo hello" - out, err := izUserData(nil, script) + out, err := izUserData(nil, script, "") if err != nil { t.Fatalf("izUserData returned error: %v", err) } @@ -81,7 +81,7 @@ func TestIzUserData_withOtelAndRunner(t *testing.T) { SecurePath: "/var/log/auth.log", MonitorGitLabRunner: true, } - out, err := izUserData(args, script) + out, err := izUserData(args, script, "") if err != nil { t.Fatalf("izUserData returned error: %v", err) }