Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
47 changes: 47 additions & 0 deletions internal/cmd/root/mesh_command_paths_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package root

import (
"strings"
"testing"
)

// Mesh is reachable both directly and under the explicit product path. The
// constructors promise both, but only the direct form was registered, so
// `kongctl get konnect mesh` failed with "unknown command". These assert the
// real command paths rather than the constructors.
func TestMeshCommandPathsResolve(t *testing.T) {
// Every verb Kong Mesh serves here. `apply konnect` and `delete konnect`
// are replaced by the declarative commands and take their own arguments,
// so those two are served by the direct form only.
paths := [][]string{
{"apply", "mesh", "--help"},
{"get", "mesh", "--help"},
{"get", "konnect", "mesh", "--help"},
{"create", "mesh", "--help"},
{"create", "konnect", "mesh", "--help"},
{"delete", "mesh", "--help"},
}

for _, args := range paths {
path := strings.Join(args[:len(args)-1], " ")

t.Run(path, func(t *testing.T) {
result := executeRootForTest(t, args...)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add request-level command tests and dedicated Mesh E2E scenarios.

The additional tests are useful, but these command-path tests execute only --help, which bypasses the bindings and requests that currently fail on the explicit Konnect path. Selector tests exercise flag detection rather than the combined URL/authentication decision. TestMeshOptionPrecedence also claims environment coverage but contains no environment-variable case.

Add complete command/HTTP tests for direct and explicit paths, selector/authentication combinations, file/environment/flag precedence, pagination, TLS credential isolation, and write behavior. Add isolated Mesh discovery/read, apply/get/delete, and token E2E scenarios with cleanup, and run them through trusted E2E. There are still no Mesh scenarios in this PR; passing smoke/version does not validate the feature.


if result.exitCode != 0 {
t.Fatalf("expected %q to succeed\nstdout:\n%s\nstderr:\n%s",
path, result.stdout, result.stderr)
}
if strings.Contains(result.stderr, "unknown command") {
t.Fatalf("expected %q to be registered\nstderr:\n%s", path, result.stderr)
}
// The mesh command's own help, not a parent's help that merely
// lists it: a swallowed argument prints the parent's help and
// still exits zero.
if !strings.Contains(result.stdout, "Kong Mesh control plane") {
t.Fatalf("expected %q help to describe the mesh command\nstdout:\n%s",
path, result.stdout)
}
})
}
}
42 changes: 42 additions & 0 deletions internal/cmd/root/products/konnect/konnect.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package konnect
import (
"context"
"fmt"
"slices"

cmdpkg "github.com/kong/kongctl/internal/cmd"
commoncmd "github.com/kong/kongctl/internal/cmd/common"
Expand All @@ -19,6 +20,7 @@ import (
"github.com/kong/kongctl/internal/cmd/root/products/konnect/eventgateway"
"github.com/kong/kongctl/internal/cmd/root/products/konnect/gateway"
"github.com/kong/kongctl/internal/cmd/root/products/konnect/me"
"github.com/kong/kongctl/internal/cmd/root/products/konnect/mesh"
"github.com/kong/kongctl/internal/cmd/root/products/konnect/organization"
"github.com/kong/kongctl/internal/cmd/root/products/konnect/portal"
"github.com/kong/kongctl/internal/cmd/root/products/konnect/regions"
Expand Down Expand Up @@ -201,6 +203,39 @@ func preRunE(c *cobra.Command, args []string) error {
return bindFlags(c, args)
}

// meshVerbs are the verbs Kong Mesh serves under the explicit product path.
var meshVerbs = []verbs.VerbValue{verbs.Get, verbs.Create, verbs.Dump}

// addMeshCommand registers Kong Mesh under the explicit product path, giving
// `kongctl <verb> konnect mesh ...` alongside the direct `kongctl <verb> mesh
// ...` form that the root command registers.
//
// Mesh serves a subset of the verbs, so an unsupported verb registers nothing
// rather than adding a command that cannot run.
//
// Apply and delete are absent deliberately. `apply konnect` and `delete
// konnect` are replaced by the declarative commands, which take their own
// arguments, so a `mesh` subcommand there is read as one of them instead of
// dispatching. Both are served by the direct `apply mesh` and `delete mesh`
// forms.
func addMeshCommand(
cmd *cobra.Command,
verb verbs.VerbValue,
addParentFlags func(verbs.VerbValue, *cobra.Command),
parentPreRun func(*cobra.Command, []string) error,
) error {
if !slices.Contains(meshVerbs, verb) {
return nil
}

meshCmd, err := mesh.NewMeshCmd(verb, addParentFlags, parentPreRun)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium: bind Mesh flags on the explicit Konnect command path.

This passes the general Konnect pre-run handler, which does not call meshcommon.BindFlags. Registering the command makes help work, but the actual Mesh flags do not reach configuration.

Reproduced:
get konnect mesh meshes --control-plane-url <local-server>
fails with no Kong Mesh control plane selected, while the direct get mesh form works. With existing configuration, ignored flags can instead leave the command using configured defaults.

Compose the Konnect and Mesh bindings for this path, including token subcommands. Test actual requests through both direct and explicit command trees, verifying that selectors and command-specific settings take effect.

if err != nil {
return err
}
cmd.AddCommand(meshCmd)
return nil
}

func NewKonnectCmd(verb verbs.VerbValue) (*cobra.Command, error) {
cmd := &cobra.Command{
Use: konnectUse,
Expand Down Expand Up @@ -234,6 +269,9 @@ func NewKonnectCmd(verb verbs.VerbValue) (*cobra.Command, error) {
if err := addTokenCommands(cmd, verb, addFlags, preRunE); err != nil {
return nil, err
}
if err := addMeshCommand(cmd, verb, addFlags, preRunE); err != nil {
return nil, err
}
addFlags(verb, cmd)
return cmd, nil
}
Expand Down Expand Up @@ -420,6 +458,10 @@ func NewKonnectCmd(verb verbs.VerbValue) (*cobra.Command, error) {
}
cmd.AddCommand(egcpc)

if err := addMeshCommand(cmd, verb, addFlags, preRunE); err != nil {
return nil, err
}

if verb == verbs.Get {
cmd.RunE = func(c *cobra.Command, args []string) error {
helper := cmdpkg.BuildHelper(c, args)
Expand Down
Loading
Loading