Skip to content

Commit 88ddf24

Browse files
Merge pull request #353 from vmware/feature/eks_wait_for_cluster_healthy
EKS wait for management cluster and pinniped to be healthy and set kubeconfig when wait_for_kubeconfig is set to true
2 parents 4dfb7a3 + c947580 commit 88ddf24

File tree

7 files changed

+161
-14
lines changed

7 files changed

+161
-14
lines changed

Diff for: docs/data-sources/ekscluster.md

+2
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,12 @@ data "tanzu-mission-control_ekscluster" "tf_eks_cluster" {
4848
- `meta` (Block List, Max: 1) Metadata for the resource (see [below for nested schema](#nestedblock--meta))
4949
- `ready_wait_timeout` (String) Wait timeout duration until cluster resource reaches READY state. Accepted timeout duration values like 5s, 45m, or 3h, higher than zero
5050
- `spec` (Block List, Max: 1) Spec for the cluster (see [below for nested schema](#nestedblock--spec))
51+
- `wait_for_kubeconfig` (Boolean) Wait until pinniped extension is ready to provide kubeconfig
5152

5253
### Read-Only
5354

5455
- `id` (String) The ID of this resource.
56+
- `kubeconfig` (String) Kubeconfig for connecting to newly created cluster base64 encoded. This will only be returned if you have elected to wait for kubeconfig.
5557
- `status` (Map of String) Status of the cluster
5658

5759
<a id="nestedblock--meta"></a>

Diff for: docs/resources/ekscluster.md

+10-11
Original file line numberDiff line numberDiff line change
@@ -80,22 +80,19 @@ resource "tanzu-mission-control_ekscluster" "tf_eks_cluster" {
8080
]
8181
}
8282
83-
addons_config {
83+
addons_config { // this whole section is optional
8484
vpc_cni_config {
8585
eni_config {
86-
id = "subnet-0a680171b6330619f" // Required, should belong to the same VPC as the cluster
87-
security_groups = [
86+
id = "subnet-0a680171b6330619f" // Required, need not belong to the same VPC as the cluster, subnets provided in vpc_cni_config are expected to be in different AZs
87+
security_groups = [ //optional, if not provided, the cluster security group will be used
8888
"sg-00c96ad9d02a22522",
8989
]
9090
}
9191
eni_config {
92-
id = "subnet-06feb0bb0451cda78" // Required, should belong to the same VPC as the cluster
93-
security_groups = [
94-
"sg-00c96ad9d02a22522",
95-
]
92+
id = "subnet-06feb0bb0451cda79" // Required, need not belong to the same VPC as the cluster, subnets provided in vpc_cni_config are expected to be in different AZs
9693
}
9794
}
98-
}
95+
}
9996
}
10097
10198
nodepool {
@@ -213,10 +210,12 @@ resource "tanzu-mission-control_ekscluster" "tf_eks_cluster" {
213210
- `meta` (Block List, Max: 1) Metadata for the resource (see [below for nested schema](#nestedblock--meta))
214211
- `ready_wait_timeout` (String) Wait timeout duration until cluster resource reaches READY state. Accepted timeout duration values like 5s, 45m, or 3h, higher than zero
215212
- `spec` (Block List, Max: 1) Spec for the cluster (see [below for nested schema](#nestedblock--spec))
213+
- `wait_for_kubeconfig` (Boolean) Wait until pinniped extension is ready to provide kubeconfig
216214

217215
### Read-Only
218216

219217
- `id` (String) The ID of this resource.
218+
- `kubeconfig` (String) Kubeconfig for connecting to newly created cluster base64 encoded. This will only be returned if you have elected to wait for kubeconfig.
220219
- `status` (Map of String) Status of the cluster
221220

222221
<a id="nestedblock--meta"></a>
@@ -283,14 +282,14 @@ Optional:
283282

284283
Optional:
285284

286-
- `vpc_cni_config` (Block List, Max: 1) VPC CNI addon config contains the configuration for the VPC CNI addon of the cluster. (see [below for nested schema](#nestedblock--spec--config--addons_config--vpc_cni_config))
285+
- `vpc_cni_config` (Block List, Max: 1) VPC CNI addon config contains the configuration for the VPC CNI addon of the cluster (see [below for nested schema](#nestedblock--spec--config--addons_config--vpc_cni_config))
287286

288287
<a id="nestedblock--spec--config--addons_config--vpc_cni_config"></a>
289288
### Nested Schema for `spec.config.addons_config.vpc_cni_config`
290289

291290
Optional:
292291

293-
- `eni_config` (Block List) ENI config is the VPC CNI Elastic Network Interface config for providing the configuration of subnet and security groups for pods in each AZ. Subnets need not be in the same VPC as the cluster. The subnets provided across eniConfigs should be in different availability zones. Nodepool subnets need to be in the same AZ as the AZs used in ENIConfig. (see [below for nested schema](#nestedblock--spec--config--addons_config--vpc_cni_config--eni_config))
292+
- `eni_config` (Block List) ENI config is the VPC CNI Elastic Network Interface config for providing the configuration of subnet and security groups for pods in each AZ. Subnets need not be in the same VPC as the cluster. The subnets provided across eniConfigs should be in different availability zones. Nodepool subnets need to be in the same AZ as the AZs used in ENIConfig. (see [below for nested schema](#nestedblock--spec--config--addons_config--vpc_cni_config--eni_config))
294293

295294
<a id="nestedblock--spec--config--addons_config--vpc_cni_config--eni_config"></a>
296295
### Nested Schema for `spec.config.addons_config.vpc_cni_config.eni_config`
@@ -301,7 +300,7 @@ Required:
301300

302301
Optional:
303302

304-
- `security_groups` (Set of String) List of security group is optional and if not provided default security group created by EKS will be used.
303+
- `security_groups` (Set of String) List of security group is optional and if not provided default security group created by EKS will be used.
305304

306305

307306

Diff for: internal/models/ekscluster/spec.go

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: internal/resources/ekscluster/constants.go

+2
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,6 @@ const (
6969
releaseVersionKey = "release_version"
7070
readyCondition = "Ready"
7171
errorSeverity = "ERROR"
72+
waitForKubeconfig = "wait_for_kubeconfig"
73+
kubeconfigKey = "kubeconfig"
7274
)

Diff for: internal/resources/ekscluster/data_source.go

+62
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ import (
2020
"github.com/vmware/terraform-provider-tanzu-mission-control/internal/helper"
2121
eksmodel "github.com/vmware/terraform-provider-tanzu-mission-control/internal/models/ekscluster"
2222
"github.com/vmware/terraform-provider-tanzu-mission-control/internal/resources/common"
23+
24+
clustermodel "github.com/vmware/terraform-provider-tanzu-mission-control/internal/models/cluster"
25+
configModels "github.com/vmware/terraform-provider-tanzu-mission-control/internal/models/kubeconfig"
2326
)
2427

2528
func DataSourceTMCEKSCluster() *schema.Resource {
@@ -79,6 +82,49 @@ func dataSourceTMCEKSClusterRead(ctx context.Context, d *schema.ResourceData, m
7982
return true, nil
8083
}
8184

85+
if isWaitForKubeconfig(d) {
86+
clusFullName := &clustermodel.VmwareTanzuManageV1alpha1ClusterFullName{
87+
Name: resp.EksCluster.Spec.AgentName,
88+
OrgID: clusterFn.OrgID,
89+
ManagementClusterName: "eks",
90+
ProvisionerName: "eks",
91+
}
92+
clusterResp, err := config.TMCConnection.ClusterResourceService.ManageV1alpha1ClusterResourceServiceGet(clusFullName)
93+
// nolint: wsl
94+
if err != nil {
95+
log.Printf("Unable to get Tanzu Mission Control cluster entry, name : %s, error : %s", clusterFn.Name, err.Error())
96+
return true, err
97+
}
98+
99+
clusterHealthy, err := isClusterHealthy(clusterResp)
100+
if err != nil || !clusterHealthy {
101+
log.Printf("[DEBUG] waiting for cluster(%s) to be in Healthy status", clusterFn.Name)
102+
return true, err
103+
}
104+
105+
fn := &configModels.VmwareTanzuManageV1alpha1ClusterFullName{
106+
ManagementClusterName: "eks",
107+
ProvisionerName: "eks",
108+
Name: resp.EksCluster.Spec.AgentName,
109+
}
110+
resp, err := config.TMCConnection.KubeConfigResourceService.KubeconfigServiceGet(fn)
111+
// nolint: wsl
112+
if err != nil {
113+
log.Printf("Unable to get Tanzu Mission Control Kubeconfig entry, name : %s, error : %s", fn.Name, err.Error())
114+
return true, err
115+
}
116+
117+
if kubeConfigReady(resp) {
118+
if err = d.Set(kubeconfigKey, resp.Kubeconfig); err != nil {
119+
log.Printf("Failed to set Kubeconfig for cluster %s, error : %s", clusterFn.Name, err.Error())
120+
return false, err
121+
}
122+
} else {
123+
log.Printf("[DEBUG] waiting for cluster(%s)'s Kubeconfig to be in Ready status", clusterFn.Name)
124+
return true, nil
125+
}
126+
}
127+
82128
return false, nil
83129
}
84130

@@ -124,6 +170,22 @@ func dataSourceTMCEKSClusterRead(ctx context.Context, d *schema.ResourceData, m
124170
return diags
125171
}
126172

173+
func isClusterHealthy(cluster *clustermodel.VmwareTanzuManageV1alpha1ClusterGetClusterResponse) (bool, error) {
174+
if cluster == nil || cluster.Cluster == nil || cluster.Cluster.Status == nil || cluster.Cluster.Status.Health == nil {
175+
return false, errors.New("cluster data is invalid or nil")
176+
}
177+
178+
if *cluster.Cluster.Status.Health == clustermodel.VmwareTanzuManageV1alpha1CommonClusterHealthHEALTHY {
179+
return true, nil
180+
}
181+
182+
return false, nil
183+
}
184+
185+
func kubeConfigReady(resp *configModels.VmwareTanzuManageV1alpha1ClusterKubeconfigGetKubeconfigResponse) bool {
186+
return *resp.Status == configModels.VmwareTanzuManageV1alpha1ClusterKubeconfigGetKubeconfigResponseStatusREADY
187+
}
188+
127189
func setResourceData(d *schema.ResourceData, eksCluster *eksmodel.VmwareTanzuManageV1alpha1EksclusterEksCluster, remoteNodepools []*eksmodel.VmwareTanzuManageV1alpha1EksclusterNodepoolNodepool) error {
128190
status := map[string]interface{}{
129191
// TODO: add condition

Diff for: internal/resources/ekscluster/data_source_test.go

+59-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
//go:build ekscluster
2-
// +build ekscluster
3-
41
/*
52
Copyright 2022 VMware, Inc. All Rights Reserved.
63
SPDX-License-Identifier: MPL-2.0
@@ -11,8 +8,10 @@ package ekscluster
118
import (
129
"testing"
1310

11+
"github.com/pkg/errors"
1412
"github.com/stretchr/testify/require"
1513

14+
clustermodel "github.com/vmware/terraform-provider-tanzu-mission-control/internal/models/cluster"
1615
eksmodel "github.com/vmware/terraform-provider-tanzu-mission-control/internal/models/ekscluster"
1716
)
1817

@@ -62,3 +61,60 @@ func TestNodepoolPosMap(t *testing.T) {
6261
})
6362
}
6463
}
64+
65+
func TestIsManagemetClusterHealthy(t *testing.T) {
66+
tests := []struct {
67+
name string
68+
cluster *clustermodel.VmwareTanzuManageV1alpha1ClusterGetClusterResponse
69+
response bool
70+
err error
71+
}{
72+
{
73+
name: "Not healthy",
74+
cluster: &clustermodel.VmwareTanzuManageV1alpha1ClusterGetClusterResponse{
75+
Cluster: &clustermodel.VmwareTanzuManageV1alpha1ClusterCluster{
76+
Status: &clustermodel.VmwareTanzuManageV1alpha1ClusterStatus{
77+
Health: clustermodel.NewVmwareTanzuManageV1alpha1CommonClusterHealth(clustermodel.VmwareTanzuManageV1alpha1CommonClusterHealthUNHEALTHY),
78+
},
79+
},
80+
},
81+
response: false,
82+
err: nil,
83+
},
84+
{
85+
name: "Healthy",
86+
cluster: &clustermodel.VmwareTanzuManageV1alpha1ClusterGetClusterResponse{
87+
Cluster: &clustermodel.VmwareTanzuManageV1alpha1ClusterCluster{
88+
Status: &clustermodel.VmwareTanzuManageV1alpha1ClusterStatus{
89+
Health: clustermodel.NewVmwareTanzuManageV1alpha1CommonClusterHealth(clustermodel.VmwareTanzuManageV1alpha1CommonClusterHealthHEALTHY),
90+
},
91+
},
92+
},
93+
response: true,
94+
err: nil,
95+
},
96+
{
97+
name: "Error",
98+
cluster: &clustermodel.VmwareTanzuManageV1alpha1ClusterGetClusterResponse{
99+
Cluster: &clustermodel.VmwareTanzuManageV1alpha1ClusterCluster{
100+
Status: nil,
101+
},
102+
},
103+
response: false,
104+
err: errors.New("cluster data is invalid or nil"),
105+
},
106+
}
107+
108+
for _, test := range tests {
109+
t.Run(test.name, func(t *testing.T) {
110+
result, err := isClusterHealthy(test.cluster)
111+
if err != nil {
112+
if err.Error() != test.err.Error() {
113+
t.Errorf("expected error to match")
114+
}
115+
} else if test.response != result {
116+
t.Errorf("expected function output to match")
117+
}
118+
})
119+
}
120+
}

Diff for: internal/resources/ekscluster/resource_ekscluster.go

+20
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,17 @@ var clusterSchema = map[string]*schema.Schema{
8686
return true
8787
},
8888
},
89+
waitForKubeconfig: {
90+
Type: schema.TypeBool,
91+
Description: "Wait until pinniped extension is ready to provide kubeconfig",
92+
Default: false,
93+
Optional: true,
94+
},
95+
kubeconfigKey: {
96+
Type: schema.TypeString,
97+
Description: "Kubeconfig for connecting to newly created cluster base64 encoded. This will only be returned if you have elected to wait for kubeconfig.",
98+
Computed: true,
99+
},
89100
}
90101

91102
var clusterSpecSchema = &schema.Schema{
@@ -958,3 +969,12 @@ func flattenEniConfig(item *eksmodel.VmwareTanzuManageV1alpha1EksclusterEniConfi
958969

959970
return data
960971
}
972+
973+
func isWaitForKubeconfig(data *schema.ResourceData) bool {
974+
v := data.Get(waitForKubeconfig)
975+
if v != nil {
976+
return v.(bool)
977+
}
978+
979+
return false
980+
}

0 commit comments

Comments
 (0)