terraform-provider: lock-step microservice version (#2733)

This commit is contained in:
Moritz Sanft 2023-12-18 14:21:19 +01:00 committed by GitHub
parent 615e731855
commit 9414f29b51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 104 additions and 54 deletions

View File

@ -31,7 +31,6 @@ resource "random_bytes" "measurement_salt" {
resource "constellation_cluster" "azure_example" { resource "constellation_cluster" "azure_example" {
csp = "azure" csp = "azure"
constellation_microservice_version = "vX.Y.Z"
name = "constell" name = "constell"
uid = "..." uid = "..."
image = data.constellation_image.bar.image image = data.constellation_image.bar.image
@ -79,7 +78,7 @@ resource "constellation_cluster" "azure_example" {
- `api_server_cert_sans` (List of String) List of Subject Alternative Names (SANs) for the API server certificate. Usually, this will be the out-of-cluster endpoint and the in-cluster endpoint, if existing. - `api_server_cert_sans` (List of String) List of Subject Alternative Names (SANs) for the API server certificate. Usually, this will be the out-of-cluster endpoint and the in-cluster endpoint, if existing.
- `azure` (Attributes) Azure-specific configuration. (see [below for nested schema](#nestedatt--azure)) - `azure` (Attributes) Azure-specific configuration. (see [below for nested schema](#nestedatt--azure))
- `constellation_microservice_version` (String) The version of Constellation's microservices used within the cluster. When not set, the provider default version is used. - `constellation_microservice_version` (String) The version of Constellation's microservices used within the cluster. When not set, the provider version is used.
- `extra_microservices` (Attributes) Extra microservice settings. (see [below for nested schema](#nestedatt--extra_microservices)) - `extra_microservices` (Attributes) Extra microservice settings. (see [below for nested schema](#nestedatt--extra_microservices))
- `gcp` (Attributes) GCP-specific configuration. (see [below for nested schema](#nestedatt--gcp)) - `gcp` (Attributes) GCP-specific configuration. (see [below for nested schema](#nestedatt--gcp))
- `in_cluster_endpoint` (String) The endpoint of the cluster. When not set, the out-of-cluster endpoint is used. - `in_cluster_endpoint` (String) The endpoint of the cluster. When not set, the out-of-cluster endpoint is used.

View File

@ -89,18 +89,17 @@ data "constellation_image" "bar" {
} }
resource "constellation_cluster" "aws_example" { resource "constellation_cluster" "aws_example" {
csp = local.csp csp = local.csp
constellation_microservice_version = local.version name = module.aws_infrastructure.name
name = module.aws_infrastructure.name uid = module.aws_infrastructure.uid
uid = module.aws_infrastructure.uid image = data.constellation_image.bar.image
image = data.constellation_image.bar.image attestation = data.constellation_attestation.foo.attestation
attestation = data.constellation_attestation.foo.attestation init_secret = module.aws_infrastructure.init_secret
init_secret = module.aws_infrastructure.init_secret master_secret = local.master_secret
master_secret = local.master_secret master_secret_salt = local.master_secret_salt
master_secret_salt = local.master_secret_salt measurement_salt = local.measurement_salt
measurement_salt = local.measurement_salt out_of_cluster_endpoint = module.aws_infrastructure.out_of_cluster_endpoint
out_of_cluster_endpoint = module.aws_infrastructure.out_of_cluster_endpoint in_cluster_endpoint = module.aws_infrastructure.in_cluster_endpoint
in_cluster_endpoint = module.aws_infrastructure.in_cluster_endpoint
network_config = { network_config = {
ip_cidr_node = module.aws_infrastructure.ip_cidr_node ip_cidr_node = module.aws_infrastructure.ip_cidr_node
ip_cidr_service = "10.96.0.0/12" ip_cidr_service = "10.96.0.0/12"

View File

@ -84,18 +84,17 @@ data "constellation_image" "bar" {
} }
resource "constellation_cluster" "azure_example" { resource "constellation_cluster" "azure_example" {
csp = local.csp csp = local.csp
constellation_microservice_version = local.version name = module.azure_infrastructure.name
name = module.azure_infrastructure.name uid = module.azure_infrastructure.uid
uid = module.azure_infrastructure.uid image = data.constellation_image.bar.image
image = data.constellation_image.bar.image attestation = data.constellation_attestation.foo.attestation
attestation = data.constellation_attestation.foo.attestation init_secret = module.azure_infrastructure.init_secret
init_secret = module.azure_infrastructure.init_secret master_secret = local.master_secret
master_secret = local.master_secret master_secret_salt = local.master_secret_salt
master_secret_salt = local.master_secret_salt measurement_salt = local.measurement_salt
measurement_salt = local.measurement_salt out_of_cluster_endpoint = module.azure_infrastructure.out_of_cluster_endpoint
out_of_cluster_endpoint = module.azure_infrastructure.out_of_cluster_endpoint in_cluster_endpoint = module.azure_infrastructure.in_cluster_endpoint
in_cluster_endpoint = module.azure_infrastructure.in_cluster_endpoint
azure = { azure = {
tenant_id = module.azure_iam.tenant_id tenant_id = module.azure_iam.tenant_id
subscription_id = module.azure_iam.subscription_id subscription_id = module.azure_iam.subscription_id

View File

@ -88,18 +88,17 @@ data "constellation_image" "bar" {
} }
resource "constellation_cluster" "gcp_example" { resource "constellation_cluster" "gcp_example" {
csp = local.csp csp = local.csp
constellation_microservice_version = local.version name = module.gcp_infrastructure.name
name = module.gcp_infrastructure.name uid = module.gcp_infrastructure.uid
uid = module.gcp_infrastructure.uid image = data.constellation_image.bar.image
image = data.constellation_image.bar.image attestation = data.constellation_attestation.foo.attestation
attestation = data.constellation_attestation.foo.attestation init_secret = module.gcp_infrastructure.init_secret
init_secret = module.gcp_infrastructure.init_secret master_secret = local.master_secret
master_secret = local.master_secret master_secret_salt = local.master_secret_salt
master_secret_salt = local.master_secret_salt measurement_salt = local.measurement_salt
measurement_salt = local.measurement_salt out_of_cluster_endpoint = module.gcp_infrastructure.out_of_cluster_endpoint
out_of_cluster_endpoint = module.gcp_infrastructure.out_of_cluster_endpoint in_cluster_endpoint = module.gcp_infrastructure.in_cluster_endpoint
in_cluster_endpoint = module.gcp_infrastructure.in_cluster_endpoint
gcp = { gcp = {
project_id = module.gcp_infrastructure.project project_id = module.gcp_infrastructure.project
service_account_key = module.gcp_iam.service_account_key service_account_key = module.gcp_iam.service_account_key

View File

@ -16,7 +16,6 @@ resource "random_bytes" "measurement_salt" {
resource "constellation_cluster" "azure_example" { resource "constellation_cluster" "azure_example" {
csp = "azure" csp = "azure"
constellation_microservice_version = "vX.Y.Z"
name = "constell" name = "constell"
uid = "..." uid = "..."
image = data.constellation_image.bar.image image = data.constellation_image.bar.image

View File

@ -8,4 +8,5 @@ go_library(
], ],
importpath = "github.com/edgelesssys/constellation/v2/terraform-provider-constellation/internal/data", importpath = "github.com/edgelesssys/constellation/v2/terraform-provider-constellation/internal/data",
visibility = ["//terraform-provider-constellation:__subpackages__"], visibility = ["//terraform-provider-constellation:__subpackages__"],
deps = ["//internal/semver"],
) )

View File

@ -6,8 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only
package data package data
import "github.com/edgelesssys/constellation/v2/internal/semver"
// ProviderData is the data that get's passed down from the provider // ProviderData is the data that get's passed down from the provider
// configuration to the resources and data sources. // configuration to the resources and data sources.
type ProviderData struct { type ProviderData struct {
Version string Version semver.Semver
} }

View File

@ -66,7 +66,7 @@ func (d *AttestationDataSource) Configure(_ context.Context, req datasource.Conf
) )
return return
} }
d.version = providerData.Version d.version = providerData.Version.String()
d.client = http.DefaultClient d.client = http.DefaultClient
d.fetcher = attestationconfigapi.NewFetcher() d.fetcher = attestationconfigapi.NewFetcher()

View File

@ -35,6 +35,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/kms/uri" "github.com/edgelesssys/constellation/v2/internal/kms/uri"
"github.com/edgelesssys/constellation/v2/internal/semver" "github.com/edgelesssys/constellation/v2/internal/semver"
"github.com/edgelesssys/constellation/v2/internal/versions" "github.com/edgelesssys/constellation/v2/internal/versions"
datastruct "github.com/edgelesssys/constellation/v2/terraform-provider-constellation/internal/data"
"github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource"
@ -60,7 +61,8 @@ func NewClusterResource() resource.Resource {
// ClusterResource defines the resource implementation. // ClusterResource defines the resource implementation.
type ClusterResource struct { type ClusterResource struct {
newApplier func(ctx context.Context, validator atls.Validator) *constellation.Applier providerData datastruct.ProviderData
newApplier func(ctx context.Context, validator atls.Validator) *constellation.Applier
} }
// ClusterResourceModel describes the resource data model. // ClusterResourceModel describes the resource data model.
@ -155,8 +157,8 @@ func (r *ClusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re
Optional: true, Optional: true,
}, },
"constellation_microservice_version": schema.StringAttribute{ "constellation_microservice_version": schema.StringAttribute{
MarkdownDescription: "The version of Constellation's microservices used within the cluster. When not set, the provider default version is used.", MarkdownDescription: "The version of Constellation's microservices used within the cluster. When not set, the provider version is used.",
Description: "The version of Constellation's microservices used within the cluster. When not set, the provider default version is used.", Description: "The version of Constellation's microservices used within the cluster. When not set, the provider version is used.",
Optional: true, Optional: true,
}, },
"out_of_cluster_endpoint": schema.StringAttribute{ "out_of_cluster_endpoint": schema.StringAttribute{
@ -330,11 +332,20 @@ func (r *ClusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re
} }
// Configure configures the resource. // Configure configures the resource.
func (r *ClusterResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { func (r *ClusterResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
// Prevent panic if the provider has not been configured. // Prevent panic if the provider has not been configured.
if req.ProviderData == nil { if req.ProviderData == nil {
return return
} }
var ok bool
r.providerData, ok = req.ProviderData.(datastruct.ProviderData)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Resource Configure Type",
fmt.Sprintf("Expected datastruct.ProviderData, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)
return
}
newDialer := func(validator atls.Validator) *dialer.Dialer { newDialer := func(validator atls.Validator) *dialer.Dialer {
return dialer.New(nil, validator, &net.Dialer{}) return dialer.New(nil, validator, &net.Dialer{})
@ -365,7 +376,19 @@ func (r *ClusterResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
} }
// Warn the user about possibly destructive changes in case microservice changes are to be applied. // Warn the user about possibly destructive changes in case microservice changes are to be applied.
if currentState.MicroserviceVersion.ValueString() != plannedState.MicroserviceVersion.ValueString() { currVer, diags := r.getMicroserviceVersion(ctx, &currentState)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
plannedVer, diags := r.getMicroserviceVersion(ctx, &plannedState)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
if currVer.Compare(plannedVer) != 0 { // if versions are not equal
resp.Diagnostics.AddWarning("Microservice version change", resp.Diagnostics.AddWarning("Microservice version change",
"Changing the microservice version can be a destructive operation.\n"+ "Changing the microservice version can be a destructive operation.\n"+
"Upgrading cert-manager will destroy all custom resources you have manually created that are based on the current version of cert-manager.\n"+ "Upgrading cert-manager will destroy all custom resources you have manually created that are based on the current version of cert-manager.\n"+
@ -575,12 +598,9 @@ func (r *ClusterResource) apply(ctx context.Context, data *ClusterResourceModel,
} }
// parse Constellation microservice version // parse Constellation microservice version
microserviceVersion, err := semver.New(data.MicroserviceVersion.ValueString()) microserviceVersion, convertDiags := r.getMicroserviceVersion(ctx, data)
if err != nil { diags.Append(convertDiags...)
diags.AddAttributeError( if diags.HasError() {
path.Root("constellation_microservice_version"),
"Invalid microservice version",
fmt.Sprintf("Parsing microservice version: %s", err))
return diags return diags
} }
@ -1008,6 +1028,28 @@ func (r *ClusterResource) getK8sVersion(ctx context.Context, data *ClusterResour
return k8sVersion, diags return k8sVersion, diags
} }
// getK8sVersion returns the Microservice version from the Terraform state if set, and the default
// version otherwise.
func (r *ClusterResource) getMicroserviceVersion(ctx context.Context, data *ClusterResourceModel) (semver.Semver, diag.Diagnostics) {
diags := diag.Diagnostics{}
var ver semver.Semver
var err error
if data.MicroserviceVersion.ValueString() != "" {
ver, err = semver.New(data.MicroserviceVersion.ValueString())
if err != nil {
diags.AddAttributeError(
path.Root("constellation_microservice_version"),
"Invalid microservice version",
fmt.Sprintf("Parsing microservice version: %s", err))
return semver.Semver{}, diags
}
} else {
tflog.Info(ctx, fmt.Sprintf("No Microservice version specified. Using default version %s.", r.providerData.Version))
ver = r.providerData.Version
}
return ver, diags
}
// tfContextLogger is a logging adapter between the tflog package and // tfContextLogger is a logging adapter between the tflog package and
// Constellation's logger. // Constellation's logger.
type tfContextLogger struct { type tfContextLogger struct {

View File

@ -136,7 +136,7 @@ func (d *ImageDataSource) Configure(_ context.Context, req datasource.ConfigureR
return return
} }
d.version = providerData.Version d.version = providerData.Version.String()
} }
// Read reads from the data source. // Read reads from the data source.

View File

@ -11,7 +11,9 @@ package provider
import ( import (
"context" "context"
"fmt"
"github.com/edgelesssys/constellation/v2/internal/semver"
datastruct "github.com/edgelesssys/constellation/v2/terraform-provider-constellation/internal/data" datastruct "github.com/edgelesssys/constellation/v2/terraform-provider-constellation/internal/data"
"github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/provider"
@ -69,8 +71,16 @@ func (p *ConstellationProvider) Configure(ctx context.Context, req provider.Conf
return return
} }
ver, err := semver.New(p.version)
if err != nil {
resp.Diagnostics.AddError("Invalid provider version",
fmt.Sprintf("Expected a valid semantic version, got %s: %s", p.version, err),
)
return
}
config := datastruct.ProviderData{ config := datastruct.ProviderData{
Version: p.version, Version: ver,
} }
// Make the clients available during data source and resource "Configure" methods. // Make the clients available during data source and resource "Configure" methods.