Skip to content

Commit db20519

Browse files
authored
feat: deploy konnect via main deck command (#645)
1 parent 2ff5804 commit db20519

24 files changed

+535
-78
lines changed

cmd/common.go

+82-18
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,30 @@ import (
2020
)
2121

2222
const (
23-
exitCodeDiffDetection = 2
23+
exitCodeDiffDetection = 2
24+
defaultFetchedKongVersion = "2.8.0"
2425
)
2526

2627
var (
2728
dumpConfig dump.Config
2829
assumeYes bool
2930
)
3031

32+
type mode int
33+
34+
const (
35+
modeKonnect = iota
36+
modeKong
37+
modeKongEnterprise
38+
)
39+
40+
func getMode(targetContent *file.Content) mode {
41+
if inKonnectMode(targetContent) {
42+
return modeKonnect
43+
}
44+
return modeKong
45+
}
46+
3147
// workspaceExists checks if workspace exists in Kong.
3248
func workspaceExists(ctx context.Context, config utils.KongClientConfig, workspaceName string) (bool, error) {
3349
rootConfig := config.ForWorkspace("")
@@ -73,6 +89,34 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
7389
if dumpConfig.SkipConsumers {
7490
targetContent.Consumers = []file.FConsumer{}
7591
}
92+
if dumpConfig.SkipCACerts {
93+
targetContent.CACertificates = []file.FCACertificate{}
94+
}
95+
96+
cmd := "sync"
97+
if dry {
98+
cmd = "diff"
99+
}
100+
101+
var kongClient *kong.Client
102+
mode := getMode(targetContent)
103+
if mode == modeKonnect {
104+
if targetContent.Konnect != nil {
105+
if konnectRuntimeGroup != "" &&
106+
targetContent.Konnect.RuntimeGroupName != konnectRuntimeGroup {
107+
return fmt.Errorf("warning: runtime group '%v' specified via "+
108+
"--konnect-runtime-group flag is "+
109+
"different from '%v' found in state file(s)",
110+
konnectRuntimeGroup, targetContent.Konnect.RuntimeGroupName)
111+
}
112+
konnectRuntimeGroup = targetContent.Konnect.RuntimeGroupName
113+
}
114+
kongClient, err = getKonnectClient(ctx)
115+
if err != nil {
116+
return err
117+
}
118+
dumpConfig.KonnectRuntimeGroup = konnectRuntimeGroup
119+
}
76120

77121
rootClient, err := utils.GetKongClient(rootConfig)
78122
if err != nil {
@@ -85,32 +129,34 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
85129
wsConfig = rootConfig.ForWorkspace(workspaceName)
86130

87131
// load Kong version after workspace
88-
kongVersion, err := fetchKongVersion(ctx, wsConfig)
89-
if err != nil {
90-
return fmt.Errorf("reading Kong version: %w", err)
132+
kongVersion := defaultFetchedKongVersion
133+
var parsedKongVersion semver.Version
134+
if mode == modeKong {
135+
kongVersion, err = fetchKongVersion(ctx, wsConfig)
136+
if err != nil {
137+
return fmt.Errorf("reading Kong version: %w", err)
138+
}
91139
}
92-
parsedKongVersion, err := parseKongVersion(kongVersion)
140+
parsedKongVersion, err = parseKongVersion(kongVersion)
93141
if err != nil {
94142
return fmt.Errorf("parsing Kong version: %w", err)
95143
}
96144

97145
// TODO: instead of guessing the cobra command here, move the sendAnalytics
98146
// call to the RunE function. That is not trivial because it requires the
99147
// workspace name and kong client to be present on that level.
100-
cmd := "sync"
101-
if dry {
102-
cmd = "diff"
103-
}
104-
_ = sendAnalytics(cmd, kongVersion)
148+
_ = sendAnalytics(cmd, kongVersion, mode)
105149

106150
workspaceExists, err := workspaceExists(ctx, rootConfig, workspaceName)
107151
if err != nil {
108152
return err
109153
}
110154

111-
wsClient, err := utils.GetKongClient(wsConfig)
112-
if err != nil {
113-
return err
155+
if kongClient == nil {
156+
kongClient, err = utils.GetKongClient(wsConfig)
157+
if err != nil {
158+
return err
159+
}
114160
}
115161

116162
dumpConfig.SelectorTags, err = determineSelectorTag(*targetContent, dumpConfig)
@@ -121,7 +167,7 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
121167
// read the current state
122168
var currentState *state.KongState
123169
if workspaceExists {
124-
currentState, err = fetchCurrentState(ctx, wsClient, dumpConfig)
170+
currentState, err = fetchCurrentState(ctx, kongClient, dumpConfig)
125171
if err != nil {
126172
return err
127173
}
@@ -145,7 +191,7 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
145191
rawState, err := file.Get(ctx, targetContent, file.RenderConfig{
146192
CurrentState: currentState,
147193
KongVersion: parsedKongVersion,
148-
}, dumpConfig, wsClient)
194+
}, dumpConfig, kongClient)
149195
if err != nil {
150196
return err
151197
}
@@ -157,7 +203,7 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
157203
return err
158204
}
159205

160-
totalOps, err := performDiff(ctx, currentState, targetState, dry, parallelism, delay, wsClient)
206+
totalOps, err := performDiff(ctx, currentState, targetState, dry, parallelism, delay, kongClient)
161207
if err != nil {
162208
return err
163209
}
@@ -310,9 +356,27 @@ func containsRBACConfiguration(content utils.KongRawState) bool {
310356
return len(content.RBACRoles) != 0
311357
}
312358

313-
func sendAnalytics(cmd, kongVersion string) error {
359+
func sendAnalytics(cmd, kongVersion string, mode mode) error {
314360
if disableAnalytics {
315361
return nil
316362
}
317-
return utils.SendAnalytics(cmd, VERSION, kongVersion)
363+
var modeStr string
364+
switch mode {
365+
case modeKong:
366+
modeStr = "kong"
367+
case modeKonnect:
368+
modeStr = "konnect"
369+
case modeKongEnterprise:
370+
modeStr = "enterprise"
371+
}
372+
return utils.SendAnalytics(cmd, VERSION, kongVersion, modeStr)
373+
}
374+
375+
func inKonnectMode(targetContent *file.Content) bool {
376+
if (targetContent != nil && targetContent.Konnect != nil) ||
377+
konnectConfig.Email != "" ||
378+
konnectConfig.Password != "" {
379+
return true
380+
}
381+
return false
318382
}

cmd/common_konnect.go

+121
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package cmd
33
import (
44
"context"
55
"fmt"
6+
"net/url"
67
"os"
8+
"strings"
79

810
"github.com/kong/deck/diff"
911
"github.com/kong/deck/dump"
@@ -15,6 +17,107 @@ import (
1517
"golang.org/x/sync/errgroup"
1618
)
1719

20+
const (
21+
defaultRuntimeGroupName = "default"
22+
konnectWithRuntimeGroupsDomain = "api.konghq"
23+
)
24+
25+
func authenticate(ctx context.Context, client *konnect.Client, host string) (konnect.AuthResponse, error) {
26+
if strings.Contains(host, konnectWithRuntimeGroupsDomain) {
27+
return client.Auth.LoginV2(ctx, konnectConfig.Email,
28+
konnectConfig.Password)
29+
}
30+
return client.Auth.Login(ctx, konnectConfig.Email,
31+
konnectConfig.Password)
32+
}
33+
34+
func getKonnectClient(ctx context.Context) (*kong.Client, error) {
35+
httpClient := utils.HTTPClient()
36+
// get Konnect client
37+
konnectClient, err := utils.GetKonnectClient(httpClient, konnectConfig)
38+
if err != nil {
39+
return nil, err
40+
}
41+
42+
var address string
43+
u, _ := url.Parse(konnectConfig.Address)
44+
// authenticate with konnect
45+
if _, err := authenticate(ctx, konnectClient, u.Host); err != nil {
46+
return nil, fmt.Errorf("authenticating with Konnect: %w", err)
47+
}
48+
if strings.Contains(u.Host, konnectWithRuntimeGroupsDomain) {
49+
// get kong runtime group ID
50+
kongRGID, err := fetchKongRuntimeGroupID(ctx, konnectClient)
51+
if err != nil {
52+
return nil, err
53+
}
54+
55+
// set the kong runtime group ID in the client
56+
konnectClient.SetRuntimeGroupID(kongRGID)
57+
address = konnectConfig.Address + "/konnect-api/api/runtime_groups/" + kongRGID
58+
} else {
59+
// get kong control plane ID
60+
kongCPID, err := fetchKongControlPlaneID(ctx, konnectClient)
61+
if err != nil {
62+
return nil, err
63+
}
64+
65+
// set the kong control plane ID in the client
66+
konnectClient.SetControlPlaneID(kongCPID)
67+
address = konnectConfig.Address + "/api/control_planes/" + kongCPID
68+
}
69+
// initialize kong client
70+
return utils.GetKongClient(utils.KongClientConfig{
71+
Address: address,
72+
HTTPClient: httpClient,
73+
Debug: konnectConfig.Debug,
74+
})
75+
}
76+
77+
func resetKonnectV2(ctx context.Context) error {
78+
client, err := getKonnectClient(ctx)
79+
if err != nil {
80+
return err
81+
}
82+
currentState, err := fetchCurrentState(ctx, client, dumpConfig)
83+
if err != nil {
84+
return err
85+
}
86+
targetState, err := state.NewKongState()
87+
if err != nil {
88+
return err
89+
}
90+
_, err = performDiff(ctx, currentState, targetState, false, 10, 0, client)
91+
if err != nil {
92+
return err
93+
}
94+
return nil
95+
}
96+
97+
func dumpKonnectV2(ctx context.Context) error {
98+
client, err := getKonnectClient(ctx)
99+
if err != nil {
100+
return err
101+
}
102+
if dumpCmdKongStateFile == "-" {
103+
return fmt.Errorf("writing to stdout is not supported in Konnect mode")
104+
}
105+
rawState, err := dump.Get(ctx, client, dumpConfig)
106+
if err != nil {
107+
return fmt.Errorf("reading configuration from Kong: %w", err)
108+
}
109+
ks, err := state.Get(rawState)
110+
if err != nil {
111+
return fmt.Errorf("building state: %w", err)
112+
}
113+
return file.KongStateToFile(ks, file.WriteConfig{
114+
Filename: dumpCmdKongStateFile,
115+
FileFormat: file.Format(strings.ToUpper(konnectDumpCmdStateFormat)),
116+
WithID: dumpWithID,
117+
RuntimeGroupName: konnectRuntimeGroup,
118+
})
119+
}
120+
18121
func syncKonnect(ctx context.Context,
19122
filenames []string, dry bool, parallelism int,
20123
) error {
@@ -119,6 +222,24 @@ func fetchKongControlPlaneID(ctx context.Context,
119222
return singleOutKongCP(controlPlanes)
120223
}
121224

225+
func fetchKongRuntimeGroupID(ctx context.Context,
226+
client *konnect.Client,
227+
) (string, error) {
228+
runtimeGroups, _, err := client.RuntimeGroups.List(ctx, nil)
229+
if err != nil {
230+
return "", fmt.Errorf("fetching runtime groups: %w", err)
231+
}
232+
if konnectRuntimeGroup == "" {
233+
konnectRuntimeGroup = defaultRuntimeGroupName
234+
}
235+
for _, rg := range runtimeGroups {
236+
if *rg.Name == konnectRuntimeGroup {
237+
return *rg.ID, nil
238+
}
239+
}
240+
return "", fmt.Errorf("runtime groups not found: %s", konnectRuntimeGroup)
241+
}
242+
122243
func singleOutKongCP(controlPlanes []konnect.ControlPlane) (string, error) {
123244
kongCPCount := 0
124245
kongCPID := ""

cmd/dump.go

+12-7
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,17 @@ configure Kong.`,
4848
RunE: func(cmd *cobra.Command, args []string) error {
4949
ctx := cmd.Context()
5050

51+
if yes, err := utils.ConfirmFileOverwrite(dumpCmdKongStateFile, dumpCmdStateFormat, assumeYes); err != nil {
52+
return err
53+
} else if !yes {
54+
return nil
55+
}
56+
57+
if inKonnectMode(nil) {
58+
_ = sendAnalytics("dump", "", modeKonnect)
59+
return dumpKonnectV2(ctx)
60+
}
61+
5162
wsClient, err := utils.GetKongClient(rootConfig)
5263
if err != nil {
5364
return err
@@ -59,7 +70,7 @@ configure Kong.`,
5970
if err != nil {
6071
return fmt.Errorf("reading Kong version: %w", err)
6172
}
62-
_ = sendAnalytics("dump", kongVersion)
73+
_ = sendAnalytics("dump", kongVersion, modeKong)
6374

6475
// Kong Enterprise dump all workspace
6576
if dumpAllWorkspaces {
@@ -102,12 +113,6 @@ configure Kong.`,
102113
return nil
103114
}
104115

105-
if yes, err := utils.ConfirmFileOverwrite(dumpCmdKongStateFile, dumpCmdStateFormat, assumeYes); err != nil {
106-
return err
107-
} else if !yes {
108-
return nil
109-
}
110-
111116
// Kong OSS
112117
// or Kong Enterprise single workspace
113118
if dumpWorkspace != "" {

0 commit comments

Comments
 (0)