ProxyStatsGathering/scripts/rundev.py (133 lines of code) (raw):
#!/usr/bin/env python
import boto3
import subprocess
import os.path
import yaml
import logging
from optparse import OptionParser
from time import sleep
from datetime import datetime
from tzlocal import get_localzone
logging.basicConfig(level=logging.WARN)
logger = logging.getLogger(__name__)
logger.level = logging.INFO
class LogViewer(object):
def __init__(self, log_group_name, log_stream_name):
super(LogViewer, self).__init__()
self.log_group_name = log_group_name
self.log_stream_name = log_stream_name
self.most_recent_timestamp=None
self._client = boto3.client('logs', region_name=options.region)
def get_loglines(self):
results = self._client.get_log_events(logGroupName = self.log_stream_name, logStreamName = self.log_stream_name, startTime=self.most_recent_timestamp, startFromHead=True)
if len(results['events'])>0:
last_event = results['events'][-1]
self.most_recent_timestamp=last_event['timestamp']
return map(lambda entry: "{0}: {1}".format(datetime.from_timestamp(entry['timestamp']), entry['message']), results['events'])
def find_sbt_dir(starting_path):
if starting_path=="" or starting_path=="/":
raise StandardError("Could not find a build.sbt file in any specified path")
logger.debug("at {0}".format(starting_path))
if os.path.exists(os.path.join(starting_path, "build.sbt")):
return starting_path
else:
return find_sbt_dir(os.path.abspath(os.path.join(starting_path, "..")))
def build_and_push(host, user, sbtdir):
proc = subprocess.Popen(["sbt", "-Ddocker.host={0}".format(host), "-Ddocker.username={0}".format(user), "project proxyStatsGathering", "docker:publish"], cwd=sbtdir)
proc.wait()
if proc.returncode!=0:
raise StandardError("sbt dockerPublish failed")
def extract_cf_outputs(cfinfo):
return dict(map(lambda entry: (entry["OutputKey"], entry["OutputValue"]), cfinfo["Outputs"]))
def get_cloudformation_info(stackname):
client = boto3.client('cloudformation', region_name=options.region)
result = client.describe_stacks(StackName=stackname)
if len(result["Stacks"])==0:
raise StandardError("Could not find cloudformation stack {0}".format(stackname))
info = result["Stacks"][0]
logger.info("Found stack {0} in status {1}".format(info["StackName"], info["StackStatus"]))
return extract_cf_outputs(info)
def run_task(cluster_id, task_arn, container_name, subnet_list, sg_list, allow_external_ip, collection_name, mode):
client = boto3.client('ecs', region_name=options.region)
network_config = {
"awsvpcConfiguration": {
"subnets": subnet_list,
"securityGroups": sg_list,
"assignPublicIp": "ENABLED" if allow_external_ip else "DISABLED"
}
}
overrides = {
'containerOverrides': [
{
'name': container_name,
'environment': [
]
}
]
}
if collection_name:
overrides['containerOverrides'][0]['environment'].append({
'name': "FOR_COLLECTION",
'value': collection_name
})
if mode:
overrides['containerOverrides'][0]['environment'].append({
'name': "MODE",
'value': mode
})
result = client.run_task(cluster=cluster_id, taskDefinition=task_arn, networkConfiguration=network_config, overrides=overrides, launchType="FARGATE")
if len(result["failures"])>0:
for entry in result.failures:
logger.error("\t{0}: {1}".format(entry["arn"], entry["reason"]))
raise StandardError("Failed to start up container")
logger.debug(result["tasks"][0])
return {
"task_arn": result["tasks"][0]["taskArn"],
"cluster_arn": result["tasks"][0]["clusterArn"],
"container_id": result["tasks"][0]["containers"][0]["containerArn"]
}
def monitor_task(cluster_arn, task_arn): #, log_group_name, log_stream_name):
client = boto3.client('ecs', region_name=options.region)
while True:
try:
response = client.describe_tasks(cluster=cluster_arn, tasks=[task_arn],)
info = response["tasks"][0]
if "startedAt" in info:
start_time = info["startedAt"]
else:
start_time = None
if "stoppedAt" in info:
finish_time = info["stoppedAt"]
else:
finish_time = None
logger.info("Task status is {0} (desired status {1})".format(info["lastStatus"], info["desiredStatus"]))
if start_time:
logger.info("Running since {0} ({1})".format(start_time, datetime.now(get_localzone())-start_time))
if finish_time:
logger.info("Ran from {0} to {1}, total of {2}".format(start_time, finish_time, finish_time-start_time))
break
except KeyError as e:
logger.error(str(e))
logger.debug(str(info))
sleep(10)
###START MAIN
parser = OptionParser()
parser.add_option("-c","--config", dest="configfile", help="Configuration YAML", default="ecs_rundev.yaml")
parser.add_option("-r","--region", dest="region", help="AWS region", default="eu-west-1")
parser.add_option("-s","--stackname", dest="stackname", help="Cloudformation stack that contains the deployed task")
parser.add_option("-m","--mode", dest="mode", help="Run in stats-gathering or index fix mode. Specify either 'stats' or 'indexfix'", default="stats")
parser.add_option("--collection", dest="collection", help="limit to this ArchiveHunter collection")
(options, args) = parser.parse_args()
with open(options.configfile,"r") as f:
config = yaml.load(f.read())
sbt_dir = find_sbt_dir(os.path.dirname(os.path.realpath(__file__)))
logger.info("Got SBT directory {0}".format(sbt_dir))
if not "docker" in config:
raise StandardError("You must have a docker: section in the yaml config file")
build_and_push(config["docker"].get("host"), config["docker"].get("user"), sbt_dir)
cfinfo = get_cloudformation_info(options.stackname)
logger.debug(str(cfinfo))
if not "TaskDefinitionArn" in cfinfo:
raise StandardError("No TaskDefinitionArn output in {0}".format(options.stackname))
logger.info("Got task ARN {0}".format(cfinfo["TaskDefinitionArn"]))
taskinfo = run_task(config["ecs"].get("cluster"), cfinfo["TaskDefinitionArn"], cfinfo["AppContainerName"], config["ecs"].get("subnets"), config["ecs"].get("security_groups"), config["ecs"].get("external_ip"), options.collection, options.mode)
logger.debug(str(taskinfo))
monitor_task(taskinfo["cluster_arn"], taskinfo["task_arn"])