From 61ba05b77b954733cbc851675521eb3320d84911 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 8 Sep 2023 10:30:37 -0700 Subject: [PATCH] Adds logging for DynamoDB operations --- internal/backend/remote-state/s3/backend.go | 4 +- internal/backend/remote-state/s3/client.go | 105 ++++++++++++-------- internal/backend/remote-state/s3/logging.go | 37 +++++++ 3 files changed, 101 insertions(+), 45 deletions(-) create mode 100644 internal/backend/remote-state/s3/logging.go diff --git a/internal/backend/remote-state/s3/backend.go b/internal/backend/remote-state/s3/backend.go index 64b4661e72..1fecc86b3c 100644 --- a/internal/backend/remote-state/s3/backend.go +++ b/internal/backend/remote-state/s3/backend.go @@ -635,8 +635,8 @@ func (b *Backend) Configure(obj cty.Value) tfdiags.Diagnostics { b.keyName = stringAttr(obj, "key") log = log.With( - "tf_backend_s3.bucket", b.bucketName, - "tf_backend_s3.path", b.path, + logKeyBucket, b.bucketName, + logKeyPath, b.keyName, ) b.acl = stringAttr(obj, "acl") diff --git a/internal/backend/remote-state/s3/client.go b/internal/backend/remote-state/s3/client.go index f3fd1bed90..8a06f45450 100644 --- a/internal/backend/remote-state/s3/client.go +++ b/internal/backend/remote-state/s3/client.go @@ -183,6 +183,7 @@ func (c *RemoteClient) get(ctx context.Context) (*remote.Payload, error) { func (c *RemoteClient) Put(data []byte) error { ctx := context.TODO() log := c.logger() + ctx, baselog := baselogging.NewHcLogger(ctx, log) ctx = baselogging.RegisterLogger(ctx, baselog) @@ -233,9 +234,12 @@ func (c *RemoteClient) Put(data []byte) error { func (c *RemoteClient) Delete() error { ctx := context.TODO() log := c.logger() + ctx, baselog := baselogging.NewHcLogger(ctx, log) ctx = baselogging.RegisterLogger(ctx, baselog) + log.Info("Deleting remote state") + _, err := c.s3Client.DeleteObject(ctx, &s3.DeleteObjectInput{ Bucket: aws.String(c.bucketName), Key: aws.String(c.path), @@ -256,6 +260,7 @@ func (c *RemoteClient) Delete() error { func (c *RemoteClient) Lock(info *statemgr.LockInfo) (string, error) { ctx := context.TODO() + log := c.logger() if c.ddbTable == "" { return "", nil @@ -272,6 +277,12 @@ func (c *RemoteClient) Lock(info *statemgr.LockInfo) (string, error) { info.ID = lockID } + log = logWithLockInfo(log, info) + ctx, baselog := baselogging.NewHcLogger(ctx, log) + ctx = baselogging.RegisterLogger(ctx, baselog) + + log.Info("Locking remote state") + putParams := &dynamodb.PutItemInput{ Item: map[string]dynamodbtypes.AttributeValue{ "LockID": &dynamodbtypes.AttributeValueMemberS{ @@ -302,6 +313,55 @@ func (c *RemoteClient) Lock(info *statemgr.LockInfo) (string, error) { return info.ID, nil } +func (c *RemoteClient) Unlock(id string) error { + ctx := context.TODO() + log := c.logger() + + log = logWithLockID(log, id) + + ctx, baselog := baselogging.NewHcLogger(ctx, log) + ctx = baselogging.RegisterLogger(ctx, baselog) + + if c.ddbTable == "" { + return nil + } + + lockErr := &statemgr.LockError{} + + log.Info("Unlocking remote state") + + // TODO: store the path and lock ID in separate fields, and have proper + // projection expression only delete the lock if both match, rather than + // checking the ID from the info field first. + lockInfo, err := c.getLockInfo(ctx) + if err != nil { + lockErr.Err = fmt.Errorf("failed to retrieve lock info for lock ID %q: %s", id, err) + return lockErr + } + lockErr.Info = lockInfo + + if lockInfo.ID != id { + lockErr.Err = fmt.Errorf("lock ID %q does not match existing lock (%q)", id, lockInfo.ID) + return lockErr + } + + params := &dynamodb.DeleteItemInput{ + Key: map[string]dynamodbtypes.AttributeValue{ + "LockID": &dynamodbtypes.AttributeValueMemberS{ + Value: c.lockPath(), + }, + }, + TableName: aws.String(c.ddbTable), + } + _, err = c.dynClient.DeleteItem(ctx, params) + + if err != nil { + lockErr.Err = err + return lockErr + } + return nil +} + func (c *RemoteClient) getMD5(ctx context.Context) ([]byte, error) { if c.ddbTable == "" { return nil, nil @@ -420,47 +480,6 @@ func (c *RemoteClient) getLockInfo(ctx context.Context) (*statemgr.LockInfo, err return lockInfo, nil } -func (c *RemoteClient) Unlock(id string) error { - ctx := context.TODO() - - if c.ddbTable == "" { - return nil - } - - lockErr := &statemgr.LockError{} - - // TODO: store the path and lock ID in separate fields, and have proper - // projection expression only delete the lock if both match, rather than - // checking the ID from the info field first. - lockInfo, err := c.getLockInfo(ctx) - if err != nil { - lockErr.Err = fmt.Errorf("failed to retrieve lock info: %s", err) - return lockErr - } - lockErr.Info = lockInfo - - if lockInfo.ID != id { - lockErr.Err = fmt.Errorf("lock id %q does not match existing lock", id) - return lockErr - } - - params := &dynamodb.DeleteItemInput{ - Key: map[string]dynamodbtypes.AttributeValue{ - "LockID": &dynamodbtypes.AttributeValueMemberS{ - Value: c.lockPath(), - }, - }, - TableName: aws.String(c.ddbTable), - } - _, err = c.dynClient.DeleteItem(ctx, params) - - if err != nil { - lockErr.Err = err - return lockErr - } - return nil -} - func (c *RemoteClient) lockPath() string { return fmt.Sprintf("%s/%s", c.bucketName, c.path) } @@ -473,8 +492,8 @@ func (c *RemoteClient) getSSECustomerKeyMD5() string { // logger returns the S3 backend logger configured with the client's bucket and path func (c *RemoteClient) logger() hclog.Logger { return logger().With( - "tf_backend_s3.bucket", c.bucketName, - "tf_backend_s3.path", c.path, + logKeyBucket, c.bucketName, + logKeyPath, c.path, ) } diff --git a/internal/backend/remote-state/s3/logging.go b/internal/backend/remote-state/s3/logging.go new file mode 100644 index 0000000000..fe765e3e23 --- /dev/null +++ b/internal/backend/remote-state/s3/logging.go @@ -0,0 +1,37 @@ +package s3 + +import ( + "github.com/hashicorp/go-hclog" + "github.com/hashicorp/terraform/internal/states/statemgr" +) + +const ( + logKeyBucket = "tf_backend.s3.bucket" + logKeyPath = "tf_backend.s3.path" +) + +const ( + logKeyBackendLockId = "tf_backend.lock.id" + logKeyBackendLockOperation = "tf_backend.lock.operation" + logKeyBackendLockInfo = "tf_backend.lock.info" + logKeyBackendLockWho = "tf_backend.lock.who" + logKeyBackendLockVersion = "tf_backend.lock.version" + logKeyBackendLockPath = "tf_backend.lock.path" +) + +func logWithLockInfo(in hclog.Logger, info *statemgr.LockInfo) hclog.Logger { + return in.With( + logKeyBackendLockId, info.ID, + logKeyBackendLockOperation, info.Operation, + logKeyBackendLockInfo, info.Info, + logKeyBackendLockWho, info.Who, + logKeyBackendLockVersion, info.Version, + logKeyBackendLockPath, info.Path, + ) +} + +func logWithLockID(in hclog.Logger, id string) hclog.Logger { + return in.With( + logKeyBackendLockId, id, + ) +}