Basic volume support for k8s #622
@ -18,6 +18,7 @@ from typing import Any, List, Set
|
|||||||
|
|
||||||
from app.opts import opts
|
from app.opts import opts
|
||||||
from app.util import get_yaml
|
from app.util import get_yaml
|
||||||
|
from app.deploy.k8s.helpers import named_volumes_from_pod_files, volume_mounts_for_service, volumes_for_pod_files
|
||||||
|
|
||||||
|
|
||||||
class ClusterInfo:
|
class ClusterInfo:
|
||||||
@ -47,6 +48,28 @@ class ClusterInfo:
|
|||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"image_set: {self.image_set}")
|
print(f"image_set: {self.image_set}")
|
||||||
|
|
||||||
|
def get_pvcs(self):
|
||||||
|
result = []
|
||||||
|
volumes = named_volumes_from_pod_files(self.parsed_pod_yaml_map)
|
||||||
|
if opts.o.debug:
|
||||||
|
print(f"Volumes: {volumes}")
|
||||||
|
for volume_name in volumes:
|
||||||
|
spec = client.V1PersistentVolumeClaimSpec(
|
||||||
|
storage_class_name="standard",
|
||||||
|
access_modes=["ReadWriteOnce"],
|
||||||
|
resources=client.V1ResourceRequirements(
|
||||||
|
requests={"storage": "2Gi"}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
pvc = client.V1PersistentVolumeClaim(
|
||||||
|
metadata=client.V1ObjectMeta(name=volume_name,
|
||||||
|
labels={"volume-label": volume_name}),
|
||||||
|
spec=spec,
|
||||||
|
)
|
||||||
|
result.append(pvc)
|
||||||
|
return result
|
||||||
|
|
||||||
|
# to suit the deployment, and also annotate the container specs to point at said volumes
|
||||||
def get_deployment(self):
|
def get_deployment(self):
|
||||||
containers = []
|
containers = []
|
||||||
for pod_name in self.parsed_pod_yaml_map:
|
for pod_name in self.parsed_pod_yaml_map:
|
||||||
@ -56,19 +79,22 @@ class ClusterInfo:
|
|||||||
container_name = service_name
|
container_name = service_name
|
||||||
service_info = services[service_name]
|
service_info = services[service_name]
|
||||||
image = service_info["image"]
|
image = service_info["image"]
|
||||||
|
volume_mounts = volume_mounts_for_service(self.parsed_pod_yaml_map, service_name)
|
||||||
container = client.V1Container(
|
container = client.V1Container(
|
||||||
name=container_name,
|
name=container_name,
|
||||||
image=image,
|
image=image,
|
||||||
ports=[client.V1ContainerPort(container_port=80)],
|
ports=[client.V1ContainerPort(container_port=80)],
|
||||||
|
volume_mounts=volume_mounts,
|
||||||
resources=client.V1ResourceRequirements(
|
resources=client.V1ResourceRequirements(
|
||||||
requests={"cpu": "100m", "memory": "200Mi"},
|
requests={"cpu": "100m", "memory": "200Mi"},
|
||||||
limits={"cpu": "500m", "memory": "500Mi"},
|
limits={"cpu": "500m", "memory": "500Mi"},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
containers.append(container)
|
containers.append(container)
|
||||||
|
volumes = volumes_for_pod_files(self.parsed_pod_yaml_map)
|
||||||
template = client.V1PodTemplateSpec(
|
template = client.V1PodTemplateSpec(
|
||||||
metadata=client.V1ObjectMeta(labels={"app": self.app_name}),
|
metadata=client.V1ObjectMeta(labels={"app": self.app_name}),
|
||||||
spec=client.V1PodSpec(containers=containers),
|
spec=client.V1PodSpec(containers=containers, volumes=volumes),
|
||||||
)
|
)
|
||||||
spec = client.V1DeploymentSpec(
|
spec = client.V1DeploymentSpec(
|
||||||
replicas=1, template=template, selector={
|
replicas=1, template=template, selector={
|
||||||
|
@ -26,6 +26,7 @@ class K8sDeployer(Deployer):
|
|||||||
name: str = "k8s"
|
name: str = "k8s"
|
||||||
core_api: client.CoreV1Api
|
core_api: client.CoreV1Api
|
||||||
apps_api: client.AppsV1Api
|
apps_api: client.AppsV1Api
|
||||||
|
k8s_namespace: str = "default"
|
||||||
kind_cluster_name: str
|
kind_cluster_name: str
|
||||||
cluster_info : ClusterInfo
|
cluster_info : ClusterInfo
|
||||||
|
|
||||||
@ -49,17 +50,27 @@ class K8sDeployer(Deployer):
|
|||||||
self.connect_api()
|
self.connect_api()
|
||||||
# Ensure the referenced containers are copied into kind
|
# Ensure the referenced containers are copied into kind
|
||||||
load_images_into_kind(self.kind_cluster_name, self.cluster_info.image_set)
|
load_images_into_kind(self.kind_cluster_name, self.cluster_info.image_set)
|
||||||
|
# Figure out the PVCs for this deployment
|
||||||
|
pvcs = self.cluster_info.get_pvcs()
|
||||||
|
for pvc in pvcs:
|
||||||
|
if opts.o.debug:
|
||||||
|
print(f"Sending this: {pvc}")
|
||||||
|
pvc_resp = self.core_api.create_namespaced_persistent_volume_claim(body=pvc, namespace=self.k8s_namespace)
|
||||||
|
if opts.o.debug:
|
||||||
|
print("PVCs created:")
|
||||||
|
print(f"{pvc_resp}")
|
||||||
# Process compose files into a Deployment
|
# Process compose files into a Deployment
|
||||||
deployment = self.cluster_info.get_deployment()
|
deployment = self.cluster_info.get_deployment()
|
||||||
# Create the k8s objects
|
# Create the k8s objects
|
||||||
resp = self.apps_api.create_namespaced_deployment(
|
|
||||||
body=deployment, namespace="default"
|
|
||||||
)
|
|
||||||
|
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print("Deployment created.\n")
|
print(f"Sending this: {deployment}")
|
||||||
print(f"{resp.metadata.namespace} {resp.metadata.name} \
|
deployment_resp = self.apps_api.create_namespaced_deployment(
|
||||||
{resp.metadata.generation} {resp.spec.template.spec.containers[0].image}")
|
body=deployment, namespace=self.k8s_namespace
|
||||||
|
)
|
||||||
|
if opts.o.debug:
|
||||||
|
print("Deployment created:")
|
||||||
|
print(f"{deployment_resp.metadata.namespace} {deployment_resp.metadata.name} \
|
||||||
|
{deployment_resp.metadata.generation} {deployment_resp.spec.template.spec.containers[0].image}")
|
||||||
|
|
||||||
def down(self, timeout, volumes):
|
def down(self, timeout, volumes):
|
||||||
# Delete the k8s objects
|
# Delete the k8s objects
|
||||||
|
@ -55,3 +55,50 @@ def pods_in_deployment(core_api: client.CoreV1Api, deployment_name: str):
|
|||||||
def log_stream_from_string(s: str):
|
def log_stream_from_string(s: str):
|
||||||
# Note response has to be UTF-8 encoded because the caller expects to decode it
|
# Note response has to be UTF-8 encoded because the caller expects to decode it
|
||||||
yield ("ignore", s.encode())
|
yield ("ignore", s.encode())
|
||||||
|
|
||||||
|
|
||||||
|
def named_volumes_from_pod_files(parsed_pod_files):
|
||||||
|
# Parse the compose files looking for named volumes
|
||||||
|
named_volumes = []
|
||||||
|
for pod in parsed_pod_files:
|
||||||
|
parsed_pod_file = parsed_pod_files[pod]
|
||||||
|
if "volumes" in parsed_pod_file:
|
||||||
|
volumes = parsed_pod_file["volumes"]
|
||||||
|
for volume in volumes.keys():
|
||||||
|
# Volume definition looks like:
|
||||||
|
# 'laconicd-data': None
|
||||||
|
named_volumes.append(volume)
|
||||||
|
return named_volumes
|
||||||
|
|
||||||
|
|
||||||
|
def volume_mounts_for_service(parsed_pod_files, service):
|
||||||
|
result = []
|
||||||
|
# Find the service
|
||||||
|
for pod in parsed_pod_files:
|
||||||
|
parsed_pod_file = parsed_pod_files[pod]
|
||||||
|
if "services" in parsed_pod_file:
|
||||||
|
services = parsed_pod_file["services"]
|
||||||
|
for service_name in services:
|
||||||
|
if service_name == service:
|
||||||
|
service_obj = services[service_name]
|
||||||
|
if "volumes" in service_obj:
|
||||||
|
volumes = service_obj["volumes"]
|
||||||
|
for mount_string in volumes:
|
||||||
|
# Looks like: test-data:/data
|
||||||
|
(volume_name, mount_path) = mount_string.split(":")
|
||||||
|
volume_device = client.V1VolumeMount(mount_path=mount_path, name=volume_name)
|
||||||
|
result.append(volume_device)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def volumes_for_pod_files(parsed_pod_files):
|
||||||
|
result = []
|
||||||
|
for pod in parsed_pod_files:
|
||||||
|
parsed_pod_file = parsed_pod_files[pod]
|
||||||
|
if "volumes" in parsed_pod_file:
|
||||||
|
volumes = parsed_pod_file["volumes"]
|
||||||
|
for volume_name in volumes.keys():
|
||||||
|
claim = client.V1PersistentVolumeClaimVolumeSource(claim_name=volume_name)
|
||||||
|
volume = client.V1Volume(name=volume_name, persistent_volume_claim=claim)
|
||||||
|
result.append(volume)
|
||||||
|
return result
|
||||||
|
Loading…
Reference in New Issue
Block a user