Skip to content

Commit

Permalink
Fix portals when querying apps
Browse files Browse the repository at this point in the history
  • Loading branch information
Qubad786 committed Jul 24, 2024
1 parent ff6e85e commit 6a9ab15
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 10 deletions.
20 changes: 14 additions & 6 deletions src/middlewared/middlewared/plugins/apps/crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
import textwrap

from middlewared.schema import accepts, Bool, Dict, Int, List, returns, Str
from middlewared.service import CallError, CRUDService, filterable, InstanceNotFound, job, private
from middlewared.service import CallError, CRUDService, filterable, InstanceNotFound, job, pass_app, private
from middlewared.utils import filter_list
from middlewared.validators import Match, Range

from .compose_utils import compose_action
from .ix_apps.lifecycle import add_context_to_values, get_current_app_config, update_app_config
from .ix_apps.metadata import update_app_metadata
from .ix_apps.metadata import update_app_metadata, update_app_metadata_for_portals
from .ix_apps.path import get_installed_app_path, get_installed_app_version_path
from .ix_apps.query import list_apps
from .ix_apps.setup import setup_install_app_dir
Expand Down Expand Up @@ -66,18 +66,25 @@ class Config:
)

@filterable
def query(self, filters, options):
@pass_app(rest=True)
def query(self, app, filters, options):
"""
Query all apps with `query-filters` and `query-options`.
`query-options.extra.host_ip` can be provided to override portal IP address if it is a wildcard.
"""
if not self.middleware.call_sync('docker.state.validate', False):
return filter_list([], filters, options)

kwargs = {}
extra = options.get('extra', {})
kwargs = {
'host_ip': extra.get('host_ip') or self.middleware.call_sync('interface.websocket_local_ip', app=app)
}
if len(filters) == 1 and filters[0][0] in ('id', 'name') and filters[0][1] == '=':
kwargs = {'specific_app': filters[0][2]}

available_apps_mapping = self.middleware.call_sync('catalog.train_to_apps_version_mapping')

return filter_list(list_apps(available_apps_mapping, **kwargs), filters, options)

@accepts(Str('app_name'))
Expand Down Expand Up @@ -121,7 +128,7 @@ def do_create(self, job, data):
"""
self.middleware.call_sync('docker.state.validate')

if self.query([['id', '=', data['app_name']]]):
if self.middleware.call_sync('app.query', [['id', '=', data['app_name']]]):
raise CallError(f'Application with name {data["app_name"]} already exists', errno=errno.EEXIST)

app_name = data['app_name']
Expand Down Expand Up @@ -157,9 +164,9 @@ def do_create(self, job, data):
app_version_details = self.middleware.call_sync(
'catalog.app_version_details', get_installed_app_version_path(app_name, version)
)
update_app_metadata(app_name, app_version_details)
new_values = add_context_to_values(app_name, new_values, install=True)
update_app_config(app_name, version, new_values)
update_app_metadata(app_name, app_version_details)

job.set_progress(60, 'App installation in progress, pulling images')
compose_action(app_name, version, 'up', force_recreate=True, remove_orphans=True)
Expand Down Expand Up @@ -215,6 +222,7 @@ def update_internal(self, job, app, data, progress_keyword='Update'):

new_values = add_context_to_values(app_name, new_values, update=True)
update_app_config(app_name, app['version'], new_values)
update_app_metadata_for_portals(app_name, app['version'])
job.set_progress(60, 'Configuration updated, updating docker resources')
compose_action(app_name, app['version'], 'up', force_recreate=True, remove_orphans=True)

Expand Down
15 changes: 14 additions & 1 deletion src/middlewared/middlewared/plugins/apps/ix_apps/metadata.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import os
import typing

import yaml

from .path import get_collective_config_path, get_collective_metadata_path, get_installed_app_metadata_path
from .portals import get_portals_and_app_notes


def get_app_metadata(app_name: str) -> dict[str, typing.Any]:
Expand All @@ -17,7 +19,18 @@ def update_app_metadata(app_name: str, app_version_details: dict):
with open(get_installed_app_metadata_path(app_name), 'w') as f:
f.write(yaml.safe_dump({
'metadata': app_version_details['app_metadata'],
**{k: app_version_details[k] for k in ('version', 'human_version')}
**{k: app_version_details[k] for k in ('version', 'human_version')},
**get_portals_and_app_notes(app_name, app_version_details['version']),
}))


def update_app_metadata_for_portals(app_name: str, version: str):
# This should be called after config of app has been updated as that will render compose files
app_metadata = get_app_metadata(app_name)
with open(get_installed_app_metadata_path(app_name), 'w') as f:
f.write(yaml.safe_dump({
**app_metadata,
**get_portals_and_app_notes(app_name, version),
}))


Expand Down
44 changes: 44 additions & 0 deletions src/middlewared/middlewared/plugins/apps/ix_apps/portals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import contextlib

import yaml

from apps_validation.portals import IX_NOTES_KEY, IX_PORTAL_KEY, validate_portals_and_notes, ValidationErrors

from .lifecycle import get_rendered_templates_of_app


def normalized_port_value(scheme: str, port: int) -> str:
return '' if ((scheme == 'http' and port == 80) or (scheme == 'https' and port == 443)) else f':{port}'


def get_portals_and_app_notes(app_name: str, version: str) -> dict:
rendered_config = {}
for rendered_file in get_rendered_templates_of_app(app_name, version):
with contextlib.suppress(FileNotFoundError, yaml.YAMLError):
with open(rendered_file, 'r') as f:
rendered_config.update(yaml.safe_load(f.read()))

portal_and_notes_config = {
k: rendered_config[k]
for k in (IX_NOTES_KEY, IX_PORTAL_KEY)
if k in rendered_config
}
config = {
'portals': {},
'notes': None,
}
if portal_and_notes_config:
try:
validate_portals_and_notes('portal', portal_and_notes_config)
except ValidationErrors:
return config

portals = {}
for portal in portal_and_notes_config.get(IX_PORTAL_KEY, []):
port_value = normalized_port_value(portal['scheme'], portal['port'])
portals[portal['name']] = f'{portal["scheme"]}://{portal["host"]}{port_value}{portal.get("path", "")}'

return {
'portals': portals,
'notes': portal_and_notes_config.get(IX_NOTES_KEY),
}
18 changes: 15 additions & 3 deletions src/middlewared/middlewared/plugins/apps/ix_apps/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,21 @@ def upgrade_available_for_app(
return False


def normalize_portal_uri(portal_uri: str, host_ip: str | None) -> str:
if not host_ip or '0.0.0.0' not in portal_uri:
return portal_uri

return portal_uri.replace('0.0.0.0', host_ip)


def normalize_portal_uris(portals: dict[str, str], host_ip: str | None) -> dict[str, str]:
return {name: normalize_portal_uri(uri, host_ip) for name, uri in portals.items()}


def list_apps(
train_to_apps_version_mapping: dict[str, dict[str, dict[str, str]]],
specific_app: str | None = None
specific_app: str | None = None,
host_ip: str | None = None,
) -> list[dict]:
apps = []
app_names = set()
Expand Down Expand Up @@ -71,7 +83,7 @@ def list_apps(
'active_workloads': get_default_workload_values() if state == 'STOPPED' else workloads,
'state': state,
'upgrade_available': upgrade_available_for_app(train_to_apps_version_mapping, app_metadata['metadata']),
**app_metadata,
**app_metadata | {'portals': normalize_portal_uris(app_metadata['portals'], host_ip)}
})

if specific_app and specific_app in app_names:
Expand All @@ -94,7 +106,7 @@ def list_apps(
'active_workloads': get_default_workload_values(),
'state': 'STOPPED',
'upgrade_available': upgrade_available_for_app(train_to_apps_version_mapping, app_metadata['metadata']),
**app_metadata,
**app_metadata | {'portals': normalize_portal_uris(app_metadata['portals'], host_ip)}
})

return apps
Expand Down

0 comments on commit 6a9ab15

Please sign in to comment.