Skip to content

Commit

Permalink
Merge pull request #9 from gruntwork-io/username
Browse files Browse the repository at this point in the history
Use IAM username instead of OS username for lock metadata
  • Loading branch information
brikis98 committed Jun 3, 2016
2 parents 5727d34 + 1164d39 commit 80a42b2
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 17 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,5 +307,4 @@ See `circle.yml` and `_ci/build-and-push-release-asset.sh` for details.
* Add a `show-lock` command.
* Add a command to automatically set up best-practices remote state storage in a versioned, encrypted, S3 bucket.
* Add a command to list the different versions of state available in a versioned S3 bucket and to diff any two state
files.
* Use IAM username instead of the local OS username in lock metadata.
files.
6 changes: 6 additions & 0 deletions cli/cli_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ func runApp(cliContext *cli.Context) error {
return err
}

// If someone calls us with no args at all, show the help text and exit
if !cliContext.Args().Present() {
cli.ShowAppHelp(cliContext)
return nil
}

if err := downloadModules(cliContext); err != nil {
return err
}
Expand Down
13 changes: 12 additions & 1 deletion dynamodb/dynamo_lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/gruntwork-io/terragrunt/util"
"github.com/gruntwork-io/terragrunt/errors"
"github.com/aws/aws-sdk-go/aws"
)

// A lock that uses AWS's DynamoDB to acquire and release locks
Expand Down Expand Up @@ -82,14 +83,24 @@ func (dynamoLock DynamoDbLock) String() string {

// Create an authenticated client for DynamoDB
func createDynamoDbClient(awsRegion string) (*dynamodb.DynamoDB, error) {
config, err := createAwsConfig(awsRegion)
if err != nil {
return nil, err
}

return dynamodb.New(session.New(), config), nil
}

// Returns an AWS config object for the given region, ensuring that the config has credentials
func createAwsConfig(awsRegion string) (*aws.Config, error) {
config := defaults.Get().Config.WithRegion(awsRegion)

_, err := config.Credentials.Get()
if err != nil {
return nil, errors.WithStackTraceAndPrefix(err, "Error finding AWS credentials (did you set the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables?)")
}

return dynamodb.New(session.New(), config), nil
return config, nil
}

var StateFileIdMissing = fmt.Errorf("The dynamodb.stateFileId field cannot be empty")
Expand Down
22 changes: 20 additions & 2 deletions dynamodb/dynamo_lock_item.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"github.com/aws/aws-sdk-go/aws"
"fmt"
"github.com/gruntwork-io/terragrunt/errors"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/aws/aws-sdk-go/aws/session"
)

// Create a DynamoDB key for the given item id
Expand Down Expand Up @@ -87,8 +89,13 @@ func getAttribute(item map[string]*dynamodb.AttributeValue, attribute string) (s

// Create a DynamoDB item for the given item id. This item represents a lock and will include metadata about the
// current user, who is trying to acquire the lock.
func createItem(itemId string) (map[string]*dynamodb.AttributeValue, error) {
lockMetadata, err := locks.CreateLockMetadata(itemId)
func createItemAttributes(itemId string, client *dynamodb.DynamoDB) (map[string]*dynamodb.AttributeValue, error) {
iamUsername, err := getIamUsername(client)
if err != nil {
return nil, err
}

lockMetadata, err := locks.CreateLockMetadata(itemId, iamUsername)
if err != nil {
return nil, err
}
Expand All @@ -101,6 +108,17 @@ func createItem(itemId string) (map[string]*dynamodb.AttributeValue, error) {
}, nil
}

// Return the IAM username of the currently logged in user
func getIamUsername(client *dynamodb.DynamoDB) (string, error) {
iamClient := iam.New(session.New(), &client.Config)
output, err := iamClient.GetUser(&iam.GetUserInput{})
if err != nil {
return "", errors.WithStackTrace(err)
}

return *output.User.UserName, nil
}

type AttributeMissing struct {
AttributeName string
}
Expand Down
2 changes: 1 addition & 1 deletion dynamodb/dynamo_lock_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func removeItemFromLockTable(itemId string, tableName string, client *dynamodb.D

// Write the given item to the DynamoDB lock table. If the given item already exists, return an error.
func writeItemToLockTable(itemId string, tableName string, client *dynamodb.DynamoDB) error {
item, err := createItem(itemId)
item, err := createItemAttributes(itemId, client)
if err != nil {
return err
}
Expand Down
12 changes: 3 additions & 9 deletions locks/lock_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package locks
import (
"time"
"net"
"os/user"
"github.com/gruntwork-io/terragrunt/errors"
"fmt"
)
Expand All @@ -19,13 +18,8 @@ type LockMetadata struct {
DateCreated time.Time
}

// Create the LockMetadata for the current user
func CreateLockMetadata(stateFileId string) (*LockMetadata, error) {
user, err := user.Current()
if err != nil {
return nil, errors.WithStackTrace(err)
}

// Create the LockMetadata for the given state file and user
func CreateLockMetadata(stateFileId string, username string) (*LockMetadata, error) {
ipAddress, err := getIpAddress()
if err != nil {
return nil, errors.WithStackTrace(err)
Expand All @@ -35,7 +29,7 @@ func CreateLockMetadata(stateFileId string) (*LockMetadata, error) {

return &LockMetadata{
StateFileId: stateFileId,
Username: user.Username,
Username: username,
IpAddress: ipAddress,
DateCreated: dateCreated,
}, nil
Expand Down
5 changes: 3 additions & 2 deletions locks/lock_metdata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ func TestCreateLockMetadata(t *testing.T) {
t.Parallel()

expectedStateFileId := "expected-state-file-id"
lockMetadata, err := CreateLockMetadata(expectedStateFileId)
expectedUsername := "jim"
lockMetadata, err := CreateLockMetadata(expectedStateFileId, expectedUsername)

assert.Nil(t, err)
assert.Equal(t, expectedStateFileId, lockMetadata.StateFileId)
assert.False(t, lockMetadata.DateCreated.IsZero())
assertIsValidIp(t, lockMetadata.IpAddress)
assert.NotEmpty(t, lockMetadata.Username)
assert.Equal(t, expectedUsername, lockMetadata.Username)
}

func assertIsValidIp(t *testing.T, ip string) {
Expand Down

0 comments on commit 80a42b2

Please sign in to comment.