mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-07-24 15:55:17 -04:00
terraform-provider: data skeleton for cluster resource (#2678)
This commit is contained in:
parent
f5718b6655
commit
c07c333d3d
20 changed files with 1091 additions and 454 deletions
|
@ -0,0 +1,303 @@
|
|||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/choose"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versions"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"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/types"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
)
|
||||
|
||||
// Ensure provider defined types fully satisfy framework interfaces.
|
||||
var (
|
||||
_ resource.Resource = &ClusterResource{}
|
||||
_ resource.ResourceWithImportState = &ClusterResource{}
|
||||
)
|
||||
|
||||
// NewClusterResource creates a new cluster resource.
|
||||
func NewClusterResource() resource.Resource {
|
||||
return &ClusterResource{}
|
||||
}
|
||||
|
||||
// ClusterResource defines the resource implementation.
|
||||
type ClusterResource struct{}
|
||||
|
||||
// ClusterResourceModel describes the resource data model.
|
||||
type ClusterResourceModel struct {
|
||||
UID types.String `tfsdk:"uid"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Image types.String `tfsdk:"image"`
|
||||
KubernetesVersion types.String `tfsdk:"kubernetes_version"`
|
||||
InitEndpoint types.String `tfsdk:"init_endpoint"`
|
||||
KubernetesAPIEndpoint types.String `tfsdk:"kubernetes_api_endpoint"`
|
||||
MicroserviceVersion types.String `tfsdk:"constellation_microservices_version"`
|
||||
ExtraMicroservices types.Object `tfsdk:"extra_microservices"`
|
||||
MasterSecret types.String `tfsdk:"master_secret"`
|
||||
InitSecret types.String `tfsdk:"init_secret"`
|
||||
Attestation types.Object `tfsdk:"attestation"`
|
||||
OwnerID types.String `tfsdk:"owner_id"`
|
||||
ClusterID types.String `tfsdk:"cluster_id"`
|
||||
Kubeconfig types.String `tfsdk:"kubeconfig"`
|
||||
// NetworkConfig types.Object `tfsdk:"network_config"` // TODO(elchead): do when clear what is needed
|
||||
}
|
||||
|
||||
// Metadata returns the metadata of the resource.
|
||||
func (r *ClusterResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_cluster"
|
||||
}
|
||||
|
||||
// Schema returns the schema of the resource.
|
||||
func (r *ClusterResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
resp.Schema = schema.Schema{
|
||||
MarkdownDescription: "Resource for a Constellation cluster.",
|
||||
Description: "Resource for a Constellation cluster.",
|
||||
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"uid": schema.StringAttribute{
|
||||
MarkdownDescription: "The UID of the cluster.",
|
||||
Description: "The UID of the cluster.",
|
||||
Required: true,
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
MarkdownDescription: "Name used in the cluster's named resources / cluster name.",
|
||||
Description: "Name used in the cluster's named resources / cluster name.",
|
||||
Optional: true, // TODO(elchead): use "constell" as default
|
||||
},
|
||||
"image": schema.StringAttribute{
|
||||
MarkdownDescription: "The Constellation OS image to use in the CSP specific reference format. Use the `constellation_image` data source to find the correct image for your CSP.",
|
||||
Description: "The Constellation OS image to use in the CSP specific reference format. Use the `constellation_image` data source to find the correct image for your CSP. When not set, the latest default version will be used.",
|
||||
Optional: true,
|
||||
},
|
||||
"kubernetes_version": schema.StringAttribute{
|
||||
MarkdownDescription: fmt.Sprintf("The Kubernetes version to use for the cluster. When not set, the latest default version (%q) will be used. The supported versions are %s.", versions.Default, versions.SupportedK8sVersions()),
|
||||
Description: fmt.Sprintf("The Kubernetes version to use for the cluster. When not set, the latest default version (%q) will be used. The supported versions are %s.", versions.Default, versions.SupportedK8sVersions()),
|
||||
Optional: true,
|
||||
},
|
||||
"constellation_microservices_version": schema.StringAttribute{
|
||||
MarkdownDescription: "The Constellation microservices version to use for the cluster.",
|
||||
Description: "The Constellation microservices version to use for the cluster. When not set, the latest default version will be used.",
|
||||
Optional: true,
|
||||
},
|
||||
"init_endpoint": schema.StringAttribute{
|
||||
MarkdownDescription: "The endpoint to use for cluster initialization. This is the endpoint of the node running the bootstrapper.",
|
||||
Description: "The endpoint to use for cluster initialization.",
|
||||
Optional: true,
|
||||
},
|
||||
"kubernetes_api_endpoint": schema.StringAttribute{
|
||||
MarkdownDescription: "The endpoint to use for the Kubernetes API.",
|
||||
Description: "The endpoint to use for the Kubernetes API. When not set, the default endpoint will be used.",
|
||||
Optional: true,
|
||||
},
|
||||
"extra_microservices": schema.SingleNestedAttribute{
|
||||
MarkdownDescription: "Extra microservice settings.",
|
||||
Description: "Extra microservice settings.",
|
||||
Optional: true,
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"csi_driver": schema.BoolAttribute{
|
||||
Optional: true,
|
||||
MarkdownDescription: "Enable the CSI driver microservice.",
|
||||
Description: "Enable the CSI driver microservice.",
|
||||
},
|
||||
},
|
||||
},
|
||||
"master_secret": schema.StringAttribute{
|
||||
MarkdownDescription: "The master secret to use for the cluster.",
|
||||
Description: "The master secret to use for the cluster.",
|
||||
Required: true,
|
||||
},
|
||||
"init_secret": schema.StringAttribute{
|
||||
MarkdownDescription: "The init secret to use for the cluster.",
|
||||
Description: "The init secret to use for the cluster.",
|
||||
Required: true,
|
||||
},
|
||||
"attestation": newAttestationConfigAttribute(attributeInput),
|
||||
"owner_id": schema.StringAttribute{
|
||||
MarkdownDescription: "The owner ID of the cluster.",
|
||||
Description: "The owner ID of the cluster.",
|
||||
Computed: true,
|
||||
},
|
||||
"cluster_id": schema.StringAttribute{
|
||||
MarkdownDescription: "The cluster ID of the cluster.",
|
||||
Description: "The cluster ID of the cluster.",
|
||||
Computed: true,
|
||||
},
|
||||
"kubeconfig": schema.StringAttribute{
|
||||
MarkdownDescription: "The kubeconfig of the cluster.",
|
||||
Description: "The kubeconfig of the cluster.",
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Configure configures the resource.
|
||||
func (r *ClusterResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) {
|
||||
// Prevent panic if the provider has not been configured.
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// client, ok := req.ProviderData.(*http.Client)
|
||||
|
||||
// if !ok {
|
||||
// resp.Diagnostics.AddError(
|
||||
// "Unexpected Resource Configure Type",
|
||||
// fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
|
||||
// )
|
||||
|
||||
// return
|
||||
//}
|
||||
}
|
||||
|
||||
// Create is called when the resource is created.
|
||||
func (r *ClusterResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
var data ClusterResourceModel
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var tfAttestation attestation
|
||||
diags := data.Attestation.As(ctx, &tfAttestation, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
attestationVariant, err := variant.FromString(tfAttestation.Variant)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddAttributeError(
|
||||
path.Root("attestation_variant"),
|
||||
"Invalid Attestation Variant",
|
||||
fmt.Sprintf("Invalid attestation variant: %s", tfAttestation.Variant))
|
||||
return
|
||||
}
|
||||
attestationCfg, err := convertFromTfAttestationCfg(tfAttestation, attestationVariant)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Parsing attestation config", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var extraMicroservices extraMicroservices
|
||||
diags = data.ExtraMicroservices.As(ctx, &extraMicroservices, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
// TODO(elchead): implement in follow up PR
|
||||
data.OwnerID = types.StringValue("owner_id")
|
||||
data.ClusterID = types.StringValue("cluster_id")
|
||||
data.Kubeconfig = types.StringValue("kubeconfig")
|
||||
// applier := constellation.NewApplier(log)
|
||||
_, err = choose.Validator(attestationCfg, &tfLogger{dg: &resp.Diagnostics})
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Choosing validator", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Write logs using the tflog package
|
||||
// Documentation: https://terraform.io/plugin/log
|
||||
tflog.Trace(ctx, "created a resource")
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
// Read is called when the resource is read or refreshed.
|
||||
func (r *ClusterResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||
var data ClusterResourceModel
|
||||
|
||||
// Read Terraform prior state data into the model
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// If applicable, this is a great opportunity to initialize any necessary
|
||||
// provider client data and make a call using it.
|
||||
// httpResp, err := r.client.Do(httpReq)
|
||||
// if err != nil {
|
||||
// resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read example, got error: %s", err))
|
||||
// return
|
||||
// }
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
// Update is called when the resource is updated.
|
||||
func (r *ClusterResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||
var data ClusterResourceModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// If applicable, this is a great opportunity to initialize any necessary
|
||||
// provider client data and make a call using it.
|
||||
// httpResp, err := r.client.Do(httpReq)
|
||||
// if err != nil {
|
||||
// resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update example, got error: %s", err))
|
||||
// return
|
||||
// }
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
// Delete is called when the resource is destroyed.
|
||||
func (r *ClusterResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||
var data ClusterResourceModel
|
||||
|
||||
// Read Terraform prior state data into the model
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// If applicable, this is a great opportunity to initialize any necessary
|
||||
// provider client data and make a call using it.
|
||||
// httpResp, err := r.client.Do(httpReq)
|
||||
// if err != nil {
|
||||
// resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete example, got error: %s", err))
|
||||
// return
|
||||
// }
|
||||
}
|
||||
|
||||
// ImportState imports to the resource.
|
||||
func (r *ClusterResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
|
||||
}
|
||||
|
||||
type tfLogger struct {
|
||||
dg *diag.Diagnostics
|
||||
}
|
||||
|
||||
func (l *tfLogger) Infof(format string, args ...any) {
|
||||
tflog.Info(context.Background(), fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (l *tfLogger) Warnf(format string, args ...any) {
|
||||
l.dg.AddWarning(fmt.Sprintf(format, args...), "")
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue