Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Templates #72

Merged
merged 1 commit into from
May 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 46 additions & 22 deletions pkg/dashboard/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,16 @@ import (
)

const (
// TemplateName references the dashboard template to use
TemplateName = "dashboard.gohtml"
// MainTemplateName is the main template
MainTemplateName = "main.gohtml"
// HeaderTemplateName contains the navbar
HeaderTemplateName = "header.gohtml"
// PreambleTemplateName contains an empty preamble that can be overridden
PreambleTemplateName = "preamble.gohtml"
// DashboardTemplateName contains the content of the dashboard
DashboardTemplateName = "dashboard.gohtml"
// FooterTemplateName contains the footer
FooterTemplateName = "footer.gohtml"
)

var (
Expand Down Expand Up @@ -59,6 +67,40 @@ type TemplateData struct {
JSON template.JS
}

// GetBaseTemplate puts together the dashboard template. Individual pieces can be overridden before rendering.
func GetBaseTemplate(name string) (*template.Template, error) {
tmpl := template.New(name).Funcs(template.FuncMap{
"getWarningWidth": getWarningWidth,
"getSuccessWidth": getSuccessWidth,
"getWeatherIcon": getWeatherIcon,
"getWeatherText": getWeatherText,
"getGrade": getGrade,
"getScore": getScore,
"getIcon": getIcon,
})

templateBox := GetTemplateBox()
templateFileNames := []string{
DashboardTemplateName,
HeaderTemplateName,
PreambleTemplateName,
FooterTemplateName,
MainTemplateName,
}
for _, fname := range templateFileNames {
templateFile, err := templateBox.Find(fname)
if err != nil {
return nil, err
}

tmpl, err = tmpl.Parse(string(templateFile))
if err != nil {
return nil, err
}
}
return tmpl, nil
}

// MainHandler gets template data and renders the dashboard with it.
func MainHandler(w http.ResponseWriter, r *http.Request, auditData validator.AuditData) {
jsonData, err := json.Marshal(auditData)
Expand All @@ -72,33 +114,15 @@ func MainHandler(w http.ResponseWriter, r *http.Request, auditData validator.Aud
AuditData: auditData,
JSON: template.JS(jsonData),
}

templateBox := GetTemplateBox()
templateFile, err := templateBox.Find(TemplateName)

tmpl, err := GetBaseTemplate("main")
if err != nil {
logrus.Printf("Error getting template data %v", err)
http.Error(w, "Error getting template data", 500)
return
}

tmpl, err := template.New(TemplateName).Funcs(template.FuncMap{
"getWarningWidth": getWarningWidth,
"getSuccessWidth": getSuccessWidth,
"getWeatherIcon": getWeatherIcon,
"getWeatherText": getWeatherText,
"getGrade": getGrade,
"getScore": getScore,
"getIcon": getIcon,
}).Parse(string(templateFile))

if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

buf := &bytes.Buffer{}
err = template.Must(tmpl.Clone()).Execute(buf, templateData)
err = tmpl.ExecuteTemplate(buf, "main", templateData)

if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
Expand Down
244 changes: 99 additions & 145 deletions pkg/dashboard/templates/dashboard.gohtml
Original file line number Diff line number Diff line change
@@ -1,163 +1,117 @@
<!doctype html>
<html>

<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Fairwinds</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">

<link rel="icon" type="image/png" href="/static/favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="/static/favicon-16x16.png" sizes="16x16" />
<link href="https://fonts.googleapis.com/css?family=Muli:300,400,700" rel="stylesheet">
<link rel="stylesheet" href="/static/css/normalize.css">
<link rel="stylesheet" href="/static/css/main.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/cash/3.0.0-beta.3/cash.min.js"></script>
<script>
window.fairwindsAuditData = {{ .JSON }};
</script>
<script type="text/javascript" src="/static/js/main.js"></script>
</head>

<body>
<div class="header">
<div class="header-content">
<img class="logo" src="/static/images/polaris-logo.png" alt="Polaris" />
<div class="header-right">
<a href="https://reactiveops.com?source=fairwinds" target="_blank">
<span class="oss-text">Open Source Project By</span>
<img class="ro-logo" src="/static/images/ro-logo.png" alt="ReactiveOps" />
</a>
{{define "dashboard"}}
<div class="card cluster">
<h3>Cluster Overview</h3>
<div class="cluster-overview">
<div class="cluster-score">
<div class="weather"><i class="fas {{ getWeatherIcon .AuditData.ClusterSummary.Results }}"></i></div>
<div class="sailing">{{ getWeatherText .AuditData.ClusterSummary.Results }}</div>
<div class="scores">Grade: <strong>{{ getGrade .AuditData.ClusterSummary.Results }}</strong> | Score: <strong>{{ getScore .AuditData.ClusterSummary.Results }}%</strong></div>
</div>
</div>
</div>

<div class="dashboard-content">
<div class="card cluster">
<h3>Cluster Overview</h3>
<div class="cluster-overview">
<div class="cluster-score">
<div class="weather"><i class="fas {{ getWeatherIcon .AuditData.ClusterSummary.Results }}"></i></div>
<div class="sailing">{{ getWeatherText .AuditData.ClusterSummary.Results }}</div>
<div class="scores">Grade: <strong>{{ getGrade .AuditData.ClusterSummary.Results }}</strong> | Score: <strong>{{ getScore .AuditData.ClusterSummary.Results }}%</strong></div>
</div>
<div class="result-messages">
<ul class="message-list">
<li class="success"><i class="fas fa-check"></i> {{ .AuditData.ClusterSummary.Results.Totals.Successes }} checks passed</li>
<li class="warning"><i class="fas fa-exclamation"></i> {{ .AuditData.ClusterSummary.Results.Totals.Warnings }} checks had warnings</li>
<li class="error"><i class="fas fa-times"></i> {{ .AuditData.ClusterSummary.Results.Totals.Errors }} checks had errors</li>
</ul>
</div>
<canvas id="clusterScoreChart"></canvas>
<div class="result-messages">
<ul class="message-list">
<li class="success"><i class="fas fa-check"></i> {{ .AuditData.ClusterSummary.Results.Totals.Successes }} checks passed</li>
<li class="warning"><i class="fas fa-exclamation"></i> {{ .AuditData.ClusterSummary.Results.Totals.Warnings }} checks had warnings</li>
<li class="error"><i class="fas fa-times"></i> {{ .AuditData.ClusterSummary.Results.Totals.Errors }} checks had errors</li>
</ul>
</div>
<table class="expandable-table" cellspacing="0">
<tr>
<td class="resource-info">
<div class="name"><span class="caret-expander"></span>Cluster details</div>
<div class="expandable-content">
<ul class="message-list">
<li>
<span class="detail-label">Kubernetes Version:</span>
<span class="detail-value">{{ .AuditData.ClusterSummary.Version }}</span>
</li>
<li>
<span class="detail-label">Nodes:</span>
<span class="detail-value">{{ .AuditData.ClusterSummary.Nodes }}</span>
</li>
<li>
<span class="detail-label">Pods:</span>
<span class="detail-value">{{ .AuditData.ClusterSummary.Pods }}</span>
</li>
<canvas id="clusterScoreChart"></canvas>
</div>
<table class="expandable-table" cellspacing="0">
<tr>
<td class="resource-info">
<div class="name"><span class="caret-expander"></span>Cluster details</div>
<div class="expandable-content">
<ul class="message-list">
<li>
<span class="detail-label">Kubernetes Version:</span>
<span class="detail-value">{{ .AuditData.ClusterSummary.Version }}</span>
</li>
<li>
<span class="detail-label">Nodes:</span>
<span class="detail-value">{{ .AuditData.ClusterSummary.Nodes }}</span>
</li>
<li>
<span class="detail-label">Pods:</span>
<span class="detail-value">{{ .AuditData.ClusterSummary.Pods }}</span>
</li>
<li>
<span class="detail-label">Namespaces:</span>
<span class="detail-value">{{ .AuditData.ClusterSummary.Namespaces }}</span>
</li>
</ul>
</div>
</td>
</tr>
<tr>
<td class="resource-info">
<div class="name"><span class="caret-expander"></span>Health summary</div>
<div class="expandable-content">
<ul class="message-list">
{{ range $category, $summary := .AuditData.ClusterSummary.Results.ByCategory }}
<li>
<span class="detail-label">Namespaces:</span>
<span class="detail-value">{{ .AuditData.ClusterSummary.Namespaces }}</span>
</li>
</ul>
</div>
</td>
</tr>
<tr>
<td class="resource-info">
<div class="name"><span class="caret-expander"></span>Health summary</div>
<div class="expandable-content">
<ul class="message-list">
{{ range $category, $summary := .AuditData.ClusterSummary.Results.ByCategory }}
<li>
<span class="detail-label">{{ $category }}</span>
<span class="detail-value">{{ $summary.Errors }} errors, {{ $summary.Warnings }} warnings</span>
<div class="status-bar">
<div class="status">
<div class="failing">
<div class="warning" style="width: {{ getWarningWidth $summary 280 }}px;">
<div class="passing" style="width: {{ getSuccessWidth $summary 280 }}px;"></div>
</div>
<span class="detail-label">{{ $category }}</span>
<span class="detail-value">{{ $summary.Errors }} errors, {{ $summary.Warnings }} warnings</span>
<div class="status-bar">
<div class="status">
<div class="failing">
<div class="warning" style="width: {{ getWarningWidth $summary 280 }}px;">
<div class="passing" style="width: {{ getSuccessWidth $summary 280 }}px;"></div>
</div>
</div>
</div>
</li>
{{ end }}
</ul>
</div>
</td>
</tr>
</table>
</div>
</li>
{{ end }}
</ul>
</div>
</td>
</tr>
</table>

</div>
</div>

{{ range $namespace, $nsResult := .AuditData.NamespacedResults }}
<div class="card namespace">
<h3>Namespace: <strong>{{ $namespace }}</strong></h3>
<table class="expandable-table" cellspacing="0">
{{ range .DeploymentResults }}
<tr>
<td class="resource-info">
<div class="name"><span class="caret-expander"></span>Deployment: <strong>{{ .Name }}</strong></div>
{{ range $namespace, $nsResult := .AuditData.NamespacedResults }}
<div class="card namespace">
<h3>Namespace: <strong>{{ $namespace }}</strong></h3>
<table class="expandable-table" cellspacing="0">
{{ range .DeploymentResults }}
<tr>
<td class="resource-info">
<div class="name"><span class="caret-expander"></span>Deployment: <strong>{{ .Name }}</strong></div>
<div class="result-messages expandable-content">
<h4>Pod Spec:</h4>
<ul class="message-list">
{{ range $message := .PodResult.Messages}}
<li class="{{ .Type }}"><i class="{{ getIcon $message }}"></i> {{ .Message }}</li>
{{ end }}
</ul>
</div>
{{ range .PodResult.ContainerResults}}
<div class="result-messages expandable-content">
<h4>Pod Spec:</h4>
<h4>Container: {{ .Name }}</h4>
<ul class="message-list">
{{ range $message := .PodResult.Messages}}
{{ range $message := .Messages}}
<li class="{{ .Type }}"><i class="{{ getIcon $message }}"></i> {{ .Message }}</li>
{{ end }}
</ul>
</div>
{{ range .PodResult.ContainerResults}}
<div class="result-messages expandable-content">
<h4>Container: {{ .Name }}</h4>
<ul class="message-list">
{{ range $message := .Messages}}
<li class="{{ .Type }}"><i class="{{ getIcon $message }}"></i> {{ .Message }}</li>
{{ end }}
</ul>
</div>
{{ end }} {{/* end range .PodResult.ContainerResults */}}
</div>
</td>
<td class="status-bar">
<div class="status">
<div class="failing">
<div class="warning" style="width: {{ getWarningWidth .PodResult.Summary.Totals 200 }}px;">
<div class="passing" style="width: {{ getSuccessWidth .PodResult.Summary.Totals 200 }}px;"></div>
</div>
{{ end }} {{/* end range .PodResult.ContainerResults */}}
</div>
</td>
<td class="status-bar">
<div class="status">
<div class="failing">
<div class="warning" style="width: {{ getWarningWidth .PodResult.Summary.Totals 200 }}px;">
<div class="passing" style="width: {{ getSuccessWidth .PodResult.Summary.Totals 200 }}px;"></div>
</div>
</div>
</td>
</tr>
{{ end }} {{/* end range .DeploymentResults */}}
</table>
</div>
{{ end }} {{/* end range .AuditData.NamespacedResults */}}
</div>

<div class="footer">
<a href="https://reactiveops.com?source=fairwinds" target="_blank">&copy;2019 ReactiveOps Inc.</a>
</div>

</div>
</td>
</tr>
{{ end }} {{/* end range .DeploymentResults */}}
</table>
</div>
{{ end }} {{/* end range .AuditData.NamespacedResults */}}
<script src="/static/js/charts.js">
</script>
</body>

</html>
{{end}}
5 changes: 5 additions & 0 deletions pkg/dashboard/templates/footer.gohtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{{define "footer"}}
<div class="footer">
<a href="https://reactiveops.com?source=fairwinds" target="_blank">&copy;2019 ReactiveOps Inc.</a>
</div>
{{end}}
13 changes: 13 additions & 0 deletions pkg/dashboard/templates/header.gohtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{{define "header"}}
<div class="header">
<div class="header-content">
<img class="logo" src="/static/images/polaris-logo.png" alt="Polaris" />
<div class="header-right">
<a href="https://reactiveops.com?source=fairwinds" target="_blank">
<span class="oss-text">Open Source Project By</span>
<img class="ro-logo" src="/static/images/ro-logo.png" alt="ReactiveOps" />
</a>
</div>
</div>
</div>
{{end}}
Loading