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 resource_kubernetes_endpointslice #2086

Merged
merged 29 commits into from
Jun 20, 2023
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d6f253d
initial resource creation
BBBmau Apr 26, 2023
010c4dd
initial structures commit
BBBmau May 2, 2023
295fb28
add flatteners and expanders
BBBmau May 2, 2023
60b173c
initial create/read functions
BBBmau May 3, 2023
196bd15
update read function
BBBmau May 4, 2023
5ef8196
add to provider.go
BBBmau May 8, 2023
18daafb
add finished schema with working tfplan
BBBmau May 22, 2023
23f4e7b
fix expand on SliceEndpoints
BBBmau May 24, 2023
b247972
Merge branch 'main' into add-endpointSlice-resource
BBBmau May 30, 2023
dce0323
update to TypeList
BBBmau May 30, 2023
52126fc
update to using TypeList
BBBmau May 31, 2023
a95f72e
update endpoints expander
BBBmau May 31, 2023
eb19587
fix flattener errors
BBBmau May 31, 2023
02e1e8c
add expander for objectref and flattener for conditions
BBBmau Jun 6, 2023
4780848
add changelog-entry
BBBmau Jun 6, 2023
84a191d
initial tests
BBBmau Jun 6, 2023
b743983
add missing port change check and use proper read function
BBBmau Jun 7, 2023
7334479
get update working as well as adding ForceNew to address_type attribute
BBBmau Jun 7, 2023
d081c2a
finish endpoint_slice_v1_test.go
BBBmau Jun 7, 2023
c82a10f
add descriptions to attributes
BBBmau Jun 7, 2023
7cf239c
add docs
BBBmau Jun 7, 2023
5a87ad7
Merge branch 'main' into add-endpointSlice-resource
BBBmau Jun 7, 2023
66a6eda
website-lint-fix
BBBmau Jun 7, 2023
62a19e3
add validator functions to port, hostname, and addresstype
BBBmau Jun 10, 2023
df115cd
add int check
BBBmau Jun 11, 2023
3069c11
fix test format
BBBmau Jun 11, 2023
66f2f08
refactor validate functions
BBBmau Jun 13, 2023
861b884
Merge branch 'main' into add-endpointSlice-resource
BBBmau Jun 13, 2023
baae8dc
set to correct port
BBBmau Jun 13, 2023
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
3 changes: 3 additions & 0 deletions .changelog/2086.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
`kubernetes/resource_kubernetes_endpointslice.go`: Add kubernetes_endpoint_slice resource
```
1 change: 1 addition & 0 deletions kubernetes/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ func Provider() *schema.Provider {
"kubernetes_pod_v1": resourceKubernetesPod(),
"kubernetes_endpoints": resourceKubernetesEndpoints(),
"kubernetes_endpoints_v1": resourceKubernetesEndpoints(),
"kubernetes_endpoint_slice_v1": resourceKubernetesEndpointSlice(),
"kubernetes_env": resourceKubernetesEnv(),
"kubernetes_limit_range": resourceKubernetesLimitRange(),
"kubernetes_limit_range_v1": resourceKubernetesLimitRange(),
Expand Down
185 changes: 185 additions & 0 deletions kubernetes/resource_kubernetes_endpointslice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package kubernetes

import (
"context"
"log"

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

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
api "k8s.io/api/discovery/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
pkgApi "k8s.io/apimachinery/pkg/types"
)

func resourceKubernetesEndpointSlice() *schema.Resource {
return &schema.Resource{
CreateContext: resourceKubernetesEndpointSliceCreate,
ReadContext: resourceKubernetesEndpointSliceRead,
UpdateContext: resourceKubernetesEndpointSliceUpdate,
DeleteContext: resourceKubernetesEndpointSliceDelete,

Schema: map[string]*schema.Schema{
"metadata": namespacedMetadataSchema("endpoint_slice", true),
"address_type": {
BBBmau marked this conversation as resolved.
Show resolved Hide resolved
Type: schema.TypeString,
Description: "address_type specifies the type of address carried by this EndpointSlice. All addresses in this slice must be the same type. This field is immutable after creation.",
Required: true,
ForceNew: true,
},
"endpoint": {
BBBmau marked this conversation as resolved.
Show resolved Hide resolved
Type: schema.TypeList,
Description: "endpoint is a list of unique endpoints in this slice. Each slice may include a maximum of 1000 endpoints.",
Required: true,
Elem: schemaEndpointSliceSubsetEndpoints(),
},
"port": {
Description: "port specifies the list of network ports exposed by each endpoint in this slice. Each port must have a unique name. Each slice may include a maximum of 100 ports.",
BBBmau marked this conversation as resolved.
Show resolved Hide resolved
Type: schema.TypeList,
Required: true,
Elem: schemaEndpointSliceSubsetPorts(),
},
},
}
}

func resourceKubernetesEndpointSliceCreate(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{}))
endpoint_slice := api.EndpointSlice{
ObjectMeta: metadata,
AddressType: api.AddressType(d.Get("address_type").(string)),
Endpoints: expandEndpointSliceEndpoints(d.Get("endpoint").([]interface{})),
Ports: expandEndpointSlicePorts(d.Get("port").([]interface{})),
}

log.Printf("[INFO] Creating new endpoint_slice: %#v", endpoint_slice)
out, err := conn.DiscoveryV1().EndpointSlices(metadata.Namespace).Create(ctx, &endpoint_slice, metav1.CreateOptions{})
if err != nil {
return diag.Errorf("Failed to create endpoint_slice because: %s", err)
}
log.Printf("[INFO] Submitted new endpoint_slice: %#v", out)
d.SetId(buildId(out.ObjectMeta))

return resourceKubernetesEndpointSliceRead(ctx, d, meta)
}

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

log.Printf("[INFO] Reading endpoint slice %s", name)
endpoint, err := conn.DiscoveryV1().EndpointSlices(namespace).Get(ctx, name, metav1.GetOptions{})
if err != nil {
return diag.Errorf("Failed to read endpoint_slice because: %s", err)
}
log.Printf("[INFO] Received endpoint slice: %#v", endpoint)

address_type := d.Get("address_type").(string)
log.Printf("[DEBUG] Default address type is %q", address_type)
d.Set("address_type", address_type)

err = d.Set("metadata", flattenMetadata(endpoint.ObjectMeta, d, meta))
if err != nil {
return diag.FromErr(err)
}

flattenedEndpoints := flattenEndpointSliceEndpoints(endpoint.Endpoints)
log.Printf("[DEBUG] Flattened EndpointSlice Endpoints: %#v", flattenedEndpoints)
err = d.Set("endpoint", flattenedEndpoints)
if err != nil {
return diag.FromErr(err)
}

flattenedPorts := flattenEndpointSlicePorts(endpoint.Ports)
log.Printf("[DEBUG] Flattened EndpointSlice Ports: %#v", flattenedPorts)
err = d.Set("port", flattenedPorts)
if err != nil {
return diag.FromErr(err)
}

return nil
}

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

namespace, name, err := idParts(d.Id())
if err != nil {
return diag.Errorf("Failed to update endpointSlice because: %s", err)
}

ops := patchMetadata("metadata.0.", "/metadata/", d)
if d.HasChange("address_type") {
address_type := d.Get("address_type").(string)
ops = append(ops, &ReplaceOperation{
Path: "/addressType",
Value: address_type,
})
}
if d.HasChange("endpoint") {
endpoints := expandEndpointSliceEndpoints(d.Get("endpoint").([]interface{}))
ops = append(ops, &ReplaceOperation{
Path: "/endpoints",
Value: endpoints,
})
}
if d.HasChange("port") {
ports := expandEndpointSlicePorts(d.Get("port").([]interface{}))
ops = append(ops, &ReplaceOperation{
Path: "/ports",
Value: ports,
})
}
data, err := ops.MarshalJSON()
if err != nil {
return diag.Errorf("Failed to marshal update operations: %s", err)
}
log.Printf("[INFO] Updating endpointSlice %q: %v", name, string(data))
out, err := conn.DiscoveryV1().EndpointSlices(namespace).Patch(ctx, name, pkgApi.JSONPatchType, data, metav1.PatchOptions{})
if err != nil {
return diag.Errorf("Failed to update endpointSlice: %s", err)
}
log.Printf("[INFO] Submitted updated endpointSlice: %#v", out)
d.SetId(buildId(out.ObjectMeta))

return resourceKubernetesEndpointSliceRead(ctx, d, meta)
}

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

namespace, name, err := idParts(d.Id())
if err != nil {
return diag.Errorf("Failed to delete endpointSlice because: %s", err)
}
log.Printf("[INFO] Deleting endpointSlice: %#v", name)
err = conn.DiscoveryV1().EndpointSlices(namespace).Delete(ctx, name, metav1.DeleteOptions{})
if err != nil {
if statusErr, ok := err.(*errors.StatusError); ok && errors.IsNotFound(statusErr) {
return nil
}
return diag.Errorf("Failed to delete endpoints because: %s", err)
}
log.Printf("[INFO] EndpointSlice %s deleted", name)
d.SetId("")

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

package kubernetes

import (
"fmt"
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccKubernetesEndpointSlice_basic(t *testing.T) {
name := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
IDRefreshName: "kubernetes_endpoint_slice_v1.test",
IDRefreshIgnore: []string{"metadata.0.resource_version"},
ProviderFactories: testAccProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccKubernetesEndpointSliceConfig_basic(name),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "metadata.0.name", name),
resource.TestCheckResourceAttrSet("kubernetes_endpoint_slice_v1.test", "metadata.0.generation"),
resource.TestCheckResourceAttrSet("kubernetes_endpoint_slice_v1.test", "metadata.0.resource_version"),
resource.TestCheckResourceAttrSet("kubernetes_endpoint_slice_v1.test", "metadata.0.uid"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "endpoint.#", "1"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "endpoint.0.condition.#", "1"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "endpoint.0.addresses.#", "1"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "endpoint.0.addresses.0", "129.144.50.56"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "port.#", "1"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "port.0.port", "90"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "port.0.name", "first"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "port.0.app_protocol", "test"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "address_type", "IPv4"),
),
},
{
Config: testAccKubernetesEndpointSliceConfig_modified(name),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "metadata.0.name", name),
resource.TestCheckResourceAttrSet("kubernetes_endpoint_slice_v1.test", "metadata.0.generation"),
resource.TestCheckResourceAttrSet("kubernetes_endpoint_slice_v1.test", "metadata.0.resource_version"),
resource.TestCheckResourceAttrSet("kubernetes_endpoint_slice_v1.test", "metadata.0.uid"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "endpoint.#", "1"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "endpoint.0.condition.#", "1"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "endpoint.0.condition.0.ready", "true"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "endpoint.0.hostname", "test"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "endpoint.0.node_name", "test"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "endpoint.0.addresses.#", "2"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "endpoint.0.target_ref.0.name", "test"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "endpoint.0.addresses.0", "2001:db8:3333:4444:5555:6666:7777:8888"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "endpoint.0.addresses.1", "2002:db8:3333:4444:5555:6666:7777:8888"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "port.#", "2"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "port.0.port", "90"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "port.0.name", "first"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "port.0.app_protocol", "test"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "port.1.port", "900"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "port.1.name", "second"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "port.1.app_protocol", "test"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "address_type", "IPv6"),
),
},
},
})
}

func TestAccKubernetesEndpointSlice_generatedName(t *testing.T) {
prefix := "tf-acc-test-gen-"

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
IDRefreshName: "kubernetes_endpoint_slice_v1.test",
IDRefreshIgnore: []string{"metadata.0.resource_version"},
ProviderFactories: testAccProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccKubernetesEndpointSliceConfig_generatedName(prefix),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet("kubernetes_endpoint_slice_v1.test", "metadata.0.generation"),
resource.TestCheckResourceAttrSet("kubernetes_endpoint_slice_v1.test", "metadata.0.resource_version"),
resource.TestCheckResourceAttrSet("kubernetes_endpoint_slice_v1.test", "metadata.0.uid"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "endpoint.#", "1"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "endpoint.0.condition.#", "1"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "endpoint.0.addresses.#", "1"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "endpoint.0.addresses.0", "129.144.50.56"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "port.#", "1"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "port.0.port", "90"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "port.0.name", "first"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "port.0.app_protocol", "test"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "address_type", "IPv4"),
resource.TestCheckResourceAttr("kubernetes_endpoint_slice_v1.test", "metadata.0.generate_name", prefix),
resource.TestMatchResourceAttr("kubernetes_endpoint_slice_v1.test", "metadata.0.name", regexp.MustCompile("^"+prefix)),
resource.TestCheckResourceAttrSet("kubernetes_endpoint_slice_v1.test", "metadata.0.generation"),
resource.TestCheckResourceAttrSet("kubernetes_endpoint_slice_v1.test", "metadata.0.resource_version"),
resource.TestCheckResourceAttrSet("kubernetes_endpoint_slice_v1.test", "metadata.0.uid"),
),
},
},
})
}

func testAccKubernetesEndpointSliceConfig_basic(name string) string {
return fmt.Sprintf(`resource "kubernetes_endpoint_slice_v1" "test" {
metadata {
name = "%s"
}

endpoint {
condition {

}
addresses = ["129.144.50.56"]
}

port {
port = "90"
name = "first"
app_protocol = "test"
}

address_type = "IPv4"
}
`, name)
}

func testAccKubernetesEndpointSliceConfig_modified(name string) string {
return fmt.Sprintf(`resource "kubernetes_endpoint_slice_v1" "test" {
metadata {
name = "%s"
}

endpoint {
condition {
ready = true
}
target_ref{
name = "test"
}
addresses = ["2001:db8:3333:4444:5555:6666:7777:8888", "2002:db8:3333:4444:5555:6666:7777:8888"]
hostname = "test"
node_name = "test"
zone = "us-west"
}

port {
port = "90"
name = "first"
app_protocol = "test"
}

port {
port = "900"
name = "second"
app_protocol = "test"
}

address_type = "IPv6"
}
`, name)
}

func testAccKubernetesEndpointSliceConfig_generatedName(prefix string) string {
return fmt.Sprintf(`resource "kubernetes_endpoint_slice_v1" "test" {
metadata {
generate_name = "%s"
}

endpoint {
condition {

}
addresses = ["129.144.50.56"]

}

port {
port = "90"
name = "first"
app_protocol = "test"
}

address_type = "IPv4"
}
`, prefix)
}
Loading