mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-06-30 02:31:10 +00:00
feat(acme): implement resources and data sources for ACME plugins (#1479)
* feat(acme): implement CRUD API for proxmox cluster ACME plugins Signed-off-by: Björn Brauer <zaubernerd@zaubernerd.de> * feat(acme): implement acme_plugins data source Signed-off-by: Björn Brauer <zaubernerd@zaubernerd.de> * feat(acme): implement acme_plugin data source Signed-off-by: Björn Brauer <zaubernerd@zaubernerd.de> * feat(acme): implement plugin resource creation Signed-off-by: Björn Brauer <zaubernerd@zaubernerd.de> * feat(acme): implement plugin resource read Signed-off-by: Björn Brauer <zaubernerd@zaubernerd.de> * feat(acme): implement plugin resource update Signed-off-by: Björn Brauer <zaubernerd@zaubernerd.de> * feat(acme): implement plugin resource deletion Signed-off-by: Björn Brauer <zaubernerd@zaubernerd.de> * feat(acme): implement plugin resource import Signed-off-by: Björn Brauer <zaubernerd@zaubernerd.de> * docs(acme): generate documentation Signed-off-by: Björn Brauer <zaubernerd@zaubernerd.de> * fix: apply suggestions from code review Co-authored-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com> Signed-off-by: Björn Brauer <zaubernerd@zaubernerd.de> * refactor: extract common fields into BasePluginData Signed-off-by: Björn Brauer <zaubernerd@zaubernerd.de> * fix: restrict plugin resource to type=dns only because type=standalone is not configurable and always enabled by default. Signed-off-by: Björn Brauer <zaubernerd@zaubernerd.de> * fix: remove unused 'nodes' property https://github.com/bpg/terraform-provider-proxmox/pull/1479/files#r1710916265 Signed-off-by: Björn Brauer <zaubernerd@zaubernerd.de> * fix: remove "delete" property https://github.com/bpg/terraform-provider-proxmox/pull/1479/files#r1710908809 Signed-off-by: Björn Brauer <zaubernerd@zaubernerd.de> * feat: implement attribute deletion Signed-off-by: Björn Brauer <zaubernerd@zaubernerd.de> * fix: ignore empty lines in dns plugin data Signed-off-by: Björn Brauer <zaubernerd@zaubernerd.de> * fix: partial revert of code review suggestions Joining the values with a string literal would produce \\n instead of \n and splitting at \\n doesn't match a newline. Signed-off-by: Björn Brauer <zaubernerd@zaubernerd.de> * refactor: extract acme plugin models into separate file Signed-off-by: Björn Brauer <zaubernerd@zaubernerd.de> * fix: format disable parameter as int Signed-off-by: Björn Brauer <zaubernerd@zaubernerd.de> --------- Signed-off-by: Björn Brauer <zaubernerd@zaubernerd.de> Co-authored-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
parent
112f058e66
commit
a6eb81af08
39
docs/data-sources/virtual_environment_acme_plugin.md
Normal file
39
docs/data-sources/virtual_environment_acme_plugin.md
Normal file
@ -0,0 +1,39 @@
|
||||
---
|
||||
layout: page
|
||||
title: proxmox_virtual_environment_acme_plugin
|
||||
parent: Data Sources
|
||||
subcategory: Virtual Environment
|
||||
description: |-
|
||||
Retrieves a single ACME plugin by plugin ID name.
|
||||
---
|
||||
|
||||
# Data Source: proxmox_virtual_environment_acme_plugin
|
||||
|
||||
Retrieves a single ACME plugin by plugin ID name.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
data "proxmox_virtual_environment_acme_plugin" "example" {
|
||||
plugin = "standalone"
|
||||
}
|
||||
|
||||
output "data_proxmox_virtual_environment_acme_plugin" {
|
||||
value = data.proxmox_virtual_environment_acme_plugin.example
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `plugin` (String) ACME Plugin ID name.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `api` (String) API plugin name.
|
||||
- `data` (Map of String) DNS plugin data.
|
||||
- `digest` (String) Prevent changes if current configuration file has a different digest. This can be used to prevent concurrent modifications.
|
||||
- `type` (String) ACME challenge type (dns, standalone).
|
||||
- `validation_delay` (Number) Extra delay in seconds to wait before requesting validation. Allows to cope with a long TTL of DNS records (0 - 172800).
|
41
docs/data-sources/virtual_environment_acme_plugins.md
Normal file
41
docs/data-sources/virtual_environment_acme_plugins.md
Normal file
@ -0,0 +1,41 @@
|
||||
---
|
||||
layout: page
|
||||
title: proxmox_virtual_environment_acme_plugins
|
||||
parent: Data Sources
|
||||
subcategory: Virtual Environment
|
||||
description: |-
|
||||
Retrieves the list of ACME plugins.
|
||||
---
|
||||
|
||||
# Data Source: proxmox_virtual_environment_acme_plugins
|
||||
|
||||
Retrieves the list of ACME plugins.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
data "proxmox_virtual_environment_acme_plugins" "example" {}
|
||||
|
||||
output "data_proxmox_virtual_environment_acme_plugins" {
|
||||
value = data.proxmox_virtual_environment_acme_plugins.example.plugins
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `plugins` (Attributes List) List of ACME plugins (see [below for nested schema](#nestedatt--plugins))
|
||||
|
||||
<a id="nestedatt--plugins"></a>
|
||||
### Nested Schema for `plugins`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `api` (String) API plugin name.
|
||||
- `data` (Map of String) DNS plugin data.
|
||||
- `digest` (String) Prevent changes if current configuration file has a different digest. This can be used to prevent concurrent modifications.
|
||||
- `plugin` (String) ACME Plugin ID name.
|
||||
- `type` (String) ACME challenge type (dns, standalone).
|
||||
- `validation_delay` (Number) Extra delay in seconds to wait before requesting validation. Allows to cope with a long TTL of DNS records (0 - 172800).
|
50
docs/resources/virtual_environment_acme_dns_plugin.md
Normal file
50
docs/resources/virtual_environment_acme_dns_plugin.md
Normal file
@ -0,0 +1,50 @@
|
||||
---
|
||||
layout: page
|
||||
title: proxmox_virtual_environment_acme_dns_plugin
|
||||
parent: Resources
|
||||
subcategory: Virtual Environment
|
||||
description: |-
|
||||
Manages an ACME plugin in a Proxmox VE cluster.
|
||||
---
|
||||
|
||||
# Resource: proxmox_virtual_environment_acme_dns_plugin
|
||||
|
||||
Manages an ACME plugin in a Proxmox VE cluster.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
resource "proxmox_virtual_environment_acme_dns_plugin" "example" {
|
||||
plugin = "test"
|
||||
api = "aws"
|
||||
data = {
|
||||
AWS_ACCESS_KEY_ID = "EXAMPLE"
|
||||
AWS_SECRET_ACCESS_KEY = "EXAMPLE"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `api` (String) API plugin name.
|
||||
- `plugin` (String) ACME Plugin ID name.
|
||||
|
||||
### Optional
|
||||
|
||||
- `data` (Map of String) DNS plugin data.
|
||||
- `digest` (String) SHA1 digest of the current configuration. Prevent changes if current configuration file has a different digest. This can be used to prevent concurrent modifications.
|
||||
- `disable` (Boolean) Flag to disable the config.
|
||||
- `validation_delay` (Number) Extra delay in seconds to wait before requesting validation. Allows to cope with a long TTL of DNS records (0 - 172800).
|
||||
|
||||
## Import
|
||||
|
||||
Import is supported using the following syntax:
|
||||
|
||||
```shell
|
||||
#!/usr/bin/env sh
|
||||
# ACME accounts can be imported using their name, e.g.:
|
||||
terraform import proxmox_virtual_environment_acme_dns_plugin.example test
|
||||
```
|
@ -0,0 +1,7 @@
|
||||
data "proxmox_virtual_environment_acme_plugin" "example" {
|
||||
plugin = "standalone"
|
||||
}
|
||||
|
||||
output "data_proxmox_virtual_environment_acme_plugin" {
|
||||
value = data.proxmox_virtual_environment_acme_plugin.example
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
data "proxmox_virtual_environment_acme_plugins" "example" {}
|
||||
|
||||
output "data_proxmox_virtual_environment_acme_plugins" {
|
||||
value = data.proxmox_virtual_environment_acme_plugins.example.plugins
|
||||
}
|
3
examples/resources/proxmox_virtual_environment_acme_dns_plugin/import.sh
Executable file
3
examples/resources/proxmox_virtual_environment_acme_dns_plugin/import.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env sh
|
||||
# ACME accounts can be imported using their name, e.g.:
|
||||
terraform import proxmox_virtual_environment_acme_dns_plugin.example test
|
@ -0,0 +1,8 @@
|
||||
resource "proxmox_virtual_environment_acme_dns_plugin" "example" {
|
||||
plugin = "test"
|
||||
api = "aws"
|
||||
data = {
|
||||
AWS_ACCESS_KEY_ID = "EXAMPLE"
|
||||
AWS_SECRET_ACCESS_KEY = "EXAMPLE"
|
||||
}
|
||||
}
|
153
fwprovider/acme/datasource_acme_plugin.go
Normal file
153
fwprovider/acme/datasource_acme_plugin.go
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* 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 acme
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox"
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/cluster/acme/plugins"
|
||||
)
|
||||
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var (
|
||||
_ datasource.DataSource = &acmePluginDatasource{}
|
||||
_ datasource.DataSourceWithConfigure = &acmePluginDatasource{}
|
||||
)
|
||||
|
||||
// NewACMEPluginDataSource is a helper function to simplify the provider implementation.
|
||||
func NewACMEPluginDataSource() datasource.DataSource {
|
||||
return &acmePluginDatasource{}
|
||||
}
|
||||
|
||||
// acmePluginDatasource is the data source implementation for ACME plugin.
|
||||
type acmePluginDatasource struct {
|
||||
client *plugins.Client
|
||||
}
|
||||
|
||||
// Metadata returns the data source type name.
|
||||
func (d *acmePluginDatasource) Metadata(
|
||||
_ context.Context,
|
||||
req datasource.MetadataRequest,
|
||||
resp *datasource.MetadataResponse,
|
||||
) {
|
||||
resp.TypeName = req.ProviderTypeName + "_acme_plugin"
|
||||
}
|
||||
|
||||
// Schema returns the schema for the data source.
|
||||
func (d *acmePluginDatasource) Schema(
|
||||
_ context.Context,
|
||||
_ datasource.SchemaRequest,
|
||||
resp *datasource.SchemaResponse,
|
||||
) {
|
||||
resp.Schema = schema.Schema{
|
||||
Description: "Retrieves a single ACME plugin by plugin ID name.",
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"api": schema.StringAttribute{
|
||||
Description: "API plugin name.",
|
||||
Computed: true,
|
||||
},
|
||||
"data": schema.MapAttribute{
|
||||
Description: "DNS plugin data.",
|
||||
Computed: true,
|
||||
ElementType: types.StringType,
|
||||
},
|
||||
"digest": schema.StringAttribute{
|
||||
Description: "Prevent changes if current configuration file has a different digest. " +
|
||||
"This can be used to prevent concurrent modifications.",
|
||||
Computed: true,
|
||||
},
|
||||
"plugin": schema.StringAttribute{
|
||||
Description: "ACME Plugin ID name.",
|
||||
Required: true,
|
||||
},
|
||||
"type": schema.StringAttribute{
|
||||
Description: "ACME challenge type (dns, standalone).",
|
||||
Computed: true,
|
||||
Validators: []validator.String{
|
||||
stringvalidator.OneOf("dns", "standalone"),
|
||||
},
|
||||
},
|
||||
"validation_delay": schema.Int64Attribute{
|
||||
Description: "Extra delay in seconds to wait before requesting validation. " +
|
||||
"Allows to cope with a long TTL of DNS records (0 - 172800).",
|
||||
Computed: true,
|
||||
Validators: []validator.Int64{
|
||||
int64validator.Between(0, 172800),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Configure adds the provider-configured client to the data source.
|
||||
func (d *acmePluginDatasource) Configure(
|
||||
_ context.Context,
|
||||
req datasource.ConfigureRequest,
|
||||
resp *datasource.ConfigureResponse,
|
||||
) {
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
|
||||
client, ok := req.ProviderData.(proxmox.Client)
|
||||
if !ok {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected Resource Configure Type",
|
||||
fmt.Sprintf("Expected *proxmox.Client, got: %T",
|
||||
req.ProviderData),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
d.client = client.Cluster().ACME().Plugins()
|
||||
}
|
||||
|
||||
// Read fetches the ACME plugin from the Proxmox cluster.
|
||||
func (d *acmePluginDatasource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
var state acmePluginModel
|
||||
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &state)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
id := state.Plugin.ValueString()
|
||||
|
||||
plugin, err := d.client.Get(ctx, id)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to read ACME plugin",
|
||||
err.Error(),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
state.API = types.StringValue(plugin.API)
|
||||
|
||||
mapValue, diags := types.MapValueFrom(ctx, types.StringType, plugin.Data)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
|
||||
state.Data = mapValue
|
||||
state.Digest = types.StringValue(plugin.Digest)
|
||||
state.Plugin = types.StringValue(plugin.Plugin)
|
||||
state.Type = types.StringValue(plugin.Type)
|
||||
state.ValidationDelay = types.Int64Value(plugin.ValidationDelay)
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
}
|
158
fwprovider/acme/datasource_acme_plugins.go
Normal file
158
fwprovider/acme/datasource_acme_plugins.go
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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 acme
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox"
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/cluster/acme/plugins"
|
||||
)
|
||||
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var (
|
||||
_ datasource.DataSource = &acmePluginsDatasource{}
|
||||
_ datasource.DataSourceWithConfigure = &acmePluginsDatasource{}
|
||||
)
|
||||
|
||||
// NewACMEPluginsDataSource is a helper function to simplify the provider implementation.
|
||||
func NewACMEPluginsDataSource() datasource.DataSource {
|
||||
return &acmePluginsDatasource{}
|
||||
}
|
||||
|
||||
// acmePluginsDatasource is the data source implementation for ACME plugins.
|
||||
type acmePluginsDatasource struct {
|
||||
client *plugins.Client
|
||||
}
|
||||
|
||||
// Metadata returns the data source type name.
|
||||
func (d *acmePluginsDatasource) Metadata(
|
||||
_ context.Context,
|
||||
req datasource.MetadataRequest,
|
||||
resp *datasource.MetadataResponse,
|
||||
) {
|
||||
resp.TypeName = req.ProviderTypeName + "_acme_plugins"
|
||||
}
|
||||
|
||||
// Schema returns the schema for the data source.
|
||||
func (d *acmePluginsDatasource) Schema(
|
||||
_ context.Context,
|
||||
_ datasource.SchemaRequest,
|
||||
resp *datasource.SchemaResponse,
|
||||
) {
|
||||
resp.Schema = schema.Schema{
|
||||
Description: "Retrieves the list of ACME plugins.",
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"plugins": schema.ListNestedAttribute{
|
||||
Description: "List of ACME plugins",
|
||||
NestedObject: schema.NestedAttributeObject{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"api": schema.StringAttribute{
|
||||
Description: "API plugin name.",
|
||||
Computed: true,
|
||||
},
|
||||
"data": schema.MapAttribute{
|
||||
Description: "DNS plugin data.",
|
||||
Computed: true,
|
||||
ElementType: types.StringType,
|
||||
},
|
||||
"digest": schema.StringAttribute{
|
||||
Description: "Prevent changes if current configuration file has a different digest. " +
|
||||
"This can be used to prevent concurrent modifications.",
|
||||
Computed: true,
|
||||
},
|
||||
"plugin": schema.StringAttribute{
|
||||
Description: "ACME Plugin ID name.",
|
||||
Computed: true,
|
||||
},
|
||||
"type": schema.StringAttribute{
|
||||
Description: "ACME challenge type (dns, standalone).",
|
||||
Computed: true,
|
||||
Validators: []validator.String{
|
||||
stringvalidator.OneOf("dns", "standalone"),
|
||||
},
|
||||
},
|
||||
"validation_delay": schema.Int64Attribute{
|
||||
Description: "Extra delay in seconds to wait before requesting validation. " +
|
||||
"Allows to cope with a long TTL of DNS records (0 - 172800).",
|
||||
Computed: true,
|
||||
Validators: []validator.Int64{
|
||||
int64validator.Between(0, 172800),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Configure adds the provider-configured client to the data source.
|
||||
func (d *acmePluginsDatasource) Configure(
|
||||
_ context.Context,
|
||||
req datasource.ConfigureRequest,
|
||||
resp *datasource.ConfigureResponse,
|
||||
) {
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
|
||||
client, ok := req.ProviderData.(proxmox.Client)
|
||||
if !ok {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected Resource Configure Type",
|
||||
fmt.Sprintf("Expected *proxmox.Client, got: %T",
|
||||
req.ProviderData),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
d.client = client.Cluster().ACME().Plugins()
|
||||
}
|
||||
|
||||
// Read fetches the list of ACME plugins from the Proxmox cluster.
|
||||
func (d *acmePluginsDatasource) Read(ctx context.Context, _ datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
var state acmePluginsModel
|
||||
|
||||
list, err := d.client.List(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to read ACME plugins",
|
||||
err.Error(),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
for _, plugin := range list {
|
||||
mapValue, diags := types.MapValueFrom(ctx, types.StringType, plugin.Data)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
|
||||
state.Plugins = append(state.Plugins, acmePluginModel{
|
||||
baseACMEPluginModel: baseACMEPluginModel{
|
||||
API: types.StringValue(plugin.API),
|
||||
Data: mapValue,
|
||||
Digest: types.StringValue(plugin.Digest),
|
||||
Plugin: types.StringValue(plugin.Plugin),
|
||||
ValidationDelay: types.Int64Value(plugin.ValidationDelay),
|
||||
},
|
||||
Type: types.StringValue(plugin.Type),
|
||||
})
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
}
|
43
fwprovider/acme/plugin_model.go
Normal file
43
fwprovider/acme/plugin_model.go
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 acme
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
)
|
||||
|
||||
// acmePluginsModel maps the schema data for the ACME plugins data source.
|
||||
type acmePluginsModel struct {
|
||||
Plugins []acmePluginModel `tfsdk:"plugins"`
|
||||
}
|
||||
|
||||
type baseACMEPluginModel struct {
|
||||
// API plugin name
|
||||
API types.String `tfsdk:"api"`
|
||||
// DNS plugin data
|
||||
Data types.Map `tfsdk:"data"`
|
||||
// Prevent changes if current configuration file has a different digest.
|
||||
// This can be used to prevent concurrent modifications.
|
||||
Digest types.String `tfsdk:"digest"`
|
||||
// Plugin ID name
|
||||
Plugin types.String `tfsdk:"plugin"`
|
||||
// Extra delay in seconds to wait before requesting validation (0 - 172800)
|
||||
ValidationDelay types.Int64 `tfsdk:"validation_delay"`
|
||||
}
|
||||
|
||||
// acmePluginModel maps the schema data for an ACME plugin.
|
||||
type acmePluginModel struct {
|
||||
baseACMEPluginModel
|
||||
Type types.String `tfsdk:"type"`
|
||||
}
|
||||
|
||||
// acmePluginCreateModel maps the schema data for an ACME plugin.
|
||||
type acmePluginCreateModel struct {
|
||||
baseACMEPluginModel
|
||||
// Flag to disable the config
|
||||
Disable types.Bool `tfsdk:"disable"`
|
||||
}
|
306
fwprovider/acme/resource_acme_dns_plugin.go
Normal file
306
fwprovider/acme/resource_acme_dns_plugin.go
Normal file
@ -0,0 +1,306 @@
|
||||
/*
|
||||
* 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 acme
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox"
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/cluster/acme/plugins"
|
||||
)
|
||||
|
||||
var (
|
||||
_ resource.Resource = &acmePluginResource{}
|
||||
_ resource.ResourceWithConfigure = &acmePluginResource{}
|
||||
_ resource.ResourceWithImportState = &acmePluginResource{}
|
||||
)
|
||||
|
||||
// NewACMEPluginResource creates a new resource for managing ACME plugins.
|
||||
func NewACMEPluginResource() resource.Resource {
|
||||
return &acmePluginResource{}
|
||||
}
|
||||
|
||||
// acmePluginResource contains the resource's internal data.
|
||||
type acmePluginResource struct {
|
||||
// The ACME account API client
|
||||
client plugins.Client
|
||||
}
|
||||
|
||||
// Metadata defines the name of the resource.
|
||||
func (r *acmePluginResource) Metadata(
|
||||
_ context.Context,
|
||||
req resource.MetadataRequest,
|
||||
resp *resource.MetadataResponse,
|
||||
) {
|
||||
resp.TypeName = req.ProviderTypeName + "_acme_dns_plugin"
|
||||
}
|
||||
|
||||
// Schema defines the schema for the resource.
|
||||
func (r *acmePluginResource) Schema(
|
||||
_ context.Context,
|
||||
_ resource.SchemaRequest,
|
||||
resp *resource.SchemaResponse,
|
||||
) {
|
||||
resp.Schema = schema.Schema{
|
||||
Description: "Manages an ACME plugin in a Proxmox VE cluster.",
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"api": schema.StringAttribute{
|
||||
Description: "API plugin name.",
|
||||
Required: true,
|
||||
},
|
||||
"data": schema.MapAttribute{
|
||||
Description: "DNS plugin data.",
|
||||
Optional: true,
|
||||
ElementType: types.StringType,
|
||||
},
|
||||
"digest": schema.StringAttribute{
|
||||
Description: "SHA1 digest of the current configuration.",
|
||||
MarkdownDescription: "SHA1 digest of the current configuration. " +
|
||||
"Prevent changes if current configuration file has a different digest. " +
|
||||
"This can be used to prevent concurrent modifications.",
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
"disable": schema.BoolAttribute{
|
||||
Description: "Flag to disable the config.",
|
||||
Optional: true,
|
||||
},
|
||||
"plugin": schema.StringAttribute{
|
||||
Description: "ACME Plugin ID name.",
|
||||
Required: true,
|
||||
},
|
||||
"validation_delay": schema.Int64Attribute{
|
||||
Description: "Extra delay in seconds to wait before requesting validation.",
|
||||
MarkdownDescription: "Extra delay in seconds to wait before requesting validation. " +
|
||||
"Allows to cope with a long TTL of DNS records (0 - 172800).",
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Default: int64default.StaticInt64(30),
|
||||
Validators: []validator.Int64{
|
||||
int64validator.Between(0, 172800),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Configure accesses the provider-configured Proxmox API client on behalf of the resource.
|
||||
func (r *acmePluginResource) Configure(
|
||||
_ context.Context,
|
||||
req resource.ConfigureRequest,
|
||||
resp *resource.ConfigureResponse,
|
||||
) {
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
|
||||
client, ok := req.ProviderData.(proxmox.Client)
|
||||
if ok {
|
||||
r.client = *client.Cluster().ACME().Plugins()
|
||||
} else {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected Resource Configure Type",
|
||||
fmt.Sprintf("Expected *proxmox.Client, got: %T",
|
||||
req.ProviderData),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Create creates a new ACME plugin on the Proxmox cluster.
|
||||
func (r *acmePluginResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
var plan acmePluginCreateModel
|
||||
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
createRequest := &plugins.ACMEPluginsCreateRequestBody{}
|
||||
createRequest.Plugin = plan.Plugin.ValueString()
|
||||
createRequest.Type = "dns"
|
||||
createRequest.API = plan.API.ValueString()
|
||||
data := make(plugins.DNSPluginData)
|
||||
|
||||
plan.Data.ElementsAs(ctx, &data, false)
|
||||
|
||||
createRequest.Data = &data
|
||||
createRequest.Disable = plan.Disable.ValueBool()
|
||||
createRequest.ValidationDelay = plan.ValidationDelay.ValueInt64()
|
||||
|
||||
err := r.client.Create(ctx, createRequest)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "already exists") {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Unable to create ACME plugin '%s'", createRequest.Plugin),
|
||||
err.Error(),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("ACME plugin '%s' already exists", createRequest.Plugin),
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
|
||||
plugin, err := r.client.Get(ctx, plan.Plugin.ValueString())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to read ACME plugin",
|
||||
err.Error(),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
plan.Digest = types.StringValue(plugin.Digest)
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, plan)...)
|
||||
}
|
||||
|
||||
// Read retrieves the current state of the ACME plugin from the Proxmox cluster.
|
||||
func (r *acmePluginResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||
var state acmePluginCreateModel
|
||||
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
id := state.Plugin.ValueString()
|
||||
|
||||
plugin, err := r.client.Get(ctx, id)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to read ACME plugin",
|
||||
err.Error(),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
state.API = types.StringValue(plugin.API)
|
||||
state.Digest = types.StringValue(plugin.Digest)
|
||||
state.ValidationDelay = types.Int64Value(plugin.ValidationDelay)
|
||||
|
||||
mapValue, diags := types.MapValueFrom(ctx, types.StringType, plugin.Data)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
|
||||
state.Data = mapValue
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
}
|
||||
|
||||
// Update modifies an existing ACME plugin on the Proxmox cluster.
|
||||
func (r *acmePluginResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||
var plan, state acmePluginCreateModel
|
||||
|
||||
toDelete := make([]string, 0)
|
||||
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
updateRequest := &plugins.ACMEPluginsUpdateRequestBody{}
|
||||
updateRequest.API = plan.API.ValueString()
|
||||
|
||||
data := make(plugins.DNSPluginData)
|
||||
|
||||
plan.Data.ElementsAs(ctx, &data, false)
|
||||
|
||||
if plan.Data.IsNull() && !state.Data.IsNull() {
|
||||
toDelete = append(toDelete, "data")
|
||||
} else {
|
||||
updateRequest.Data = &data
|
||||
}
|
||||
|
||||
updateRequest.Digest = plan.Digest.ValueString()
|
||||
|
||||
if plan.Disable.IsNull() && !state.Disable.IsNull() || !plan.Disable.ValueBool() {
|
||||
toDelete = append(toDelete, "disable")
|
||||
} else {
|
||||
updateRequest.Disable = plan.Disable.ValueBool()
|
||||
}
|
||||
|
||||
if plan.ValidationDelay.IsNull() && !state.ValidationDelay.IsNull() {
|
||||
toDelete = append(toDelete, "validation_delay")
|
||||
} else {
|
||||
updateRequest.ValidationDelay = plan.ValidationDelay.ValueInt64()
|
||||
}
|
||||
|
||||
if len(toDelete) > 0 {
|
||||
updateRequest.Delete = strings.Join(toDelete, ",")
|
||||
}
|
||||
|
||||
err := r.client.Update(ctx, plan.Plugin.ValueString(), updateRequest)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Unable to update ACME account '%s'", plan.Plugin.ValueString()),
|
||||
err.Error(),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
plugin, err := r.client.Get(ctx, plan.Plugin.ValueString())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to read ACME plugin",
|
||||
err.Error(),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
plan.Digest = types.StringValue(plugin.Digest)
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, plan)...)
|
||||
}
|
||||
|
||||
// Delete removes an existing ACME plugin from the Proxmox cluster.
|
||||
func (r *acmePluginResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||
var state acmePluginCreateModel
|
||||
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := r.client.Delete(ctx, state.Plugin.ValueString())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Unable to delete ACME plugin '%s'", state.Plugin.ValueString()),
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ImportState retrieves the current state of an existing ACME plugin from the Proxmox cluster.
|
||||
func (r *acmePluginResource) ImportState(
|
||||
ctx context.Context,
|
||||
req resource.ImportStateRequest,
|
||||
resp *resource.ImportStateResponse,
|
||||
) {
|
||||
resource.ImportStatePassthroughID(ctx, path.Root("plugin"), req, resp)
|
||||
}
|
@ -446,6 +446,7 @@ func (p *proxmoxProvider) Resources(_ context.Context) []func() resource.Resourc
|
||||
NewClusterOptionsResource,
|
||||
NewDownloadFileResource,
|
||||
acme.NewACMEAccountResource,
|
||||
acme.NewACMEPluginResource,
|
||||
apt.NewRepositoryResource,
|
||||
apt.NewStandardRepositoryResource,
|
||||
access.NewACLResource,
|
||||
@ -465,6 +466,8 @@ func (p *proxmoxProvider) DataSources(_ context.Context) []func() datasource.Dat
|
||||
NewVersionDataSource,
|
||||
acme.NewACMEAccountsDataSource,
|
||||
acme.NewACMEAccountDataSource,
|
||||
acme.NewACMEPluginsDataSource,
|
||||
acme.NewACMEPluginDataSource,
|
||||
apt.NewRepositoryDataSource,
|
||||
apt.NewStandardRepositoryDataSource,
|
||||
ha.NewHAGroupDataSource,
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/api"
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/cluster/acme/account"
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/cluster/acme/plugins"
|
||||
)
|
||||
|
||||
// Client is an interface for accessing the Proxmox ACME API.
|
||||
@ -18,7 +19,7 @@ type Client struct {
|
||||
api.Client
|
||||
}
|
||||
|
||||
// ExpandPath expands a relative path to a full cluster API path.
|
||||
// ExpandPath expands a relative path to a full cluster ACME API path.
|
||||
func (c *Client) ExpandPath(path string) string {
|
||||
return fmt.Sprintf("cluster/acme/%s", path)
|
||||
}
|
||||
@ -27,3 +28,8 @@ func (c *Client) ExpandPath(path string) string {
|
||||
func (c *Client) Account() *account.Client {
|
||||
return &account.Client{Client: c.Client}
|
||||
}
|
||||
|
||||
// Plugins returns a client for managing the cluster's ACME plugins.
|
||||
func (c *Client) Plugins() *plugins.Client {
|
||||
return &plugins.Client{Client: c.Client}
|
||||
}
|
||||
|
83
proxmox/cluster/acme/plugins/acme_plugins.go
Normal file
83
proxmox/cluster/acme/plugins/acme_plugins.go
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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 plugins
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/api"
|
||||
)
|
||||
|
||||
// List returns a list of ACME plugins.
|
||||
func (c *Client) List(ctx context.Context) ([]*ACMEPluginsListResponseData, error) {
|
||||
resBody := &ACMEPluginsListResponseBody{}
|
||||
|
||||
err := c.DoRequest(ctx, http.MethodGet, c.ExpandPath(""), nil, resBody)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing ACME plugins: %w", err)
|
||||
}
|
||||
|
||||
if resBody.Data == nil {
|
||||
return nil, api.ErrNoDataObjectInResponse
|
||||
}
|
||||
|
||||
sort.Slice(resBody.Data, func(i, j int) bool {
|
||||
return resBody.Data[i].Plugin < resBody.Data[j].Plugin
|
||||
})
|
||||
|
||||
return resBody.Data, nil
|
||||
}
|
||||
|
||||
// Get retrieves a single ACME plugin based on its identifier.
|
||||
func (c *Client) Get(ctx context.Context, id string) (*ACMEPluginsGetResponseData, error) {
|
||||
resBody := &ACMEPluginsGetResponseBody{}
|
||||
|
||||
err := c.DoRequest(ctx, http.MethodGet, c.ExpandPath(url.PathEscape(id)), nil, resBody)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading ACME plugin: %w", err)
|
||||
}
|
||||
|
||||
if resBody.Data == nil {
|
||||
return nil, api.ErrNoDataObjectInResponse
|
||||
}
|
||||
|
||||
return resBody.Data, nil
|
||||
}
|
||||
|
||||
// Create creates a new ACME plugin.
|
||||
func (c *Client) Create(ctx context.Context, data *ACMEPluginsCreateRequestBody) error {
|
||||
err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath(""), data, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating ACME plugin: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update updates an existing ACME plugin.
|
||||
func (c *Client) Update(ctx context.Context, id string, data *ACMEPluginsUpdateRequestBody) error {
|
||||
err := c.DoRequest(ctx, http.MethodPut, c.ExpandPath(url.PathEscape(id)), data, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error updating ACME plugin: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete removes an ACME plugin.
|
||||
func (c *Client) Delete(ctx context.Context, id string) error {
|
||||
err := c.DoRequest(ctx, http.MethodDelete, c.ExpandPath(url.PathEscape(id)), nil, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error deleting ACME plugin: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
117
proxmox/cluster/acme/plugins/acme_plugins_types.go
Normal file
117
proxmox/cluster/acme/plugins/acme_plugins_types.go
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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 plugins
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BaseACMEPluginData contains common fields for ACME plugin data.
|
||||
type BaseACMEPluginData struct {
|
||||
// ACME challenge type (dns, standalone).
|
||||
Type string `json:"type,omitempty" url:"type,omitempty"`
|
||||
// Prevent changes if current configuration file has a different digest. This can be used to prevent concurrent modifications.
|
||||
Digest string `json:"digest,omitempty" url:"digest,omitempty"`
|
||||
// API plugin name
|
||||
API string `json:"api,omitempty" url:"api,omitempty"`
|
||||
// Extra delay in seconds to wait before requesting validation. Allows to cope with a long TTL of DNS records (0 - 172800).
|
||||
ValidationDelay int64 `json:"validation-delay,omitempty" url:"validation-delay,omitempty"`
|
||||
}
|
||||
|
||||
// ACMEPluginsListResponseBody contains the body from an ACME plugins list response.
|
||||
type ACMEPluginsListResponseBody struct {
|
||||
// Unique identifier for ACME plugin instance.
|
||||
Data []*ACMEPluginsListResponseData `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// ACMEPluginsListResponseData contains the data from an ACME plugins list response.
|
||||
type ACMEPluginsListResponseData struct {
|
||||
BaseACMEPluginData
|
||||
// ACME Plugin ID name
|
||||
Plugin string `json:"plugin" url:"plugin"`
|
||||
// DNS plugin data.
|
||||
Data *DNSPluginData `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// ACMEPluginsGetResponseBody contains the body from an ACME plugins get response.
|
||||
type ACMEPluginsGetResponseBody struct {
|
||||
Data *ACMEPluginsGetResponseData `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// ACMEPluginsGetResponseData contains the data from an ACME plugins get response.
|
||||
type ACMEPluginsGetResponseData struct {
|
||||
BaseACMEPluginData
|
||||
// ACME Plugin ID name
|
||||
Plugin string `json:"plugin" url:"plugin"`
|
||||
// DNS plugin data.
|
||||
Data *DNSPluginData `json:"data"`
|
||||
}
|
||||
|
||||
// ACMEPluginsCreateRequestBody contains the body for creating a new ACME plugin.
|
||||
type ACMEPluginsCreateRequestBody struct {
|
||||
BaseACMEPluginData
|
||||
// ACME Plugin ID name
|
||||
Plugin string `json:"id" url:"id"`
|
||||
// DNS plugin data. (base64 encoded)
|
||||
Data *DNSPluginData `url:"data,omitempty"`
|
||||
// Flag to disable the config.
|
||||
Disable bool `url:"disable,omitempty,int"`
|
||||
}
|
||||
|
||||
// ACMEPluginsUpdateRequestBody contains the body for updating an existing ACME plugin.
|
||||
type ACMEPluginsUpdateRequestBody struct {
|
||||
BaseACMEPluginData
|
||||
// DNS plugin data. (base64 encoded)
|
||||
Data *DNSPluginData `url:"data,omitempty"`
|
||||
// A list of settings you want to delete.
|
||||
Delete string `url:"delete,omitempty"`
|
||||
// Flag to disable the config.
|
||||
Disable bool `url:"disable,omitempty,int"`
|
||||
}
|
||||
|
||||
// DNSPluginData is a map of DNS plugin data.
|
||||
type DNSPluginData map[string]string
|
||||
|
||||
// EncodeValues encodes the DNSPluginData into the URL values.
|
||||
func (d DNSPluginData) EncodeValues(key string, v *url.Values) error {
|
||||
values := make([]string, 0, len(d))
|
||||
|
||||
for key, value := range d {
|
||||
values = append(values, fmt.Sprintf("%s=%s", key, value))
|
||||
}
|
||||
|
||||
v.Add(key, base64.StdEncoding.EncodeToString([]byte(strings.Join(values, "\n"))))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals a DNSPluginData struct from JSON.
|
||||
func (d *DNSPluginData) UnmarshalJSON(b []byte) error {
|
||||
mapData := make(map[string]string)
|
||||
|
||||
s := ""
|
||||
if err := json.Unmarshal(b, &s); err != nil {
|
||||
return fmt.Errorf("error unmarshaling json: %w", err)
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(s, "\n") {
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
before, after, _ := strings.Cut(line, "=")
|
||||
mapData[before] = after
|
||||
}
|
||||
|
||||
*d = mapData
|
||||
|
||||
return nil
|
||||
}
|
23
proxmox/cluster/acme/plugins/client.go
Normal file
23
proxmox/cluster/acme/plugins/client.go
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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 plugins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/api"
|
||||
)
|
||||
|
||||
// Client is an interface for accessing the Proxmox ACME plugins API.
|
||||
type Client struct {
|
||||
api.Client
|
||||
}
|
||||
|
||||
// ExpandPath expands a relative path to the Proxmox ACME plugins API path.
|
||||
func (c *Client) ExpandPath(path string) string {
|
||||
return fmt.Sprintf("cluster/acme/plugins/%s", path)
|
||||
}
|
@ -30,6 +30,8 @@ import (
|
||||
//go:generate cp -R ../build/docs-gen/guides/. ../docs/guides/
|
||||
//go:generate cp ../build/docs-gen/data-sources/virtual_environment_acme_account.md ../docs/data-sources/
|
||||
//go:generate cp ../build/docs-gen/data-sources/virtual_environment_acme_accounts.md ../docs/data-sources/
|
||||
//go:generate cp ../build/docs-gen/data-sources/virtual_environment_acme_plugin.md ../docs/data-sources/
|
||||
//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_hagroup.md ../docs/data-sources/
|
||||
@ -43,6 +45,7 @@ import (
|
||||
//go:generate cp ../build/docs-gen/data-sources/virtual_environment_vm2.md ../docs/data-sources/
|
||||
//go:generate cp ../build/docs-gen/resources/virtual_environment_acl.md ../docs/resources/
|
||||
//go:generate cp ../build/docs-gen/resources/virtual_environment_acme_account.md ../docs/resources/
|
||||
//go:generate cp ../build/docs-gen/resources/virtual_environment_acme_dns_plugin.md ../docs/resources/
|
||||
//go:generate cp ../build/docs-gen/resources/virtual_environment_apt_repository.md ../docs/resources/
|
||||
//go:generate cp ../build/docs-gen/resources/virtual_environment_apt_standard_repository.md ../docs/resources/
|
||||
//go:generate cp ../build/docs-gen/resources/virtual_environment_cluster_options.md ../docs/resources/
|
||||
|
Loading…
Reference in New Issue
Block a user