Skip to content

Commit 58b60e7

Browse files
authored
Merge pull request #69 from hieumoscow/main
Feat: enhance az fleet tool with structured parameters and validation
2 parents 9c44c2c + 2d82b4c commit 58b60e7

File tree

7 files changed

+506
-19
lines changed

7 files changed

+506
-19
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,15 @@ The AKS-MCP server provides the following tools for interacting with AKS cluster
173173
- `get_nsg_info`: Get information about the network security groups used by the AKS cluster
174174
</details>
175175
176+
<details>
177+
<summary>Fleet Tools</summary>
178+
179+
- `az_fleet`: Execute Azure Fleet commands with structured parameters for AKS Fleet management
180+
- Supports operations: list, show, create, update, delete, start, stop
181+
- Supports resources: fleet, member, updaterun, updatestrategy
182+
- Requires readwrite or admin access for write operations
183+
</details>
184+
176185
## Contributing
177186
178187
This project welcomes contributions and suggestions. Most contributions require you to agree to a

internal/azcli/fleet_executor.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package azcli
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/Azure/aks-mcp/internal/config"
8+
)
9+
10+
// FleetExecutor handles structured fleet command execution
11+
type FleetExecutor struct {
12+
*AzExecutor
13+
}
14+
15+
// NewFleetExecutor creates a new fleet command executor
16+
func NewFleetExecutor() *FleetExecutor {
17+
return &FleetExecutor{
18+
AzExecutor: NewExecutor(),
19+
}
20+
}
21+
22+
// Execute processes structured fleet commands
23+
func (e *FleetExecutor) Execute(params map[string]interface{}, cfg *config.ConfigData) (string, error) {
24+
// Extract structured parameters
25+
operation, ok := params["operation"].(string)
26+
if !ok {
27+
return "", fmt.Errorf("operation parameter is required and must be a string")
28+
}
29+
30+
resource, ok := params["resource"].(string)
31+
if !ok {
32+
return "", fmt.Errorf("resource parameter is required and must be a string")
33+
}
34+
35+
args, ok := params["args"].(string)
36+
if !ok {
37+
return "", fmt.Errorf("args parameter is required and must be a string")
38+
}
39+
40+
// Validate operation/resource combination
41+
if err := e.validateCombination(operation, resource); err != nil {
42+
return "", err
43+
}
44+
45+
// Construct the full command
46+
command := fmt.Sprintf("az fleet %s %s", resource, operation)
47+
if operation == "list" && resource == "fleet" {
48+
// Special case: "az fleet list" without resource in between
49+
command = "az fleet list"
50+
}
51+
52+
// Check access level
53+
if err := e.checkAccessLevel(operation, resource, cfg.AccessLevel); err != nil {
54+
return "", err
55+
}
56+
57+
// Build full command with args
58+
fullCommand := command
59+
if args != "" {
60+
fullCommand = fmt.Sprintf("%s %s", command, args)
61+
}
62+
63+
// Create params for the base executor
64+
execParams := map[string]interface{}{
65+
"command": fullCommand,
66+
}
67+
68+
// Execute using the base executor
69+
return e.AzExecutor.Execute(execParams, cfg)
70+
}
71+
72+
// validateCombination validates if the operation/resource combination is valid
73+
func (e *FleetExecutor) validateCombination(operation, resource string) error {
74+
validCombinations := map[string][]string{
75+
"fleet": {"list", "show", "create", "update", "delete"},
76+
"member": {"list", "show", "create", "update", "delete"},
77+
"updaterun": {"list", "show", "create", "start", "stop", "delete"},
78+
"updatestrategy": {"list", "show", "create", "delete"},
79+
}
80+
81+
validOps, exists := validCombinations[resource]
82+
if !exists {
83+
return fmt.Errorf("invalid resource type: %s", resource)
84+
}
85+
86+
for _, validOp := range validOps {
87+
if operation == validOp {
88+
return nil
89+
}
90+
}
91+
92+
return fmt.Errorf("invalid operation '%s' for resource '%s'. Valid operations: %s",
93+
operation, resource, strings.Join(validOps, ", "))
94+
}
95+
96+
// checkAccessLevel ensures the operation is allowed for the current access level
97+
func (e *FleetExecutor) checkAccessLevel(operation, resource string, accessLevel string) error {
98+
// Read-only operations are allowed for all access levels
99+
readOnlyOps := []string{"list", "show"}
100+
for _, op := range readOnlyOps {
101+
if operation == op {
102+
return nil
103+
}
104+
}
105+
106+
// Write operations require readwrite or admin access
107+
if accessLevel == "readonly" {
108+
return fmt.Errorf("operation '%s' requires readwrite or admin access level, current level is readonly", operation)
109+
}
110+
111+
// All operations are allowed for readwrite and admin
112+
return nil
113+
}
114+
115+
// GetCommandForValidation returns the constructed command for security validation
116+
func (e *FleetExecutor) GetCommandForValidation(operation, resource, args string) string {
117+
command := fmt.Sprintf("az fleet %s %s", resource, operation)
118+
if operation == "list" && resource == "fleet" {
119+
command = "az fleet list"
120+
}
121+
122+
if args != "" {
123+
command = fmt.Sprintf("%s %s", command, args)
124+
}
125+
126+
return command
127+
}

0 commit comments

Comments
 (0)