From 937b983ec99ee06c7198efbf278452881942fc05 Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Tue, 6 Feb 2024 22:42:44 +0000 Subject: [PATCH 1/9] Update links from github.com to git.vdb.to (#732) Update links and references to github.com to git.vdb.to. Also enable the flake8 lint action in gitea. Reviewed-on: https://git.vdb.to/cerc-io/stack-orchestrator/pulls/732 Co-authored-by: Thomas E Lackey Co-committed-by: Thomas E Lackey --- .gitea/workflows/lint.yml | 21 +++++++++++++++++++ README.md | 6 +++--- docs/CONTRIBUTING.md | 2 +- docs/adding-a-new-stack.md | 4 ++-- docs/spec.md | 2 +- scripts/cloud-init-dev-mode-install.yaml | 2 +- scripts/cloud-init-user-mode-install.yaml | 2 +- scripts/quick-install-linux.sh | 2 +- setup.py | 2 +- .../data/stacks/fixturenet-eth/README.md | 2 +- .../fixturenet-laconic-loaded/README.md | 4 ++-- .../data/stacks/fixturenet-laconicd/README.md | 4 ++-- .../data/stacks/mobymask/README.md | 2 +- 13 files changed, 38 insertions(+), 17 deletions(-) create mode 100644 .gitea/workflows/lint.yml diff --git a/.gitea/workflows/lint.yml b/.gitea/workflows/lint.yml new file mode 100644 index 00000000..f9be1e6b --- /dev/null +++ b/.gitea/workflows/lint.yml @@ -0,0 +1,21 @@ +name: Lint Checks + +on: + pull_request: + branches: '*' + push: + branches: '*' + +jobs: + test: + name: "Run linter" + runs-on: ubuntu-latest + steps: + - name: "Clone project repository" + uses: actions/checkout@v3 + - name: "Install Python" + uses: actions/setup-python@v4 + with: + python-version: '3.8' + - name : "Run flake8" + uses: py-actions/flake8@v2 diff --git a/README.md b/README.md index aa979e3a..ef504295 100644 --- a/README.md +++ b/README.md @@ -29,10 +29,10 @@ chmod +x ~/.docker/cli-plugins/docker-compose Next decide on a directory where you would like to put the stack-orchestrator program. Typically this would be a "user" binary directory such as `~/bin` or perhaps `/usr/local/laconic` or possibly just the current working directory. -Now, having selected that directory, download the latest release from [this page](https://github.com/cerc-io/stack-orchestrator/tags) into it (we're using `~/bin` below for concreteness but edit to suit if you selected a different directory). Also be sure that the destination directory exists and is writable: +Now, having selected that directory, download the latest release from [this page](https://git.vdb.to/cerc-io/stack-orchestrator/tags) into it (we're using `~/bin` below for concreteness but edit to suit if you selected a different directory). Also be sure that the destination directory exists and is writable: ```bash -curl -L -o ~/bin/laconic-so https://github.com/cerc-io/stack-orchestrator/releases/latest/download/laconic-so +curl -L -o ~/bin/laconic-so https://git.vdb.to/cerc-io/stack-orchestrator/releases/download/latest/laconic-so ``` Give it execute permissions: @@ -52,7 +52,7 @@ Version: 1.1.0-7a607c2-202304260513 Save the distribution url to `~/.laconic-so/config.yml`: ```bash mkdir ~/.laconic-so -echo "distribution-url: https://github.com/cerc-io/stack-orchestrator/releases/latest/download/laconic-so" > ~/.laconic-so/config.yml +echo "distribution-url: https://git.vdb.to/cerc-io/stack-orchestrator/releases/download/latest/laconic-so" > ~/.laconic-so/config.yml ``` ### Update diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 39b294fb..375a2d05 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -26,7 +26,7 @@ In addition to the pre-requisites listed in the [README](/README.md), the follow 1. Clone this repository: ``` - $ git clone https://github.com/cerc-io/stack-orchestrator.git + $ git clone https://git.vdb.to/cerc-io/stack-orchestrator.git ``` 2. Enter the project directory: diff --git a/docs/adding-a-new-stack.md b/docs/adding-a-new-stack.md index 2b2d1a65..1e42420e 100644 --- a/docs/adding-a-new-stack.md +++ b/docs/adding-a-new-stack.md @@ -1,10 +1,10 @@ # Adding a new stack -See [this PR](https://github.com/cerc-io/stack-orchestrator/pull/434) for an example of how to currently add a minimal stack to stack orchestrator. The [reth stack](https://github.com/cerc-io/stack-orchestrator/pull/435) is another good example. +See [this PR](https://git.vdb.to/cerc-io/stack-orchestrator/pull/434) for an example of how to currently add a minimal stack to stack orchestrator. The [reth stack](https://git.vdb.to/cerc-io/stack-orchestrator/pull/435) is another good example. For external developers, we recommend forking this repo and adding your stack directly to your fork. This initially requires running in "developer mode" as described [here](/docs/CONTRIBUTING.md). Check out the [Namada stack](https://github.com/vknowable/stack-orchestrator/blob/main/app/data/stacks/public-namada/digitalocean_quickstart.md) from Knowable to see how that is done. -Core to the feature completeness of stack orchestrator is to [decouple the tool functionality from payload](https://github.com/cerc-io/stack-orchestrator/issues/315) which will no longer require forking to add a stack. +Core to the feature completeness of stack orchestrator is to [decouple the tool functionality from payload](https://git.vdb.to/cerc-io/stack-orchestrator/issues/315) which will no longer require forking to add a stack. ## Example diff --git a/docs/spec.md b/docs/spec.md index 1dc9ac62..aa09274c 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -1,6 +1,6 @@ # Specification -Note: this page is out of date (but still useful) - it will no longer be useful once stacks are [decoupled from the tool functionality](https://github.com/cerc-io/stack-orchestrator/issues/315). +Note: this page is out of date (but still useful) - it will no longer be useful once stacks are [decoupled from the tool functionality](https://git.vdb.to/cerc-io/stack-orchestrator/issues/315). ## Implementation diff --git a/scripts/cloud-init-dev-mode-install.yaml b/scripts/cloud-init-dev-mode-install.yaml index 965afe3a..7f9d9bd8 100644 --- a/scripts/cloud-init-dev-mode-install.yaml +++ b/scripts/cloud-init-dev-mode-install.yaml @@ -41,4 +41,4 @@ runcmd: - apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin - systemctl enable docker - systemctl start docker - - git clone https://github.com/cerc-io/stack-orchestrator.git /home/ubuntu/stack-orchestrator + - git clone https://git.vdb.to/cerc-io/stack-orchestrator.git /home/ubuntu/stack-orchestrator diff --git a/scripts/cloud-init-user-mode-install.yaml b/scripts/cloud-init-user-mode-install.yaml index bd02416c..f98f21bc 100644 --- a/scripts/cloud-init-user-mode-install.yaml +++ b/scripts/cloud-init-user-mode-install.yaml @@ -31,5 +31,5 @@ runcmd: - apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin - systemctl enable docker - systemctl start docker - - curl -L -o /usr/local/bin/laconic-so https://github.com/cerc-io/stack-orchestrator/releases/latest/download/laconic-so + - curl -L -o /usr/local/bin/laconic-so https://git.vdb.to/cerc-io/stack-orchestrator/releases/download/latest/laconic-so - chmod +x /usr/local/bin/laconic-so diff --git a/scripts/quick-install-linux.sh b/scripts/quick-install-linux.sh index 5670a403..b8642416 100755 --- a/scripts/quick-install-linux.sh +++ b/scripts/quick-install-linux.sh @@ -137,7 +137,7 @@ fi echo "**************************************************************************************" echo "Installing laconic-so" # install latest `laconic-so` -distribution_url=https://github.com/cerc-io/stack-orchestrator/releases/latest/download/laconic-so +distribution_url=https://git.vdb.to/cerc-io/stack-orchestrator/releases/download/latest/laconic-so install_filename=${install_dir}/laconic-so mkdir -p ${install_dir} curl -L -o ${install_filename} ${distribution_url} diff --git a/setup.py b/setup.py index d89dfc4d..773451f5 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ setup( description='Orchestrates deployment of the Laconic stack', long_description=long_description, long_description_content_type="text/markdown", - url='https://github.com/cerc-io/stack-orchestrator', + url='https://git.vdb.to/cerc-io/stack-orchestrator', py_modules=['stack_orchestrator'], packages=find_packages(), install_requires=[requirements], diff --git a/stack_orchestrator/data/stacks/fixturenet-eth/README.md b/stack_orchestrator/data/stacks/fixturenet-eth/README.md index 853ab90c..6f1ec8e8 100644 --- a/stack_orchestrator/data/stacks/fixturenet-eth/README.md +++ b/stack_orchestrator/data/stacks/fixturenet-eth/README.md @@ -1,6 +1,6 @@ # fixturenet-eth -Instructions for deploying a local a geth + lighthouse blockchain "fixturenet" for development and testing purposes using laconic-stack-orchestrator (the installation of which is covered [here](https://github.com/cerc-io/stack-orchestrator)): +Instructions for deploying a local a geth + lighthouse blockchain "fixturenet" for development and testing purposes using laconic-stack-orchestrator (the installation of which is covered [here](https://git.vdb.to/cerc-io/stack-orchestrator)): ## Clone required repositories diff --git a/stack_orchestrator/data/stacks/fixturenet-laconic-loaded/README.md b/stack_orchestrator/data/stacks/fixturenet-laconic-loaded/README.md index c82e48ad..94f9eb36 100644 --- a/stack_orchestrator/data/stacks/fixturenet-laconic-loaded/README.md +++ b/stack_orchestrator/data/stacks/fixturenet-laconic-loaded/README.md @@ -7,11 +7,11 @@ Instructions for deploying a local Laconic blockchain "fixturenet" for developme **Note:** For building some NPMs, access to the @lirewine repositories is required. If you don't have access, see [this tutorial](/docs/laconicd-fixturenet.md) to run this stack ## 1. Install Laconic Stack Orchestrator -Installation is covered in detail [here](https://github.com/cerc-io/stack-orchestrator#user-mode) but if you're on Linux and already have docker installed it should be as simple as: +Installation is covered in detail [here](https://git.vdb.to/cerc-io/stack-orchestrator#user-mode) but if you're on Linux and already have docker installed it should be as simple as: ``` $ mkdir my-working-dir $ cd my-working-dir -$ curl -L -o ./laconic-so https://github.com/cerc-io/stack-orchestrator/releases/latest/download/laconic-so +$ curl -L -o ./laconic-so https://git.vdb.to/cerc-io/stack-orchestrator/releases/download/latest/laconic-so $ chmod +x ./laconic-so $ export PATH=$PATH:$(pwd) # Or move laconic-so to ~/bin or your favorite on-path directory ``` diff --git a/stack_orchestrator/data/stacks/fixturenet-laconicd/README.md b/stack_orchestrator/data/stacks/fixturenet-laconicd/README.md index 77a017a2..1b04b875 100644 --- a/stack_orchestrator/data/stacks/fixturenet-laconicd/README.md +++ b/stack_orchestrator/data/stacks/fixturenet-laconicd/README.md @@ -3,11 +3,11 @@ Instructions for deploying a local Laconic blockchain "fixturenet" for development and testing purposes using laconic-stack-orchestrator. ## 1. Install Laconic Stack Orchestrator -Installation is covered in detail [here](https://github.com/cerc-io/stack-orchestrator#user-mode) but if you're on Linux and already have docker installed it should be as simple as: +Installation is covered in detail [here](https://git.vdb.to/cerc-io/stack-orchestrator#user-mode) but if you're on Linux and already have docker installed it should be as simple as: ``` $ mkdir my-working-dir $ cd my-working-dir -$ curl -L -o ./laconic-so https://github.com/cerc-io/stack-orchestrator/releases/latest/download/laconic-so +$ curl -L -o ./laconic-so https://git.vdb.to/cerc-io/stack-orchestrator/releases/download/latest/laconic-so $ chmod +x ./laconic-so $ export PATH=$PATH:$(pwd) # Or move laconic-so to ~/bin or your favorite on-path directory ``` diff --git a/stack_orchestrator/data/stacks/mobymask/README.md b/stack_orchestrator/data/stacks/mobymask/README.md index ef222cce..92a21c9d 100644 --- a/stack_orchestrator/data/stacks/mobymask/README.md +++ b/stack_orchestrator/data/stacks/mobymask/README.md @@ -4,7 +4,7 @@ The MobyMask watcher is a Laconic Network component that provides efficient acce ## Deploy the MobyMask Watcher -The instructions below show how to deploy a MobyMask watcher using laconic-stack-orchestrator (the installation of which is covered [here](https://github.com/cerc-io/stack-orchestrator#install)). +The instructions below show how to deploy a MobyMask watcher using laconic-stack-orchestrator (the installation of which is covered [here](https://git.vdb.to/cerc-io/stack-orchestrator#install)). This deployment expects that ipld-eth-server's endpoints are available on the local machine at http://ipld-eth-server.example.com:8083/graphql and http://ipld-eth-server.example.com:8082. More advanced configurations are supported by modifying the watcher's [config file](../../config/watcher-mobymask/mobymask-watcher.toml). From 88a0236ca9ce802b15b9da30a2dfc2dfff5fc42c Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Wed, 7 Feb 2024 03:12:40 +0000 Subject: [PATCH 2/9] Add the ability to filter deployment requests by tag. (#730) Reviewed-on: https://git.vdb.to/cerc-io/stack-orchestrator/pulls/730 --- .../webapp/deploy_webapp_from_registry.py | 74 ++++++++++++++----- stack_orchestrator/deploy/webapp/util.py | 20 ++--- 2 files changed, 68 insertions(+), 26 deletions(-) diff --git a/stack_orchestrator/deploy/webapp/deploy_webapp_from_registry.py b/stack_orchestrator/deploy/webapp/deploy_webapp_from_registry.py index 45f50377..4540ceac 100644 --- a/stack_orchestrator/deploy/webapp/deploy_webapp_from_registry.py +++ b/stack_orchestrator/deploy/webapp/deploy_webapp_from_registry.py @@ -19,6 +19,8 @@ import shlex import shutil import sys import tempfile +import time +import uuid import click @@ -39,8 +41,19 @@ def process_app_deployment_request( dns_suffix, deployment_parent_dir, kube_config, - image_registry + image_registry, + log_parent_dir ): + run_id = f"{app_deployment_request.id}-{str(time.time()).split('.')[0]}-{str(uuid.uuid4()).split('-')[0]}" + log_file = None + if log_parent_dir: + log_dir = os.path.join(log_parent_dir, app_deployment_request.id) + if not os.path.exists(log_dir): + os.mkdir(log_dir) + log_file_path = os.path.join(log_dir, f"{run_id}.log") + print(f"Directing build logs to: {log_file_path}") + log_file = open(log_file_path, "wt") + # 1. look up application app = laconic.get_record(app_deployment_request.attributes.application, require=True) @@ -102,8 +115,10 @@ def process_app_deployment_request( needs_k8s_deploy = False # 6. build container (if needed) if not deployment_record or deployment_record.attributes.application != app.id: - build_container_image(app, deployment_container_tag) - push_container_image(deployment_dir) + # TODO: pull from request + extra_build_args = [] + build_container_image(app, deployment_container_tag, extra_build_args, log_file) + push_container_image(deployment_dir, log_file) needs_k8s_deploy = True # 7. update config (if needed) @@ -116,6 +131,7 @@ def process_app_deployment_request( deploy_to_k8s( deployment_record, deployment_dir, + log_file ) publish_deployment( @@ -162,10 +178,14 @@ def dump_known_requests(filename, requests, status="SEEN"): @click.option("--record-namespace-dns", help="eg, crn://laconic/dns") @click.option("--record-namespace-deployments", help="eg, crn://laconic/deployments") @click.option("--dry-run", help="Don't do anything, just report what would be done.", is_flag=True) +@click.option("--include-tags", help="Only include requests with matching tags (comma-separated).", default="") +@click.option("--exclude-tags", help="Exclude requests with matching tags (comma-separated).", default="") +@click.option("--log-dir", help="Output build/deployment logs to directory.", default=None) @click.pass_context -def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_dir, +def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_dir, # noqa: C901 request_id, discover, state_file, only_update_state, - dns_suffix, record_namespace_dns, record_namespace_deployments, dry_run): + dns_suffix, record_namespace_dns, record_namespace_deployments, dry_run, + include_tags, exclude_tags, log_dir): if request_id and discover: print("Cannot specify both --request-id and --discover", file=sys.stderr) sys.exit(2) @@ -183,6 +203,10 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_ print("--dns-suffix, --record-namespace-dns, and --record-namespace-deployments are all required", file=sys.stderr) sys.exit(2) + # Split CSV and clean up values. + include_tags = [tag.strip() for tag in include_tags.split(",") if tag] + exclude_tags = [tag.strip() for tag in exclude_tags.split(",") if tag] + laconic = LaconicRegistryClient(laconic_config) # Find deployment requests. @@ -198,12 +222,24 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_ dump_known_requests(state_file, requests) return + def skip_by_tag(r): + for tag in exclude_tags: + if tag and r.attributes.tags and tag in r.attributes.tags: + return True + + for tag in include_tags: + if tag and (not r.attributes.tags or tag not in r.attributes.tags): + return True + + return False + previous_requests = load_known_requests(state_file) # Collapse related requests. requests.sort(key=lambda r: r.createTime) requests.reverse() requests_by_name = {} + skipped_by_name = {} for r in requests: # TODO: Do this _after_ filtering deployments and cancellations to minimize round trips. app = laconic.get_record(r.attributes.application) @@ -216,17 +252,20 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_ requested_name = generate_hostname_for_app(app) print("Generating name %s for request %s." % (requested_name, r.id)) - if requested_name not in requests_by_name: - print( - "Found request %s to run application %s on %s." - % (r.id, r.attributes.application, requested_name) - ) - requests_by_name[requested_name] = r - else: - print( - "Ignoring request %s, it is superseded by %s." - % (r.id, requests_by_name[requested_name].id) - ) + if requested_name in skipped_by_name or requested_name in requests_by_name: + print("Ignoring request %s, it has been superseded." % r.id) + continue + + if skip_by_tag(r): + print("Skipping request %s, filtered by tag (include %s, exclude %s, present %s)" % (r.id, + include_tags, + exclude_tags, + r.attributes.tags)) + skipped_by_name[requested_name] = r + continue + + print("Found request %s to run application %s on %s." % (r.id, r.attributes.application, requested_name)) + requests_by_name[requested_name] = r # Find deployments. deployments = laconic.app_deployments() @@ -273,7 +312,8 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_ dns_suffix, os.path.abspath(deployment_parent_dir), kube_config, - image_registry + image_registry, + log_dir ) status = "DEPLOYED" finally: diff --git a/stack_orchestrator/deploy/webapp/util.py b/stack_orchestrator/deploy/webapp/util.py index 50485110..74c8cd04 100644 --- a/stack_orchestrator/deploy/webapp/util.py +++ b/stack_orchestrator/deploy/webapp/util.py @@ -212,7 +212,7 @@ def determine_base_container(clone_dir, app_type="webapp"): return base_container -def build_container_image(app_record, tag, extra_build_args=[]): +def build_container_image(app_record, tag, extra_build_args=[], log_file=None): tmpdir = tempfile.mkdtemp() try: @@ -227,10 +227,10 @@ def build_container_image(app_record, tag, extra_build_args=[]): git_env = dict(os.environ.copy()) # Never prompt git_env["GIT_TERMINAL_PROMPT"] = "0" - subprocess.check_call(["git", "clone", repo, clone_dir], env=git_env) - subprocess.check_call(["git", "checkout", ref], cwd=clone_dir, env=git_env) + subprocess.check_call(["git", "clone", repo, clone_dir], env=git_env, stdout=log_file, stderr=log_file) + subprocess.check_call(["git", "checkout", ref], cwd=clone_dir, env=git_env, stdout=log_file, stderr=log_file) else: - result = subprocess.run(["git", "clone", "--depth", "1", repo, clone_dir]) + result = subprocess.run(["git", "clone", "--depth", "1", repo, clone_dir], stdout=log_file, stderr=log_file) result.check_returncode() base_container = determine_base_container(clone_dir, app_record.attributes.app_type) @@ -246,25 +246,27 @@ def build_container_image(app_record, tag, extra_build_args=[]): build_command.append("--extra-build-args") build_command.append(" ".join(extra_build_args)) - result = subprocess.run(build_command) + result = subprocess.run(build_command, stdout=log_file, stderr=log_file) result.check_returncode() finally: cmd("rm", "-rf", tmpdir) -def push_container_image(deployment_dir): +def push_container_image(deployment_dir, log_file=None): print("Pushing image ...") - result = subprocess.run([sys.argv[0], "deployment", "--dir", deployment_dir, "push-images"]) + result = subprocess.run([sys.argv[0], "deployment", "--dir", deployment_dir, "push-images"], + stdout=log_file, stderr=log_file) result.check_returncode() -def deploy_to_k8s(deploy_record, deployment_dir): +def deploy_to_k8s(deploy_record, deployment_dir, log_file=None): if not deploy_record: command = "up" else: command = "update" - result = subprocess.run([sys.argv[0], "deployment", "--dir", deployment_dir, command]) + result = subprocess.run([sys.argv[0], "deployment", "--dir", deployment_dir, command], + stdout=log_file, stderr=log_file) result.check_returncode() From 4a981d8d2e54b870fbfdbc6f3ef18e45c27a6aad Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Wed, 7 Feb 2024 18:52:07 +0000 Subject: [PATCH 3/9] Fix repo URL (#733) Needs a '/' (http) not ':' (ssh). Reviewed-on: https://git.vdb.to/cerc-io/stack-orchestrator/pulls/733 Co-authored-by: Thomas E Lackey Co-committed-by: Thomas E Lackey --- .../data/stacks/webapp-deployer-backend/stack.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stack_orchestrator/data/stacks/webapp-deployer-backend/stack.yml b/stack_orchestrator/data/stacks/webapp-deployer-backend/stack.yml index ca61049c..04000a1b 100644 --- a/stack_orchestrator/data/stacks/webapp-deployer-backend/stack.yml +++ b/stack_orchestrator/data/stacks/webapp-deployer-backend/stack.yml @@ -2,10 +2,10 @@ version: "1.0" name: webapp-deployer-backend description: "Deployer for webapps" repos: - - git.vdb.to:telackey/webapp-deployment-status-api + - git.vdb.to/telackey/webapp-deployment-status-api containers: - cerc/webapp-deployer-backend pods: - name: webapp-deployer-backend - repository: git.vdb.to:telackey/webapp-deployment-status-api + repository: git.vdb.to/telackey/webapp-deployment-status-api path: ./ From d2ebb81d773186897abad863917e790dd8d4c69b Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Wed, 7 Feb 2024 21:45:16 +0000 Subject: [PATCH 4/9] Tags for undeploy (#734) ``` --include-tags TEXT Only include requests with matching tags (comma-separated). --exclude-tags TEXT Exclude requests with matching tags (comma- separated). ``` Reviewed-on: https://git.vdb.to/cerc-io/stack-orchestrator/pulls/734 Co-authored-by: Thomas E Lackey Co-committed-by: Thomas E Lackey --- .../webapp/deploy_webapp_from_registry.py | 15 ++---------- .../webapp/undeploy_webapp_from_registry.py | 24 ++++++++++++++++--- stack_orchestrator/deploy/webapp/util.py | 12 ++++++++++ 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/stack_orchestrator/deploy/webapp/deploy_webapp_from_registry.py b/stack_orchestrator/deploy/webapp/deploy_webapp_from_registry.py index 4540ceac..299eeba9 100644 --- a/stack_orchestrator/deploy/webapp/deploy_webapp_from_registry.py +++ b/stack_orchestrator/deploy/webapp/deploy_webapp_from_registry.py @@ -29,7 +29,7 @@ from stack_orchestrator.deploy.webapp.util import (LaconicRegistryClient, build_container_image, push_container_image, file_hash, deploy_to_k8s, publish_deployment, hostname_for_deployment_request, generate_hostname_for_app, - match_owner) + match_owner, skip_by_tag) def process_app_deployment_request( @@ -222,17 +222,6 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_ dump_known_requests(state_file, requests) return - def skip_by_tag(r): - for tag in exclude_tags: - if tag and r.attributes.tags and tag in r.attributes.tags: - return True - - for tag in include_tags: - if tag and (not r.attributes.tags or tag not in r.attributes.tags): - return True - - return False - previous_requests = load_known_requests(state_file) # Collapse related requests. @@ -256,7 +245,7 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_ print("Ignoring request %s, it has been superseded." % r.id) continue - if skip_by_tag(r): + if skip_by_tag(r, include_tags, exclude_tags): print("Skipping request %s, filtered by tag (include %s, exclude %s, present %s)" % (r.id, include_tags, exclude_tags, diff --git a/stack_orchestrator/deploy/webapp/undeploy_webapp_from_registry.py b/stack_orchestrator/deploy/webapp/undeploy_webapp_from_registry.py index 74cf0d60..4aa2307d 100644 --- a/stack_orchestrator/deploy/webapp/undeploy_webapp_from_registry.py +++ b/stack_orchestrator/deploy/webapp/undeploy_webapp_from_registry.py @@ -20,7 +20,7 @@ import sys import click -from stack_orchestrator.deploy.webapp.util import LaconicRegistryClient, match_owner +from stack_orchestrator.deploy.webapp.util import LaconicRegistryClient, match_owner, skip_by_tag def process_app_removal_request(ctx, @@ -107,10 +107,12 @@ def dump_known_requests(filename, requests): @click.option("--delete-names/--preserve-names", help="Delete all names associated with removed deployments.", default=True) @click.option("--delete-volumes/--preserve-volumes", default=True, help="delete data volumes") @click.option("--dry-run", help="Don't do anything, just report what would be done.", is_flag=True) +@click.option("--include-tags", help="Only include requests with matching tags (comma-separated).", default="") +@click.option("--exclude-tags", help="Exclude requests with matching tags (comma-separated).", default="") @click.pass_context def command(ctx, laconic_config, deployment_parent_dir, request_id, discover, state_file, only_update_state, - delete_names, delete_volumes, dry_run): + delete_names, delete_volumes, dry_run, include_tags, exclude_tags): if request_id and discover: print("Cannot specify both --request-id and --discover", file=sys.stderr) sys.exit(2) @@ -123,6 +125,10 @@ def command(ctx, laconic_config, deployment_parent_dir, print("--only-update-state requires --state-file", file=sys.stderr) sys.exit(2) + # Split CSV and clean up values. + include_tags = [tag.strip() for tag in include_tags.split(",") if tag] + exclude_tags = [tag.strip() for tag in exclude_tags.split(",") if tag] + laconic = LaconicRegistryClient(laconic_config) # Find deployment removal requests. @@ -155,10 +161,22 @@ def command(ctx, laconic_config, deployment_parent_dir, # TODO: should we handle CRNs? removals_by_deployment[r.attributes.deployment] = r - requests_to_execute = [] + one_per_deployment = {} for r in requests: if not r.attributes.deployment: print(f"Skipping removal request {r.id} since it was a cancellation.") + elif r.attributes.deployment in one_per_deployment: + print(f"Skipping removal request {r.id} since it was superseded.") + else: + one_per_deployment[r.attributes.deployment] = r + + requests_to_execute = [] + for r in one_per_deployment.values(): + if skip_by_tag(r, include_tags, exclude_tags): + print("Skipping removal request %s, filtered by tag (include %s, exclude %s, present %s)" % (r.id, + include_tags, + exclude_tags, + r.attributes.tags)) elif r.id in removals_by_request: print(f"Found satisfied request for {r.id} at {removals_by_request[r.id].id}") elif r.attributes.deployment in removals_by_deployment: diff --git a/stack_orchestrator/deploy/webapp/util.py b/stack_orchestrator/deploy/webapp/util.py index 74c8cd04..6cdec448 100644 --- a/stack_orchestrator/deploy/webapp/util.py +++ b/stack_orchestrator/deploy/webapp/util.py @@ -351,3 +351,15 @@ def generate_hostname_for_app(app): else: m.update(app.attributes.repository.encode()) return "%s-%s" % (last_part, m.hexdigest()[0:10]) + + +def skip_by_tag(r, include_tags, exclude_tags): + for tag in exclude_tags: + if tag and r.attributes.tags and tag in r.attributes.tags: + return True + + for tag in include_tags: + if tag and (not r.attributes.tags or tag not in r.attributes.tags): + return True + + return False From 8964e1c0fea2bbda79a0bc9457d6076d4778dc13 Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Wed, 7 Feb 2024 16:48:02 -0600 Subject: [PATCH 5/9] Add resource limit options to spec. --- stack_orchestrator/deploy/k8s/cluster_info.py | 28 ++++++++++++++++--- stack_orchestrator/deploy/spec.py | 6 ++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/stack_orchestrator/deploy/k8s/cluster_info.py b/stack_orchestrator/deploy/k8s/cluster_info.py index 35b2b9da..527fe7c5 100644 --- a/stack_orchestrator/deploy/k8s/cluster_info.py +++ b/stack_orchestrator/deploy/k8s/cluster_info.py @@ -28,6 +28,15 @@ from stack_orchestrator.deploy.deploy_types import DeployEnvVars from stack_orchestrator.deploy.spec import Spec from stack_orchestrator.deploy.images import remote_tag_for_image +DEFAULT_VOLUME_RESOURCES = { + "requests": {"storage": "2Gi"} +} + +DEFAULT_CONTAINER_RESOURCES = { + "requests": {"cpu": "100m", "memory": "200Mi"}, + "limits": {"cpu": "1000m", "memory": "2000Mi"}, +} + class ClusterInfo: parsed_pod_yaml_map: Any @@ -135,6 +144,9 @@ class ClusterInfo: result = [] spec_volumes = self.spec.get_volumes() named_volumes = named_volumes_from_pod_files(self.parsed_pod_yaml_map) + resources = self.spec.get_volume_resources() + if not resources: + resources = DEFAULT_VOLUME_RESOURCES if opts.o.debug: print(f"Spec Volumes: {spec_volumes}") print(f"Named Volumes: {named_volumes}") @@ -147,7 +159,8 @@ class ClusterInfo: access_modes=["ReadWriteOnce"], storage_class_name="manual", resources=client.V1ResourceRequirements( - requests={"storage": "2Gi"} + requests=resources.get("requests"), + limits=resources.get("limits") ), volume_name=f"{self.app_name}-{volume_name}" ) @@ -192,6 +205,9 @@ class ClusterInfo: result = [] spec_volumes = self.spec.get_volumes() named_volumes = named_volumes_from_pod_files(self.parsed_pod_yaml_map) + resources = self.spec.get_volume_resources() + if not resources: + resources = DEFAULT_VOLUME_RESOURCES for volume_name in spec_volumes: if volume_name not in named_volumes: if opts.o.debug: @@ -200,7 +216,7 @@ class ClusterInfo: spec = client.V1PersistentVolumeSpec( storage_class_name="manual", access_modes=["ReadWriteOnce"], - capacity={"storage": "2Gi"}, + capacity=resources.get("requests", DEFAULT_VOLUME_RESOURCES["requests"]), host_path=client.V1HostPathVolumeSource(path=get_node_pv_mount_path(volume_name)) ) pv = client.V1PersistentVolume( @@ -214,6 +230,10 @@ class ClusterInfo: # TODO: put things like image pull policy into an object-scope struct def get_deployment(self, image_pull_policy: str = None): containers = [] + resources = self.spec.get_container_resources() + if not resources: + resources = DEFAULT_CONTAINER_RESOURCES + print(resources) for pod_name in self.parsed_pod_yaml_map: pod = self.parsed_pod_yaml_map[pod_name] services = pod["services"] @@ -238,8 +258,8 @@ class ClusterInfo: ports=[client.V1ContainerPort(container_port=port)], volume_mounts=volume_mounts, resources=client.V1ResourceRequirements( - requests={"cpu": "100m", "memory": "200Mi"}, - limits={"cpu": "1000m", "memory": "2000Mi"}, + requests=resources.get("requests"), + limits=resources.get("limits") ), ) containers.append(container) diff --git a/stack_orchestrator/deploy/spec.py b/stack_orchestrator/deploy/spec.py index dd6cd107..e7fa8583 100644 --- a/stack_orchestrator/deploy/spec.py +++ b/stack_orchestrator/deploy/spec.py @@ -47,6 +47,12 @@ class Spec: if self.obj and "configmaps" in self.obj else {}) + def get_container_resources(self): + return self.obj.get("resources", {}).get("containers") + + def get_volume_resources(self): + return self.obj.get("resources", {}).get("volumes") + def get_http_proxy(self): return (self.obj[constants.network_key][constants.http_proxy_key] if self.obj and constants.network_key in self.obj From 2a9955055ce4452c0eba725036e3ec6c2c122043 Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Wed, 7 Feb 2024 16:56:35 -0600 Subject: [PATCH 6/9] debug --- stack_orchestrator/deploy/k8s/cluster_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stack_orchestrator/deploy/k8s/cluster_info.py b/stack_orchestrator/deploy/k8s/cluster_info.py index 527fe7c5..c65a299d 100644 --- a/stack_orchestrator/deploy/k8s/cluster_info.py +++ b/stack_orchestrator/deploy/k8s/cluster_info.py @@ -150,6 +150,7 @@ class ClusterInfo: if opts.o.debug: print(f"Spec Volumes: {spec_volumes}") print(f"Named Volumes: {named_volumes}") + print(f"Resources: {resources}") for volume_name in spec_volumes: if volume_name not in named_volumes: if opts.o.debug: @@ -233,7 +234,6 @@ class ClusterInfo: resources = self.spec.get_container_resources() if not resources: resources = DEFAULT_CONTAINER_RESOURCES - print(resources) for pod_name in self.parsed_pod_yaml_map: pod = self.parsed_pod_yaml_map[pod_name] services = pod["services"] From 4b3b3478e7b931aec88e6509e2acdba7b44fc358 Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Thu, 8 Feb 2024 00:43:41 -0600 Subject: [PATCH 7/9] Switch to Docker-style limits --- requirements.txt | 1 + stack_orchestrator/deploy/k8s/cluster_info.py | 48 +++++++++++------ stack_orchestrator/deploy/spec.py | 54 +++++++++++++++++-- 3 files changed, 83 insertions(+), 20 deletions(-) diff --git a/requirements.txt b/requirements.txt index bbf97b4a..f6e3d07c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,3 +10,4 @@ pydantic==1.10.9 tomli==2.0.1 validators==0.22.0 kubernetes>=28.1.0 +humanfriendly>=10.0 diff --git a/stack_orchestrator/deploy/k8s/cluster_info.py b/stack_orchestrator/deploy/k8s/cluster_info.py index c65a299d..6f7d0cec 100644 --- a/stack_orchestrator/deploy/k8s/cluster_info.py +++ b/stack_orchestrator/deploy/k8s/cluster_info.py @@ -25,17 +25,37 @@ from stack_orchestrator.deploy.k8s.helpers import get_node_pv_mount_path from stack_orchestrator.deploy.k8s.helpers import envs_from_environment_variables_map from stack_orchestrator.deploy.deploy_util import parsed_pod_files_map_from_file_names, images_for_deployment from stack_orchestrator.deploy.deploy_types import DeployEnvVars -from stack_orchestrator.deploy.spec import Spec +from stack_orchestrator.deploy.spec import Spec, Resources, ResourceLimits from stack_orchestrator.deploy.images import remote_tag_for_image -DEFAULT_VOLUME_RESOURCES = { - "requests": {"storage": "2Gi"} -} +DEFAULT_VOLUME_RESOURCES = Resources({ + "reservations": {"storage": "2Gi"} +}) -DEFAULT_CONTAINER_RESOURCES = { - "requests": {"cpu": "100m", "memory": "200Mi"}, - "limits": {"cpu": "1000m", "memory": "2000Mi"}, -} +DEFAULT_CONTAINER_RESOURCES = Resources({ + "reservations": {"cpus": "0.1", "memory": "200M"}, + "limits": {"cpus": "1.0", "memory": "2000M"}, +}) + + +def get_k8s_resource_requirements(resources: Resources) -> client.V1ResourceRequirements: + def to_dict(limits: ResourceLimits): + if not limits: + return None + + ret = {} + if limits.cpus: + ret["cpu"] = str(limits.cpus) + if limits.memory: + ret["memory"] = f"{int(limits.memory / (1000 * 1000))}M" + if limits.storage: + ret["storage"] = f"{int(limits.storage / (1000 * 1000))}M" + return ret + + return client.V1ResourceRequirements( + requests=to_dict(resources.reservations), + limits=to_dict(resources.limits) + ) class ClusterInfo: @@ -159,10 +179,7 @@ class ClusterInfo: spec = client.V1PersistentVolumeClaimSpec( access_modes=["ReadWriteOnce"], storage_class_name="manual", - resources=client.V1ResourceRequirements( - requests=resources.get("requests"), - limits=resources.get("limits") - ), + resources=get_k8s_resource_requirements(resources), volume_name=f"{self.app_name}-{volume_name}" ) pvc = client.V1PersistentVolumeClaim( @@ -217,7 +234,7 @@ class ClusterInfo: spec = client.V1PersistentVolumeSpec( storage_class_name="manual", access_modes=["ReadWriteOnce"], - capacity=resources.get("requests", DEFAULT_VOLUME_RESOURCES["requests"]), + capacity=get_k8s_resource_requirements(resources).requests, host_path=client.V1HostPathVolumeSource(path=get_node_pv_mount_path(volume_name)) ) pv = client.V1PersistentVolume( @@ -257,10 +274,7 @@ class ClusterInfo: env=envs_from_environment_variables_map(self.environment_variables.map), ports=[client.V1ContainerPort(container_port=port)], volume_mounts=volume_mounts, - resources=client.V1ResourceRequirements( - requests=resources.get("requests"), - limits=resources.get("limits") - ), + resources=get_k8s_resource_requirements(resources), ) containers.append(container) volumes = volumes_for_pod_files(self.parsed_pod_yaml_map, self.spec, self.app_name) diff --git a/stack_orchestrator/deploy/spec.py b/stack_orchestrator/deploy/spec.py index e7fa8583..fa0489e7 100644 --- a/stack_orchestrator/deploy/spec.py +++ b/stack_orchestrator/deploy/spec.py @@ -13,12 +13,60 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from pathlib import Path import typing +import humanfriendly + +from pathlib import Path + from stack_orchestrator.util import get_yaml from stack_orchestrator import constants +class ResourceLimits: + cpus: float = None + memory: int = None + storage: int = None + + def __init__(self, obj={}): + if "cpus" in obj: + self.cpus = float(obj["cpus"]) + if "memory" in obj: + self.memory = humanfriendly.parse_size(obj["memory"]) + if "storage" in obj: + self.storage = humanfriendly.parse_size(obj["storage"]) + + def __len__(self): + return len(self.__dict__) + + def __iter__(self): + for k in self.__dict__: + yield k, self.__dict__[k] + + def __repr__(self): + return str(self.__dict__) + + +class Resources: + limits: ResourceLimits = None + reservations: ResourceLimits = None + + def __init__(self, obj={}): + if "reservations" in obj: + self.reservations = ResourceLimits(obj["reservations"]) + if "limits" in obj: + self.limits = ResourceLimits(obj["limits"]) + + def __len__(self): + return len(self.__dict__) + + def __iter__(self): + for k in self.__dict__: + yield k, self.__dict__[k] + + def __repr__(self): + return str(self.__dict__) + + class Spec: obj: typing.Any @@ -48,10 +96,10 @@ class Spec: else {}) def get_container_resources(self): - return self.obj.get("resources", {}).get("containers") + return Resources(self.obj.get("resources", {}).get("containers", {})) def get_volume_resources(self): - return self.obj.get("resources", {}).get("volumes") + return Resources(self.obj.get("resources", {}).get("volumes", {})) def get_http_proxy(self): return (self.obj[constants.network_key][constants.http_proxy_key] From 3309782439373ab252639c0010ae9369de4a8885 Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Thu, 8 Feb 2024 00:47:46 -0600 Subject: [PATCH 8/9] Refactor --- stack_orchestrator/deploy/k8s/cluster_info.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stack_orchestrator/deploy/k8s/cluster_info.py b/stack_orchestrator/deploy/k8s/cluster_info.py index 6f7d0cec..30b2ab11 100644 --- a/stack_orchestrator/deploy/k8s/cluster_info.py +++ b/stack_orchestrator/deploy/k8s/cluster_info.py @@ -38,7 +38,7 @@ DEFAULT_CONTAINER_RESOURCES = Resources({ }) -def get_k8s_resource_requirements(resources: Resources) -> client.V1ResourceRequirements: +def to_k8s_resource_requirements(resources: Resources) -> client.V1ResourceRequirements: def to_dict(limits: ResourceLimits): if not limits: return None @@ -179,7 +179,7 @@ class ClusterInfo: spec = client.V1PersistentVolumeClaimSpec( access_modes=["ReadWriteOnce"], storage_class_name="manual", - resources=get_k8s_resource_requirements(resources), + resources=to_k8s_resource_requirements(resources), volume_name=f"{self.app_name}-{volume_name}" ) pvc = client.V1PersistentVolumeClaim( @@ -234,7 +234,7 @@ class ClusterInfo: spec = client.V1PersistentVolumeSpec( storage_class_name="manual", access_modes=["ReadWriteOnce"], - capacity=get_k8s_resource_requirements(resources).requests, + capacity=to_k8s_resource_requirements(resources).requests, host_path=client.V1HostPathVolumeSource(path=get_node_pv_mount_path(volume_name)) ) pv = client.V1PersistentVolume( @@ -274,7 +274,7 @@ class ClusterInfo: env=envs_from_environment_variables_map(self.environment_variables.map), ports=[client.V1ContainerPort(container_port=port)], volume_mounts=volume_mounts, - resources=get_k8s_resource_requirements(resources), + resources=to_k8s_resource_requirements(resources), ) containers.append(container) volumes = volumes_for_pod_files(self.parsed_pod_yaml_map, self.spec, self.app_name) From 8be1e684e8422d16c128362b960f159b84a5a330 Mon Sep 17 00:00:00 2001 From: David Boreham Date: Thu, 8 Feb 2024 19:41:57 +0000 Subject: [PATCH 9/9] Process environment variables defined in compose files (#736) Reviewed-on: https://git.vdb.to/cerc-io/stack-orchestrator/pulls/736 Co-authored-by: David Boreham Co-committed-by: David Boreham --- .../data/compose/docker-compose-test.yml | 1 + .../cerc-test-container/run.sh | 3 ++ stack_orchestrator/deploy/k8s/cluster_info.py | 11 ++++- stack_orchestrator/deploy/k8s/helpers.py | 28 +++++++++++++ tests/deploy/run-deploy-test.sh | 41 +++++++++++++++++++ tests/k8s-deploy/run-deploy-test.sh | 9 ++++ 6 files changed, 91 insertions(+), 2 deletions(-) diff --git a/stack_orchestrator/data/compose/docker-compose-test.yml b/stack_orchestrator/data/compose/docker-compose-test.yml index 50151f65..5c9e7f9b 100644 --- a/stack_orchestrator/data/compose/docker-compose-test.yml +++ b/stack_orchestrator/data/compose/docker-compose-test.yml @@ -5,6 +5,7 @@ services: environment: CERC_SCRIPT_DEBUG: ${CERC_SCRIPT_DEBUG} CERC_TEST_PARAM_1: ${CERC_TEST_PARAM_1:-FAILED} + CERC_TEST_PARAM_2: "CERC_TEST_PARAM_2_VALUE" volumes: - test-data:/data - test-config:/config:ro diff --git a/stack_orchestrator/data/container-build/cerc-test-container/run.sh b/stack_orchestrator/data/container-build/cerc-test-container/run.sh index 01fb874b..fa860951 100755 --- a/stack_orchestrator/data/container-build/cerc-test-container/run.sh +++ b/stack_orchestrator/data/container-build/cerc-test-container/run.sh @@ -17,6 +17,9 @@ fi if [ -n "$CERC_TEST_PARAM_1" ]; then echo "Test-param-1: ${CERC_TEST_PARAM_1}" fi +if [ -n "$CERC_TEST_PARAM_2" ]; then + echo "Test-param-2: ${CERC_TEST_PARAM_2}" +fi if [ -d "/config" ]; then echo "/config: EXISTS" diff --git a/stack_orchestrator/deploy/k8s/cluster_info.py b/stack_orchestrator/deploy/k8s/cluster_info.py index 30b2ab11..17f75651 100644 --- a/stack_orchestrator/deploy/k8s/cluster_info.py +++ b/stack_orchestrator/deploy/k8s/cluster_info.py @@ -22,7 +22,7 @@ from stack_orchestrator.opts import opts from stack_orchestrator.util import env_var_map_from_file from stack_orchestrator.deploy.k8s.helpers import named_volumes_from_pod_files, volume_mounts_for_service, volumes_for_pod_files from stack_orchestrator.deploy.k8s.helpers import get_node_pv_mount_path -from stack_orchestrator.deploy.k8s.helpers import envs_from_environment_variables_map +from stack_orchestrator.deploy.k8s.helpers import envs_from_environment_variables_map, envs_from_compose_file, merge_envs from stack_orchestrator.deploy.deploy_util import parsed_pod_files_map_from_file_names, images_for_deployment from stack_orchestrator.deploy.deploy_types import DeployEnvVars from stack_orchestrator.deploy.spec import Spec, Resources, ResourceLimits @@ -263,6 +263,13 @@ class ClusterInfo: if opts.o.debug: print(f"image: {image}") print(f"service port: {port}") + merged_envs = merge_envs( + envs_from_compose_file( + service_info["environment"]), self.environment_variables.map + ) if "environment" in service_info else self.environment_variables.map + envs = envs_from_environment_variables_map(merged_envs) + if opts.o.debug: + print(f"Merged envs: {envs}") # Re-write the image tag for remote deployment image_to_use = remote_tag_for_image( image, self.spec.get_image_registry()) if self.spec.get_image_registry() is not None else image @@ -271,7 +278,7 @@ class ClusterInfo: name=container_name, image=image_to_use, image_pull_policy=image_pull_policy, - env=envs_from_environment_variables_map(self.environment_variables.map), + env=envs, ports=[client.V1ContainerPort(container_port=port)], volume_mounts=volume_mounts, resources=to_k8s_resource_requirements(resources), diff --git a/stack_orchestrator/deploy/k8s/helpers.py b/stack_orchestrator/deploy/k8s/helpers.py index e386b353..1fe697e7 100644 --- a/stack_orchestrator/deploy/k8s/helpers.py +++ b/stack_orchestrator/deploy/k8s/helpers.py @@ -17,6 +17,7 @@ from kubernetes import client import os from pathlib import Path import subprocess +import re from typing import Set, Mapping, List from stack_orchestrator.opts import opts @@ -214,6 +215,33 @@ def _generate_kind_port_mappings(parsed_pod_files): ) +# Note: this makes any duplicate definition in b overwrite a +def merge_envs(a: Mapping[str, str], b: Mapping[str, str]) -> Mapping[str, str]: + result = {**a, **b} + return result + + +def _expand_shell_vars(raw_val: str) -> str: + # could be: or ${} or ${:-} + # TODO: implement support for variable substitution and default values + # if raw_val is like ${} print a warning and substitute an empty string + # otherwise return raw_val + match = re.search(r"^\$\{(.*)\}$", raw_val) + if match: + print(f"WARNING: found unimplemented environment variable substitution: {raw_val}") + else: + return raw_val + + +# TODO: handle the case where the same env var is defined in multiple places +def envs_from_compose_file(compose_file_envs: Mapping[str, str]) -> Mapping[str, str]: + result = {} + for env_var, env_val in compose_file_envs.items(): + expanded_env_val = _expand_shell_vars(env_val) + result.update({env_var: expanded_env_val}) + return result + + def envs_from_environment_variables_map(map: Mapping[str, str]) -> List[client.V1EnvVar]: result = [] for env_var, env_val in map.items(): diff --git a/tests/deploy/run-deploy-test.sh b/tests/deploy/run-deploy-test.sh index 4456c28d..f7a008f3 100755 --- a/tests/deploy/run-deploy-test.sh +++ b/tests/deploy/run-deploy-test.sh @@ -6,6 +6,12 @@ fi # Dump environment variables for debugging echo "Environment variables:" env + +delete_cluster_exit () { + $TEST_TARGET_SO deployment --dir $test_deployment_dir stop --delete-volumes + exit 1 +} + # Test basic stack-orchestrator deploy echo "Running stack-orchestrator deploy test" # Bit of a hack, test the most recent package @@ -106,6 +112,10 @@ if [ ! "$create_file_content" == "create-command-output-data" ]; then echo "deploy create test: FAILED" exit 1 fi + +# Add a config file to be picked up by the ConfigMap before starting. +echo "dbfc7a4d-44a7-416d-b5f3-29842cc47650" > $test_deployment_dir/data/test-config/test_config + echo "deploy create output file test: passed" # Try to start the deployment $TEST_TARGET_SO deployment --dir $test_deployment_dir start @@ -124,6 +134,37 @@ else echo "deployment config test: FAILED" exit 1 fi +# Check the config variable CERC_TEST_PARAM_2 was passed correctly from the compose file +if [[ "$log_output_3" == *"Test-param-2: CERC_TEST_PARAM_2_VALUE"* ]]; then + echo "deployment compose config test: passed" +else + echo "deployment compose config test: FAILED" + exit 1 +fi + +# Check that the ConfigMap is mounted and contains the expected content. +log_output_4=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs ) +if [[ "$log_output_4" == *"/config/test_config:"* ]] && [[ "$log_output_4" == *"dbfc7a4d-44a7-416d-b5f3-29842cc47650"* ]]; then + echo "deployment ConfigMap test: passed" +else + echo "deployment ConfigMap test: FAILED" + delete_cluster_exit +fi + +# Stop then start again and check the volume was preserved +$TEST_TARGET_SO deployment --dir $test_deployment_dir stop +# Sleep a bit just in case +# sleep for longer to check if that's why the subsequent create cluster fails +sleep 20 +$TEST_TARGET_SO deployment --dir $test_deployment_dir start +log_output_5=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs ) +if [[ "$log_output_5" == *"Filesystem is old"* ]]; then + echo "Retain volumes test: passed" +else + echo "Retain volumes test: FAILED" + delete_cluster_exit +fi + # Stop and clean up $TEST_TARGET_SO deployment --dir $test_deployment_dir stop --delete-volumes echo "Test passed" diff --git a/tests/k8s-deploy/run-deploy-test.sh b/tests/k8s-deploy/run-deploy-test.sh index 04008217..4b17ae49 100755 --- a/tests/k8s-deploy/run-deploy-test.sh +++ b/tests/k8s-deploy/run-deploy-test.sh @@ -114,6 +114,7 @@ else echo "deployment logs test: FAILED" delete_cluster_exit fi + # Check the config variable CERC_TEST_PARAM_1 was passed correctly if [[ "$log_output_3" == *"Test-param-1: PASSED"* ]]; then echo "deployment config test: passed" @@ -122,6 +123,14 @@ else delete_cluster_exit fi +# Check the config variable CERC_TEST_PARAM_2 was passed correctly from the compose file +if [[ "$log_output_3" == *"Test-param-2: CERC_TEST_PARAM_2_VALUE"* ]]; then + echo "deployment compose config test: passed" +else + echo "deployment compose config test: FAILED" + exit 1 +fi + # Check that the ConfigMap is mounted and contains the expected content. log_output_4=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs ) if [[ "$log_output_4" == *"/config/test_config:"* ]] && [[ "$log_output_4" == *"dbfc7a4d-44a7-416d-b5f3-29842cc47650"* ]]; then