Skip to content

Commit

Permalink
Support multiple NodePorts, static NodePort mapping, and add 'replica…
Browse files Browse the repository at this point in the history
…s' spec option (#913)

NodePort example:

```
network:
  ports:
    caddy:
     - 1234
     - 32020:2020
```

Replicas example:

```
replicas: 2
```

This also adds an optimization for k8s where if a directory matching the name of a configmap exists in beneath config/ in the stack, its contents will be copied into the corresponding configmap.

For example:

```
# Config files in the stack
❯ ls stack-orchestrator/config/caddyconfig
Caddyfile  Caddyfile.one-req-per-upstream-example

# ConfigMap in the spec
❯ cat foo.yml | grep config
...
configmaps:
  caddyconfig: ./configmaps/caddyconfig

# Create the deployment
❯ laconic-so --stack ~/cerc/caddy-ethcache/stack-orchestrator/stacks/caddy-ethcache deploy create --spec-file foo.yml

# The files from beneath config/<config_map_name> have been copied to the ConfigMap directory from the spec.
❯ ls deployment-001/configmaps/caddyconfig
Caddyfile  Caddyfile.one-req-per-upstream-example
```

Reviewed-on: https://git.vdb.to/cerc-io/stack-orchestrator/pulls/913
Reviewed-by: David Boreham <[email protected]>
Co-authored-by: Thomas E Lackey <[email protected]>
Co-committed-by: Thomas E Lackey <[email protected]>
  • Loading branch information
telackey authored and Thomas E Lackey committed Aug 9, 2024
1 parent 64691bd commit 1def279
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 23 deletions.
1 change: 1 addition & 0 deletions stack_orchestrator/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@
security_key = "security"
annotations_key = "annotations"
labels_key = "labels"
replicas_key = "replicas"
kind_config_filename = "kind-config.yml"
kube_config_filename = "kubeconfig.yml"
7 changes: 7 additions & 0 deletions stack_orchestrator/deploy/deployment_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,13 @@ def create_operation(deployment_command_context, spec_file, deployment_dir, netw
os.mkdir(destination_script_dir)
script_paths = get_pod_script_paths(parsed_stack, pod)
_copy_files_to_directory(script_paths, destination_script_dir)
if parsed_spec.is_kubernetes_deployment():
for configmap in parsed_spec.get_configmaps():
source_config_dir = resolve_config_dir(stack_name, configmap)
if os.path.exists(source_config_dir):
destination_config_dir = deployment_dir_path.joinpath("configmaps", configmap)
copytree(source_config_dir, destination_config_dir, dirs_exist_ok=True)

# Delegate to the stack's Python code
# The deploy create command doesn't require a --stack argument so we need to insert the
# stack member here.
Expand Down
51 changes: 33 additions & 18 deletions stack_orchestrator/deploy/k8s/cluster_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,28 +78,40 @@ def int(self, pod_files: List[str], compose_env_file, deployment_name, spec: Spe
if (opts.o.debug):
print(f"Env vars: {self.environment_variables.map}")

def get_nodeport(self):
def get_nodeports(self):
nodeports = []
for pod_name in self.parsed_pod_yaml_map:
pod = self.parsed_pod_yaml_map[pod_name]
services = pod["services"]
for service_name in services:
service_info = services[service_name]
if "ports" in service_info:
port = int(service_info["ports"][0])
if opts.o.debug:
print(f"service port: {port}")
service = client.V1Service(
metadata=client.V1ObjectMeta(name=f"{self.app_name}-nodeport"),
spec=client.V1ServiceSpec(
type="NodePort",
ports=[client.V1ServicePort(
port=port,
target_port=port
)],
selector={"app": self.app_name}
)
)
return service
for raw_port in service_info["ports"]:
if opts.o.debug:
print(f"service port: {raw_port}")
if ":" in raw_port:
parts = raw_port.split(":")
if len(parts) != 2:
raise Exception(f"Invalid port definition: {raw_port}")
node_port = int(parts[0])
pod_port = int(parts[1])
else:
node_port = None
pod_port = int(raw_port)
service = client.V1Service(
metadata=client.V1ObjectMeta(name=f"{self.app_name}-nodeport-{pod_port}"),
spec=client.V1ServiceSpec(
type="NodePort",
ports=[client.V1ServicePort(
port=pod_port,
target_port=pod_port,
node_port=node_port
)],
selector={"app": self.app_name}
)
)
nodeports.append(service)
return nodeports

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
Expand Down Expand Up @@ -373,9 +385,12 @@ def get_deployment(self, image_pull_policy: str = None):
spec=client.V1PodSpec(containers=containers, image_pull_secrets=image_pull_secrets, volumes=volumes),
)
spec = client.V1DeploymentSpec(
replicas=1, template=template, selector={
replicas=self.spec.get_replicas(),
template=template, selector={
"matchLabels":
{"app": self.app_name}})
{"app": self.app_name}
}
)

deployment = client.V1Deployment(
api_version="apps/v1",
Expand Down
11 changes: 6 additions & 5 deletions stack_orchestrator/deploy/k8s/deploy_k8s.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from pathlib import Path
from kubernetes import client, config
from typing import List

from stack_orchestrator import constants
from stack_orchestrator.deploy.deployer import Deployer, DeployerConfigGenerator
Expand Down Expand Up @@ -246,8 +247,8 @@ def up(self, detach, services):
if opts.o.debug:
print("No ingress configured")

nodeport: client.V1Service = self.cluster_info.get_nodeport()
if nodeport:
nodeports: List[client.V1Service] = self.cluster_info.get_nodeports()
for nodeport in nodeports:
if opts.o.debug:
print(f"Sending this nodeport: {nodeport}")
if not opts.o.dry_run:
Expand Down Expand Up @@ -342,10 +343,10 @@ def down(self, timeout, volumes): # noqa: C901
if opts.o.debug:
print("No ingress to delete")

nodeport: client.V1Service = self.cluster_info.get_nodeport()
if nodeport:
nodeports: List[client.V1Service] = self.cluster_info.get_nodeports()
for nodeport in nodeports:
if opts.o.debug:
print(f"Deleting this nodeport: {ingress}")
print(f"Deleting this nodeport: {nodeport}")
try:
self.core_api.delete_namespaced_service(
namespace=self.k8s_namespace,
Expand Down
3 changes: 3 additions & 0 deletions stack_orchestrator/deploy/spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ def get_http_proxy(self):
def get_annotations(self):
return self.obj.get(constants.annotations_key, {})

def get_replicas(self):
return self.obj.get(constants.replicas_key, 1)

def get_labels(self):
return self.obj.get(constants.labels_key, {})

Expand Down

0 comments on commit 1def279

Please sign in to comment.