Skip to content

Commit b358dee

Browse files
committed
refactor: centralize runner model mapping and add mapping tests
Extract runner Terraform model types and runner->state mapping into internal/runnermodel, then reuse them from both runner resource and runner data source. Why: - remove duplicated mapping/model code - keep resource and data source state mapping consistent - make future runner field changes safer and easier Also add unit tests for shared MapRunnerToModel conversion and create/update param mapping behavior for null/unknown values.
1 parent 853a604 commit b358dee

6 files changed

Lines changed: 324 additions & 167 deletions

File tree

internal/datasource_runner/runner_data_source.go

Lines changed: 3 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ package datasource_runner
33
import (
44
"context"
55

6+
"github.com/combor/terraform-provider-ona/internal/runnermodel"
67
gitpod "github.com/gitpod-io/gitpod-sdk-go"
78
"github.com/hashicorp/terraform-plugin-framework/datasource"
89
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
9-
"github.com/hashicorp/terraform-plugin-framework/types"
1010
)
1111

1212
var _ datasource.DataSource = (*runnerDataSource)(nil)
@@ -15,47 +15,6 @@ type runnerDataSource struct {
1515
client *gitpod.Client
1616
}
1717

18-
type runnerDataSourceModel struct {
19-
ID types.String `tfsdk:"id"`
20-
Name types.String `tfsdk:"name"`
21-
Kind types.String `tfsdk:"kind"`
22-
ProviderType types.String `tfsdk:"provider_type"`
23-
Spec *runnerSpecModel `tfsdk:"spec"`
24-
Status *runnerStatusModel `tfsdk:"status"`
25-
CreatedAt types.String `tfsdk:"created_at"`
26-
UpdatedAt types.String `tfsdk:"updated_at"`
27-
CreatorID types.String `tfsdk:"creator_id"`
28-
}
29-
30-
type runnerSpecModel struct {
31-
DesiredPhase types.String `tfsdk:"desired_phase"`
32-
Variant types.String `tfsdk:"variant"`
33-
Configuration *runnerConfigurationModel `tfsdk:"configuration"`
34-
}
35-
36-
type runnerConfigurationModel struct {
37-
AutoUpdate types.Bool `tfsdk:"auto_update"`
38-
DevcontainerImageCacheEnabled types.Bool `tfsdk:"devcontainer_image_cache_enabled"`
39-
LogLevel types.String `tfsdk:"log_level"`
40-
Region types.String `tfsdk:"region"`
41-
ReleaseChannel types.String `tfsdk:"release_channel"`
42-
Metrics *metricsModel `tfsdk:"metrics"`
43-
}
44-
45-
type metricsModel struct {
46-
Enabled types.Bool `tfsdk:"enabled"`
47-
Password types.String `tfsdk:"password"`
48-
URL types.String `tfsdk:"url"`
49-
Username types.String `tfsdk:"username"`
50-
}
51-
52-
type runnerStatusModel struct {
53-
Phase types.String `tfsdk:"phase"`
54-
Message types.String `tfsdk:"message"`
55-
Version types.String `tfsdk:"version"`
56-
Region types.String `tfsdk:"region"`
57-
}
58-
5918
func NewRunnerDataSource() datasource.DataSource {
6019
return &runnerDataSource{}
6120
}
@@ -186,7 +145,7 @@ func (d *runnerDataSource) Configure(_ context.Context, req datasource.Configure
186145
}
187146

188147
func (d *runnerDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
189-
var config runnerDataSourceModel
148+
var config runnermodel.RunnerModel
190149
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
191150
if resp.Diagnostics.HasError() {
192151
return
@@ -200,39 +159,7 @@ func (d *runnerDataSource) Read(ctx context.Context, req datasource.ReadRequest,
200159
return
201160
}
202161

203-
r := result.Runner
204-
state := runnerDataSourceModel{
205-
ID: types.StringValue(r.RunnerID),
206-
Name: types.StringValue(r.Name),
207-
Kind: types.StringValue(string(r.Kind)),
208-
ProviderType: types.StringValue(string(r.Provider)),
209-
Spec: &runnerSpecModel{
210-
DesiredPhase: types.StringValue(string(r.Spec.DesiredPhase)),
211-
Variant: types.StringValue(string(r.Spec.Variant)),
212-
Configuration: &runnerConfigurationModel{
213-
AutoUpdate: types.BoolValue(r.Spec.Configuration.AutoUpdate),
214-
DevcontainerImageCacheEnabled: types.BoolValue(r.Spec.Configuration.DevcontainerImageCacheEnabled),
215-
LogLevel: types.StringValue(string(r.Spec.Configuration.LogLevel)),
216-
Region: types.StringValue(r.Spec.Configuration.Region),
217-
ReleaseChannel: types.StringValue(string(r.Spec.Configuration.ReleaseChannel)),
218-
Metrics: &metricsModel{
219-
Enabled: types.BoolValue(r.Spec.Configuration.Metrics.Enabled),
220-
Password: types.StringValue(r.Spec.Configuration.Metrics.Password),
221-
URL: types.StringValue(r.Spec.Configuration.Metrics.URL),
222-
Username: types.StringValue(r.Spec.Configuration.Metrics.Username),
223-
},
224-
},
225-
},
226-
Status: &runnerStatusModel{
227-
Phase: types.StringValue(string(r.Status.Phase)),
228-
Message: types.StringValue(r.Status.Message),
229-
Version: types.StringValue(r.Status.Version),
230-
Region: types.StringValue(r.Status.Region),
231-
},
232-
CreatedAt: types.StringValue(r.CreatedAt.String()),
233-
UpdatedAt: types.StringValue(r.UpdatedAt.String()),
234-
CreatorID: types.StringValue(r.Creator.ID),
235-
}
162+
state := runnermodel.MapRunnerToModel(result.Runner)
236163

237164
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
238165
}

internal/resource_runner/runner_model.go

Lines changed: 3 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,11 @@
11
package resource_runner
22

33
import (
4+
"github.com/combor/terraform-provider-ona/internal/runnermodel"
45
gitpod "github.com/gitpod-io/gitpod-sdk-go"
5-
"github.com/hashicorp/terraform-plugin-framework/types"
66
)
77

8-
type RunnerModel struct {
9-
ID types.String `tfsdk:"id"`
10-
Name types.String `tfsdk:"name"`
11-
Kind types.String `tfsdk:"kind"`
12-
ProviderType types.String `tfsdk:"provider_type"`
13-
Spec *RunnerSpecModel `tfsdk:"spec"`
14-
Status *RunnerStatusModel `tfsdk:"status"`
15-
CreatedAt types.String `tfsdk:"created_at"`
16-
UpdatedAt types.String `tfsdk:"updated_at"`
17-
CreatorID types.String `tfsdk:"creator_id"`
18-
}
19-
20-
type RunnerSpecModel struct {
21-
DesiredPhase types.String `tfsdk:"desired_phase"`
22-
Variant types.String `tfsdk:"variant"`
23-
Configuration *RunnerConfigurationModel `tfsdk:"configuration"`
24-
}
25-
26-
type RunnerConfigurationModel struct {
27-
AutoUpdate types.Bool `tfsdk:"auto_update"`
28-
DevcontainerImageCacheEnabled types.Bool `tfsdk:"devcontainer_image_cache_enabled"`
29-
LogLevel types.String `tfsdk:"log_level"`
30-
Region types.String `tfsdk:"region"`
31-
ReleaseChannel types.String `tfsdk:"release_channel"`
32-
Metrics *MetricsModel `tfsdk:"metrics"`
33-
}
34-
35-
type MetricsModel struct {
36-
Enabled types.Bool `tfsdk:"enabled"`
37-
Password types.String `tfsdk:"password"`
38-
URL types.String `tfsdk:"url"`
39-
Username types.String `tfsdk:"username"`
40-
}
41-
42-
type RunnerStatusModel struct {
43-
Phase types.String `tfsdk:"phase"`
44-
Message types.String `tfsdk:"message"`
45-
Version types.String `tfsdk:"version"`
46-
Region types.String `tfsdk:"region"`
47-
}
48-
49-
func mapRunnerToModel(r gitpod.Runner) RunnerModel {
50-
model := RunnerModel{
51-
ID: types.StringValue(r.RunnerID),
52-
Name: types.StringValue(r.Name),
53-
Kind: types.StringValue(string(r.Kind)),
54-
ProviderType: types.StringValue(string(r.Provider)),
55-
CreatedAt: types.StringValue(r.CreatedAt.String()),
56-
UpdatedAt: types.StringValue(r.UpdatedAt.String()),
57-
CreatorID: types.StringValue(r.Creator.ID),
58-
}
59-
60-
model.Spec = &RunnerSpecModel{
61-
DesiredPhase: types.StringValue(string(r.Spec.DesiredPhase)),
62-
Variant: types.StringValue(string(r.Spec.Variant)),
63-
Configuration: &RunnerConfigurationModel{
64-
AutoUpdate: types.BoolValue(r.Spec.Configuration.AutoUpdate),
65-
DevcontainerImageCacheEnabled: types.BoolValue(r.Spec.Configuration.DevcontainerImageCacheEnabled),
66-
LogLevel: types.StringValue(string(r.Spec.Configuration.LogLevel)),
67-
Region: types.StringValue(r.Spec.Configuration.Region),
68-
ReleaseChannel: types.StringValue(string(r.Spec.Configuration.ReleaseChannel)),
69-
Metrics: &MetricsModel{
70-
Enabled: types.BoolValue(r.Spec.Configuration.Metrics.Enabled),
71-
Password: types.StringValue(r.Spec.Configuration.Metrics.Password),
72-
URL: types.StringValue(r.Spec.Configuration.Metrics.URL),
73-
Username: types.StringValue(r.Spec.Configuration.Metrics.Username),
74-
},
75-
},
76-
}
77-
78-
model.Status = &RunnerStatusModel{
79-
Phase: types.StringValue(string(r.Status.Phase)),
80-
Message: types.StringValue(r.Status.Message),
81-
Version: types.StringValue(r.Status.Version),
82-
Region: types.StringValue(r.Status.Region),
83-
}
84-
85-
return model
86-
}
87-
88-
func mapModelToNewParams(m RunnerModel) gitpod.RunnerNewParams {
8+
func mapModelToNewParams(m runnermodel.RunnerModel) gitpod.RunnerNewParams {
899
params := gitpod.RunnerNewParams{
9010
Name: gitpod.F(m.Name.ValueString()),
9111
Kind: gitpod.F(gitpod.RunnerKind(m.Kind.ValueString())),
@@ -147,7 +67,7 @@ func mapModelToNewParams(m RunnerModel) gitpod.RunnerNewParams {
14767
return params
14868
}
14969

150-
func mapModelToUpdateParams(m RunnerModel) gitpod.RunnerUpdateParams {
70+
func mapModelToUpdateParams(m runnermodel.RunnerModel) gitpod.RunnerUpdateParams {
15171
params := gitpod.RunnerUpdateParams{
15272
RunnerID: gitpod.F(m.ID.ValueString()),
15373
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package resource_runner
2+
3+
import (
4+
"testing"
5+
6+
"github.com/combor/terraform-provider-ona/internal/runnermodel"
7+
gitpod "github.com/gitpod-io/gitpod-sdk-go"
8+
"github.com/hashicorp/terraform-plugin-framework/types"
9+
)
10+
11+
func TestMapModelToNewParams(t *testing.T) {
12+
model := runnermodel.RunnerModel{
13+
Name: types.StringValue("runner"),
14+
Kind: types.StringValue(string(gitpod.RunnerKindRemote)),
15+
ProviderType: types.StringValue(string(gitpod.RunnerProviderAwsEc2)),
16+
Spec: &runnermodel.RunnerSpecModel{
17+
DesiredPhase: types.StringUnknown(),
18+
Variant: types.StringValue(string(gitpod.RunnerVariantEnterprise)),
19+
Configuration: &runnermodel.RunnerConfigurationModel{
20+
AutoUpdate: types.BoolValue(true),
21+
DevcontainerImageCacheEnabled: types.BoolNull(),
22+
LogLevel: types.StringValue(string(gitpod.LogLevelInfo)),
23+
Region: types.StringUnknown(),
24+
ReleaseChannel: types.StringValue(string(gitpod.RunnerReleaseChannelLatest)),
25+
Metrics: &runnermodel.MetricsModel{
26+
Enabled: types.BoolValue(true),
27+
Password: types.StringNull(),
28+
URL: types.StringValue("https://metrics"),
29+
Username: types.StringUnknown(),
30+
},
31+
},
32+
},
33+
}
34+
35+
params := mapModelToNewParams(model)
36+
37+
if !params.Name.Present || params.Name.Value != "runner" {
38+
t.Fatalf("unexpected name field: %+v", params.Name)
39+
}
40+
if !params.Kind.Present || params.Kind.Value != gitpod.RunnerKindRemote {
41+
t.Fatalf("unexpected kind field: %+v", params.Kind)
42+
}
43+
if !params.Provider.Present || params.Provider.Value != gitpod.RunnerProviderAwsEc2 {
44+
t.Fatalf("unexpected provider field: %+v", params.Provider)
45+
}
46+
if !params.Spec.Present {
47+
t.Fatal("expected spec to be present")
48+
}
49+
50+
spec := params.Spec.Value
51+
if spec.DesiredPhase.Present {
52+
t.Fatalf("expected desired_phase to be omitted: %+v", spec.DesiredPhase)
53+
}
54+
if !spec.Variant.Present || spec.Variant.Value != gitpod.RunnerVariantEnterprise {
55+
t.Fatalf("unexpected variant field: %+v", spec.Variant)
56+
}
57+
if !spec.Configuration.Present {
58+
t.Fatal("expected configuration to be present")
59+
}
60+
61+
cfg := spec.Configuration.Value
62+
if !cfg.AutoUpdate.Present || !cfg.AutoUpdate.Value {
63+
t.Fatalf("unexpected auto_update field: %+v", cfg.AutoUpdate)
64+
}
65+
if cfg.DevcontainerImageCacheEnabled.Present {
66+
t.Fatalf("expected devcontainer_image_cache_enabled to be omitted: %+v", cfg.DevcontainerImageCacheEnabled)
67+
}
68+
if !cfg.LogLevel.Present || cfg.LogLevel.Value != gitpod.LogLevelInfo {
69+
t.Fatalf("unexpected log_level field: %+v", cfg.LogLevel)
70+
}
71+
if cfg.Region.Present {
72+
t.Fatalf("expected region to be omitted: %+v", cfg.Region)
73+
}
74+
if !cfg.ReleaseChannel.Present || cfg.ReleaseChannel.Value != gitpod.RunnerReleaseChannelLatest {
75+
t.Fatalf("unexpected release_channel field: %+v", cfg.ReleaseChannel)
76+
}
77+
if !cfg.Metrics.Present {
78+
t.Fatal("expected metrics to be present")
79+
}
80+
81+
metrics := cfg.Metrics.Value
82+
if !metrics.Enabled.Present || !metrics.Enabled.Value {
83+
t.Fatalf("unexpected metrics.enabled field: %+v", metrics.Enabled)
84+
}
85+
if metrics.Password.Present {
86+
t.Fatalf("expected metrics.password to be omitted: %+v", metrics.Password)
87+
}
88+
if !metrics.URL.Present || metrics.URL.Value != "https://metrics" {
89+
t.Fatalf("unexpected metrics.url field: %+v", metrics.URL)
90+
}
91+
if metrics.Username.Present {
92+
t.Fatalf("expected metrics.username to be omitted: %+v", metrics.Username)
93+
}
94+
}
95+
96+
func TestMapModelToUpdateParams(t *testing.T) {
97+
model := runnermodel.RunnerModel{
98+
ID: types.StringValue("runner-123"),
99+
Name: types.StringNull(),
100+
Spec: &runnermodel.RunnerSpecModel{
101+
DesiredPhase: types.StringValue(string(gitpod.RunnerPhaseDeleting)),
102+
Configuration: &runnermodel.RunnerConfigurationModel{
103+
AutoUpdate: types.BoolUnknown(),
104+
ReleaseChannel: types.StringValue(string(gitpod.RunnerReleaseChannelStable)),
105+
},
106+
},
107+
}
108+
109+
params := mapModelToUpdateParams(model)
110+
111+
if !params.RunnerID.Present || params.RunnerID.Value != "runner-123" {
112+
t.Fatalf("unexpected runner_id field: %+v", params.RunnerID)
113+
}
114+
if params.Name.Present {
115+
t.Fatalf("expected name to be omitted: %+v", params.Name)
116+
}
117+
if !params.Spec.Present {
118+
t.Fatal("expected spec to be present")
119+
}
120+
121+
spec := params.Spec.Value
122+
if !spec.DesiredPhase.Present || spec.DesiredPhase.Value != gitpod.RunnerPhaseDeleting {
123+
t.Fatalf("unexpected desired_phase field: %+v", spec.DesiredPhase)
124+
}
125+
if !spec.Configuration.Present {
126+
t.Fatal("expected configuration to be present")
127+
}
128+
129+
cfg := spec.Configuration.Value
130+
if cfg.AutoUpdate.Present {
131+
t.Fatalf("expected auto_update to be omitted: %+v", cfg.AutoUpdate)
132+
}
133+
if !cfg.ReleaseChannel.Present || cfg.ReleaseChannel.Value != gitpod.RunnerReleaseChannelStable {
134+
t.Fatalf("unexpected release_channel field: %+v", cfg.ReleaseChannel)
135+
}
136+
if cfg.Metrics.Present {
137+
t.Fatalf("expected metrics to be omitted: %+v", cfg.Metrics)
138+
}
139+
}

0 commit comments

Comments
 (0)