Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5fbfc8e
fix(lint): clear golangci-lint leftover issues in internal/api
cristim Jun 20, 2026
1dcb63f
fix(lint): fix godot/misspell/fieldalignment/bodyclose/errcheck/httpN…
cristim Jun 19, 2026
ecb5643
fix(lint): resolve golangci-lint v2.10.1 issues in internal/secrets a…
cristim Jun 19, 2026
8841c1c
fix(lint): resolve all golangci-lint v2 issues in internal/{config,se…
cristim Jun 19, 2026
b65b9ed
fix(lint): fix all golangci-lint leftover issues in cmd/, internal/au…
cristim Jun 19, 2026
a28457a
fix(lint): resolve golangci-lint issues in providers/aws, providers/g…
cristim Jun 19, 2026
b52adf5
fix(pkg): resolve all golangci-lint issues in pkg/{errors,provider,lo…
cristim Jun 19, 2026
784ec39
fix(lint): resolve golangci-lint issues in g8 packages
cristim Jun 19, 2026
7861e64
fix(lint): fix remaining fieldalignment issues in internal/api
cristim Jun 20, 2026
cba9d10
fix(lint): reorder fakeRecsLister fields to satisfy fieldalignment
cristim Jun 20, 2026
f845dc3
fix(lint): remove all nolints from internal/api and handle root causes
cristim Jun 20, 2026
49545fb
fix(lint/azure): remove all nolint directives from providers/azure
cristim Jun 20, 2026
25db769
fix(lint): replace all nolint directives in cmd/, internal/auth/, int…
cristim Jun 20, 2026
5c7964a
fix(lint): remove all //nolint directives from providers/aws, provide…
cristim Jun 21, 2026
4c498d0
fix(lint): remove all //nolint directives in root-module packages (no…
cristim Jun 20, 2026
a8ae57f
fix(lint): resolve gocritic/fieldalignment issues in ri-exchange refa…
cristim Jun 20, 2026
d5908a0
test: update mocks to pointer signatures after B's interface cascade
cristim Jun 21, 2026
b2a766d
fix(lint): address CR #1276 nil guards, SSL mode, status code, and ca…
cristim Jun 24, 2026
cb3de34
fix(lint): graceful nil handling for exported funcs + full cancelled …
cristim Jun 24, 2026
89b375b
fix(lint): flow persisted cancel status to responses + cap nil reserv…
cristim Jun 24, 2026
a8110da
test(api): make scheduled-revoke mock explicit + deterministic reserv…
cristim Jun 24, 2026
bb85fc6
fix(test): align GetExecutionByID twin test with (nil, nil) contract
cristim Jun 26, 2026
f4d6a21
fix(rebase): post-rebase build fixups after rebasing onto origin/main
cristim Jul 3, 2026
985f55e
fix(server): suppress G705 gosec on server-generated response body write
cristim Jul 3, 2026
338c5ad
test(integration): align GetExecutionByID not-found contract with imp…
cristim Jul 9, 2026
39a7bef
fix(lint): restore DB-schema 'cancelled' values changed by misspell f…
cristim Jul 10, 2026
3b8f854
fix(lint): reduce cyclomatic complexity of six flagged test funcs
cristim Jul 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 4 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ linters:
- linters:
- all
path: .*_gen\.go
- linters:
- revive
text: "avoid meaningless package names"
path: internal/api/
paths:
- third_party$
- builtin$
Expand Down
11 changes: 8 additions & 3 deletions ci_cd_sanity_tests/cmd/azure_sanity/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import (
)

func main() {
os.Exit(run())
}

func run() int {
var (
subID = flag.String("subscription-id", "", "Azure subscription ID (or set AZURE_SUBSCRIPTION_ID)")
expectedTenant = flag.String("expected-tenant", "", "Expected Azure tenant ID (optional)")
Expand All @@ -31,18 +35,19 @@ func main() {
})
if err != nil {
fmt.Fprintf(os.Stderr, "azure sanity run failed: %v\n", err)
os.Exit(2)
return 2
}

if err := rep.WriteJSON(*outPath); err != nil {
fmt.Fprintf(os.Stderr, "write report failed: %v\n", err)
os.Exit(2)
return 2
}

if rep.HasFailures() {
fmt.Fprintf(os.Stderr, "azure sanity: FAIL (see %s)\n", *outPath)
os.Exit(1)
return 1
}

fmt.Printf("azure sanity: PASS (see %s)\n", *outPath)
return 0
}
209 changes: 126 additions & 83 deletions ci_cd_sanity_tests/cmd/ri-exchange/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"flag"
"fmt"
"math"
"os"
"strings"
"time"
Expand All @@ -13,17 +14,16 @@ import (
)

type Output struct {
Mode string `json:"mode"` // dry-run | execute
Region string `json:"region"`
AccountChk string `json:"expected_account,omitempty"`

ReservedIDs []string `json:"reserved_instance_ids"`
Quote any `json:"quote"`
Mode string `json:"mode"`
Region string `json:"region"`
AccountChk string `json:"expected_account,omitempty"`
TargetOfferingID string `json:"target_offering_id"`
TargetCount int32 `json:"target_count"`
MaxPaymentDueUSD string `json:"max_payment_due_usd,omitempty"`
ExchangeID string `json:"exchange_id,omitempty"`
Quote any `json:"quote"`
Error string `json:"error,omitempty"`
ReservedIDs []string `json:"reserved_instance_ids"`
TargetCount int32 `json:"target_count"`
}

func parseIDs(s string) []string {
Expand All @@ -38,115 +38,158 @@ func parseIDs(s string) []string {
}

func main() {
var (
region = flag.String("region", "us-east-1", "AWS region")
expectedAccount = flag.String("expected-account", "", "Safety check: expected AWS account ID (optional)")

riIDsCSV = flag.String("ri-ids", "", "Comma-separated Convertible Reserved Instance IDs to exchange (required)")
targetOffering = flag.String("target-offering-id", "", "Target RI offering ID (required)")
targetCount = flag.Int("target-count", 1, "Target instance count (default 1)")

// Execution gating
execute = flag.Bool("execute", false, "Actually execute the exchange (default false = quote only)")
ack = flag.String("ack", "", "Must be 'YES' to execute (safety)")
maxPaymentDue = flag.String("max-payment-due-usd", "", "Max allowed paymentDue from quote (required for execute). Example: 5.00")

outPath = flag.String("out", "ri_exchange_result.json", "Output JSON path")
timeoutSec = flag.Int("timeout-sec", 180, "Timeout seconds")
)
flag.Parse()
os.Exit(run())
}

ctx, cancel := context.WithTimeout(context.Background(), time.Duration(*timeoutSec)*time.Second)
defer cancel()
// runArgs holds the parsed CLI arguments for run().
type runArgs struct {
region string
expectedAccount string
targetOffering string
ack string
maxPaymentDue string
outPath string
ids []string
timeoutSec int
targetCount32 int32
execute bool
}

// parseArgs parses CLI flags and validates required inputs.
// Returns (args, exit code) where exit code 0 means success.
func parseArgs() (args runArgs, code int) {
region := flag.String("region", "us-east-1", "AWS region")
expectedAccount := flag.String("expected-account", "", "Safety check: expected AWS account ID (optional)")
riIDsCSV := flag.String("ri-ids", "", "Comma-separated Convertible Reserved Instance IDs to exchange (required)")
targetOffering := flag.String("target-offering-id", "", "Target RI offering ID (required)")
targetCount := flag.Int("target-count", 1, "Target instance count (default 1)")
execute := flag.Bool("execute", false, "Actually execute the exchange (default false = quote only)")
ack := flag.String("ack", "", "Must be 'YES' to execute (safety)")
maxPaymentDue := flag.String("max-payment-due-usd", "", "Max allowed paymentDue from quote (required for execute). Example: 5.00")
outPath := flag.String("out", "ri_exchange_result.json", "Output JSON path")
timeoutSec := flag.Int("timeout-sec", 180, "Timeout seconds")
flag.Parse()

ids := parseIDs(*riIDsCSV)
if len(ids) == 0 {
fmt.Fprintln(os.Stderr, "ERROR: --ri-ids is required (comma-separated)")
os.Exit(2)
return runArgs{}, 2
}
if strings.TrimSpace(*targetOffering) == "" {
fmt.Fprintln(os.Stderr, "ERROR: --target-offering-id is required")
os.Exit(2)
return runArgs{}, 2
}
tcVal := *targetCount
if tcVal < 0 || tcVal > math.MaxInt32 {
fmt.Fprintf(os.Stderr, "ERROR: --target-count value %d out of range [0, %d]\n", tcVal, math.MaxInt32)
return runArgs{}, 2
}
return runArgs{
region: *region,
expectedAccount: *expectedAccount,
ids: ids,
targetOffering: *targetOffering,
targetCount32: int32(tcVal),
execute: *execute,
ack: *ack,
maxPaymentDue: *maxPaymentDue,
outPath: *outPath,
timeoutSec: *timeoutSec,
}, 0
}

o := Output{
Region: *region,
AccountChk: *expectedAccount,
ReservedIDs: ids,
TargetOfferingID: *targetOffering,
TargetCount: int32(*targetCount),
}

if !*execute {
o.Mode = "dry-run"
q, err := exchange.GetExchangeQuote(ctx, exchange.ExchangeQuoteRequest{
Region: *region,
ExpectedAccount: *expectedAccount,
ReservedIDs: ids,
TargetOfferingID: *targetOffering,
TargetCount: int32(*targetCount),
DryRun: false, // IAMCheckOnly: false = real quote, true = only verify IAM permissions
})
if err != nil {
o.Error = err.Error()
o.Quote = q
writeOrExit(o, *outPath)
fmt.Fprintf(os.Stderr, "quote: FAIL (see %s)\n", *outPath)
os.Exit(1)
}
// runQuote handles the dry-run (quote-only) path.
func runQuote(ctx context.Context, a *runArgs, o *Output) int {
o.Mode = "dry-run"
q, err := exchange.GetExchangeQuote(ctx, &exchange.QuoteRequest{
Region: a.region,
ExpectedAccount: a.expectedAccount,
ReservedIDs: a.ids,
TargetOfferingID: a.targetOffering,
TargetCount: a.targetCount32,
DryRun: false, // IAMCheckOnly: false = real quote, true = only verify IAM permissions
})
if err != nil {
o.Error = err.Error()
o.Quote = q
writeOrExit(o, *outPath)
writeOrExit(*o, a.outPath)
fmt.Fprintf(os.Stderr, "quote: FAIL (see %s)\n", a.outPath)
return 1
}
o.Quote = q
writeOrExit(*o, a.outPath)
if !q.IsValidExchange {
fmt.Fprintf(os.Stderr, "quote: INVALID (%s) (see %s)\n", q.ValidationFailureReason, a.outPath)
return 1
}
fmt.Printf("quote: OK (valid=%v, paymentDue=%s %s) (see %s)\n", q.IsValidExchange, q.PaymentDueRaw, q.CurrencyCode, a.outPath)
return 0
}

if !q.IsValidExchange {
fmt.Fprintf(os.Stderr, "quote: INVALID (%s) (see %s)\n", q.ValidationFailureReason, *outPath)
os.Exit(1)
}
fmt.Printf("quote: OK (valid=%v, paymentDue=%s %s) (see %s)\n", q.IsValidExchange, q.PaymentDueRaw, q.CurrencyCode, *outPath)
os.Exit(0)
func run() int {
a, code := parseArgs()
if code != 0 {
return code
}

ctx, cancel := context.WithTimeout(context.Background(), time.Duration(a.timeoutSec)*time.Second)
defer cancel()

o := Output{
Region: a.region,
AccountChk: a.expectedAccount,
ReservedIDs: a.ids,
TargetOfferingID: a.targetOffering,
TargetCount: a.targetCount32,
}

if !a.execute {
return runQuote(ctx, &a, &o)
}

// Execute path
o.Mode = "execute"
if strings.TrimSpace(*ack) != "YES" {
if strings.TrimSpace(a.ack) != "YES" {
o.Error = "refusing to execute: pass --ack YES"
writeOrExit(o, *outPath)
fmt.Fprintf(os.Stderr, "execute: REFUSED (see %s)\n", *outPath)
os.Exit(2)
writeOrExit(o, a.outPath)
fmt.Fprintf(os.Stderr, "execute: REFUSED (see %s)\n", a.outPath)
return 2
}
if strings.TrimSpace(*maxPaymentDue) == "" {
if strings.TrimSpace(a.maxPaymentDue) == "" {
o.Error = "refusing to execute: --max-payment-due-usd is required as a safety cap"
writeOrExit(o, *outPath)
fmt.Fprintf(os.Stderr, "execute: REFUSED (see %s)\n", *outPath)
os.Exit(2)
writeOrExit(o, a.outPath)
fmt.Fprintf(os.Stderr, "execute: REFUSED (see %s)\n", a.outPath)
return 2
}
maxRat, err := exchange.ParseDecimalRat(*maxPaymentDue)
maxRat, err := exchange.ParseDecimalRat(a.maxPaymentDue)
if err != nil {
o.Error = err.Error()
writeOrExit(o, *outPath)
fmt.Fprintf(os.Stderr, "execute: BAD INPUT (see %s)\n", *outPath)
os.Exit(2)
writeOrExit(o, a.outPath)
fmt.Fprintf(os.Stderr, "execute: BAD INPUT (see %s)\n", a.outPath)
return 2
}
o.MaxPaymentDueUSD = maxRat.FloatString(2)

exID, q, err := exchange.ExecuteExchange(ctx, exchange.ExchangeExecuteRequest{
Region: *region,
ExpectedAccount: *expectedAccount,
ReservedIDs: ids,
TargetOfferingID: *targetOffering,
TargetCount: int32(*targetCount),
exID, q, err := exchange.ExecuteExchange(ctx, &exchange.ExecuteRequest{
Region: a.region,
ExpectedAccount: a.expectedAccount,
ReservedIDs: a.ids,
TargetOfferingID: a.targetOffering,
TargetCount: a.targetCount32,
MaxPaymentDueUSD: maxRat,
})
o.Quote = q
if err != nil {
o.Error = err.Error()
writeOrExit(o, *outPath)
fmt.Fprintf(os.Stderr, "execute: FAIL (see %s)\n", *outPath)
os.Exit(1)
writeOrExit(o, a.outPath)
fmt.Fprintf(os.Stderr, "execute: FAIL (see %s)\n", a.outPath)
return 1
}

o.ExchangeID = exID
writeOrExit(o, *outPath)
fmt.Printf("execute: OK exchangeId=%s (see %s)\n", exID, *outPath)
writeOrExit(o, a.outPath)
fmt.Printf("execute: OK exchangeId=%s (see %s)\n", exID, a.outPath)
return 0
}

func write(v any, path string) error {
Expand Down
19 changes: 15 additions & 4 deletions ci_cd_sanity_tests/cmd/sanity/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@ import (
"context"
"flag"
"fmt"
"math"
"os"
"time"

"github.com/LeanerCloud/CUDly/ci_cd_sanity_tests/pkg/sanity/aws"
)

func main() {
os.Exit(run())
}

func run() int {
var (
region = flag.String("region", "us-east-1", "AWS region for sanity checks")
expectedAccount = flag.String("expected-account", "", "Expected AWS Account ID (optional)")
Expand All @@ -22,25 +27,31 @@ func main() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
defer cancel()

maxListVal := *maxList
if maxListVal < 0 || maxListVal > math.MaxInt32 {
fmt.Fprintf(os.Stderr, "-max-list value %d out of range [0, %d]\n", maxListVal, math.MaxInt32)
return 2
}
rep, err := aws.Run(ctx, aws.Options{
Region: *region,
ExpectedAccount: *expectedAccount,
MaxList: int32(*maxList),
MaxList: int32(maxListVal),
})
if err != nil {
fmt.Fprintf(os.Stderr, "sanity run failed: %v\n", err)
os.Exit(2)
return 2
}

if err := rep.WriteJSON(*outPath); err != nil {
fmt.Fprintf(os.Stderr, "write report failed: %v\n", err)
os.Exit(2)
return 2
}

if rep.HasFailures() {
fmt.Fprintf(os.Stderr, "sanity checks: FAIL (see %s)\n", *outPath)
os.Exit(1)
return 1
}

fmt.Printf("sanity checks: PASS (see %s)\n", *outPath)
return 0
}
Loading
Loading