0
0
mirror of https://github.com/bpg/terraform-provider-proxmox.git synced 2025-07-01 19:12:59 +00:00

fix(cluster): inconsistencies in applying cluster options (#573)

* fix(cluster): inconsistencies in applying cluster options

Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>

* address review comments

Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>

---------

Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
Pavel Boldyrev 2023-09-23 08:04:52 -04:00 committed by GitHub
parent 11c09405ea
commit 03f3ed7871
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 275 additions and 169 deletions

View File

@ -37,18 +37,18 @@ resource "proxmox_virtual_environment_cluster_options" "options" {
- `bandwidth_limit_move` (Number) Move I/O bandwidth limit in KiB/s. - `bandwidth_limit_move` (Number) Move I/O bandwidth limit in KiB/s.
- `bandwidth_limit_restore` (Number) Restore I/O bandwidth limit in KiB/s. - `bandwidth_limit_restore` (Number) Restore I/O bandwidth limit in KiB/s.
- `console` (String) Select the default Console viewer. Must be `applet` | `vv`| `html5` | `xtermjs`. You can either use the builtin java applet (VNC; deprecated and maps to html5), an external virt-viewer compatible application (SPICE), an HTML5 based vnc viewer (noVNC), or an HTML5 based console client (xtermjs). If the selected viewer is not available (e.g. SPICE not activated for the VM), the fallback is noVNC. - `console` (String) Select the default Console viewer. Must be `applet` | `vv`| `html5` | `xtermjs`. You can either use the builtin java applet (VNC; deprecated and maps to html5), an external virt-viewer compatible application (SPICE), an HTML5 based vnc viewer (noVNC), or an HTML5 based console client (xtermjs). If the selected viewer is not available (e.g. SPICE not activated for the VM), the fallback is noVNC.
- `crs_ha` (String) Cluster resource scheduling setting for HA. Must be `static` | `basic`. - `crs_ha` (String) Cluster resource scheduling setting for HA. Must be `static` | `basic` (default is `basic`).
- `crs_ha_rebalance_on_start` (Boolean) Cluster resource scheduling setting for HA rebalance on start. - `crs_ha_rebalance_on_start` (Boolean) Cluster resource scheduling setting for HA rebalance on start.
- `description` (String) Datacenter description. Shown in the web-interface datacenter notes panel. This is saved as comment inside the configuration file. - `description` (String) Datacenter description. Shown in the web-interface datacenter notes panel. This is saved as comment inside the configuration file.
- `email_from` (String) email address to send notification from (default is root@$hostname). - `email_from` (String) email address to send notification from (default is root@$hostname).
- `ha_shutdown_policy` (String) Cluster wide HA shutdown policy. Must be `freeze` | `failover` | `migrate` | `conditional`. - `ha_shutdown_policy` (String) Cluster wide HA shutdown policy (). Must be `freeze` | `failover` | `migrate` | `conditional` (default is `conditional`).
- `http_proxy` (String) Specify external http proxy which is used for downloads (example: `http://username:password@host:port/`). - `http_proxy` (String) Specify external http proxy which is used for downloads (example: `http://username:password@host:port/`).
- `keyboard` (String) Default keyboard layout for vnc server. Must be `de` | `de-ch` | `da` | `en-gb` | `en-us` | `es` | `fi` | `fr` | `fr-be` | `fr-ca` | `fr-ch` | `hu` | `is` | `it` | `ja` | `lt` | `mk` | `nl` | `no` | `pl` | `pt` | `pt-br` | `sv` | `sl` | `tr`. - `keyboard` (String) Default keyboard layout for vnc server. Must be `de` | `de-ch` | `da` | `en-gb` | `en-us` | `es` | `fi` | `fr` | `fr-be` | `fr-ca` | `fr-ch` | `hu` | `is` | `it` | `ja` | `lt` | `mk` | `nl` | `no` | `pl` | `pt` | `pt-br` | `sv` | `sl` | `tr`.
- `language` (String) Default GUI language. Must be `ca` | `da` | `de` | `en` | `es` | `eu` | `fa` | `fr` | `he` | `it` | `ja` | `nb` | `nn` | `pl` | `pt_BR` | `ru` | `sl` | `sv` | `tr` | `zh_CN` | `zh_TW`. - `language` (String) Default GUI language. Must be `ca` | `da` | `de` | `en` | `es` | `eu` | `fa` | `fr` | `he` | `it` | `ja` | `nb` | `nn` | `pl` | `pt_BR` | `ru` | `sl` | `sv` | `tr` | `zh_CN` | `zh_TW`.
- `mac_prefix` (String) Prefix for autogenerated MAC addresses. - `mac_prefix` (String) Prefix for autogenerated MAC addresses.
- `max_workers` (Number) Defines how many workers (per node) are maximal started on actions like 'stopall VMs' or task from the ha-manager. - `max_workers` (Number) Defines how many workers (per node) are maximal started on actions like 'stopall VMs' or task from the ha-manager.
- `migration_cidr` (String) Cluster wide migration network CIDR. - `migration_cidr` (String) Cluster wide migration network CIDR.
- `migration_type` (String) Cluster wide migration type. Must be `secure` | `unsecure`. - `migration_type` (String) Cluster wide migration type. Must be `secure` | `unsecure` (default is `secure`).
### Read-Only ### Read-Only

View File

@ -40,21 +40,23 @@ type clusterOptionsModel struct {
BandwidthLimitMove types.Int64 `tfsdk:"bandwidth_limit_move"` BandwidthLimitMove types.Int64 `tfsdk:"bandwidth_limit_move"`
BandwidthLimitRestore types.Int64 `tfsdk:"bandwidth_limit_restore"` BandwidthLimitRestore types.Int64 `tfsdk:"bandwidth_limit_restore"`
Console types.String `tfsdk:"console"` Console types.String `tfsdk:"console"`
HTTPProxy types.String `tfsdk:"http_proxy"`
MacPrefix types.String `tfsdk:"mac_prefix"`
Description types.String `tfsdk:"description"`
HAShutdownPolicy types.String `tfsdk:"ha_shutdown_policy"`
MigrationType types.String `tfsdk:"migration_type"`
MigrationNetwork types.String `tfsdk:"migration_cidr"`
CrsHA types.String `tfsdk:"crs_ha"` CrsHA types.String `tfsdk:"crs_ha"`
CrsHARebalanceOnStart types.Bool `tfsdk:"crs_ha_rebalance_on_start"` CrsHARebalanceOnStart types.Bool `tfsdk:"crs_ha_rebalance_on_start"`
Description types.String `tfsdk:"description"`
EmailFrom types.String `tfsdk:"email_from"` EmailFrom types.String `tfsdk:"email_from"`
HAShutdownPolicy types.String `tfsdk:"ha_shutdown_policy"`
HTTPProxy types.String `tfsdk:"http_proxy"`
Keyboard types.String `tfsdk:"keyboard"` Keyboard types.String `tfsdk:"keyboard"`
Language types.String `tfsdk:"language"` Language types.String `tfsdk:"language"`
MacPrefix types.String `tfsdk:"mac_prefix"`
MaxWorkers types.Int64 `tfsdk:"max_workers"` MaxWorkers types.Int64 `tfsdk:"max_workers"`
MigrationNetwork types.String `tfsdk:"migration_cidr"`
MigrationType types.String `tfsdk:"migration_type"`
} }
func (m *clusterOptionsModel) haData() *string { // haData returns HA settings parameter string for API, HA settings are
// defined, otherwise empty string is returned.
func (m *clusterOptionsModel) haData() string {
var haDataParams []string var haDataParams []string
if !m.HAShutdownPolicy.IsNull() && m.HAShutdownPolicy.ValueString() != "" { if !m.HAShutdownPolicy.IsNull() && m.HAShutdownPolicy.ValueString() != "" {
@ -62,15 +64,15 @@ func (m *clusterOptionsModel) haData() *string {
} }
if len(haDataParams) > 0 { if len(haDataParams) > 0 {
haDataValue := strings.Join(haDataParams, ",") return strings.Join(haDataParams, ",")
return &haDataValue
} }
return nil return ""
} }
func (m *clusterOptionsModel) migrationData() *string { // migrationData returns migration settings parameter string for API, if any of migration
// settings are defined, otherwise empty string is returned.
func (m *clusterOptionsModel) migrationData() string {
var migrationDataParams []string var migrationDataParams []string
if !m.MigrationType.IsNull() && m.MigrationType.ValueString() != "" { if !m.MigrationType.IsNull() && m.MigrationType.ValueString() != "" {
@ -82,15 +84,15 @@ func (m *clusterOptionsModel) migrationData() *string {
} }
if len(migrationDataParams) > 0 { if len(migrationDataParams) > 0 {
migrationDataValue := strings.Join(migrationDataParams, ",") return strings.Join(migrationDataParams, ",")
return &migrationDataValue
} }
return nil return ""
} }
func (m *clusterOptionsModel) crsData() *string { // crsData returns cluster resource scheduling settings parameter string for API, if any of cluster resource scheduling
// settings are defined, otherwise empty string is returned.
func (m *clusterOptionsModel) crsData() string {
var crsDataParams []string var crsDataParams []string
if !m.CrsHA.IsNull() && m.CrsHA.ValueString() != "" { if !m.CrsHA.IsNull() && m.CrsHA.ValueString() != "" {
@ -109,15 +111,15 @@ func (m *clusterOptionsModel) crsData() *string {
} }
if len(crsDataParams) > 0 { if len(crsDataParams) > 0 {
crsDataValue := strings.Join(crsDataParams, ",") return strings.Join(crsDataParams, ",")
return &crsDataValue
} }
return nil return ""
} }
func (m *clusterOptionsModel) bandwidthData() *string { // bandwidthData returns bandwidth limit settings parameter string for API, if any of bandwidth
// limit settings are defined, otherwise empty string is returned.
func (m *clusterOptionsModel) bandwidthData() string {
var bandwidthParams []string var bandwidthParams []string
if !m.BandwidthLimitClone.IsNull() && m.BandwidthLimitClone.ValueInt64() != 0 { if !m.BandwidthLimitClone.IsNull() && m.BandwidthLimitClone.ValueInt64() != 0 {
@ -141,12 +143,10 @@ func (m *clusterOptionsModel) bandwidthData() *string {
} }
if len(bandwidthParams) > 0 { if len(bandwidthParams) > 0 {
bandwithDataValue := strings.Join(bandwidthParams, ",") return strings.Join(bandwidthParams, ",")
return &bandwithDataValue
} }
return nil return ""
} }
func (m *clusterOptionsModel) toOptionsRequestBody() *cluster.OptionsRequestData { func (m *clusterOptionsModel) toOptionsRequestBody() *cluster.OptionsRequestData {
@ -184,17 +184,32 @@ func (m *clusterOptionsModel) toOptionsRequestBody() *cluster.OptionsRequestData
body.Description = m.Description.ValueStringPointer() body.Description = m.Description.ValueStringPointer()
} }
body.HASettings = m.haData() haData := m.haData()
body.BandwidthLimit = m.bandwidthData() if haData != "" {
body.ClusterResourceScheduling = m.crsData() body.HASettings = &haData
body.Migration = m.migrationData() }
bandwidthData := m.bandwidthData()
if bandwidthData != "" {
body.BandwidthLimit = &bandwidthData
}
crsData := m.crsData()
if crsData != "" {
body.ClusterResourceScheduling = &crsData
}
migrationData := m.migrationData()
if migrationData != "" {
body.Migration = &migrationData
}
return body return body
} }
func (m *clusterOptionsModel) importFromOptionsAPI( func (m *clusterOptionsModel) importFromOptionsAPI(
_ context.Context, _ context.Context,
iface *cluster.OptionsResponseData, opts *cluster.OptionsResponseData,
) error { ) error {
m.BandwidthLimitClone = types.Int64Null() m.BandwidthLimitClone = types.Int64Null()
m.BandwidthLimitDefault = types.Int64Null() m.BandwidthLimitDefault = types.Int64Null()
@ -203,14 +218,14 @@ func (m *clusterOptionsModel) importFromOptionsAPI(
m.BandwidthLimitRestore = types.Int64Null() m.BandwidthLimitRestore = types.Int64Null()
//nolint:nestif //nolint:nestif
if iface.BandwidthLimit != nil { if opts.BandwidthLimit != nil {
for _, bandwidth := range strings.Split(*iface.BandwidthLimit, ",") { for _, bandwidth := range strings.Split(*opts.BandwidthLimit, ",") {
bandwidthData := strings.SplitN(bandwidth, "=", 2) bandwidthData := strings.SplitN(bandwidth, "=", 2)
bandwidthName := bandwidthData[0] bandwidthName := bandwidthData[0]
bandwidthLimit, err := strconv.ParseInt(bandwidthData[1], 10, 64) bandwidthLimit, err := strconv.ParseInt(bandwidthData[1], 10, 64)
if err != nil { if err != nil {
return fmt.Errorf("failed to parse bandwidth limit: %s", *iface.BandwidthLimit) return fmt.Errorf("failed to parse bandwidth limit: %s", *opts.BandwidthLimit)
} }
if bandwidthName == "clone" { if bandwidthName == "clone" {
@ -235,39 +250,46 @@ func (m *clusterOptionsModel) importFromOptionsAPI(
} }
} }
m.EmailFrom = types.StringPointerValue(iface.EmailFrom) m.EmailFrom = types.StringPointerValue(opts.EmailFrom)
m.Keyboard = types.StringPointerValue(iface.Keyboard) m.Keyboard = types.StringPointerValue(opts.Keyboard)
m.Language = types.StringPointerValue(iface.Language) m.Language = types.StringPointerValue(opts.Language)
if iface.MaxWorkers != nil { if opts.MaxWorkers != nil {
m.MaxWorkers = types.Int64Value(int64(*iface.MaxWorkers)) m.MaxWorkers = types.Int64PointerValue(opts.MaxWorkers.PointerInt64())
} else {
m.MaxWorkers = types.Int64Null()
} }
m.Console = types.StringPointerValue(iface.Console) m.Console = types.StringPointerValue(opts.Console)
m.HTTPProxy = types.StringPointerValue(iface.HTTPProxy) m.HTTPProxy = types.StringPointerValue(opts.HTTPProxy)
m.MacPrefix = types.StringPointerValue(iface.MacPrefix) m.MacPrefix = types.StringPointerValue(opts.MacPrefix)
m.Description = types.StringPointerValue(iface.Description)
if iface.HASettings != nil { if opts.Description != nil && *opts.Description != "" {
m.HAShutdownPolicy = types.StringPointerValue(iface.HASettings.ShutdownPolicy) m.Description = types.StringPointerValue(opts.Description)
} else { } else {
m.HAShutdownPolicy = types.StringPointerValue(nil) m.Description = types.StringNull()
} }
if iface.Migration != nil { if opts.HASettings != nil {
m.MigrationType = types.StringPointerValue(iface.Migration.Type) m.HAShutdownPolicy = types.StringPointerValue(opts.HASettings.ShutdownPolicy)
m.MigrationNetwork = types.StringPointerValue(iface.Migration.Network)
} else { } else {
m.MigrationType = types.StringPointerValue(nil) m.HAShutdownPolicy = types.StringNull()
m.MigrationNetwork = types.StringPointerValue(nil)
} }
if iface.ClusterResourceScheduling != nil { if opts.Migration != nil {
m.CrsHARebalanceOnStart = types.BoolValue(bool(*iface.ClusterResourceScheduling.HaRebalanceOnStart)) m.MigrationType = types.StringPointerValue(opts.Migration.Type)
m.CrsHA = types.StringPointerValue(iface.ClusterResourceScheduling.HA) m.MigrationNetwork = types.StringPointerValue(opts.Migration.Network)
} else { } else {
m.CrsHARebalanceOnStart = types.BoolPointerValue(nil) m.MigrationType = types.StringNull()
m.CrsHA = types.StringPointerValue(nil) m.MigrationNetwork = types.StringNull()
}
if opts.ClusterResourceScheduling != nil {
m.CrsHARebalanceOnStart = types.BoolPointerValue(opts.ClusterResourceScheduling.HaRebalanceOnStart.PointerBool())
m.CrsHA = types.StringPointerValue(opts.ClusterResourceScheduling.HA)
} else {
m.CrsHARebalanceOnStart = types.BoolNull()
m.CrsHA = types.StringNull()
} }
return nil return nil
@ -303,7 +325,6 @@ func (r *clusterOptionsResource) Schema(
"email_from": schema.StringAttribute{ "email_from": schema.StringAttribute{
Description: "email address to send notification from (default is root@$hostname).", Description: "email address to send notification from (default is root@$hostname).",
Optional: true, Optional: true,
Computed: true,
}, },
"keyboard": schema.StringAttribute{ "keyboard": schema.StringAttribute{
Description: "Default keyboard layout for vnc server.", Description: "Default keyboard layout for vnc server.",
@ -311,24 +332,25 @@ func (r *clusterOptionsResource) Schema(
"`de-ch` | `da` | `en-gb` | `en-us` | `es` | `fi` | `fr` | `fr-be` | `fr-ca` " + "`de-ch` | `da` | `en-gb` | `en-us` | `es` | `fi` | `fr` | `fr-be` | `fr-ca` " +
"| `fr-ch` | `hu` | `is` | `it` | `ja` | `lt` | `mk` | `nl` | `no` | `pl` | " + "| `fr-ch` | `hu` | `is` | `it` | `ja` | `lt` | `mk` | `nl` | `no` | `pl` | " +
"`pt` | `pt-br` | `sv` | `sl` | `tr`.", "`pt` | `pt-br` | `sv` | `sl` | `tr`.",
Optional: true, Optional: true,
Computed: true, Validators: []validator.String{
Validators: []validator.String{validators.KeyboardLayoutValidator()}, validators.KeyboardLayoutValidator(),
},
}, },
"max_workers": schema.Int64Attribute{ "max_workers": schema.Int64Attribute{
Description: "Defines how many workers (per node) are maximal started on" + Description: "Defines how many workers (per node) are maximal started on" +
" actions like 'stopall VMs' or task from the ha-manager.", " actions like 'stopall VMs' or task from the ha-manager.",
Optional: true, Optional: true,
Computed: true,
}, },
"language": schema.StringAttribute{ "language": schema.StringAttribute{
Description: "Default GUI language.", Description: "Default GUI language.",
MarkdownDescription: "Default GUI language. Must be `ca` | `da` | `de` " + MarkdownDescription: "Default GUI language. Must be `ca` | `da` | `de` " +
"| `en` | `es` | `eu` | `fa` | `fr` | `he` | `it` | `ja` | `nb` | " + "| `en` | `es` | `eu` | `fa` | `fr` | `he` | `it` | `ja` | `nb` | " +
"`nn` | `pl` | `pt_BR` | `ru` | `sl` | `sv` | `tr` | `zh_CN` | `zh_TW`.", "`nn` | `pl` | `pt_BR` | `ru` | `sl` | `sv` | `tr` | `zh_CN` | `zh_TW`.",
Optional: true, Optional: true,
Computed: true, Validators: []validator.String{
Validators: []validator.String{validators.LanguageValidator()}, validators.LanguageValidator(),
},
}, },
"console": schema.StringAttribute{ "console": schema.StringAttribute{
Description: "Select the default Console viewer.", Description: "Select the default Console viewer.",
@ -342,7 +364,6 @@ func (r *clusterOptionsResource) Schema(
"(e.g. SPICE not activated for the VM), " + "(e.g. SPICE not activated for the VM), " +
"the fallback is noVNC.", "the fallback is noVNC.",
Optional: true, Optional: true,
Computed: true,
Validators: []validator.String{stringvalidator.OneOf([]string{ Validators: []validator.String{stringvalidator.OneOf([]string{
"applet", "applet",
"vv", "vv",
@ -355,25 +376,21 @@ func (r *clusterOptionsResource) Schema(
MarkdownDescription: "Specify external http proxy which is used for downloads " + MarkdownDescription: "Specify external http proxy which is used for downloads " +
"(example: `http://username:password@host:port/`).", "(example: `http://username:password@host:port/`).",
Optional: true, Optional: true,
Computed: true,
}, },
"mac_prefix": schema.StringAttribute{ "mac_prefix": schema.StringAttribute{
Description: "Prefix for autogenerated MAC addresses.", Description: "Prefix for autogenerated MAC addresses.",
Optional: true, Optional: true,
Computed: true,
}, },
"description": schema.StringAttribute{ "description": schema.StringAttribute{
Description: "Datacenter description. Shown in the web-interface datacenter notes panel. " + Description: "Datacenter description. Shown in the web-interface datacenter notes panel. " +
"This is saved as comment inside the configuration file.", "This is saved as comment inside the configuration file.",
Optional: true, Optional: true,
Computed: true,
}, },
"ha_shutdown_policy": schema.StringAttribute{ "ha_shutdown_policy": schema.StringAttribute{
Description: "Cluster wide HA shutdown policy.", Description: "Cluster wide HA shutdown policy.",
MarkdownDescription: "Cluster wide HA shutdown policy. " + MarkdownDescription: "Cluster wide HA shutdown policy (). " +
"Must be `freeze` | `failover` | `migrate` | `conditional`.", "Must be `freeze` | `failover` | `migrate` | `conditional` (default is `conditional`).",
Optional: true, Optional: true,
Computed: true,
Validators: []validator.String{stringvalidator.OneOf([]string{ Validators: []validator.String{stringvalidator.OneOf([]string{
"freeze", "freeze",
"failover", "failover",
@ -382,10 +399,10 @@ func (r *clusterOptionsResource) Schema(
}...)}, }...)},
}, },
"migration_type": schema.StringAttribute{ "migration_type": schema.StringAttribute{
Description: "Cluster wide migration type.", Description: "Cluster wide migration type.",
MarkdownDescription: "Cluster wide migration type. Must be `secure` | `unsecure`.", MarkdownDescription: "Cluster wide migration type. Must be `secure` | `unsecure` " +
Optional: true, "(default is `secure`).",
Computed: true, Optional: true,
Validators: []validator.String{stringvalidator.OneOf([]string{ Validators: []validator.String{stringvalidator.OneOf([]string{
"secure", "secure",
"unsecure", "unsecure",
@ -394,11 +411,10 @@ func (r *clusterOptionsResource) Schema(
"migration_cidr": schema.StringAttribute{ "migration_cidr": schema.StringAttribute{
Description: "Cluster wide migration network CIDR.", Description: "Cluster wide migration network CIDR.",
Optional: true, Optional: true,
Computed: true,
}, },
"crs_ha": schema.StringAttribute{ "crs_ha": schema.StringAttribute{
Description: "Cluster resource scheduling setting for HA.", Description: "Cluster resource scheduling setting for HA.",
MarkdownDescription: "Cluster resource scheduling setting for HA. Must be `static` | `basic`.", MarkdownDescription: "Cluster resource scheduling setting for HA. Must be `static` | `basic` (default is `basic`).",
Optional: true, Optional: true,
Computed: true, Computed: true,
Validators: []validator.String{stringvalidator.OneOf([]string{ Validators: []validator.String{stringvalidator.OneOf([]string{
@ -409,32 +425,26 @@ func (r *clusterOptionsResource) Schema(
"crs_ha_rebalance_on_start": schema.BoolAttribute{ "crs_ha_rebalance_on_start": schema.BoolAttribute{
Description: "Cluster resource scheduling setting for HA rebalance on start.", Description: "Cluster resource scheduling setting for HA rebalance on start.",
Optional: true, Optional: true,
Computed: true,
}, },
"bandwidth_limit_clone": schema.Int64Attribute{ "bandwidth_limit_clone": schema.Int64Attribute{
Description: "Clone I/O bandwidth limit in KiB/s.", Description: "Clone I/O bandwidth limit in KiB/s.",
Optional: true, Optional: true,
Computed: true,
}, },
"bandwidth_limit_default": schema.Int64Attribute{ "bandwidth_limit_default": schema.Int64Attribute{
Description: "Default I/O bandwidth limit in KiB/s.", Description: "Default I/O bandwidth limit in KiB/s.",
Optional: true, Optional: true,
Computed: true,
}, },
"bandwidth_limit_migration": schema.Int64Attribute{ "bandwidth_limit_migration": schema.Int64Attribute{
Description: "Migration I/O bandwidth limit in KiB/s.", Description: "Migration I/O bandwidth limit in KiB/s.",
Optional: true, Optional: true,
Computed: true,
}, },
"bandwidth_limit_move": schema.Int64Attribute{ "bandwidth_limit_move": schema.Int64Attribute{
Description: "Move I/O bandwidth limit in KiB/s.", Description: "Move I/O bandwidth limit in KiB/s.",
Optional: true, Optional: true,
Computed: true,
}, },
"bandwidth_limit_restore": schema.Int64Attribute{ "bandwidth_limit_restore": schema.Int64Attribute{
Description: "Restore I/O bandwidth limit in KiB/s.", Description: "Restore I/O bandwidth limit in KiB/s.",
Optional: true, Optional: true,
Computed: true,
}, },
}, },
} }
@ -464,10 +474,12 @@ func (r *clusterOptionsResource) Configure(
r.client = client r.client = client
} }
// Create update must-existing cluster options interface. // Create update must-existing cluster options.
// func (r *clusterOptionsResource) Create(
//nolint:lll ctx context.Context,
func (r *clusterOptionsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { req resource.CreateRequest,
resp *resource.CreateResponse,
) {
var plan clusterOptionsModel var plan clusterOptionsModel
diags := req.Plan.Get(ctx, &plan) diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...) resp.Diagnostics.Append(diags...)
@ -481,7 +493,7 @@ func (r *clusterOptionsResource) Create(ctx context.Context, req resource.Create
err := r.client.Cluster().CreateUpdateOptions(ctx, body) err := r.client.Cluster().CreateUpdateOptions(ctx, body)
if err != nil { if err != nil {
resp.Diagnostics.AddError( resp.Diagnostics.AddError(
"Error creating cluster options interface", "Error creating cluster options",
"Could not create cluster options, unexpected error: "+err.Error(), "Could not create cluster options, unexpected error: "+err.Error(),
) )
@ -515,7 +527,7 @@ func (r *clusterOptionsResource) read(ctx context.Context, model *clusterOptions
if err != nil { if err != nil {
diags.AddError( diags.AddError(
"Error converting cluster options interface to a model", "Error converting cluster options to a model",
"Could not import cluster options from API response, unexpected error: "+err.Error(), "Could not import cluster options from API response, unexpected error: "+err.Error(),
) )
@ -523,7 +535,7 @@ func (r *clusterOptionsResource) read(ctx context.Context, model *clusterOptions
} }
} }
// Read reads a cluster options interface. // Read reads cluster options.
func (r *clusterOptionsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { func (r *clusterOptionsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
// Get current state // Get current state
var state clusterOptionsModel var state clusterOptionsModel
@ -544,10 +556,12 @@ func (r *clusterOptionsResource) Read(ctx context.Context, req resource.ReadRequ
resp.Diagnostics.Append(diags...) resp.Diagnostics.Append(diags...)
} }
// Update updates a cluster options interface. // Update updates cluster options.
// func (r *clusterOptionsResource) Update(
//nolint:lll ctx context.Context,
func (r *clusterOptionsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { req resource.UpdateRequest,
resp *resource.UpdateResponse,
) {
var plan, state clusterOptionsModel var plan, state clusterOptionsModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
@ -565,19 +579,19 @@ func (r *clusterOptionsResource) Update(ctx context.Context, req resource.Update
toDelete = append(toDelete, "keyboard") toDelete = append(toDelete, "keyboard")
} }
if (plan.bandwidthData() == nil && state.bandwidthData() != nil) || (*plan.bandwidthData() != *state.bandwidthData() && *plan.bandwidthData() == "") { if plan.bandwidthData() != state.bandwidthData() && plan.bandwidthData() == "" {
toDelete = append(toDelete, "bwlimit") toDelete = append(toDelete, "bwlimit")
} }
if (plan.crsData() == nil && state.crsData() != nil) || (*plan.crsData() != *state.crsData() && *plan.crsData() == "") { if plan.crsData() != state.crsData() && plan.crsData() == "" {
toDelete = append(toDelete, "crs") toDelete = append(toDelete, "crs")
} }
if (plan.haData() == nil && state.haData() != nil) || (*plan.haData() != *state.haData() && *plan.haData() == "") { if plan.haData() != state.haData() && plan.haData() == "" {
toDelete = append(toDelete, "ha") toDelete = append(toDelete, "ha")
} }
if (plan.migrationData() == nil && state.migrationData() != nil) || (*plan.migrationData() != *state.migrationData() && *plan.migrationData() == "") { if plan.migrationData() != state.migrationData() && plan.migrationData() == "" {
toDelete = append(toDelete, "migration") toDelete = append(toDelete, "migration")
} }
@ -617,7 +631,7 @@ func (r *clusterOptionsResource) Update(ctx context.Context, req resource.Update
err := r.client.Cluster().CreateUpdateOptions(ctx, body) err := r.client.Cluster().CreateUpdateOptions(ctx, body)
if err != nil { if err != nil {
resp.Diagnostics.AddError( resp.Diagnostics.AddError(
"Error updating cluster options interface", "Error updating cluster options",
"Could not update cluster options, unexpected error: "+err.Error(), "Could not update cluster options, unexpected error: "+err.Error(),
) )
@ -633,20 +647,83 @@ func (r *clusterOptionsResource) Update(ctx context.Context, req resource.Update
resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) resp.Diagnostics.Append(resp.State.Set(ctx, plan)...)
} }
// Delete deletes a cluster options interface. // Delete deletes cluster options.
// func (r *clusterOptionsResource) Delete(
//nolint:lll ctx context.Context,
func (r *clusterOptionsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { req resource.DeleteRequest,
resp *resource.DeleteResponse,
) {
var state clusterOptionsModel var state clusterOptionsModel
diags := req.State.Get(ctx, &state) diags := req.State.Get(ctx, &state)
resp.Diagnostics.Append(diags...) resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() { var toDelete []string
return
if !state.Keyboard.IsNull() && state.Keyboard.ValueString() != "" {
toDelete = append(toDelete, "keyboard")
}
if state.bandwidthData() != "" {
toDelete = append(toDelete, "bwlimit")
}
if state.crsData() != "" {
toDelete = append(toDelete, "crs")
}
if state.haData() != "" {
toDelete = append(toDelete, "ha")
}
if state.migrationData() != "" {
toDelete = append(toDelete, "migration")
}
if !state.EmailFrom.IsNull() && state.EmailFrom.ValueString() != "" {
toDelete = append(toDelete, "email_from")
}
if !state.Language.IsNull() && state.Language.ValueString() != "" {
toDelete = append(toDelete, "language")
}
if !state.Console.IsNull() && state.Console.ValueString() != "" {
toDelete = append(toDelete, "console")
}
if !state.HTTPProxy.IsNull() && state.HTTPProxy.ValueString() != "" {
toDelete = append(toDelete, "http_proxy")
}
if !state.MacPrefix.IsNull() && state.MacPrefix.ValueString() != "" {
toDelete = append(toDelete, "mac_prefix")
}
if !state.Description.IsNull() && state.Description.ValueString() != "" {
toDelete = append(toDelete, "description")
}
if !state.MaxWorkers.IsNull() && state.MaxWorkers.ValueInt64() != 0 {
toDelete = append(toDelete, "max_workers")
}
if len(toDelete) > 0 {
d := strings.Join(toDelete, ",")
body := &cluster.OptionsRequestData{
Delete: &d,
}
err := r.client.Cluster().CreateUpdateOptions(ctx, body)
if err != nil {
resp.Diagnostics.AddError(
"Error updating cluster options",
"Could not update cluster options, unexpected error: "+err.Error(),
)
}
} }
} }
// ImportState a cluster options interface. // ImportState imports cluster options.
func (r *clusterOptionsResource) ImportState( func (r *clusterOptionsResource) ImportState(
ctx context.Context, ctx context.Context,
req resource.ImportStateRequest, req resource.ImportStateRequest,

View File

@ -13,84 +13,102 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
) )
func TestClusterOptionsResource(t *testing.T) { const accTestClusterOptionsName = "proxmox_virtual_environment_cluster_options.test_options"
func TestAccResourceClusterOptions(t *testing.T) {
t.Parallel() t.Parallel()
accProviders := testAccMuxProviders(context.Background(), t) accProviders := testAccMuxProviders(context.Background(), t)
resourceName := "proxmox_virtual_environment_cluster_options.test_options"
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: accProviders, ProtoV6ProviderFactories: accProviders,
Steps: []resource.TestStep{ Steps: []resource.TestStep{
// Create and Read testing // Create and Read testing
{ {
Config: ` Config: testAccResourceClusterOptionsCreatedConfig(),
resource "proxmox_virtual_environment_cluster_options" "test_options" { Check: testAccResourceClusterOptionsCreatedCheck(),
language = "en"
keyboard = "pl"
email_from = "example@example.com"
bandwidth_limit_migration = 555554
bandwidth_limit_default = 666666
max_workers = 5
crs_ha = "static"
ha_shutdown_policy = "freeze"
migration_cidr = "10.0.0.0/8"
migration_type = "secure"
}
`,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "language", "en"),
resource.TestCheckResourceAttr(resourceName, "keyboard", "pl"),
resource.TestCheckResourceAttr(resourceName, "email_from", "example@example.com"),
resource.TestCheckResourceAttr(resourceName, "bandwidth_limit_migration", "555554"),
resource.TestCheckResourceAttr(resourceName, "bandwidth_limit_default", "666666"),
resource.TestCheckResourceAttr(resourceName, "max_workers", "5"),
resource.TestCheckResourceAttr(resourceName, "crs_ha", "static"),
resource.TestCheckResourceAttr(resourceName, "ha_shutdown_policy", "freeze"),
resource.TestCheckResourceAttr(resourceName, "migration_cidr", "10.0.0.0/8"),
resource.TestCheckResourceAttr(resourceName, "migration_type", "secure"),
resource.TestCheckResourceAttr(resourceName, "id", "cluster"),
resource.TestCheckNoResourceAttr(resourceName, "bandwidth_limit_restore"),
resource.TestCheckNoResourceAttr(resourceName, "bandwidth_limit_move"),
),
}, },
// ImportState testing // ImportState testing
{ {
ResourceName: resourceName, ResourceName: accTestClusterOptionsName,
ImportState: true, ImportState: true,
ImportStateVerify: true, ImportStateVerify: true,
}, },
// Update testing // Update testing
{ {
Config: ` Config: testAccResourceClusterOptionsUpdatedConfig(),
resource "proxmox_virtual_environment_cluster_options" "test_options" { Check: testAccResourceClusterOptionsUpdatedCheck(),
language = "en"
keyboard = "pl"
email_from = "ged@gont.earthsea"
bandwidth_limit_migration = 111111
bandwidth_limit_default = 666666
max_workers = 6
migration_cidr = "10.0.0.0/8"
migration_type = "secure"
}
`,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "language", "en"),
resource.TestCheckResourceAttr(resourceName, "keyboard", "pl"),
resource.TestCheckResourceAttr(resourceName, "email_from", "ged@gont.earthsea"),
resource.TestCheckResourceAttr(resourceName, "bandwidth_limit_migration", "111111"),
resource.TestCheckResourceAttr(resourceName, "bandwidth_limit_default", "666666"),
resource.TestCheckResourceAttr(resourceName, "max_workers", "6"),
resource.TestCheckResourceAttr(resourceName, "migration_cidr", "10.0.0.0/8"),
resource.TestCheckResourceAttr(resourceName, "migration_type", "secure"),
resource.TestCheckResourceAttr(resourceName, "id", "cluster"),
resource.TestCheckNoResourceAttr(resourceName, "bandwidth_limit_restore"),
resource.TestCheckNoResourceAttr(resourceName, "bandwidth_limit_move"),
resource.TestCheckNoResourceAttr(resourceName, "crs_ha"),
resource.TestCheckNoResourceAttr(resourceName, "ha_shutdown_policy"),
),
}, },
}, },
}) })
} }
func testAccResourceClusterOptionsCreatedConfig() string {
return `
resource "proxmox_virtual_environment_cluster_options" "test_options" {
bandwidth_limit_default = 666666
bandwidth_limit_migration = 555554
crs_ha = "static"
email_from = "example@example.com"
ha_shutdown_policy = "freeze"
http_proxy = "http://example.com"
keyboard = "pl"
language = "en"
max_workers = 5
migration_cidr = "10.0.0.0/8"
migration_type = "secure"
bandwidth_limit_restore = 777777
}
`
}
func testAccResourceClusterOptionsCreatedCheck() resource.TestCheckFunc {
return resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(accTestClusterOptionsName, "bandwidth_limit_default", "666666"),
resource.TestCheckResourceAttr(accTestClusterOptionsName, "bandwidth_limit_migration", "555554"),
resource.TestCheckResourceAttr(accTestClusterOptionsName, "bandwidth_limit_restore", "777777"),
resource.TestCheckResourceAttr(accTestClusterOptionsName, "crs_ha", "static"),
resource.TestCheckResourceAttr(accTestClusterOptionsName, "email_from", "example@example.com"),
resource.TestCheckResourceAttr(accTestClusterOptionsName, "ha_shutdown_policy", "freeze"),
resource.TestCheckResourceAttr(accTestClusterOptionsName, "http_proxy", "http://example.com"),
resource.TestCheckResourceAttr(accTestClusterOptionsName, "id", "cluster"),
resource.TestCheckResourceAttr(accTestClusterOptionsName, "keyboard", "pl"),
resource.TestCheckResourceAttr(accTestClusterOptionsName, "language", "en"),
resource.TestCheckResourceAttr(accTestClusterOptionsName, "max_workers", "5"),
resource.TestCheckResourceAttr(accTestClusterOptionsName, "migration_cidr", "10.0.0.0/8"),
resource.TestCheckResourceAttr(accTestClusterOptionsName, "migration_type", "secure"),
resource.TestCheckNoResourceAttr(accTestClusterOptionsName, "bandwidth_limit_move"),
)
}
func testAccResourceClusterOptionsUpdatedConfig() string {
return `
resource "proxmox_virtual_environment_cluster_options" "test_options" {
bandwidth_limit_default = 333333
bandwidth_limit_migration = 111111
email_from = "ged@gont.earthsea"
language = "en"
max_workers = 6
migration_cidr = "10.0.0.1/8"
migration_type = "secure"
}
`
}
func testAccResourceClusterOptionsUpdatedCheck() resource.TestCheckFunc {
return resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(accTestClusterOptionsName, "bandwidth_limit_default", "333333"),
resource.TestCheckResourceAttr(accTestClusterOptionsName, "bandwidth_limit_migration", "111111"),
resource.TestCheckResourceAttr(accTestClusterOptionsName, "email_from", "ged@gont.earthsea"),
resource.TestCheckResourceAttr(accTestClusterOptionsName, "id", "cluster"),
resource.TestCheckResourceAttr(accTestClusterOptionsName, "language", "en"),
resource.TestCheckResourceAttr(accTestClusterOptionsName, "max_workers", "6"),
resource.TestCheckResourceAttr(accTestClusterOptionsName, "migration_cidr", "10.0.0.1/8"),
resource.TestCheckResourceAttr(accTestClusterOptionsName, "migration_type", "secure"),
resource.TestCheckNoResourceAttr(accTestClusterOptionsName, "bandwidth_limit_move"),
resource.TestCheckNoResourceAttr(accTestClusterOptionsName, "crs_ha"),
resource.TestCheckNoResourceAttr(accTestClusterOptionsName, "ha_shutdown_policy"),
resource.TestCheckNoResourceAttr(accTestClusterOptionsName, "http_proxy"),
resource.TestCheckNoResourceAttr(accTestClusterOptionsName, "keyboard"),
)
}

View File

@ -114,6 +114,17 @@ func (r *CustomInt) UnmarshalJSON(b []byte) error {
return nil return nil
} }
// PointerInt64 returns a pointer to an int64.
func (r *CustomInt) PointerInt64() *int64 {
if r == nil {
return nil
}
i := int64(*r)
return &i
}
// MarshalJSON converts a boolean to a JSON value. // MarshalJSON converts a boolean to a JSON value.
func (r *CustomLineBreakSeparatedList) MarshalJSON() ([]byte, error) { func (r *CustomLineBreakSeparatedList) MarshalJSON() ([]byte, error) {
s := strings.Join(*r, "\n") s := strings.Join(*r, "\n")