Skip to content

Commit 6f5a769

Browse files
committed
feat(apply): add deck gateway apply command
The `deck gateway apply` command allows you to apply partial configuration to a running Gateway instance. To do this, it runs a `sync` with the NoDeletes flag enabled. This means that only new and existing resources are updated. Existing resources that do not exist in the declarative configuration file are left untouched.
1 parent caaf75c commit 6f5a769

File tree

7 files changed

+89
-22
lines changed

7 files changed

+89
-22
lines changed

cmd/common.go

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ func RemoveConsumerPlugins(targetContentPlugins []file.FPlugin) []file.FPlugin {
131131
}
132132

133133
func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
134-
delay int, workspace string, enableJSONOutput bool,
134+
delay int, workspace string, enableJSONOutput bool, noDeletes bool,
135135
) error {
136136
// read target file
137137
if enableJSONOutput {
@@ -157,6 +157,13 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
157157
}
158158

159159
cmd := "sync"
160+
161+
isPartialApply := false
162+
if noDeletes {
163+
cmd = "apply"
164+
isPartialApply = true
165+
}
166+
160167
if dry {
161168
cmd = "diff"
162169
}
@@ -248,21 +255,24 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
248255
return err
249256
}
250257

251-
dumpConfig.LookUpSelectorTagsConsumerGroups, err = determineLookUpSelectorTagsConsumerGroups(*targetContent)
252-
if err != nil {
253-
return fmt.Errorf("error determining lookup selector tags for consumer groups: %w", err)
254-
}
255-
256-
if dumpConfig.LookUpSelectorTagsConsumerGroups != nil {
257-
consumerGroupsGlobal, err := dump.GetAllConsumerGroups(ctx, kongClient, dumpConfig.LookUpSelectorTagsConsumerGroups)
258+
// Consumer groups are a 3.4+ feature
259+
if parsedKongVersion.GTE(reconcilerUtils.Kong340Version) {
260+
dumpConfig.LookUpSelectorTagsConsumerGroups, err = determineLookUpSelectorTagsConsumerGroups(*targetContent)
258261
if err != nil {
259-
return fmt.Errorf("error retrieving global consumer groups via lookup selector tags: %w", err)
262+
return fmt.Errorf("error determining lookup selector tags for consumer groups: %w", err)
260263
}
261-
for _, c := range consumerGroupsGlobal {
262-
targetContent.ConsumerGroups = append(targetContent.ConsumerGroups,
263-
file.FConsumerGroupObject{ConsumerGroup: *c.ConsumerGroup})
264+
265+
if dumpConfig.LookUpSelectorTagsConsumerGroups != nil || isPartialApply {
266+
consumerGroupsGlobal, err := dump.GetAllConsumerGroups(ctx, kongClient, dumpConfig.LookUpSelectorTagsConsumerGroups)
264267
if err != nil {
265-
return fmt.Errorf("error adding global consumer group %v: %w", *c.ConsumerGroup.Name, err)
268+
return fmt.Errorf("error retrieving global consumer groups via lookup selector tags: %w", err)
269+
}
270+
for _, c := range consumerGroupsGlobal {
271+
targetContent.ConsumerGroups = append(targetContent.ConsumerGroups,
272+
file.FConsumerGroupObject{ConsumerGroup: *c.ConsumerGroup})
273+
if err != nil {
274+
return fmt.Errorf("error adding global consumer group %v: %w", *c.ConsumerGroup.Name, err)
275+
}
266276
}
267277
}
268278
}
@@ -272,7 +282,7 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
272282
return fmt.Errorf("error determining lookup selector tags for consumers: %w", err)
273283
}
274284

275-
if dumpConfig.LookUpSelectorTagsConsumers != nil {
285+
if dumpConfig.LookUpSelectorTagsConsumers != nil || isPartialApply {
276286
consumersGlobal, err := dump.GetAllConsumers(ctx, kongClient, dumpConfig.LookUpSelectorTagsConsumers)
277287
if err != nil {
278288
return fmt.Errorf("error retrieving global consumers via lookup selector tags: %w", err)
@@ -290,7 +300,7 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
290300
return fmt.Errorf("error determining lookup selector tags for routes: %w", err)
291301
}
292302

293-
if dumpConfig.LookUpSelectorTagsRoutes != nil {
303+
if dumpConfig.LookUpSelectorTagsRoutes != nil || isPartialApply {
294304
routesGlobal, err := dump.GetAllRoutes(ctx, kongClient, dumpConfig.LookUpSelectorTagsRoutes)
295305
if err != nil {
296306
return fmt.Errorf("error retrieving global routes via lookup selector tags: %w", err)
@@ -308,7 +318,7 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
308318
return fmt.Errorf("error determining lookup selector tags for services: %w", err)
309319
}
310320

311-
if dumpConfig.LookUpSelectorTagsServices != nil {
321+
if dumpConfig.LookUpSelectorTagsServices != nil || isPartialApply {
312322
servicesGlobal, err := dump.GetAllServices(ctx, kongClient, dumpConfig.LookUpSelectorTagsServices)
313323
if err != nil {
314324
return fmt.Errorf("error retrieving global services via lookup selector tags: %w", err)
@@ -373,7 +383,7 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
373383
}
374384

375385
totalOps, err := performDiff(
376-
ctx, currentState, targetState, dry, parallelism, delay, kongClient, mode == modeKonnect, enableJSONOutput)
386+
ctx, currentState, targetState, dry, parallelism, delay, kongClient, mode == modeKonnect, enableJSONOutput, noDeletes)
377387
if err != nil {
378388
if enableJSONOutput {
379389
var errs reconcilerUtils.ErrArray
@@ -502,7 +512,7 @@ func fetchCurrentState(ctx context.Context, client *kong.Client, dumpConfig dump
502512

503513
func performDiff(ctx context.Context, currentState, targetState *state.KongState,
504514
dry bool, parallelism int, delay int, client *kong.Client, isKonnect bool,
505-
enableJSONOutput bool,
515+
enableJSONOutput bool, noDeletes bool,
506516
) (int, error) {
507517
s, err := diff.NewSyncer(diff.SyncerOpts{
508518
CurrentState: currentState,
@@ -511,6 +521,7 @@ func performDiff(ctx context.Context, currentState, targetState *state.KongState
511521
StageDelaySec: delay,
512522
NoMaskValues: noMaskValues,
513523
IsKonnect: isKonnect,
524+
NoDeletes: noDeletes,
514525
})
515526
if err != nil {
516527
return 0, err

cmd/common_konnect.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ func resetKonnectV2(ctx context.Context) error {
127127
if err != nil {
128128
return err
129129
}
130-
_, err = performDiff(ctx, currentState, targetState, false, 10, 0, client, true, resetJSONOutput)
130+
_, err = performDiff(ctx, currentState, targetState, false, 10, 0, client, true, resetJSONOutput, false)
131131
if err != nil {
132132
return err
133133
}

cmd/gateway_apply.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package cmd
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
)
6+
7+
var (
8+
applyCmdParallelism int
9+
applyCmdDBUpdateDelay int
10+
applyWorkspace string
11+
applyJSONOutput bool
12+
)
13+
14+
var applyCmdKongStateFile []string
15+
16+
func executeApply(cmd *cobra.Command, _ []string) error {
17+
return syncMain(cmd.Context(), applyCmdKongStateFile, false,
18+
applyCmdParallelism, applyCmdDBUpdateDelay, applyWorkspace, applyJSONOutput, true)
19+
}
20+
21+
func newApplyCmd() *cobra.Command {
22+
short := "Apply configuration to Kong without deleting existing entities"
23+
execute := executeApply
24+
25+
applyCmd := &cobra.Command{
26+
Use: "apply [flags] [kong-state-files...]",
27+
Short: short,
28+
Long: `The apply command allows you to apply partial Kong configuration files without deleting existing entities.`,
29+
Args: cobra.MinimumNArgs(0),
30+
RunE: execute,
31+
PreRunE: func(_ *cobra.Command, args []string) error {
32+
applyCmdKongStateFile = args
33+
if len(applyCmdKongStateFile) == 0 {
34+
applyCmdKongStateFile = []string{"-"}
35+
}
36+
return preRunSilenceEventsFlag()
37+
},
38+
}
39+
40+
applyCmd.Flags().StringVarP(&applyWorkspace, "workspace", "w", "",
41+
"Apply configuration to a specific workspace "+
42+
"(Kong Enterprise only).\n"+
43+
"This takes precedence over _workspace fields in state files.")
44+
applyCmd.Flags().IntVar(&applyCmdParallelism, "parallelism",
45+
10, "Maximum number of concurrent operations.")
46+
applyCmd.Flags().IntVar(&applyCmdDBUpdateDelay, "db-update-propagation-delay",
47+
0, "artificial delay (in seconds) that is injected between insert operations \n"+
48+
"for related entities (usually for Cassandra deployments).\n"+
49+
"See `db_update_propagation` in kong.conf.")
50+
applyCmd.Flags().BoolVar(&syncJSONOutput, "json-output",
51+
false, "generate command execution report in a JSON format")
52+
addSilenceEventsFlag(applyCmd.Flags())
53+
54+
return applyCmd
55+
}

cmd/gateway_diff.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ var (
1717

1818
func executeDiff(cmd *cobra.Command, _ []string) error {
1919
return syncMain(cmd.Context(), diffCmdKongStateFile, true,
20-
diffCmdParallelism, 0, diffWorkspace, diffJSONOutput)
20+
diffCmdParallelism, 0, diffWorkspace, diffJSONOutput, false)
2121
}
2222

2323
// newDiffCmd represents the diff command

cmd/gateway_reset.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ func executeReset(cmd *cobra.Command, _ []string) error {
9797
if err != nil {
9898
return err
9999
}
100-
_, err = performDiff(ctx, currentState, targetState, false, 10, 0, wsClient, false, resetJSONOutput)
100+
_, err = performDiff(ctx, currentState, targetState, false, 10, 0, wsClient, false, resetJSONOutput, false)
101101
if err != nil {
102102
return err
103103
}

cmd/gateway_sync.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ var syncCmdKongStateFile []string
1818

1919
func executeSync(cmd *cobra.Command, _ []string) error {
2020
return syncMain(cmd.Context(), syncCmdKongStateFile, false,
21-
syncCmdParallelism, syncCmdDBUpdateDelay, syncWorkspace, syncJSONOutput)
21+
syncCmdParallelism, syncCmdDBUpdateDelay, syncWorkspace, syncJSONOutput, false)
2222
}
2323

2424
// newSyncCmd represents the sync command

cmd/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ It can be used to export, import, or sync entities to Kong.`,
237237
gatewayCmd.AddCommand(newPingCmd(false))
238238
gatewayCmd.AddCommand(newDumpCmd(false))
239239
gatewayCmd.AddCommand(newDiffCmd(false))
240+
gatewayCmd.AddCommand(newApplyCmd())
240241
}
241242
{
242243
fileCmd := newFileSubCmd()

0 commit comments

Comments
 (0)