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

Validate that helm chart values exist #138

Merged
merged 5 commits into from
Jan 30, 2024
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
1 change: 1 addition & 0 deletions src/aosm/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Unreleased
* Add `publisher` command group for management of publisher resources.
* Changed the name of the `path_to_mappings` parameter in the CNF input file to `default_values`
* Added a `helm template` validation step to the `az aosm nfd build` command for the `cnf` definition type
* Added validation of the values file for helm charts when using the `az aosm nfd build` command for the `cnf` definition type
* Fixed helm chart image parsing in the `az aosm nfd build` command for the `cnf` definition type. This means that the images can now be extracted correctly from the helm chart.
* Fixed: infinite loop bug when retrying failed artifact uploads to the ACR

Expand Down
7 changes: 7 additions & 0 deletions src/aosm/azext_aosm/cli_handlers/onboarding_cnf_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,16 @@ def _validate_helm_template(self):

raise ValidationError(error_message)

def _validate_helm_values(self):
for helm_processor in self.processors:
helm_processor.input_artifact.validate_values()

def pre_validate_build(self):
"""Run all validation functions required before building the cnf."""
logger.debug("Pre-validating build")

self._validate_helm_values()

if self.skip != HELM_TEMPLATE:
self._validate_helm_template()

Expand Down
2 changes: 1 addition & 1 deletion src/aosm/azext_aosm/common/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ class SchemaGetOrGenerateError(Exception):
"""Raised when the schema cannot be generated or retrieved"""


class DefaultValuesNotFoundError(Exception):
class DefaultValuesNotFoundError(UserFault):
"""Raised when the default values file cannot be found"""
51 changes: 26 additions & 25 deletions src/aosm/azext_aosm/inputs/helm_chart_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,30 +196,37 @@ def validate_template(self) -> None:
)
return error_message

def get_defaults(self) -> Dict[str, Any]:
def validate_values(self) -> None:
"""
Retrieves the default values for the Helm chart.
Confirm that the values.yaml file exists in the Helm chart directory
or that the default values are provided.

:return: The default values for the Helm chart.
:rtype: Dict[str, Any]
:raises DefaultValuesNotFoundError: If no default values were found for the Helm chart.
:raises DefaultValuesNotFoundError: If the values.yaml and default values do not exist.
"""
logger.info("Getting default values for Helm chart input")

try:
default_config = self.default_config or self._read_values_yaml()
logger.debug(
"Default values for Helm chart input: %s",
json.dumps(default_config, indent=4),
)
return copy.deepcopy(default_config)
except FileNotFoundError as error:
logger.error("No default values found for Helm chart '%s'", self.chart_path)
default_config = self.default_config or self._read_values_yaml()

if not default_config:
logger.error("No values found for Helm chart '%s'", self.chart_path)
raise DefaultValuesNotFoundError(
"ERROR: No default values found for the Helm chart"
f" '{self.chart_path}'. Please provide default values"
" or add a values.yaml file to the Helm chart."
) from error
)

def get_defaults(self) -> Dict[str, Any]:
"""
Retrieves the default values for the Helm chart.

:return: The default values for the Helm chart.
:rtype: Dict[str, Any]
"""
default_config = self.default_config or self._read_values_yaml()
logger.debug(
"Default values for Helm chart input: %s",
json.dumps(default_config, indent=4),
)
return copy.deepcopy(default_config)

def get_schema(self) -> Dict[str, Any]:
"""
Expand All @@ -244,10 +251,10 @@ def get_schema(self) -> Dict[str, Any]:
schema = json.load(schema_file)

if not schema:
# Otherwise, generate a schema from the default values in values.yaml.
logger.debug("Generating schema from values.yaml")
# Otherwise, generate a schema from the default values or values in values.yaml.
logger.debug("Generating schema from default values")
built_schema = genson.Schema()
built_schema.add_object(self._read_values_yaml())
built_schema.add_object(self.get_defaults())
schema = built_schema.to_dict()

logger.debug(
Expand Down Expand Up @@ -412,11 +419,5 @@ def _read_values_yaml(self) -> Dict[str, Any]:
content = yaml_processor.load(f)
return content

logger.error("No values.yaml file found in Helm chart '%s'", self.chart_path)
raise FileNotFoundError(
f"ERROR: No values file was found in the Helm chart"
f" '{self.chart_path}'."
)

def __del__(self):
shutil.rmtree(self._temp_dir_path)

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import logging
from unittest import TestCase
from pathlib import Path
import os
import sys

from azext_aosm.inputs.helm_chart_input import (
HelmChartInput,
)
from azext_aosm.common.exceptions import DefaultValuesNotFoundError

code_directory = os.path.dirname(__file__)
parent_directory = os.path.abspath(os.path.join(code_directory, ".."))
Expand All @@ -21,6 +24,9 @@
class TestHelmChartInput(TestCase):
"""Test the HelmChartInput class."""

def setUp(self):
logging.basicConfig(level=logging.INFO, stream=sys.stdout)

def test_validate_template_valid_chart(self):
"""Test validating a valid Helm chart using helm template."""

Expand Down Expand Up @@ -56,3 +62,51 @@ def test_validate_template_invalid_chart(self):
output = helm_chart_input.validate_template()

assert output != ""

def test_validate_values(self):
"""Test validating whether values exist in a helm chart."""

helm_chart_input = HelmChartInput(
artifact_name="test-invalid",
artifact_version="1.0.0",
chart_path=Path(
os.path.join(
helm_charts_directory,
VALID_CHART_NAME,
)
),
)

helm_chart_input.validate_values()

# Use an Invalid chart (because it does not have values.yaml in it),
# but provide a default config parameter which will override those values.
helm_chart_input_with_default_values = HelmChartInput(
artifact_name="test-default-values",
artifact_version="1.0.0",
chart_path=Path(
os.path.join(
helm_charts_directory,
INVALID_CHART_NAME,
)
),
default_config={"test": "test"},
)

helm_chart_input_with_default_values.validate_values()

def test_validate_invalid_values(self):
"""Test validating a helm chart with values.yaml missing."""
helm_chart_input = HelmChartInput(
artifact_name="test-invalid",
artifact_version="1.0.0",
chart_path=Path(
os.path.join(
helm_charts_directory,
INVALID_CHART_NAME,
)
),
)

with self.assertRaises(DefaultValuesNotFoundError):
helm_chart_input.validate_values()