0
0
mirror of https://github.com/bpg/terraform-provider-proxmox.git synced 2025-06-30 02:31:10 +00:00
terraform-provider-proxmox/proxmoxtf/resource/certificate.go
Pavel Boldyrev 5ecf135398
chore(code): fix proxmox package dependencies (#536)
move `types` back from `internal` to `proxmox` and adjust a few other types, to make sure `proxmox` package is not dependent on anything else, and therefore can be extracted to a separate repo (#423)
2023-09-03 00:40:47 +00:00

348 lines
11 KiB
Go

/*
* 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 resource
import (
"context"
"fmt"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/bpg/terraform-provider-proxmox/proxmox/nodes"
"github.com/bpg/terraform-provider-proxmox/proxmox/types"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf"
)
const (
dvResourceVirtualEnvironmentCertificateCertificateChain = ""
dvResourceVirtualEnvironmentCertificateOverwrite = false
mkResourceVirtualEnvironmentCertificateCertificate = "certificate"
mkResourceVirtualEnvironmentCertificateCertificateChain = "certificate_chain"
mkResourceVirtualEnvironmentCertificateFileName = "file_name"
mkResourceVirtualEnvironmentCertificateIssuer = "issuer"
mkResourceVirtualEnvironmentCertificateNodeName = "node_name"
mkResourceVirtualEnvironmentCertificateExpirationDate = "expiration_date"
mkResourceVirtualEnvironmentCertificateOverwrite = "overwrite"
mkResourceVirtualEnvironmentCertificatePrivateKey = "private_key"
mkResourceVirtualEnvironmentCertificatePublicKeySize = "public_key_size"
mkResourceVirtualEnvironmentCertificatePublicKeyType = "public_key_type"
mkResourceVirtualEnvironmentCertificateSSLFingerprint = "ssl_fingerprint"
mkResourceVirtualEnvironmentCertificateStartDate = "start_date"
mkResourceVirtualEnvironmentCertificateSubject = "subject"
mkResourceVirtualEnvironmentCertificateSubjectAlternativeNames = "subject_alternative_names"
)
// Certificate returns a resource that manages a certificate.
func Certificate() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
mkResourceVirtualEnvironmentCertificateCertificate: {
Type: schema.TypeString,
Description: "The PEM encoded certificate",
Required: true,
},
mkResourceVirtualEnvironmentCertificateCertificateChain: {
Type: schema.TypeString,
Description: "The PEM encoded certificate chain",
Optional: true,
Default: dvResourceVirtualEnvironmentCertificateCertificateChain,
},
mkResourceVirtualEnvironmentCertificateExpirationDate: {
Type: schema.TypeString,
Description: "The expiration date",
Computed: true,
},
mkResourceVirtualEnvironmentCertificateFileName: {
Type: schema.TypeString,
Description: "The file name",
Computed: true,
},
mkResourceVirtualEnvironmentCertificateIssuer: {
Type: schema.TypeString,
Description: "The issuer",
Computed: true,
},
mkResourceVirtualEnvironmentCertificateNodeName: {
Type: schema.TypeString,
Description: "The node name",
Required: true,
ForceNew: true,
},
mkResourceVirtualEnvironmentCertificateOverwrite: {
Type: schema.TypeBool,
Description: "Whether to overwrite an existing certificate",
Optional: true,
Default: dvResourceVirtualEnvironmentCertificateOverwrite,
},
mkResourceVirtualEnvironmentCertificatePrivateKey: {
Type: schema.TypeString,
Description: "The PEM encoded private key",
Required: true,
Sensitive: true,
},
mkResourceVirtualEnvironmentCertificatePublicKeySize: {
Type: schema.TypeInt,
Description: "The public key size",
Computed: true,
},
mkResourceVirtualEnvironmentCertificatePublicKeyType: {
Type: schema.TypeString,
Description: "The public key type",
Computed: true,
},
mkResourceVirtualEnvironmentCertificateSSLFingerprint: {
Type: schema.TypeString,
Description: "The SSL fingerprint",
Computed: true,
},
mkResourceVirtualEnvironmentCertificateStartDate: {
Type: schema.TypeString,
Description: "The start date",
Computed: true,
},
mkResourceVirtualEnvironmentCertificateSubject: {
Type: schema.TypeString,
Description: "The subject",
Computed: true,
},
mkResourceVirtualEnvironmentCertificateSubjectAlternativeNames: {
Type: schema.TypeList,
Description: "The subject alternative names",
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
CreateContext: certificateCreate,
ReadContext: certificateRead,
UpdateContext: certificateUpdate,
DeleteContext: certificateDelete,
}
}
func certificateCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
diags := certificateUpdate(ctx, d, m)
if diags.HasError() {
return diags
}
nodeName := d.Get(mkResourceVirtualEnvironmentCertificateNodeName).(string)
d.SetId(fmt.Sprintf("%s_certificate", nodeName))
return nil
}
func certificateGetUpdateBody(d *schema.ResourceData) *nodes.CertificateUpdateRequestBody {
certificate := d.Get(mkResourceVirtualEnvironmentCertificateCertificate).(string)
certificateChain := d.Get(mkResourceVirtualEnvironmentCertificateCertificateChain).(string)
overwrite := types.CustomBool(d.Get(mkResourceVirtualEnvironmentCertificateOverwrite).(bool))
privateKey := d.Get(mkResourceVirtualEnvironmentCertificatePrivateKey).(string)
combinedCertificates := strings.TrimSpace(certificate) + "\n"
if certificateChain != "" {
combinedCertificates += strings.TrimSpace(certificateChain) + "\n"
}
force := overwrite
if d.Id() != "" {
force = true
}
restart := types.CustomBool(true)
body := &nodes.CertificateUpdateRequestBody{
Certificates: combinedCertificates,
Force: &force,
PrivateKey: &privateKey,
Restart: &restart,
}
return body
}
func certificateRead(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(mkResourceVirtualEnvironmentCertificateNodeName).(string)
list, err := api.Node(nodeName).ListCertificates(ctx)
if err != nil {
return diag.FromErr(err)
}
err = d.Set(mkResourceVirtualEnvironmentCertificateCertificate, "")
diags = append(diags, diag.FromErr(err)...)
err = d.Set(mkResourceVirtualEnvironmentCertificateCertificateChain, "")
diags = append(diags, diag.FromErr(err)...)
certificateChain := d.Get(mkResourceVirtualEnvironmentCertificateCertificateChain).(string)
for _, c := range *list {
if c.FileName != nil && *c.FileName == "pveproxy-ssl.pem" {
if c.Certificates != nil {
newCertificate := ""
newCertificateChain := ""
if certificateChain != "" {
certificates := strings.Split(strings.TrimSpace(*c.Certificates), "\n")
newCertificate = certificates[0] + "\n"
if len(certificates) > 1 {
newCertificateChain = strings.Join(certificates[1:], "\n") + "\n"
}
} else {
newCertificate = *c.Certificates
}
err = d.Set(mkResourceVirtualEnvironmentCertificateCertificate, newCertificate)
diags = append(diags, diag.FromErr(err)...)
err = d.Set(
mkResourceVirtualEnvironmentCertificateCertificateChain,
newCertificateChain,
)
diags = append(diags, diag.FromErr(err)...)
}
err = d.Set(mkResourceVirtualEnvironmentCertificateFileName, *c.FileName)
diags = append(diags, diag.FromErr(err)...)
if c.NotAfter != nil {
t := time.Time(*c.NotAfter)
err = d.Set(
mkResourceVirtualEnvironmentCertificateExpirationDate,
t.UTC().Format(time.RFC3339),
)
} else {
err = d.Set(mkResourceVirtualEnvironmentCertificateExpirationDate, "")
}
diags = append(diags, diag.FromErr(err)...)
if c.Issuer != nil {
err = d.Set(mkResourceVirtualEnvironmentCertificateIssuer, *c.Issuer)
} else {
err = d.Set(mkResourceVirtualEnvironmentCertificateIssuer, "")
}
diags = append(diags, diag.FromErr(err)...)
if c.PublicKeyBits != nil {
err = d.Set(mkResourceVirtualEnvironmentCertificatePublicKeySize, *c.PublicKeyBits)
} else {
err = d.Set(mkResourceVirtualEnvironmentCertificatePublicKeySize, 0)
}
diags = append(diags, diag.FromErr(err)...)
if c.PublicKeyType != nil {
pkType := *c.PublicKeyType
for _, pkt := range []string{"ecdsa", "dsa", "rsa"} {
if strings.Contains(pkType, pkt) {
pkType = pkt
}
}
err = d.Set(mkResourceVirtualEnvironmentCertificatePublicKeyType, pkType)
} else {
err = d.Set(mkResourceVirtualEnvironmentCertificatePublicKeyType, "")
}
diags = append(diags, diag.FromErr(err)...)
if c.Fingerprint != nil {
err = d.Set(mkResourceVirtualEnvironmentCertificateSSLFingerprint, *c.Fingerprint)
} else {
err = d.Set(mkResourceVirtualEnvironmentCertificateSSLFingerprint, "")
}
diags = append(diags, diag.FromErr(err)...)
if c.NotBefore != nil {
t := time.Time(*c.NotBefore)
err = d.Set(
mkResourceVirtualEnvironmentCertificateStartDate,
t.UTC().Format(time.RFC3339),
)
} else {
err = d.Set(mkResourceVirtualEnvironmentCertificateStartDate, "")
}
diags = append(diags, diag.FromErr(err)...)
if c.Subject != nil {
err = d.Set(mkResourceVirtualEnvironmentCertificateSubject, *c.Subject)
} else {
err = d.Set(mkResourceVirtualEnvironmentCertificateSubject, "")
}
diags = append(diags, diag.FromErr(err)...)
if c.SubjectAlternativeNames != nil {
sanList := make([]interface{}, len(*c.SubjectAlternativeNames))
for i, san := range *c.SubjectAlternativeNames {
sanList[i] = san
}
err = d.Set(mkResourceVirtualEnvironmentCertificateSubjectAlternativeNames, sanList)
} else {
err = d.Set(mkResourceVirtualEnvironmentCertificateSubjectAlternativeNames, []interface{}{})
}
diags = append(diags, diag.FromErr(err)...)
}
}
return diags
}
func certificateUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
config := m.(proxmoxtf.ProviderConfiguration)
api, err := config.GetClient()
if err != nil {
return diag.FromErr(err)
}
nodeName := d.Get(mkResourceVirtualEnvironmentCertificateNodeName).(string)
body := certificateGetUpdateBody(d)
err = api.Node(nodeName).UpdateCertificate(ctx, body)
if err != nil {
return diag.FromErr(err)
}
return certificateRead(ctx, d, m)
}
func certificateDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
config := m.(proxmoxtf.ProviderConfiguration)
api, err := config.GetClient()
if err != nil {
return diag.FromErr(err)
}
nodeName := d.Get(mkResourceVirtualEnvironmentCertificateNodeName).(string)
restart := types.CustomBool(true)
err = api.Node(nodeName).DeleteCertificate(
ctx,
&nodes.CertificateDeleteRequestBody{
Restart: &restart,
},
)
if err != nil {
return diag.FromErr(err)
}
d.SetId("")
return nil
}