-
Notifications
You must be signed in to change notification settings - Fork 992
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add
resource_kubernetes_endpointslice
(#2086)
* initial resource creation * initial structures commit * add flatteners and expanders * initial create/read functions * update read function * add to provider.go * add finished schema with working tfplan * fix expand on SliceEndpoints * update to TypeList * update to using TypeList * update endpoints expander * fix flattener errors * add expander for objectref and flattener for conditions * add changelog-entry * initial tests * add missing port change check and use proper read function * get update working as well as adding ForceNew to address_type attribute * finish endpoint_slice_v1_test.go * add descriptions to attributes * add docs * website-lint-fix * add validator functions to port, hostname, and addresstype * add int check * fix test format * refactor validate functions * set to correct port
- Loading branch information
Showing
8 changed files
with
861 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
// 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" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" | ||
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": { | ||
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, | ||
ValidateFunc: validation.StringInSlice([]string{"IPv4", "IPv6", "FQDN"}, false), | ||
}, | ||
"endpoint": { | ||
Description: "endpoint is a list of unique endpoints in this slice. Each slice may include a maximum of 1000 endpoints.", | ||
Type: schema.TypeList, | ||
MaxItems: 1000, | ||
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.", | ||
Type: schema.TypeList, | ||
MaxItems: 100, | ||
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
Oops, something went wrong.