Support uploaded config, add 'publish-webapp-deployer' and 'request-webapp-deployment' commands #938
@ -11,3 +11,5 @@ tomli==2.0.1
|
||||
validators==0.22.0
|
||||
kubernetes>=28.1.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):
|
||||
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
|
||||
with open(compose_file) as rfile:
|
||||
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):
|
||||
# url is like: https://example.com/path
|
||||
parsed_url = urlparse(url)
|
||||
http_proxy_spec = f'''
|
||||
http_proxy_spec = f"""
|
||||
telackey marked this conversation as resolved
Outdated
|
||||
http-proxy:
|
||||
- host-name: {parsed_url.hostname}
|
||||
routes:
|
||||
- path: '{parsed_url.path if parsed_url.path else "/"}'
|
||||
proxy-to: webapp:80
|
||||
'''
|
||||
"""
|
||||
spec_file_path = Path(spec_file_name)
|
||||
with open(spec_file_path) as rfile:
|
||||
contents = rfile.read()
|
||||
@ -54,7 +56,9 @@ def _fixup_url_spec(spec_file_name: str, url: str):
|
||||
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:
|
||||
# 1. laconic-so --stack webapp-template deploy --deploy-to k8s init --output webapp-spec.yml
|
||||
# --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,
|
||||
image_registry,
|
||||
spec_file_name,
|
||||
None
|
||||
None,
|
||||
)
|
||||
# Add the TLS and DNS spec
|
||||
_fixup_url_spec(spec_file_name, url)
|
||||
create_operation(
|
||||
deploy_command_context,
|
||||
spec_file_name,
|
||||
deployment_dir,
|
||||
None,
|
||||
None
|
||||
)
|
||||
create_operation(deploy_command_context, spec_file_name, deployment_dir, None, None)
|
||||
# Fix up the container tag inside the deployment compose file
|
||||
_fixup_container_tag(deployment_dir, image)
|
||||
os.remove(spec_file_name)
|
||||
@ -102,7 +100,7 @@ def create_deployment(ctx, deployment_dir, image, url, kube_config, image_regist
|
||||
@click.group()
|
||||
@click.pass_context
|
||||
def command(ctx):
|
||||
'''manage a webapp deployment'''
|
||||
"""manage a webapp deployment"""
|
||||
|
||||
# Check that --stack wasn't supplied
|
||||
if ctx.parent.obj.stack:
|
||||
@ -111,13 +109,20 @@ def command(ctx):
|
||||
|
||||
@command.command()
|
||||
@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("--deployment-dir", help="Create deployment files in this directory", required=True)
|
||||
@click.option(
|
||||
"--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("--url", help="url to serve", required=True)
|
||||
@click.option("--env-file", help="environment file for webapp")
|
||||
@click.pass_context
|
||||
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 click
|
||||
from pkg_resources import require
|
||||
|
||||
from stack_orchestrator.deploy.images import remote_image_exists
|
||||
from stack_orchestrator.deploy.webapp import deploy_webapp
|
||||
@ -55,7 +56,7 @@ def process_app_deployment_request(
|
||||
force_rebuild,
|
||||
fqdn_policy,
|
||||
recreate_on_deploy,
|
||||
payment_address,
|
||||
deployer_record,
|
||||
logger,
|
||||
):
|
||||
logger.log("BEGIN - process_app_deployment_request")
|
||||
@ -227,7 +228,7 @@ def process_app_deployment_request(
|
||||
dns_lrn,
|
||||
deployment_dir,
|
||||
app_deployment_request,
|
||||
payment_address,
|
||||
deployer_record,
|
||||
logger,
|
||||
)
|
||||
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",
|
||||
default="prohibit",
|
||||
)
|
||||
@click.option("--record-namespace-dns", help="eg, lrn://laconic/dns")
|
||||
@click.option("--record-namespace-deployments", help="eg, lrn://laconic/deployments")
|
||||
@click.option("--record-namespace-dns", help="eg, lrn://laconic/dns", required=True)
|
||||
@click.option(
|
||||
"--record-namespace-deployments",
|
||||
help="eg, lrn://laconic/deployments",
|
||||
required=True,
|
||||
)
|
||||
@click.option(
|
||||
"--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(
|
||||
"--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,
|
||||
)
|
||||
@click.option(
|
||||
"--payment-address",
|
||||
help="The address to which payments should be made. "
|
||||
"Default is the current laconic account.",
|
||||
default=None,
|
||||
)
|
||||
@click.option("--lrn", help="The LRN of this deployer.", required=True)
|
||||
@click.option(
|
||||
"--all-requests",
|
||||
help="Handle requests addressed to anyone (by default only requests to"
|
||||
@ -350,7 +350,7 @@ def command( # noqa: C901
|
||||
recreate_on_deploy,
|
||||
log_dir,
|
||||
min_required_payment,
|
||||
payment_address,
|
||||
lrn,
|
||||
all_requests,
|
||||
):
|
||||
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]
|
||||
|
||||
laconic = LaconicRegistryClient(laconic_config, log_file=sys.stderr)
|
||||
if not payment_address:
|
||||
payment_address = laconic.whoami().address
|
||||
|
||||
main_logger.log(f"Payment address: {payment_address}")
|
||||
deployer_record = laconic.get_record(lrn, require=True)
|
||||
main_logger.log(f"Payment address: {deployer_record.attributes.paymentAddress}")
|
||||
|
||||
# Find deployment requests.
|
||||
# single request
|
||||
@ -408,7 +406,7 @@ def command( # noqa: C901
|
||||
if all_requests:
|
||||
requests = laconic.app_deployment_requests()
|
||||
else:
|
||||
requests = laconic.app_deployment_requests({"to": payment_address})
|
||||
requests = laconic.app_deployment_requests({"deployer": lrn})
|
||||
|
||||
if only_update_state:
|
||||
if not dry_run:
|
||||
@ -487,7 +485,7 @@ def command( # noqa: C901
|
||||
if all_requests:
|
||||
deployments = laconic.app_deployments()
|
||||
else:
|
||||
deployments = laconic.app_deployments({"by": payment_address})
|
||||
deployments = laconic.app_deployments({"deployer": lrn})
|
||||
deployments_by_request = {}
|
||||
for d in deployments:
|
||||
if d.attributes.request:
|
||||
@ -530,7 +528,11 @@ def command( # noqa: C901
|
||||
for r in requests_to_check_for_payment:
|
||||
main_logger.log(f"{r.id}: Confirming 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.")
|
||||
requests_to_execute.append(r)
|
||||
@ -583,7 +585,7 @@ def command( # noqa: C901
|
||||
force_rebuild,
|
||||
fqdn_policy,
|
||||
recreate_on_deploy,
|
||||
payment_address,
|
||||
deployer_record,
|
||||
build_logger,
|
||||
)
|
||||
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.pass_context
|
||||
def command(ctx, image, env_file, port):
|
||||
'''run the specified webapp container'''
|
||||
"""run the specified webapp container"""
|
||||
|
||||
env = {}
|
||||
if env_file:
|
||||
@ -46,20 +46,33 @@ def command(ctx, image, env_file, port):
|
||||
hash = hashlib.md5(unique_cluster_descriptor.encode()).hexdigest()
|
||||
cluster = f"laconic-webapp-{hash}"
|
||||
|
||||
deployer = getDeployer(type=constants.compose_deploy_type,
|
||||
deployment_context=None,
|
||||
compose_files=None,
|
||||
compose_project_name=cluster,
|
||||
compose_env_file=None)
|
||||
deployer = getDeployer(
|
||||
type=constants.compose_deploy_type,
|
||||
deployment_context=None,
|
||||
compose_files=None,
|
||||
compose_project_name=cluster,
|
||||
compose_env_file=None,
|
||||
)
|
||||
|
||||
ports = []
|
||||
if 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?
|
||||
webappPort = f"{WEBAPP_PORT}/tcp"
|
||||
# TODO: This assumes a Docker container object...
|
||||
if webappPort in container.network_settings.ports:
|
||||
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(
|
||||
"--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,
|
||||
)
|
||||
@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
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
@ -453,6 +453,24 @@ class LaconicRegistryClient:
|
||||
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):
|
||||
return hashlib.sha1(open(filename).read().encode()).hexdigest()
|
||||
@ -609,7 +627,7 @@ def publish_deployment(
|
||||
dns_lrn,
|
||||
deployment_dir,
|
||||
app_deployment_request=None,
|
||||
payment_address=None,
|
||||
deployment_record=None,
|
||||
logger=None,
|
||||
):
|
||||
if not deploy_record:
|
||||
@ -666,8 +684,8 @@ def publish_deployment(
|
||||
"payment"
|
||||
] = app_deployment_request.attributes.payment
|
||||
|
||||
if payment_address:
|
||||
new_deployment_record["record"]["by"] = payment_address
|
||||
if deployment_record:
|
||||
new_deployment_record["record"]["deployer"] = deployment_record.names[0]
|
||||
|
||||
if logger:
|
||||
logger.log("Publishing ApplicationDeploymentRecord.")
|
||||
|
@ -24,7 +24,9 @@ from stack_orchestrator.build import build_webapp
|
||||
from stack_orchestrator.deploy.webapp import (run_webapp,
|
||||
deploy_webapp,
|
||||
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 import version
|
||||
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_from_registry.command, "deploy-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-system")
|
||||
cli.add_command(deployment.command, "deployment")
|
||||
|
Loading…
Reference in New Issue
Block a user
What's going on here?
linted the wrong file. Backed out...