使用Boto检查CloudFormation堆栈是否存在且未处于损坏状态的最佳方法是什么?破碎的意思是失败和回滚状态。
我不想使用try/except
解决方案,因为boto将其记录为错误,在我的方案中,它会将异常日志发送到警报系统。
目前,我有以下解决方案:
1)使用boto.cloudformation.connection.CloudFormationConnection.describe_stacks()
valid_states = '''\
CREATE_IN_PROGRESS
CREATE_COMPLETE
UPDATE_IN_PROGRESS
UPDATE_COMPLETE_CLEANUP_IN_PROGRESS
UPDATE_COMPLETE'''.splitlines()
def describe_stacks():
result = []
resp = cf_conn.describe_stacks()
result.extend(resp)
while resp.next_token:
resp = cf_conn.describe_stacks(next_token=resp.next_token)
result.extend(resp)
return result
stacks = [stack for stack in describe_stacks() if stack.stack_name == STACK_NAME and stack.stack_status in valid_states]
exists = len(stacks) >= 1
这很慢,因为我有很多堆栈。
2)使用boto.cloudformation.connection.CloudFormationConnection.list_stacks()
def list_stacks(filters):
result = []
resp = cf_conn.list_stacks(filters)
result.extend(resp)
while resp.next_token:
resp = cf_conn.list_stacks(filters, next_token=resp.next_token)
result.extend(resp)
return result
stacks = [stack for stack in list_stacks(valid_states) if stack.stack_name == STACK_NAME]
exists = len(stacks) >= 1
这需要永远,因为摘要保留了90天,而且我有很多堆栈。
问题:检查给定堆栈是否存在且未处于故障或回滚状态的理想解决方案是什么?
答案 0 :(得分:4)
来自boto docs:
describe_stacks(stack_name_or_id = None,next_token = None)
返回指定堆栈的描述;如果未指定堆栈名称,则返回所有已创建堆栈的描述。
参数:stack_name_or_id(string) - 与堆栈关联的名称或唯一标识符。
由于您知道堆栈名称,因此可以使用describe_stacks(stack_name_or_id=STACK_NAME)
。这应该会为你加快速度。
答案 1 :(得分:1)
我实现了以下可行的方法:
import boto3
from botocore.exceptions import ClientError
client = boto3.client('cloudformation')
def stack_exists(name, required_status = 'CREATE_COMPLETE'):
try:
data = client.describe_stacks(StackName = name)
except ClientError:
return False
return data['Stacks'][0]['StackStatus'] == required_status
我没有找到以前的任何完整解决方案,也没有使用boto3进行此操作的快速方法,所以我创建了上面的
答案 2 :(得分:0)
执行此操作的最佳方法是将其分为两个单独的问题:
可能看起来像这样:
failure_states = ['CREATE_FAILED', ... ]
stack_names = ['prod', 'dev', 'test']
c = boto.cloudformation.connect_to_region(region)
existing_stacks = [s.stack_name for s in c.describe_stacks()]
nonexistent_stacks = set(stack_names) - set(existing_stacks)
error_stacks = c.list_stacks(stack_status_filters=failure_states)
你可能有更多的堆栈然后我这样做也许你需要使用next_token
分页,这将是一个麻烦。但是,您可以看到两个操作都可以使用单个请求完成,并且不会引发异常。
答案 3 :(得分:0)
您可以通过将Boto记录器的级别设置为ERROR以上来使其静音。这样一来,您无需捕获警报即可捕获异常:
domain.com/symfonyfolder/web//web//web//web//web//web//web//web//web//web//web//web//web//web//web//web//web//web//web//
答案 4 :(得分:0)
我会通过分页器结合 ListStacks API 调用进行检查,因为可能是我的 lambda 或我没有 cloudformation
的权限,如果发生这种情况,我应该返回相关信息而不是说堆栈不存在。
import boto3
from botocore.exceptions import ClientError
cfn = boto3.client('cloudformation')
def stack_exists(stack_name: str, stack_status: str) -> bool:
try:
paginator = cfn.get_paginator('list_stacks')
response_iterator = paginator.paginate()
for page in response_iterator:
for stack in page['StackSummaries']:
if stack_name == stack.get('StackName') and stack.get('StackStatus') != stack_status:
return True
except ClientError as e:
logger.exception(f'Client error while checking stack : {e}')
raise
except Exception:
logger.exception('Error while checking stack')
raise
return False
或者如果我们不想遍历所有堆栈:
import boto3
from botocore.exceptions import ClientError
cfn = boto3.client('cloudformation')
def stack_exists(stack_name: str, required_status='DELETE_COMPLETE'):
try:
stacks_summary = cfn.describe_stacks(StackName=stack_name)
stack_info = stacks_summary.get('Stacks')[0]
return stack_name == stack_info.get('StackName') and stack_info.get('StackStatus') != required_status
except ClientError as e:
stack_not_found_error = f'Stack with id {stack_name} does not exist'
error_received = e.response('Error')
error_code_received = error_received.get('Code')
error_message_received = error_received.get('Message')
if error_code_received == 'ValidationError' and error_message_received == stack_not_found_error:
return True
logger.exception(f'Client error while describing stacks: {e}')
raise
except Exception:
logger.exception('Error while checking stack')
raise
答案 5 :(得分:-1)
我知道这已经过时了,但有人问过几周之前是否有解决方案,所以这里有...
如果您阅读了boto3文档,它会经常提到已删除的堆栈。为此,您拥有以使用完整堆栈ID。您无法使用堆栈名称。这是因为堆栈唯一真正独特的是ID。
示例:
resource = boto3.resource('cloudformation')
status = resource.Stack('id:of:stack').stack_status
这将返回异常的唯一时间是该堆栈ID是否存在。
干杯!