Skip to content

Commit ad07686

Browse files
committed
Support footer: false to hide footer (fixes #28)
Implements support for setting footer: false in Dashboard CRD to hide the footer content, matching Homer's native behavior. Changes: - Add FooterHidden constant to represent hidden footer state - Add custom UnmarshalYAML/UnmarshalJSON to handle footer: false input - Update YAML generation to output footer: false as boolean - Add comprehensive unit tests for footer handling - Add e2e test verifying footer: false behavior - Update config.yml documentation When users set footer: false in their Dashboard spec, the operator now correctly generates footer: false in the Homer config YAML, which Homer parses as a boolean and uses in v-if="config.footer" to hide the footer content. Closes #28
1 parent f61a096 commit ad07686

File tree

4 files changed

+214
-2
lines changed

4 files changed

+214
-2
lines changed

homer/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ header: true # Set to false to hide the header
1717
# Optional: Different hotkey for search, defaults to "/"
1818
# hotkey:
1919
# search: "Shift"
20-
footer: '<p>Homer-Operator</p>' # set false if you want to hide it.
20+
footer: '<p>Homer-Operator</p>' # Set to false to hide the footer: footer: false
2121

2222
columns: "3" # "auto" or number (must be a factor of 12: 1, 2, 3, 4, 6, 12)
2323
connectivityCheck:

pkg/homer/config.go

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const (
4545
DangerValueField = "danger_value"
4646
BooleanTrue = "true"
4747
BooleanFalse = "false"
48+
FooterHidden = "__FOOTER_HIDDEN__"
4849
NamespaceIconURL = "https://raw.githubusercontent.com/kubernetes/community/master/icons/png/" +
4950
"resources/labeled/ns-128.png"
5051
IngressIconURL = "https://raw.githubusercontent.com/kubernetes/community/master/icons/png/" +
@@ -93,6 +94,52 @@ type HomerConfig struct {
9394
ExternalConfig string `json:"externalConfig,omitempty" yaml:"externalConfig,omitempty"`
9495
}
9596

97+
// UnmarshalYAML custom unmarshaler to handle footer: false
98+
func (c *HomerConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
99+
type Alias HomerConfig
100+
aux := &struct {
101+
Footer interface{} `yaml:"footer,omitempty"`
102+
*Alias
103+
}{
104+
Alias: (*Alias)(c),
105+
}
106+
if err := unmarshal(aux); err != nil {
107+
return err
108+
}
109+
switch v := aux.Footer.(type) {
110+
case bool:
111+
if !v {
112+
c.Footer = FooterHidden
113+
}
114+
case string:
115+
c.Footer = v
116+
}
117+
return nil
118+
}
119+
120+
// UnmarshalJSON custom unmarshaler to handle footer: false
121+
func (c *HomerConfig) UnmarshalJSON(data []byte) error {
122+
type Alias HomerConfig
123+
aux := &struct {
124+
Footer interface{} `json:"footer,omitempty"`
125+
*Alias
126+
}{
127+
Alias: (*Alias)(c),
128+
}
129+
if err := json.Unmarshal(data, aux); err != nil {
130+
return err
131+
}
132+
switch v := aux.Footer.(type) {
133+
case bool:
134+
if !v {
135+
c.Footer = FooterHidden
136+
}
137+
case string:
138+
c.Footer = v
139+
}
140+
return nil
141+
}
142+
96143
// ProxyConfig contains configuration for proxy settings.
97144
type ProxyConfig struct {
98145
UseCredentials bool `json:"useCredentials,omitempty"`
@@ -2134,7 +2181,11 @@ func addBasicFields(configMap map[string]interface{}, config *HomerConfig) {
21342181
}
21352182
configMap["header"] = config.Header
21362183
if config.Footer != "" {
2137-
configMap["footer"] = config.Footer
2184+
if config.Footer == FooterHidden {
2185+
configMap["footer"] = false
2186+
} else {
2187+
configMap["footer"] = config.Footer
2188+
}
21382189
}
21392190
if config.Columns != "" {
21402191
configMap["columns"] = config.Columns

pkg/homer/footer_test.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package homer
2+
3+
import (
4+
"encoding/json"
5+
"strings"
6+
"testing"
7+
8+
yaml "gopkg.in/yaml.v2"
9+
)
10+
11+
func TestFooterFalseHandling(t *testing.T) {
12+
t.Run("YAML unmarshal footer: false", func(t *testing.T) {
13+
yamlData := `
14+
title: "Test Dashboard"
15+
footer: false
16+
`
17+
var config HomerConfig
18+
err := yaml.Unmarshal([]byte(yamlData), &config)
19+
if err != nil {
20+
t.Fatalf("Failed to unmarshal YAML: %v", err)
21+
}
22+
23+
if config.Footer != FooterHidden {
24+
t.Errorf("Expected Footer to be %q, got %q", FooterHidden, config.Footer)
25+
}
26+
})
27+
28+
t.Run("YAML unmarshal footer: string", func(t *testing.T) {
29+
yamlData := `
30+
title: "Test Dashboard"
31+
footer: "<p>Custom Footer</p>"
32+
`
33+
var config HomerConfig
34+
err := yaml.Unmarshal([]byte(yamlData), &config)
35+
if err != nil {
36+
t.Fatalf("Failed to unmarshal YAML: %v", err)
37+
}
38+
39+
expected := "<p>Custom Footer</p>"
40+
if config.Footer != expected {
41+
t.Errorf("Expected Footer to be %q, got %q", expected, config.Footer)
42+
}
43+
})
44+
45+
t.Run("JSON unmarshal footer: false", func(t *testing.T) {
46+
jsonData := `{
47+
"title": "Test Dashboard",
48+
"footer": false
49+
}`
50+
var config HomerConfig
51+
err := json.Unmarshal([]byte(jsonData), &config)
52+
if err != nil {
53+
t.Fatalf("Failed to unmarshal JSON: %v", err)
54+
}
55+
56+
if config.Footer != FooterHidden {
57+
t.Errorf("Expected Footer to be %q, got %q", FooterHidden, config.Footer)
58+
}
59+
})
60+
61+
t.Run("JSON unmarshal footer: string", func(t *testing.T) {
62+
jsonData := `{
63+
"title": "Test Dashboard",
64+
"footer": "<p>Custom Footer</p>"
65+
}`
66+
var config HomerConfig
67+
err := json.Unmarshal([]byte(jsonData), &config)
68+
if err != nil {
69+
t.Fatalf("Failed to unmarshal JSON: %v", err)
70+
}
71+
72+
expected := "<p>Custom Footer</p>"
73+
if config.Footer != expected {
74+
t.Errorf("Expected Footer to be %q, got %q", expected, config.Footer)
75+
}
76+
})
77+
78+
t.Run("YAML marshal footer: false", func(t *testing.T) {
79+
config := &HomerConfig{
80+
Title: "Test Dashboard",
81+
Footer: FooterHidden,
82+
}
83+
84+
yamlBytes, err := marshalHomerConfigToYAML(config)
85+
if err != nil {
86+
t.Fatalf("Failed to generate Homer YAML: %v", err)
87+
}
88+
89+
yamlStr := string(yamlBytes)
90+
if !strings.Contains(yamlStr, "footer: false") {
91+
t.Errorf("Expected YAML to contain 'footer: false', got:\n%s", yamlStr)
92+
}
93+
})
94+
95+
t.Run("YAML marshal footer: string", func(t *testing.T) {
96+
config := &HomerConfig{
97+
Title: "Test Dashboard",
98+
Footer: "<p>Custom Footer</p>",
99+
}
100+
101+
yamlBytes, err := marshalHomerConfigToYAML(config)
102+
if err != nil {
103+
t.Fatalf("Failed to generate Homer YAML: %v", err)
104+
}
105+
106+
yamlStr := string(yamlBytes)
107+
if !strings.Contains(yamlStr, "footer: <p>Custom Footer</p>") {
108+
t.Errorf("Expected YAML to contain custom footer string, got:\n%s", yamlStr)
109+
}
110+
})
111+
112+
t.Run("YAML marshal footer: empty (omitted)", func(t *testing.T) {
113+
config := &HomerConfig{
114+
Title: "Test Dashboard",
115+
Footer: "",
116+
}
117+
118+
yamlBytes, err := marshalHomerConfigToYAML(config)
119+
if err != nil {
120+
t.Fatalf("Failed to generate Homer YAML: %v", err)
121+
}
122+
123+
yamlStr := string(yamlBytes)
124+
if strings.Contains(yamlStr, "footer:") {
125+
t.Errorf("Expected YAML to not contain footer field when empty, got:\n%s", yamlStr)
126+
}
127+
})
128+
}

test/e2e/e2e_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,39 @@ var _ = Describe("Homer Operator E2E Tests", func() {
244244
Expect(configMap.Data["config.yml"]).To(ContainSubstring("footer: Updated Footer"))
245245
})
246246

247+
It("should support footer: false to hide footer", func() {
248+
By("Creating Dashboard with footer: false")
249+
dashboard := &homerv1alpha1.Dashboard{
250+
ObjectMeta: metav1.ObjectMeta{
251+
Name: "e2e-footer-false-dashboard",
252+
Namespace: testNs,
253+
},
254+
Spec: homerv1alpha1.DashboardSpec{
255+
HomerConfig: homer.HomerConfig{
256+
Title: "Footer False Test",
257+
Subtitle: "Testing footer opt-out",
258+
Footer: homer.FooterHidden,
259+
},
260+
},
261+
}
262+
err := k8sClient.Create(ctx, dashboard)
263+
Expect(err).NotTo(HaveOccurred())
264+
265+
By("Waiting for ConfigMap to be created")
266+
configMap := &corev1.ConfigMap{}
267+
Eventually(func() bool {
268+
err := k8sClient.Get(ctx, types.NamespacedName{
269+
Name: "e2e-footer-false-dashboard-homer",
270+
Namespace: testNs,
271+
}, configMap)
272+
return err == nil
273+
}, time.Minute*2, time.Second*5).Should(BeTrue())
274+
275+
By("Verifying footer: false is in configuration")
276+
Expect(configMap.Data["config.yml"]).To(ContainSubstring("title: Footer False Test"))
277+
Expect(configMap.Data["config.yml"]).To(ContainSubstring("footer: false"))
278+
})
279+
247280
It("should clean up resources when Dashboard is deleted", func() {
248281
By("Creating Dashboard")
249282
dashboard := &homerv1alpha1.Dashboard{

0 commit comments

Comments
 (0)