Compare commits

..

1 Commits

Author SHA1 Message Date
3ba9436863 Add git to webapp base container. 2024-06-24 23:57:15 -05:00
3 changed files with 27 additions and 58 deletions

View File

@ -18,7 +18,7 @@ from stack_orchestrator.deploy.deploy_types import DeployCommandContext, Laconic
from stack_orchestrator.deploy.deployment_context import DeploymentContext
from stack_orchestrator.deploy.stack_state import State
from stack_orchestrator.deploy.deploy_util import VolumeMapping, run_container_command
from stack_orchestrator.opts import opts
from stack_orchestrator.command_types import CommandOptions
from enum import Enum
from pathlib import Path
from shutil import copyfile, copytree
@ -62,7 +62,7 @@ def _get_node_moniker_from_config(network_dir: Path):
return moniker
def _get_node_key_from_gentx(gentx_file_name: str):
def _get_node_key_from_gentx(options: CommandOptions, gentx_file_name: str):
gentx_file_path = Path(gentx_file_name)
if gentx_file_path.exists():
with open(Path(gentx_file_name), "rb") as f:
@ -77,24 +77,24 @@ def _comma_delimited_to_list(list_str: str):
return list_str.split(",") if list_str else []
def _get_node_keys_from_gentx_files(gentx_file_list: str):
def _get_node_keys_from_gentx_files(options: CommandOptions, gentx_file_list: str):
node_keys = []
gentx_files = _comma_delimited_to_list(gentx_file_list)
for gentx_file in gentx_files:
node_key = _get_node_key_from_gentx(gentx_file)
node_key = _get_node_key_from_gentx(options, gentx_file)
if node_key:
node_keys.append(node_key)
return node_keys
def _copy_gentx_files(network_dir: Path, gentx_file_list: str):
def _copy_gentx_files(options: CommandOptions, network_dir: Path, gentx_file_list: str):
gentx_files = _comma_delimited_to_list(gentx_file_list)
for gentx_file in gentx_files:
gentx_file_path = Path(gentx_file)
copyfile(gentx_file_path, os.path.join(network_dir, "config", "gentx", os.path.basename(gentx_file_path)))
def _remove_persistent_peers(network_dir: Path):
def _remove_persistent_peers(options: CommandOptions, network_dir: Path):
config_file_path = _config_toml_path(network_dir)
if not config_file_path.exists():
print("Error: config.toml not found")
@ -108,7 +108,7 @@ def _remove_persistent_peers(network_dir: Path):
output_file.write(config_file_content)
def _insert_persistent_peers(config_dir: Path, new_persistent_peers: str):
def _insert_persistent_peers(options: CommandOptions, config_dir: Path, new_persistent_peers: str):
config_file_path = config_dir.joinpath("config.toml")
if not config_file_path.exists():
print("Error: config.toml not found")
@ -151,7 +151,7 @@ def _phase_from_params(parameters):
def setup(command_context: DeployCommandContext, parameters: LaconicStackSetupCommand, extra_args):
options = opts.o
options = command_context.cluster_context.options
currency = "stake" # Does this need to be a parameter?
@ -238,7 +238,7 @@ def setup(command_context: DeployCommandContext, parameters: LaconicStackSetupCo
print("Error: --gentx-files must be supplied")
sys.exit(1)
# First look in the supplied gentx files for the other nodes' keys
other_node_keys = _get_node_keys_from_gentx_files(parameters.gentx_file_list)
other_node_keys = _get_node_keys_from_gentx_files(options, parameters.gentx_file_list)
# Add those keys to our genesis, with balances we determine here (why?)
for other_node_key in other_node_keys:
outputk, statusk = run_container_command(
@ -247,7 +247,7 @@ def setup(command_context: DeployCommandContext, parameters: LaconicStackSetupCo
if options.debug:
print(f"Command output: {outputk}")
# Copy the gentx json files into our network dir
_copy_gentx_files(network_dir, parameters.gentx_file_list)
_copy_gentx_files(options, network_dir, parameters.gentx_file_list)
# Now we can run collect-gentxs
output1, status1 = run_container_command(
command_context, "laconicd", f"laconicd collect-gentxs --home {laconicd_home_path_in_container}", mounts)
@ -256,7 +256,7 @@ def setup(command_context: DeployCommandContext, parameters: LaconicStackSetupCo
print(f"Generated genesis file, please copy to other nodes as required: \
{os.path.join(network_dir, 'config', 'genesis.json')}")
# Last thing, collect-gentxs puts a likely bogus set of persistent_peers in config.toml so we remove that now
_remove_persistent_peers(network_dir)
_remove_persistent_peers(options, network_dir)
# In both cases we validate the genesis file now
output2, status1 = run_container_command(
command_context, "laconicd", f"laconicd validate-genesis --home {laconicd_home_path_in_container}", mounts)
@ -267,7 +267,7 @@ def setup(command_context: DeployCommandContext, parameters: LaconicStackSetupCo
sys.exit(1)
def create(deployment_context: DeploymentContext, extra_args):
def create(context: DeploymentContext, extra_args):
network_dir = extra_args[0]
if network_dir is None:
print("Error: --network-dir must be supplied")
@ -286,15 +286,15 @@ def create(deployment_context: DeploymentContext, extra_args):
sys.exit(1)
# Copy the network directory contents into our deployment
# TODO: change this to work with non local paths
deployment_config_dir = deployment_context.deployment_dir.joinpath("data", "laconicd-config")
deployment_config_dir = context.deployment_dir.joinpath("data", "laconicd-config")
copytree(config_dir_path, deployment_config_dir, dirs_exist_ok=True)
# If supplied, add the initial persistent peers to the config file
if extra_args[1]:
initial_persistent_peers = extra_args[1]
_insert_persistent_peers(deployment_config_dir, initial_persistent_peers)
_insert_persistent_peers(context.command_context.cluster_context.options, deployment_config_dir, initial_persistent_peers)
# Copy the data directory contents into our deployment
# TODO: change this to work with non local paths
deployment_data_dir = deployment_context.deployment_dir.joinpath("data", "laconicd-data")
deployment_data_dir = context.deployment_dir.joinpath("data", "laconicd-data")
copytree(data_dir_path, deployment_data_dir, dirs_exist_ok=True)

View File

@ -24,7 +24,7 @@ import uuid
import click
from stack_orchestrator.deploy.images import remote_image_exists
from stack_orchestrator.deploy.images import remote_image_exists, add_tags_to_image
from stack_orchestrator.deploy.webapp import deploy_webapp
from stack_orchestrator.deploy.webapp.util import (LaconicRegistryClient, TimedLogger,
build_container_image, push_container_image,
@ -99,62 +99,44 @@ def process_app_deployment_request(
deployment_record = laconic.get_record(app_deployment_crn)
deployment_dir = os.path.join(deployment_parent_dir, fqdn)
# At present we use this to generate a unique but stable ID for the app's host container
# TODO: implement support to derive this transparently from the already-unique deployment id
unique_deployment_id = hashlib.md5(fqdn.encode()).hexdigest()[:16]
deployment_config_file = os.path.join(deployment_dir, "config.env")
deployment_container_tag = "laconic-webapp/%s:local" % unique_deployment_id
# TODO: Is there any reason not to simplify the hash input to the app_deployment_crn?
deployment_container_tag = "laconic-webapp/%s:local" % hashlib.md5(deployment_dir.encode()).hexdigest()
app_image_shared_tag = f"laconic-webapp/{app.id}:local"
# b. check for deployment directory (create if necessary)
if not os.path.exists(deployment_dir):
if deployment_record:
raise Exception("Deployment record %s exists, but not deployment dir %s. Please remove name." %
(app_deployment_crn, deployment_dir))
logger.log(f"Creating webapp deployment in: {deployment_dir} with container id: {deployment_container_tag}")
print("deploy_webapp", deployment_dir)
deploy_webapp.create_deployment(ctx, deployment_dir, deployment_container_tag,
f"https://{fqdn}", kube_config, image_registry, env_filename)
elif env_filename:
shutil.copyfile(env_filename, deployment_config_file)
needs_k8s_deploy = False
if force_rebuild:
logger.log("--force-rebuild is enabled so the container will always be built now, even if nothing has changed in the app")
# 6. build container (if needed)
# TODO: add a comment that explains what this code is doing (not clear to me)
if not deployment_record or deployment_record.attributes.application != app.id or force_rebuild:
if not deployment_record or deployment_record.attributes.application != app.id:
needs_k8s_deploy = True
# check if the image already exists
shared_tag_exists = remote_image_exists(image_registry, app_image_shared_tag)
# Note: in the code below, calls to add_tags_to_image() won't work at present.
# This is because SO deployment code in general re-names the container image
# to be unique to the deployment. This is done transparently
# and so when we call add_tags_to_image() here and try to add tags to the remote image,
# we get the image name wrong. Accordingly I've disabled the relevant code for now.
# This is safe because we are running with --force-rebuild at present
if shared_tag_exists and not force_rebuild:
# simply add our unique tag to the existing image and we are done
logger.log(
f"(SKIPPED) Existing image found for this app: {app_image_shared_tag} "
"tagging it with: {deployment_container_tag} to use in this deployment"
)
# add_tags_to_image(image_registry, app_image_shared_tag, deployment_container_tag)
logger.log(f"Using existing app image {app_image_shared_tag} for {deployment_container_tag}")
add_tags_to_image(image_registry, app_image_shared_tag, deployment_container_tag)
logger.log("Tag complete")
else:
extra_build_args = [] # TODO: pull from request
logger.log(f"Building container image: {deployment_container_tag}")
logger.log(f"Building container image {deployment_container_tag}")
build_container_image(app, deployment_container_tag, extra_build_args, logger)
logger.log("Build complete")
logger.log(f"Pushing container image: {deployment_container_tag}")
logger.log(f"Pushing container image {deployment_container_tag}")
push_container_image(deployment_dir, logger)
logger.log("Push complete")
# The build/push commands above will use the unique deployment tag, so now we need to add the shared tag.
logger.log(
f"(SKIPPED) Adding global app image tag: {app_image_shared_tag} to newly built image: {deployment_container_tag}"
)
# add_tags_to_image(image_registry, deployment_container_tag, app_image_shared_tag)
logger.log(f"Updating app image tag {app_image_shared_tag} from build of {deployment_container_tag}")
add_tags_to_image(image_registry, deployment_container_tag, app_image_shared_tag)
logger.log("Tag complete")
else:
logger.log("Requested app is already deployed, skipping build and image push")
# 7. update config (if needed)
if not deployment_record or file_hash(deployment_config_file) != deployment_record.attributes.meta.config:

View File

@ -242,7 +242,6 @@ def determine_base_container(clone_dir, app_type="webapp"):
def build_container_image(app_record, tag, extra_build_args=[], logger=None):
tmpdir = tempfile.mkdtemp()
# TODO: determine if this code could be calling into the Python git library like setup-repositories
try:
record_id = app_record["id"]
ref = app_record.attributes.repository_ref
@ -250,16 +249,6 @@ def build_container_image(app_record, tag, extra_build_args=[], logger=None):
clone_dir = os.path.join(tmpdir, record_id)
logger.log(f"Cloning repository {repo} to {clone_dir} ...")
# Set github credentials if present running a command like:
# git config --global url."https://${TOKEN}:@github.com/".insteadOf "https://github.com/"
github_token = os.environ.get("DEPLOYER_GITHUB_TOKEN")
if github_token:
logger.log("Github token detected, setting it in the git environment")
git_config_args = [
"git", "config", "--global", f"url.https://{github_token}:@github.com/.insteadOf", "https://github.com/"
]
result = subprocess.run(git_config_args, stdout=logger.file, stderr=logger.file)
result.check_returncode()
if ref:
# TODO: Determing branch or hash, and use depth 1 if we can.
git_env = dict(os.environ.copy())
@ -276,7 +265,6 @@ def build_container_image(app_record, tag, extra_build_args=[], logger=None):
logger.log(f"git checkout failed. Does ref {ref} exist?")
raise e
else:
# TODO: why is this code different vs the branch above (run vs check_call, and no prompt disable)?
result = subprocess.run(["git", "clone", "--depth", "1", repo, clone_dir], stdout=logger.file, stderr=logger.file)
result.check_returncode()
@ -311,12 +299,11 @@ def push_container_image(deployment_dir, logger):
def deploy_to_k8s(deploy_record, deployment_dir, logger):
if not deploy_record:
command = "start"
command = "up"
else:
command = "update"
logger.log("Deploying to k8s ...")
logger.log(f"Running {command} command on deployment dir: {deployment_dir}")
result = subprocess.run([sys.argv[0], "deployment", "--dir", deployment_dir, command],
stdout=logger.file, stderr=logger.file)
result.check_returncode()