Skip to content
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
4be5e78
Fix Terraform state authentication by passing auth context
osterman Oct 22, 2025
66da0a6
Merge main and resolve conflicts
osterman Oct 22, 2025
145b5eb
Add blog post: Auth Context implementation for contributors
osterman Oct 22, 2025
a3006c9
Update blog post to emphasize concurrent multi-provider support
osterman Oct 22, 2025
fb4de7e
Merge main and resolve conflicts
osterman Oct 22, 2025
82254ba
Merge remote-tracking branch 'origin/main' into osterman/terraform-st…
osterman Oct 22, 2025
8ff8a64
Refactor SetAuthContext to use parameter struct.
osterman Oct 22, 2025
7391ddf
Add comprehensive tests for SetAuthContext and region override.
osterman Oct 22, 2025
7e1c359
Increase test coverage from 80.9% to 84.1%.
osterman Oct 22, 2025
b472da5
Document multiple %w error wrapping patterns.
osterman Oct 22, 2025
075e816
Clarify critical difference between error chains and flat lists.
osterman Oct 22, 2025
6a070a4
Fix Windows path test and clarify PRD implementation status.
osterman Oct 22, 2025
b686385
Merge branch 'main' into osterman/terraform-state-auth-fix
osterman Oct 23, 2025
a7f5a01
Add auth console command for web console access (#1684)
osterman Oct 23, 2025
31710c6
Replace hard tabs with spaces in markdown code blocks.
osterman Oct 23, 2025
96b8dad
Merge branch 'main' into osterman/terraform-state-auth-fix
osterman Oct 23, 2025
081c47a
Fix mockgen directives, AWS console URL, and add console config.
osterman Oct 23, 2025
5191db9
Add support for configurable console session duration.
osterman Oct 23, 2025
fe450cf
Document console.session_duration configuration.
osterman Oct 23, 2025
2edcede
Merge branch 'main' into osterman/terraform-state-auth-fix
osterman Oct 23, 2025
a2953df
Fix Azure backend function signature to match registry type.
osterman Oct 23, 2025
8234a59
Add tests for resolveConsoleDuration function.
osterman Oct 23, 2025
cca065e
Add tests for LoadAWSConfigWithAuth function.
osterman Oct 23, 2025
5881f93
Restore helpful AWS credential resolution documentation.
osterman Oct 23, 2025
e0701e7
Add comment preservation guidelines to CLAUDE.md.
osterman Oct 23, 2025
7065428
Add comprehensive tests for terraform generation functions.
osterman Oct 23, 2025
ea0efa6
Merge branch 'main' into osterman/terraform-state-auth-fix
osterman Oct 23, 2025
90f4fed
test: Improve LoadAWSConfigWithAuth test quality
osterman Oct 23, 2025
cacc10f
test: Remove tautological and duplicate tests
osterman Oct 23, 2025
d82c630
Merge branch 'main' into osterman/terraform-state-auth-fix
osterman Oct 24, 2025
4934fdb
fix: Thread stackInfo/authContext through YAML tag processing
osterman Oct 24, 2025
d01c016
test: Add tests for stackInfo/authContext threading
osterman Oct 24, 2025
1978983
fix: Add stackInfo parameter to ProcessCustomYamlTagsWithContext
osterman Oct 24, 2025
a88a868
test: Add mock-based tests for authContext threading
osterman Oct 24, 2025
4f7d5bb
test: Add ignore_trailing_whitespace option for snapshot comparison
osterman Oct 24, 2025
86f14cb
[autofix.ci] apply automated fixes
autofix-ci[bot] Oct 24, 2025
f83f6bd
test: Update schema.json with missing test configuration fields
osterman Oct 24, 2025
6444ffe
fix: Allow boolean values for environment variables in test schema
osterman Oct 24, 2025
4354c86
refactor: Decouple test setup from test name in aws_utils_test
osterman Oct 24, 2025
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
55 changes: 53 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,51 @@ pkg/newfeature/
### Comment Style (MANDATORY)
All comments must end with periods (enforced by `godot` linter).

### Comment Preservation (MANDATORY)
**NEVER delete existing comments without a very strong reason.**

Comments are documentation that helps developers understand:
- **Why** code was written a certain way
- **How** complex algorithms or flows work
- **What** edge cases or gotchas to be aware of
- **Where** credentials or configuration come from

**Guidelines:**
- **Preserve helpful comments** - Especially those explaining credential resolution, complex logic, or non-obvious behavior
- **Update comments to match code** - When refactoring, update comments to reflect current implementation
- **Refactor for clarity** - It's okay to improve comment wording or structure for better readability
- **Add context when modifying** - If changing code with comments, ensure comments still accurately describe the behavior

**Acceptable reasons to remove comments:**
- Comment is factually incorrect and cannot be updated
- Code is completely removed
- Comment duplicates what the code obviously does (e.g., `// increment counter` above `counter++`)
- Comment is outdated TODO that has been completed

**Anti-pattern:**
```go
// WRONG: Deleting helpful documentation during refactoring
-// LoadAWSConfig looks for credentials in the following order:
-// 1. Environment variables (AWS_ACCESS_KEY_ID, etc.)
-// 2. Shared credentials file (~/.aws/credentials)
-// 3. EC2 Instance Metadata Service (IMDS)
-// ... (more helpful details)
func LoadAWSConfig(ctx context.Context) (aws.Config, error) {
```

**Correct pattern:**
```go
// CORRECT: Preserving and updating helpful documentation
-// LoadAWSConfig looks for credentials in the following order:
+// LoadAWSConfigWithAuth looks for credentials in the following order:
+// When authContext is provided, uses Atmos-managed credentials.
+// Otherwise, falls back to standard AWS SDK resolution:
// 1. Environment variables (AWS_ACCESS_KEY_ID, etc.)
// 2. Shared credentials file (~/.aws/credentials)
// 3. EC2 Instance Metadata Service (IMDS)
// ... (more helpful details)
```

### Import Organization (MANDATORY)
Three groups separated by blank lines, sorted alphabetically:
1. Go stdlib
Expand All @@ -168,11 +213,17 @@ Precedence: CLI flags → ENV vars → config files → defaults (use Viper)

### Error Handling (MANDATORY)
- Wrap with static errors from `errors/errors.go`
- Combine: `errors.Join(errUtils.ErrFoo, err)`
- Add context: `fmt.Errorf("%w: msg", errUtils.ErrFoo)`
- Chain errors: `fmt.Errorf("%w: msg", errUtils.ErrFoo)` - creates error chain
- Join errors: `errors.Join(errUtils.ErrFoo, err)` - combines independent errors
- Multiple wrapping: `fmt.Errorf("%w: context: %w", errUtils.ErrBase, err)` (valid Go 1.20+)
- Check: `errors.Is(err, target)`
- Never dynamic errors or string comparison

**Important distinction:**
- **`fmt.Errorf` with single `%w`**: Creates error **chain** - `errors.Unwrap()` returns next error. Use when error context builds sequentially through call stack. **Prefer this when error chain matters.**
- **`errors.Join`**: Creates **flat list** - `errors.Unwrap()` returns `nil`, must use `Unwrap() []error` interface. Use for independent errors (parallel operations, multiple validations).
- **`fmt.Errorf` with multiple `%w`**: Like `errors.Join` but adds format string. Valid Go 1.20+, returns `Unwrap() []error`.

### Testing Strategy (MANDATORY)
- **Prefer unit tests with mocks** over integration tests
- Use interfaces + dependency injection for testability
Expand Down
41 changes: 40 additions & 1 deletion cmd/auth_console.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,16 @@ func executeAuthConsoleCommand(cmd *cobra.Command, args []string) error {
return fmt.Errorf("%w: %w", errUtils.ErrAuthConsole, err)
}

// Resolve session duration (flag takes precedence over provider config).
sessionDuration, err := resolveConsoleDuration(cmd, authManager, whoami.Provider)
if err != nil {
return fmt.Errorf("%w: %w", errUtils.ErrAuthConsole, err)
}

// Generate console URL.
options := types.ConsoleURLOptions{
Destination: consoleDestination,
SessionDuration: consoleDuration,
SessionDuration: sessionDuration,
Issuer: consoleIssuer,
OpenInBrowser: !consoleSkipOpen && !consolePrintOnly,
}
Expand Down Expand Up @@ -258,6 +264,39 @@ func retrieveCredentials(whoami *types.WhoamiInfo) (types.ICredentials, error) {
}
}

// resolveConsoleDuration resolves console session duration from flag or provider config.
// Flag takes precedence over provider configuration.
func resolveConsoleDuration(cmd *cobra.Command, authManager types.AuthManager, providerName string) (time.Duration, error) {
defer perf.Track(nil, "cmd.resolveConsoleDuration")()

// Check if flag was explicitly set by user.
if cmd.Flags().Changed("duration") {
return consoleDuration, nil
}

// Get provider configuration.
providers := authManager.GetProviders()
provider, exists := providers[providerName]
if !exists {
// No provider config found, use default from flag.
return consoleDuration, nil
}

// Check if provider has console configuration.
if provider.Console == nil || provider.Console.SessionDuration == "" {
// No console config, use default from flag.
return consoleDuration, nil
}

// Parse provider's session duration.
duration, err := time.ParseDuration(provider.Console.SessionDuration)
if err != nil {
return 0, fmt.Errorf("invalid session_duration in provider %q console config: %w", providerName, err)
}

return duration, nil
}

func init() {
authConsoleCmd.Flags().StringVar(&consoleDestination, "destination", "",
"Console page to navigate to. Supports full URLs or shorthand aliases like 's3', 'ec2', 'lambda', etc.")
Expand Down
94 changes: 94 additions & 0 deletions cmd/auth_console_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"

errUtils "github.com/cloudposse/atmos/errors"
"github.com/cloudposse/atmos/pkg/auth/types"
Expand Down Expand Up @@ -724,3 +725,96 @@ func (m *mockAuthManagerForIdentity) LogoutProvider(ctx context.Context, provide
func (m *mockAuthManagerForIdentity) LogoutAll(ctx context.Context) error {
return errors.New("not implemented")
}

func TestResolveConsoleDuration(t *testing.T) {
_ = NewTestKit(t)

tests := []struct {
name string
flagSet bool
flagValue time.Duration
providerConfig *schema.ConsoleConfig
expectedDuration time.Duration
expectError bool
}{
{
name: "flag explicitly set takes precedence",
flagSet: true,
flagValue: 4 * time.Hour,
providerConfig: &schema.ConsoleConfig{SessionDuration: "12h"},
expectedDuration: 4 * time.Hour,
expectError: false,
},
{
name: "provider config used when flag not set",
flagSet: false,
flagValue: 1 * time.Hour, // default flag value
providerConfig: &schema.ConsoleConfig{SessionDuration: "8h"},
expectedDuration: 8 * time.Hour,
expectError: false,
},
{
name: "default flag value when no provider config",
flagSet: false,
flagValue: 1 * time.Hour,
providerConfig: nil,
expectedDuration: 1 * time.Hour,
expectError: false,
},
{
name: "default flag value when provider config empty",
flagSet: false,
flagValue: 1 * time.Hour,
providerConfig: &schema.ConsoleConfig{SessionDuration: ""},
expectedDuration: 1 * time.Hour,
expectError: false,
},
{
name: "invalid provider config duration",
flagSet: false,
flagValue: 1 * time.Hour,
providerConfig: &schema.ConsoleConfig{SessionDuration: "invalid"},
expectError: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_ = NewTestKit(t)

// Create test command with duration flag.
cmd := &cobra.Command{}
cmd.Flags().DurationVar(&consoleDuration, "duration", 1*time.Hour, "duration flag")

// Set flag value and simulate whether user explicitly set it.
consoleDuration = tt.flagValue
if tt.flagSet {
require.NoError(t, cmd.Flags().Set("duration", tt.flagValue.String()))
}

// Create mock auth manager using gomock.
ctrl := gomock.NewController(t)
mockManager := types.NewMockAuthManager(ctrl)

// Setup expectation for GetProviders.
providers := map[string]schema.Provider{
"test-provider": {
Kind: "aws/iam-identity-center",
Console: tt.providerConfig,
},
}
mockManager.EXPECT().GetProviders().Return(providers).AnyTimes()

// Call resolveConsoleDuration.
duration, err := resolveConsoleDuration(cmd, mockManager, "test-provider")

if tt.expectError {
assert.Error(t, err)
return
}

require.NoError(t, err)
assert.Equal(t, tt.expectedDuration, duration)
})
}
}
Loading