check for nil state in s3 client Get

The S3 client can return (nil, nil) when the remote state doesn't exist.
The caused a nil pointer dereference when checking the payload.MD5
against the expected value.

This can happen if the remote state was manually removed, but the digest
entry was left in the DynamoDB table.
This commit is contained in:
James Bardin 2017-05-30 13:59:16 -04:00
parent bcd57a963c
commit e7502454b4
2 changed files with 27 additions and 6 deletions

View File

@ -58,11 +58,19 @@ func (c *RemoteClient) Get() (payload *remote.Payload, err error) {
return nil, err
}
// If the remote state was manually removed the payload will be nil,
// but if there's still a digest entry for that state we will still try
// to compare the MD5 below.
var digest []byte
if payload != nil {
digest = payload.MD5
}
// verify that this state is what we expect
if expected, err := c.getMD5(); err != nil {
log.Printf("[WARNING] failed to fetch state md5: %s", err)
} else if len(expected) > 0 && !bytes.Equal(expected, payload.MD5) {
log.Printf("[WARNING] state md5 mismatch: expected '%x', got '%x'", expected, payload.MD5)
} else if len(expected) > 0 && !bytes.Equal(expected, digest) {
log.Printf("[WARNING] state md5 mismatch: expected '%x', got '%x'", expected, digest)
if testChecksumHook != nil {
testChecksumHook()
@ -74,7 +82,7 @@ func (c *RemoteClient) Get() (payload *remote.Payload, err error) {
continue
}
return nil, fmt.Errorf(errBadChecksumFmt, payload.MD5)
return nil, fmt.Errorf(errBadChecksumFmt, digest)
}
break

View File

@ -167,6 +167,8 @@ func TestRemoteClient_clientMD5(t *testing.T) {
"lock_table": bucketName,
}).(*Backend)
createS3Bucket(t, b.s3Client, bucketName)
defer deleteS3Bucket(t, b.s3Client, bucketName)
createDynamoDBTable(t, b.dynClient, bucketName)
defer deleteDynamoDBTable(t, b.dynClient, bucketName)
@ -263,8 +265,8 @@ func TestRemoteClient_stateChecksum(t *testing.T) {
t.Fatal(err)
}
// put the old state in place of the new, without updating the checksum
if err := client2.Put(oldState.Bytes()); err != nil {
// put an empty state in place to check for panics during get
if err := client2.Put([]byte{}); err != nil {
t.Fatal(err)
}
@ -278,7 +280,18 @@ func TestRemoteClient_stateChecksum(t *testing.T) {
consistencyRetryTimeout = 0
consistencyRetryPollInterval = 0
// fetching the state through client1 should now error out due to a
// fetching an empty state through client1 should now error out due to a
// mismatched checksum.
if _, err := client1.Get(); !strings.HasPrefix(err.Error(), errBadChecksumFmt[:80]) {
t.Fatalf("expected state checksum error: got %s", err)
}
// put the old state in place of the new, without updating the checksum
if err := client2.Put(oldState.Bytes()); err != nil {
t.Fatal(err)
}
// fetching the wrong state through client1 should now error out due to a
// mismatched checksum.
if _, err := client1.Get(); !strings.HasPrefix(err.Error(), errBadChecksumFmt[:80]) {
t.Fatalf("expected state checksum error: got %s", err)