Skip to content

Commit

Permalink
Merge pull request digitalocean#429 from varshavaradarajan/varsha/ass…
Browse files Browse the repository at this point in the history
…ociated-resources

kubernetes - support optional cascading deletes
  • Loading branch information
ChiefMateStarbuck authored Jan 15, 2021
2 parents fd7b687 + 9ddf791 commit c0262d4
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 0 deletions.
65 changes: 65 additions & 0 deletions kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ type KubernetesService interface {
Update(context.Context, string, *KubernetesClusterUpdateRequest) (*KubernetesCluster, *Response, error)
Upgrade(context.Context, string, *KubernetesClusterUpgradeRequest) (*Response, error)
Delete(context.Context, string) (*Response, error)
DeleteSelective(context.Context, string, *KubernetesClusterDeleteSelectiveRequest) (*Response, error)
DeleteDangerous(context.Context, string) (*Response, error)
ListAssociatedResourcesForDeletion(context.Context, string) (*KubernetesAssociatedResources, *Response, error)

CreateNodePool(ctx context.Context, clusterID string, req *KubernetesNodePoolCreateRequest) (*KubernetesNodePool, *Response, error)
GetNodePool(ctx context.Context, clusterID, poolID string) (*KubernetesNodePool, *Response, error)
Expand Down Expand Up @@ -84,6 +87,13 @@ type KubernetesClusterUpdateRequest struct {
SurgeUpgrade bool `json:"surge_upgrade,omitempty"`
}

// KubernetesClusterDeleteSelectiveRequest represents a delete selective request to delete a cluster and it's associated resources.
type KubernetesClusterDeleteSelectiveRequest struct {
Volumes []string `json:"volumes"`
VolumeSnapshots []string `json:"volume_snapshots"`
LoadBalancers []string `json:"load_balancers"`
}

// KubernetesClusterUpgradeRequest represents a request to upgrade a Kubernetes cluster.
type KubernetesClusterUpgradeRequest struct {
VersionSlug string `json:"version,omitempty"`
Expand Down Expand Up @@ -445,6 +455,13 @@ type ClusterlintOwner struct {
Name string `json:"name"`
}

// KubernetesAssociatedResources represents a cluster's associated resources
type KubernetesAssociatedResources struct {
Volumes []string `json:"volumes"`
VolumeSnapshots []string `json:"volume_snapshots"`
LoadBalancers []string `json:"load_balancers"`
}

type kubernetesClustersRoot struct {
Clusters []*KubernetesCluster `json:"kubernetes_clusters,omitempty"`
Links *Links `json:"links,omitempty"`
Expand Down Expand Up @@ -548,6 +565,54 @@ func (svc *KubernetesServiceOp) Delete(ctx context.Context, clusterID string) (*
return resp, nil
}

// DeleteSelective deletes a Kubernetes cluster and the specified associated resources.
// Users can choose to delete specific volumes, volume snapshots or load balancers along with the cluster
// There is no way to recover a cluster or the specified resources once destroyed.
func (svc *KubernetesServiceOp) DeleteSelective(ctx context.Context, clusterID string, request *KubernetesClusterDeleteSelectiveRequest) (*Response, error) {
path := fmt.Sprintf("%s/%s/destroy_with_associated_resources/selective", kubernetesClustersPath, clusterID)
req, err := svc.client.NewRequest(ctx, http.MethodDelete, path, request)
if err != nil {
return nil, err
}
resp, err := svc.client.Do(ctx, req, nil)
if err != nil {
return resp, err
}
return resp, nil
}

// DeleteDangerous deletes a Kubernetes cluster and all its associated resources. There is no way to recover a cluster
// or it's associated resources once destroyed.
func (svc *KubernetesServiceOp) DeleteDangerous(ctx context.Context, clusterID string) (*Response, error) {
path := fmt.Sprintf("%s/%s/destroy_with_associated_resources/dangerous", kubernetesClustersPath, clusterID)
req, err := svc.client.NewRequest(ctx, http.MethodDelete, path, nil)
if err != nil {
return nil, err
}
resp, err := svc.client.Do(ctx, req, nil)
if err != nil {
return resp, err
}
return resp, nil
}

// ListAssociatedResourcesForDeletion lists a Kubernetes cluster's resources that can be selected
// for deletion along with the cluster. See DeleteSelective
// Associated resources include volumes, volume snapshots and load balancers.
func (svc *KubernetesServiceOp) ListAssociatedResourcesForDeletion(ctx context.Context, clusterID string) (*KubernetesAssociatedResources, *Response, error) {
path := fmt.Sprintf("%s/%s/destroy_with_associated_resources", kubernetesClustersPath, clusterID)
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
if err != nil {
return nil, nil, err
}
root := new(KubernetesAssociatedResources)
resp, err := svc.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
return root, resp, nil
}

// List returns a list of the Kubernetes clusters visible with the caller's API token.
func (svc *KubernetesServiceOp) List(ctx context.Context, opts *ListOptions) ([]*KubernetesCluster, *Response, error) {
path := kubernetesClustersPath
Expand Down
75 changes: 75 additions & 0 deletions kubernetes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,81 @@ func TestKubernetesClusters_Destroy(t *testing.T) {
require.NoError(t, err)
}

func TestKubernetesClusters_DeleteDangerous(t *testing.T) {
setup()
defer teardown()

kubeSvc := client.Kubernetes

mux.HandleFunc("/v2/kubernetes/clusters/deadbeef-dead-4aa5-beef-deadbeef347d/destroy_with_associated_resources/dangerous", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, http.MethodDelete)
})

_, err := kubeSvc.DeleteDangerous(ctx, "deadbeef-dead-4aa5-beef-deadbeef347d")
require.NoError(t, err)
}

func TestKubernetesClusters_DeleteSelective(t *testing.T) {
setup()
defer teardown()

kubeSvc := client.Kubernetes

deleteRequest := &KubernetesClusterDeleteSelectiveRequest{
Volumes: []string{"2241"},
VolumeSnapshots: []string{"7258"},
LoadBalancers: []string{"9873"},
}

expectedReqJSON := `{"volumes":["2241"],"volume_snapshots":["7258"],"load_balancers":["9873"]}
`

mux.HandleFunc("/v2/kubernetes/clusters/deadbeef-dead-4aa5-beef-deadbeef347d/destroy_with_associated_resources/selective", func(w http.ResponseWriter, r *http.Request) {
buf := new(bytes.Buffer)
buf.ReadFrom(r.Body)
require.Equal(t, expectedReqJSON, buf.String())

v := new(KubernetesClusterDeleteSelectiveRequest)
err := json.NewDecoder(buf).Decode(v)
require.NoError(t, err)

testMethod(t, r, http.MethodDelete)
require.Equal(t, v, deleteRequest)
})

_, err := kubeSvc.DeleteSelective(ctx, "deadbeef-dead-4aa5-beef-deadbeef347d", deleteRequest)
require.NoError(t, err)
}

func TestKubernetesClusters_ListAssociatedResourcesForDeletion(t *testing.T) {
setup()
defer teardown()

kubeSvc := client.Kubernetes
expectedRes := &KubernetesAssociatedResources{
Volumes: []string{"2241"},
VolumeSnapshots: []string{"2425"},
LoadBalancers: []string{"4235"},
}
jBlob := `
{
"volumes": ["2241"],
"volume_snapshots": ["2425"],
"load_balancers": ["4235"]
}
`

mux.HandleFunc("/v2/kubernetes/clusters/deadbeef-dead-4aa5-beef-deadbeef347d/destroy_with_associated_resources", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, http.MethodGet)
fmt.Fprint(w, jBlob)
})

ar, _, err := kubeSvc.ListAssociatedResourcesForDeletion(ctx, "deadbeef-dead-4aa5-beef-deadbeef347d")
require.NoError(t, err)
require.Equal(t, expectedRes, ar)

}

func TestKubernetesClusters_CreateNodePool(t *testing.T) {
setup()
defer teardown()
Expand Down

0 comments on commit c0262d4

Please sign in to comment.