diff --git a/charts/manager/crds/greenhouse.sap_clusters.yaml b/charts/manager/crds/greenhouse.sap_clusters.yaml
index c25b8c50e..d5297cce6 100644
--- a/charts/manager/crds/greenhouse.sap_clusters.yaml
+++ b/charts/manager/crds/greenhouse.sap_clusters.yaml
@@ -58,6 +58,19 @@ spec:
enum:
- direct
type: string
+ kubeConfig:
+ description: KubeConfig contains specific values for `KubeConfig`
+ for the cluster.
+ properties:
+ maxTokenValidity:
+ default: 72
+ description: MaxTokenValidity specifies the maximum duration for
+ which a token remains valid in hours.
+ format: int32
+ maximum: 72
+ minimum: 24
+ type: integer
+ type: object
required:
- accessMode
type: object
diff --git a/docs/reference/api/index.html b/docs/reference/api/index.html
index 2c5937289..4a4c95329 100644
--- a/docs/reference/api/index.html
+++ b/docs/reference/api/index.html
@@ -112,6 +112,19 @@
Cluster
AccessMode configures how the cluster is accessed from the Greenhouse operator.
+
+
+kubeConfig
+
+
+ClusterKubeConfig
+
+
+ |
+
+ KubeConfig contains specific values for KubeConfig for the cluster.
+ |
+
@@ -141,6 +154,38 @@ ClusterAccessMode
ClusterConditionType
(string
alias)
ClusterConditionType is a valid condition of a cluster.
+ClusterKubeConfig
+
+
+(Appears on:
+ClusterSpec)
+
+ClusterKubeConfig configures kube config values.
+
ClusterKubeconfig
ClusterKubeconfig is the Schema for the clusterkubeconfigs API
@@ -727,6 +772,19 @@
ClusterSpec
AccessMode configures how the cluster is accessed from the Greenhouse operator.
+
+
+kubeConfig
+
+
+ClusterKubeConfig
+
+
+ |
+
+ KubeConfig contains specific values for KubeConfig for the cluster.
+ |
+
diff --git a/docs/reference/api/openapi.yaml b/docs/reference/api/openapi.yaml
index 834f02d8b..f70f4f0bb 100755
--- a/docs/reference/api/openapi.yaml
+++ b/docs/reference/api/openapi.yaml
@@ -1,7 +1,7 @@
openapi: 3.0.0
info:
title: Greenhouse
- version: 84e81b8
+ version: aad9425
description: PlusOne operations platform
paths:
/TeamMembership:
@@ -1097,6 +1097,17 @@ components:
enum:
- direct
type: string
+ kubeConfig:
+ description: KubeConfig contains specific values for `KubeConfig` for the cluster.
+ properties:
+ maxTokenValidity:
+ default: 72
+ description: MaxTokenValidity specifies the maximum duration for which a token remains valid in hours.
+ format: int32
+ maximum: 72
+ minimum: 24
+ type: integer
+ type: object
required:
- accessMode
type: object
diff --git a/docs/user-guides/cluster/onboarding.md b/docs/user-guides/cluster/onboarding.md
index 39b3c1967..929bf22d3 100644
--- a/docs/user-guides/cluster/onboarding.md
+++ b/docs/user-guides/cluster/onboarding.md
@@ -74,6 +74,7 @@ metadata:
uid: 0db6e464-ec36-459e-8a05-4ad668b57f42
spec:
accessMode: direct
+ maxTokenValidity: 72h
status:
bearerTokenExpirationTimestamp: "2024-02-09T06:28:57Z"
kubernetesVersion: v1.27.8
diff --git a/pkg/admission/cluster_webhook.go b/pkg/admission/cluster_webhook.go
index 11db3eecf..b3002dbc1 100644
--- a/pkg/admission/cluster_webhook.go
+++ b/pkg/admission/cluster_webhook.go
@@ -47,6 +47,7 @@ func DefaultCluster(ctx context.Context, _ client.Client, obj runtime.Object) er
if !ok {
return nil
}
+
annotations := cluster.GetAnnotations()
deletionVal, deletionMarked := annotations[apis.MarkClusterDeletionAnnotation]
_, scheduleExists := annotations[apis.ScheduleClusterDeletionAnnotation]
@@ -95,6 +96,7 @@ func ValidateCreateCluster(ctx context.Context, _ client.Client, obj runtime.Obj
logger.Error(err, "found deletion annotation on cluster creation, admission will be denied")
return admission.Warnings{"you cannot create a cluster with deletion annotation"}, err
}
+
return nil, nil
}
diff --git a/pkg/admission/cluster_webhook_test.go b/pkg/admission/cluster_webhook_test.go
index 0863d6ef0..9076dc342 100644
--- a/pkg/admission/cluster_webhook_test.go
+++ b/pkg/admission/cluster_webhook_test.go
@@ -171,6 +171,21 @@ var _ = Describe("Cluster Webhook", func() {
},
true,
),
+ Entry("it should allow creation of cluster with not too long token validity",
+ &greenhousev1alpha1.Cluster{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test-cluster",
+ Namespace: "test-namespace",
+ },
+ Spec: greenhousev1alpha1.ClusterSpec{
+ AccessMode: greenhousev1alpha1.ClusterAccessModeDirect,
+ KubeConfig: greenhousev1alpha1.ClusterKubeConfig{
+ MaxTokenValidity: 72,
+ },
+ },
+ },
+ false,
+ ),
)
DescribeTable("Validate Update Cluster",
diff --git a/pkg/apis/greenhouse/v1alpha1/cluster_types.go b/pkg/apis/greenhouse/v1alpha1/cluster_types.go
index 9dd0c578d..b313b2b3b 100644
--- a/pkg/apis/greenhouse/v1alpha1/cluster_types.go
+++ b/pkg/apis/greenhouse/v1alpha1/cluster_types.go
@@ -11,12 +11,24 @@ import (
type ClusterSpec struct {
// AccessMode configures how the cluster is accessed from the Greenhouse operator.
AccessMode ClusterAccessMode `json:"accessMode"`
+
+ // KubeConfig contains specific values for `KubeConfig` for the cluster.
+ KubeConfig ClusterKubeConfig `json:"kubeConfig,omitempty"`
}
// ClusterAccessMode configures the access mode to the customer cluster.
// +kubebuilder:validation:Enum=direct
type ClusterAccessMode string
+// ClusterKubeConfig configures kube config values.
+type ClusterKubeConfig struct {
+ // MaxTokenValidity specifies the maximum duration for which a token remains valid in hours.
+ // +kubebuilder:default:=72
+ // +kubebuilder:validation:Minimum=24
+ // +kubebuilder:validation:Maximum=72
+ MaxTokenValidity int32 `json:"maxTokenValidity,omitempty"`
+}
+
const (
// ClusterAccessModeDirect configures direct access to the cluster.
ClusterAccessModeDirect ClusterAccessMode = "direct"
@@ -26,6 +38,12 @@ const (
// KubeConfigValid reflects the validity of the kubeconfig of a cluster.
KubeConfigValid ConditionType = "KubeConfigValid"
+
+ // MaxTokenValidity contains maximum bearer token validity duration. It is also default value.
+ MaxTokenValidity = 72
+
+ // MinTokenValidity contains maximum bearer token validity duration.
+ MinTokenValidity = 24
)
// ClusterStatus defines the observed state of Cluster
@@ -91,3 +109,11 @@ type ClusterList struct {
func init() {
SchemeBuilder.Register(&Cluster{}, &ClusterList{})
}
+
+func (c *Cluster) SetDefaultTokenValidityIfNeeded() {
+ if c.Spec.KubeConfig.MaxTokenValidity != 0 {
+ return
+ }
+
+ c.Spec.KubeConfig.MaxTokenValidity = MaxTokenValidity
+}
diff --git a/pkg/apis/greenhouse/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/greenhouse/v1alpha1/zz_generated.deepcopy.go
index bddeef083..4566f257f 100644
--- a/pkg/apis/greenhouse/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/greenhouse/v1alpha1/zz_generated.deepcopy.go
@@ -9,7 +9,7 @@ package v1alpha1
import (
rbacv1 "k8s.io/api/rbac/v1"
- v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+ "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
@@ -65,6 +65,21 @@ func (in *Cluster) DeepCopyObject() runtime.Object {
return nil
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterKubeConfig) DeepCopyInto(out *ClusterKubeConfig) {
+ *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterKubeConfig.
+func (in *ClusterKubeConfig) DeepCopy() *ClusterKubeConfig {
+ if in == nil {
+ return nil
+ }
+ out := new(ClusterKubeConfig)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterKubeconfig) DeepCopyInto(out *ClusterKubeconfig) {
*out = *in
@@ -372,6 +387,7 @@ func (in *ClusterOptionOverride) DeepCopy() *ClusterOptionOverride {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterSpec) DeepCopyInto(out *ClusterSpec) {
*out = *in
+ out.KubeConfig = in.KubeConfig
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterSpec.
diff --git a/pkg/controllers/cluster/cluster_controller.go b/pkg/controllers/cluster/cluster_controller.go
index c7a6f33c1..4af3d3bd8 100644
--- a/pkg/controllers/cluster/cluster_controller.go
+++ b/pkg/controllers/cluster/cluster_controller.go
@@ -110,9 +110,10 @@ func (r *RemoteClusterReconciler) EnsureCreated(ctx context.Context, resource li
return ctrl.Result{}, lifecycle.Failed, err
}
+ cluster.SetDefaultTokenValidityIfNeeded()
var tokenRequest = &tokenHelper{
Client: r.Client,
- RemoteClusterBearerTokenValidity: r.RemoteClusterBearerTokenValidity,
+ RemoteClusterBearerTokenValidity: time.Duration(cluster.Spec.KubeConfig.MaxTokenValidity) * time.Hour,
RenewRemoteClusterBearerTokenAfter: r.RenewRemoteClusterBearerTokenAfter,
}
if err := tokenRequest.ReconcileServiceAccountToken(ctx, restClientGetter, cluster); err != nil {
diff --git a/pkg/controllers/cluster/status_test.go b/pkg/controllers/cluster/status_test.go
index 3da268513..75ee7aebd 100644
--- a/pkg/controllers/cluster/status_test.go
+++ b/pkg/controllers/cluster/status_test.go
@@ -51,7 +51,7 @@ var _ = Describe("Cluster status", Ordered, func() {
},
ObjectMeta: metav1.ObjectMeta{
Name: "test-node",
- Namespace: setup.Namespace(),
+ Namespace: "",
},
Status: corev1.NodeStatus{
Conditions: []corev1.NodeCondition{
@@ -72,7 +72,7 @@ var _ = Describe("Cluster status", Ordered, func() {
},
ObjectMeta: metav1.ObjectMeta{
Name: "test-node-2",
- Namespace: setup.Namespace(),
+ Namespace: "",
},
Status: corev1.NodeStatus{
Conditions: []corev1.NodeCondition{
@@ -93,7 +93,7 @@ var _ = Describe("Cluster status", Ordered, func() {
},
ObjectMeta: metav1.ObjectMeta{
Name: "test-node-3",
- Namespace: setup.Namespace(),
+ Namespace: "",
},
Status: corev1.NodeStatus{
Conditions: []corev1.NodeCondition{