lint
Some checks failed
Lint Checks / Run linter (pull_request) Failing after 35s
Webapp Test / Run webapp test suite (pull_request) Successful in 5m42s
Deploy Test / Run deploy test suite (pull_request) Successful in 6m48s
K8s Deployment Control Test / Run deployment control suite on kind/k8s (pull_request) Successful in 9m11s
K8s Deploy Test / Run deploy test suite on kind/k8s (pull_request) Successful in 10m26s
Smoke Test / Run basic test suite (pull_request) Successful in 4m27s
Some checks failed
Lint Checks / Run linter (pull_request) Failing after 35s
Webapp Test / Run webapp test suite (pull_request) Successful in 5m42s
Deploy Test / Run deploy test suite (pull_request) Successful in 6m48s
K8s Deployment Control Test / Run deployment control suite on kind/k8s (pull_request) Successful in 9m11s
K8s Deploy Test / Run deploy test suite on kind/k8s (pull_request) Successful in 10m26s
Smoke Test / Run basic test suite (pull_request) Successful in 4m27s
This commit is contained in:
parent
75ff60752a
commit
145271464b
@ -11,3 +11,5 @@ tomli==2.0.1
|
|||||||
validators==0.22.0
|
validators==0.22.0
|
||||||
kubernetes>=28.1.0
|
kubernetes>=28.1.0
|
||||||
humanfriendly>=10.0
|
humanfriendly>=10.0
|
||||||
|
python-gnupg>=0.5.2
|
||||||
|
requests>=2.3.2
|
||||||
|
@ -27,7 +27,9 @@ from stack_orchestrator.deploy.deploy_types import DeployCommandContext
|
|||||||
|
|
||||||
def _fixup_container_tag(deployment_dir: str, image: str):
|
def _fixup_container_tag(deployment_dir: str, image: str):
|
||||||
deployment_dir_path = Path(deployment_dir)
|
deployment_dir_path = Path(deployment_dir)
|
||||||
compose_file = deployment_dir_path.joinpath("compose", "docker-compose-webapp-template.yml")
|
compose_file = deployment_dir_path.joinpath(
|
||||||
|
"compose", "docker-compose-webapp-template.yml"
|
||||||
|
)
|
||||||
# replace "cerc/webapp-container:local" in the file with our image tag
|
# replace "cerc/webapp-container:local" in the file with our image tag
|
||||||
with open(compose_file) as rfile:
|
with open(compose_file) as rfile:
|
||||||
contents = rfile.read()
|
contents = rfile.read()
|
||||||
@ -39,13 +41,13 @@ def _fixup_container_tag(deployment_dir: str, image: str):
|
|||||||
def _fixup_url_spec(spec_file_name: str, url: str):
|
def _fixup_url_spec(spec_file_name: str, url: str):
|
||||||
# url is like: https://example.com/path
|
# url is like: https://example.com/path
|
||||||
parsed_url = urlparse(url)
|
parsed_url = urlparse(url)
|
||||||
http_proxy_spec = f'''
|
http_proxy_spec = f"""
|
||||||
http-proxy:
|
http-proxy:
|
||||||
- host-name: {parsed_url.hostname}
|
- host-name: {parsed_url.hostname}
|
||||||
routes:
|
routes:
|
||||||
- path: '{parsed_url.path if parsed_url.path else "/"}'
|
- path: '{parsed_url.path if parsed_url.path else "/"}'
|
||||||
proxy-to: webapp:80
|
proxy-to: webapp:80
|
||||||
'''
|
"""
|
||||||
spec_file_path = Path(spec_file_name)
|
spec_file_path = Path(spec_file_name)
|
||||||
with open(spec_file_path) as rfile:
|
with open(spec_file_path) as rfile:
|
||||||
contents = rfile.read()
|
contents = rfile.read()
|
||||||
@ -54,7 +56,9 @@ def _fixup_url_spec(spec_file_name: str, url: str):
|
|||||||
wfile.write(contents)
|
wfile.write(contents)
|
||||||
|
|
||||||
|
|
||||||
def create_deployment(ctx, deployment_dir, image, url, kube_config, image_registry, env_file):
|
def create_deployment(
|
||||||
|
ctx, deployment_dir, image, url, kube_config, image_registry, env_file
|
||||||
|
):
|
||||||
# Do the equivalent of:
|
# Do the equivalent of:
|
||||||
# 1. laconic-so --stack webapp-template deploy --deploy-to k8s init --output webapp-spec.yml
|
# 1. laconic-so --stack webapp-template deploy --deploy-to k8s init --output webapp-spec.yml
|
||||||
# --config (eqivalent of the contents of my-config.env)
|
# --config (eqivalent of the contents of my-config.env)
|
||||||
@ -83,17 +87,11 @@ def create_deployment(ctx, deployment_dir, image, url, kube_config, image_regist
|
|||||||
kube_config,
|
kube_config,
|
||||||
image_registry,
|
image_registry,
|
||||||
spec_file_name,
|
spec_file_name,
|
||||||
None
|
None,
|
||||||
)
|
)
|
||||||
# Add the TLS and DNS spec
|
# Add the TLS and DNS spec
|
||||||
_fixup_url_spec(spec_file_name, url)
|
_fixup_url_spec(spec_file_name, url)
|
||||||
create_operation(
|
create_operation(deploy_command_context, spec_file_name, deployment_dir, None, None)
|
||||||
deploy_command_context,
|
|
||||||
spec_file_name,
|
|
||||||
deployment_dir,
|
|
||||||
None,
|
|
||||||
None
|
|
||||||
)
|
|
||||||
# Fix up the container tag inside the deployment compose file
|
# Fix up the container tag inside the deployment compose file
|
||||||
_fixup_container_tag(deployment_dir, image)
|
_fixup_container_tag(deployment_dir, image)
|
||||||
os.remove(spec_file_name)
|
os.remove(spec_file_name)
|
||||||
@ -102,7 +100,7 @@ def create_deployment(ctx, deployment_dir, image, url, kube_config, image_regist
|
|||||||
@click.group()
|
@click.group()
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def command(ctx):
|
def command(ctx):
|
||||||
'''manage a webapp deployment'''
|
"""manage a webapp deployment"""
|
||||||
|
|
||||||
# Check that --stack wasn't supplied
|
# Check that --stack wasn't supplied
|
||||||
if ctx.parent.obj.stack:
|
if ctx.parent.obj.stack:
|
||||||
@ -111,13 +109,20 @@ def command(ctx):
|
|||||||
|
|
||||||
@command.command()
|
@command.command()
|
||||||
@click.option("--kube-config", help="Provide a config file for a k8s deployment")
|
@click.option("--kube-config", help="Provide a config file for a k8s deployment")
|
||||||
@click.option("--image-registry", help="Provide a container image registry url for this k8s cluster")
|
@click.option(
|
||||||
@click.option("--deployment-dir", help="Create deployment files in this directory", required=True)
|
"--image-registry",
|
||||||
|
help="Provide a container image registry url for this k8s cluster",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--deployment-dir", help="Create deployment files in this directory", required=True
|
||||||
|
)
|
||||||
@click.option("--image", help="image to deploy", required=True)
|
@click.option("--image", help="image to deploy", required=True)
|
||||||
@click.option("--url", help="url to serve", required=True)
|
@click.option("--url", help="url to serve", required=True)
|
||||||
@click.option("--env-file", help="environment file for webapp")
|
@click.option("--env-file", help="environment file for webapp")
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def create(ctx, deployment_dir, image, url, kube_config, image_registry, env_file):
|
def create(ctx, deployment_dir, image, url, kube_config, image_registry, env_file):
|
||||||
'''create a deployment for the specified webapp container'''
|
"""create a deployment for the specified webapp container"""
|
||||||
|
|
||||||
return create_deployment(ctx, deployment_dir, image, url, kube_config, image_registry, env_file)
|
return create_deployment(
|
||||||
|
ctx, deployment_dir, image, url, kube_config, image_registry, env_file
|
||||||
|
)
|
||||||
|
@ -23,6 +23,7 @@ import time
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
from pkg_resources import require
|
||||||
|
|
||||||
from stack_orchestrator.deploy.images import remote_image_exists
|
from stack_orchestrator.deploy.images import remote_image_exists
|
||||||
from stack_orchestrator.deploy.webapp import deploy_webapp
|
from stack_orchestrator.deploy.webapp import deploy_webapp
|
||||||
@ -55,7 +56,7 @@ def process_app_deployment_request(
|
|||||||
force_rebuild,
|
force_rebuild,
|
||||||
fqdn_policy,
|
fqdn_policy,
|
||||||
recreate_on_deploy,
|
recreate_on_deploy,
|
||||||
payment_address,
|
deployer_record,
|
||||||
logger,
|
logger,
|
||||||
):
|
):
|
||||||
logger.log("BEGIN - process_app_deployment_request")
|
logger.log("BEGIN - process_app_deployment_request")
|
||||||
@ -227,7 +228,7 @@ def process_app_deployment_request(
|
|||||||
dns_lrn,
|
dns_lrn,
|
||||||
deployment_dir,
|
deployment_dir,
|
||||||
app_deployment_request,
|
app_deployment_request,
|
||||||
payment_address,
|
deployer_record,
|
||||||
logger,
|
logger,
|
||||||
)
|
)
|
||||||
logger.log("Publication complete.")
|
logger.log("Publication complete.")
|
||||||
@ -285,8 +286,12 @@ def dump_known_requests(filename, requests, status="SEEN"):
|
|||||||
help="How to handle requests with an FQDN: prohibit, allow, preexisting",
|
help="How to handle requests with an FQDN: prohibit, allow, preexisting",
|
||||||
default="prohibit",
|
default="prohibit",
|
||||||
)
|
)
|
||||||
@click.option("--record-namespace-dns", help="eg, lrn://laconic/dns")
|
@click.option("--record-namespace-dns", help="eg, lrn://laconic/dns", required=True)
|
||||||
@click.option("--record-namespace-deployments", help="eg, lrn://laconic/deployments")
|
@click.option(
|
||||||
|
"--record-namespace-deployments",
|
||||||
|
help="eg, lrn://laconic/deployments",
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--dry-run", help="Don't do anything, just report what would be done.", is_flag=True
|
"--dry-run", help="Don't do anything, just report what would be done.", is_flag=True
|
||||||
)
|
)
|
||||||
@ -313,15 +318,10 @@ def dump_known_requests(filename, requests, status="SEEN"):
|
|||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--min-required-payment",
|
"--min-required-payment",
|
||||||
help="Requests must have a minimum payment to be processed",
|
help="Requests must have a minimum payment to be processed (in alnt)",
|
||||||
default=0,
|
default=0,
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option("--lrn", help="The LRN of this deployer.", required=True)
|
||||||
"--payment-address",
|
|
||||||
help="The address to which payments should be made. "
|
|
||||||
"Default is the current laconic account.",
|
|
||||||
default=None,
|
|
||||||
)
|
|
||||||
@click.option(
|
@click.option(
|
||||||
"--all-requests",
|
"--all-requests",
|
||||||
help="Handle requests addressed to anyone (by default only requests to"
|
help="Handle requests addressed to anyone (by default only requests to"
|
||||||
@ -350,7 +350,7 @@ def command( # noqa: C901
|
|||||||
recreate_on_deploy,
|
recreate_on_deploy,
|
||||||
log_dir,
|
log_dir,
|
||||||
min_required_payment,
|
min_required_payment,
|
||||||
payment_address,
|
lrn,
|
||||||
all_requests,
|
all_requests,
|
||||||
):
|
):
|
||||||
if request_id and discover:
|
if request_id and discover:
|
||||||
@ -392,10 +392,8 @@ def command( # noqa: C901
|
|||||||
exclude_tags = [tag.strip() for tag in exclude_tags.split(",") if tag]
|
exclude_tags = [tag.strip() for tag in exclude_tags.split(",") if tag]
|
||||||
|
|
||||||
laconic = LaconicRegistryClient(laconic_config, log_file=sys.stderr)
|
laconic = LaconicRegistryClient(laconic_config, log_file=sys.stderr)
|
||||||
if not payment_address:
|
deployer_record = laconic.get_record(lrn, require=True)
|
||||||
payment_address = laconic.whoami().address
|
main_logger.log(f"Payment address: {deployer_record.attributes.paymentAddress}")
|
||||||
|
|
||||||
main_logger.log(f"Payment address: {payment_address}")
|
|
||||||
|
|
||||||
# Find deployment requests.
|
# Find deployment requests.
|
||||||
# single request
|
# single request
|
||||||
@ -408,7 +406,7 @@ def command( # noqa: C901
|
|||||||
if all_requests:
|
if all_requests:
|
||||||
requests = laconic.app_deployment_requests()
|
requests = laconic.app_deployment_requests()
|
||||||
else:
|
else:
|
||||||
requests = laconic.app_deployment_requests({"to": payment_address})
|
requests = laconic.app_deployment_requests({"deployer": lrn})
|
||||||
|
|
||||||
if only_update_state:
|
if only_update_state:
|
||||||
if not dry_run:
|
if not dry_run:
|
||||||
@ -487,7 +485,7 @@ def command( # noqa: C901
|
|||||||
if all_requests:
|
if all_requests:
|
||||||
deployments = laconic.app_deployments()
|
deployments = laconic.app_deployments()
|
||||||
else:
|
else:
|
||||||
deployments = laconic.app_deployments({"by": payment_address})
|
deployments = laconic.app_deployments({"deployer": lrn})
|
||||||
deployments_by_request = {}
|
deployments_by_request = {}
|
||||||
for d in deployments:
|
for d in deployments:
|
||||||
if d.attributes.request:
|
if d.attributes.request:
|
||||||
@ -530,7 +528,11 @@ def command( # noqa: C901
|
|||||||
for r in requests_to_check_for_payment:
|
for r in requests_to_check_for_payment:
|
||||||
main_logger.log(f"{r.id}: Confirming payment...")
|
main_logger.log(f"{r.id}: Confirming payment...")
|
||||||
if confirm_payment(
|
if confirm_payment(
|
||||||
laconic, r, payment_address, min_required_payment, main_logger
|
laconic,
|
||||||
|
r,
|
||||||
|
deployer_record.attributes.paymentAddress,
|
||||||
|
min_required_payment,
|
||||||
|
main_logger,
|
||||||
):
|
):
|
||||||
main_logger.log(f"{r.id}: Payment confirmed.")
|
main_logger.log(f"{r.id}: Payment confirmed.")
|
||||||
requests_to_execute.append(r)
|
requests_to_execute.append(r)
|
||||||
@ -583,7 +585,7 @@ def command( # noqa: C901
|
|||||||
force_rebuild,
|
force_rebuild,
|
||||||
fqdn_policy,
|
fqdn_policy,
|
||||||
recreate_on_deploy,
|
recreate_on_deploy,
|
||||||
payment_address,
|
deployer_record,
|
||||||
build_logger,
|
build_logger,
|
||||||
)
|
)
|
||||||
status = "DEPLOYED"
|
status = "DEPLOYED"
|
||||||
|
95
stack_orchestrator/deploy/webapp/publish_webapp_deployer.py
Normal file
95
stack_orchestrator/deploy/webapp/publish_webapp_deployer.py
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
# Copyright ©2023 Vulcanize
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import base64
|
||||||
|
from email.quoprimime import decode
|
||||||
|
|
||||||
|
import click
|
||||||
|
import sys
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
from stack_orchestrator.deploy.webapp.util import (
|
||||||
|
LaconicRegistryClient,
|
||||||
|
TimedLogger,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.option(
|
||||||
|
"--laconic-config", help="Provide a config file for laconicd", required=True
|
||||||
|
)
|
||||||
|
@click.option("--api-url", help="The API URL of the deployer.", required=True)
|
||||||
|
@click.option(
|
||||||
|
"--public-key-file",
|
||||||
|
help="The public key to use. This should be a binary file.",
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--lrn", help="eg, lrn://laconic/deployers/my.deployer.name", required=True
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--payment-address",
|
||||||
|
help="The address to which payments should be made. "
|
||||||
|
"Default is the current laconic account.",
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--min-required-payment",
|
||||||
|
help="List the minimum required payment (in alnt) to process a deployment request.",
|
||||||
|
default=0,
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--dry-run",
|
||||||
|
help="Don't publish anything, just report what would be done.",
|
||||||
|
is_flag=True,
|
||||||
|
)
|
||||||
|
@click.pass_context
|
||||||
|
def command( # noqa: C901
|
||||||
|
ctx,
|
||||||
|
laconic_config,
|
||||||
|
api_url,
|
||||||
|
public_key_file,
|
||||||
|
lrn,
|
||||||
|
payment_address,
|
||||||
|
min_required_payment,
|
||||||
|
dry_run,
|
||||||
|
):
|
||||||
|
laconic = LaconicRegistryClient(laconic_config)
|
||||||
|
if not payment_address:
|
||||||
|
payment_address = laconic.whoami().address
|
||||||
|
|
||||||
|
pub_key = base64.b64encode(open(public_key_file, "rb").read()).decode("ASCII")
|
||||||
|
hostname = urlparse(api_url).hostname
|
||||||
|
deployer_record = {
|
||||||
|
"record": {
|
||||||
|
"type": "WebappDeployer",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"apiUrl": api_url,
|
||||||
|
"name": hostname,
|
||||||
|
"publicKey": pub_key,
|
||||||
|
"paymentAddress": payment_address,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if min_required_payment:
|
||||||
|
deployer_record["record"]["minimumPayment"] = f"{min_required_payment}alnt"
|
||||||
|
|
||||||
|
if dry_run:
|
||||||
|
yaml.dump(deployer_record, sys.stdout)
|
||||||
|
return
|
||||||
|
|
||||||
|
laconic.publish(deployer_record, [lrn])
|
170
stack_orchestrator/deploy/webapp/request_webapp_deployment.py
Normal file
170
stack_orchestrator/deploy/webapp/request_webapp_deployment.py
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
# Copyright ©2023 Vulcanize
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
import base64
|
||||||
|
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
import gnupg
|
||||||
|
import click
|
||||||
|
import requests
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from stack_orchestrator.deploy.webapp.util import (
|
||||||
|
LaconicRegistryClient,
|
||||||
|
)
|
||||||
|
from dotenv import dotenv_values
|
||||||
|
|
||||||
|
|
||||||
|
def fatal(msg: str):
|
||||||
|
print(msg, file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.option(
|
||||||
|
"--laconic-config", help="Provide a config file for laconicd", required=True
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--app",
|
||||||
|
help="The LRN of the application to deploy.",
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--deployer",
|
||||||
|
help="The LRN of the deployer to process this request.",
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
@click.option("--env-file", help="environment file for webapp")
|
||||||
|
@click.option(
|
||||||
|
"--make-payment",
|
||||||
|
help="The payment to make (in alnt). The value should be a number or 'auto' to use the deployer's minimum required payment.",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--use-payment", help="The TX id of an existing, unused payment", default=None
|
||||||
|
)
|
||||||
|
@click.option("--dns", help="the DNS name to request (default is autogenerated)")
|
||||||
|
@click.option(
|
||||||
|
"--dry-run",
|
||||||
|
help="Don't publish anything, just report what would be done.",
|
||||||
|
is_flag=True,
|
||||||
|
)
|
||||||
|
@click.pass_context
|
||||||
|
def command(
|
||||||
|
ctx,
|
||||||
|
laconic_config,
|
||||||
|
app,
|
||||||
|
deployer,
|
||||||
|
env_file,
|
||||||
|
make_payment,
|
||||||
|
use_payment,
|
||||||
|
dns,
|
||||||
|
dry_run,
|
||||||
|
): # noqa: C901
|
||||||
|
tempdir = tempfile.mkdtemp()
|
||||||
|
try:
|
||||||
|
laconic = LaconicRegistryClient(laconic_config)
|
||||||
|
|
||||||
|
app_record = laconic.get_record(app)
|
||||||
|
if not app_record:
|
||||||
|
fatal(f"Unable to locate app: {app}")
|
||||||
|
|
||||||
|
deployer_record = laconic.get_record(deployer)
|
||||||
|
if not deployer_record:
|
||||||
|
fatal(f"Unable to locate deployer: {deployer}")
|
||||||
|
|
||||||
|
config_ref = None
|
||||||
|
|
||||||
|
# If env_file
|
||||||
|
if env_file:
|
||||||
|
gpg = gnupg.GPG(gnupghome=tempdir)
|
||||||
|
|
||||||
|
# Import the deployer's public key
|
||||||
|
result = gpg.import_keys(
|
||||||
|
base64.b64decode(deployer_record.attributes.publicKey)
|
||||||
|
)
|
||||||
|
if 1 != result.imported:
|
||||||
|
fatal("Failed to import deployer's public key.")
|
||||||
|
|
||||||
|
recip = gpg.list_keys()[0]["uids"][0]
|
||||||
|
|
||||||
|
# Wrap the config
|
||||||
|
config = {
|
||||||
|
# Include account (and payment?) details
|
||||||
|
"authorized": [laconic.whoami().address],
|
||||||
|
"config": {"env": dict(dotenv_values(env_file))},
|
||||||
|
}
|
||||||
|
serialized = yaml.dump(config)
|
||||||
|
|
||||||
|
# Encrypt
|
||||||
|
result = gpg.encrypt(serialized, recip, always_trust=True, armor=False)
|
||||||
|
if not result.ok:
|
||||||
|
fatal("Failed to encrypt config.")
|
||||||
|
|
||||||
|
# Upload it to the deployer's API
|
||||||
|
# deployer_record.attributes.apiUrl
|
||||||
|
response = requests.post(
|
||||||
|
"http://localhost:9555/upload/config",
|
||||||
|
data=result.data,
|
||||||
|
headers={"Content-Type": "application/octet-stream"},
|
||||||
|
)
|
||||||
|
if not response.ok:
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
config_ref = response.json()["id"]
|
||||||
|
|
||||||
|
deployment_request = {
|
||||||
|
"record": {
|
||||||
|
"type": "ApplicationDeploymentRequest",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"name": f"{app_record.attributes.name}@{app_record.attributes.version}",
|
||||||
|
"deployer": deployer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config_ref:
|
||||||
|
deployment_request["record"]["config"] = {"ref": config_ref}
|
||||||
|
|
||||||
|
if dns:
|
||||||
|
deployment_request["record"]["dns"] = dns.lower()
|
||||||
|
|
||||||
|
if make_payment:
|
||||||
|
amount = 0
|
||||||
|
if dry_run:
|
||||||
|
deployment_request["record"]["paymentTx"] = "DRY_RUN"
|
||||||
|
elif "auto" == make_payment:
|
||||||
|
if "minimumPayment" in deployer_record.attributes:
|
||||||
|
amount = int(
|
||||||
|
deployer_record.attributes.minimumPayment.replace("alnt", "")
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
amount = make_payment
|
||||||
|
if amount:
|
||||||
|
receipt = laconic.send_tokens(
|
||||||
|
deployer_record.attributes.paymentAddress, amount
|
||||||
|
)
|
||||||
|
deployment_request["record"]["paymentTx"] = receipt.tx.hash
|
||||||
|
print("Payment TX:", receipt.tx.hash)
|
||||||
|
elif use_payment:
|
||||||
|
deployment_request["record"]["paymentTx"] = use_payment
|
||||||
|
|
||||||
|
if dry_run:
|
||||||
|
print(yaml.dump(deployment_request))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Send the request
|
||||||
|
laconic.publish(deployment_request)
|
||||||
|
finally:
|
||||||
|
shutil.rmtree(tempdir)
|
@ -36,7 +36,7 @@ WEBAPP_PORT = 80
|
|||||||
@click.option("--port", help="port to use (default random)")
|
@click.option("--port", help="port to use (default random)")
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def command(ctx, image, env_file, port):
|
def command(ctx, image, env_file, port):
|
||||||
'''run the specified webapp container'''
|
"""run the specified webapp container"""
|
||||||
|
|
||||||
env = {}
|
env = {}
|
||||||
if env_file:
|
if env_file:
|
||||||
@ -46,20 +46,33 @@ def command(ctx, image, env_file, port):
|
|||||||
hash = hashlib.md5(unique_cluster_descriptor.encode()).hexdigest()
|
hash = hashlib.md5(unique_cluster_descriptor.encode()).hexdigest()
|
||||||
cluster = f"laconic-webapp-{hash}"
|
cluster = f"laconic-webapp-{hash}"
|
||||||
|
|
||||||
deployer = getDeployer(type=constants.compose_deploy_type,
|
deployer = getDeployer(
|
||||||
deployment_context=None,
|
type=constants.compose_deploy_type,
|
||||||
compose_files=None,
|
deployment_context=None,
|
||||||
compose_project_name=cluster,
|
compose_files=None,
|
||||||
compose_env_file=None)
|
compose_project_name=cluster,
|
||||||
|
compose_env_file=None,
|
||||||
|
)
|
||||||
|
|
||||||
ports = []
|
ports = []
|
||||||
if port:
|
if port:
|
||||||
ports = [(port, WEBAPP_PORT)]
|
ports = [(port, WEBAPP_PORT)]
|
||||||
container = deployer.run(image, command=[], user=None, volumes=[], entrypoint=None, env=env, ports=ports, detach=True)
|
container = deployer.run(
|
||||||
|
image,
|
||||||
|
command=[],
|
||||||
|
user=None,
|
||||||
|
volumes=[],
|
||||||
|
entrypoint=None,
|
||||||
|
env=env,
|
||||||
|
ports=ports,
|
||||||
|
detach=True,
|
||||||
|
)
|
||||||
|
|
||||||
# Make configurable?
|
# Make configurable?
|
||||||
webappPort = f"{WEBAPP_PORT}/tcp"
|
webappPort = f"{WEBAPP_PORT}/tcp"
|
||||||
# TODO: This assumes a Docker container object...
|
# TODO: This assumes a Docker container object...
|
||||||
if webappPort in container.network_settings.ports:
|
if webappPort in container.network_settings.ports:
|
||||||
mapping = container.network_settings.ports[webappPort][0]
|
mapping = container.network_settings.ports[webappPort][0]
|
||||||
print(f"""Image: {image}\nID: {container.id}\nURL: http://localhost:{mapping['HostPort']}""")
|
print(
|
||||||
|
f"""Image: {image}\nID: {container.id}\nURL: http://localhost:{mapping['HostPort']}"""
|
||||||
|
)
|
||||||
|
@ -168,7 +168,7 @@ def dump_known_requests(filename, requests):
|
|||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--min-required-payment",
|
"--min-required-payment",
|
||||||
help="Requests must have a minimum payment to be processed",
|
help="Requests must have a minimum payment to be processed (in alnt)",
|
||||||
default=0,
|
default=0,
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright © 2023 Vulcanize
|
# = str(min_required_payment) Copyright © 2023 Vulcanize
|
||||||
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU Affero General Public License as published by
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
@ -453,6 +453,24 @@ class LaconicRegistryClient:
|
|||||||
name,
|
name,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def send_tokens(self, address, amount, type="alnt"):
|
||||||
|
args = [
|
||||||
|
"laconic",
|
||||||
|
"-c",
|
||||||
|
self.config_file,
|
||||||
|
"registry",
|
||||||
|
"tokens",
|
||||||
|
"send",
|
||||||
|
"--address",
|
||||||
|
address,
|
||||||
|
"--quantity",
|
||||||
|
str(amount),
|
||||||
|
"--type",
|
||||||
|
type,
|
||||||
|
]
|
||||||
|
|
||||||
|
return AttrDict(json.loads(logged_cmd(self.log_file, *args)))
|
||||||
|
|
||||||
|
|
||||||
def file_hash(filename):
|
def file_hash(filename):
|
||||||
return hashlib.sha1(open(filename).read().encode()).hexdigest()
|
return hashlib.sha1(open(filename).read().encode()).hexdigest()
|
||||||
@ -609,7 +627,7 @@ def publish_deployment(
|
|||||||
dns_lrn,
|
dns_lrn,
|
||||||
deployment_dir,
|
deployment_dir,
|
||||||
app_deployment_request=None,
|
app_deployment_request=None,
|
||||||
payment_address=None,
|
deployment_record=None,
|
||||||
logger=None,
|
logger=None,
|
||||||
):
|
):
|
||||||
if not deploy_record:
|
if not deploy_record:
|
||||||
@ -666,8 +684,8 @@ def publish_deployment(
|
|||||||
"payment"
|
"payment"
|
||||||
] = app_deployment_request.attributes.payment
|
] = app_deployment_request.attributes.payment
|
||||||
|
|
||||||
if payment_address:
|
if deployment_record:
|
||||||
new_deployment_record["record"]["by"] = payment_address
|
new_deployment_record["record"]["deployer"] = deployment_record.names[0]
|
||||||
|
|
||||||
if logger:
|
if logger:
|
||||||
logger.log("Publishing ApplicationDeploymentRecord.")
|
logger.log("Publishing ApplicationDeploymentRecord.")
|
||||||
|
@ -24,7 +24,9 @@ from stack_orchestrator.build import build_webapp
|
|||||||
from stack_orchestrator.deploy.webapp import (run_webapp,
|
from stack_orchestrator.deploy.webapp import (run_webapp,
|
||||||
deploy_webapp,
|
deploy_webapp,
|
||||||
deploy_webapp_from_registry,
|
deploy_webapp_from_registry,
|
||||||
undeploy_webapp_from_registry)
|
undeploy_webapp_from_registry,
|
||||||
|
publish_webapp_deployer,
|
||||||
|
request_webapp_deployment)
|
||||||
from stack_orchestrator.deploy import deploy
|
from stack_orchestrator.deploy import deploy
|
||||||
from stack_orchestrator import version
|
from stack_orchestrator import version
|
||||||
from stack_orchestrator.deploy import deployment
|
from stack_orchestrator.deploy import deployment
|
||||||
@ -61,6 +63,8 @@ cli.add_command(run_webapp.command, "run-webapp")
|
|||||||
cli.add_command(deploy_webapp.command, "deploy-webapp")
|
cli.add_command(deploy_webapp.command, "deploy-webapp")
|
||||||
cli.add_command(deploy_webapp_from_registry.command, "deploy-webapp-from-registry")
|
cli.add_command(deploy_webapp_from_registry.command, "deploy-webapp-from-registry")
|
||||||
cli.add_command(undeploy_webapp_from_registry.command, "undeploy-webapp-from-registry")
|
cli.add_command(undeploy_webapp_from_registry.command, "undeploy-webapp-from-registry")
|
||||||
|
cli.add_command(publish_webapp_deployer.command, "publish-deployer-to-registry")
|
||||||
|
cli.add_command(request_webapp_deployment.command, "request-webapp-deployment")
|
||||||
cli.add_command(deploy.command, "deploy") # deploy is an alias for deploy-system
|
cli.add_command(deploy.command, "deploy") # deploy is an alias for deploy-system
|
||||||
cli.add_command(deploy.command, "deploy-system")
|
cli.add_command(deploy.command, "deploy-system")
|
||||||
cli.add_command(deployment.command, "deployment")
|
cli.add_command(deployment.command, "deployment")
|
||||||
|
Loading…
Reference in New Issue
Block a user