GreenNode CLI (grn) is a unified command-line tool for managing GreenNode (VNG Cloud) services. Written in Go, distributed as a single binary. VKS (VNG Kubernetes Service) is the first service; other product teams add their own services.
- Repo:
vngcloud/greennode-cli - Docs: https://vngcloud.github.io/greennode-cli/
- Language: Go (using cobra CLI framework)
- Binary: Single file, zero runtime dependencies
go/
├── main.go # Entry point
├── cmd/
│ ├── root.go # Root command + global flags + --version
│ ├── configure/
│ │ ├── configure.go # Interactive setup
│ │ ├── list.go # grn configure list
│ │ ├── get.go # grn configure get
│ │ └── set.go # grn configure set
│ └── vks/
│ ├── vks.go # VKS parent command
│ ├── helpers.go # Shared utilities (client, output, parsing)
│ ├── list_clusters.go # Auto-pagination
│ ├── get_cluster.go
│ ├── create_cluster.go # --dry-run validation
│ ├── update_cluster.go
│ ├── delete_cluster.go # Confirm + --force + --dry-run
│ ├── list_nodegroups.go
│ ├── get_nodegroup.go
│ ├── create_nodegroup.go
│ ├── update_nodegroup.go
│ ├── delete_nodegroup.go
│ ├── wait_cluster_active.go # Polling waiter
│ └── auto_upgrade.go # Set/delete auto-upgrade
├── internal/
│ ├── config/
│ │ ├── config.go # Config + credentials loading (INI)
│ │ └── writer.go # ConfigFileWriter (0600 perms)
│ ├── auth/token.go # OAuth2 Client Credentials (IAM)
│ ├── client/client.go # HTTP client with retry + auto-refresh
│ ├── formatter/formatter.go # JSON/Table/Text + JMESPath
│ └── validator/validator.go # ID format validation
├── go.mod, go.sum
- All source code text in English
- Use cobra for all commands
- Internal packages in
internal/(not importable externally) - Commands in
cmd/following cobra patterns - Use
cobra.CommandwithRunEfor error handling
- IAM API uses camelCase:
grantType,accessToken,expiresIn - VKS API pagination is 0-based: page 0 = first page
--versionconflict: Use--k8s-versionfor Kubernetes version
- Create file in
cmd/vks/<command_name>.go - Define
cobra.Commandwith Use, Short, RunE - Add flags via
cmd.Flags() - Register in
cmd/vks/vks.goinit():VksCmd.AddCommand(newCmd) - Add
validator.ValidateID()for any ID args in URLs - Add
--dry-runfor create/update/delete commands - Add
--force+ confirmation for delete commands
- Create
cmd/<service>/directory - Create parent command file with
cobra.Command - Register in
cmd/root.goinit():rootCmd.AddCommand(serviceCmd)
- Credential masking:
configure listandconfigure getmask client_id/client_secret (last 4 chars only) - Credential env vars supported:
GRN_ACCESS_KEY_ID/GRN_SECRET_ACCESS_KEYoverride credentials file (highest priority) - Input validation: All cluster-id/nodegroup-id validated via
validator.ValidateID()before URLs - SSL default on:
--no-verify-sslprints warning to stderr - Tokens in memory only: Never written to disk or logged
- File permissions: Credentials file created with 0600, directory 0700
cd go
CGO_ENABLED=0 go build -o grn .
# Cross-compile
GOOS=linux GOARCH=amd64 go build -o grn-linux-amd64 .
GOOS=darwin GOARCH=arm64 go build -o grn-darwin-arm64 .
GOOS=windows GOARCH=amd64 go build -o grn-windows-amd64.exe .- Do not auto commit/push — only change source code, user will ask for commit/push
- Main branch is protected — must use PR
- Changelog:
./scripts/new-changefor every change - Release:
./scripts/bump-version minor→git push && git push --tags
After ANY change to business logic, security, configuration, or commands:
- Review ALL docs below and update what's affected
- If unsure whether a doc needs updating, read it and check
Docs to check:
docs/commands/vks/(GitHub Pages) — add/update command reference page, checkindex.mdtablemkdocs.yml— add nav entry for any new command pageREADME.mdCLAUDE.mdCONTRIBUTING.mddocs/DEVELOPMENT.md./scripts/new-change— changelog fragment
Examples:
- Added a command → create
docs/commands/vks/<command>.md+ add todocs/commands/vks/index.mdtable + add tomkdocs.ymlnav - Removed a command → delete doc page + remove from
index.md+ remove frommkdocs.yml - Changed flags or output → update the command's doc page
- Changed auth/credentials → update README config section + CLAUDE.md security rules
- Changed project structure → update README structure + CLAUDE.md repository structure
Code without docs is not done.
| File | Purpose |
|---|---|
cmd/root.go |
Root command, global flags, --version |
cmd/vks/helpers.go |
Client creation, output formatting, label/taint parsing |
internal/config/config.go |
Config loading from ~/.greenode/, REGIONS map |
internal/config/writer.go |
INI file writer with 0600 perms |
internal/auth/token.go |
TokenManager — OAuth2 with IAM (camelCase) |
internal/client/client.go |
HTTP client with retry (3x backoff) + 401 refresh |
internal/formatter/formatter.go |
JSON/Table/Text + JMESPath |
internal/validator/validator.go |
ID format validation |