Skip to content
Merged
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
35 changes: 35 additions & 0 deletions internal/components/azaks/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/Azure/aks-mcp/internal/command"
"github.com/Azure/aks-mcp/internal/config"
"github.com/Azure/aks-mcp/internal/security"
"github.com/google/shlex"
)

// AksOperationsExecutor handles execution of AKS operations
Expand All @@ -31,6 +32,12 @@ func (e *AksOperationsExecutor) Execute(params map[string]interface{}, cfg *conf
args = ""
}

// Ensure az CLI returns JSON unless caller already specified an output format
args, err := ensureOutputFormatArgs(args)
if err != nil {
return "", err
}

// Validate access for this operation
if err := ValidateOperationAccess(operation, cfg); err != nil {
return "", err
Expand Down Expand Up @@ -91,3 +98,31 @@ func (e *AksOperationsExecutor) ExecuteSpecificCommand(operation string, params

return e.Execute(newParams, cfg)
}

// ensureOutputFormatArgs adds --output json when no explicit output format is provided
func ensureOutputFormatArgs(args string) (string, error) {
if strings.TrimSpace(args) == "" {
return "--output json", nil
}

parsedArgs, err := shlex.Split(args)
if err != nil {
return "", fmt.Errorf("parsing args: %w", err)
}

for _, token := range parsedArgs {
lowerToken := strings.ToLower(token)
switch {
case lowerToken == "--output":
return args, nil
case strings.HasPrefix(lowerToken, "--output="):
return args, nil
case lowerToken == "-o":
return args, nil
case strings.HasPrefix(lowerToken, "-o") && len(lowerToken) > 2:
return args, nil
}
}

return args + " --output json", nil
}
68 changes: 68 additions & 0 deletions internal/components/azaks/executor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package azaks

import "testing"

func TestEnsureOutputFormatArgs(t *testing.T) {
tests := []struct {
name string
input string
want string
wantErr bool
}{
{
name: "empty args",
input: "",
want: "--output json",
},
{
name: "already has long output flag",
input: "--resource-group rg --output table",
want: "--resource-group rg --output table",
},
{
name: "already has long output flag equals",
input: "--output=json --resource-group rg",
want: "--output=json --resource-group rg",
},
{
name: "already has short output flag",
input: "-o json --name test",
want: "-o json --name test",
},
{
name: "already has short compact output flag",
input: "-ojson --name test",
want: "-ojson --name test",
},
{
name: "no output flag appends json",
input: "--name test --resource-group rg",
want: "--name test --resource-group rg --output json",
},
{
name: "malformed args returns error",
input: "--name \"missing-end",
wantErr: true,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got, err := ensureOutputFormatArgs(tc.input)
if tc.wantErr {
if err == nil {
t.Fatalf("expected error, got none")
}
return
}

if err != nil {
t.Fatalf("unexpected error: %v", err)
}

if got != tc.want {
t.Fatalf("got %q, want %q", got, tc.want)
}
})
}
}
21 changes: 21 additions & 0 deletions internal/components/monitor/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,8 @@ func handleMetricsOperation(params map[string]interface{}, cfg *config.ConfigDat
args = append(args, fmt.Sprintf("%v", value))
}

args = ensureOutputArg(args)

// Map query type to command
baseCommand, err := MapMetricsQueryTypeToCommand(queryType)
if err != nil {
Expand Down Expand Up @@ -387,3 +389,22 @@ func handleLogsOperation(params map[string]interface{}, azClient *azureclient.Az
// Use existing control plane logs handler
return diagnostics.GetControlPlaneLogsHandler(azClient, cfg).Handle(mergedParams, cfg)
}

// ensureOutputArg appends --output json when no explicit output flag is present
func ensureOutputArg(args []string) []string {
for _, arg := range args {
lower := strings.ToLower(arg)
switch {
case lower == "--output":
return args
case strings.HasPrefix(lower, "--output="):
return args
case lower == "-o":
return args
case strings.HasPrefix(lower, "-o") && len(lower) > 2:
return args
}
}

return append(args, "--output", "json")
}
48 changes: 48 additions & 0 deletions internal/components/monitor/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,54 @@ import (
"testing"
)

func TestEnsureOutputArg(t *testing.T) {
tests := []struct {
name string
in []string
out []string
}{
{
name: "appends when missing",
in: []string{"--name", "test"},
out: []string{"--name", "test", "--output", "json"},
},
{
name: "no change with long flag",
in: []string{"--name", "test", "--output", "table"},
out: []string{"--name", "test", "--output", "table"},
},
{
name: "no change with equals",
in: []string{"--output=json", "--name", "test"},
out: []string{"--output=json", "--name", "test"},
},
{
name: "no change with short flag",
in: []string{"-o", "table", "--name", "test"},
out: []string{"-o", "table", "--name", "test"},
},
{
name: "no change with combined short flag",
in: []string{"-otable", "--name", "test"},
out: []string{"-otable", "--name", "test"},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
result := ensureOutputArg(tc.in)
if len(result) != len(tc.out) {
t.Fatalf("length mismatch: got %d, want %d", len(result), len(tc.out))
}
for i := range tc.out {
if result[i] != tc.out[i] {
t.Fatalf("index %d mismatch: got %q, want %q", i, result[i], tc.out[i])
}
}
})
}
}

func TestHandleAppInsightsQuery_ValidParameters(t *testing.T) {
params := map[string]interface{}{
"subscription_id": "test-subscription",
Expand Down
Loading