Should be working
All checks were successful
Lint Checks / Run linter (pull_request) Successful in 25s
Deploy Test / Run deploy test suite (pull_request) Successful in 5m13s
Webapp Test / Run webapp test suite (pull_request) Successful in 3m58s
K8s Deploy Test / Run deploy test suite on kind/k8s (pull_request) Successful in 7m26s
Smoke Test / Run basic test suite (pull_request) Successful in 5m50s

This commit is contained in:
Thomas E Lackey 2024-03-06 17:17:17 -06:00
parent 2064373da6
commit 57e1cf7883
3 changed files with 69 additions and 36 deletions

View File

@ -101,7 +101,7 @@ class ClusterInfo:
) )
return service return service
def get_ingress(self, use_tls=False): def get_ingress(self, use_tls=False, certificate=None, cluster_issuer="letsencrypt-prod"):
# No ingress for a deployment that has no http-proxy defined, for now # No ingress for a deployment that has no http-proxy defined, for now
http_proxy_info_list = self.spec.get_http_proxy() http_proxy_info_list = self.spec.get_http_proxy()
ingress = None ingress = None
@ -112,10 +112,16 @@ class ClusterInfo:
print(f"http-proxy: {http_proxy_info}") print(f"http-proxy: {http_proxy_info}")
# TODO: good enough parsing for webapp deployment for now # TODO: good enough parsing for webapp deployment for now
host_name = http_proxy_info["host-name"] host_name = http_proxy_info["host-name"]
tls_hosts = [host_name],
tls_secret_name = f"{self.app_name}-tls"
if certificate:
tls_hosts = certificate["spec"]["dnsNames"]
tls_secret_name = certificate["spec"]["secretName"]
rules = [] rules = []
tls = [client.V1IngressTLS( tls = [client.V1IngressTLS(
hosts=[host_name], hosts=tls_hosts,
secret_name=f"{self.app_name}-tls" secret_name=tls_secret_name
)] if use_tls else None )] if use_tls else None
paths = [] paths = []
for route in http_proxy_info["routes"]: for route in http_proxy_info["routes"]:
@ -147,13 +153,17 @@ class ClusterInfo:
tls=tls, tls=tls,
rules=rules rules=rules
) )
ingress_annotations = {
"kubernetes.io/ingress.class": "nginx",
}
if not certificate:
ingress_annotations["cert-manager.io/cluster-issuer"] = cluster_issuer
ingress = client.V1Ingress( ingress = client.V1Ingress(
metadata=client.V1ObjectMeta( metadata=client.V1ObjectMeta(
name=f"{self.app_name}-ingress", name=f"{self.app_name}-ingress",
annotations={ annotations=ingress_annotations
"kubernetes.io/ingress.class": "nginx",
"cert-manager.io/cluster-issuer": "letsencrypt-prod"
}
), ),
spec=spec spec=spec
) )

View File

@ -170,14 +170,37 @@ class K8sDeployer(Deployer):
print(f"{service_resp}") print(f"{service_resp}")
def _find_certificate_for_host_name(self, host_name): def _find_certificate_for_host_name(self, host_name):
all_certificates = self.custom_obj_api.get_namespaced_custom_object( all_certificates = self.custom_obj_api.list_namespaced_custom_object(
group="cert-manager.io", group="cert-manager.io",
version="v1", version="v1",
namespace=self.k8s_namespace, namespace=self.k8s_namespace,
plural="certificates" plural="certificates"
) )
for cert in all_certificates:
print(cert) host_parts = host_name.split(".", 1)
host_as_wild = None
if len(host_parts) == 2:
host_as_wild = f"*.{host_parts[1]}"
now = datetime.utcnow().replace(tzinfo=timezone.utc)
fmt = "%Y-%m-%dT%H:%M:%S%z"
# Walk over all the configured certificates.
for cert in all_certificates["items"]:
dns = cert["spec"]["dnsNames"]
# Check for an exact hostname match or a wildcard match.
if host_name in dns or host_as_wild in dns:
status = cert.get("status", {})
# Check the certificate date.
if "notAfter" in status and "notBefore" in status:
before = datetime.strptime(status["notBefore"], fmt)
after = datetime.strptime(status["notAfter"], fmt)
if before < now < after:
# Check the status is Ready
for condition in status.get("conditions", []):
if "True" == condition.get("status") and "Ready" == condition.get("type"):
return cert
return None
def up(self, detach, services): def up(self, detach, services):
if not opts.o.dry_run: if not opts.o.dry_run:
@ -199,8 +222,11 @@ class K8sDeployer(Deployer):
self._create_volume_data() self._create_volume_data()
self._create_deployment() self._create_deployment()
use_tls = not self.is_kind()
certificate = self._find_certificate_for_host_name(self.spec.get_http_proxy()[0]["host-name"]) if use_tls else None
# Note: at present we don't support tls for kind (and enabling tls causes errors) # Note: at present we don't support tls for kind (and enabling tls causes errors)
ingress: client.V1Ingress = self.cluster_info.get_ingress(use_tls=not self.is_kind()) ingress: client.V1Ingress = self.cluster_info.get_ingress(use_tls=use_tls, certificate=certificate)
if ingress: if ingress:
if opts.o.debug: if opts.o.debug:
print(f"Sending this ingress: {ingress}") print(f"Sending this ingress: {ingress}")
@ -333,7 +359,6 @@ class K8sDeployer(Deployer):
def status(self): def status(self):
self.connect_api() self.connect_api()
self._find_certificate_for_host_name("foo")
# Call whatever API we need to get the running container list # Call whatever API we need to get the running container list
all_pods = self.core_api.list_pod_for_all_namespaces(watch=False) all_pods = self.core_api.list_pod_for_all_namespaces(watch=False)
pods = [] pods = []

View File

@ -27,7 +27,9 @@ class ResourceLimits:
memory: int = None memory: int = None
storage: int = None storage: int = None
def __init__(self, obj={}): def __init__(self, obj=None):
if obj is None:
obj = {}
if "cpus" in obj: if "cpus" in obj:
self.cpus = float(obj["cpus"]) self.cpus = float(obj["cpus"])
if "memory" in obj: if "memory" in obj:
@ -50,7 +52,9 @@ class Resources:
limits: ResourceLimits = None limits: ResourceLimits = None
reservations: ResourceLimits = None reservations: ResourceLimits = None
def __init__(self, obj={}): def __init__(self, obj=None):
if obj is None:
obj = {}
if "reservations" in obj: if "reservations" in obj:
self.reservations = ResourceLimits(obj["reservations"]) self.reservations = ResourceLimits(obj["reservations"])
if "limits" in obj: if "limits" in obj:
@ -72,7 +76,9 @@ class Spec:
obj: typing.Any obj: typing.Any
file_path: Path file_path: Path
def __init__(self, file_path: Path = None, obj={}) -> None: def __init__(self, file_path: Path = None, obj=None) -> None:
if obj is None:
obj = {}
self.file_path = file_path self.file_path = file_path
self.obj = obj self.obj = obj
@ -91,49 +97,41 @@ class Spec:
self.file_path = file_path self.file_path = file_path
def get_image_registry(self): def get_image_registry(self):
return (self.obj[constants.image_registry_key] return self.obj.get(constants.image_registry_key)
if self.obj and constants.image_registry_key in self.obj
else None)
def get_volumes(self): def get_volumes(self):
return (self.obj["volumes"] return self.obj.get(constants.volumes_key, {})
if self.obj and "volumes" in self.obj
else {})
def get_configmaps(self): def get_configmaps(self):
return (self.obj["configmaps"] return self.obj.get(constants.configmaps_key, {})
if self.obj and "configmaps" in self.obj
else {})
def get_container_resources(self): def get_container_resources(self):
return Resources(self.obj.get("resources", {}).get("containers", {})) return Resources(self.obj.get(constants.resources_key, {}).get("containers", {}))
def get_volume_resources(self): def get_volume_resources(self):
return Resources(self.obj.get("resources", {}).get("volumes", {})) return Resources(self.obj.get(constants.resources_key, {}).get(constants.volumes_key, {}))
def get_http_proxy(self): def get_http_proxy(self):
return (self.obj[constants.network_key][constants.http_proxy_key] return self.obj.get(constants.network_key, {}).get(constants.http_proxy_key, [])
if self.obj and constants.network_key in self.obj
and constants.http_proxy_key in self.obj[constants.network_key]
else None)
def get_annotations(self): def get_annotations(self):
return self.obj.get("annotations", {}) return self.obj.get(constants.annotations_key, {})
def get_labels(self): def get_labels(self):
return self.obj.get("labels", {}) return self.obj.get(constants.labels_key, {})
def get_privileged(self): def get_privileged(self):
return "true" == str(self.obj.get("security", {}).get("privileged", "false")).lower() return "true" == str(self.obj.get(constants.security_key, {}).get("privileged", "false")).lower()
def get_capabilities(self): def get_capabilities(self):
return self.obj.get("security", {}).get("capabilities", []) return self.obj.get(constants.security_key, {}).get("capabilities", [])
def get_deployment_type(self): def get_deployment_type(self):
return self.obj[constants.deploy_to_key] return self.obj.get(constants.deploy_to_key)
def is_kubernetes_deployment(self): def is_kubernetes_deployment(self):
return self.get_deployment_type() in [constants.k8s_kind_deploy_type, constants.k8s_deploy_type] return self.get_deployment_type() in [constants.k8s_kind_deploy_type,
constants.k8s_deploy_type]
def is_kind_deployment(self): def is_kind_deployment(self):
return self.get_deployment_type() in [constants.k8s_kind_deploy_type] return self.get_deployment_type() in [constants.k8s_kind_deploy_type]