feat(ske): add refresh_before field to ske_kubeconfig resource (#1000)

This commit is contained in:
Maximilian Bischoff 2025-10-06 10:52:53 +02:00 committed by GitHub
parent 5f71eef4a9
commit 0763a5f01f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 64 additions and 24 deletions

View file

@ -16,7 +16,10 @@ SKE kubeconfig resource schema. Must have a `region` specified in the provider c
resource "stackit_ske_kubeconfig" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
cluster_name = "example-cluster"
refresh = true
refresh = true
expiration = 7200 # 2 hours
refresh_before = 3600 # 1 hour
}
```
@ -32,6 +35,7 @@ resource "stackit_ske_kubeconfig" "example" {
- `expiration` (Number) Expiration time of the kubeconfig, in seconds. Defaults to `3600`
- `refresh` (Boolean) If set to true, the provider will check if the kubeconfig has expired and will generated a new valid one in-place
- `refresh_before` (Number) Number of seconds before expiration to trigger refresh of the kubeconfig at. Only used if refresh is set to true.
- `region` (String) The resource region. If not defined, the provider region is used.
### Read-Only

View file

@ -1,5 +1,8 @@
resource "stackit_ske_kubeconfig" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
cluster_name = "example-cluster"
refresh = true
refresh = true
expiration = 7200 # 2 hours
refresh_before = 3600 # 1 hour
}

View file

@ -10,6 +10,7 @@ import (
skeUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/ske/utils"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-log/tflog"
@ -38,16 +39,17 @@ var (
)
type Model struct {
Id types.String `tfsdk:"id"` // needed by TF
ClusterName types.String `tfsdk:"cluster_name"`
ProjectId types.String `tfsdk:"project_id"`
KubeconfigId types.String `tfsdk:"kube_config_id"` // uuid generated internally because kubeconfig has no identifier
Kubeconfig types.String `tfsdk:"kube_config"`
Expiration types.Int64 `tfsdk:"expiration"`
Refresh types.Bool `tfsdk:"refresh"`
ExpiresAt types.String `tfsdk:"expires_at"`
CreationTime types.String `tfsdk:"creation_time"`
Region types.String `tfsdk:"region"`
Id types.String `tfsdk:"id"` // needed by TF
ClusterName types.String `tfsdk:"cluster_name"`
ProjectId types.String `tfsdk:"project_id"`
KubeconfigId types.String `tfsdk:"kube_config_id"` // uuid generated internally because kubeconfig has no identifier
Kubeconfig types.String `tfsdk:"kube_config"`
Expiration types.Int64 `tfsdk:"expiration"`
Refresh types.Bool `tfsdk:"refresh"`
RefreshBefore types.Int64 `tfsdk:"refresh_before"`
ExpiresAt types.String `tfsdk:"expires_at"`
CreationTime types.String `tfsdk:"creation_time"`
Region types.String `tfsdk:"region"`
}
// NewKubeconfigResource is a helper function to simplify the provider implementation.
@ -94,6 +96,7 @@ func (r *kubeconfigResource) Schema(_ context.Context, _ resource.SchemaRequest,
"expiration": "Expiration time of the kubeconfig, in seconds. Defaults to `3600`",
"expires_at": "Timestamp when the kubeconfig expires",
"refresh": "If set to true, the provider will check if the kubeconfig has expired and will generated a new valid one in-place",
"refresh_before": "Number of seconds before expiration to trigger refresh of the kubeconfig at. Only used if refresh is set to true.",
"creation_time": "Date-time when the kubeconfig was created",
"region": "The resource region. If not defined, the provider region is used.",
}
@ -155,6 +158,16 @@ func (r *kubeconfigResource) Schema(_ context.Context, _ resource.SchemaRequest,
boolplanmodifier.RequiresReplace(),
},
},
"refresh_before": schema.Int64Attribute{
Description: descriptions["refresh_before"],
Optional: true,
PlanModifiers: []planmodifier.Int64{
int64planmodifier.UseStateForUnknown(),
},
Validators: []validator.Int64{
int64validator.AtLeast(1),
},
},
"kube_config": schema.StringAttribute{
Description: descriptions["kube_config"],
Computed: true,
@ -442,6 +455,9 @@ func checkHasExpired(model *Model, currentTime time.Time) (bool, error) {
if err != nil {
return false, fmt.Errorf("converting expiresAt field to timestamp: %w", err)
}
if !model.RefreshBefore.IsNull() {
expiresAt = expiresAt.Add(-time.Duration(model.RefreshBefore.ValueInt64()) * time.Second)
}
if expiresAt.Before(currentTime) {
return true, nil
}

View file

@ -26,14 +26,15 @@ func TestMapFields(t *testing.T) {
Kubeconfig: utils.Ptr("kubeconfig"),
},
Model{
ClusterName: types.StringValue("name"),
ProjectId: types.StringValue("pid"),
Kubeconfig: types.StringValue("kubeconfig"),
Expiration: types.Int64Null(),
Refresh: types.BoolNull(),
ExpiresAt: types.StringValue("2024-02-07T16:42:12Z"),
CreationTime: types.StringValue("2024-02-05T14:40:12Z"),
Region: types.StringValue(testRegion),
ClusterName: types.StringValue("name"),
ProjectId: types.StringValue("pid"),
Kubeconfig: types.StringValue("kubeconfig"),
Expiration: types.Int64Null(),
Refresh: types.BoolNull(),
RefreshBefore: types.Int64Null(),
ExpiresAt: types.StringValue("2024-02-07T16:42:12Z"),
CreationTime: types.StringValue("2024-02-05T14:40:12Z"),
Region: types.StringValue(testRegion),
},
true,
},
@ -169,6 +170,17 @@ func TestCheckHasExpired(t *testing.T) {
expected: false,
expectedError: false,
},
{
description: "not expired but refresh_before reached",
inputModel: &Model{
Refresh: types.BoolValue(true),
RefreshBefore: types.Int64Value(int64(time.Hour.Seconds())),
ExpiresAt: types.StringValue(time.Now().Add(1 * time.Hour).Format(time.RFC3339)), // in one hour
},
currentTime: time.Now(),
expected: true,
expectedError: false,
},
{
description: "invalid time",
inputModel: &Model{

View file

@ -86,6 +86,7 @@ var testConfigVarsMax = config.Variables{
"region": config.StringVariable(testutil.Region),
"expiration": config.StringVariable("3600"),
"refresh": config.StringVariable("true"),
"refresh_before": config.StringVariable("600"),
"dns_zone_name": config.StringVariable("acc-" + acctest.RandStringFromCharSet(6, acctest.CharSetAlpha)),
"dns_name": config.StringVariable("acc-" + acctest.RandStringFromCharSet(6, acctest.CharSetAlpha) + ".runs.onstackit.cloud"),
}
@ -301,6 +302,8 @@ func TestAccSKEMax(t *testing.T) {
"stackit_ske_cluster.cluster", "name",
),
resource.TestCheckResourceAttr("stackit_ske_kubeconfig.kubeconfig", "expiration", testutil.ConvertConfigVariable(testConfigVarsMax["expiration"])),
resource.TestCheckResourceAttr("stackit_ske_kubeconfig.kubeconfig", "refresh", testutil.ConvertConfigVariable(testConfigVarsMax["refresh"])),
resource.TestCheckResourceAttr("stackit_ske_kubeconfig.kubeconfig", "refresh_before", testutil.ConvertConfigVariable(testConfigVarsMax["refresh_before"])),
resource.TestCheckResourceAttrSet("stackit_ske_kubeconfig.kubeconfig", "expires_at"),
),
},

View file

@ -32,6 +32,7 @@ variable "maintenance_end" {}
variable "region" {}
variable "expiration" {}
variable "refresh" {}
variable "refresh_before" {}
variable "dns_zone_name" {}
variable "dns_name" {}
@ -94,10 +95,11 @@ resource "stackit_ske_cluster" "cluster" {
}
resource "stackit_ske_kubeconfig" "kubeconfig" {
project_id = stackit_ske_cluster.cluster.project_id
cluster_name = stackit_ske_cluster.cluster.name
expiration = var.expiration
refresh = var.refresh
project_id = stackit_ske_cluster.cluster.project_id
cluster_name = stackit_ske_cluster.cluster.name
expiration = var.expiration
refresh = var.refresh
refresh_before = var.refresh_before
}
data "stackit_ske_cluster" "cluster" {