Skip to content

Commit b39bbe1

Browse files
committed
feat: add support for deletion of privatelink (both pro and aa)
1 parent be9e7a4 commit b39bbe1

File tree

3 files changed

+206
-1
lines changed

3 files changed

+206
-1
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
All notable changes to this project will be documented in this file.
33
See updating [Changelog example here](https://keepachangelog.com/en/1.0.0/).
44

5-
## 0.44.1 (Unreleased)
5+
## 0.45.0 (January 2026)
6+
7+
### Added:
8+
* Added `DeletePrivateLink()` and `DeleteActiveActivePrivateLink()` methods to delete PrivateLink resources (marks records as deleted status without removing AWS resources)
9+
10+
## 0.44.1 (7th January 2026)
611

712
### Fixed:
813
* Fixed nil pointer dereference when setting request headers before error check

privatelink_test.go

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,180 @@ func TestGetPrivateLinkScript(t *testing.T) {
502502
}
503503
}
504504

505+
func TestDeletePrivateLink(t *testing.T) {
506+
tc := []struct {
507+
description string
508+
mockedResponse []endpointRequest
509+
expectedError error
510+
expectedErrorAs error
511+
}{
512+
{
513+
description: "should successfully delete a privatelink",
514+
mockedResponse: []endpointRequest{
515+
deleteRequest(
516+
t,
517+
"/subscriptions/114019/private-link",
518+
`{
519+
"taskId": "612fd31f-fd44-4cb0-a429-07882309a972",
520+
"commandType": "privateLinkDeleteRequest",
521+
"status": "received",
522+
"description": "Task request received and is being queued for processing.",
523+
"timestamp": "2024-07-16T09:26:40.929904847Z",
524+
"links": [
525+
{
526+
"href": "https://api-staging.qa.redislabs.com/v1/tasks/612fd31f-fd44-4cb0-a429-07882309a972",
527+
"rel": "task",
528+
"type": "GET"
529+
}
530+
]
531+
}`,
532+
),
533+
getRequest(
534+
t,
535+
"/tasks/612fd31f-fd44-4cb0-a429-07882309a972",
536+
`{
537+
"taskId": "612fd31f-fd44-4cb0-a429-07882309a972",
538+
"commandType": "privateLinkDeleteRequest",
539+
"status": "processing-completed",
540+
"description": "Request processing completed successfully and its resources are now being provisioned / de-provisioned.",
541+
"timestamp": "2024-07-16T09:26:49.847808891Z",
542+
"links": [
543+
{
544+
"href": "https://api-staging.qa.redislabs.com/v1/tasks/612fd31f-fd44-4cb0-a429-07882309a972",
545+
"rel": "self",
546+
"type": "GET"
547+
}
548+
]
549+
}`,
550+
),
551+
},
552+
},
553+
{
554+
description: "should fail when subscription is not found",
555+
mockedResponse: []endpointRequest{
556+
deleteRequestWithStatus(
557+
t,
558+
"/subscriptions/114019/private-link",
559+
404,
560+
`{
561+
"timestamp": "2025-01-17T09:34:25.803+00:00",
562+
"status": 404,
563+
"error": "Not Found",
564+
"path": "/v1/subscriptions/114019/private-link"
565+
}`,
566+
),
567+
},
568+
expectedError: errors.New("privatelink resource not found - subscription 114019"),
569+
expectedErrorAs: &pl.NotFound{},
570+
},
571+
}
572+
573+
for _, testCase := range tc {
574+
t.Run(testCase.description, func(t *testing.T) {
575+
server := httptest.NewServer(
576+
testServer("key", "secret", testCase.mockedResponse...))
577+
578+
subject, err := clientFromTestServer(server, "key", "secret")
579+
require.NoError(t, err)
580+
581+
err = subject.PrivateLink.DeletePrivateLink(context.TODO(), 114019)
582+
if testCase.expectedError == nil {
583+
assert.NoError(t, err)
584+
} else {
585+
assert.IsType(t, err, testCase.expectedErrorAs)
586+
assert.EqualError(t, err, testCase.expectedError.Error())
587+
}
588+
})
589+
}
590+
}
591+
592+
func TestDeleteActiveActivePrivateLink(t *testing.T) {
593+
tc := []struct {
594+
description string
595+
mockedResponse []endpointRequest
596+
expectedError error
597+
expectedErrorAs error
598+
}{
599+
{
600+
description: "should successfully delete an active active privatelink",
601+
mockedResponse: []endpointRequest{
602+
deleteRequest(
603+
t,
604+
"/subscriptions/114019/regions/1/private-link",
605+
`{
606+
"taskId": "723ge42g-ge55-5dc1-b530-18993fb82083",
607+
"commandType": "activeActivePrivateLinkDeleteRequest",
608+
"status": "received",
609+
"description": "Task request received and is being queued for processing.",
610+
"timestamp": "2024-07-16T09:26:40.929904847Z",
611+
"links": [
612+
{
613+
"href": "https://api-staging.qa.redislabs.com/v1/tasks/723ge42g-ge55-5dc1-b530-18993fb82083",
614+
"rel": "task",
615+
"type": "GET"
616+
}
617+
]
618+
}`,
619+
),
620+
getRequest(
621+
t,
622+
"/tasks/723ge42g-ge55-5dc1-b530-18993fb82083",
623+
`{
624+
"taskId": "723ge42g-ge55-5dc1-b530-18993fb82083",
625+
"commandType": "activeActivePrivateLinkDeleteRequest",
626+
"status": "processing-completed",
627+
"description": "Request processing completed successfully and its resources are now being provisioned / de-provisioned.",
628+
"timestamp": "2024-07-16T09:26:49.847808891Z",
629+
"links": [
630+
{
631+
"href": "https://api-staging.qa.redislabs.com/v1/tasks/723ge42g-ge55-5dc1-b530-18993fb82083",
632+
"rel": "self",
633+
"type": "GET"
634+
}
635+
]
636+
}`,
637+
),
638+
},
639+
},
640+
{
641+
description: "should fail when subscription is not found",
642+
mockedResponse: []endpointRequest{
643+
deleteRequestWithStatus(
644+
t,
645+
"/subscriptions/114019/regions/1/private-link",
646+
404,
647+
`{
648+
"timestamp": "2025-01-17T09:34:25.803+00:00",
649+
"status": 404,
650+
"error": "Not Found",
651+
"path": "/v1/subscriptions/114019/regions/1/private-link"
652+
}`,
653+
),
654+
},
655+
expectedError: errors.New("privatelink resource not found - subscription 114019"),
656+
expectedErrorAs: &pl.NotFound{},
657+
},
658+
}
659+
660+
for _, testCase := range tc {
661+
t.Run(testCase.description, func(t *testing.T) {
662+
server := httptest.NewServer(
663+
testServer("key", "secret", testCase.mockedResponse...))
664+
665+
subject, err := clientFromTestServer(server, "key", "secret")
666+
require.NoError(t, err)
667+
668+
err = subject.PrivateLink.DeleteActiveActivePrivateLink(context.TODO(), 114019, 1)
669+
if testCase.expectedError == nil {
670+
assert.NoError(t, err)
671+
} else {
672+
assert.IsType(t, err, testCase.expectedErrorAs)
673+
assert.EqualError(t, err, testCase.expectedError.Error())
674+
}
675+
})
676+
}
677+
}
678+
505679
func TestGetActiveActivePrivateLinkScript(t *testing.T) {
506680
tc := []struct {
507681
description string

service/privatelink/service.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,19 @@ func (a *API) DeletePrincipal(ctx context.Context, subscriptionId int, principal
100100
return nil
101101
}
102102

103+
// DeletePrivateLink will delete a PrivateLink for a subscription.
104+
// This marks the PrivateLink record as deleted but does not remove the actual AWS RL resources.
105+
func (a *API) DeletePrivateLink(ctx context.Context, subscriptionId int) error {
106+
message := fmt.Sprintf("delete privatelink for subscription %d", subscriptionId)
107+
path := fmt.Sprintf("/subscriptions/%d/private-link", subscriptionId)
108+
109+
err := a.delete(ctx, message, path, nil, nil)
110+
if err != nil {
111+
return wrap404Error(subscriptionId, err)
112+
}
113+
return nil
114+
}
115+
103116
// CreateActiveActivePrivateLink will create a new active active PrivateLink.
104117
func (a *API) CreateActiveActivePrivateLink(ctx context.Context, subscriptionId int, regionId int, privateLink CreatePrivateLink) error {
105118
message := fmt.Sprintf("create active active PrivateLink for subscription %d", subscriptionId)
@@ -161,6 +174,19 @@ func (a *API) DeleteActiveActivePrincipal(ctx context.Context, subscriptionId in
161174
return nil
162175
}
163176

177+
// DeleteActiveActivePrivateLink will delete an Active-Active PrivateLink for a subscription region.
178+
// This marks the PrivateLink record as deleted but does not remove the actual AWS RL resources.
179+
func (a *API) DeleteActiveActivePrivateLink(ctx context.Context, subscriptionId int, regionId int) error {
180+
message := fmt.Sprintf("delete active active privatelink for subscription %d region %d", subscriptionId, regionId)
181+
path := fmt.Sprintf("/subscriptions/%d/regions/%d/private-link", subscriptionId, regionId)
182+
183+
err := a.delete(ctx, message, path, nil, nil)
184+
if err != nil {
185+
return wrap404Error(subscriptionId, err)
186+
}
187+
return nil
188+
}
189+
164190
func (a *API) create(ctx context.Context, message string, path string, requestBody interface{}) error {
165191
var task internal.TaskResponse
166192
err := a.client.Post(ctx, message, path, requestBody, &task)

0 commit comments

Comments
 (0)