Skip to content

Commit

Permalink
Merge pull request #1097 from ministryofjustice/hotfix-tooling-env-pa…
Browse files Browse the repository at this point in the history
…ge-after-idler-2

Hotfix tooling env page after idler 2
  • Loading branch information
ymao2 authored Nov 30, 2022
2 parents aa5c392 + 8e439dd commit 801ec2f
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 70 deletions.
20 changes: 18 additions & 2 deletions controlpanel/api/cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -697,12 +697,27 @@ def restart(self, id_token):
label_selector=(f"app={self.chart_name}"),
)

@classmethod
def is_tool_deployment(cls, metadata):
"""
Currently the logic for checking whether a deployment is for tool is based on the information we put in the
deployment yaml, the common info cross tools' helm chart is the unidler-key or unide
(somehow typo in the helm chart :(), we have other alternative field for such check, e.g. whether name contains
some key words, but IMO, it is too specific.
We may change this part if we want to refactor how the tool is released and managed.
"""
return metadata.labels.get('unidler-key') is not None or metadata.labels.get('unidle-key')

@classmethod
def get_deployments(cls, user, id_token, search_name=None, search_version=None):
deployments = []
k8s = KubernetesClient(id_token=id_token)
results = k8s.AppsV1Api.list_namespaced_deployment(user.k8s_namespace)
for deployment in results.items:
if not cls.is_tool_deployment(deployment.metadata):
continue

app_name = deployment.metadata.labels["app"]
_, version = deployment.metadata.labels["chart"].rsplit("-", 1)
if search_name and search_name not in app_name:
Expand Down Expand Up @@ -744,9 +759,10 @@ def get_installed_chart_version(self, id_token):
except ObjectDoesNotExist:
return None

def get_status(self, id_token):
def get_status(self, id_token, deployment=None):
try:
deployment = self.get_deployment(id_token)
if deployment is None:
deployment = self.get_deployment(id_token)

except ObjectDoesNotExist:
log.warning(f"{self} not found")
Expand Down
45 changes: 44 additions & 1 deletion controlpanel/api/helm.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ class HelmChart:
Instances represent a Helm chart.
"""

def __init__(self, name, description, version, app_version):
def __init__(self, name, description, version, app_version, chart_url):
self.name = name
self.description = description # Human readable description.
self.version = version # Helm chart version.
self.app_version = app_version # App version used in the chart.
self.chart_url = chart_url


def _execute(*args, **kwargs):
Expand Down Expand Up @@ -137,6 +138,47 @@ def update_helm_repository():
raise HelmError(error)


def get_default_image_tag_from_helm_chart(chart_url, chart_name):
proc = _execute("show", "values", chart_url)
if proc:
output = proc.stdout.read()
values = yaml.safe_load(output) or {}
tool_name = chart_name.split("-")[0]
return values.get(tool_name, {}).get("tag") or \
values.get(tool_name, {}).get("image", {}).get("tag")
else:
return None


def get_helm_entries():
# Update and grab repository metadata.
repository = update_helm_repository()
if not repository:
# Metadata not available, so bail with empty dictionary.
return {}
return repository.get("entries")


def get_chart_version_info(entries, chart_name, chart_version):
versions = entries.get(chart_name)
chart = None
# There are versions for the referenced chart. Add them to the
# result as HelmChart instances.
for version in versions or []:
if version["version"] == chart_version:
chart = HelmChart(
version["name"],
version["description"],
version["version"],
# appVersion is relatively new so some old charts don't
# have it.
version.get("appVersion"),
version["urls"][0],
)
break
return chart


def upgrade_release(release, chart, *args):
"""
Upgrade to a new release version (for an app - e.g. RStudio).
Expand Down Expand Up @@ -213,6 +255,7 @@ def get_chart_info(chart_name):
# appVersion is relatively new so some old charts don't
# have it.
version.get("appVersion"),
version["urls"][0],
)
chart_info[chart.version] = chart
return chart_info
Expand Down
15 changes: 13 additions & 2 deletions controlpanel/api/models/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,16 @@ def app_version(self):
"""
return helm.get_chart_app_version(self.chart_name, self.version)

@property
def image_tag(self):
chart_image_key_name = self.chart_name.split("-")[0]
values = self.values or {}
return values.get("{}.tag".format(chart_image_key_name)) or \
values.get("{}.image.tag".format(chart_image_key_name))

def tool_release_tag(self, image_tag=None):
return "{}-{}-{}".format(self.chart_name, self.version, image_tag or self.image_tag)


class ToolDeploymentManager:
"""
Expand Down Expand Up @@ -192,7 +202,7 @@ def save(self, *args, **kwargs):
self.user, self.tool, self.old_chart_name
).install()

def get_status(self, id_token):
def get_status(self, id_token, deployment=None):
"""
Get the current status of the deployment.
Polls the subprocess if running, otherwise returns idled status.
Expand All @@ -205,7 +215,8 @@ def get_status(self, id_token):
log.info(status)
return status
return cluster.ToolDeployment(self.user, self.tool).get_status(
id_token
id_token,
deployment=deployment
)

def _poll(self):
Expand Down
22 changes: 2 additions & 20 deletions controlpanel/frontend/consumers.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,24 +154,9 @@ def tool_restart(self, message):
log.debug(f"Restarted {tool.name} for {user}")

def get_tool_and_user(self, message):
tool_args = {"chart_name": message["tool_name"]}
if "version" in message:
tool_args["version"] = message["version"]
# There may be two charts with the same name and version, but
# targetting different instances of our infrastructure. Ensure the
# filter args flag which infrastructure we're running on, to make sure
# the correct tool is the *first* one returned.
tool_args["target_infrastructure"] = Tool.EKS

# On restart we don't specify the version as it doesn't make
# sense to do so. As we're now allowing more than one
# Tool instance for the same chart name this means we have to
# use `filter().first()` (instead of `.get()`) to avoid getting
# a Django exception when `get()` finds more than 1 Tool record
# with the same chart name
tool = Tool.objects.filter(**tool_args).first()
tool = Tool.objects.get(pk=message["tool_id"])
if not tool:
raise Exception(f"no Tool record found for query {tool_args}")
raise Exception(f"no Tool record found for query {message['tool_id']}")
user = User.objects.get(auth0_id=message["user_id"])
return tool, user

Expand Down Expand Up @@ -211,12 +196,9 @@ def update_tool_status(tool_deployment, id_token, status):
user = tool_deployment.user
tool = tool_deployment.tool

app_version = tool_deployment.get_installed_app_version(id_token)

payload = {
"toolName": tool.chart_name,
"version": tool.version,
"appVersion": app_version,
"status": status,
}
send_sse(user.auth0_id, {"event": "toolStatus", "data": json.dumps(payload),})
Expand Down
22 changes: 12 additions & 10 deletions controlpanel/frontend/jinja2/tool-list.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ <h2 class="govuk-heading-m">{{ tool_info.name }}</h2>
{% if deployment %}
<input
type="hidden"
value="{{ deployment.tool.chart_name }}"
value="{{ deployment.chart_name }}"
name="deployed_chart_name" />
{% endif%}
<div class="govuk-form-group">
Expand All @@ -37,18 +37,20 @@ <h2 class="govuk-heading-m">{{ tool_info.name }}</h2>
data-action-target="{{ chart_name }}"
id="tools-{{ chart_name }}" name="version">
{% set installed_chart_version = None %}
{% set installed_release_version = None %}
{% if deployment %}
{% set installed_chart_version = deployment.get_installed_chart_version(id_token) %}
<option class="installed" value="{{ deployment.tool.chart_name }}__{{ installed_chart_version }}">
[{{ deployment.tool.chart_name }} {{ installed_chart_version }}] {{ deployment.get_installed_app_version(id_token) or "Unknown" }} (installed)
{% set installed_chart_version = deployment.app_version %}
{% set installed_release_version = deployment.tool_release_tag %}
<option class="installed" value="{{ deployment.chart_name }}__{{ installed_chart_version }}__{{ deployment.tool_id }}">
[{{ deployment.chart_name }} {{ deployment.image_tag }}] {{ installed_chart_version or "Unknown" }} (installed)
</option>
{% else %}
<option class="not-installed">Not deployed - select a tool from this list and click "Deploy" to start</option>
{% endif %}
{% for chart_version, app_version in tool_info["versions"].items(): %}
{% if chart_version != installed_chart_version: %}
<option value="{{ app_version.chart_name }}__{{ chart_version }}">
[{{ app_version.chart_name }} {{ chart_version }}] {{ app_version.description or "Unknown" }}
{% for release_version, release_detail in tool_info["releases"].items(): %}
{% if release_version != installed_release_version: %}
<option value="{{ release_detail.chart_name }}__{{ release_detail.chart_version }}__{{ release_detail.tool_id }}">
[{{ release_detail.chart_name }} {{ release_detail.image_tag }}] {{ release_detail.description or "Unknown" }}
</option>
{% endif %}
{% endfor %}
Expand All @@ -60,7 +62,7 @@ <h2 class="govuk-heading-m">{{ tool_info.name }}</h2>
<p class="govuk-!-margin-bottom-1">Status:
<span class="govuk-!-font-weight-bold tool-status-label">
{% if deployment %}
{{ deployment.get_status(id_token) | default("") }}
{{ deployment.status | default("") }}
{% else %}
Not deployed
{% endif %}
Expand Down Expand Up @@ -89,7 +91,7 @@ <h2 class="govuk-heading-m">{{ tool_info.name }}</h2>

<form
{% if deployment %}
action="{{ url('restart-tool', kwargs={'name': deployment.tool.chart_name}) }}"
action="{{ url('restart-tool', kwargs={'name': deployment.chart_name, 'tool_id': deployment.tool_id}) }}"
{% endif %}
data-action-name="restart"
method="post"
Expand Down
4 changes: 2 additions & 2 deletions controlpanel/frontend/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@
"parameters/delete/", views.ParameterDelete.as_view(), name="delete-parameter"
),
path("tools/", views.ToolList.as_view(), name="list-tools"),
path("tools/<str:name>/deploy/", views.DeployTool.as_view(), name="deploy-tool"),
path("tools/<str:name>/restart/", views.RestartTool.as_view(), name="restart-tool"),
path("tools/<str:name>/deploy", views.DeployTool.as_view(), name="deploy-tool"),
path("tools/<str:name>/restart/<str:tool_id>", views.RestartTool.as_view(), name="restart-tool"),
path("users/", views.UserList.as_view(), name="list-users"),
path("users/<str:pk>/", views.UserDetail.as_view(), name="manage-user"),
path("users/<str:pk>/delete/", views.UserDelete.as_view(), name="delete-user"),
Expand Down
Loading

0 comments on commit 801ec2f

Please sign in to comment.