Skip to content

Commit 4baa411

Browse files
committed
Switch to nested object type on project setting overwrites
1 parent 7853f18 commit 4baa411

File tree

3 files changed

+62
-53
lines changed

3 files changed

+62
-53
lines changed

internal/provider/resource_tfe_project_settings.go

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,17 @@ import (
1919
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
2020
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
2121
"github.com/hashicorp/terraform-plugin-framework/types"
22+
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
2223
)
2324

2425
// tfe_project_settings resource
2526
var _ resource.Resource = &projectSettings{}
2627

2728
// projectOverwritesElementType is the object type definition for the
2829
// overwrites field schema.
29-
var projectOverwritesElementType = types.ObjectType{
30-
AttrTypes: map[string]attr.Type{
31-
"default_execution_mode": types.BoolType,
32-
"default_agent_pool_id": types.BoolType,
33-
},
30+
var projectOverwritesElementType = map[string]attr.Type{
31+
"default_execution_mode": types.BoolType,
32+
"default_agent_pool_id": types.BoolType,
3433
}
3534

3635
type projectSettings struct {
@@ -42,7 +41,7 @@ type modelProjectSettings struct {
4241
ProjectID types.String `tfsdk:"project_id"`
4342
DefaultExecutionMode types.String `tfsdk:"default_execution_mode"`
4443
DefaultAgentPoolID types.String `tfsdk:"default_agent_pool_id"`
45-
Overwrites types.List `tfsdk:"overwrites"`
44+
Overwrites types.Object `tfsdk:"overwrites"`
4645
}
4746

4847
type projectOverwrites struct {
@@ -79,9 +78,9 @@ type unknownIfDefaultExecutionModeUnset struct{}
7978
type overwriteExecutionModeIfSpecified struct{}
8079

8180
var _ planmodifier.String = (*validateProjectDefaultAgentExecutionMode)(nil)
82-
var _ planmodifier.List = (*revertOverwritesIfDefaultExecutionModeUnset)(nil)
81+
var _ planmodifier.Object = (*revertOverwritesIfDefaultExecutionModeUnset)(nil)
8382
var _ planmodifier.String = (*unknownIfDefaultExecutionModeUnset)(nil)
84-
var _ planmodifier.List = (*overwriteExecutionModeIfSpecified)(nil)
83+
var _ planmodifier.Object = (*overwriteExecutionModeIfSpecified)(nil)
8584

8685
func (m validateProjectDefaultAgentExecutionMode) PlanModifyString(ctx context.Context, req planmodifier.StringRequest, resp *planmodifier.StringResponse) {
8786
configured := modelProjectSettings{}
@@ -104,7 +103,7 @@ func (m validateProjectDefaultAgentExecutionMode) MarkdownDescription(_ context.
104103
return "Validates that configuration values for \"default_agent_pool_id\" and \"default_execution_mode\" are compatible"
105104
}
106105

107-
func (m revertOverwritesIfDefaultExecutionModeUnset) PlanModifyList(ctx context.Context, req planmodifier.ListRequest, resp *planmodifier.ListResponse) {
106+
func (m revertOverwritesIfDefaultExecutionModeUnset) PlanModifyObject(ctx context.Context, req planmodifier.ObjectRequest, resp *planmodifier.ObjectResponse) {
108107
// Check if the resource is being created.
109108
if req.State.Raw.IsNull() {
110109
return
@@ -122,18 +121,19 @@ func (m revertOverwritesIfDefaultExecutionModeUnset) PlanModifyList(ctx context.
122121
return
123122
}
124123

125-
overwritesState := make([]projectOverwrites, 1)
126-
state.Overwrites.ElementsAs(ctx, &overwritesState, true)
124+
overwritesState := projectOverwrites{}
125+
126+
state.Overwrites.As(ctx, &overwritesState, basetypes.ObjectAsOptions{})
127127

128128
// if there is a default execution mode set in state, but not one configured, then set the overwrites to false
129-
if configured.DefaultExecutionMode.IsNull() && overwritesState[0].DefaultExecutionMode.ValueBool() {
130-
overwritesState[0].DefaultAgentPoolID = types.BoolValue(false)
131-
overwritesState[0].DefaultExecutionMode = types.BoolValue(false)
129+
if configured.DefaultExecutionMode.IsNull() && overwritesState.DefaultExecutionMode.ValueBool() {
130+
overwritesState.DefaultAgentPoolID = types.BoolValue(false)
131+
overwritesState.DefaultExecutionMode = types.BoolValue(false)
132132

133-
newList, diags := types.ListValueFrom(ctx, projectOverwritesElementType, overwritesState)
133+
newProjOverwrites, diags := types.ObjectValueFrom(ctx, projectOverwritesElementType, overwritesState)
134134
resp.Diagnostics.Append(diags...)
135135

136-
resp.PlanValue = newList
136+
resp.PlanValue = newProjOverwrites
137137
}
138138
}
139139

@@ -159,11 +159,11 @@ func (m unknownIfDefaultExecutionModeUnset) PlanModifyString(ctx context.Context
159159
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
160160

161161
if !state.Overwrites.IsNull() {
162-
overwritesState := make([]projectOverwrites, 1)
163-
state.Overwrites.ElementsAs(ctx, &overwritesState, true)
162+
overwritesState := projectOverwrites{}
163+
state.Overwrites.As(ctx, &overwritesState, basetypes.ObjectAsOptions{})
164164

165165
// if there is a default execution mode set in state, but not one configured, then set the planned value for the default execution mode and agent pool to unknown
166-
if configured.DefaultExecutionMode.IsNull() && overwritesState[0].DefaultExecutionMode.ValueBool() {
166+
if configured.DefaultExecutionMode.IsNull() && overwritesState.DefaultExecutionMode.ValueBool() {
167167
resp.PlanValue = types.StringUnknown()
168168
}
169169
}
@@ -177,7 +177,7 @@ func (m unknownIfDefaultExecutionModeUnset) MarkdownDescription(_ context.Contex
177177
return "Resets default_execution_mode to an unknown value if it is unset"
178178
}
179179

180-
func (m overwriteExecutionModeIfSpecified) PlanModifyList(ctx context.Context, req planmodifier.ListRequest, resp *planmodifier.ListResponse) {
180+
func (m overwriteExecutionModeIfSpecified) PlanModifyObject(ctx context.Context, req planmodifier.ObjectRequest, resp *planmodifier.ObjectResponse) {
181181
// Check if the resource is being created.
182182
if req.State.Raw.IsNull() {
183183
return
@@ -189,16 +189,16 @@ func (m overwriteExecutionModeIfSpecified) PlanModifyList(ctx context.Context, r
189189
resp.Diagnostics.Append(req.Config.Get(ctx, &configured)...)
190190
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
191191

192-
overwritesState := make([]projectOverwrites, 1)
193-
state.Overwrites.ElementsAs(ctx, &overwritesState, true)
192+
overwritesState := projectOverwrites{}
193+
state.Overwrites.As(ctx, &overwritesState, basetypes.ObjectAsOptions{})
194194

195195
if !state.Overwrites.IsNull() {
196196
// if an execution mode is configured, ensure that the overwrites are set to true
197197
if !configured.DefaultExecutionMode.IsNull() {
198-
overwritesState[0].DefaultAgentPoolID = types.BoolValue(true)
199-
overwritesState[0].DefaultExecutionMode = types.BoolValue(true)
198+
overwritesState.DefaultAgentPoolID = types.BoolValue(true)
199+
overwritesState.DefaultExecutionMode = types.BoolValue(true)
200200

201-
newList, diags := types.ListValueFrom(ctx, projectOverwritesElementType, overwritesState)
201+
newList, diags := types.ObjectValueFrom(ctx, projectOverwritesElementType, overwritesState)
202202
resp.Diagnostics.Append(diags...)
203203

204204
resp.PlanValue = newList
@@ -255,13 +255,20 @@ func (r *projectSettings) Schema(ctx context.Context, req resource.SchemaRequest
255255
validateProjectDefaultAgentExecutionMode{},
256256
},
257257
},
258-
// ListAttribute was required here because we are still using plugin protocol v5.
259-
// Once compatibility is broken for v1, and we convert all
260-
// providers to protocol v6, this can become a single nested object.
261-
"overwrites": schema.ListAttribute{
258+
"overwrites": schema.SingleNestedAttribute{
262259
Computed: true,
263-
ElementType: projectOverwritesElementType,
264-
PlanModifiers: []planmodifier.List{
260+
Description: "Describes which settings are being overwritten from the organization defaults",
261+
Attributes: map[string]schema.Attribute{
262+
"default_execution_mode": schema.BoolAttribute{
263+
Computed: true,
264+
Description: "Whether the default_execution_mode is being overwritten from the organization default",
265+
},
266+
"default_agent_pool_id": schema.BoolAttribute{
267+
Computed: true,
268+
Description: "Whether the default_agent_pool_id is being overwritten from the organization default",
269+
},
270+
},
271+
PlanModifiers: []planmodifier.Object{
265272
revertOverwritesIfDefaultExecutionModeUnset{},
266273
overwriteExecutionModeIfSpecified{},
267274
},
@@ -282,19 +289,19 @@ func (r *projectSettings) projectSettingsModelFromTFEProject(proj *tfe.Project)
282289
result.DefaultAgentPoolID = types.StringValue(proj.DefaultAgentPool.ID)
283290
}
284291

285-
result.Overwrites = types.ListNull(projectOverwritesElementType)
292+
result.Overwrites = types.ObjectNull(projectOverwritesElementType)
286293
if proj.SettingOverwrites != nil {
287294
settingsModel := projectOverwrites{
288295
DefaultExecutionMode: types.BoolValue(*proj.SettingOverwrites.ExecutionMode),
289296
DefaultAgentPoolID: types.BoolValue(*proj.SettingOverwrites.AgentPool),
290297
}
291298

292-
listOverwrites, diags := types.ListValueFrom(ctx, projectOverwritesElementType, []projectOverwrites{settingsModel})
299+
objectOverwrites, diags := types.ObjectValueFrom(ctx, projectOverwritesElementType, settingsModel)
293300
if diags.HasError() {
294-
panic("Could not build list value from slice of models. This should not be possible unless the model breaks reflection rules.")
301+
panic("Could not build object value from model. This should not be possible unless the model breaks reflection rules.")
295302
}
296303

297-
result.Overwrites = listOverwrites
304+
result.Overwrites = objectOverwrites
298305
}
299306

300307
return &result

internal/provider/resource_tfe_project_settings_test.go

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ func TestAccTFEProjectSettings_DefaultExecutionMode(t *testing.T) {
5858
resource.TestCheckNoResourceAttr(
5959
"tfe_project_settings.foobar_settings", "default_agent_pool_id"),
6060
resource.TestCheckResourceAttr(
61-
"tfe_project_settings.foobar_settings", "overwrites.0.default_execution_mode", "false"),
61+
"tfe_project_settings.foobar_settings", "overwrites.default_execution_mode", "false"),
6262
resource.TestCheckResourceAttr(
63-
"tfe_project_settings.foobar_settings", "overwrites.0.default_agent_pool_id", "false"),
63+
"tfe_project_settings.foobar_settings", "overwrites.default_agent_pool_id", "false"),
6464
),
6565
},
6666
{
@@ -74,10 +74,10 @@ func TestAccTFEProjectSettings_DefaultExecutionMode(t *testing.T) {
7474
tfjsonpath.New("default_agent_pool_id"),
7575
knownvalue.Null()),
7676
plancheck.ExpectKnownValue("tfe_project_settings.foobar_settings",
77-
tfjsonpath.New("overwrites").AtSliceIndex(0).AtMapKey("default_execution_mode"),
77+
tfjsonpath.New("overwrites").AtMapKey("default_execution_mode"),
7878
knownvalue.Bool(true)),
7979
plancheck.ExpectKnownValue("tfe_project_settings.foobar_settings",
80-
tfjsonpath.New("overwrites").AtSliceIndex(0).AtMapKey("default_agent_pool_id"),
80+
tfjsonpath.New("overwrites").AtMapKey("default_agent_pool_id"),
8181
knownvalue.Bool(true)),
8282
},
8383
},
@@ -87,9 +87,9 @@ func TestAccTFEProjectSettings_DefaultExecutionMode(t *testing.T) {
8787
resource.TestCheckNoResourceAttr(
8888
"tfe_project_settings.foobar_settings", "default_agent_pool_id"),
8989
resource.TestCheckResourceAttr(
90-
"tfe_project_settings.foobar_settings", "overwrites.0.default_execution_mode", "true"),
90+
"tfe_project_settings.foobar_settings", "overwrites.default_execution_mode", "true"),
9191
resource.TestCheckResourceAttr(
92-
"tfe_project_settings.foobar_settings", "overwrites.0.default_agent_pool_id", "true"),
92+
"tfe_project_settings.foobar_settings", "overwrites.default_agent_pool_id", "true"),
9393
),
9494
},
9595
{
@@ -100,10 +100,10 @@ func TestAccTFEProjectSettings_DefaultExecutionMode(t *testing.T) {
100100
tfjsonpath.New("default_execution_mode"),
101101
knownvalue.StringExact("local")),
102102
plancheck.ExpectKnownValue("tfe_project_settings.foobar_settings",
103-
tfjsonpath.New("overwrites").AtSliceIndex(0).AtMapKey("default_execution_mode"),
103+
tfjsonpath.New("overwrites").AtMapKey("default_execution_mode"),
104104
knownvalue.Bool(true)),
105105
plancheck.ExpectKnownValue("tfe_project_settings.foobar_settings",
106-
tfjsonpath.New("overwrites").AtSliceIndex(0).AtMapKey("default_agent_pool_id"),
106+
tfjsonpath.New("overwrites").AtMapKey("default_agent_pool_id"),
107107
knownvalue.Bool(true)),
108108
},
109109
},
@@ -113,9 +113,9 @@ func TestAccTFEProjectSettings_DefaultExecutionMode(t *testing.T) {
113113
resource.TestCheckNoResourceAttr(
114114
"tfe_project_settings.foobar_settings", "default_agent_pool_id"),
115115
resource.TestCheckResourceAttr(
116-
"tfe_project_settings.foobar_settings", "overwrites.0.default_execution_mode", "true"),
116+
"tfe_project_settings.foobar_settings", "overwrites.default_execution_mode", "true"),
117117
resource.TestCheckResourceAttr(
118-
"tfe_project_settings.foobar_settings", "overwrites.0.default_agent_pool_id", "true"),
118+
"tfe_project_settings.foobar_settings", "overwrites.default_agent_pool_id", "true"),
119119
),
120120
},
121121
{
@@ -134,10 +134,10 @@ func TestAccTFEProjectSettings_DefaultExecutionMode(t *testing.T) {
134134
return fmt.Errorf("expected an agent pool id, got %s", v)
135135
})),
136136
plancheck.ExpectKnownValue("tfe_project_settings.foobar_settings",
137-
tfjsonpath.New("overwrites").AtSliceIndex(0).AtMapKey("default_execution_mode"),
137+
tfjsonpath.New("overwrites").AtMapKey("default_execution_mode"),
138138
knownvalue.Bool(true)),
139139
plancheck.ExpectKnownValue("tfe_project_settings.foobar_settings",
140-
tfjsonpath.New("overwrites").AtSliceIndex(0).AtMapKey("default_agent_pool_id"),
140+
tfjsonpath.New("overwrites").AtMapKey("default_agent_pool_id"),
141141
knownvalue.Bool(true)),
142142
},
143143
},
@@ -147,9 +147,9 @@ func TestAccTFEProjectSettings_DefaultExecutionMode(t *testing.T) {
147147
resource.TestCheckResourceAttrSet(
148148
"tfe_project_settings.foobar_settings", "default_agent_pool_id"),
149149
resource.TestCheckResourceAttr(
150-
"tfe_project_settings.foobar_settings", "overwrites.0.default_execution_mode", "true"),
150+
"tfe_project_settings.foobar_settings", "overwrites.default_execution_mode", "true"),
151151
resource.TestCheckResourceAttr(
152-
"tfe_project_settings.foobar_settings", "overwrites.0.default_agent_pool_id", "true"),
152+
"tfe_project_settings.foobar_settings", "overwrites.default_agent_pool_id", "true"),
153153
),
154154
},
155155
{
@@ -159,10 +159,10 @@ func TestAccTFEProjectSettings_DefaultExecutionMode(t *testing.T) {
159159
plancheck.ExpectUnknownValue("tfe_project_settings.foobar_settings", tfjsonpath.New("default_execution_mode")),
160160
plancheck.ExpectUnknownValue("tfe_project_settings.foobar_settings", tfjsonpath.New("default_agent_pool_id")),
161161
plancheck.ExpectKnownValue("tfe_project_settings.foobar_settings",
162-
tfjsonpath.New("overwrites").AtSliceIndex(0).AtMapKey("default_execution_mode"),
162+
tfjsonpath.New("overwrites").AtMapKey("default_execution_mode"),
163163
knownvalue.Bool(false)),
164164
plancheck.ExpectKnownValue("tfe_project_settings.foobar_settings",
165-
tfjsonpath.New("overwrites").AtSliceIndex(0).AtMapKey("default_agent_pool_id"),
165+
tfjsonpath.New("overwrites").AtMapKey("default_agent_pool_id"),
166166
knownvalue.Bool(false)),
167167
},
168168
},
@@ -172,9 +172,9 @@ func TestAccTFEProjectSettings_DefaultExecutionMode(t *testing.T) {
172172
resource.TestCheckNoResourceAttr(
173173
"tfe_project_settings.foobar_settings", "default_agent_pool_id"),
174174
resource.TestCheckResourceAttr(
175-
"tfe_project_settings.foobar_settings", "overwrites.0.default_execution_mode", "false"),
175+
"tfe_project_settings.foobar_settings", "overwrites.default_execution_mode", "false"),
176176
resource.TestCheckResourceAttr(
177-
"tfe_project_settings.foobar_settings", "overwrites.0.default_agent_pool_id", "false"),
177+
"tfe_project_settings.foobar_settings", "overwrites.default_agent_pool_id", "false"),
178178
),
179179
},
180180
},

website/docs/r/project_settings.markdown

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ description: |-
77

88
# tfe_project_settings
99

10+
**Requires Terraform CLI version 1.0 and later**
11+
1012
Use this resource to manage Project Settings.
1113

1214
Primarily, this resource allows setting default execution mode and agent pool for all workspaces within a project. When not specified, the organization defaults will be used.

0 commit comments

Comments
 (0)