Scenario 2: Structured JSON Log Output

ObjectiveVerify the fixed hcp CLI produces structured JSON log output with RFC3339 timestamps, consistent with hypershift-operator and control-plane-operator
Maps toAcceptance criteria — structured JSON logging
NoteThis is integration-level confirmation of what TestNewLogger already proves at unit level

Verification Steps

StepCheckResultEvidence
1 Run fixed binary and capture JSON log lines PASS JSON log line produced during destroy command
2 Validate JSON structure PASS python3 -m json.tool confirms valid JSON
3 Verify RFC3339 timestamp format PASS ts field = 2026-07-13T08:46:56-04:00 (RFC3339)
4 Compare logger pattern with other binaries PASS Same zap.JSONEncoder + zapcore.RFC3339TimeEncoder pattern

Evidence

Step 1: Fixed binary JSON log output

$ KUBECONFIG=/Users/brcox/aws_dev_kubeconfig /tmp/hcp-after destroy cluster azure \
    --name nonexistent-cluster-78310 \
    --azure-creds /Users/brcox/.azure/self-managed-azure-credentials.json \
    --dns-zone-rg-name fake-rg 2>&1
{"level":"info","ts":"2026-07-13T08:46:56-04:00","msg":"Hosted cluster not found, destroying infrastructure from user input","namespace":"clusters","name":"nonexistent-cluster-78310","infraID":""}

Step 2: JSON validation

$ echo '{"level":"info","ts":"2026-07-13T08:46:56-04:00","msg":"Hosted cluster not found..."}' | python3 -m json.tool
VALID JSON

Step 3: RFC3339 timestamp verification

ts=2026-07-13T08:46:56-04:00  (RFC3339: True)

Step 4: Logger pattern comparison

--- hypershift-operator/main.go:118 ---
ctrl.SetLogger(zap.New(zap.JSONEncoder(func(o *zapcore.EncoderConfig) {
    o.EncodeTime = zapcore.RFC3339TimeEncoder
})))

--- product-cli/main.go (fixed) ---
func newLogger(extraOpts ...zap.Opts) logr.Logger {
    opts := []zap.Opts{zap.JSONEncoder(func(o *zapcore.EncoderConfig) {
        o.EncodeTime = zapcore.RFC3339TimeEncoder
    })}
    opts = append(opts, extraOpts...)
    return zap.New(opts...)
}

« Scenario 1: Warning Eliminated Scenario 3: Platform Create — No Regression »