Skip to content

Commit

Permalink
feat: 0.2.0 - added support for deployment when putting new config
Browse files Browse the repository at this point in the history
  • Loading branch information
nicksnell committed Aug 1, 2023
1 parent dc75b8c commit 7bf70a3
Show file tree
Hide file tree
Showing 9 changed files with 344 additions and 18 deletions.
2 changes: 1 addition & 1 deletion appconf/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.1.1"
__version__ = "0.2.0"
72 changes: 62 additions & 10 deletions appconf/api.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import logging

from appconf.models import Application, ConfigurationProfile, HostedConfigurationVersion
from appconf.models import (
Application,
ConfigurationProfile,
HostedConfigurationVersion,
Deployment,
DeploymentStrategy,
Environment,
)
from appconf.exceptions import NoHostedConfigurationVersionsFound


def get_application(appconfig_client, app_name):
def get_application(client, app_name):
"""
Get the application id from the application name
"""
apps = appconfig_client.list_applications()
apps = client.list_applications()

for a in apps["Items"]:
if a["Name"] == app_name:
Expand All @@ -17,11 +24,11 @@ def get_application(appconfig_client, app_name):
return None


def get_config_profile(appconfig_client, app_id, profile_name):
def get_config_profile(client, app_id, profile_name):
"""
Get the configuration profile from the application id and profile name
"""
profiles = appconfig_client.list_configuration_profiles(ApplicationId=app_id)
profiles = client.list_configuration_profiles(ApplicationId=app_id)

for p in profiles["Items"]:
if p["Name"] == profile_name:
Expand All @@ -30,13 +37,11 @@ def get_config_profile(appconfig_client, app_id, profile_name):
return None


def get_latest_hosted_configuration_version(
appconfig_client, application, config_profile
):
def get_latest_hosted_configuration_version(client, application, config_profile):
"""
Get the latest hosted configuration version from the configuration profile id
"""
config_versions = appconfig_client.list_hosted_configuration_versions(
config_versions = client.list_hosted_configuration_versions(
ApplicationId=application.Id, ConfigurationProfileId=config_profile.Id
)

Expand All @@ -52,7 +57,7 @@ def get_latest_hosted_configuration_version(
if v["VersionNumber"] > latest_version["VersionNumber"]:
latest_version = v

latest_hosted_config = appconfig_client.get_hosted_configuration_version(
latest_hosted_config = client.get_hosted_configuration_version(
ApplicationId=application.Id,
ConfigurationProfileId=config_profile.Id,
VersionNumber=latest_version["VersionNumber"],
Expand Down Expand Up @@ -112,3 +117,50 @@ def setup(client, app_name, profile_name):
return

return application, config_profile


def get_deployment_strategy(client, strategy_name):
"""
Get all deployment strategies
"""
strategies = client.list_deployment_strategies()

for p in strategies["Items"]:
if p["Name"] == strategy_name:
return DeploymentStrategy.from_dict(p)

return None


def get_environment(client, application, environment_name):
environments = client.list_environments(ApplicationId=application.Id)

for p in environments["Items"]:
if p["Name"] == environment_name:
return Environment.from_dict(p)

return None


def start_deployment(
client, application, config_profile, deployment_strategy, environment
):
deployment_response = client.start_deployment(
ApplicationId=application.Id,
ConfigurationProfileId=config_profile.Id,
ConfigurationVersion="1",
DeploymentStrategyId=deployment_strategy.Id,
EnvironmentId=environment.Id,
)

return Deployment.from_dict(deployment_response)


def get_deployment(client, application, environment, deployment_number):
deployment_response = client.get_deployment(
ApplicationId=application.Id,
DeploymentNumber=deployment_number,
EnvironmentId=environment.Id,
)

return Deployment.from_dict(deployment_response)
61 changes: 58 additions & 3 deletions appconf/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,26 @@
import logging
import os
import sys
import time

import boto3
import click
from rich.console import Console
from rich.logging import RichHandler
from rich.progress import Progress
from rich.rule import Rule

from appconf import api
from appconf import api, __version__
from appconf.exceptions import NoHostedConfigurationVersionsFound

logging.basicConfig(level=logging.INFO, handlers=[RichHandler()])
logging.basicConfig(level=logging.CRITICAL, handlers=[RichHandler()])

console = Console()


@click.group()
@click.pass_context
@click.version_option(__version__)
@click.option(
"--aws-profile",
help="AWS Profile Name",
Expand Down Expand Up @@ -76,7 +79,18 @@ def get_config(ctx, app, profile, meta):
@click.option("-a", "--app", help="Application Name", required=True)
@click.option("-p", "--profile", help="Configuration profile name", required=True)
@click.option("-d", "--description", help="Description for the version", default="")
def put_config(ctx, config_file, app, profile, description):
@click.option(
"--deploy", is_flag=True, help="Deploy to configuration environment", default=False
)
@click.option(
"--deploy-strategy",
help="Deploy using named strategy",
default="AppConfig.Linear50PercentEvery30Seconds",
)
@click.option("--env", help="Deploy to environment", default="default")
def put_config(
ctx, config_file, app, profile, description, deploy, deploy_strategy, env
):
"""
Upload a new hosted configuration version for the application & profile
"""
Expand All @@ -100,6 +114,47 @@ def put_config(ctx, config_file, app, profile, description):

console.print(f"[green]Created new configuration version:[/green] {version}")

if deploy:
strategy = api.get_deployment_strategy(ctx.obj["appconfig"], deploy_strategy)

if strategy is None:
console.print(
f"[red]Unable to deploy configuration profile using strategy {strategy} "
f"for {application.Name} ({application.Id})![/red]"
)
return

environment = api.get_environment(ctx.obj["appconfig"], application, env)

deployment = api.start_deployment(
ctx.obj["appconfig"], application, config_profile, strategy, environment
)

with Progress() as progress:
task = progress.add_task("Deployment in progress...", total=100)
complete_so_far = 0

while deployment.PercentageComplete != 100.0:
time.sleep(20)
deployment = api.get_deployment(
ctx.obj["appconfig"],
application,
environment,
deployment.DeploymentNumber,
)
percent_complete = int(deployment.PercentageComplete)
advance = percent_complete - complete_so_far
progress.update(task, advance=advance)
complete_so_far = percent_complete

progress.update(task, visible=False)

console.print(
f"[green]Successful deployment to [/green] {env} [green](took "
f"{deployment.FinalBakeTimeInMinutes} minute"
f"{'s' if deployment.FinalBakeTimeInMinutes > 1 else ''})[/green]"
)


if __name__ == "__main__":
cli(obj={})
29 changes: 29 additions & 0 deletions appconf/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,32 @@ class HostedConfigurationVersion(Base):

def get_json(self):
return self.Content.read().decode("utf-8")


@dataclasses.dataclass
class DeploymentStrategy(Base):
Id: str
Name: str
DeploymentDurationInMinutes: int
GrowthType: str


@dataclasses.dataclass
class Environment(Base):
ApplicationId: str
Id: str
Name: str
State: str


@dataclasses.dataclass
class Deployment(Base):
ApplicationId: str
EnvironmentId: str
DeploymentStrategyId: str
ConfigurationProfileId: str
DeploymentNumber: int
DeploymentDurationInMinutes: int
FinalBakeTimeInMinutes: int
State: str
PercentageComplete: float
Loading

0 comments on commit 7bf70a3

Please sign in to comment.