Skip to content

Commit

Permalink
Adding a Runtime class resource (#2080)
Browse files Browse the repository at this point in the history
Added a runtime class resource to the provider.
  • Loading branch information
sheneska authored May 5, 2023
1 parent c1408ac commit 73e2558
Show file tree
Hide file tree
Showing 5 changed files with 415 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .changelog/2080.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
`resource/kubernetes_runtime_class_v1`: Add a new resource `kubernetes_runtime_class_v1`.
```
3 changes: 3 additions & 0 deletions kubernetes/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,9 @@ func Provider() *schema.Provider {

// authentication
"kubernetes_token_request_v1": resourceKubernetesTokenRequestV1(),

//node
"kubernetes_runtime_class_v1": resourceKubernetesRuntimeClassV1(),
},
}

Expand Down
177 changes: 177 additions & 0 deletions kubernetes/resource_kubernetes_runtime_class_v1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package kubernetes

import (
"context"
"log"
"regexp"

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

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
nodev1 "k8s.io/api/node/v1"

"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
pkgApi "k8s.io/apimachinery/pkg/types"
)

func resourceKubernetesRuntimeClassV1() *schema.Resource {
return &schema.Resource{
CreateContext: resourceKubernetesRuntimeClassV1Create,
ReadContext: resourceKubernetesRuntimeClassV1Read,
UpdateContext: resourceKubernetesRuntimeClassV1Update,
DeleteContext: resourceKubernetesRuntimeClassV1Delete,

Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

Schema: map[string]*schema.Schema{
"metadata": metadataSchema("runtimeclass", true),

"handler": {
Type: schema.TypeString,
Description: "Specifies the underlying runtime and configuration that the CRI implementation will use to handle pods of this class",
Required: true,
ValidateFunc: validation.StringMatch(regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?`), ""),
ForceNew: true,
},
},
}

}

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

metadata := expandMetadata(d.Get("metadata").([]interface{}))

runtimeClass := nodev1.RuntimeClass{
ObjectMeta: metadata,
Handler: d.Get("handler").(string),
}

out, err := conn.NodeV1().RuntimeClasses().Create(ctx, &runtimeClass, metav1.CreateOptions{})
if err != nil {
return diag.FromErr(err)
}

log.Printf("[INFO] New runtime class created: %#v", out)
d.SetId(out.Name)

return resourceKubernetesRuntimeClassV1Read(ctx, d, meta)
}

func resourceKubernetesRuntimeClassV1Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
exists, err := resourceKubernetesRuntimeClassV1Exists(ctx, d, meta)
if err != nil {
return diag.FromErr(err)
}
if !exists {
d.SetId("")
return diag.Diagnostics{}
}

conn, err := meta.(KubeClientsets).MainClientset()
if err != nil {
return diag.FromErr(err)
}

name := d.Id()

log.Printf("[INFO] Reading runtime class %s", name)
rc, err := conn.NodeV1().RuntimeClasses().Get(ctx, name, metav1.GetOptions{})
if err != nil {
log.Printf("[DEBUG] Received error: %#v", err)
return diag.FromErr(err)
}

log.Printf("[INFO] Received runtime class: %#v", rc)
err = d.Set("metadata", flattenMetadata(rc.ObjectMeta, d, meta))
if err != nil {
return diag.FromErr(err)
}

err = d.Set("handler", rc.Handler)
if err != nil {
return diag.FromErr(err)
}

return nil
}

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

name := d.Id()

patch := patchMetadata("metadata.0.", "/metadata/", d)

data, err := patch.MarshalJSON()
if err != nil {
return diag.Errorf("Failed to marshal update operations: %s", err)
}

log.Printf("[INFO] Updating runtime class %s: %#v", d.Id(), patch)

out, err := conn.NodeV1().RuntimeClasses().Patch(ctx, name, pkgApi.JSONPatchType, data, metav1.PatchOptions{})
if err != nil {
return diag.Errorf("Failed to update runtime class! API error: %s", err)
}

log.Printf("[INFO] Submitted updated runtime class: %#v", out)
d.SetId(out.Name)

return resourceKubernetesRuntimeClassV1Read(ctx, d, meta)
}

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

name := d.Id()

log.Printf("[INFO] Deleting runtime class: %#v", name)
err = conn.NodeV1().RuntimeClasses().Delete(ctx, name, metav1.DeleteOptions{})
if err != nil {
if statusErr, ok := err.(*errors.StatusError); ok && errors.IsNotFound(statusErr) {
return nil
}
return diag.FromErr(err)
}
log.Printf("[INFO] runtime class %s deleted", name)

return nil
}

func resourceKubernetesRuntimeClassV1Exists(ctx context.Context, d *schema.ResourceData, meta interface{}) (bool, error) {
conn, err := meta.(KubeClientsets).MainClientset()
if err != nil {
return false, err
}

name := d.Id()

log.Printf("[INFO] Checking runtime class %s", name)
_, err = conn.NodeV1().RuntimeClasses().Get(ctx, name, metav1.GetOptions{})
if err != nil {
if statusErr, ok := err.(*errors.StatusError); ok && errors.IsNotFound(statusErr) {
return false, nil
}
log.Printf("[DEBUG] Received error: %#v", err)
}

return true, err
}
168 changes: 168 additions & 0 deletions kubernetes/resource_kubernetes_runtime_class_v1_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package kubernetes

import (
"context"
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
nodev1 "k8s.io/api/node/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestAccKubernetesruntime_class_v1_basic(t *testing.T) {
var conf nodev1.RuntimeClass
rcName := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
resourceName := "kubernetes_runtime_class_v1.test"

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
IDRefreshName: resourceName,
IDRefreshIgnore: []string{"metadata.0.resource_version"},
ProviderFactories: testAccProviderFactories,
CheckDestroy: testAccCheckKubernetesRuntimeClassV1Destroy,
Steps: []resource.TestStep{
{
Config: testAccKubernetesruntime_class_v1_basic(rcName),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesruntime_class_v1Exists(resourceName, &conf),
resource.TestCheckResourceAttr(resourceName, "metadata.0.name", rcName),
resource.TestCheckResourceAttr(resourceName, "handler", "myclass"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"metadata.0.resource_version"},
},
{
Config: testAccKubernetesruntime_class_v1_addAnnotations(rcName),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesruntime_class_v1Exists(resourceName, &conf),
resource.TestCheckResourceAttr(resourceName, "metadata.0.annotations.%", "2"),
resource.TestCheckResourceAttr(resourceName, "metadata.0.annotations.TestAnnotationOne", "one"),
resource.TestCheckResourceAttr(resourceName, "metadata.0.annotations.TestAnnotationTwo", "two"),
resource.TestCheckResourceAttr(resourceName, "metadata.0.labels.%", "0"),
resource.TestCheckResourceAttr(resourceName, "metadata.0.name", rcName),
resource.TestCheckResourceAttr(resourceName, "handler", "newclass"),
resource.TestCheckResourceAttrSet(resourceName, "metadata.0.generation"),
resource.TestCheckResourceAttrSet(resourceName, "metadata.0.resource_version"),
resource.TestCheckResourceAttrSet(resourceName, "metadata.0.uid"),
),
},
{
Config: testAccKubernetesruntime_class_v1_addLabels(rcName),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesruntime_class_v1Exists(resourceName, &conf),
resource.TestCheckResourceAttr(resourceName, "metadata.0.annotations.%", "2"),
resource.TestCheckResourceAttr(resourceName, "metadata.0.annotations.TestAnnotationOne", "one"),
resource.TestCheckResourceAttr(resourceName, "metadata.0.annotations.TestAnnotationTwo", "two"),
resource.TestCheckResourceAttr(resourceName, "metadata.0.labels.%", "2"),
resource.TestCheckResourceAttr(resourceName, "metadata.0.labels.TestLabelOne", "one"),
resource.TestCheckResourceAttr(resourceName, "metadata.0.labels.TestLabelTwo", "two"),
resource.TestCheckResourceAttr(resourceName, "metadata.0.name", rcName),
resource.TestCheckResourceAttr(resourceName, "handler", "my-class"),
resource.TestCheckResourceAttrSet(resourceName, "metadata.0.generation"),
resource.TestCheckResourceAttrSet(resourceName, "metadata.0.resource_version"),
resource.TestCheckResourceAttrSet(resourceName, "metadata.0.uid"),
),
},
},
})
}

func testAccKubernetesruntime_class_v1_basic(name string) string {
return fmt.Sprintf(`resource "kubernetes_runtime_class_v1" "test" {
metadata {
name = %q
}
handler = "myclass"
}
`, name)
}

func testAccKubernetesruntime_class_v1_addAnnotations(name string) string {
return fmt.Sprintf(`resource "kubernetes_runtime_class_v1" "test" {
metadata {
annotations = {
TestAnnotationOne = "one"
TestAnnotationTwo = "two"
}
name = %q
}
handler = "newclass"
}
`, name)
}

func testAccKubernetesruntime_class_v1_addLabels(name string) string {
return fmt.Sprintf(`resource "kubernetes_runtime_class_v1" "test" {
metadata {
annotations = {
TestAnnotationOne = "one"
TestAnnotationTwo = "two"
}
labels = {
TestLabelOne = "one"
TestLabelTwo = "two"
}
name = %q
}
handler = "my-class"
}
`, name)
}

func testAccCheckKubernetesRuntimeClassV1Destroy(s *terraform.State) error {
conn, err := testAccProvider.Meta().(KubeClientsets).MainClientset()

if err != nil {
return err
}
ctx := context.TODO()

for _, rs := range s.RootModule().Resources {
if rs.Type != "kubernetes_runtime_class_v1" {
continue
}

resp, err := conn.NodeV1().RuntimeClasses().Get(ctx, rs.Primary.ID, metav1.GetOptions{})
if err == nil {
if resp.Name == rs.Primary.ID {
return fmt.Errorf("Runtime Class still exists: %s", rs.Primary.ID)
}
}
}

return nil
}

func testAccCheckKubernetesruntime_class_v1Exists(n string, obj *nodev1.RuntimeClass) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}

conn, err := testAccProvider.Meta().(KubeClientsets).MainClientset()
if err != nil {
return err
}
ctx := context.TODO()

out, err := conn.NodeV1().RuntimeClasses().Get(ctx, rs.Primary.ID, metav1.GetOptions{})
if err != nil {
return err
}

*obj = *out
return nil
}
}
Loading

0 comments on commit 73e2558

Please sign in to comment.