Skip to content

Commit 0e52df5

Browse files
authored
Merge pull request #83 from appuio/ocp-4.19/logos
Support for new per-theme custom logos and custom favicon for OpenShift 4.19
2 parents fc129b9 + 5df3085 commit 0e52df5

File tree

7 files changed

+333
-13
lines changed

7 files changed

+333
-13
lines changed

class/defaults.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ parameters:
1414
openshift.io/node-selector: ''
1515
openshift_version:
1616
Major: '4'
17-
Minor: '17'
17+
Minor: '19'
1818
route:
1919
console: {}
2020
downloads: {}
@@ -25,7 +25,11 @@ parameters:
2525
cert_manager_certs: {}
2626

2727
console_links: {}
28-
custom_logo: {}
28+
custom_logos:
29+
'*': {}
30+
light: {}
31+
dark: {}
32+
custom_favicon: {}
2933

3034
notifications: {}
3135
upgrade_notification:

component/main.jsonnet

Lines changed: 155 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,92 @@ local params = inv.parameters.openshift4_console;
77

88
local versionGroup = 'operator.openshift.io/v1';
99

10-
local logoFileName =
11-
if std.length(std.objectFields(params.custom_logo)) > 0 then
12-
assert std.length(std.objectFields(params.custom_logo)) == 1 :
10+
local openshiftMinor = std.parseInt(params.openshift_version.Minor);
11+
12+
local validateConfig(obj, kind='logo') =
13+
assert
14+
std.set(std.objectFields(obj)) == std.set([ 'type', 'data' ]) :
15+
'Expected custom %s config to have keys `type` and `data`' % [ kind ];
16+
obj;
17+
18+
local customLogos =
19+
local keys = std.set(std.objectFields(params.custom_logos));
20+
local unsupportedKeys = std.setDiff(keys, std.set([ 'light', 'dark', '*' ]));
21+
assert std.length(unsupportedKeys) == 0 :
22+
'Parameter `custom_logos` contains unsupported keys: %s' %
23+
[ unsupportedKeys ];
24+
local config = {
25+
default: if std.length(params.custom_logos['*']) > 0 then
26+
validateConfig(params.custom_logos['*']) {
27+
key: 'default.%s' % super.type,
28+
}
29+
else {},
30+
Dark: if std.length(params.custom_logos.dark) > 0 then
31+
validateConfig(params.custom_logos.dark) {
32+
key: 'dark.%s' % super.type,
33+
}
34+
else {},
35+
Light: if std.length(params.custom_logos.light) > 0 then
36+
validateConfig(params.custom_logos.light) {
37+
key: 'light.%s' % super.type,
38+
}
39+
else {},
40+
};
41+
if openshiftMinor > 18 && std.length(std.prune(config)) > 0 then
42+
{
43+
cm_data: {
44+
[if std.length(config[theme]) > 0 && config[theme].type == 'svg' then config[theme].key]:
45+
config[theme].data
46+
for theme in [ 'default', 'Light', 'Dark' ]
47+
},
48+
cm_bindata: {
49+
[if std.length(config[theme]) > 0 && config[theme].type != 'svg' then config[theme].key]:
50+
config[theme].data
51+
for theme in [ 'default', 'Light', 'Dark' ]
52+
},
53+
config: {
54+
type: 'Masthead',
55+
themes: [
56+
{
57+
mode: theme,
58+
source: {
59+
from: 'ConfigMap',
60+
configMap: {
61+
name: 'console-logos',
62+
key: (if std.length(config[theme]) > 0 then config[theme] else config.default).key,
63+
},
64+
},
65+
}
66+
for theme in [ 'Light', 'Dark' ]
67+
],
68+
},
69+
}
70+
else
71+
{};
72+
73+
local favicon =
74+
if openshiftMinor > 18 && std.length(params.custom_favicon) > 0 then
75+
local config = validateConfig(params.custom_favicon, kind='favicon');
76+
config {
77+
key: 'favicon.%s' % super.type,
78+
}
79+
else
80+
{};
81+
82+
local legacyLogoFileName =
83+
local legacy_logo = std.get(params, 'custom_logo', {});
84+
if std.length(std.objectFields(legacy_logo)) > 0 then
85+
assert std.length(std.objectFields(legacy_logo)) == 1 :
1386
'The parameter custom_logo can only contain a single logo';
14-
local name = std.objectFields(params.custom_logo)[0];
87+
local name = std.objectFields(legacy_logo)[0];
1588
local nameParts = std.split(name, '.');
1689
assert std.length(nameParts) > 1 :
1790
'The key of custom_logo must provide a filename with a valid filename extension';
18-
name
91+
std.trace(
92+
'Parameter `custom_logo` is deprecated for OpenShift 4.19 and newer. '
93+
+ 'Use parameters `custom_logos` and `custom_favicon` instead.',
94+
name
95+
)
1996
else
2097
'';
2198

@@ -48,7 +125,7 @@ local consolePlugins =
48125
// set default plugins dynamically based on OCP minor version and append
49126
// user-configured plugins to the default.
50127
local defaults = [ 'monitoring-plugin' ] + (
51-
if std.parseInt(params.openshift_version.Minor) > 16 then
128+
if openshiftMinor > 16 then
52129
[ 'networking-console-plugin' ]
53130
else
54131
[]
@@ -75,21 +152,58 @@ local consoleSpec =
75152
plugins: consolePlugins,
76153
} +
77154
(
78-
if logoFileName != '' then
155+
if legacyLogoFileName != '' then
79156
{
80157
customization+: {
81158
customLogoFile: {
82-
key: logoFileName,
159+
key: legacyLogoFileName,
83160
name: 'console-logo',
84161
},
85162
},
86163
}
87164
else
88165
{}
166+
) + (
167+
if std.length(customLogos) > 0 then
168+
{
169+
customization+: {
170+
logos+: [
171+
customLogos.config,
172+
],
173+
},
174+
}
175+
else
176+
{}
177+
) + (
178+
if std.length(favicon) > 0 then
179+
{
180+
customization+: {
181+
logos+: [
182+
{
183+
type: 'Favicon',
184+
themes: [
185+
{
186+
mode: mode,
187+
source: {
188+
from: 'ConfigMap',
189+
configMap: {
190+
name: 'console-favicon',
191+
key: favicon.key,
192+
},
193+
},
194+
}
195+
for mode in [ 'Dark', 'Light' ]
196+
],
197+
},
198+
],
199+
},
200+
}
201+
else
202+
{}
89203
);
90204

91205
local faviconRoute =
92-
if logoFileName != '' && hostname != null then
206+
if legacyLogoFileName != '' && hostname != null then
93207
kube._Object('route.openshift.io/v1', 'Route', 'console-favicon') {
94208
metadata+: {
95209
namespace: 'openshift-console',
@@ -187,7 +301,7 @@ local notifications = import 'notifications.libsonnet';
187301
},
188302
[if std.length(tls.secrets) > 0 then '01_tls_secrets']: tls.secrets,
189303
[if std.length(tls.certs) > 0 then '01_certs']: tls.certs,
190-
[if logoFileName != '' then '01_logo']:
304+
[if legacyLogoFileName != '' then '01_logo']:
191305
kube.ConfigMap('console-logo') {
192306
metadata+: {
193307
// ConfigMap must be deployed in namespace openshift-config
@@ -201,6 +315,37 @@ local notifications = import 'notifications.libsonnet';
201315
},
202316
data: params.custom_logo,
203317
},
318+
[if std.length(customLogos) > 0 then '01_logos']:
319+
kube.ConfigMap('console-logos') {
320+
metadata+: {
321+
// ConfigMap must be deployed in namespace openshift-config
322+
namespace: 'openshift-config',
323+
// ConfigMap will be copied to namespace openshift-console
324+
// To prevent ArgoCD from removing or complaining about the copy we add these annotations
325+
annotations+: {
326+
'argocd.argoproj.io/sync-options': 'Prune=false',
327+
'argocd.argoproj.io/compare-options': 'IgnoreExtraneous',
328+
},
329+
},
330+
binaryData: customLogos.cm_bindata,
331+
data: customLogos.cm_data,
332+
},
333+
[if std.length(favicon) > 0 then '01_favicon']:
334+
kube.ConfigMap('console-favicon') {
335+
metadata+: {
336+
// ConfigMap must be deployed in namespace openshift-config
337+
namespace: 'openshift-config',
338+
// ConfigMap will be copied to namespace openshift-console
339+
// To prevent ArgoCD from removing or complaining about the copy we add these annotations
340+
annotations+: {
341+
'argocd.argoproj.io/sync-options': 'Prune=false',
342+
'argocd.argoproj.io/compare-options': 'IgnoreExtraneous',
343+
},
344+
},
345+
[if favicon.type == 'svg' then 'data' else 'binaryData']: {
346+
[favicon.key]: favicon.data,
347+
},
348+
},
204349
'10_console': kube._Object(versionGroup, 'Console', 'cluster') {
205350
spec+: consoleSpec,
206351
},

docs/modules/ROOT/pages/references/parameters.adoc

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ This allows users to remove links which were configured higher up in the hierarc
121121
type:: dictionary
122122
default:: `{}`
123123

124+
[NOTE]
125+
====
126+
This parameter is deprecated on OpenShift 4.19 and newer.
127+
Use `custom_logos` and `custom_favicon` instead.
128+
====
129+
124130
Add a custom logo to the console.
125131
Takes a single key with the filename and the value is the base64 encoded logo.
126132
The logo can be a file in any common image format, including GIF, JPG, PNG, or SVG, and is constrained to a max-height of 60px
@@ -131,13 +137,110 @@ The filename needs to have a filename extension which matches the image format.
131137
For SVG logos the file must *not* be base64 encoded, but inserted directly as a string.
132138
====
133139

134-
By default, OCP won't serve a favicon if a custom logo is configured for the console.
140+
By default, OpenShift won't serve a favicon if a custom logo is configured for the console.
135141
This is an intentional design decision as documented in this https://bugzilla.redhat.com/show_bug.cgi?id=1844883#c1[bug report].
136142

137143
The component tries to ensure that a favicon is served even if a custom logo is configured.
138144
However, because the current workaround for the missing favicon requires an additional custom route for the console hostname, it can only be implemented for configurations which use a custom console hostname.
139145
Otherwise, the component is unable to correctly configure `spec.hostname` for the console.
140146

147+
== `custom_logos`
148+
149+
[horizontal]
150+
type:: dictionary
151+
default:: https://github.com/appuio/component-openshift4-console/blob/master/class/defaults.yml[See `class/defaults.yml`]
152+
153+
Configure custom logos for the console.
154+
This parameter only has an effect on OpenShift 4.19 and newer.
155+
For older versions of OpenShift, use parameter `custom_logo`.
156+
157+
OpenShift 4.19 introduces a new console look with a dark and light theme.
158+
The new custom logo mechanism allows configuring separate logos for each theme.
159+
The component supports keys `dark` and `light` to specify logos for the dark and light theme respectively.
160+
The component additionally supports specifying a single logo for all themes in key `'*'`.
161+
If there's no logo for a theme, the console will show the default logo for that theme.
162+
163+
Each logo can be a file in any common image format, including GIF, JPG, PNG, or SVG, and is constrained to a max-height of 60px.
164+
165+
The component looks for fields `type` and `data` in each entry of the parameter.
166+
Field `type` is expected to be a valid file extension in all lower case matching the image data provided in field `data`.
167+
Field data is the logo image data, in plain text for SVG and base64 encoded for all other formats.
168+
169+
The component will render all the logos in a single ConfigMap.
170+
The component will dynamically add logos to fields `data` (for SVG) and `binaryData` (for other formats) in the ConfigMap.
171+
172+
Additionally, the component will configure an entry in `spec.customization.logos` in the `console.operator.openshift.io` custom resource to actually configure the custom logos.
173+
174+
=== Examples
175+
176+
.Separate logo per theme
177+
[source,yaml]
178+
----
179+
custom_logos:
180+
dark:
181+
type: svg
182+
data: |
183+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
184+
<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60">
185+
<circle cx="30" cy="30" r="20" stroke="#fff" stroke-width="4" fill-opacity="0%"/>
186+
</svg>
187+
light:
188+
type: svg
189+
data: |
190+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
191+
<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60">
192+
<circle cx="30" cy="30" r="20" stroke="#000" stroke-width="4" fill-opacity="0%"/>
193+
</svg>
194+
----
195+
196+
.Same logo for all themes
197+
[source,yaml]
198+
----
199+
custom_logos:
200+
'*':
201+
type: svg
202+
data: |
203+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
204+
<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60">
205+
<circle cx="30" cy="30" r="20" stroke="#f00" stroke-width="4" fill-opacity="0%"/>
206+
</svg>
207+
----
208+
209+
.Different file types
210+
[source,yaml]
211+
----
212+
custom_logos:
213+
dark:
214+
type: svg
215+
data: | <1>
216+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
217+
<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60">
218+
<circle cx="30" cy="30" r="20" stroke="#fff" stroke-width="4" fill-opacity="0%"/>
219+
</svg>
220+
light:
221+
type: png
222+
data: iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAABHNCSVQICAgIfAhkiAAAA5dJREFUaIHtmk1vTUEYx39HgkTYtMTOQtVL2WiKoF5W1IpGmrTpUlLxMUTDF6B8AKp8gWqxkHbtLUQixZbqQlVvScjf4pxK89y59869Z+Zei/NPZjE39zz/35Mz85w5MwcKFSpUqFChQq1SEjO4YDNwAjgJHAB2A9tIfwf4AXwB5oA3wDNgJoHlmFxBJUgE5wQPBSsC1dlWBBOCPkW+IbklGBC8biDJSu2V4GKr8yqToFPwJGCitk0LdrU6TwAEw4KlKrBzgluCIcFBQZtgfdbast+GBGOCD1XifBcMtTLRRHC9AtxvwT3B0QZiHhOMZzFcsUebPrczsNsVgB4prcZ5PfZkQ9nlMdbUpAU3HBDLgksRvEYEJYfftdBelQCGHeafBd0RPXsE8w7fwVieq8adjgL1RbAnqjH/hrhNelHQEdPUPnqWY95Zh3+PY3hPxTIbcAyp4HPWg2PEwdEf2iRR+QpqOqhJfTyThuVl0KqtdG1sn7O5Hz05ePY5ntN9IQ0emuB3gwVvnOm+YbofKvBmlb/11LWCiiHBccNUEmwKEdgO57mmrnIqcyWCj4btTK3r1nnEPmX6UwmoMcxwyhhs4Txd6zqfhPeb/qwnUzM0Y/qWtUw+Cdtq/M4bJ74sS/4nh2DBzJO23EEDSbDVsH0NEfSXCbo+AGsQCTYatp+1rvEZ0lYtr9B55JPwD9PfEgOkQVmWpVoX+CS8YPo7vHHiy7JY1jL5JPze9Pd648RXl+lb1jL5JPzG9E9448RXr+lb1vql9ARgbSX88B8tLT/Vu7T0Cex6eTgWgDkvV69j9yX/y0MW/IEJfi9I4HxME4ZpPGRwO6x/qwkbd1V4uhwbAGdDGiSC5//RFs9Tw/IieF0RXDQmEowENfHjuOLguBDLzB59lAQ9Uczc/ocdBXQypuEupad4aw3nmzGfBXtVvhH/TbAztvGQY0jNCw5F9DziSFaCgVieFmDUYb4iuBzB64pjGEtwNbRXNYhE6ZGlhZDgsWBfAI8uRzVebTdD5FEvUFLhTq8+p8eVbqV6Py6ymL3ZouJPhdjNu7MVIAcdhWxt+yi4o/SYtVvQLtiQtfbst+HsP3ZtvLYtNm3O1pKgQzBVBTZvm4xejRuRoF/pwVaoRF8Izrc6r6rK5mGf0rMf16cKtVopm/9n65n/vor96eEm0pf0U6Sb5LuB7ZR/evgeeEv66eFsAqWYXIUKFSpUqFCh1ugvn+zZeVm8jdkAAAAASUVORK5CYII= <2>
223+
----
224+
<1> SVG needs to be configured as plain text
225+
<2> All other formats, such as PNG need to be configured base64 encoded
226+
227+
== `custom_favicon`
228+
229+
[horizontal]
230+
type:: dictionary
231+
default:: `{}`
232+
233+
Starting with OpenShift 4.19, users can configure custom favicons for the console.
234+
This parameter has no effect on OpenShift versions before 4.19.
235+
236+
OpenShift 4.19 supports specifying separate favicons for the dark and light themes, but the component currently only supports configuring a single favicon for both themes.
237+
238+
The component looks for fields `type` and `data` in the parameter.
239+
Field `type` is expected to be a valid file extension in all lower case matching the image data provided in field `data`.
240+
Field data is the favicon data, in plain text for SVG and base64 encoded for all other formats.
241+
242+
The component will write the favicon into a ConfigMap and configure an entry in `spec.customization.logos` in the `console.operator.openshift.io` custom resource to enable the custom favicon for the console.
243+
141244
== `secrets`
142245

143246
[horizontal]

tests/custom-logo.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@ parameters:
1818
name: console-company-cloud-tls
1919
customization:
2020
customProductName: Company Cloud
21+
custom_logos:
22+
'*':
23+
type: svg
24+
data: |-
25+
<SVG HERE>
26+
light:
27+
type: svg
28+
data: |-
29+
<SVG HERE>
30+
custom_favicon:
31+
type: png
32+
data: <PNG|base64 HERE>
2133
custom_logo:
2234
logo.png: |-
2335
V2FpdCB0aGlzIGlzbid0IGEgcGljdHVyZS4uCgpMb3JlbSBpcHN1bSBkb2xvciBzaXQgYW1ldC4g
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
apiVersion: v1
2+
binaryData:
3+
favicon.png: <PNG|base64 HERE>
4+
data: {}
5+
kind: ConfigMap
6+
metadata:
7+
annotations:
8+
argocd.argoproj.io/compare-options: IgnoreExtraneous
9+
argocd.argoproj.io/sync-options: Prune=false
10+
labels:
11+
name: console-favicon
12+
name: console-favicon
13+
namespace: openshift-config

0 commit comments

Comments
 (0)