0
0
mirror of https://github.com/bpg/terraform-provider-proxmox.git synced 2025-06-29 18:21:10 +00:00

feat(datastores)!: implement new structured format and filters for datastores data source (#1875)

Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
Pavel Boldyrev 2025-04-01 17:32:29 -04:00 committed by GitHub
parent ece13f7716
commit b5b61b48dc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 533 additions and 335 deletions

View File

@ -3,32 +3,54 @@ layout: page
title: proxmox_virtual_environment_datastores
parent: Data Sources
subcategory: Virtual Environment
description: |-
Retrieves information about all the datastores available to a specific node.
---
# Data Source: proxmox_virtual_environment_datastores
Retrieves information about all the datastores available to a specific node.
## Example Usage
```hcl
data "proxmox_virtual_environment_datastores" "first_node" {
node_name = "first-node"
}
```
## Argument Reference
<!-- schema generated by tfplugindocs -->
## Schema
- `node_name` - (Required) A node name.
### Required
## Attribute Reference
- `node_name` (String) The name of the node to retrieve the stores from.
- `active` - Whether the datastore is active.
- `content_types` - The allowed content types.
- `datastore_ids` - The datastore identifiers.
- `enabled` - Whether the datastore is enabled.
- `shared` - Whether the datastore is shared.
- `space_available` - The available space in bytes.
- `space_total` - The total space in bytes.
- `space_used` - The used space in bytes.
- `types` - The storage types.
### Optional
- `datastores` (Attributes List) The list of datastores. (see [below for nested schema](#nestedatt--datastores))
- `filters` (Attributes) The filters to apply to the stores. (see [below for nested schema](#nestedatt--filters))
<a id="nestedatt--datastores"></a>
### Nested Schema for `datastores`
Required:
- `content_types` (Set of String) Allowed store content types.
- `id` (String) The ID of the store.
- `node_name` (String) The name of the node the store is on.
- `type` (String) Store type.
Optional:
- `active` (Boolean) Whether the store is active.
- `enabled` (Boolean) Whether the store is enabled.
- `shared` (Boolean) Shared flag from store configuration.
- `space_available` (Number) Available store space in bytes.
- `space_total` (Number) Total store space in bytes.
- `space_used` (Number) Used store space in bytes.
- `space_used_fraction` (Number) Used fraction (used/total).
<a id="nestedatt--filters"></a>
### Nested Schema for `filters`
Optional:
- `content_types` (Set of String) Only list stores with the given content types.
- `id` (String) Only list stores with the given ID.
- `target` (String) If `target` is different to `node_name`, then only lists shared stores which content is accessible on this node and the specified `target` node.

View File

@ -55,7 +55,7 @@ func (r *aclResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *
stringplanmodifier.RequiresReplace(),
},
},
"id": attribute.ID(),
"id": attribute.ResourceID(),
"path": schema.StringAttribute{
Description: "Access control path",
Required: true,

View File

@ -76,7 +76,7 @@ func (r *userTokenResource) Schema(
}, "must be a valid RFC3339 date"),
},
},
"id": attribute.ID("Unique token identifier with format `<user_id>!<token_name>`."),
"id": attribute.ResourceID("Unique token identifier with format `<user_id>!<token_name>`."),
"privileges_separation": schema.BoolAttribute{
Description: "Restrict API token privileges with separate ACLs (default)",
MarkdownDescription: "Restrict API token privileges with separate ACLs (default), " +

View File

@ -13,8 +13,8 @@ import (
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
)
// ID generates an attribute definition suitable for the always-present `id` attribute.
func ID(desc ...string) schema.StringAttribute {
// ResourceID generates an attribute definition suitable for the always-present resource `id` attribute.
func ResourceID(desc ...string) schema.StringAttribute {
a := schema.StringAttribute{
Computed: true,
Description: "The unique identifier of this resource.",

View File

@ -51,7 +51,7 @@ func (d *haGroupDatasource) Schema(_ context.Context, _ datasource.SchemaRequest
resp.Schema = schema.Schema{
Description: "Retrieves information about a specific High Availability group.",
Attributes: map[string]schema.Attribute{
"id": attribute.ID(),
"id": attribute.ResourceID(),
"group": schema.StringAttribute{
Description: "The identifier of the High Availability group to read.",
Required: true,

View File

@ -57,7 +57,7 @@ func (d *haGroupsDatasource) Schema(_ context.Context, _ datasource.SchemaReques
resp.Schema = schema.Schema{
Description: "Retrieves the list of High Availability groups.",
Attributes: map[string]schema.Attribute{
"id": attribute.ID(),
"id": attribute.ResourceID(),
"group_ids": schema.SetAttribute{
Description: "The identifiers of the High Availability groups.",
ElementType: types.StringType,

View File

@ -50,7 +50,7 @@ func (d *haResourceDatasource) Schema(_ context.Context, _ datasource.SchemaRequ
resp.Schema = schema.Schema{
Description: "Retrieves the list of High Availability resources.",
Attributes: map[string]schema.Attribute{
"id": attribute.ID(),
"id": attribute.ResourceID(),
"resource_id": schema.StringAttribute{
Description: "The identifier of the Proxmox HA resource to read.",
Required: true,

View File

@ -64,7 +64,7 @@ func (d *haResourcesDatasource) Schema(_ context.Context, _ datasource.SchemaReq
resp.Schema = schema.Schema{
Description: "Retrieves the list of High Availability resources.",
Attributes: map[string]schema.Attribute{
"id": attribute.ID(),
"id": attribute.ResourceID(),
"type": schema.StringAttribute{
Description: "The type of High Availability resources to fetch (`vm` or `ct`). All resources " +
"will be fetched if this option is unset.",

View File

@ -64,7 +64,7 @@ func (r *hagroupResource) Schema(
resp.Schema = schema.Schema{
Description: "Manages a High Availability group in a Proxmox VE cluster.",
Attributes: map[string]schema.Attribute{
"id": attribute.ID(),
"id": attribute.ResourceID(),
"group": schema.StringAttribute{
Description: "The identifier of the High Availability group to manage.",
Required: true,

View File

@ -68,7 +68,7 @@ func (r *haResourceResource) Schema(
resp.Schema = schema.Schema{
Description: "Manages Proxmox HA resources.",
Attributes: map[string]schema.Attribute{
"id": attribute.ID(),
"id": attribute.ResourceID(),
"resource_id": schema.StringAttribute{
Description: "The Proxmox HA resource identifier",
Required: true,

View File

@ -71,7 +71,7 @@ func (r *metricsServerDatasource) Schema(
resp.Schema = schema.Schema{
Description: "Retrieves information about a specific PVE metric server.",
Attributes: map[string]schema.Attribute{
"id": attribute.ID(),
"id": attribute.ResourceID(),
"name": schema.StringAttribute{
Description: "Unique name that will be ID of this metric server in PVE.",
Required: true,

View File

@ -79,7 +79,7 @@ func (r *metricsServerResource) Schema(
resp.Schema = schema.Schema{
Description: "Manages PVE metrics server.",
Attributes: map[string]schema.Attribute{
"id": attribute.ID(),
"id": attribute.ResourceID(),
"name": schema.StringAttribute{
Description: "Unique name that will be ID of this metric server in PVE.",
Required: true,

View File

@ -457,7 +457,7 @@ func (r *clusterOptionsResource) Schema(
resp.Schema = schema.Schema{
Description: "Manages Proxmox VE Cluster Datacenter options.",
Attributes: map[string]schema.Attribute{
"id": attribute.ID(),
"id": attribute.ResourceID(),
"email_from": schema.StringAttribute{
Description: "email address to send notification from (default is root@$hostname).",
Optional: true,

View File

@ -153,7 +153,7 @@ func (d *repositoryDataSource) Schema(
Description: "The list of package distributions.",
ElementType: types.StringType,
},
SchemaAttrNameTerraformID: attribute.ID("The unique identifier of this APT repository data source."),
SchemaAttrNameTerraformID: attribute.ResourceID("The unique identifier of this APT repository data source."),
SchemaAttrNameURIs: schema.ListAttribute{
Computed: true,
Description: "The list of repository URIs.",

View File

@ -132,7 +132,7 @@ func (d *standardRepositoryDataSource) Schema(
Computed: true,
Description: "Indicates the activation status.",
},
SchemaAttrNameTerraformID: attribute.ID(
SchemaAttrNameTerraformID: attribute.ResourceID(
"The unique identifier of this APT standard repository data source.",
),
},

View File

@ -309,7 +309,7 @@ func (r *repositoryResource) Schema(_ context.Context, _ resource.SchemaRequest,
Description: "The list of package distributions.",
ElementType: types.StringType,
},
SchemaAttrNameTerraformID: attribute.ID("The unique identifier of this APT repository resource."),
SchemaAttrNameTerraformID: attribute.ResourceID("The unique identifier of this APT repository resource."),
SchemaAttrNameURIs: schema.ListAttribute{
Computed: true,
Description: "The list of repository URIs.",

View File

@ -275,7 +275,7 @@ func (r *standardRepositoryResource) Schema(
Computed: true,
Description: "Indicates the activation status.",
},
SchemaAttrNameTerraformID: attribute.ID(
SchemaAttrNameTerraformID: attribute.ResourceID(
"The unique identifier of this APT standard repository resource.",
),
},

View File

@ -0,0 +1,122 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package datastores
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/bpg/terraform-provider-proxmox/fwprovider/config"
"github.com/bpg/terraform-provider-proxmox/fwprovider/types/stringset"
"github.com/bpg/terraform-provider-proxmox/proxmox"
"github.com/bpg/terraform-provider-proxmox/proxmox/nodes/storage"
)
// Ensure the implementation satisfies the expected interfaces.
var (
_ datasource.DataSource = &Datasource{}
_ datasource.DataSourceWithConfigure = &Datasource{}
)
// Datasource is the implementation of datastores datasource.
type Datasource struct {
client proxmox.Client
}
// NewDataSource creates a new datastores datasource.
func NewDataSource() datasource.DataSource {
return &Datasource{}
}
// Metadata defines the name of the resource.
func (d *Datasource) Metadata(
_ context.Context,
req datasource.MetadataRequest,
resp *datasource.MetadataResponse,
) {
resp.TypeName = req.ProviderTypeName + "_datastores"
}
// Configure sets the client for the resource.
func (d *Datasource) Configure(
_ context.Context,
req datasource.ConfigureRequest,
resp *datasource.ConfigureResponse,
) {
if req.ProviderData == nil {
return
}
cfg, ok := req.ProviderData.(config.DataSource)
if !ok {
resp.Diagnostics.AddError(
"Unexpected DataSource Configure Type",
fmt.Sprintf("Expected config.DataSource, got: %T", req.ProviderData),
)
return
}
d.client = cfg.Client
}
func (d *Datasource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var model Model
resp.Diagnostics.Append(req.Config.Get(ctx, &model)...)
if resp.Diagnostics.HasError() {
return
}
storageAPI := d.client.Node(model.NodeName.ValueString()).Storage("")
r := storage.DatastoreListRequestBody{}
if model.Filters != nil {
r.ContentTypes = model.Filters.ContentTypes.ValueList(ctx, &resp.Diagnostics)
r.ID = model.Filters.ID.ValueStringPointer()
r.Target = model.Filters.Target.ValueStringPointer()
}
dsList, err := storageAPI.ListDatastores(ctx, &r)
if err != nil {
resp.Diagnostics.AddError(
"Unable to read datastores",
err.Error(),
)
return
}
model.Datastores = make([]Datastore, 0, len(dsList))
for _, ds := range dsList {
datastore := Datastore{}
if ds.ContentTypes != nil {
datastore.ContentTypes = stringset.NewValueList(*ds.ContentTypes, &resp.Diagnostics)
}
datastore.Active = types.BoolPointerValue(ds.Active.PointerBool())
datastore.Enabled = types.BoolPointerValue(ds.Enabled.PointerBool())
datastore.ID = types.StringValue(ds.ID)
datastore.NodeName = types.StringValue(model.NodeName.ValueString())
datastore.Shared = types.BoolPointerValue(ds.Shared.PointerBool())
datastore.SpaceAvailable = types.Int64PointerValue(ds.SpaceAvailable.PointerInt64())
datastore.SpaceTotal = types.Int64PointerValue(ds.SpaceTotal.PointerInt64())
datastore.SpaceUsed = types.Int64PointerValue(ds.SpaceUsed.PointerInt64())
datastore.SpaceUsedFraction = types.Float64PointerValue(ds.SpaceUsedPercentage.PointerFloat64())
datastore.Type = types.StringValue(ds.Type)
model.Datastores = append(model.Datastores, datastore)
}
resp.Diagnostics.Append(resp.State.Set(ctx, model)...)
}

View File

@ -0,0 +1,98 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package datastores
import (
"context"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/bpg/terraform-provider-proxmox/fwprovider/types/stringset"
)
// Schema defines the schema for the resource.
func (d *Datasource) Schema(
_ context.Context,
_ datasource.SchemaRequest,
resp *datasource.SchemaResponse,
) {
resp.Schema = schema.Schema{
Description: "Retrieves information about all the datastores available to a specific node.",
Attributes: map[string]schema.Attribute{
"node_name": schema.StringAttribute{
Description: "The name of the node to retrieve the stores from.",
Required: true,
},
"filters": schema.SingleNestedAttribute{
Description: "The filters to apply to the stores.",
Optional: true,
Attributes: map[string]schema.Attribute{
"content_types": stringset.DataSourceAttribute("Only list stores with the given content types.", "", true),
"id": schema.StringAttribute{
Description: "Only list stores with the given ID.",
Optional: true,
},
"target": schema.StringAttribute{
Description: "If `target` is different to `node_name`, then only lists shared stores which " +
"content is accessible on this node and the specified `target` node.",
Optional: true,
},
},
},
"datastores": schema.ListNestedAttribute{
Description: "The list of datastores.",
Optional: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"active": schema.BoolAttribute{
Description: "Whether the store is active.",
Optional: true,
},
"content_types": stringset.DataSourceAttribute("Allowed store content types.", "", false),
"enabled": schema.BoolAttribute{
Description: "Whether the store is enabled.",
Optional: true,
},
"id": schema.StringAttribute{
Description: "The ID of the store.",
Required: true,
},
"node_name": schema.StringAttribute{
Description: "The name of the node the store is on.",
Required: true,
},
"shared": schema.BoolAttribute{
Description: "Shared flag from store configuration.",
Optional: true,
},
"space_available": schema.Int64Attribute{
Description: "Available store space in bytes.",
Optional: true,
},
"space_total": schema.Int64Attribute{
Description: "Total store space in bytes.",
Optional: true,
},
"space_used": schema.Int64Attribute{
Description: "Used store space in bytes.",
Optional: true,
},
"space_used_fraction": schema.Float64Attribute{
Description: "Used fraction (used/total).",
Optional: true,
},
"type": schema.StringAttribute{
Description: "Store type.",
Required: true,
},
},
},
},
},
}
}

View File

@ -0,0 +1,57 @@
//go:build acceptance || all
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package datastores_test
import (
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/bpg/terraform-provider-proxmox/fwprovider/test"
)
func TestAccDatasourceDatastores(t *testing.T) {
t.Parallel()
te := test.InitEnvironment(t)
tests := []struct {
name string
steps []resource.TestStep
}{
{"read datastores attributes", []resource.TestStep{{
Config: te.RenderConfig(`data "proxmox_virtual_environment_datastores" "test" {
node_name = "{{.NodeName}}"
filters = {
content_types = ["iso"]
}
}`),
Check: resource.ComposeTestCheckFunc(
test.ResourceAttributesSet("data.proxmox_virtual_environment_datastores.test", []string{
"node_name",
}),
test.ResourceAttributes("data.proxmox_virtual_environment_datastores.test", map[string]string{
"datastores.#": "1",
"datastores.0.active": "true",
"datastores.0.id": "local",
}),
),
}}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
ProtoV6ProviderFactories: te.AccProviders,
Steps: tt.steps,
})
})
}
}

View File

@ -0,0 +1,37 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package datastores
import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/bpg/terraform-provider-proxmox/fwprovider/types/stringset"
)
type Model struct {
NodeName types.String `tfsdk:"node_name"`
Filters *struct {
ContentTypes stringset.Value `tfsdk:"content_types"`
ID types.String `tfsdk:"id"`
Target types.String `tfsdk:"target"`
} `tfsdk:"filters"`
Datastores []Datastore `tfsdk:"datastores"`
}
type Datastore struct {
Active types.Bool `tfsdk:"active"`
ContentTypes stringset.Value `tfsdk:"content_types"`
Enabled types.Bool `tfsdk:"enabled"`
ID types.String `tfsdk:"id"`
NodeName types.String `tfsdk:"node_name"`
Shared types.Bool `tfsdk:"shared"`
SpaceAvailable types.Int64 `tfsdk:"space_available"`
SpaceTotal types.Int64 `tfsdk:"space_total"`
SpaceUsed types.Int64 `tfsdk:"space_used"`
SpaceUsedFraction types.Float64 `tfsdk:"space_used_fraction"`
Type types.String `tfsdk:"type"`
}

View File

@ -179,7 +179,7 @@ func (d *dataSource) Schema(
Computed: true,
Description: "The identifiers of the hardware mappings.",
},
schemaAttrNameTerraformID: attribute.ID(
schemaAttrNameTerraformID: attribute.ResourceID(
"The unique identifier of this hardware mappings data source.",
),
schemaAttrNameType: schema.StringAttribute{

View File

@ -155,7 +155,7 @@ func (d *pciDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, re
Description: "The name of this PCI hardware mapping.",
Required: true,
},
schemaAttrNameTerraformID: attribute.ID(
schemaAttrNameTerraformID: attribute.ResourceID(
"The unique identifier of this PCI hardware mapping data source.",
),
},

View File

@ -137,7 +137,7 @@ func (d *usbDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, re
Description: "The name of this USB hardware mapping.",
Required: true,
},
schemaAttrNameTerraformID: attribute.ID(
schemaAttrNameTerraformID: attribute.ResourceID(
"The unique identifier of this USB hardware mapping data source.",
),
},

View File

@ -266,7 +266,7 @@ func (r *pciResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *
Description: "The name of this PCI hardware mapping.",
Required: true,
},
schemaAttrNameTerraformID: attribute.ID(
schemaAttrNameTerraformID: attribute.ResourceID(
"The unique identifier of this PCI hardware mapping resource.",
),
},

View File

@ -250,7 +250,7 @@ func (r *usbResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *
Description: "The name of this hardware mapping.",
Required: true,
},
schemaAttrNameTerraformID: attribute.ID(
schemaAttrNameTerraformID: attribute.ResourceID(
"The unique identifier of this USB hardware mapping resource.",
),
},

View File

@ -174,7 +174,7 @@ func (r *linuxBridgeResource) Schema(
Description: "Manages a Linux Bridge network interface in a Proxmox VE node.",
Attributes: map[string]schema.Attribute{
// Base attributes
"id": attribute.ID("A unique identifier with format `<node name>:<iface>`"),
"id": attribute.ResourceID("A unique identifier with format `<node name>:<iface>`"),
"node_name": schema.StringAttribute{
Description: "The name of the node.",
Required: true,

View File

@ -147,7 +147,7 @@ func (r *linuxVLANResource) Schema(
Description: "Manages a Linux VLAN network interface in a Proxmox VE node.",
Attributes: map[string]schema.Attribute{
// Base attributes
"id": attribute.ID("A unique identifier with format `<node name>:<iface>`."),
"id": attribute.ResourceID("A unique identifier with format `<node name>:<iface>`."),
"node_name": schema.StringAttribute{
Description: "The name of the node.",
Required: true,

View File

@ -190,7 +190,7 @@ func (r *downloadFileResource) Schema(
"It can be fully compatible and faster replacement for image files created using " +
"`proxmox_virtual_environment_file`. Supports images for VMs (ISO images) and LXC (CT Templates).",
Attributes: map[string]schema.Attribute{
"id": attribute.ID(),
"id": attribute.ResourceID(),
"content_type": schema.StringAttribute{
Description: "The file content type. Must be `iso` for VM images or `vztmpl` for LXC images.",
Required: true,

View File

@ -82,7 +82,7 @@ func read(ctx context.Context, client proxmox.Client, model *Model, diags *diag.
// Optional fields can be removed from the model, use StringPointerValue to handle removal on nil
model.Description = types.StringPointerValue(config.Description)
model.Name = types.StringPointerValue(config.Name)
model.Tags = stringset.NewValue(config.Tags, diags)
model.Tags = stringset.NewValueString(config.Tags, diags)
model.Template = types.BoolPointerValue(config.Template.PointerBool())
// Blocks

View File

@ -32,6 +32,7 @@ import (
"github.com/bpg/terraform-provider-proxmox/fwprovider/config"
"github.com/bpg/terraform-provider-proxmox/fwprovider/nodes"
"github.com/bpg/terraform-provider-proxmox/fwprovider/nodes/apt"
"github.com/bpg/terraform-provider-proxmox/fwprovider/nodes/datastores"
"github.com/bpg/terraform-provider-proxmox/fwprovider/nodes/hardwaremapping"
"github.com/bpg/terraform-provider-proxmox/fwprovider/nodes/network"
"github.com/bpg/terraform-provider-proxmox/fwprovider/nodes/vm"
@ -525,6 +526,7 @@ func (p *proxmoxProvider) DataSources(_ context.Context) []func() datasource.Dat
acme.NewACMEPluginsDataSource,
apt.NewRepositoryDataSource,
apt.NewStandardRepositoryDataSource,
datastores.NewDataSource,
ha.NewHAGroupDataSource,
ha.NewHAGroupsDataSource,
ha.NewHAResourceDataSource,

View File

@ -1,3 +1,9 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package stringset
import (
@ -24,7 +30,7 @@ func ResourceAttribute(desc, markdownDesc string) schema.SetAttribute {
Computed: true,
ElementType: types.StringType,
Validators: []validator.Set{
// NOTE: we allow empty list to remove all previously set tags
// NOTE: we allow empty list to remove all previously set values
setvalidator.ValueStringsAre(
stringvalidator.RegexMatches(
regexp.MustCompile(`(.|\s)*\S(.|\s)*`),
@ -35,3 +41,25 @@ func ResourceAttribute(desc, markdownDesc string) schema.SetAttribute {
},
}
}
// DataSourceAttribute returns a data source schema attribute for string set.
func DataSourceAttribute(desc, markdownDesc string, optional bool) schema.SetAttribute {
attribute := schema.SetAttribute{
CustomType: Type{
SetType: types.SetType{
ElemType: types.StringType,
},
},
Description: desc,
MarkdownDescription: markdownDesc,
ElementType: types.StringType,
}
if optional {
attribute.Optional = true
} else {
attribute.Required = true
}
return attribute
}

View File

@ -0,0 +1,38 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package stringset
type options struct {
separator string
}
type Option struct {
apply func(*options)
}
func defaultOptions(opts ...Option) options {
opt := options{
separator: ";",
}
for _, o := range opts {
o.apply(&opt)
}
return opt
}
// WithSeparator sets the separator for the string set value.
// For future use, to set the separator to a different value than the default ";".
// Would allow us to replace types.CustomCommaSeparatedList custom type that can only handle commas.
func WithSeparator(separator string) Option {
return Option{
apply: func(o *options) {
o.separator = separator
},
}
}

View File

@ -1,3 +1,9 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package stringset
import (
@ -40,8 +46,8 @@ func (v Value) Equal(o attr.Value) bool {
return v.SetValue.Equal(other.SetValue)
}
// ValueStringPointer returns a pointer to the string representation of string set value.
func (v Value) ValueStringPointer(ctx context.Context, diags *diag.Diagnostics) *string {
// ValueList returns a string slice of set value.
func (v Value) ValueList(ctx context.Context, diags *diag.Diagnostics) []string {
if v.IsNull() || v.IsUnknown() || len(v.Elements()) == 0 {
return nil
}
@ -67,16 +73,27 @@ func (v Value) ValueStringPointer(ctx context.Context, diags *diag.Diagnostics)
}
}
return ptr.Ptr(strings.Join(sanitizedItems, ";"))
return sanitizedItems
}
// NewValue converts a string of items to a new string set value.
func NewValue(str *string, diags *diag.Diagnostics) Value {
if str == nil {
// ValueStringPointer returns a pointer to the string representation of string set value.
func (v Value) ValueStringPointer(ctx context.Context, diags *diag.Diagnostics, opts ...Option) *string {
elems := v.ValueList(ctx, diags)
if len(elems) == 0 {
return nil
}
o := defaultOptions(opts...)
return ptr.Ptr(strings.Join(elems, o.separator))
}
// NewValueList converts a slice of items to a new string set value.
func NewValueList(items []string, diags *diag.Diagnostics) Value {
if len(items) == 0 {
return Value{types.SetValueMust(types.StringType, []attr.Value{})}
}
items := strings.Split(*str, ";")
elems := make([]attr.Value, len(items))
for i, item := range items {
@ -88,3 +105,16 @@ func NewValue(str *string, diags *diag.Diagnostics) Value {
return Value{setValue}
}
// NewValueString converts a string of items to a new string set value.
func NewValueString(str *string, diags *diag.Diagnostics, opts ...Option) Value {
if str == nil || *str == "" {
return Value{types.SetValueMust(types.StringType, []attr.Value{})}
}
o := defaultOptions(opts...)
items := strings.Split(*str, o.separator)
return NewValueList(items, diags)
}

View File

@ -39,6 +39,7 @@ import (
//go:generate cp ./build/docs-gen/data-sources/virtual_environment_acme_plugins.md ./docs/data-sources/
//go:generate cp ./build/docs-gen/data-sources/virtual_environment_apt_repository.md ./docs/data-sources/
//go:generate cp ./build/docs-gen/data-sources/virtual_environment_apt_standard_repository.md ./docs/data-sources/
//go:generate cp ./build/docs-gen/data-sources/virtual_environment_datastores.md ./docs/data-sources/
//go:generate cp ./build/docs-gen/data-sources/virtual_environment_hagroup.md ./docs/data-sources/
//go:generate cp ./build/docs-gen/data-sources/virtual_environment_hagroups.md ./docs/data-sources/
//go:generate cp ./build/docs-gen/data-sources/virtual_environment_hardware_mapping_pci.md ./docs/data-sources/

View File

@ -31,9 +31,9 @@ type DatastoreListResponseData struct {
Enabled *types.CustomBool `json:"enabled,omitempty"`
ID string `json:"storage,omitempty"`
Shared *types.CustomBool `json:"shared,omitempty"`
SpaceAvailable *int `json:"avail,omitempty"`
SpaceTotal *int `json:"total,omitempty"`
SpaceUsed *int `json:"used,omitempty"`
SpaceUsedPercentage *float64 `json:"used_fraction,omitempty"`
SpaceAvailable *types.CustomInt64 `json:"avail,omitempty"`
SpaceTotal *types.CustomInt64 `json:"total,omitempty"`
SpaceUsed *types.CustomInt64 `json:"used,omitempty"`
SpaceUsedPercentage *types.CustomFloat64 `json:"used_fraction,omitempty"`
Type string `json:"type,omitempty"`
}

View File

@ -25,6 +25,9 @@ type CustomBool bool
// CustomCommaSeparatedList allows a JSON string to also be a string array.
type CustomCommaSeparatedList []string
// CustomFloat64 allows a JSON float64 value to also be a string.
type CustomFloat64 float64
// CustomInt allows a JSON integer value to also be a string.
type CustomInt int
@ -111,6 +114,29 @@ func (r *CustomCommaSeparatedList) UnmarshalJSON(b []byte) error {
return nil
}
// UnmarshalJSON converts a JSON value to a float64 value.
func (r *CustomFloat64) UnmarshalJSON(b []byte) error {
s := string(b)
if strings.HasPrefix(s, "\"") && strings.HasSuffix(s, "\"") {
s = s[1 : len(s)-1]
}
f, err := strconv.ParseFloat(s, 64)
if err != nil {
return fmt.Errorf("cannot parse float64 %q: %w", s, err)
}
*r = CustomFloat64(f)
return nil
}
// PointerFloat64 returns a pointer to a float64.
func (r *CustomFloat64) PointerFloat64() *float64 {
return (*float64)(r)
}
// UnmarshalJSON converts a JSON value to an integer.
func (r *CustomInt) UnmarshalJSON(b []byte) error {
s := string(b)

View File

@ -1,201 +0,0 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package datasource
import (
"context"
"fmt"
"sort"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf"
)
const (
mkDataSourceVirtualEnvironmentDatastoresActive = "active"
mkDataSourceVirtualEnvironmentDatastoresContentTypes = "content_types"
mkDataSourceVirtualEnvironmentDatastoresDatastoreIDs = "datastore_ids"
mkDataSourceVirtualEnvironmentDatastoresEnabled = "enabled"
mkDataSourceVirtualEnvironmentDatastoresNodeName = "node_name"
mkDataSourceVirtualEnvironmentDatastoresShared = "shared"
mkDataSourceVirtualEnvironmentDatastoresSpaceAvailable = "space_available"
mkDataSourceVirtualEnvironmentDatastoresSpaceTotal = "space_total"
mkDataSourceVirtualEnvironmentDatastoresSpaceUsed = "space_used"
mkDataSourceVirtualEnvironmentDatastoresTypes = "types"
)
// Datastores returns a resource for the Proxmox data store.
func Datastores() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
mkDataSourceVirtualEnvironmentDatastoresActive: {
Type: schema.TypeList,
Description: "Whether a datastore is active",
Computed: true,
Elem: &schema.Schema{Type: schema.TypeBool},
},
mkDataSourceVirtualEnvironmentDatastoresContentTypes: {
Type: schema.TypeList,
Description: "The allowed content types",
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
mkDataSourceVirtualEnvironmentDatastoresDatastoreIDs: {
Type: schema.TypeList,
Description: "The datastore id",
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
mkDataSourceVirtualEnvironmentDatastoresEnabled: {
Type: schema.TypeList,
Description: "Whether a datastore is enabled",
Computed: true,
Elem: &schema.Schema{Type: schema.TypeBool},
},
mkDataSourceVirtualEnvironmentDatastoresNodeName: {
Type: schema.TypeString,
Description: "The node name",
Required: true,
},
mkDataSourceVirtualEnvironmentDatastoresShared: {
Type: schema.TypeList,
Description: "Whether a datastore is shared",
Computed: true,
Elem: &schema.Schema{Type: schema.TypeBool},
},
mkDataSourceVirtualEnvironmentDatastoresSpaceAvailable: {
Type: schema.TypeList,
Description: "The available space in bytes",
Computed: true,
Elem: &schema.Schema{Type: schema.TypeInt},
},
mkDataSourceVirtualEnvironmentDatastoresSpaceTotal: {
Type: schema.TypeList,
Description: "The total space in bytes",
Computed: true,
Elem: &schema.Schema{Type: schema.TypeInt},
},
mkDataSourceVirtualEnvironmentDatastoresSpaceUsed: {
Type: schema.TypeList,
Description: "The used space in bytes",
Computed: true,
Elem: &schema.Schema{Type: schema.TypeInt},
},
mkDataSourceVirtualEnvironmentDatastoresTypes: {
Type: schema.TypeList,
Description: "The storage type",
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
ReadContext: datastoresRead,
}
}
func datastoresRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
var diags diag.Diagnostics
config := m.(proxmoxtf.ProviderConfiguration)
api, err := config.GetClient()
if err != nil {
return diag.FromErr(err)
}
nodeName := d.Get(mkDataSourceVirtualEnvironmentDatastoresNodeName).(string)
list, err := api.Node(nodeName).Storage("").ListDatastores(ctx, nil)
if err != nil {
return diag.FromErr(err)
}
active := make([]interface{}, len(list))
contentTypes := make([]interface{}, len(list))
datastoreIDs := make([]interface{}, len(list))
enabled := make([]interface{}, len(list))
shared := make([]interface{}, len(list))
spaceAvailable := make([]interface{}, len(list))
spaceTotal := make([]interface{}, len(list))
spaceUsed := make([]interface{}, len(list))
types := make([]interface{}, len(list))
for i, v := range list {
if v.Active != nil {
active[i] = bool(*v.Active)
} else {
active[i] = true
}
if v.ContentTypes != nil {
contentTypeList := []string(*v.ContentTypes)
sort.Strings(contentTypeList)
contentTypes[i] = contentTypeList
} else {
contentTypes[i] = []string{}
}
datastoreIDs[i] = v.ID
if v.Enabled != nil {
enabled[i] = bool(*v.Enabled)
} else {
enabled[i] = true
}
if v.Shared != nil {
shared[i] = bool(*v.Shared)
} else {
shared[i] = true
}
if v.SpaceAvailable != nil {
spaceAvailable[i] = *v.SpaceAvailable
} else {
spaceAvailable[i] = 0
}
if v.SpaceTotal != nil {
spaceTotal[i] = *v.SpaceTotal
} else {
spaceTotal[i] = 0
}
if v.SpaceUsed != nil {
spaceUsed[i] = *v.SpaceUsed
} else {
spaceUsed[i] = 0
}
types[i] = v.Type
}
d.SetId(fmt.Sprintf("%s_datastores", nodeName))
err = d.Set(mkDataSourceVirtualEnvironmentDatastoresActive, active)
diags = append(diags, diag.FromErr(err)...)
err = d.Set(mkDataSourceVirtualEnvironmentDatastoresContentTypes, contentTypes)
diags = append(diags, diag.FromErr(err)...)
err = d.Set(mkDataSourceVirtualEnvironmentDatastoresDatastoreIDs, datastoreIDs)
diags = append(diags, diag.FromErr(err)...)
err = d.Set(mkDataSourceVirtualEnvironmentDatastoresEnabled, enabled)
diags = append(diags, diag.FromErr(err)...)
err = d.Set(mkDataSourceVirtualEnvironmentDatastoresShared, shared)
diags = append(diags, diag.FromErr(err)...)
err = d.Set(mkDataSourceVirtualEnvironmentDatastoresSpaceAvailable, spaceAvailable)
diags = append(diags, diag.FromErr(err)...)
err = d.Set(mkDataSourceVirtualEnvironmentDatastoresSpaceTotal, spaceTotal)
diags = append(diags, diag.FromErr(err)...)
err = d.Set(mkDataSourceVirtualEnvironmentDatastoresSpaceUsed, spaceUsed)
diags = append(diags, diag.FromErr(err)...)
err = d.Set(mkDataSourceVirtualEnvironmentDatastoresTypes, types)
diags = append(diags, diag.FromErr(err)...)
return diags
}

View File

@ -1,61 +0,0 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package datasource
import (
"testing"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/test"
)
// TestDatastoresInstantiation tests whether the Datastores instance can be instantiated.
func TestDatastoresInstantiation(t *testing.T) {
t.Parallel()
s := Datastores()
if s == nil {
t.Fatalf("Cannot instantiate Datastores")
}
}
// TestDatastoresSchema tests the Datastores schema.
func TestDatastoresSchema(t *testing.T) {
t.Parallel()
s := Datastores().Schema
test.AssertRequiredArguments(t, s, []string{
mkDataSourceVirtualEnvironmentDatastoresNodeName,
})
test.AssertComputedAttributes(t, s, []string{
mkDataSourceVirtualEnvironmentDatastoresActive,
mkDataSourceVirtualEnvironmentDatastoresContentTypes,
mkDataSourceVirtualEnvironmentDatastoresDatastoreIDs,
mkDataSourceVirtualEnvironmentDatastoresEnabled,
mkDataSourceVirtualEnvironmentDatastoresShared,
mkDataSourceVirtualEnvironmentDatastoresSpaceAvailable,
mkDataSourceVirtualEnvironmentDatastoresSpaceTotal,
mkDataSourceVirtualEnvironmentDatastoresSpaceUsed,
mkDataSourceVirtualEnvironmentDatastoresTypes,
})
test.AssertValueTypes(t, s, map[string]schema.ValueType{
mkDataSourceVirtualEnvironmentDatastoresActive: schema.TypeList,
mkDataSourceVirtualEnvironmentDatastoresContentTypes: schema.TypeList,
mkDataSourceVirtualEnvironmentDatastoresDatastoreIDs: schema.TypeList,
mkDataSourceVirtualEnvironmentDatastoresEnabled: schema.TypeList,
mkDataSourceVirtualEnvironmentDatastoresNodeName: schema.TypeString,
mkDataSourceVirtualEnvironmentDatastoresShared: schema.TypeList,
mkDataSourceVirtualEnvironmentDatastoresSpaceAvailable: schema.TypeList,
mkDataSourceVirtualEnvironmentDatastoresSpaceTotal: schema.TypeList,
mkDataSourceVirtualEnvironmentDatastoresSpaceUsed: schema.TypeList,
mkDataSourceVirtualEnvironmentDatastoresTypes: schema.TypeList,
})
}

View File

@ -14,22 +14,21 @@ import (
func createDatasourceMap() map[string]*schema.Resource {
return map[string]*schema.Resource{
"proxmox_virtual_environment_datastores": datasource.Datastores(),
"proxmox_virtual_environment_dns": datasource.DNS(),
"proxmox_virtual_environment_group": datasource.Group(),
"proxmox_virtual_environment_groups": datasource.Groups(),
"proxmox_virtual_environment_hosts": datasource.Hosts(),
"proxmox_virtual_environment_node": datasource.Node(),
"proxmox_virtual_environment_nodes": datasource.Nodes(),
"proxmox_virtual_environment_pool": datasource.Pool(),
"proxmox_virtual_environment_pools": datasource.Pools(),
"proxmox_virtual_environment_role": datasource.Role(),
"proxmox_virtual_environment_roles": datasource.Roles(),
"proxmox_virtual_environment_time": datasource.Time(),
"proxmox_virtual_environment_user": datasource.User(),
"proxmox_virtual_environment_users": datasource.Users(),
"proxmox_virtual_environment_vm": datasource.VM(),
"proxmox_virtual_environment_vms": datasource.VMs(),
"proxmox_virtual_environment_container": datasource.Container(),
"proxmox_virtual_environment_dns": datasource.DNS(),
"proxmox_virtual_environment_group": datasource.Group(),
"proxmox_virtual_environment_groups": datasource.Groups(),
"proxmox_virtual_environment_hosts": datasource.Hosts(),
"proxmox_virtual_environment_node": datasource.Node(),
"proxmox_virtual_environment_nodes": datasource.Nodes(),
"proxmox_virtual_environment_pool": datasource.Pool(),
"proxmox_virtual_environment_pools": datasource.Pools(),
"proxmox_virtual_environment_role": datasource.Role(),
"proxmox_virtual_environment_roles": datasource.Roles(),
"proxmox_virtual_environment_time": datasource.Time(),
"proxmox_virtual_environment_user": datasource.User(),
"proxmox_virtual_environment_users": datasource.Users(),
"proxmox_virtual_environment_vm": datasource.VM(),
"proxmox_virtual_environment_vms": datasource.VMs(),
"proxmox_virtual_environment_container": datasource.Container(),
}
}