mirror of
https://github.com/FairwindsOps/polaris.git
synced 2026-08-23 22:26:34 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eab320af38 | ||
|
|
2494dd097b | ||
|
|
12e10f9111 | ||
|
|
139e6611b1 | ||
|
|
804171b289 | ||
|
|
f061e0e012 | ||
|
|
d5b3b6b4ae | ||
|
|
9a03f87c0b | ||
|
|
69d8b959d4 |
@@ -39,3 +39,7 @@
|
||||
* [Fix](https://github.com/reactiveops/polaris/issues/116): details pages getting template errors
|
||||
* [Fix](https://github.com/reactiveops/polaris/issues/114): support all auth providers
|
||||
* [Fix](https://github.com/reactiveops/polaris/issues/112): Ignore readiness probe for initContainers
|
||||
|
||||
# 0.1.5
|
||||
* [Fix](https://github.com/reactiveops/polaris/issues/125): ignore limits/requests for initContainers
|
||||
* [Fix](https://github.com/reactiveops/polaris/issues/132): support custom base path
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
* @bobby-brennan @robscott
|
||||
* @bobby-brennan @endzyme @kimschles @robscott
|
||||
|
||||
@@ -42,3 +42,19 @@ Each new pull request should:
|
||||
- Contain a clear indication of if they're ready for review or a work in progress
|
||||
- Be up to date and/or rebased on the master branch
|
||||
|
||||
## Creating a new release
|
||||
* Change the version number everywhere. E.g. for `0.1.4` to `0.1.5`:
|
||||
```
|
||||
find ./ -type f ! -path "./vendor/*" ! -path "./.git/*" ! -name CHANGELOG.md -exec sed -i 's/0\.1\.4/0.1.5/g' {} \;
|
||||
```
|
||||
* Add relevant entries to CHANGELOG.md
|
||||
* Create a PR to merge your changes
|
||||
* Once the PR is merged, wait for CircleCI to finish building the merge commit
|
||||
* Tag and push the latest:
|
||||
```
|
||||
git checkout master
|
||||
git pull
|
||||
git tag $VERSION
|
||||
git push --tags
|
||||
```
|
||||
* CircleCI will add an entry to the releases page on GitHub
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[![Version][version-image]][version-link] [![CircleCI][circleci-image]][circleci-link] [![Go Report Card][goreport-image]][goreport-link]
|
||||
</div>
|
||||
|
||||
[version-image]: https://img.shields.io/static/v1.svg?label=Version&message=0.1.4&color=239922
|
||||
[version-image]: https://img.shields.io/static/v1.svg?label=Version&message=0.1.5&color=239922
|
||||
[version-link]: https://github.com/reactiveops/polaris
|
||||
|
||||
[goreport-image]: https://goreportcard.com/badge/github.com/reactiveops/polaris
|
||||
@@ -19,6 +19,8 @@ Polaris helps keep your cluster healthy. It runs a variety of checks to ensure t
|
||||
- A dashboard that provides an overview of how well current deployments are configured within a cluster.
|
||||
- An experimental validating webhook that can prevent any future deployments that do not live up to a configured standard.
|
||||
|
||||
**Want to learn more?** ReactiveOps holds [office hours on Zoom](https://zoom.us/j/951540265) the first Friday of every month, at 12pm Eastern. You can also reach out via email at `opensource@reactiveops.com`
|
||||
|
||||
## Dashboard
|
||||
|
||||
The Polaris dashboard is a way to get a simple visual overview of the current state of your Kubernetes deployments as well as a roadmap for what can be improved. The dashboard provides a cluster wide overview as well as breaking out results by category, namespace, and deployment.
|
||||
@@ -106,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.
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ spec:
|
||||
- --dashboard
|
||||
- --config
|
||||
- /opt/app/config.yaml
|
||||
image: 'quay.io/reactiveops/polaris:0.1.4'
|
||||
image: 'quay.io/reactiveops/polaris:0.1.5'
|
||||
imagePullPolicy: 'Always'
|
||||
name: dashboard
|
||||
ports:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
apiVersion: v1
|
||||
description: Validation of best practices in your Kubernetes clusters
|
||||
name: polaris
|
||||
version: 0.1.4
|
||||
version: 0.1.5
|
||||
|
||||
@@ -35,9 +35,8 @@ Create chart name and version as used by the chart label.
|
||||
Standard labels
|
||||
*/}}
|
||||
{{- define "polaris.labels" -}}
|
||||
{{- if .Values.templateOnly -}}
|
||||
app: {{ include "polaris.name" . }}
|
||||
{{- else -}}
|
||||
{{- if not .Values.templateOnly }}
|
||||
app.kubernetes.io/name: {{ include "polaris.name" . }}
|
||||
helm.sh/chart: {{ include "polaris.chart" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
@@ -49,9 +48,8 @@ app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
Standard selector
|
||||
*/}}
|
||||
{{- define "polaris.selectors" -}}
|
||||
{{- if .Values.templateOnly -}}
|
||||
app: {{ include "polaris.name" . }}
|
||||
{{- else -}}
|
||||
{{- if not .Values.templateOnly }}
|
||||
app.kubernetes.io/name: {{ include "polaris.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end -}}
|
||||
|
||||
@@ -50,7 +50,7 @@ dashboard:
|
||||
type: ClusterIP
|
||||
image:
|
||||
repository: quay.io/reactiveops/polaris
|
||||
tag: 0.1.4
|
||||
tag: 0.1.5
|
||||
pullPolicy: Always
|
||||
|
||||
webhook:
|
||||
@@ -58,7 +58,7 @@ webhook:
|
||||
replicas: 1
|
||||
image:
|
||||
repository: quay.io/reactiveops/polaris
|
||||
tag: 0.1.4
|
||||
tag: 0.1.5
|
||||
pullPolicy: Always
|
||||
|
||||
rbac:
|
||||
|
||||
+1
-1
@@ -210,7 +210,7 @@ spec:
|
||||
- --webhook
|
||||
- --config
|
||||
- /opt/app/config.yaml
|
||||
image: 'quay.io/reactiveops/polaris:0.1.4'
|
||||
image: 'quay.io/reactiveops/polaris:0.1.5'
|
||||
imagePullPolicy: 'Always'
|
||||
ports:
|
||||
- containerPort: 9876
|
||||
|
||||
@@ -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"
|
||||
@@ -44,7 +42,7 @@ import (
|
||||
|
||||
const (
|
||||
// Version represents the current release version of Polaris
|
||||
Version = "0.1.4"
|
||||
Version = "0.1.5"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -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")
|
||||
@@ -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)
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
+73
-12
@@ -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"
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -132,8 +135,62 @@ 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 {
|
||||
@@ -141,7 +198,8 @@ func MainHandler(w http.ResponseWriter, r *http.Request, auditData validator.Aud
|
||||
return
|
||||
}
|
||||
|
||||
templateData := TemplateData{
|
||||
data := templateData{
|
||||
BasePath: basePath,
|
||||
AuditData: auditData,
|
||||
JSON: template.JS(jsonData),
|
||||
}
|
||||
@@ -151,12 +209,12 @@ 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
|
||||
@@ -164,11 +222,11 @@ func EndpointHandler(w http.ResponseWriter, r *http.Request, c conf.Configuratio
|
||||
|
||||
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 {
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 */}}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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}}
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -99,6 +99,9 @@ func CreateResourceProviderFromPath(directory string) (*ResourceProvider, error)
|
||||
}
|
||||
specs := regexp.MustCompile("\n-+\n").Split(string(contents), -1)
|
||||
for _, spec := range specs {
|
||||
if strings.TrimSpace(spec) == "" {
|
||||
continue
|
||||
}
|
||||
err = addYaml(spec)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error parsing YAML %v", err)
|
||||
|
||||
@@ -55,6 +55,12 @@ func ValidateContainer(cnConf *conf.Configuration, container *corev1.Container,
|
||||
}
|
||||
|
||||
func (cv *ContainerValidation) validateResources(resConf *conf.Resources) {
|
||||
// Only validate resources for primary containers. Although it can
|
||||
// be helpful to set these in certain cases, it usually isn't
|
||||
if cv.IsInitContainer {
|
||||
return
|
||||
}
|
||||
|
||||
category := messages.CategoryResources
|
||||
res := cv.Container.Resources
|
||||
|
||||
|
||||
@@ -161,6 +161,27 @@ func TestValidateResourcesPartiallyValid(t *testing.T) {
|
||||
testValidateResources(t, &container, &resourceConf1, &expectedErrors, &expectedWarnings)
|
||||
}
|
||||
|
||||
func TestValidateResourcesInit(t *testing.T) {
|
||||
cvEmpty := ContainerValidation{
|
||||
Container: &corev1.Container{},
|
||||
ResourceValidation: &ResourceValidation{},
|
||||
}
|
||||
cvInit := ContainerValidation{
|
||||
Container: &corev1.Container{},
|
||||
ResourceValidation: &ResourceValidation{},
|
||||
IsInitContainer: true,
|
||||
}
|
||||
|
||||
parsedConf, err := conf.Parse([]byte(resourceConf1))
|
||||
assert.NoError(t, err, "Expected no error when parsing config")
|
||||
|
||||
cvEmpty.validateResources(&parsedConf.Resources)
|
||||
assert.Len(t, cvEmpty.Errors, 4)
|
||||
|
||||
cvInit.validateResources(&parsedConf.Resources)
|
||||
assert.Len(t, cvInit.Errors, 0)
|
||||
}
|
||||
|
||||
func TestValidateResourcesFullyValid(t *testing.T) {
|
||||
cpuRequest, err := resource.ParseQuantity("300m")
|
||||
assert.NoError(t, err, "Error parsing quantity")
|
||||
|
||||
@@ -109,11 +109,9 @@ func (v *Validator) Handle(ctx context.Context, req types.Request) types.Respons
|
||||
|
||||
if podResult.Summary.Totals.Errors > 0 {
|
||||
allowed = false
|
||||
logrus.Infof("%d validation errors found when validating %s", podResult.Summary.Totals.Errors, podResult.Name)
|
||||
|
||||
reason = getFailureReason(podResult)
|
||||
}
|
||||
|
||||
logrus.Infof("%d validation errors found when validating %s", podResult.Summary.Totals.Errors, podResult.Name)
|
||||
return admission.ValidationResponse(allowed, reason)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user