-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
189 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
node_modules | ||
prompt.txt | ||
package-lock.json | ||
**/*.secrets |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import os | ||
import argparse | ||
import subprocess | ||
import json | ||
|
||
def get_ecr_uri(service): | ||
"""Retrieve the ECR URI dynamically from AWS based on the service repository name.""" | ||
account_id = subprocess.check_output( | ||
['aws', 'sts', 'get-caller-identity', '--query', 'Account', '--output', 'text'] | ||
).decode().strip() | ||
|
||
region = os.getenv('AWS_DEFAULT_REGION', 'us-west-2') | ||
ecr_uri = f"{account_id}.dkr.ecr.{region}.amazonaws.com" | ||
|
||
# Check if the repository exists | ||
repositories = subprocess.check_output( | ||
['aws', 'ecr', 'describe-repositories', '--query', 'repositories[*].repositoryName', '--output', 'json'] | ||
).decode() | ||
|
||
if service not in json.loads(repositories): | ||
raise ValueError(f"ECR repository '{service}' not found.") | ||
|
||
return f"{ecr_uri}/{service}" | ||
|
||
def docker_build_push(service_path, service, ecr, version): | ||
original_dir = os.getcwd() | ||
basepath = os.path.dirname(service_path) | ||
os.chdir(f"..") | ||
cmd = [ | ||
'docker', | ||
'buildx', | ||
'build', | ||
'--platform', | ||
'linux/arm64', | ||
'-t', | ||
f'{ecr}:{version}', | ||
'-f', | ||
f'{service_path}', | ||
'--push', | ||
basepath | ||
] | ||
print("-- Running Docker Build and Push for Service:", service) | ||
subprocess.check_call(cmd) | ||
os.chdir(original_dir) | ||
|
||
def main(): | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument('pattern', nargs='?', default=None) | ||
args = parser.parse_args() | ||
|
||
print("Logging into AWS ECR") | ||
aws_password = subprocess.check_output(['aws', 'ecr', 'get-login-password']).decode().strip() | ||
|
||
version = os.getenv('VERSION', '1.0') | ||
service_paths = { | ||
'smartpool-platform': './Dockerfile' | ||
} | ||
services = service_paths.keys() | ||
|
||
for service in services: | ||
if args.pattern is None or args.pattern in service: | ||
service_path = service_paths.get(service) | ||
ecr = get_ecr_uri(service) # Dynamically retrieve ECR URI | ||
subprocess.run( | ||
['docker', 'login', '--username', 'AWS', '--password-stdin', ecr], | ||
input=aws_password, text=True, check=True | ||
) | ||
docker_build_push(service_path, service, ecr, version) | ||
else: | ||
print("Skipping ", service) | ||
|
||
print("Project has been built but not redeployed. To redeploy run:") | ||
print(" python deploy.py") | ||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import subprocess | ||
import argparse | ||
import sys | ||
import os | ||
import json | ||
|
||
def get_manager_ip(): | ||
"""Find the IP of the EC2 instance named 'docker-manager'.""" | ||
try: | ||
response = subprocess.check_output( | ||
[ | ||
'aws', 'ec2', 'describe-instances', | ||
'--filters', 'Name=tag:Name,Values=docker-manager', | ||
'--query', 'Reservations[*].Instances[*].PublicIpAddress', | ||
'--output', 'text' | ||
] | ||
).decode().strip() | ||
|
||
if not response: | ||
raise ValueError("No instance found with the name 'docker-manager'. Ensure the EC2 instance is running and correctly tagged.") | ||
|
||
return response | ||
except subprocess.CalledProcessError as e: | ||
print(f"Failed to retrieve manager IP: {e}", file=sys.stderr) | ||
sys.exit(1) | ||
|
||
def get_ecr_uri(): | ||
"""Dynamically retrieve the ECR URI using AWS CLI.""" | ||
account_id = subprocess.check_output( | ||
['aws', 'sts', 'get-caller-identity', '--query', 'Account', '--output', 'text'] | ||
).decode().strip() | ||
region = os.getenv('AWS_DEFAULT_REGION', 'us-west-2') | ||
return f"{account_id}.dkr.ecr.{region}.amazonaws.com" | ||
|
||
def ecr_login(manager, key, ecr_uri): | ||
"""Logs into AWS ECR on the remote manager node.""" | ||
get_login_password_cmd = "aws ecr get-login-password --region us-west-2" | ||
docker_login_cmd = f"{get_login_password_cmd} | docker login --username AWS --password-stdin {ecr_uri}" | ||
ssh_command = f"ssh -i {key} {manager} '{docker_login_cmd}'" | ||
subprocess.check_call(ssh_command, shell=True) | ||
|
||
def parse_arguments(): | ||
parser = argparse.ArgumentParser(description='Deploy services to Docker Swarm.') | ||
parser.add_argument('project', help='Specify the project to deploy.') | ||
parser.add_argument('--manager', default='auto', help='Specify the Docker Swarm manager host or use "auto" to find it by name.') | ||
parser.add_argument('--key', required=True, help='Specify the SSH key path.') | ||
return parser.parse_args() | ||
|
||
def file_exists(project): | ||
return os.path.isfile(f'./docker-swarm/{project}.yml') | ||
|
||
def sync_files(project, manager, key): | ||
subprocess.check_call(['rsync', '-avz', '-e', f'ssh -i {key}', f'./secrets/{project}.yml', f'{manager}:~/']) | ||
|
||
def deploy_stack(project, manager, key): | ||
deploy_command = f'docker stack deploy --compose-file ~/{project}.yml --with-registry-auth {project} --resolve-image always' | ||
ssh_command = f'ssh -i {key} {manager} \"{deploy_command}\"' | ||
subprocess.check_call(ssh_command, shell=True) | ||
|
||
def read_env_file(filename): | ||
"""Reads secrets from a file and returns them as a dictionary.""" | ||
env_variables = {} | ||
with open(filename, 'r') as file: | ||
for line in file: | ||
line = line.strip() | ||
if line: | ||
key, value = line.split('=', 1) | ||
env_variables[key] = value | ||
return env_variables | ||
|
||
def replace_secrets(ymlfile, secretsfile, outputymlfile): | ||
replaced = replace_secrets_in_file(ymlfile, secretsfile) | ||
with open(outputymlfile, "w") as file: | ||
file.write(replaced) | ||
|
||
def replace_secrets_in_file(source_file, secret_file): | ||
secrets = read_env_file(secret_file) | ||
with open(source_file, 'r') as file: | ||
content = file.read() | ||
for key, value in secrets.items(): | ||
placeholder = f"${key}" | ||
content = content.replace(placeholder, value) | ||
return content | ||
|
||
def main(): | ||
args = parse_arguments() | ||
|
||
# Determine the manager IP | ||
if args.manager == 'auto': | ||
manager_ip = get_manager_ip() | ||
args.manager = f'ubuntu@{manager_ip}' | ||
|
||
if not file_exists(args.project): | ||
print(f'The yml file for the project "{args.project}" does not exist.', file=sys.stderr) | ||
sys.exit(1) | ||
if not os.path.isfile(f'./secrets/{args.project}.secrets'): | ||
print(f'The secrets file for the project "{args.project}" does not exist.', file=sys.stderr) | ||
sys.exit(1) | ||
|
||
try: | ||
ecr_uri = get_ecr_uri() # Dynamically fetch ECR URI | ||
replace_secrets(f'./docker-swarm/{args.project}.yml', f'./secrets/{args.project}.secrets', f'./secrets/{args.project}.yml') | ||
ecr_login(args.manager, args.key, ecr_uri) | ||
sync_files(args.project, args.manager, args.key) | ||
deploy_stack(args.project, args.manager, args.key) | ||
print(f'Project {args.project} deployed successfully!') | ||
except subprocess.CalledProcessError as e: | ||
print(f'An error occurred: {e}', file=sys.stderr) | ||
sys.exit(1) | ||
|
||
if __name__ == '__main__': | ||
main() |