Skip to content

Commit

Permalink
Allow disabling the dashboard (#639)
Browse files Browse the repository at this point in the history
  • Loading branch information
sudermanjr authored Aug 29, 2023
1 parent fe23cc5 commit 8eb60d1
Show file tree
Hide file tree
Showing 11 changed files with 88 additions and 62 deletions.
3 changes: 3 additions & 0 deletions cmd/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ var (
showAllVPAs bool
basePath string
insightsHost string
enableCost bool
)

func init() {
Expand All @@ -40,6 +41,7 @@ func init() {
dashboardCmd.PersistentFlags().BoolVar(&onByDefault, "on-by-default", false, "Display every namespace that isn't explicitly excluded.")
dashboardCmd.PersistentFlags().BoolVar(&showAllVPAs, "show-all", false, "Display every VPA, even if it isn't managed by Goldilocks")
dashboardCmd.PersistentFlags().StringVar(&basePath, "base-path", "/", "Path on which the dashboard is served.")
dashboardCmd.PersistentFlags().BoolVar(&enableCost, "enable-cost", true, "If set to false, the cost integration will be disabled on the dashboard.")
dashboardCmd.PersistentFlags().StringVar(&insightsHost, "insights-host", "https://insights.fairwinds.com", "Insights host for retrieving optional cost data.")
}

Expand All @@ -56,6 +58,7 @@ var dashboardCmd = &cobra.Command{
dashboard.OnByDefault(onByDefault),
dashboard.ShowAllVPAs(showAllVPAs),
dashboard.InsightsHost(insightsHost),
dashboard.EnableCost(enableCost),
)
http.Handle("/", router)
klog.Infof("Starting goldilocks dashboard server on port %d and basePath %v", serverPort, validBasePath)
Expand Down
14 changes: 7 additions & 7 deletions pkg/dashboard/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ func Dashboard(opts Options) http.Handler {
}

data := struct {
VpaData summary.Summary
InsightsHost string
VpaData summary.Summary
Opts Options
}{
VpaData: vpaData,
InsightsHost: opts.insightsHost,
VpaData: vpaData,
Opts: opts,
}

writeTemplate(tmpl, opts, &data, w)
Expand Down Expand Up @@ -114,14 +114,14 @@ func API(opts Options) http.Handler {
func getVPAData(opts Options, namespace, costPerCPU, costPerGB string) (summary.Summary, error) {

filterLabels := make(map[string]string)
if !opts.showAllVPAs {
filterLabels = opts.vpaLabels
if !opts.ShowAllVPAs {
filterLabels = opts.VpaLabels
}

summarizer := summary.NewSummarizer(
summary.ForNamespace(namespace),
summary.ForVPAsWithLabels(filterLabels),
summary.ExcludeContainers(opts.excludedContainers),
summary.ExcludeContainers(opts.ExcludedContainers),
)

vpaData, err := summarizer.GetSummary()
Expand Down
16 changes: 11 additions & 5 deletions pkg/dashboard/namespace-list.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
func NamespaceList(opts Options) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var listOptions v1.ListOptions
if opts.onByDefault || opts.showAllVPAs {
if opts.OnByDefault || opts.ShowAllVPAs {
listOptions = v1.ListOptions{
LabelSelector: fmt.Sprintf("%s!=false", utils.VpaEnabledLabel),
}
Expand Down Expand Up @@ -48,17 +48,23 @@ func NamespaceList(opts Options) http.Handler {
// this helps to not leak additional information like
// annotations, labels, metadata about the Namespace to the
// client UI source code or javascript console
data := []struct {
Name string
}{}

data := struct {
Namespaces []struct {
Name string
}
Opts Options
}{
Opts: opts,
}

for _, ns := range namespacesList.Items {
item := struct {
Name string
}{
Name: ns.Name,
}
data = append(data, item)
data.Namespaces = append(data.Namespaces, item)
}

writeTemplate(tmpl, opts, &data, w)
Expand Down
48 changes: 28 additions & 20 deletions pkg/dashboard/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,69 +10,77 @@ type Option func(*Options)

// Options are options for getting and caching the Summarizer's VPAs
type Options struct {
port int
basePath string
vpaLabels map[string]string
excludedContainers sets.Set[string]
onByDefault bool
showAllVPAs bool
insightsHost string
Port int
BasePath string
VpaLabels map[string]string
ExcludedContainers sets.Set[string]
OnByDefault bool
ShowAllVPAs bool
InsightsHost string
EnableCost bool
}

// default options for the dashboard
func defaultOptions() *Options {
return &Options{
port: 8080,
basePath: "/",
vpaLabels: utils.VPALabels,
excludedContainers: sets.Set[string]{},
onByDefault: false,
showAllVPAs: false,
Port: 8080,
BasePath: "/",
VpaLabels: utils.VPALabels,
ExcludedContainers: sets.Set[string]{},
OnByDefault: false,
ShowAllVPAs: false,
EnableCost: true,
}
}

// OnPort is an Option for running the dashboard on a different port
func OnPort(port int) Option {
return func(opts *Options) {
opts.port = port
opts.Port = port
}
}

// ExcludeContainers is an Option for excluding containers in the dashboard summary
func ExcludeContainers(excludedContainers sets.Set[string]) Option {
return func(opts *Options) {
opts.excludedContainers = excludedContainers
opts.ExcludedContainers = excludedContainers
}
}

// ForVPAsWithLabels Option for limiting the dashboard to certain VPAs matching the labels
func ForVPAsWithLabels(vpaLabels map[string]string) Option {
return func(opts *Options) {
opts.vpaLabels = vpaLabels
opts.VpaLabels = vpaLabels
}
}

// OnByDefault is an option for listing all namespaces in the dashboard unless explicitly excluded
func OnByDefault(onByDefault bool) Option {
return func(opts *Options) {
opts.onByDefault = onByDefault
opts.OnByDefault = onByDefault
}
}

func ShowAllVPAs(showAllVPAs bool) Option {
return func(opts *Options) {
opts.showAllVPAs = showAllVPAs
opts.ShowAllVPAs = showAllVPAs
}
}

func BasePath(basePath string) Option {
return func(opts *Options) {
opts.basePath = basePath
opts.BasePath = basePath
}
}

func InsightsHost(insightsHost string) Option {
return func(opts *Options) {
opts.insightsHost = insightsHost
opts.InsightsHost = insightsHost
}
}

func EnableCost(enableCost bool) Option {
return func(opts *Options) {
opts.EnableCost = enableCost
}
}
10 changes: 5 additions & 5 deletions pkg/dashboard/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func GetRouter(setters ...Option) *mux.Router {
setter(opts)
}

router := mux.NewRouter().PathPrefix(opts.basePath).Subrouter()
router := mux.NewRouter().PathPrefix(opts.BasePath).Subrouter()

// health
router.Handle("/health", Health("OK"))
Expand All @@ -59,7 +59,7 @@ func GetRouter(setters ...Option) *mux.Router {
// assets
router.Handle("/favicon.ico", Asset("/images/favicon-32x32.png"))
fileServer := http.FileServer(GetAssetBox())
router.PathPrefix("/static/").Handler(http.StripPrefix(path.Join(opts.basePath, "/static/"), fileServer))
router.PathPrefix("/static/").Handler(http.StripPrefix(path.Join(opts.BasePath, "/static/"), fileServer))

// dashboard
router.Handle("/dashboard", Dashboard(*opts))
Expand All @@ -71,15 +71,15 @@ func GetRouter(setters ...Option) *mux.Router {
// root
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// catch all other paths that weren't matched
if r.URL.Path != "/" && r.URL.Path != opts.basePath && r.URL.Path != opts.basePath+"/" {
if r.URL.Path != "/" && r.URL.Path != opts.BasePath && r.URL.Path != opts.BasePath+"/" {
klog.Infof("404: %s", r.URL.Path)
http.NotFound(w, r)
return
}

klog.Infof("redirecting to %v", path.Join(opts.basePath, "/namespaces"))
klog.Infof("redirecting to %v", path.Join(opts.BasePath, "/namespaces"))
// default redirect on root path
http.Redirect(w, r, path.Join(opts.basePath, "/namespaces"), http.StatusMovedPermanently)
http.Redirect(w, r, path.Join(opts.BasePath, "/namespaces"), http.StatusMovedPermanently)
})

// api
Expand Down
2 changes: 1 addition & 1 deletion pkg/dashboard/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func writeTemplate(tmpl *template.Template, opts Options, data interface{}, w ht
return
}
err = tmpl.Execute(buf, baseTemplateData{
BasePath: validateBasePath(opts.basePath),
BasePath: validateBasePath(opts.BasePath),
Data: data,
JSON: template.JS(jsonData),
})
Expand Down
12 changes: 9 additions & 3 deletions pkg/dashboard/templates/dashboard.gohtml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
{{ template "head" . }}
{{ template "head" .Data }}

{{ if gt (len .Data.VpaData.Namespaces) 1 }}
<script src="static/js/filter.js" type="module"></script>
{{ end }}


<noscript>
<style>
Expand All @@ -16,12 +16,15 @@
</noscript>

<script>
window.INSIGHTS_HOST = "{{ .Data.InsightsHost }}"
window.INSIGHTS_HOST = "{{ .Data.Opts.InsightsHost }}"
</script>
{{ end }}

{{- if .Data.Opts.EnableCost }}
<script defer src="static/js/email.js"></script>
<script defer src="static/js/api-token.js"></script>
<script defer src="static/js/cost_settings.js"></script>
{{- end }}
</head>

<body class="layoutSidebar">
Expand All @@ -31,9 +34,12 @@

<div class="layoutSidebar__main">
<main class="verticalRhythm --rhythm-3">

{{- if .Data.Opts.EnableCost }}
{{ template "email" . }}
{{ template "api_token" . }}
{{ template "cost_settings" . }}
{{- end }}

<h1>Namespace Details</h1>

Expand Down
4 changes: 2 additions & 2 deletions pkg/dashboard/templates/filter.gohtml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{{ define "filter" }}
<noscript>
<p class="controls">{{ len . }} namespaces found</p>
<p class="controls">{{ len .Namespaces }} namespaces found</p>
</noscript>

<form
Expand Down Expand Up @@ -31,7 +31,7 @@
3. An output that's always shown visually. We have to remove content from the above two to get them to announce the same content back to back. This would make the words pop in and out visually.
Note that the elements used for dynamic screen reader announcements can't themselves be injected or they won't work. Must be pre-existing containers into which the announcment content is injected.
-->
{{ with $initialStatus := (len .) | printf "%d total namespaces found" }}
{{ with $initialStatus := (len .Namespaces) | printf "%d total namespaces found" }}
<div class="control-block">
<output aria-hidden="true">{{ $initialStatus }}</output>
<output class="visually-hidden" aria-live="polite">{{ $initialStatus }}</output>
Expand Down
7 changes: 5 additions & 2 deletions pkg/dashboard/templates/head.gohtml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{{ define "head" }}
<base href="{{ .BasePath }}">
<base href="{{ .Opts.BasePath }}">
<meta charset="utf-8" />
<title>Goldilocks by Fairwinds</title>
<meta
Expand All @@ -17,8 +17,11 @@
<link rel="stylesheet" href="static/css/utopia.css" />
<link rel="stylesheet" href="static/css/main.css" />
<link rel="stylesheet" href="static/css/fontawesome-5.7.2.css" />
<link rel="stylesheet" href="static/css/prism.css" />

{{- if .Opts.EnableCost }}
<link rel="stylesheet" href="static/css/prism.css" />
<script src="static/js/main.js" type="module"></script>
<script defer src="static/js/prism.js"></script>
{{- end }}

{{ end }}
10 changes: 5 additions & 5 deletions pkg/dashboard/templates/namespace_list.gohtml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
{{ template "head" . }}
{{ template "head" .Data }}

{{ if gt (len .Data) 1 }}
{{ if gt (len .Data.Namespaces ) 1 }}
<script src="static/js/filter.js" type="module"></script>
{{ end }}
</head>
Expand All @@ -17,16 +17,16 @@
<main class="verticalRhythm">
<h1>Namespaces</h1>

{{ if lt (len .Data) 1 }}
{{ if lt (len .Data.Namespaces) 1 }}
<p>No namespaces are labelled for use by Goldilocks. Try labelling one with <code class="language-shell">kubectl label ns NAMESPACE_NAME goldilocks.fairwinds.com/enabled=true</code></p>

{{ else }}
{{ if gt (len .Data) 1 }}
{{ if gt (len .Data.Namespaces) 1 }}
{{ template "filter" .Data }}
{{ end }}

<ul aria-live="off" class="namespaceList" id="js-filter-container" role="list">
{{ range .Data }}
{{ range .Data.Namespaces }}
<li data-filter="{{ .Name }}">
<a class="buttonLink --withIcon" href="{{ $.BasePath }}dashboard/{{ .Name }}">
{{ .Name }}
Expand Down
24 changes: 12 additions & 12 deletions pkg/summary/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,25 @@ type Option func(*options)

// options for getting and caching the Summarizer's VPAs
type options struct {
kubeClient *kube.ClientInstance
vpaClient *kube.VPAClientInstance
dynamicClient *kube.DynamicClientInstance
kubeClient *kube.ClientInstance
vpaClient *kube.VPAClientInstance
dynamicClient *kube.DynamicClientInstance
controllerUtilsClient *kube.ControllerUtilsClientInstance
namespace string
vpaLabels map[string]string
excludedContainers sets.Set[string]
namespace string
vpaLabels map[string]string
excludedContainers sets.Set[string]
}

// defaultOptions for a Summarizer
func defaultOptions() *options {
return &options{
kubeClient: kube.GetInstance(),
vpaClient: kube.GetVPAInstance(),
dynamicClient: kube.GetDynamicInstance(),
kubeClient: kube.GetInstance(),
vpaClient: kube.GetVPAInstance(),
dynamicClient: kube.GetDynamicInstance(),
controllerUtilsClient: kube.GetControllerUtilsInstance(),
namespace: namespaceAllNamespaces,
vpaLabels: utils.VPALabels,
excludedContainers: sets.Set[string]{},
namespace: namespaceAllNamespaces,
vpaLabels: utils.VPALabels,
excludedContainers: sets.Set[string]{},
}
}

Expand Down

0 comments on commit 8eb60d1

Please sign in to comment.