Skip to content

Commit c90818a

Browse files
authored
[release/2.1.x]: Backport 3361 to release/2.1.x (#3499)
1 parent c8cef15 commit c90818a

File tree

12 files changed

+440
-35
lines changed

12 files changed

+440
-35
lines changed

.github/workflows/_kongintegration_tests.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ jobs:
5656
MISE_VERBOSE: 1
5757
MISE_DEBUG: 1
5858
GOTESTSUM_JUNITFILE: ${{ env.GOTESTSUM_JUNITFILE }}
59+
KONG_CUSTOM_DOMAIN: konghq.tech
5960
TEST_KONG_KONNECT_ACCESS_TOKEN: ${{ secrets.KONG_TEST_KONNECT_ACCESS_TOKEN }}
6061
KONG_LICENSE_DATA: ${{ steps.license.outputs.license }}
6162
TEST_KONG_ENTERPRISE: ${{ matrix.enterprise }}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"time"
8+
9+
sdkkonnectgo "github.com/Kong/sdk-konnect-go"
10+
sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components"
11+
sdkkonnectops "github.com/Kong/sdk-konnect-go/models/operations"
12+
"github.com/go-logr/logr"
13+
"github.com/samber/lo"
14+
15+
"github.com/kong/kong-operator/v2/test"
16+
)
17+
18+
const (
19+
systemAccountsPageSize = int64(100)
20+
timeUntilSystemAccountOrphaned = time.Hour
21+
)
22+
23+
// cleanupKonnectSystemAccounts deletes orphaned system accounts created by the tests.
24+
func cleanupKonnectSystemAccounts(ctx context.Context, log logr.Logger) error {
25+
serverURL, err := canonicalizedServerURL()
26+
if err != nil {
27+
return fmt.Errorf("invalid server URL %s: %w", test.KonnectServerURL(), err)
28+
}
29+
30+
sdk := sdkkonnectgo.New(
31+
sdkkonnectgo.WithSecurity(
32+
sdkkonnectcomp.Security{
33+
PersonalAccessToken: lo.ToPtr(test.KonnectAccessToken()),
34+
},
35+
),
36+
sdkkonnectgo.WithServerURL(serverURL),
37+
)
38+
39+
orphanedSAs, err := findOrphanedSystemAccounts(ctx, log, sdk.SystemAccounts)
40+
if err != nil {
41+
return fmt.Errorf("failed to find orphaned system accounts: %w", err)
42+
}
43+
if err := deleteSystemAccounts(ctx, log, sdk.SystemAccounts, orphanedSAs); err != nil {
44+
return fmt.Errorf("failed to delete system accounts: %w", err)
45+
}
46+
47+
return nil
48+
}
49+
50+
// findOrphanedSystemAccounts finds system accounts that were created more than timeUntilSystemAccountOrphaned ago.
51+
func findOrphanedSystemAccounts(
52+
ctx context.Context,
53+
log logr.Logger,
54+
sdk *sdkkonnectgo.SystemAccounts,
55+
) ([]string, error) {
56+
var orphanedSystemAccounts []string
57+
pageNumber := int64(1)
58+
59+
for {
60+
response, err := sdk.GetSystemAccounts(ctx, sdkkonnectops.GetSystemAccountsRequest{
61+
PageSize: lo.ToPtr(systemAccountsPageSize),
62+
PageNumber: &pageNumber,
63+
Filter: &sdkkonnectops.GetSystemAccountsQueryParamFilter{
64+
// Only consider non-Konnect-managed system accounts.
65+
KonnectManaged: lo.ToPtr(false),
66+
},
67+
})
68+
if err != nil {
69+
return nil, fmt.Errorf("failed to list system accounts (page %d): %w", pageNumber, err)
70+
}
71+
if response.SystemAccountCollection == nil {
72+
return nil, fmt.Errorf("failed to list system accounts, response is nil (page %d)", pageNumber)
73+
}
74+
75+
accounts := response.SystemAccountCollection.GetData()
76+
if len(accounts) == 0 {
77+
break
78+
}
79+
80+
for _, sa := range accounts {
81+
if sa.ID == nil {
82+
log.Info("System account has no ID, skipping", "name", sa.GetName())
83+
continue
84+
}
85+
if sa.CreatedAt == nil {
86+
log.Info("System account has no creation timestamp, skipping", "id", *sa.ID, "name", sa.GetName())
87+
continue
88+
}
89+
orphanedAfter := sa.CreatedAt.Add(timeUntilSystemAccountOrphaned)
90+
if !time.Now().After(orphanedAfter) {
91+
log.Info("System account is not old enough to be considered orphaned, skipping",
92+
"id", *sa.ID, "name", sa.GetName(), "created_at", *sa.CreatedAt,
93+
)
94+
continue
95+
}
96+
orphanedSystemAccounts = append(orphanedSystemAccounts, *sa.ID)
97+
}
98+
99+
if int64(len(accounts)) < systemAccountsPageSize {
100+
break
101+
}
102+
pageNumber++
103+
}
104+
105+
return orphanedSystemAccounts, nil
106+
}
107+
108+
// deleteSystemAccounts deletes system accounts by their IDs.
109+
func deleteSystemAccounts(
110+
ctx context.Context,
111+
log logr.Logger,
112+
sdk *sdkkonnectgo.SystemAccounts,
113+
saIDs []string,
114+
) error {
115+
if len(saIDs) == 0 {
116+
log.Info("No system accounts to clean up")
117+
return nil
118+
}
119+
120+
var errs []error
121+
for _, saID := range saIDs {
122+
log.Info("Deleting system account", "id", saID)
123+
if _, err := sdk.DeleteSystemAccountsID(ctx, saID); err != nil {
124+
errs = append(errs, fmt.Errorf("failed to delete system account %s: %w", saID, err))
125+
}
126+
}
127+
return errors.Join(errs...)
128+
}

hack/cleanup/main.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,15 @@
1313
// 1. It has a label `created_in_tests` with value `true`.
1414
// 2. It was created more than 1h ago.
1515
//
16+
// A system account is considered orphaned when all conditions are satisfied:
17+
// 1. It is not managed by Konnect.
18+
// 2. It was created more than 1h ago.
19+
//
1620
// Usage: `go run ./hack/cleanup [mode]`
1721
// Where `mode` is one of:
1822
// - `all` (default): clean up both GKE clusters and Konnect control planes
1923
// - `gke`: clean up only GKE clusters
20-
// - `konnect`: clean up only Konnect control planes
24+
// - `konnect`: clean up only Konnect control planes and system accounts
2125
package main
2226

2327
import (
@@ -103,11 +107,13 @@ func resolveCleanupFuncs(mode string) []func(context.Context, logr.Logger) error
103107
case cleanupModeKonnect:
104108
return []func(context.Context, logr.Logger) error{
105109
cleanupKonnectControlPlanes,
110+
cleanupKonnectSystemAccounts,
106111
}
107112
default:
108113
return []func(context.Context, logr.Logger) error{
109114
cleanupGKEClusters,
110115
cleanupKonnectControlPlanes,
116+
cleanupKonnectSystemAccounts,
111117
}
112118
}
113119
}

ingress-controller/internal/konnect/sdk/sdk.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
package sdk
22

33
import (
4+
"strings"
5+
46
sdkkonnectgo "github.com/Kong/sdk-konnect-go"
57
sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components"
8+
"github.com/samber/lo"
69
)
710

811
// New returns a new SDK instance.
912
func New(token string, opts ...sdkkonnectgo.SDKOption) *sdkkonnectgo.SDK {
13+
security := sdkkonnectcomp.Security{}
14+
switch {
15+
case strings.HasPrefix(token, "kpat_"):
16+
security.PersonalAccessToken = lo.ToPtr(token)
17+
case strings.HasPrefix(token, "spat_"):
18+
security.SystemAccountAccessToken = lo.ToPtr(token)
19+
}
1020
sdkOpts := []sdkkonnectgo.SDKOption{
11-
sdkkonnectgo.WithSecurity(
12-
sdkkonnectcomp.Security{
13-
PersonalAccessToken: sdkkonnectgo.String(token),
14-
},
15-
),
21+
sdkkonnectgo.WithSecurity(security),
1622
}
1723
sdkOpts = append(sdkOpts, opts...)
1824

ingress-controller/test/internal/helpers/konnect/certificates.go

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88

99
sdkkonnectgo "github.com/Kong/sdk-konnect-go"
1010
sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components"
11-
"github.com/Kong/sdk-konnect-go/retry"
11+
sdkretry "github.com/Kong/sdk-konnect-go/retry"
1212
"github.com/stretchr/testify/assert"
1313
"github.com/stretchr/testify/require"
1414

@@ -18,24 +18,29 @@ import (
1818

1919
// CreateClientCertificate creates a TLS client certificate and POSTs it to Konnect Control Plane configuration API
2020
// so that KIC can use the certificates to authenticate against Konnect Admin API.
21-
func CreateClientCertificate(ctx context.Context, t *testing.T, cpID string) (certPEM string, keyPEM string) {
21+
func CreateClientCertificate(ctx context.Context, t *testing.T, cpID string, token ...string) (certPEM string, keyPEM string) {
2222
t.Helper()
2323

24-
sdk := sdk.New(accessToken(), serverURLOpt(),
25-
sdkkonnectgo.WithRetryConfig(retry.Config{
26-
Backoff: &retry.BackoffStrategy{
27-
InitialInterval: 100,
28-
MaxInterval: 2000,
29-
Exponent: 1.2,
30-
MaxElapsedTime: 10000,
31-
},
32-
}),
33-
)
24+
retryConfig := sdkkonnectgo.WithRetryConfig(sdkretry.Config{
25+
Backoff: &sdkretry.BackoffStrategy{
26+
InitialInterval: 100,
27+
MaxInterval: 2000,
28+
Exponent: 1.2,
29+
MaxElapsedTime: 10000,
30+
},
31+
})
32+
33+
var s *sdkkonnectgo.SDK
34+
if len(token) == 0 {
35+
s = sdk.New(accessToken(), serverURLOpt(), retryConfig)
36+
} else {
37+
s = sdk.New(token[0], serverURLOpt(), retryConfig)
38+
}
3439

3540
cert, key := certificate.MustGenerateCertPEMFormat()
3641

3742
t.Log("creating client certificate in Konnect")
38-
resp, err := sdk.DPCertificates.CreateDataplaneCertificate(ctx, cpID, &sdkkonnectcomp.DataPlaneClientCertificateRequest{
43+
resp, err := s.DPCertificates.CreateDataplaneCertificate(ctx, cpID, &sdkkonnectcomp.DataPlaneClientCertificateRequest{
3944
Cert: string(cert),
4045
})
4146
require.NoError(t, err)

ingress-controller/test/internal/helpers/konnect/control_plane.go

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import (
99
"testing"
1010
"time"
1111

12+
sdkkonnectgo "github.com/Kong/sdk-konnect-go"
1213
sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components"
1314
"github.com/avast/retry-go/v4"
1415
"github.com/google/uuid"
1516
"github.com/samber/lo"
16-
"github.com/stretchr/testify/assert"
1717
"github.com/stretchr/testify/require"
1818

1919
"github.com/kong/kong-operator/v2/ingress-controller/internal/adminapi"
@@ -25,14 +25,18 @@ import (
2525

2626
// CreateTestControlPlane creates a control plane to be used in tests. It returns the created control plane's ID.
2727
// It also sets up a cleanup function for it to be deleted.
28-
func CreateTestControlPlane(ctx context.Context, t *testing.T) string {
28+
func CreateTestControlPlane(ctx context.Context, t *testing.T, token ...string) string {
2929
t.Helper()
3030

31-
sdk := sdk.New(accessToken(), serverURLOpt())
32-
31+
var s *sdkkonnectgo.SDK
32+
if len(token) == 0 {
33+
s = sdk.New(accessToken(), serverURLOpt())
34+
} else {
35+
s = sdk.New(token[0], serverURLOpt())
36+
}
3337
var cpID string
3438
createRgErr := retry.Do(func() error {
35-
createResp, err := sdk.ControlPlanes.CreateControlPlane(ctx,
39+
createResp, err := s.ControlPlanes.CreateControlPlane(ctx,
3640
sdkkonnectcomp.CreateControlPlaneRequest{
3741
Name: uuid.NewString(),
3842
Description: lo.ToPtr(generateTestKonnectControlPlaneDescription(t)),
@@ -63,20 +67,29 @@ func CreateTestControlPlane(ctx context.Context, t *testing.T) string {
6367

6468
cpID = createResp.ControlPlane.ID
6569
return nil
66-
}, retry.Attempts(5), retry.Delay(time.Second))
70+
},
71+
retry.Attempts(5),
72+
retry.Delay(time.Second),
73+
retry.OnRetry(func(_ uint, err error) {
74+
t.Logf("failed to create control plane, retrying: %v", err)
75+
}),
76+
)
6777
require.NoError(t, createRgErr)
6878

6979
t.Cleanup(func() {
70-
ctx = context.Background()
71-
t.Logf("deleting test Konnect Control Plane: %q", cpID)
80+
fmt.Printf("deleting test Konnect Control Plane: %q\n", cpID)
7281
err := retry.Do(
7382
func() error { //nolint:contextcheck
74-
_, err := sdk.ControlPlanes.DeleteControlPlane(context.Background(), cpID)
83+
_, err := s.ControlPlanes.DeleteControlPlane(context.Background(), cpID)
7584
return err
7685
},
7786
retry.Attempts(5), retry.Delay(time.Second),
7887
)
79-
assert.NoErrorf(t, err, "failed to cleanup a control plane: %q", cpID)
88+
if err != nil {
89+
// Don't fail the test if cleanup fails, just log the error.
90+
// Cleanup job will eventually clean up the control plane.
91+
fmt.Printf("failed to delete control plane %q: %v\n", cpID, err)
92+
}
8093

8194
// Since Konnect authorization v2 supports cleanup of roles after control plane deleted, we do not need to delete them manually.
8295
})

0 commit comments

Comments
 (0)