Skip to content

Commit

Permalink
Merge pull request #74 from appuio/feat/console-notification
Browse files Browse the repository at this point in the history
Customer notifications via console banner
  • Loading branch information
haasad authored Jan 13, 2025
2 parents 29948d2 + 45211d5 commit 4ce8aaa
Show file tree
Hide file tree
Showing 20 changed files with 726 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .cruft.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"name": "OpenShift4 Console",
"slug": "openshift4-console",
"parameter_key": "openshift4_console",
"test_cases": "defaults custom-plugins custom-route custom-route-legacy custom-route-managed-tls custom-links custom-logo ocp-4.14",
"test_cases": "defaults custom-plugins custom-route custom-route-legacy custom-route-managed-tls custom-links custom-logo ocp-4.14 notifications upgrade-notification",
"add_lib": "n",
"add_pp": "n",
"add_golden": "y",
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ jobs:
- custom-links
- custom-logo
- ocp-4.14
- notifications
- upgrade-notification
defaults:
run:
working-directory: ${{ env.COMPONENT_NAME }}
Expand All @@ -62,6 +64,8 @@ jobs:
- custom-links
- custom-logo
- ocp-4.14
- notifications
- upgrade-notification
defaults:
run:
working-directory: ${{ env.COMPONENT_NAME }}
Expand Down
2 changes: 1 addition & 1 deletion Makefile.vars.mk
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@ KUBENT_IMAGE ?= ghcr.io/doitintl/kube-no-trouble:latest
KUBENT_DOCKER ?= $(DOCKER_CMD) $(DOCKER_ARGS) $(root_volume) --entrypoint=/app/kubent $(KUBENT_IMAGE)

instance ?= defaults
test_instances = tests/defaults.yml tests/custom-plugins.yml tests/custom-route.yml tests/custom-route-legacy.yml tests/custom-route-managed-tls.yml tests/custom-links.yml tests/custom-logo.yml tests/ocp-4.14.yml
test_instances = tests/defaults.yml tests/custom-plugins.yml tests/custom-route.yml tests/custom-route-legacy.yml tests/custom-route-managed-tls.yml tests/custom-links.yml tests/custom-logo.yml tests/ocp-4.14.yml tests/notifications.yml tests/upgrade-notification.yml
5 changes: 5 additions & 0 deletions class/defaults.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,8 @@ parameters:

console_links: {}
custom_logo: {}

notifications: {}
upgrade_notification:
enabled: false
notification: {}
6 changes: 6 additions & 0 deletions component/main.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ local consoleRoutePatch =

local tls = import 'tls.libsonnet';

local notifications = import 'notifications.libsonnet';

{
'00_namespace': kube.Namespace(params.namespace) {
metadata+: {
Expand Down Expand Up @@ -205,4 +207,8 @@ local tls = import 'tls.libsonnet';
faviconRoute,
[if consoleRoutePatch != null then '20_ingress_config_patch']:
consoleRoutePatch,
[if std.length(notifications.rbac) > 0 then '30_notification_rbac']:
notifications.rbac,
[if std.length(notifications.notifications) > 0 then '30_notifications']: notifications.notifications,
[if std.length(notifications.upgrade_notification) > 0 then '31_upgrade_notification']: notifications.upgrade_notification,
}
255 changes: 255 additions & 0 deletions component/notifications.libsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
local com = import 'lib/commodore.libjsonnet';
local kap = import 'lib/kapitan.libjsonnet';
local kube = import 'lib/kube.libjsonnet';

local inv = kap.inventory();
local params = inv.parameters.openshift4_console;

local namespace = {
metadata+: {
namespace: params.namespace,
},
};

local makeConsoleNotification(name, args) =
kube._Object('console.openshift.io/v1', 'ConsoleNotification', name) {
metadata+: {
labels+: {
'appuio.io/notification': 'true',
},
},
spec: std.prune(
{
text: args.text,
location: std.get(args, 'location', 'BannerTop'),
color: std.get(args, 'color', '#fff'),
backgroundColor: std.get(args, 'backgroundColor', '#2596be'),
link: std.get(args, 'link'),
},
),
};

local consoleNotifications = [
makeConsoleNotification(name, params.notifications[name])
for name in std.objectFields(params.notifications)
if params.notifications[name] != null
];

local nextChannelOverlay() =
local overlays = inv.parameters.openshift_upgrade_controller.cluster_version.overlays;
local channelOverlays = {
[date]: overlays[date].spec.channel
for date in std.objectFields(overlays)
if std.objectHas(overlays[date].spec, 'channel')
&& std.split(overlays[date].spec.channel, '.')[1] > params.openshift_version.Minor
};
local date = if std.length(channelOverlays) > 0 then
std.sort(std.objectFields(channelOverlays))[0];
if date != null then
{
date: date,
channel: channelOverlays[date],
version: std.split(channelOverlays[date], '-')[1],
};

local upgradeControllerNS = {
metadata+: {
namespace: inv.parameters.openshift_upgrade_controller.namespace,
},
};

local notificationRBAC =
local argocd_sa = kube.ServiceAccount('notification-manager') + namespace;
local upgrade_sa = argocd_sa + upgradeControllerNS;
local cluster_role = kube.ClusterRole('appuio:upgrade-notification-editor') {
rules: [
{
apiGroups: [ 'console.openshift.io' ],
resources: [ 'consolenotifications' ],
verbs: [ '*' ],
},
{
apiGroups: [ 'managedupgrade.appuio.io' ],
resources: [ 'upgradeconfigs' ],
verbs: [ 'get', 'list' ],
},
// needed so that `oc version` can get the OCP server version
{
apiGroups: [ 'config.openshift.io' ],
resources: [ 'clusterversions' ],
verbs: [ 'get', 'list' ],
},
{
apiGroups: [ '' ],
resources: [ 'configmaps' ],
resourceNames: [ 'upgrade-notification-template' ],
verbs: [ '*' ],
},
],
};
local cluster_role_binding =
kube.ClusterRoleBinding('appuio:upgrade-notification-manager') {
subjects_: [ argocd_sa, upgrade_sa ],
roleRef_: cluster_role,
};
{
argocd_sa: argocd_sa,
upgrade_sa: upgrade_sa,
cluster_role: cluster_role,
cluster_role_binding: cluster_role_binding,
};

local createUpgradeNotification(overlay) =
[
kube.ConfigMap('upgrade-notification-template') + namespace {
data: {
'upgrade.yaml': std.manifestYamlDoc(
makeConsoleNotification('upgrade-%s' % overlay.version, params.upgrade_notification.notification) {
metadata+: {
labels+: {
'appuio.io/ocp-version': overlay.version,
},
},
},
),
},
},

kube.ConfigMap('console-notification-script') {
metadata+: {
namespace: params.namespace,
},
data: {
'create-console-notification.sh': (importstr 'scripts/create-console-notification.sh'),
},
},

kube.Job('create-upgrade-notification') + namespace {
metadata+: {
annotations+: {
'argocd.argoproj.io/hook': 'PostSync',
'argocd.argoproj.io/hook-delete-policy': 'BeforeHookCreation',
},
},
spec+: {
template+: {
spec+: {
containers_+: {
notification: kube.Container('notification') {
image: '%(registry)s/%(repository)s:%(tag)s' % params.images.oc,
name: 'create-console-notification',
workingDir: '/export',
command: [ '/scripts/create-console-notification.sh' ],
env_+: {
OVERLAY_DATE: overlay.date,
OVERLAY_CHANNEL: overlay.channel,
OVERLAY_VERSION: overlay.version,
OVERLAY_VERSION_MINOR: std.split(overlay.version, '.')[1],
},
volumeMounts_+: {
'upgrade-notification-template': {
mountPath: 'export/template',
readOnly: true,
},
export: {
mountPath: '/export',
},
scripts: {
mountPath: '/scripts',
},
},
},
},
volumes_+: {
'upgrade-notification-template': {
configMap: {
name: 'upgrade-notification-template',
defaultMode: std.parseOctal('0550'),
},
},
export: {
emptyDir: {},
},
scripts: {
configMap: {
name: 'console-notification-script',
defaultMode: std.parseOctal('0550'),
},
},
},
serviceAccountName: notificationRBAC.argocd_sa.metadata.name,
},
},
},
},
];


local hookScript = kube.ConfigMap('cleanup-upgrade-notification') + upgradeControllerNS {
data: {
'cleanup-upgrade-notification.sh': (importstr 'scripts/cleanup-upgrade-notification.sh'),
},
};

local ujh = kube._Object('managedupgrade.appuio.io/v1beta1', 'UpgradeJobHook', 'cleanup-upgrade-notification') + upgradeControllerNS {
spec+: {
selector: {
matchLabels: {
'appuio-managed-upgrade': 'true',
},
},
events: [
'Finish',
],
template+: {
spec+: {
template+: {
spec+: {
restartPolicy: 'Never',
containers: [
kube.Container('cleanup') {
image: '%(registry)s/%(repository)s:%(tag)s' % params.images.oc,
command: [ '/usr/local/bin/cleanup' ],
volumeMounts_+: {
scripts: {
mountPath: '/usr/local/bin/cleanup',
readOnly: true,
subPath: 'cleanup-upgrade-notification.sh',
},
},
},
],
serviceAccountName: notificationRBAC.upgrade_sa.metadata.name,
volumes: [
{
name: 'scripts',
configMap: {
name: hookScript.metadata.name,
defaultMode: std.parseOctal('0550'),
},
},
],
},
},
},
},
},
};


local upgradeNotification = if params.upgrade_notification.enabled then
local channelOverlay = nextChannelOverlay();
local notification = if channelOverlay != null then
createUpgradeNotification(channelOverlay)
else [];
notification + [
hookScript,
ujh,
] else [];

{
rbac: if params.upgrade_notification.enabled then
std.objectValues(notificationRBAC) else [],
notifications: consoleNotifications,
upgrade_notification: upgradeNotification,
}
7 changes: 7 additions & 0 deletions component/scripts/cleanup-upgrade-notification.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash
set -exo pipefail

OCP_MINOR=$(echo $JOB_spec_desiredVersion_version | jq -r 'split(".") | .[0:2] | join(".")')
echo $OCP_MINOR
echo "Deleting upgrade console notification"
kubectl delete consolenotifications -l appuio.io/ocp-version="$OCP_MINOR"
23 changes: 23 additions & 0 deletions component/scripts/create-console-notification.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash
set -euo pipefail

echo "OVERLAY_DATE: $OVERLAY_DATE"
echo "OVERLAY_CHANNEL: $OVERLAY_CHANNEL"
echo "OVERLAY_VERSION: $OVERLAY_VERSION"
echo "OVERLAY_VERSION_MINOR: $OVERLAY_VERSION_MINOR"

NEXT_MAINTENANCE=$(kubectl -n appuio-openshift-upgrade-controller get upgradeconfigs -oyaml | yq '[.items[].status.nextPossibleSchedules[].time | from_yaml | select(. > env(OVERLAY_DATE))][0] | tz("Europe/Zurich") | format_datetime("02.01.2006 15:04")')
test -n "${NEXT_MAINTENANCE:-}" || (echo "No valid maintenance window found" && exit 1)
echo "NEXT_MAINTENANCE: $NEXT_MAINTENANCE"
export NEXT_MAINTENANCE

yq '(.. | select(tag == "!!str")) |= envsubst' template/upgrade.yaml > notification.yaml
cat notification.yaml

CURRENT_MINOR=$(oc version -oyaml | yq '.openshiftVersion' | cut -d'.' -f2)
if (( OVERLAY_VERSION_MINOR > CURRENT_MINOR )); then
echo "Creating console notification:"
kubectl apply -f notification.yaml
else
echo "Current OpenShift minor version matches channel overlay version."
fi
Loading

0 comments on commit 4ce8aaa

Please sign in to comment.