Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add data.load_balancer_type(s) data sources #991

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions hcloud/plugin_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (

"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/hetznercloud/terraform-provider-hcloud/internal/datacenter"
"github.com/hetznercloud/terraform-provider-hcloud/internal/loadbalancertype"
"github.com/hetznercloud/terraform-provider-hcloud/internal/location"
"github.com/hetznercloud/terraform-provider-hcloud/internal/servertype"
"github.com/hetznercloud/terraform-provider-hcloud/internal/sshkey"
Expand Down Expand Up @@ -171,6 +172,8 @@ func (p *PluginProvider) DataSources(_ context.Context) []func() datasource.Data
return []func() datasource.DataSource{
datacenter.NewDataSource,
datacenter.NewDataSourceList,
loadbalancertype.NewDataSource,
loadbalancertype.NewDataSourceList,
location.NewDataSource,
location.NewDataSourceList,
servertype.NewDataSource,
Expand Down
295 changes: 295 additions & 0 deletions internal/loadbalancertype/data_source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
package loadbalancertype

import (
"context"
_ "embed"

"github.com/hashicorp/terraform-plugin-framework-validators/datasourcevalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/types"

"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/hetznercloud/terraform-provider-hcloud/internal/util/datasourceutil"
"github.com/hetznercloud/terraform-provider-hcloud/internal/util/hcloudutil"
)

const (
// DataSourceType is the type name of the Hetzner Cloud load balancer type datasource.
DataSourceType = "hcloud_load_balancer_type"

// DataSourceListType is the type name of the Hetzner Cloud load balancer type list datasource.
DataSourceListType = "hcloud_load_balancer_types"
)

type resourceData struct {
ID types.Int64 `tfsdk:"id"`
Name types.String `tfsdk:"name"`
Description types.String `tfsdk:"description"`
MaxAssignedCertificates types.Int64 `tfsdk:"max_assigned_certificates"`
MaxConnections types.Int64 `tfsdk:"max_connections"`
MaxServices types.Int64 `tfsdk:"max_services"`
MaxTargets types.Int64 `tfsdk:"max_targets"`
}

var resourceDataAttrTypes = map[string]attr.Type{
"id": types.Int64Type,
"name": types.StringType,
"description": types.StringType,
"max_assigned_certificates": types.Int64Type,
"max_connections": types.Int64Type,
"max_services": types.Int64Type,
"max_targets": types.Int64Type,
}

func newResourceData(_ context.Context, in *hcloud.LoadBalancerType) (resourceData, diag.Diagnostics) { //nolint:unparam
var data resourceData
var diags diag.Diagnostics

data.ID = types.Int64Value(int64(in.ID))
data.Name = types.StringValue(in.Name)
data.Description = types.StringValue(in.Description)

data.MaxAssignedCertificates = types.Int64Value(int64(in.MaxAssignedCertificates))
data.MaxConnections = types.Int64Value(int64(in.MaxConnections))
data.MaxServices = types.Int64Value(int64(in.MaxServices))
data.MaxTargets = types.Int64Value(int64(in.MaxTargets))

return data, diags
}

func getCommonDataSchema() map[string]schema.Attribute {
return map[string]schema.Attribute{
"id": schema.Int64Attribute{
Optional: true,
Computed: true,
},
"name": schema.StringAttribute{
Optional: true,
Computed: true,
},
"description": schema.StringAttribute{
Computed: true,
},
"max_assigned_certificates": schema.Int64Attribute{
Optional: true,
Computed: true,
},
"max_connections": schema.Int64Attribute{
Optional: true,
Computed: true,
},
"max_services": schema.Int64Attribute{
Optional: true,
Computed: true,
},
"max_targets": schema.Int64Attribute{
Optional: true,
Computed: true,
},
}
}

// Single
var _ datasource.DataSource = (*dataSource)(nil)
var _ datasource.DataSourceWithConfigure = (*dataSource)(nil)
var _ datasource.DataSourceWithConfigValidators = (*dataSource)(nil)

type dataSource struct {
client *hcloud.Client
}

func NewDataSource() datasource.DataSource {
return &dataSource{}
}

// Metadata should return the full name of the data source.
func (d *dataSource) Metadata(_ context.Context, _ datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = DataSourceType
}

// Configure enables provider-level data or clients to be set in the
// provider-defined DataSource type. It is separately executed for each
// ReadDataSource RPC.
func (d *dataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
var newDiags diag.Diagnostics

d.client, newDiags = hcloudutil.ConfigureClient(req.ProviderData)
resp.Diagnostics.Append(newDiags...)
if resp.Diagnostics.HasError() {
return
}
}

//go:embed data_source.md
var dataSourceMarkdownDescription string

// Schema should return the schema for this data source.
func (d *dataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema.Attributes = getCommonDataSchema()
resp.Schema.MarkdownDescription = dataSourceMarkdownDescription
}

// ConfigValidators returns a list of ConfigValidators. Each ConfigValidator's Validate method will be called when validating the data source.
func (d *dataSource) ConfigValidators(_ context.Context) []datasource.ConfigValidator {
return []datasource.ConfigValidator{
datasourcevalidator.ExactlyOneOf(
path.MatchRoot("id"),
path.MatchRoot("name"),
),
}
}

// Read is called when the provider must read data source values in
// order to update state. Config values should be read from the
// ReadRequest and new state values set on the ReadResponse.
func (d *dataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data resourceData

resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}

var result *hcloud.LoadBalancerType
var err error

switch {
case !data.ID.IsNull():
result, _, err = d.client.LoadBalancerType.GetByID(ctx, int(data.ID.ValueInt64()))
if err != nil {
resp.Diagnostics.Append(hcloudutil.APIErrorDiagnostics(err)...)
return
}
if result == nil {
resp.Diagnostics.Append(hcloudutil.NotFoundDiagnostic("load balancer type", "id", data.ID.String()))
return
}
case !data.Name.IsNull():
result, _, err = d.client.LoadBalancerType.GetByName(ctx, data.Name.ValueString())
if err != nil {
resp.Diagnostics.Append(hcloudutil.APIErrorDiagnostics(err)...)
return
}
if result == nil {
resp.Diagnostics.Append(hcloudutil.NotFoundDiagnostic("load balancer type", "name", data.Name.String()))
return
}
default:
// Should not happen, see [dataSource.ConfigValidators]
resp.Diagnostics.AddError("Unexpected internal error", "")
return
}

data, diags := newResourceData(ctx, result)
resp.Diagnostics.Append(diags...)

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

// List
var _ datasource.DataSource = (*dataSourceList)(nil)
var _ datasource.DataSourceWithConfigure = (*dataSourceList)(nil)

type dataSourceList struct {
client *hcloud.Client
}

func NewDataSourceList() datasource.DataSource {
return &dataSourceList{}
}

// Metadata should return the full name of the data source.
func (d *dataSourceList) Metadata(_ context.Context, _ datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = DataSourceListType
}

// Configure enables provider-level data or clients to be set in the
// provider-defined DataSource type. It is separately executed for each
// ReadDataSource RPC.
func (d *dataSourceList) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
var newDiags diag.Diagnostics

d.client, newDiags = hcloudutil.ConfigureClient(req.ProviderData)
resp.Diagnostics.Append(newDiags...)
if resp.Diagnostics.HasError() {
return
}
}

//go:embed data_source_list.md
var dataSourceListMarkdownDescription string

// Schema should return the schema for this data source.
func (d *dataSourceList) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema.Attributes = map[string]schema.Attribute{
"id": schema.StringAttribute{
Optional: true,
},
"load_balancer_types": schema.ListNestedAttribute{
NestedObject: schema.NestedAttributeObject{
Attributes: getCommonDataSchema(),
},
Computed: true,
},
}

resp.Schema.MarkdownDescription = dataSourceListMarkdownDescription
}

type resourceDataList struct {
ID types.String `tfsdk:"id"`
LoadBalancerTypes types.List `tfsdk:"load_balancer_types"`
}

func newResourceDataList(ctx context.Context, in []*hcloud.LoadBalancerType) (resourceDataList, diag.Diagnostics) {
var data resourceDataList
var diags diag.Diagnostics
var newDiags diag.Diagnostics

ids := make([]int64, len(in))
tfItems := make([]resourceData, len(in))
for i, item := range in {
ids[i] = int64(item.ID)

tfItem, newDiags := newResourceData(ctx, item)
diags.Append(newDiags...)
tfItems[i] = tfItem
}

data.ID = types.StringValue(datasourceutil.ListID(ids))

data.LoadBalancerTypes, newDiags = types.ListValueFrom(ctx, types.ObjectType{AttrTypes: resourceDataAttrTypes}, tfItems)
diags.Append(newDiags...)

return data, diags
}

// Read is called when the provider must read data source values in
// order to update state. Config values should be read from the
// ReadRequest and new state values set on the ReadResponse.
func (d *dataSourceList) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data resourceDataList

resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}

var result []*hcloud.LoadBalancerType
var err error

result, err = d.client.LoadBalancerType.All(ctx)
if err != nil {
resp.Diagnostics.Append(hcloudutil.APIErrorDiagnostics(err)...)
return
}

data, diags := newResourceDataList(ctx, result)
resp.Diagnostics.Append(diags...)

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
15 changes: 15 additions & 0 deletions internal/loadbalancertype/data_source.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Provides details about a specific Hetzner Cloud Load Balancer Type.

Use this resource to get detailed information about specific Load Balancer Type.

## Example Usage

```hcl
data "hcloud_load_balancer_type" "by_name" {
name = "lb11"
}

data "hcloud_load_balancer_type" "by_id" {
id = 1
}
```
7 changes: 7 additions & 0 deletions internal/loadbalancertype/data_source_list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Provides a list of available Hetzner Cloud Load Balancer Types.

## Example Usage

```hcl
data "hcloud_load_balancer_types" "all" {}
```
Loading