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

Add data source kubernetes_nodes, resource kubernetes_node_taint #1921

Merged
merged 11 commits into from
Jan 30, 2023
4 changes: 4 additions & 0 deletions .changelog/1921.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
```release-note:feature
New data source: `kubernetes_nodes`
New resource: `kubernetes_node_taint`
```
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ require (
k8s.io/client-go v0.25.5
k8s.io/kube-aggregator v0.25.5
k8s.io/kubectl v0.25.5
k8s.io/kubernetes v1.14.0
)

require (
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,8 @@ k8s.io/kube-openapi v0.0.0-20221207184640-f3cff1453715 h1:tBEbstoM+K0FiBV5KGAKQ0
k8s.io/kube-openapi v0.0.0-20221207184640-f3cff1453715/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4=
k8s.io/kubectl v0.25.5 h1:m3Y3ysrlRR+wAD0TYoFCrngcYtwVH7UPCqubEwbe6oo=
k8s.io/kubectl v0.25.5/go.mod h1:EeHEydYE+pR8EUo5LkO27CP9ONxZpdmE2BWPEqTS8hY=
k8s.io/kubernetes v1.14.0 h1:6T2iAEoOYQnzQb3WvPlUkcczEEXZ7+YPlAO8olwujRw=
k8s.io/kubernetes v1.14.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y=
k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
Expand Down
114 changes: 114 additions & 0 deletions kubernetes/data_source_kubernetes_nodes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package kubernetes

import (
"context"
"crypto/sha256"
"fmt"
"log"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
)

func dataSourceKubernetesNodes() *schema.Resource {
return &schema.Resource{
ReadContext: dataSourceKubernetesNodesRead,
Schema: map[string]*schema.Schema{
"metadata": {
Type: schema.TypeList,
Description: "Metadata fields to narrow node selection.",
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"labels": {
Type: schema.TypeMap,
Description: "Select nodes with these labels. More info: http://kubernetes.io/docs/user-guide/labels",
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
ValidateFunc: validateLabels,
},
},
},
},
"nodes": {
Type: schema.TypeList,
Description: "List of nodes in a cluster.",
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"metadata": metadataSchema("node", false),
"spec": {
Type: schema.TypeList,
Required: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: nodeSpecFields(),
},
},
"status": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: nodeStatusFields(),
},
},
},
},
},
},
}
}

func dataSourceKubernetesNodesRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn, err := meta.(KubeClientsets).MainClientset()
if err != nil {
return diag.FromErr(err)
}

listOptions := metav1.ListOptions{}

m := d.Get("metadata").([]interface{})
if len(m) > 0 {
metadata := expandMetadata(m)
labelMap, err := metav1.LabelSelectorAsMap(&metav1.LabelSelector{MatchLabels: metadata.Labels})
if err != nil {
return diag.FromErr(err)
}
labelSelector := labels.SelectorFromSet(labelMap).String()
log.Printf("[DEBUG] using labelSelector: %s", labelSelector)
listOptions.LabelSelector = labelSelector
}

log.Printf("[INFO] Listing nodes")
nodesRaw, err := conn.CoreV1().Nodes().List(ctx, listOptions)
if err != nil {
return diag.FromErr(err)
}
nodes := make([]interface{}, len(nodesRaw.Items))
for i, v := range nodesRaw.Items {
log.Printf("[INFO] Received node: %s", v.Name)
prefix := fmt.Sprintf("nodes.%d.", i)
n := map[string]interface{}{
"metadata": flattenMetadata(v.ObjectMeta, d, meta, prefix),
"spec": flattenNodeSpec(v.Spec),
"status": flattenNodeStatus(v.Status),
}
nodes[i] = n
}
if err := d.Set("nodes", nodes); err != nil {
return diag.FromErr(err)
}
idsum := sha256.New()
for _, v := range nodes {
if _, err := idsum.Write([]byte(fmt.Sprintf("%#v", v))); err != nil {
return diag.FromErr(err)
}
}
id := fmt.Sprintf("%x", idsum.Sum(nil))
d.SetId(id)
return nil
}
63 changes: 63 additions & 0 deletions kubernetes/data_source_kubernetes_nodes_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package kubernetes

import (
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
kuberesource "k8s.io/apimachinery/pkg/api/resource"
)

func checkParsableQuantity(value string) error {
if _, err := kuberesource.ParseQuantity(value); err != nil {
return err
}
return nil
}

func TestAccKubernetesDataSourceNodes_basic(t *testing.T) {
nodeName := regexp.MustCompile(`^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$`)
oneOrMore := regexp.MustCompile(`^[1-9][0-9]*$`)
checkFuncs := resource.ComposeAggregateTestCheckFunc(
resource.TestMatchResourceAttr("data.kubernetes_nodes.test", "nodes.#", oneOrMore),
resource.TestMatchResourceAttr("data.kubernetes_nodes.test", "nodes.0.metadata.0.labels.%", oneOrMore),
resource.TestCheckResourceAttrSet("data.kubernetes_nodes.test", "nodes.0.metadata.0.resource_version"),
resource.TestMatchResourceAttr("data.kubernetes_nodes.test", "nodes.0.metadata.0.name", nodeName),
resource.TestMatchResourceAttr("data.kubernetes_nodes.test", "nodes.0.spec.0.%", oneOrMore),
resource.TestCheckResourceAttrWith("data.kubernetes_nodes.test", "nodes.0.status.0.capacity.cpu", checkParsableQuantity),
resource.TestCheckResourceAttrWith("data.kubernetes_nodes.test", "nodes.0.status.0.capacity.memory", checkParsableQuantity),
resource.TestCheckResourceAttrSet("data.kubernetes_nodes.test", "nodes.0.status.0.node_info.0.architecture"),
)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: testAccProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccKubernetesDataSourceNodesConfig_basic(),
Check: checkFuncs,
},
{
Config: testAccKubernetesDataSourceNodesConfig_labels(),
Check: checkFuncs,
},
},
})
}

func testAccKubernetesDataSourceNodesConfig_basic() string {
return `
data "kubernetes_nodes" "test" {}
`
}

func testAccKubernetesDataSourceNodesConfig_labels() string {
return `
data "kubernetes_nodes" "test" {
metadata {
labels = {
"kubernetes.io/os" = "linux"
}
}
}
`
}
2 changes: 2 additions & 0 deletions kubernetes/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ func Provider() *schema.Provider {
"kubernetes_service_account_v1": dataSourceKubernetesServiceAccount(),
"kubernetes_persistent_volume_claim": dataSourceKubernetesPersistentVolumeClaim(),
"kubernetes_persistent_volume_claim_v1": dataSourceKubernetesPersistentVolumeClaim(),
"kubernetes_nodes": dataSourceKubernetesNodes(),

// networking
"kubernetes_ingress": dataSourceKubernetesIngress(),
Expand Down Expand Up @@ -257,6 +258,7 @@ func Provider() *schema.Provider {
"kubernetes_env": resourceKubernetesEnv(),
"kubernetes_limit_range": resourceKubernetesLimitRange(),
"kubernetes_limit_range_v1": resourceKubernetesLimitRange(),
"kubernetes_node_taint": resourceKubernetesNodeTaint(),
"kubernetes_persistent_volume": resourceKubernetesPersistentVolume(),
"kubernetes_persistent_volume_v1": resourceKubernetesPersistentVolume(),
"kubernetes_persistent_volume_claim": resourceKubernetesPersistentVolumeClaim(),
Expand Down
Loading