Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SS 1233 Kubernetes manifest yaml validation #255

Merged
merged 20 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
f12e315
Ignoring dot files from model diagrams
alfredeen Nov 22, 2024
268a62d
Added validation of user app k8s deployment manifest yaml.
alfredeen Nov 22, 2024
eff7571
Minor changes to tasks
alfredeen Nov 26, 2024
cf50975
Merge branch 'develop' into ss-1073-shinyproxy-volume-support
alfredeen Nov 26, 2024
90113d0
Created a class and test for k8s deployment manifests.
alfredeen Nov 27, 2024
cc704a7
Added more methods and tests to deployment manifest class. Refactorin…
alfredeen Nov 28, 2024
4703e66
Added more manifest validation
alfredeen Nov 29, 2024
1f50a25
Trying to fix helm teplate command permission errorby setting repo ca…
alfredeen Nov 29, 2024
59d730f
Creating custom values file for unit test runs.
alfredeen Nov 29, 2024
b03b3df
Added manifest validation with python lib kubernetes-validate
alfredeen Nov 29, 2024
fd6043f
Cleaned up some of the manifest validation logic.
alfredeen Dec 2, 2024
aefc983
Added more tests of an invalid manifest
alfredeen Dec 2, 2024
33a95ba
Added function to extract and validate the kubernetes-pod-patches sec…
alfredeen Dec 2, 2024
9aabe19
Cleaned up tasks and made chart version dynamic
alfredeen Dec 3, 2024
4e7b747
Merge branch 'develop' into ss-1073-shinyproxy-volume-support
alfredeen Dec 3, 2024
70e4dbd
Merge branch 'develop' into ss-1073-shinyproxy-volume-support
alfredeen Dec 5, 2024
b86c844
Logging and removed an unused line
alfredeen Dec 9, 2024
da85fcd
Using named tuples for more structured method return values.
alfredeen Dec 9, 2024
6d7b13d
Added a new Django setting CLUSTER_VERSION. Made some return types mo…
alfredeen Dec 9, 2024
7a5ec60
Merge branch 'develop' into ss-1073-shinyproxy-volume-support
alfredeen Dec 9, 2024
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ coverage.xml
.hypothesis/
.pytest_cache/

# Model diagrams from graphviz
*.dot

# Translations
*.mo
*.pot
Expand Down
68 changes: 66 additions & 2 deletions apps/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import yaml
from celery import shared_task
from django.apps import apps
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.db import transaction
from django.utils import timezone
Expand Down Expand Up @@ -120,6 +121,32 @@ def helm_delete(release_name, namespace="default"):
return e.stdout, e.stderr


@shared_task
def get_manifest_yaml(release_name: str, namespace: str = "default"):
alfredeen marked this conversation as resolved.
Show resolved Hide resolved
command = f"helm get manifest {release_name} --namespace {namespace}"
# command = f"kubectl get configmap cm -n default -o yaml | yq eval '.data[\"application.yml\"]'"
# Execute the command
logger.debug(f"Executing command: {command}")
try:
result = subprocess.run(command.split(" "), check=True, text=True, capture_output=True)
return result.stdout, None
except subprocess.CalledProcessError as e:
return e.stdout, e.stderr


def validate_manifest_file(manifest_file: str):
alfredeen marked this conversation as resolved.
Show resolved Hide resolved
"""Validates a k8s deployment manifest file."""
command = f"kubectl apply --dry-run=client -f {manifest_file}"
# command = f"kubernetes-validate {manifest_file}"
# Execute the command
logger.debug(f"Executing command: {command}")
try:
result = subprocess.run(command.split(" "), check=True, text=True, capture_output=True)
return result.stdout, None
except subprocess.CalledProcessError as e:
return e.stdout, e.stderr


@shared_task
@transaction.atomic
def deploy_resource(serialized_instance):
Expand All @@ -132,12 +159,16 @@ def deploy_resource(serialized_instance):
else:
version = None
chart = instance.chart
# Create a unique identifier text for these deployment files:
now = datetime.now().strftime("%Y%m%d_%H%M%S")
deployment_fileid = f"{now}_{str(uuid.uuid4())}"
# Save helm values file for internal reference
values_file = f"charts/values/{str(uuid.uuid4())}.yaml"
values_file = f"charts/values/{deployment_fileid}.yaml"
with open(values_file, "w") as f:
f.write(yaml.dump(values))

output, error = helm_install(values["subdomain"], chart, values["namespace"], values_file, version)
release = values["subdomain"]
output, error = helm_install(release, chart, values["namespace"], values_file, version)
success = not error

helm_info = {"success": success, "info": {"stdout": output, "stderr": error}}
Expand All @@ -148,6 +179,39 @@ def deploy_resource(serialized_instance):
instance.app_status.save()
instance.save()

# In development, also generate and validate the k8s deployment manifest
if settings.DEBUG:
alfredeen marked this conversation as resolved.
Show resolved Hide resolved
logger.debug(f"Generating and validating k8s deployment yaml for release {release}")
# Get the deployment manifest yaml
output, error = get_manifest_yaml(release)
if error:
logger.warning(f"Unable to get the deployment manifest for release {release}. Error: {error}")
# Save the manifest yaml to this file:
deployment_file = f"charts/values/{deployment_fileid}_deployment.yaml"
with open(deployment_file, "w") as f:
f.write(output)
if not error:
# Validate the manifest yaml if there was no error
logger.debug(f"Validating the deployment manifest yaml for release {release}")

# TODO: Remove when done testing:
deployment_file = "./charts/values/app_deployment_invalid.yaml"

output, error = validate_manifest_file(deployment_file)
# TODO: Remove this when done testing:
logger.debug(f"Validation result={output}")

if error:
logger.warning(f"Unable to get the deployment manifest for release {release}. Error: {error}")
else:
# Parse the output for any errors:
if output is not None and "error:" in output:
alfredeen marked this conversation as resolved.
Show resolved Hide resolved
logger.warning(f"The manifest file for release {release} contains errors: {output}")
else:
logger.debug(f"The manifest file for release {release} is valid: {deployment_file}")
# Delete the file is there was no error and if it is valid
subprocess.run(["rm", "-f", deployment_file])

subprocess.run(["rm", "-f", values_file])


Expand Down