-
Notifications
You must be signed in to change notification settings - Fork 35
feat(ibmcloud): add GitHub Actions runner support for IBM Power and IBM Z #831
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 24 commits
c9a4b05
9af3e13
f68b509
68f31f4
78d423b
753b7ef
9e03696
7829062
a6893f0
60e93b8
ef87753
265345e
de8fc1f
9f02ad6
246c1c0
28e55f7
107ab7f
43b2865
93c91ee
7c2ee65
a1d6416
baad7fb
d75b79c
9761706
a8e6f9c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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)" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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,41 @@ 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") | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is this?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the user doesn't provide |
||
|
|
||
| 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") | ||
| } | ||
|
|
||
| if arch == nil { | ||
| arch = linuxArchAsGithubActionsArch(viper.GetString(LinuxArch)) | ||
| } | ||
| return &github.GithubRunnerArgs{ | ||
| Token: token, | ||
| RepoURL: repoURL, | ||
| Labels: viper.GetStringSlice(ghActionsRunnerLabels), | ||
| Platform: &github.Linux, | ||
| Arch: arch, | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func AddCirrusFlags(fs *pflag.FlagSet) { | ||
|
|
@@ -361,6 +386,10 @@ 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 | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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]) | ||
|
deekay2310 marked this conversation as resolved.
|
||
|
|
||
| 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 | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
|
|
||
| dnf install -y git-core | ||
|
|
||
| git clone --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 \ | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we explore options in config to only install the cli and set it up to join as runner?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Once the fork is moved into |
||
| --unattended \ | ||
| --disableupdate \ | ||
| --ephemeral \ | ||
| --name "{{ .Name }}" \ | ||
| --labels "{{ .Labels }}" \ | ||
| --url "{{ .RepoURL }}" \ | ||
| --token "{{ .Token }}" | ||
|
|
||
| nohup ./run.sh > /tmp/gh-runner.log 2>&1 & | ||
| ' | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should have the arch, in this case instead of fixed arch value it is coming from another flag
--arch, this applies to almost all targetsThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed it