Skip to content

Instantly share code, notes, and snippets.

@atward
Created May 1, 2019 06:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save atward/9539c8339f1dfc3be61146774d46092d to your computer and use it in GitHub Desktop.
Save atward/9539c8339f1dfc3be61146774d46092d to your computer and use it in GitHub Desktop.
Attempts to fix inconsistencies with terragrunt local tfstate, s3 backend state and dynamodb lock state
#!/usr/bin/env python3
import os
import json
from pprint import pprint
import boto3
class State:
def __init__(self, local_filename):
self.s3 = boto3.resource('s3')
self.dynamodb = boto3.resource('dynamodb')
self.local_data = json.load(open(local_filename))
self._load_from_backend()
def _load_from_backend(self):
# only s3 supported
assert safeget(self.local_data, 'backend', 'type') == 's3'
backend_config = self.local_data['backend']['config']
bucket = backend_config['bucket']
s3_key = backend_config['key']
self.dyn_table = self.dynamodb.Table(backend_config['dynamodb_table'])
self.dyn_active_key = {'LockID': '{}/{}'.format(bucket, s3_key)}
self.dyn_state_key = {'LockID': '{}/{}-md5'.format(bucket, s3_key)}
self.s3_obj = self.s3.Bucket(bucket).Object(s3_key)
self.state = self.load_s3_state()
self.active_lock = self.load_lock(self.dyn_active_key)
self.state_lock = self.load_lock(self.dyn_state_key)
def load_s3_state(self):
print("Loading state: {}".format(self.s3_obj))
try:
data = self.s3_obj.get()['Body'].read().decode('utf-8')
pprint(data)
return data
except:
print("Not found")
return None
def empty(self):
return False # TODO
def delete_state(self):
print("Deleting state: {}".format(self.s3_obj))
return self.s3_obj.delete()
def load_lock(self, key):
print("Loading lock {}".format(key))
try:
resp = self.dyn_table.get_item(Key=key)
lock = resp['Item']
pprint(lock)
return lock
except:
print("Not found")
return None
def delete_lock(self, key):
print("Deleting lock {}".format(key))
return self.dyn_table.delete_item(Key=key)
def fix(self):
if self.state:
if self.empty():
print("Deleting state & lock as state is empty")
self.delete_state()
self.delete_lock(self.dyn_state_key)
else:
print("State is not empty, not cleaning up.")
else:
print("Deleting state lock as state is missing")
self.delete_lock(self.dyn_state_key)
if self.active_lock:
self.delete_lock(self.dyn_active_key)
def safeget(dct, *keys):
dct = dict(dct)
for key in keys:
try:
dct = dct[key]
except (KeyError, AttributeError, TypeError):
return None
return dct
def get_state_files():
for folder, _, file in os.walk('.terragrunt-cache'):
for filename in file:
if filename.endswith('.tfstate'):
yield os.path.join(folder, filename)
if __name__ == '__main__':
files = list(get_state_files())
assert len(files) == 1
print("Local state: {}".format(files[0]))
state = State(files[0])
state.fix()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment