Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4642c5b
feat: event gateway CP, implementation in progress
harshadixit12 Dec 10, 2025
e2147a6
feat: bootstrap event gateway control plane
harshadixit12 Dec 17, 2025
e60c20a
feat: diff and apply for event gateway control plane
harshadixit12 Dec 17, 2025
9c18622
checkpoint: declarative CRUD works for event gateway control plane
harshadixit12 Dec 31, 2025
0879baf
feat: add imperative get command to support listing, get by ID and Na…
harshadixit12 Jan 5, 2026
46a5d1f
fix: protection updates
harshadixit12 Jan 6, 2026
6b399f8
tests: add e2e scenario for event gateway control plane CRUD and prot…
harshadixit12 Jan 6, 2026
98a7a2f
test: update protection test namespace
harshadixit12 Jan 7, 2026
05afccf
fix: UX for imperative get
harshadixit12 Jan 7, 2026
82c2c53
style: fix linting in imperative get
harshadixit12 Jan 7, 2026
b219e14
style: fix remaining linting issues
harshadixit12 Jan 7, 2026
5f24687
test: fix test failures due to missing API client initialisation
harshadixit12 Jan 7, 2026
225b852
fix: rename to event_gateways
harshadixit12 Jan 8, 2026
4642dda
style: update comments and implement copilot feedback
harshadixit12 Jan 8, 2026
f061671
docs: add example for event gateways
harshadixit12 Jan 8, 2026
add897d
Tidy: Adds event gateway to reset org command
rspurgeon Jan 8, 2026
169d4bc
fix: plan command in apply mode for event gateways
harshadixit12 Jan 9, 2026
c33ebb6
style: changes resource type to event_gateway in code
harshadixit12 Jan 9, 2026
87c9060
feat: support dumping event gateways
harshadixit12 Jan 9, 2026
99de1b0
feat: support adopting existing event gateways
harshadixit12 Jan 9, 2026
3f762d5
style: remove unused files and fix lint
harshadixit12 Jan 9, 2026
1515fce
refactor: change imperative get command to drop `control-planes` for …
harshadixit12 Jan 12, 2026
cabd50c
tests: add separate event gateway planning scenarios
harshadixit12 Jan 14, 2026
d6d1a83
tests: revert changes to generic plan workflow
harshadixit12 Jan 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/declarative.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ lists the currently supported resources and their relationships.
- Portals
- Application Auth Strategies
- Control Planes (including Control Plane Groups)
- Event Gateways

**Child Resources** (do NOT support kongctl metadata):

Expand Down
6 changes: 6 additions & 0 deletions docs/examples/declarative/event-gateway/event-gateway.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
event_gateways:
- ref: getting-started-event-gateway
name: "My First Event Gateway"
description: "My first event gateway control plane for managing event-driven architectures"
kongctl:
namespace: "default"
285 changes: 285 additions & 0 deletions internal/cmd/root/products/konnect/adopt/event_gateway.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
package adopt

import (
"fmt"
"net/url"
"strings"

kk "github.com/Kong/sdk-konnect-go"
kkComps "github.com/Kong/sdk-konnect-go/models/components"
kkOps "github.com/Kong/sdk-konnect-go/models/operations"
cmdpkg "github.com/kong/kongctl/internal/cmd"
cmdCommon "github.com/kong/kongctl/internal/cmd/common"
"github.com/kong/kongctl/internal/cmd/root/products/konnect/common"
"github.com/kong/kongctl/internal/cmd/root/verbs"
"github.com/kong/kongctl/internal/config"
"github.com/kong/kongctl/internal/declarative/labels"
"github.com/kong/kongctl/internal/declarative/validator"
"github.com/kong/kongctl/internal/konnect/helpers"
"github.com/kong/kongctl/internal/util"
"github.com/segmentio/cli"
"github.com/spf13/cobra"
)

func NewEventGatewayControlPlaneCmd(
verb verbs.VerbValue,
baseCmd *cobra.Command,
addParentFlags func(verbs.VerbValue, *cobra.Command),
parentPreRun func(*cobra.Command, []string) error,
) (*cobra.Command, error) {
cmd := baseCmd
if cmd == nil {
cmd = &cobra.Command{}
}

cmd.Use = "event-gateway <event-gateway-id|event-gateway-name>"
cmd.Short = "Adopt an existing Konnect Event Gateway Control Plane into namespace management"
cmd.Long = "Apply the KONGCTL-namespace label to an existing Konnect Event Gateway Control Plane " +
"that is not currently managed by kongctl."
cmd.Args = func(_ *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("exactly one event gateway control plane identifier (name or ID) is required")
}
if trimmed := strings.TrimSpace(args[0]); trimmed == "" {
return fmt.Errorf("event gateway control plane identifier cannot be empty")
}
return nil
}

if addParentFlags != nil {
addParentFlags(verb, cmd)
}

if parentPreRun != nil {
cmd.PreRunE = parentPreRun
}

cmd.Flags().String(NamespaceFlagName, "", "Namespace label to apply to the resource")
if err := cmd.MarkFlagRequired(NamespaceFlagName); err != nil {
return nil, err
}

cmd.RunE = func(cobraCmd *cobra.Command, args []string) error {
helper := cmdpkg.BuildHelper(cobraCmd, args)

namespace, err := cobraCmd.Flags().GetString(NamespaceFlagName)
if err != nil {
return err
}

nsValidator := validator.NewNamespaceValidator()
if err := nsValidator.ValidateNamespace(namespace); err != nil {
return &cmdpkg.ConfigurationError{Err: err}
}

outType, err := helper.GetOutputFormat()
if err != nil {
return err
}

cfg, err := helper.GetConfig()
if err != nil {
return err
}

logger, err := helper.GetLogger()
if err != nil {
return err
}

sdk, err := helper.GetKonnectSDK(cfg, logger)
if err != nil {
return err
}

result, err := adoptEventGatewayControlPlane(
helper,
sdk.GetEventGatewayControlPlaneAPI(),
cfg,
namespace,
strings.TrimSpace(args[0]),
)
if err != nil {
return err
}

streams := helper.GetStreams()
if outType == cmdCommon.TEXT {
name := result.Name
if name == "" {
name = result.ID
}
fmt.Fprintf(
streams.Out,
"Adopted Event Gateway Control Plane %q (%s) into namespace %q\n",
name,
result.ID,
result.Namespace,
)
return nil
}

printer, err := cli.Format(outType.String(), streams.Out)
if err != nil {
return err
}
defer printer.Flush()
printer.Print(result)
return nil
}

return cmd, nil
}

func adoptEventGatewayControlPlane(
helper cmdpkg.Helper,
egwClient helpers.EGWControlPlaneAPI,
cfg config.Hook,
namespace string,
identifier string,
) (*adoptResult, error) {
egw, err := resolveEventGatewayControlPlane(helper, egwClient, cfg, identifier)
if err != nil {
return nil, err
}

if existing := egw.Labels; existing != nil {
if currentNamespace, ok := existing[labels.NamespaceKey]; ok && currentNamespace != "" {
return nil, &cmdpkg.ConfigurationError{
Err: fmt.Errorf("event gateway control plane %q already has namespace label %q", egw.Name, currentNamespace),
}
}
}

updateReq := kkComps.UpdateGatewayRequest{
Name: &egw.Name,
Description: egw.Description,
Labels: stringLabelMap(egw.Labels, namespace),
}

ctx := ensureContext(helper.GetContext())

resp, err := egwClient.UpdateEGWControlPlane(ctx, egw.ID, updateReq)
if err != nil {
attrs := cmdpkg.TryConvertErrorToAttrs(err)
return nil, cmdpkg.PrepareExecutionError(
"failed to update Event Gateway Control Plane",
err,
helper.GetCmd(),
attrs...,
)
}

updated := resp.EventGatewayInfo
if updated == nil {
return nil, cmdpkg.PrepareExecutionErrorMsg(helper, "update Event Gateway Control Plane failed")
}

ns := namespace
if updated.Labels != nil {
if v, ok := updated.Labels[labels.NamespaceKey]; ok && v != "" {
ns = v
}
}

return &adoptResult{
ResourceType: "event_gateway",
ID: updated.ID,
Name: updated.Name,
Namespace: ns,
}, nil
}

func resolveEventGatewayControlPlane(
helper cmdpkg.Helper,
egwClient helpers.EGWControlPlaneAPI,
cfg config.Hook,
identifier string,
) (*kkComps.EventGatewayInfo, error) {
ctx := ensureContext(helper.GetContext())

if util.IsValidUUID(identifier) {
res, err := egwClient.FetchEGWControlPlane(ctx, identifier)
if err != nil {
attrs := cmdpkg.TryConvertErrorToAttrs(err)
return nil, cmdpkg.PrepareExecutionError(
"failed to retrieve Event Gateway Control Plane",
err,
helper.GetCmd(),
attrs...,
)
}
egw := res.EventGatewayInfo
if egw == nil {
return nil, cmdpkg.PrepareExecutionErrorMsg(
helper,
fmt.Sprintf("event gateway control plane %s not found", identifier),
)
}
return egw, nil
}

pageSize := cfg.GetInt(common.RequestPageSizeConfigPath)
if pageSize < 1 {
pageSize = common.DefaultRequestPageSize
}

var pageAfter *string
for {
req := kkOps.ListEventGatewaysRequest{
PageSize: kk.Int64(int64(pageSize)),
}

if pageAfter != nil {
req.PageAfter = pageAfter
}

res, err := egwClient.ListEGWControlPlanes(ctx, req)
if err != nil {
attrs := cmdpkg.TryConvertErrorToAttrs(err)
return nil, cmdpkg.PrepareExecutionError(
"failed to list Event Gateway Control Planes",
err,
helper.GetCmd(),
attrs...,
)
}

list := res.ListEventGatewaysResponse
if list == nil || len(list.Data) == 0 {
break
}

for _, egw := range list.Data {
if egw.Name == identifier {
egwCopy := egw
return &egwCopy, nil
}
}

if list.Meta.Page.Next == nil {
break
}

// Page.Next contains a full URL; parse it and extract the cursor from
// the `page[after]` query parameter so we can pass it to the next request.
u, err := url.Parse(*list.Meta.Page.Next)
if err != nil {
attrs := cmdpkg.TryConvertErrorToAttrs(err)
return nil, cmdpkg.PrepareExecutionError("failed to parse pagination URL", err, helper.GetCmd(), attrs...)
}

values := u.Query()
after := values.Get("page[after]")
if after == "" {
break
}
// allocate a new string so the pointer remains valid across iterations
tmp := after
pageAfter = &tmp
}

return nil, &cmdpkg.ConfigurationError{
Err: fmt.Errorf("event gateway control plane %q not found", identifier),
}
}
Loading