Skip to content

Commit d9e5de0

Browse files
committed
feat(apple-silicon): add support runner
1 parent 9c06917 commit d9e5de0

File tree

3 files changed

+605
-0
lines changed

3 files changed

+605
-0
lines changed
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
package applesilicon
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9+
applesilicon "github.com/scaleway/scaleway-sdk-go/api/applesilicon/v1alpha1"
10+
"github.com/scaleway/scaleway-sdk-go/scw"
11+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/httperrors"
12+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/zonal"
13+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/account"
14+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/types"
15+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/verify"
16+
)
17+
18+
func ResourceRunner() *schema.Resource {
19+
return &schema.Resource{
20+
CreateContext: ResourceAppleSiliconRunnerCreate,
21+
ReadContext: ResourceAppleSiliconRunnerRead,
22+
UpdateContext: ResourceAppleSiliconRunnerUpdate,
23+
DeleteContext: ResourceAppleSiliconRunnerDelete,
24+
Timeouts: &schema.ResourceTimeout{
25+
Create: schema.DefaultTimeout(5 * time.Minute),
26+
Default: schema.DefaultTimeout(5 * time.Minute),
27+
},
28+
Importer: &schema.ResourceImporter{
29+
StateContext: schema.ImportStatePassthroughContext,
30+
},
31+
SchemaVersion: 0,
32+
Schema: map[string]*schema.Schema{
33+
"name": {
34+
Type: schema.TypeString,
35+
Description: "The name of the runner",
36+
Computed: true,
37+
Optional: true,
38+
},
39+
"ci_provider": {
40+
Type: schema.TypeString,
41+
Required: true,
42+
Description: "The CI/CD provider for the runner. Must be either 'github' or 'gitlab'",
43+
ValidateDiagFunc: verify.ValidateEnum[applesilicon.RunnerConfigurationProvider](),
44+
},
45+
"url": {
46+
Type: schema.TypeString,
47+
Required: true,
48+
Description: "The URL of the runner to run",
49+
},
50+
"token": {
51+
Type: schema.TypeString,
52+
Sensitive: true,
53+
Required: true,
54+
Description: "The token used to authenticate the runner to run",
55+
},
56+
"labels": {
57+
Type: schema.TypeList,
58+
Elem: &schema.Schema{
59+
Type: schema.TypeString,
60+
},
61+
Optional: true,
62+
Computed: true,
63+
Description: "A list of labels that should be applied to the runner.",
64+
},
65+
"status": {
66+
Type: schema.TypeString,
67+
Computed: true,
68+
Description: "The status of the runner",
69+
},
70+
"zone": zonal.Schema(),
71+
"project_id": account.ProjectIDSchema(),
72+
},
73+
}
74+
}
75+
76+
func ResourceAppleSiliconRunnerCreate(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
77+
asAPI, zone, err := newAPIWithZone(d, m)
78+
if err != nil {
79+
return diag.FromErr(err)
80+
}
81+
82+
provider := d.Get("ci_provider").(string)
83+
84+
runnerConfig := &applesilicon.RunnerConfigurationV2{
85+
Name: d.Get("name").(string),
86+
Provider: applesilicon.RunnerConfigurationV2Provider(provider),
87+
GithubConfiguration: nil,
88+
GitlabConfiguration: nil,
89+
}
90+
91+
if provider == "github" {
92+
runnerConfig.GithubConfiguration = &applesilicon.GithubRunnerConfiguration{
93+
URL: d.Get("url").(string),
94+
Token: d.Get("token").(string),
95+
Labels: types.ExpandStrings(d.Get("labels")),
96+
}
97+
}
98+
99+
if provider == "gitlab" {
100+
runnerConfig.GitlabConfiguration = &applesilicon.GitlabRunnerConfiguration{
101+
URL: d.Get("url").(string),
102+
Token: d.Get("token").(string),
103+
}
104+
}
105+
106+
createRunnerReq := &applesilicon.CreateRunnerRequest{
107+
Zone: zone,
108+
ProjectID: d.Get("project_id").(string),
109+
RunnerConfiguration: runnerConfig,
110+
}
111+
112+
runner, err := asAPI.CreateRunner(createRunnerReq, scw.WithContext(ctx))
113+
if err != nil {
114+
return diag.FromErr(err)
115+
}
116+
117+
d.SetId(zonal.NewIDString(zone, runner.ID))
118+
119+
return ResourceAppleSiliconRunnerRead(ctx, d, m)
120+
}
121+
122+
func ResourceAppleSiliconRunnerRead(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
123+
asAPI, zone, ID, err := NewAPIWithZoneAndID(m, d.Id())
124+
if err != nil {
125+
return diag.FromErr(err)
126+
}
127+
128+
runner, err := asAPI.GetRunner(&applesilicon.GetRunnerRequest{
129+
Zone: zone,
130+
RunnerID: ID,
131+
}, scw.WithContext(ctx))
132+
if err != nil {
133+
if httperrors.Is404(err) {
134+
d.SetId("")
135+
136+
return nil
137+
}
138+
return diag.FromErr(err)
139+
}
140+
141+
_ = d.Set("name", runner.ID)
142+
_ = d.Set("name", runner.Configuration.Name)
143+
_ = d.Set("ci_provider", runner.Configuration.Provider)
144+
_ = d.Set("status", runner.Status)
145+
146+
if runner.Configuration.Provider == "github" {
147+
_ = d.Set("token", runner.Configuration.GithubConfiguration.Token)
148+
_ = d.Set("url", runner.Configuration.GithubConfiguration.URL)
149+
_ = d.Set("labels", runner.Configuration.GithubConfiguration.Labels)
150+
}
151+
152+
return nil
153+
}
154+
155+
func ResourceAppleSiliconRunnerUpdate(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
156+
asAPI, zone, ID, err := NewAPIWithZoneAndID(m, d.Id())
157+
if err != nil {
158+
return diag.FromErr(err)
159+
}
160+
161+
provider := d.Get("ci_provider").(string)
162+
163+
runnerConfig := &applesilicon.RunnerConfigurationV2{
164+
Name: d.Get("name").(string),
165+
Provider: applesilicon.RunnerConfigurationV2Provider(provider),
166+
GithubConfiguration: nil,
167+
GitlabConfiguration: nil,
168+
}
169+
170+
if provider == "github" {
171+
runnerConfig.GithubConfiguration = &applesilicon.GithubRunnerConfiguration{
172+
URL: d.Get("url").(string),
173+
Token: d.Get("token").(string),
174+
Labels: types.ExpandStrings(d.Get("labels")),
175+
}
176+
}
177+
178+
if provider == "gitlab" {
179+
runnerConfig.GitlabConfiguration = &applesilicon.GitlabRunnerConfiguration{
180+
URL: d.Get("url").(string),
181+
Token: d.Get("token").(string),
182+
}
183+
}
184+
185+
updateRunnerReq := &applesilicon.UpdateRunnerRequest{
186+
Zone: zone,
187+
RunnerID: ID,
188+
RunnerConfiguration: runnerConfig,
189+
}
190+
191+
_, err = asAPI.UpdateRunner(updateRunnerReq, scw.WithContext(ctx))
192+
if err != nil {
193+
return diag.FromErr(err)
194+
}
195+
196+
return ResourceAppleSiliconRunnerRead(ctx, d, m)
197+
}
198+
199+
func ResourceAppleSiliconRunnerDelete(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
200+
asAPI, zone, ID, err := NewAPIWithZoneAndID(m, d.Id())
201+
if err != nil {
202+
return diag.FromErr(err)
203+
}
204+
runnerDeleteReq := &applesilicon.DeleteRunnerRequest{
205+
Zone: zone,
206+
RunnerID: ID,
207+
}
208+
err = asAPI.DeleteRunner(runnerDeleteReq, scw.WithContext(ctx))
209+
if err != nil && !httperrors.Is404(err) {
210+
return diag.FromErr(err)
211+
}
212+
return nil
213+
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package applesilicon_test
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
8+
"github.com/hashicorp/terraform-plugin-testing/terraform"
9+
applesiliconSDK "github.com/scaleway/scaleway-sdk-go/api/applesilicon/v1alpha1"
10+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/acctest"
11+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/httperrors"
12+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/applesilicon"
13+
)
14+
15+
func TestAccRunner_BasicGithub(t *testing.T) {
16+
tt := acctest.NewTestTools(t)
17+
defer tt.Cleanup()
18+
19+
resource.ParallelTest(t, resource.TestCase{
20+
ProtoV6ProviderFactories: tt.ProviderFactories,
21+
CheckDestroy: isRunnerDestroyed(tt),
22+
Steps: []resource.TestStep{
23+
{
24+
Config: `
25+
resource "scaleway_apple_silicon_runner" "main" {
26+
name = "TestAccRunnerGithub"
27+
ci_provider = "github"
28+
url = "%s"
29+
token = "%s"
30+
labels = ["ci", "macos"]
31+
}
32+
`,
33+
Check: resource.ComposeTestCheckFunc(
34+
isRunnerPresent(tt, "scaleway_apple_silicon_runner.main"),
35+
resource.TestCheckResourceAttr("scaleway_apple_silicon_runner.main", "name", "TestAccRunnerGithub"),
36+
resource.TestCheckResourceAttr("scaleway_apple_silicon_runner.main", "provider", "github"),
37+
resource.TestCheckResourceAttr("scaleway_apple_silicon_runner.main", "url", "https://github.com/my-org/repo"),
38+
resource.TestCheckResourceAttr("scaleway_apple_silicon_runner.main", "labels.#", "2"),
39+
40+
// Computed
41+
resource.TestCheckResourceAttrSet("scaleway_apple_silicon_runner.main", "status"),
42+
),
43+
},
44+
{
45+
Config: `
46+
resource "scaleway_apple_silicon_runner" "main" {
47+
name = "TestAccRunnerGithubUpdated"
48+
ci_provider = "github"
49+
url = "%s"
50+
token = "%s"
51+
labels = ["updated"]
52+
}
53+
`,
54+
Check: resource.ComposeTestCheckFunc(
55+
isRunnerPresent(tt, "scaleway_apple_silicon_runner.main"),
56+
resource.TestCheckResourceAttr("scaleway_apple_silicon_runner.main", "name", "TestAccRunnerGithubUpdated"),
57+
resource.TestCheckResourceAttr("scaleway_apple_silicon_runner.main", "labels.#", "1"),
58+
),
59+
},
60+
},
61+
})
62+
}
63+
64+
func TestAccRunner_BasicGitlab(t *testing.T) {
65+
tt := acctest.NewTestTools(t)
66+
defer tt.Cleanup()
67+
68+
resource.ParallelTest(t, resource.TestCase{
69+
ProtoV6ProviderFactories: tt.ProviderFactories,
70+
CheckDestroy: isRunnerDestroyed(tt),
71+
Steps: []resource.TestStep{
72+
{
73+
Config: `
74+
resource "scaleway_apple_silicon_runner" "main" {
75+
name = "TestAccRunnerGitlab"
76+
ci_provider = "gitlab"
77+
url = "https://gitlab.com"
78+
token = "gitlab-token"
79+
}
80+
`,
81+
Check: resource.ComposeTestCheckFunc(
82+
isRunnerPresent(tt, "scaleway_apple_silicon_runner.main"),
83+
resource.TestCheckResourceAttr("scaleway_apple_silicon_runner.main", "name", "TestAccRunnerGitlab"),
84+
resource.TestCheckResourceAttr("scaleway_apple_silicon_runner.main", "provider", "gitlab"),
85+
resource.TestCheckResourceAttr("scaleway_apple_silicon_runner.main", "url", "https://gitlab.com"),
86+
87+
// Computed
88+
resource.TestCheckResourceAttrSet("scaleway_apple_silicon_runner.main", "status"),
89+
),
90+
},
91+
},
92+
})
93+
}
94+
95+
func isRunnerPresent(tt *acctest.TestTools, resourceName string) resource.TestCheckFunc {
96+
return func(s *terraform.State) error {
97+
rs, ok := s.RootModule().Resources[resourceName]
98+
if !ok {
99+
return fmt.Errorf("resource not found: %s", resourceName)
100+
}
101+
102+
api, zone, id, err := applesilicon.NewAPIWithZoneAndID(tt.Meta, rs.Primary.ID)
103+
if err != nil {
104+
return err
105+
}
106+
107+
_, err = api.GetRunner(&applesiliconSDK.GetRunnerRequest{
108+
Zone: zone,
109+
RunnerID: id,
110+
})
111+
112+
return err
113+
}
114+
}
115+
116+
func isRunnerDestroyed(tt *acctest.TestTools) resource.TestCheckFunc {
117+
return func(s *terraform.State) error {
118+
for _, rs := range s.RootModule().Resources {
119+
if rs.Type != "scaleway_apple_silicon_runner" {
120+
continue
121+
}
122+
123+
api, zone, id, err := applesilicon.NewAPIWithZoneAndID(tt.Meta, rs.Primary.ID)
124+
if err != nil {
125+
return err
126+
}
127+
128+
_, err = api.GetRunner(&applesiliconSDK.GetRunnerRequest{
129+
Zone: zone,
130+
RunnerID: id,
131+
})
132+
133+
if err == nil {
134+
return fmt.Errorf("runner still exists: %s", rs.Primary.ID)
135+
}
136+
137+
if !httperrors.Is404(err) {
138+
return fmt.Errorf("unexpected error: %s", err)
139+
}
140+
}
141+
142+
return nil
143+
}
144+
}

0 commit comments

Comments
 (0)