def on_update(event, context): log.info("Handle resource update event %s" % json.dumps(event, indent=2)) # get parameters passed in props = event["ResourceProperties"] nodes_per_zone = int(props["NodesPerZone"]) zone_count = int(props["NumberOfZones"]) parent_stack_name = props["ParentStackName"] parent_stack_id = props["ParentStackId"] subnets = props["Subnets"] security_group_id = props["SecurityGroup"] # old properties old_props = event["OldResourceProperties"] old_nodes_per_zone = int(old_props["NodesPerZone"]) old_zone_count = int(old_props["NumberOfZones"]) # validate diff and handle special case if old_zone_count != zone_count: reason = "Updating number of zones is not supported" log.error(reason) return cfn_failure_response(event, reason) if old_nodes_per_zone == nodes_per_zone: return cfn_success_response(event, reuse_physical_id=True) if old_nodes_per_zone > nodes_per_zone and nodes_per_zone != 0: reason = "Scaling down the number of nodes per zone by updating the stack is not recommended. " \ "Please manually remove unused network interface." log.warning(reason) return cfn_failure_response(event, reason) if nodes_per_zone == 0: log.info("Hibernating the cluster, retain network interfaces") return cfn_success_response(event, reuse_physical_id=True) # prepare ENI meta information id_hash = hashlib.md5(parent_stack_id.encode()).hexdigest() eni_tag_prefix = parent_stack_name + "-" + id_hash + "_" eni_idx = old_zone_count * old_nodes_per_zone for i in range(zone_count): for j in range(nodes_per_zone): if j < old_nodes_per_zone: continue tag = eni_tag_prefix + str(eni_idx) eni_idx += 1 eni_id = create_eni(subnets[i], security_group_id, tag) if not eni_id: reason = "Failed to create network interface with tag %s" % tag log.warning(reason) continue return cfn_success_response(event, reuse_physical_id=True)
def on_create(event, context): log.info("Handle resource create event %s" % json.dumps(event, indent=2)) # get parameters passed in props = event["ResourceProperties"] vpc = props["Vpc"] service_name = props["ServiceName"] security_group = props["SecurityGroup"] subnets = props["Subnets"] # create endpoint try: response = ec2_client.create_vpc_endpoint( VpcEndpointType='Interface', VpcId=vpc, ServiceName=service_name, SubnetIds=subnets, SecurityGroupIds=[security_group] ) except ClientError as e: reason="Failed to create vpc endpoint for service %s" % service_name log.exception(reason) time.sleep(5) # sleep for 5 seconds to allow exception info being sent to CloudWatch return cfn_failure_response(event, reason) endpoint_id = response["VpcEndpoint"]["VpcEndpointId"] log.info( "Creating vpc endpoint %s for service %s" % ( endpoint_id, service_name ) ) log.info("Waiting for vpc endpoint %s creation finish" % endpoint_id) endpoint_wait_for_creation(endpoint_id) return cfn_success_response(event)
def on_create(event, context): log.info("Handle resource create event %s" % json.dumps(event, indent=2)) # get parameters passed in props = event["ResourceProperties"] nodes_per_zone = int(props["NodesPerZone"]) zone_count = int(props["NumberOfZones"]) parent_stack_name = props["ParentStackName"] parent_stack_id = props["ParentStackId"] subnets = props["Subnets"] security_group_id = props["SecurityGroup"] # prepare ENI meta information id_hash = hashlib.md5(parent_stack_id.encode()).hexdigest() eni_tag_prefix = parent_stack_name + "-" + id_hash + "_" dns = [] # craete ENIs for i in range(0, zone_count): for j in range(0, nodes_per_zone): eni_idx = i * nodes_per_zone + j tag = eni_tag_prefix + str(eni_idx) eni_info = create_eni(subnets[i], security_group_id, tag) if not eni_info: reason = "Failed to create network interface with tag %s" % tag log.warning(reason) continue eni_id = eni_info["NetworkInterfaceId"] eni_dns = eni_info["PrivateDnsName"] eni_assign_tag(eni_id=eni_id, tag=tag) dns.append(eni_dns) return cfn_success_response(event, data={"Addresses": ",".join(dns)})
def on_delete(event, context): log.info("Handle resource delete event %s " % json.dumps(event, indent=2)) # get parameters passed in props = event["ResourceProperties"] nodes_per_zone = int(props["NodesPerZone"]) zone_count = int(props["NumberOfZones"]) parent_stack_name = props["ParentStackName"] parent_stack_id = props["ParentStackId"] # prepare ENI meta information id_hash = hashlib.md5(parent_stack_id.encode()).hexdigest() eni_tag_prefix = parent_stack_name + "-" + id_hash + "_" # delete ENIs for i in range(0, zone_count): for j in range(0, nodes_per_zone): eni_idx = i * nodes_per_zone + j tag = eni_tag_prefix + str(eni_idx) log.info("Querying EC2 for ENI with tag %s" % tag) # query response = None try: response = ec2_client.describe_network_interfaces( # TODO AWS SDK bug #1450 # Filters=[{ # "Name": "tag:cluster-eni-id", # "Values": [tag] # }] Filters=[{ "Name": "description", "Values": [tag] }]) except ClientError as e: reason = "Failed to describe network interface with tag %s" % tag log.exception(reason) time.sleep(5) continue for eni_info in response["NetworkInterfaces"]: eni_id = eni_info["NetworkInterfaceId"] log.info("Found network interface %s " % eni_id) # detach if "Attachment" in eni_info and ( eni_info["Attachment"]["Status"] == "attached" or eni_info["Attachment"]["Status"] == "attaching"): attachment_id = eni_info["Attachment"]["AttachmentId"] if not detach_eni(eni_id, attachment_id): reason = "Failed to detach network interface %s" % eni_id log.error(reason) try: ec2_client.delete_network_interface( NetworkInterfaceId=eni_id) log.info("Deleting network interface %s" % eni_id) except ClientError as e: reason = "Failed to delete network interface %s" % eni_id log.exception(reason) return cfn_success_response(event)
def on_delete(event, context): log.info("Handle resource delete event %s" % json.dumps(event, indent=2)) # get parameters passed in props = event["ResourceProperties"] service_name = props["ServiceName"] vpc = props["Vpc"] # find endpoint response = None try: response = ec2_client.describe_vpc_endpoints( Filters=[ { "Name": "service-name", "Values": [service_name] }, { "Name": "vpc-id", "Values": [vpc] } ] ) except ClientError as e: reason = "Failed to describe vpc endpoint for service %s" % service_name log.exception(reason) time.sleep(5) # sleep for 5 seconds to allow exception info being sent to CloudWatch return cfn_failure_response(event, reason) if len(response["VpcEndpoints"]) <= 0: log.info("No endpoint found for service %s" % service_name) else: endpoint_id = response["VpcEndpoints"][0]["VpcEndpointId"] try: response = ec2_client.delete_vpc_endpoints( VpcEndpointIds=[endpoint_id] ) log.info("Deleting endpoint %s" % endpoint_id) except ClientError as e: reason = "Failed to delete vpc endpoint %s" % endpoint_id log.exception(reason) time.sleep(5) return cfn_failure_response(event, reason) if "Unsuccessful" in response and len(response["Unsuccessful"]) > 0: reason = "Failed to delete vpc endpoint %s: %s" % ( endpoint_id, response["Unsuccessful"][0]["Error"]["Message"] ) log.error(reason) time.sleep(5) return cfn_failure_response(event, reason) return cfn_success_response(event)