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

optionalize base path for dashboard #134

Merged
merged 2 commits into from
Jun 4, 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
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,10 @@ Polaris validation checks fall into several different categories:

* `config`: Specify a location for the Polaris config
* `dashboard`: Runs the webserver for Polaris dashboard.
* `dashboard-port`: Port for the dashboard webserver (default 8080)
* `dashboard-port`: Port for the dashboard webserver (default `8080`)
* `dashboard-base-path`: Path on which the dashboard is being served (default `/`)
* `webhook`: Runs the webhook webserver.
* `webhook-port`: Port for the webhook webserver (default 9876)
* `webhook-port`: Port for the webhook webserver (default `9876`)
* `disable-webhook-config-installer`: disable the installer in the webhook server, so it won't install webhook configuration resources during bootstrapping
* `kubeconfig`: Paths to a kubeconfig. Only required if out-of-cluster.

Expand Down
48 changes: 4 additions & 44 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ import (
"io/ioutil"
"net/http"
"os"
"strings"

"github.com/gorilla/mux"
conf "github.com/reactiveops/polaris/pkg/config"
"github.com/reactiveops/polaris/pkg/dashboard"
"github.com/reactiveops/polaris/pkg/kube"
Expand Down Expand Up @@ -53,6 +51,7 @@ func main() {
audit := flag.Bool("audit", false, "Runs a one-time audit.")
auditPath := flag.String("audit-path", "", "If specified, audits one or more YAML files instead of a cluster")
dashboardPort := flag.Int("dashboard-port", 8080, "Port for the dashboard webserver")
dashboardBasePath := flag.String("dashboard-base-path", "/", "Path on which the dashboard is served")
webhookPort := flag.Int("webhook-port", 9876, "Port for the webhook webserver")
auditOutputURL := flag.String("output-url", "", "Destination URL to send audit results")
auditOutputFile := flag.String("output-file", "", "Destination file for audit results")
Expand Down Expand Up @@ -89,56 +88,17 @@ func main() {
if *webhook {
startWebhookServer(c, *disableWebhookConfigInstaller, *webhookPort)
} else if *dashboard {
startDashboardServer(c, *auditPath, *dashboardPort)
startDashboardServer(c, *auditPath, *dashboardPort, *dashboardBasePath)
} else if *audit {
runAudit(c, *auditPath, *auditOutputFile, *auditOutputURL)
}
}

func startDashboardServer(c conf.Configuration, auditPath string, port int) {
router := mux.NewRouter()
func startDashboardServer(c conf.Configuration, auditPath string, port int, basePath string) {
router := dashboard.GetRouter(c, auditPath, port, basePath)
router.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
})
router.HandleFunc("/results.json", func(w http.ResponseWriter, r *http.Request) {
k, err := kube.CreateResourceProvider(auditPath)
if err != nil {
logrus.Errorf("Error fetching Kubernetes resources %v", err)
http.Error(w, "Error fetching Kubernetes resources", http.StatusInternalServerError)
return
}
dashboard.EndpointHandler(w, r, c, k)
})
router.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "pkg/dashboard/assets/favicon-32x32.png")
})
router.HandleFunc("/details/{category}", func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
category := vars["category"]
category = strings.Replace(category, ".md", "", -1)
dashboard.DetailsHandler(w, r, category)
})
fileServer := http.FileServer(dashboard.GetAssetBox())
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
k, err := kube.CreateResourceProvider(auditPath)
if err != nil {
logrus.Errorf("Error fetching Kubernetes resources %v", err)
http.Error(w, "Error fetching Kubernetes resources", http.StatusInternalServerError)
return
}
auditData, err := validator.RunAudit(c, k)
if err != nil {
logrus.Errorf("Error getting audit data: %v", err)
http.Error(w, "Error running audit", 500)
return
}
dashboard.MainHandler(w, r, auditData)
})
http.Handle("/static/", http.StripPrefix("/static/", fileServer))
http.Handle("/", router)

logrus.Infof("Starting Polaris dashboard server on port %d", port)
Expand Down
6 changes: 3 additions & 3 deletions pkg/dashboard/assets/css/Muli.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
font-family: 'Muli';
font-style: normal;
font-weight: 300;
src: local('Muli Light'), local('Muli-Light'), url(/static/webfonts/Muli-Light.tff) format('truetype');
src: local('Muli Light'), local('Muli-Light'), url(../webfonts/Muli-Light.tff) format('truetype');
}
@font-face {
font-family: 'Muli';
font-style: normal;
font-weight: 400;
src: local('Muli Regular'), local('Muli-Regular'), url(/static/webfonts/Muli-Regular.tff) format('truetype');
src: local('Muli Regular'), local('Muli-Regular'), url(../webfonts/Muli-Regular.tff) format('truetype');
}
@font-face {
font-family: 'Muli';
font-style: normal;
font-weight: 700;
src: local('Muli Bold'), local('Muli-Bold'), url(/static/webfonts/Muli-Bold.tff) format('truetype');
src: local('Muli Bold'), local('Muli-Bold'), url(../webfonts/Muli-Bold.tff) format('truetype');
}
85 changes: 73 additions & 12 deletions pkg/dashboard/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ import (
"encoding/json"
"html/template"
"net/http"
"strings"

packr "github.com/gobuffalo/packr/v2"
"github.com/gorilla/mux"
conf "github.com/reactiveops/polaris/pkg/config"
"github.com/reactiveops/polaris/pkg/kube"
"github.com/reactiveops/polaris/pkg/validator"
Expand Down Expand Up @@ -75,8 +77,9 @@ func GetMarkdownBox() *packr.Box {
return markdownBox
}

// TemplateData is passed to the dashboard HTML template
type TemplateData struct {
// templateData is passed to the dashboard HTML template
type templateData struct {
BasePath string
AuditData validator.AuditData
JSON template.JS
}
Expand Down Expand Up @@ -122,7 +125,7 @@ func parseTemplateFiles(tmpl *template.Template, templateFileNames []string) (*t
return tmpl, nil
}

func writeTemplate(tmpl *template.Template, data *TemplateData, w http.ResponseWriter) {
func writeTemplate(tmpl *template.Template, data *templateData, w http.ResponseWriter) {
buf := &bytes.Buffer{}
err := tmpl.Execute(buf, data)
if err != nil {
Expand All @@ -132,16 +135,71 @@ func writeTemplate(tmpl *template.Template, data *TemplateData, w http.ResponseW
buf.WriteTo(w)
}

// GetRouter returns a mux router serving all routes necessary for the dashboard
func GetRouter(c conf.Configuration, auditPath string, port int, basePath string) *mux.Router {
router := mux.NewRouter()
router.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
})
router.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
favicon, err := GetAssetBox().Find("favicon-32x32.png")
if err != nil {
logrus.Errorf("Error getting favicon: %v", err)
http.Error(w, "Error getting favicon", http.StatusInternalServerError)
return
}
w.Write(favicon)
})
router.HandleFunc("/results.json", func(w http.ResponseWriter, r *http.Request) {
k, err := kube.CreateResourceProvider(auditPath)
if err != nil {
logrus.Errorf("Error fetching Kubernetes resources %v", err)
http.Error(w, "Error fetching Kubernetes resources", http.StatusInternalServerError)
return
}
JSONHandler(w, r, c, k)
})
router.HandleFunc("/details/{category}", func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
category := vars["category"]
category = strings.Replace(category, ".md", "", -1)
DetailsHandler(w, r, category, basePath)
})
fileServer := http.FileServer(GetAssetBox())
router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", fileServer))
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
k, err := kube.CreateResourceProvider(auditPath)
if err != nil {
logrus.Errorf("Error fetching Kubernetes resources %v", err)
http.Error(w, "Error fetching Kubernetes resources", http.StatusInternalServerError)
return
}
auditData, err := validator.RunAudit(c, k)
if err != nil {
logrus.Errorf("Error getting audit data: %v", err)
http.Error(w, "Error running audit", 500)
return
}
MainHandler(w, r, auditData, basePath)
})
return router
}

// MainHandler gets template data and renders the dashboard with it.
func MainHandler(w http.ResponseWriter, r *http.Request, auditData validator.AuditData) {
func MainHandler(w http.ResponseWriter, r *http.Request, auditData validator.AuditData, basePath string) {
jsonData, err := json.Marshal(auditData)

if err != nil {
http.Error(w, "Error serializing audit data", 500)
return
}

templateData := TemplateData{
data := templateData{
BasePath: basePath,
AuditData: auditData,
JSON: template.JS(jsonData),
}
Expand All @@ -151,24 +209,24 @@ func MainHandler(w http.ResponseWriter, r *http.Request, auditData validator.Aud
http.Error(w, "Error getting template data", 500)
return
}
writeTemplate(tmpl, &templateData, w)
writeTemplate(tmpl, &data, w)
}

// EndpointHandler gets template data and renders json with it.
func EndpointHandler(w http.ResponseWriter, r *http.Request, c conf.Configuration, kubeResources *kube.ResourceProvider) {
templateData, err := validator.RunAudit(c, kubeResources)
// JSONHandler gets template data and renders json with it.
func JSONHandler(w http.ResponseWriter, r *http.Request, c conf.Configuration, kubeResources *kube.ResourceProvider) {
auditData, err := validator.RunAudit(c, kubeResources)
if err != nil {
http.Error(w, "Error Fetching Deployments", http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(templateData)
json.NewEncoder(w).Encode(auditData)
}

// DetailsHandler returns details for a given error type
func DetailsHandler(w http.ResponseWriter, r *http.Request, category string) {
func DetailsHandler(w http.ResponseWriter, r *http.Request, category string, basePath string) {
box := GetMarkdownBox()
contents, err := box.Find(category + ".md")
if err != nil {
Expand All @@ -192,5 +250,8 @@ func DetailsHandler(w http.ResponseWriter, r *http.Request, category string) {
return
}
tmpl.Parse(detailsHTML)
writeTemplate(tmpl, nil, w)
data := templateData{
BasePath: basePath,
}
writeTemplate(tmpl, &data, w)
}
2 changes: 1 addition & 1 deletion pkg/dashboard/templates/check-details.gohtml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<head>
{{ template "head" . }}
<link rel="stylesheet" href="/static/css/check-details.css">
<link rel="stylesheet" href="static/css/check-details.css">
</head>

<body>
Expand Down
8 changes: 4 additions & 4 deletions pkg/dashboard/templates/dashboard.gohtml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
</div>
<div class="name"><span class="caret-expander"></span>{{ $category }}<span class="category-score">Score: <strong>{{ getScore $summary }}%</strong></span></div>
<div class="result-messages expandable-content">
<p class="category-info">{{ getCategoryInfo $category }} Refer to the <a href="/details/{{ getCategoryLink $category }}">Polaris documentation about {{ $category }}</a> for more information.</p>
<p class="category-info">{{ getCategoryInfo $category }} Refer to the <a href="details/{{ getCategoryLink $category }}">Polaris documentation about {{ $category }}</a> for more information.</p>
</div>
</div>
{{ end }} {{/* end range categories */}}
Expand Down Expand Up @@ -93,7 +93,7 @@
<li class="{{ .Type }}">
<i class="message-icon {{ getIcon $message }}"></i>
<span class="message">{{ .Message }}</span>
<a class="more-info" href="/details/{{ getCategoryLink .Category }}">
<a class="more-info" href="details/{{ getCategoryLink .Category }}">
<i class="far fa-question-circle"></i>
</a>
</li>
Expand All @@ -108,7 +108,7 @@
<li class="{{ .Type }}">
<i class="message-icon {{ getIcon $message }}"></i>
<span class="message">{{ .Message }}</span>
<a class="more-info" href="/details/{{ getCategoryLink .Category }}">
<a class="more-info" href="details/{{ getCategoryLink .Category }}">
<i class="far fa-question-circle"></i>
</a>
</li>
Expand All @@ -121,6 +121,6 @@
</div>
</div>
{{ end }} {{/* end range .AuditData.NamespacedResults */}}
<script src="/static/js/charts.js">
<script src="static/js/charts.js">
</script>
{{end}}
21 changes: 11 additions & 10 deletions pkg/dashboard/templates/head.gohtml
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
{{ define "head" }}
<base href="{{ .BasePath }}">
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>ReactiveOps Polaris</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="/static/css/Muli.css" rel="stylesheet">
<link rel="stylesheet" href="/static/css/normalize.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/dashboard.css">
<script type="text/javascript" src="/static/js/Chart-2.7.2.min.js"></script>
<script type="text/javascript" src="/static/js/cash-4.1.2.min.js"></script>
<script type="text/javascript" src="/static/js/main.js"></script>
<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="static/css/Muli.css" rel="stylesheet">
<link rel="stylesheet" href="static/css/normalize.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/dashboard.css">
<script type="text/javascript" src="static/js/Chart-2.7.2.min.js"></script>
<script type="text/javascript" src="static/js/cash-4.1.2.min.js"></script>
<script type="text/javascript" src="static/js/main.js"></script>
{{ end }}
4 changes: 2 additions & 2 deletions pkg/dashboard/templates/navbar.gohtml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
<div class="navbar">
<div class="navbar-content">
<a href="/">
<img class="logo" src="/static/images/polaris-logo.png" alt="Polaris" />
<img class="logo" src="static/images/polaris-logo.png" alt="Polaris" />
</a>
<div class="navbar-right">
<a href="https://reactiveops.com?source=polaris" target="_blank">
<span class="oss-text">An Open Source Project By</span>
<img class="ro-logo" src="/static/images/ro-logo.png" alt="ReactiveOps" />
<img class="ro-logo" src="static/images/ro-logo.png" alt="ReactiveOps" />
</a>
</div>
</div>
Expand Down