diff --git a/cloudman/helmsman/api.py b/cloudman/helmsman/api.py index 84e6c71b..aa96ed96 100644 --- a/cloudman/helmsman/api.py +++ b/cloudman/helmsman/api.py @@ -203,7 +203,9 @@ def list(self, namespace=None): state=release.get("STATUS"), updated=release.get("UPDATED"), values=HelmClient().releases.get_values( - release.get("NAMESPACE"), release.get("NAME"), get_all=True) + release.get("NAMESPACE"), release.get("NAME"), get_all=True), + install_template=self._find_closest_install_template( + client.releases.parse_chart_name(release.get('CHART'))) ) for release in releases ) @@ -215,6 +217,10 @@ def get(self, chart_id): self.check_permissions('helmsman.view_chart', chart) return chart + def _find_closest_install_template(self, chart_name): + client = HelmsManAPI(self.context) + return client.templates.find(chart_name=chart_name) + def _get_from_namespace(self, namespace, chart_name): matches = [c for c in self.list(namespace) if c.name == chart_name] if matches: @@ -328,15 +334,14 @@ def list(self): (tmpl for tmpl in models.HMInstallTemplate.objects.all() if self.has_permissions('helmsman.view_install_template', tmpl)))) - def find(self, name): - try: - obj = models.HMInstallTemplate.objects.get(name=name) - if self.has_permissions('helmsman.view_install_template', obj): - return self.to_api_object(obj) - else: - return None - except models.HMInstallTemplate.DoesNotExist: - return None + def find(self, name=None, chart_name=None): + search_terms = {'name': name, 'chart': chart_name} + matches = list(models.HMInstallTemplate.objects.filter( + **{k: v for k, v in search_terms.items() if v})) + if matches: + if self.has_permissions('helmsman.view_install_template', matches[0]): + return self.to_api_object(matches[0]) + return None class HelmsManResource(object): @@ -363,6 +368,7 @@ def __init__(self, service, id, name, namespace, **kwargs): self.updated = kwargs.get('updated') self.access_address = '/%s/' % name self.values = kwargs.get('values') or {} + self.install_template = kwargs.get('install_template') def delete(self): self.service.delete(self) diff --git a/cloudman/helmsman/management/commands/add_install_template.py b/cloudman/helmsman/management/commands/add_install_template.py index 3c7543b6..79d5c68e 100644 --- a/cloudman/helmsman/management/commands/add_install_template.py +++ b/cloudman/helmsman/management/commands/add_install_template.py @@ -60,7 +60,7 @@ def add_install_template(name, repo, chart, chart_version, template, print(f"Adding template: {name}") admin = User.objects.filter(is_superuser=True).first() client = HelmsManAPI(HMServiceContext(user=admin)) - existing_template = client.templates.find(name) + existing_template = client.templates.find(name=name) if existing_template: print(f"Template named: '{name}' for chart: '{repo}/{chart}'" " already exists.") diff --git a/cloudman/helmsman/serializers.py b/cloudman/helmsman/serializers.py index 6515b4f4..38ed73fb 100644 --- a/cloudman/helmsman/serializers.py +++ b/cloudman/helmsman/serializers.py @@ -4,6 +4,49 @@ from .api import HelmsManAPI +class HMInstallTemplateSerializer(serializers.Serializer): + id = serializers.CharField(read_only=True) + name = serializers.CharField() + repo = serializers.SlugField() + chart = serializers.SlugField() + chart_version = serializers.CharField(allow_blank=True, required=False) + template = serializers.CharField() + context = serializers.DictField(required=False) + display_name = serializers.CharField(allow_blank=True, required=False) + summary = serializers.CharField(allow_blank=True, required=False) + description = serializers.CharField(allow_blank=True, required=False) + maintainers = serializers.CharField(allow_blank=True, required=False) + info_url = serializers.CharField(allow_blank=True, required=False) + icon_url = serializers.CharField(allow_blank=True, required=False) + + def create(self, valid_data): + return HelmsManAPI.from_request( + self.context['request']).templates.create( + name=valid_data.get('name'), + repo=valid_data.get('repo'), + chart=valid_data.get('chart'), + chart_version=valid_data.get('chart_version'), + template=valid_data.get('template'), + context=valid_data.get('context'), + display_name=valid_data.get('display_name'), + summary=valid_data.get('summary'), + description=valid_data.get('description'), + maintainers=valid_data.get('maintainers'), + info_url=valid_data.get('info_url'), + icon_url=valid_data.get('icon_url')) + + def render_values(self, valid_data): + return HelmsManAPI.from_request(self.context['request'] + ).templates.render_values( + valid_data.get('name'), + **valid_data) + + def delete(self, valid_data): + return HelmsManAPI.from_request(self.context['request'] + ).templates.delete( + valid_data.get('name')) + + class HMChartRepoSerializer(serializers.Serializer): id = serializers.CharField(read_only=True) name = serializers.CharField() @@ -23,7 +66,8 @@ class HMChartSerializer(serializers.Serializer): values = serializers.DictField(required=False) repo = HMChartRepoSerializer(read_only=True) repo_name = serializers.CharField(write_only=True, allow_blank=True, required=False) - install_template = serializers.CharField(write_only=True, allow_blank=True, required=False) + install_template = HMInstallTemplateSerializer(read_only=True, required=False) + use_install_template = serializers.CharField(write_only=True, allow_blank=True, required=False) def create(self, valid_data): return HelmsManAPI.from_request(self.context['request']).charts.create( @@ -53,46 +97,3 @@ def delete(self, valid_data): return HelmsManAPI.from_request(self.context['request'] ).namespaces.delete( valid_data.get('name')) - - -class HMInstallTemplateSerializer(serializers.Serializer): - id = serializers.CharField(read_only=True) - name = serializers.CharField() - repo = serializers.SlugField() - chart = serializers.SlugField() - chart_version = serializers.CharField(allow_blank=True, required=False) - template = serializers.CharField() - context = serializers.DictField(required=False) - display_name = serializers.CharField(allow_blank=True, required=False) - summary = serializers.CharField(allow_blank=True, required=False) - description = serializers.CharField(allow_blank=True, required=False) - maintainers = serializers.CharField(allow_blank=True, required=False) - info_url = serializers.CharField(allow_blank=True, required=False) - icon_url = serializers.CharField(allow_blank=True, required=False) - - def create(self, valid_data): - return HelmsManAPI.from_request( - self.context['request']).templates.create( - name=valid_data.get('name'), - repo=valid_data.get('repo'), - chart=valid_data.get('chart'), - chart_version=valid_data.get('chart_version'), - template=valid_data.get('template'), - context=valid_data.get('context'), - display_name=valid_data.get('display_name'), - summary=valid_data.get('summary'), - description=valid_data.get('description'), - maintainers=valid_data.get('maintainers'), - info_url=valid_data.get('info_url'), - icon_url=valid_data.get('icon_url')) - - def render_values(self, valid_data): - return HelmsManAPI.from_request(self.context['request'] - ).templates.render_values( - valid_data.get('name'), - **valid_data) - - def delete(self, valid_data): - return HelmsManAPI.from_request(self.context['request'] - ).templates.delete( - valid_data.get('name')) diff --git a/cloudman/projman/api.py b/cloudman/projman/api.py index a4e04b59..3cafebb0 100644 --- a/cloudman/projman/api.py +++ b/cloudman/projman/api.py @@ -109,7 +109,7 @@ def _init_default_project_charts(self, project): # That way, we can define a project with pre-installed # charts client = self._get_helmsman_api() - chart_template = client.templates.find('projman') + chart_template = client.templates.find(name='projman') if chart_template: project.charts.create(chart_template.name) else: diff --git a/cloudman/projman/serializers.py b/cloudman/projman/serializers.py index cf4d826e..397d5931 100644 --- a/cloudman/projman/serializers.py +++ b/cloudman/projman/serializers.py @@ -51,7 +51,7 @@ def create(self, valid_data): raise ValidationError("Specified project id: %s does not exist" % project_id) return project.charts.create( - valid_data.get('install_template'), + valid_data.get('use_install_template'), release_name=valid_data.get('release_name'), values=valid_data.get('values')) diff --git a/cloudman/projman/tests/test_project_api.py b/cloudman/projman/tests/test_project_api.py index a0254fd3..819054c5 100644 --- a/cloudman/projman/tests/test_project_api.py +++ b/cloudman/projman/tests/test_project_api.py @@ -21,6 +21,11 @@ class ProjManManServiceTestBase(HelmsManServiceTestBase): INITIAL_HELMSMAN_DATA = os.path.join( TEST_DATA_PATH, 'helmsman_config.yaml') + @staticmethod + def load_install_template(template_path): + with open(template_path) as f: + return yaml.safe_load(f.read()) + def setUp(self): super().setUp() self.client.force_login( @@ -170,6 +175,10 @@ class ProjectChartServiceTests(ProjManManServiceTestBase): 'name': 'gvl' } + INSTALL_TEMPLATE_DATA = ProjManManServiceTestBase.load_install_template( + ProjManManServiceTestBase.INITIAL_HELMSMAN_DATA).get( + 'install_templates').get('galaxy') + CHART_DATA = { 'name': 'galaxy', 'display_name': 'Galaxy', @@ -194,7 +203,7 @@ def _create_project(self, project_data=None): def _create_project_chart(self, project_id): url = reverse('projman:chart-list', args=[project_id]) chart_data = dict(self.CHART_DATA) - chart_data['install_template'] = 'galaxy' + chart_data['use_install_template'] = 'galaxy' return self.client.post(url, chart_data, format='json') def _delete_project(self, project_id): @@ -208,9 +217,15 @@ def _list_project_chart(self, project_id, project_data=None): self.assertEqual(response.status_code, status.HTTP_200_OK, response.data) self.assertDictContainsSubset(project_data or self.PROJECT_DATA, response.data['results'][1]['project']) # Flatten dicts because assertDictContainsSubset doesn't handle nested dicts + # Validate chart response_chart = hm_helpers.flatten_dict(response.data['results'][1]) expected_chart = hm_helpers.flatten_dict(self.CHART_DATA) self.assertDictContainsSubset(expected_chart, response_chart) + # Validate template + response_values = hm_helpers.flatten_dict(response.data['results'][1]['install_template']) + expected_values = hm_helpers.flatten_dict(self.INSTALL_TEMPLATE_DATA) + self.assertDictContainsSubset(expected_values, response_values) + # Validate values response_values = hm_helpers.flatten_dict(response.data['results'][1]['values']) expected_values = hm_helpers.flatten_dict(self.EXPECTED_CHART_VALUES) self.assertDictContainsSubset(expected_values, response_values)