Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kubernetes clusters pagination support #130

Merged
merged 1 commit into from
May 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/data-sources/kubernetes_clusters.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ data "wiz_kubernetes_clusters" "myclusters" {
- OpenShift
- Kubernetes
- `external_ids` (List of String) The ID(s) to search by. i.e `Azure Subscription ID` or `AWS account number`.
- `first` (Number) How many matches to return.
- `first` (Number) How many matches to return, maximum is `500` per page.
- Defaults to `50`.
- `kind` (List of String) Query Kubernetes Cluster of specific kind(s) or cloud provider(s).
- Allowed values:
Expand All @@ -51,7 +51,9 @@ data "wiz_kubernetes_clusters" "myclusters" {
- OKE
- OPEN_SHIFT
- SELF_HOSTED
- `search` (String) Free text search.
- `max_pages` (Number) How many pages to return. 0 means all pages.
- Defaults to `0`.
- `search` (String) Free text search. Specify empty string to return all kubernetes clusters

### Read-Only

Expand Down
84 changes: 84 additions & 0 deletions internal/acceptance/data_source_kubernetes_clusters_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package acceptance

import (
"fmt"
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)

// TestAccDatasourceWizKubernetesClusters_basic tests the basic functionality of the datasource
// wiz_kubernetes_clusters. The assumption is that at least two clusters exist in the Wiz tenant in order
// to validate pagination functionality
func TestAccDatasourceWizKubernetesClusters_basic(t *testing.T) {
resource.UnitTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: testAccDatasourceWizKubernetesClustersBasic(1),
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr(
// check first kubernetes cluster has an id that matches the UUID regex
"data.wiz_kubernetes_clusters.foo",
"kubernetes_clusters.0.id",
regexp.MustCompile(UUIDPattern),
),
resource.TestMatchResourceAttr(
// check first kubernetes cluster has a name that is set to a non-empty string
"data.wiz_kubernetes_clusters.foo",
"kubernetes_clusters.0.name",
regexp.MustCompile(`\w`),
),
resource.TestMatchResourceAttr(
// check cloud_account_block has id that matches the UUID regex
"data.wiz_kubernetes_clusters.foo",
"kubernetes_clusters.0.cloud_account.0.id",
regexp.MustCompile(UUIDPattern),
),
resource.TestMatchResourceAttr(
// check cloud_account_block has external_id set to a non-empty string, different cloud providers have different formats
"data.wiz_kubernetes_clusters.foo",
"kubernetes_clusters.0.cloud_account.0.external_id",
regexp.MustCompile(`\w`),
),
resource.TestMatchResourceAttr(
// check cloud_account_block has name set to a non-empty string
"data.wiz_kubernetes_clusters.foo",
"kubernetes_clusters.0.cloud_account.0.name",
regexp.MustCompile(`\w`),
),
resource.TestMatchResourceAttr(
// check cloud_account_block has cloud_provider set to a non-empty string
"data.wiz_kubernetes_clusters.foo",
"kubernetes_clusters.0.cloud_account.0.cloud_provider",
regexp.MustCompile(`\w`),
),
),
},
{
Config: testAccDatasourceWizKubernetesClustersBasic(2),
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr(
// check that the second kubernetes cluster has an id that matches the UUID regex
"data.wiz_kubernetes_clusters.foo",
"kubernetes_clusters.1.id",
regexp.MustCompile(`\w`),
),
),
},
},
})
}

func testAccDatasourceWizKubernetesClustersBasic(maxPages int) string {
return fmt.Sprintf(`
data "wiz_kubernetes_clusters" "foo" {
first = 1
max_pages = %d
search = ""
}

`, maxPages)
}
54 changes: 35 additions & 19 deletions internal/provider/data_source_kubernetes_clusters.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,18 @@ func dataSourceWizKubernetesClusters() *schema.Resource {
Type: schema.TypeInt,
Optional: true,
Default: 50,
Description: "How many matches to return.",
Description: "How many matches to return, maximum is `500` per page.",
},
"max_pages": {
Type: schema.TypeInt,
Optional: true,
Default: 0,
Description: "How many pages to return. 0 means all pages.",
},
"search": {
Type: schema.TypeString,
Optional: true,
Description: "Free text search.",
Description: "Free text search. Specify empty string to return all kubernetes clusters",
},
"external_ids": {
Type: schema.TypeList,
Expand Down Expand Up @@ -164,6 +170,10 @@ func dataSourceWizKubernetesClustersRead(ctx context.Context, d *schema.Resource
if b {
identifier.WriteString(utils.PrettyPrint(a))
}
maxPages, b := d.GetOk("max_pages")
if b {
identifier.WriteString(utils.PrettyPrint(maxPages))
}
h := sha1.New()
h.Write([]byte(identifier.String()))
hashID := hex.EncodeToString(h.Sum(nil))
Expand Down Expand Up @@ -240,14 +250,15 @@ func dataSourceWizKubernetesClustersRead(ctx context.Context, d *schema.Resource

// process the request
data := &ReadKubernetesClusters{}
requestDiags := client.ProcessRequest(ctx, m, vars, data, query, "kubernetesClusters", "read")
requestDiags, allData := client.ProcessPagedRequest(ctx, m, vars, data, query, "kubernetesClusters", "read", maxPages.(int))
tflog.Debug(ctx, fmt.Sprintf("allData: %s", utils.PrettyPrint(allData)))

diags = append(diags, requestDiags...)
if len(diags) > 0 {
return diags
}

clusters := flattenClusters(ctx, &data.KubernetesClusters.Nodes)
clusters := flattenClusters(ctx, allData)

if err := d.Set("kubernetes_clusters", clusters); err != nil {
return append(diags, diag.FromErr(err)...)
Expand All @@ -257,28 +268,33 @@ func dataSourceWizKubernetesClustersRead(ctx context.Context, d *schema.Resource

}

func flattenClusters(ctx context.Context, clusters *[]*wiz.KubernetesCluster) []interface{} {
func flattenClusters(ctx context.Context, clusters []interface{}) []interface{} {
tflog.Info(ctx, "flattenClusters called...")
tflog.Debug(ctx, fmt.Sprintf("Clusters: %s", utils.PrettyPrint(clusters)))

// walk the slice and construct the list
var output = make([]interface{}, 0, 0)
for _, b := range *clusters {
clusterMap := make(map[string]interface{})
clusterMap["id"] = b.ID
clusterMap["name"] = b.Name
var output = make([]interface{}, 0)
for _, b := range clusters {
readClusters := b.(*ReadKubernetesClusters)
for _, cluster := range readClusters.KubernetesClusters.Nodes {
tflog.Debug(ctx, fmt.Sprintf("cluster: %s", utils.PrettyPrint(cluster)))
rootMap := make(map[string]interface{})
rootMap["id"] = cluster.ID
rootMap["name"] = cluster.Name

clusterMap := make(map[string]interface{})
clusterMap["cloud_provider"] = cluster.CloudAccount.CloudProvider
clusterMap["external_id"] = cluster.CloudAccount.ExternalID
clusterMap["id"] = cluster.CloudAccount.ID
clusterMap["name"] = cluster.CloudAccount.Name

accMap := make(map[string]interface{})
accMap["cloud_provider"] = b.CloudAccount.CloudProvider
accMap["external_id"] = b.CloudAccount.ExternalID
accMap["id"] = b.CloudAccount.ID
accMap["name"] = b.CloudAccount.Name
cloudAccountMap := make([]interface{}, 0)
cloudAccountMap = append(cloudAccountMap, clusterMap)
rootMap["cloud_account"] = cloudAccountMap

cloudAccountMap := make([]interface{}, 0, 0)
cloudAccountMap = append(cloudAccountMap, accMap)
clusterMap["cloud_account"] = cloudAccountMap
output = append(output, rootMap)

output = append(output, clusterMap)
}
}

tflog.Debug(ctx, fmt.Sprintf("flattenClusters output: %s", utils.PrettyPrint(output)))
Expand Down
52 changes: 40 additions & 12 deletions internal/provider/data_source_kubernetes_clusters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,53 @@ func TestFlattenKubernetesClusters(t *testing.T) {
"cloud_provider": "AWS",
"external_id": "151668690081",
"id": "31e5304c-baca-54fa-bff3-aaf493eaeae0",
"name": "MYK8S_TENANT_STAGE",
"name": "AWS",
},
},
},
}

var clusterLinks = &[]*wiz.KubernetesCluster{
{
ID: "8f137cbc-0810-55ff-acd6-f7574eb0d071",
Name: "24x7-dev",
CloudAccount: *&wiz.CloudAccount{
ID: "31e5304c-baca-54fa-bff3-aaf493eaeae0",
ExternalID: "151668690081",
CloudProvider: "AWS",
Name: "MYK8S_TENANT_STAGE",
map[string]interface{}{
"id": "8f137cbc-0810-55ff-acd6-f7574eb0d072",
"name": "24x7-prod",
"cloud_account": []interface{}{
map[string]interface{}{
"cloud_provider": "AZURE",
"external_id": "31e5304c-baca-54fa-bff3-aaf493eaeae2",
"id": "31e5304c-baca-54fa-bff3-aaf493eaeae0",
"name": "AZURE",
},
},
},
}

clusters := &ReadKubernetesClusters{
KubernetesClusters: wiz.KubernetesClusterConnection{
Nodes: []*wiz.KubernetesCluster{
{
ID: "8f137cbc-0810-55ff-acd6-f7574eb0d071",
Name: "24x7-dev",
CloudAccount: wiz.CloudAccount{
ID: "31e5304c-baca-54fa-bff3-aaf493eaeae0",
ExternalID: "151668690081",
CloudProvider: "AWS",
Name: "AWS",
},
},
{
ID: "8f137cbc-0810-55ff-acd6-f7574eb0d072",
Name: "24x7-prod",
CloudAccount: wiz.CloudAccount{
ID: "31e5304c-baca-54fa-bff3-aaf493eaeae0",
ExternalID: "31e5304c-baca-54fa-bff3-aaf493eaeae2",
CloudProvider: "AZURE",
Name: "AZURE",
},
},
},
}}

clusterLinks := make([]interface{}, 0)
clusterLinks = append(clusterLinks, clusters)

flattened := flattenClusters(ctx, clusterLinks)

if !reflect.DeepEqual(flattened, expected) {
Expand Down