def smtp_credentials(dnsname): if dnsname == 'localhost': user_name = f'ses-user-{dnsname}-{uuid.uuid4()}' else: user_name = f'ses-user-{dnsname}' code, output = shell.execute( f'aws iam create-user --user-name {user_name} --profile {config.PROFILE} --output json' ) logging.debug(output) code, output = shell.execute( 'aws iam put-user-policy --policy-document \'{"Version": "2012-10-17",' '"Statement":[{"Effect": "Allow","Action": "ses:SendRawEmail","Resource": "*"}]}\'' f' --policy-name OpenRemoteSendEmail --user-name {user_name} --profile {config.PROFILE}' ) code, output = shell.execute( f'aws iam create-access-key --user-name {user_name} --profile {config.PROFILE} --output json' ) if config.DRY_RUN: return 'AccessKeyId', 'SecretAccessKey' else: logging.debug(output) access = json.loads(output) return ( access['AccessKey']['AccessKeyId'], gen_aws_smtp_credentials.calculate_key( access['AccessKey']['SecretAccessKey'], config.REGION), )
def map_upload(path): shell.execute( f'aws s3 cp {path} s3://{config.BUCKET}/{path} --profile {config.PROFILE}', echo=True, ) shell.execute( 'aws s3api put-object-tagging --bucket %s --key %s --tagging \'TagSet=[{Key=type,Value=deployment-data}]\' --profile %s' % (config.BUCKET, path, config.PROFILE), echo=True, )
def remove(dnsname): if dnsname == 'localhost': shell.execute(f'docker stack rm openremote') else: if config.VERBOSE is True: print(f'> wget -nc {GITPATH}/mvp-docker-compose.yml') if not config.DRY_RUN: wget.download(f'{GITPATH}/mvp-docker-compose.yml') shell.execute( f'docker-compose -f mvp-docker-compose.yml -p openremote down')
def configure_aws_show(): if not config.QUIET: print(f'S3 bucket: s3://{config.BUCKET}\n') region = shell.execute( f'aws configure get region --profile {config.PROFILE}')[1][:-1] id = shell.execute( f'aws configure get aws_access_key_id --profile {config.PROFILE}' )[1][:-1] secret = shell.execute( f'aws configure get aws_secret_access_key --profile {config.PROFILE}' )[1][:-1] print(f'--id={id} --secret="{secret}" --region={region}')
def deploy_rich(password, smtp_user, smtp_password, project): staging = False if project.find('staging.') == 0: staging = True project = project[8:] shell.execute( f'aws s3 cp s3://{config.BUCKET}/{project} {project} --storage-class STANDARD --recursive --profile {config.PROFILE} --force-glacier-transfer' ) shell.execute(f'tar xvf {project}/deployment.tar.gz') shell.execute(f'mv mapdata.mbtiles deployment/map/', no_exception=True) env = '' # TODO reuse installed credentials docker run {project_manager_1} env if password != 'secret': env = f'PASSWORD={password} ' if smtp_user and smtp_password: env = (f'{env}EMAIL_USER={smtp_user} ' f'EMAIL_PASSWORD={smtp_password} ' f'EMAIL_HOST=email-smtp.{config.REGION}.amazonaws.com ') dnsname = f'{project}.openremote.io' if staging: env = f'{env}DEPLOYMENT_NAME=staging.{project} ' else: env = f'{env}DEPLOYMENT_NAME={project} ' generate_password, password = _password(password, url=dnsname) if generate_password: env = f'{env}PASSWORD={password} ' shell.execute(f'{env}docker-compose up -d') if not config.DRY_RUN: print('\nStack deployed, waiting for startup to complete', end=' ') while _deploy_health(dnsname, 0) == 0: time.sleep(3) print('.', end='', flush=True) print(emojis.encode(':thumbsup:')) if config.VERBOSE is True: print(f'\nOpen https://{dnsname} and login with admin:{password}')
def check_aws_perquisites(): # Check AWS profile code, output = shell.execute('aws configure list-profiles') if not config.DRY_RUN and config.PROFILE not in output: msg = f"aws-cli profile '{config.PROFILE}' missing" raise Exception(-1, msg) # Check EC2 key code, output = shell.execute( f'aws ec2 describe-key-pairs --key-names openremote --profile {config.PROFILE}' ) if not config.DRY_RUN and 'openremote' not in output: msg = f"ERROR: Missing EC2 keypair 'openremote' in {config.REGION} region (Ireland)" raise Exception(-1, msg) return True
def remove_aws(dnsname): check_aws_perquisites() if os.name == "nt": if config.VERBOSE: print(f'del aws-delete-stack-{dnsname}.bat') shell.execute(f'aws-delete-stack-{dnsname}.bat') if not config.DRY_RUN: os.remove(f'aws-delete-stack-{dnsname}.bat') else: if config.VERBOSE: print(f'rm aws-delete-stack-{dnsname}.sh') shell.execute(f'sh aws-delete-stack-{dnsname}.sh') if not config.DRY_RUN: os.remove(f'aws-delete-stack-{dnsname}.sh')
def _show_check_result(tool): code, output = shell.execute(tool, no_exception=True) if not config.QUIET: print(f'{output.rstrip()} ', end='') print(emojis.encode(':heavy_check_mark:')) if code == 0 else print( emojis.encode(':x:')) elif code != 0: raise Exception(f'{code}, {output}')
def clean(): shell.execute( 'docker volume rm --force openremote_deployment-data openremote_postgresql-data openremote_proxy-data' ) shell.execute( 'docker rmi openremote/manager-swarm openremote/deployment ' 'openremote/keycloak openremote/postgresql openremote/proxy ' 'openremote/manager', no_exception=True, ) shell.execute('docker system prune --force')
def configure_aws(id, secret, region): config.REGION = region shell.execute( f'aws configure set profile.{config.PROFILE}.aws_access_key_id {id}', echo=config.VERBOSE, ) shell.execute( f'aws configure set profile.{config.PROFILE}.aws_secret_access_key {secret}', echo=config.VERBOSE, ) shell.execute( f'aws configure set profile.{config.PROFILE}.region {region}', echo=config.VERBOSE, )
def map_delete(path): shell.execute( # No --recursive here as it can wipe the whole bucket, also for other clients f'aws s3 rm s3://{config.BUCKET}/{path} --profile {config.PROFILE}', echo=True, )
def map_download(path): shell.execute( f'aws s3 cp s3://{config.BUCKET}/{path} {path} --storage-class STANDARD --recursive --profile {config.PROFILE} --force-glacier-transfer', echo=True, )
def map_list(path): shell.execute( f'aws s3 ls s3://{config.BUCKET}/{path} --recursive --human-readable --summarize --profile {config.PROFILE}', echo=True, )
def deploy(password, smtp_user, smtp_password, dnsname): shell.execute('docker swarm init', no_exception=True) shell.execute('docker volume rm openremote_postgresql-data', no_exception=True) # TODO fetch tar from S3 and copy to the docker volume # shell.execute('docker volume create openremote_deployment-data') # shell.execute( # 'docker run --rm -v openremote_deployment-data:/deployment openremote/deployment:mvp' # ) if config.VERBOSE is True: print(f'> wget -nc {GITPATH}/mvp-docker-compose.yml') if not config.DRY_RUN: wget.download(f'{GITPATH}/mvp-docker-compose.yml') env = '' if password != 'secret': env = f'PASSWORD={password} ' if smtp_user and smtp_password: env = (f'{env}EMAIL_USER={smtp_user} ' f'EMAIL_PASSWORD={smtp_password} ' f'EMAIL_HOST=email-smtp.{config.REGION}.amazonaws.com ') if True: identity = dnsname if _check_ip(dnsname): # it is pure IP dnsname = 'localhost' # prevent proxy from issuing cert env = f'{env}DOMAINNAME={dnsname} IDENTITY_NETWORK_HOST={identity} ' # As you may be facing internet change default password for security generate_password, password = _password(password, url=dnsname) if generate_password: env = f'{env}PASSWORD={password} ' # Deploy with docker-compose as proxy container does not obey # health check within swarm mode # TODO revisit proxy image to fix this, otherwise letsencrypt has problems shell.execute( f'{env}docker-compose -f mvp-docker-compose.yml -p openremote up -d' ) if not config.DRY_RUN: print('\nStack deployed, waiting for startup to complete', end=' ') while _deploy_health(dnsname, 0) == 0: time.sleep(3) print('.', end='', flush=True) print(emojis.encode(':thumbsup:\n')) if config.VERBOSE is True: print( f'Open https://{dnsname} and login with admin:{password}\n\n' 'Remove the stack when you are done:\n' '> docker-compose -f mvp-docker-compose.yml -p openremote down\n' '> rm mvp-docker-compose.yml\n') else: shell.execute( f'{env}docker stack deploy -c mvp-docker-compose.yml openremote') if not config.DRY_RUN: print('\nStack deployed, waiting for startup to complete', end=' ') c = 0 # TODO deploying from docker image has problems with health checking hence 10min time out while _deploy_health(dnsname, 0) == 0 and c < 200: time.sleep(3) print('.', end='', flush=True) c += 1 print(emojis.encode(':thumbsup:\n')) if config.VERBOSE is True: print( f'\nOpen https://localhost and login with admin:{password}\n\n' 'To remove the stack when you are done:\n' '> docker stack rm openremote\n') if not config.DRY_RUN: os.remove(f'mvp-docker-compose.yml') if config.VERBOSE is True: print( 'To remove docker resources:\n' "> docker images --filter 'reference=openremote/*' -q | xargs docker rmi\n" "> docker volume ls --filter 'dangling=true' -q | xargs docker volume rm" )
def deploy_aws(password, dnsname, region): host, domain = _split_dns(dnsname) logging.debug(f'{dnsname} => {host} + {domain}') stack_name = f'{host}-{uuid.uuid4()}' check_aws_perquisites() generate_password, password = _password(password, url=dnsname) if config.VERBOSE is True: print(f'> wget -nc {GITPATH}/aws-cloudformation.template.yml') if not config.DRY_RUN: wget.download(f'{GITPATH}/aws-cloudformation.template.yml') shell_exec = shell.execute( f'aws cloudformation create-stack --stack-name {stack_name} ' f'--template-body file://aws-cloudformation.template.yml --parameters ' f'ParameterKey=DomainName,ParameterValue={domain} ' f'ParameterKey=HostName,ParameterValue={host} ' f'ParameterKey=HostedZone,ParameterValue=true ' f'ParameterKey=OpenRemotePassword,ParameterValue={password} ' f'ParameterKey=InstanceType,ParameterValue=t4g.small ' f'--capabilities CAPABILITY_NAMED_IAM ' f'--profile={config.PROFILE} ' f'--disable-rollback ' f'--region={region}') print(f'\n{shell_exec[1]}') if shell_exec[0] != 0: raise Exception(shell_exec) print('Waiting for CloudFormation...') # TODO make better feedback to the user # code, output = shell.execute # CREATE_STACK_STATUS=$(aws --region ${AWS_REGION} --profile ${AWS_PROFILE} cloudformation describe-stacks --stack-name ${STACK_NAME} --query 'Stacks[0].StackStatus' --output text) # while [[ $CREATE_STACK_STATUS == "REVIEW_IN_PROGRESS" ]] || [[ $CREATE_STACK_STATUS == "CREATE_IN_PROGRESS" ]] # do # # Wait 30 seconds and then check stack status again # sleep 30 # CREATE_STACK_STATUS=$(aws --region ${AWS_REGION} --profile ${AWS_PROFILE} cloudformation describe-stacks --stack-name ${STACK_NAME} --query 'Stacks[0].StackStatus' --output text) # done shell.execute( f'aws cloudformation wait stack-create-complete ' f'--stack-name {stack_name} --profile {config.PROFILE} --region={region}' ) if not config.DRY_RUN: os.remove(f'aws-cloudformation.template.yml') if generate_password: # In case of password generation get email credentials and send it to support code, output = shell.execute( f'aws cloudformation describe-stacks --stack-name {stack_name} --profile {config.PROFILE} --region={region} ' '--query "Stacks[0].Outputs[?OutputKey==\'UserId\'||OutputKey==\'UserSecret\'].OutputValue" --output json' ) credentials = json.loads(output) smtp_user = credentials[0] smtp_password = gen_aws_smtp_credentials.calculate_key( credentials[1], config.REGION) email.sendmail( "*****@*****.**", "Openremote password", f""" A new AWS stack {stack_name} has been just created. Because there was a default password used a more secure one was generated. The new admin password is {password} """, smtp_user, smtp_password, ) print( '\nAn email with generated password was sent to [email protected]\n' ) elif generate_password: print( '\nAn email with generated password would be sent to [email protected]\n' ) if not config.DRY_RUN: if os.name == "nt": shell.execute( f'echo "aws cloudformation delete-stack --stack-name {stack_name} --profile {config.PROFILE} --region={region}" > aws-delete-stack-{host}.{domain}.bat' ) else: shell.execute( f'echo "aws cloudformation delete-stack --stack-name {stack_name} --profile {config.PROFILE} --region={region}" > aws-delete-stack-{host}.{domain}.sh' ) shell.execute(f'chmod +x aws-delete-stack-{host}.{domain}.sh') print('\nStack deployed, waiting for startup to complete', end=' ') c = 0 while _deploy_health(f'{host}.{domain}', 0) == 0 and c < 200: time.sleep(3) print('.', end='', flush=True) c += 1 print(emojis.encode(':thumbsup:\n')) print( emojis.encode( '\nMind that running it cost money :moneybag::moneybag::moneybag:! To free resources execute:\n\n' f'aws cloudformation delete-stack --stack-name {stack_name} --profile {config.PROFILE} --region={region}\n\n' 'check running stack with health command:\n' f'or deploy -a health --dnsname {host}.{domain} -v'))