Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 9 additions & 21 deletions src/stack/deploy/deployment_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ def init_operation( # noqa: C901
output,
map_ports_to_host,
backup_targets=None,
clusterissuer_explicitly_set=False,
):
spec_file_content = {"stack": stack, constants.deploy_to_key: deployer_type}
if deployer_type in ["k8s", "k8s-kind"]:
Expand All @@ -287,23 +288,6 @@ def init_operation( # noqa: C901
"WARN: --image-registry not specified: locally built images can only be deployed"
" if they are published to a container registry the cluster can reach"
)
if http_proxy_targets:
routes = []
for target in http_proxy_targets:
routes.append(
{
constants.path_key: target["path"],
constants.proxy_to_key: f"{target['service']}:{target['port']}",
}
)
http_proxy = {
constants.host_name_key: http_proxy_fqdn,
constants.cluster_issuer_key: http_proxy_clusterissuer,
constants.routes_key: routes,
}
if constants.network_key not in spec_file_content:
spec_file_content[constants.network_key] = {}
spec_file_content[constants.network_key].update({constants.http_proxy_key: [http_proxy]})
else:
# Check for --kube-config supplied for non-relevant deployer types
if kube_config is not None:
Expand All @@ -322,10 +306,14 @@ def init_operation( # noqa: C901
constants.host_name_key: http_proxy_fqdn,
constants.routes_key: routes,
}
if http_proxy_clusterissuer and deployer_type in ["k8s", "k8s-kind"]:
http_proxy[constants.cluster_issuer_key] = http_proxy_clusterissuer
else:
log_info("NOTE: http-cluster-issuer is only used when deploying to Kubernetes")
if deployer_type in ["k8s", "k8s-kind"]:
if http_proxy_clusterissuer:
http_proxy[constants.cluster_issuer_key] = http_proxy_clusterissuer
elif clusterissuer_explicitly_set:
# Only worth saying when the user actually asked for a cluster issuer: the
# option carries a default, so testing its value would flag every non-k8s
# deployment, which is the ordinary case and not something to report.
log_info("NOTE: --http-proxy-clusterissuer is only used when deploying to Kubernetes; ignoring it")
if constants.network_key not in spec_file_content:
spec_file_content[constants.network_key] = {}
spec_file_content[constants.network_key].update({constants.http_proxy_key: [http_proxy]})
Expand Down
10 changes: 9 additions & 1 deletion src/stack/init/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import click
import os

from click.core import ParameterSource

from stack import constants
from stack.log import log_info, log_debug, log_warn
from stack.config.util import get_config_setting
Expand Down Expand Up @@ -114,7 +116,7 @@ def _output_checks(specs, deploy_to, http_proxy_fqdn_specified=False):
@click.option(
"--http-proxy-clusterissuer",
required=False,
help="k8s http proxy hostname to use",
help="cert-manager cluster issuer to obtain the k8s http proxy certificate from",
default=get_config_setting("http-proxy-clusterissuer", "letsencrypt-prod"),
)
@click.option("--output", required=True, help="Write yaml spec file here")
Expand Down Expand Up @@ -146,6 +148,11 @@ def command(
if deploy_to is None:
deploy_to = "compose"

# The cluster issuer option carries a default, so its value cannot tell us whether
# the user asked for one. Ask click where the value came from instead, so that we
# only report ignoring it when it was actually requested on the command line.
clusterissuer_explicitly_set = ctx.get_parameter_source("http_proxy_clusterissuer") == ParameterSource.COMMANDLINE

ctx.obj = create_deploy_context(
global_options2(ctx),
None,
Expand Down Expand Up @@ -225,6 +232,7 @@ def http_prefix_for(stack):
None,
map_ports_to_host,
backup_targets,
clusterissuer_explicitly_set,
)
specs.append(spec)

Expand Down
73 changes: 73 additions & 0 deletions tests/unit/test_init_http_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

FQDN_UNUSED_WARNING = "--http-proxy-fqdn was specified but there are no HTTP proxy targets"
NO_PROXY_NOTE = "No HTTP proxy settings specified"
CLUSTERISSUER_IGNORED_NOTE = "--http-proxy-clusterissuer is only used when deploying to Kubernetes"


def init_k8s(stack_dir, output_file, env, extra_args=None):
Expand All @@ -38,6 +39,19 @@ def init_k8s(stack_dir, output_file, env, extra_args=None):
return run_stack(args + (extra_args or []), env)


def init_compose(stack_dir, output_file, env, extra_args=None):
args = [
"init",
"--stack",
str(stack_dir),
"--deploy-to",
"compose",
"--output",
str(output_file),
]
return run_stack(args + (extra_args or []), env)


def test_fqdn_without_targets_warns(minimal_stack, tmp_path, isolated_env):
result = init_k8s(minimal_stack, tmp_path / "spec.yml", isolated_env, ["--http-proxy-fqdn", FQDN])
assert result.returncode == 0, result.stderr
Expand Down Expand Up @@ -90,3 +104,62 @@ def test_fqdn_with_annotated_port(annotated_stack, tmp_path, isolated_env):
proxies = spec["network"]["http-proxy"]
assert proxies[0]["host-name"] == FQDN
assert proxies[0]["routes"] == [{"path": "/", "proxy-to": "web:80"}]


# ---------------------------------------------------------------------------
# cluster issuer
#
# --http-proxy-clusterissuer carries a default, so the note about it being
# unused must key off whether the user asked for one, not off its value.
# ---------------------------------------------------------------------------


def test_compose_is_silent_about_an_unrequested_cluster_issuer(annotated_stack, tmp_path, isolated_env):
spec_file = tmp_path / "spec.yml"
result = init_compose(annotated_stack, spec_file, isolated_env, ["--http-proxy-fqdn", FQDN])
assert result.returncode == 0, result.stderr
assert CLUSTERISSUER_IGNORED_NOTE not in result.stdout + result.stderr

# The default must not leak into a spec that cannot use it.
spec = yaml.safe_load(spec_file.read_text())
assert "cluster-issuer" not in spec["network"]["http-proxy"][0]


def test_compose_reports_an_explicitly_requested_cluster_issuer_is_ignored(annotated_stack, tmp_path, isolated_env):
spec_file = tmp_path / "spec.yml"
result = init_compose(
annotated_stack,
spec_file,
isolated_env,
["--http-proxy-fqdn", FQDN, "--http-proxy-clusterissuer", "letsencrypt-staging"],
)
assert result.returncode == 0, result.stderr
assert CLUSTERISSUER_IGNORED_NOTE in result.stdout + result.stderr

spec = yaml.safe_load(spec_file.read_text())
assert "cluster-issuer" not in spec["network"]["http-proxy"][0]


def test_k8s_keeps_the_default_cluster_issuer_without_comment(annotated_stack, tmp_path, isolated_env):
spec_file = tmp_path / "spec.yml"
result = init_k8s(annotated_stack, spec_file, isolated_env, ["--http-proxy-fqdn", FQDN])
assert result.returncode == 0, result.stderr
assert CLUSTERISSUER_IGNORED_NOTE not in result.stdout + result.stderr

spec = yaml.safe_load(spec_file.read_text())
assert spec["network"]["http-proxy"][0]["cluster-issuer"] == "letsencrypt-prod"


def test_k8s_honours_an_explicit_cluster_issuer(annotated_stack, tmp_path, isolated_env):
spec_file = tmp_path / "spec.yml"
result = init_k8s(
annotated_stack,
spec_file,
isolated_env,
["--http-proxy-fqdn", FQDN, "--http-proxy-clusterissuer", "letsencrypt-staging"],
)
assert result.returncode == 0, result.stderr
assert CLUSTERISSUER_IGNORED_NOTE not in result.stdout + result.stderr

spec = yaml.safe_load(spec_file.read_text())
assert spec["network"]["http-proxy"][0]["cluster-issuer"] == "letsencrypt-staging"
Loading