Commit 8ccbfd55 authored by ale's avatar ale

Show service -> host assignments

parent a96d6afe
Pipeline #3555 passed with stages
in 1 minute and 4 seconds
......@@ -78,7 +78,7 @@ func staticCssBootstrapMinCss() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/css/bootstrap.min.css", size: 142041, mode: os.FileMode(420), modTime: time.Unix(1541086086, 0)}
info := bindataFileInfo{name: "static/css/bootstrap.min.css", size: 142041, mode: os.FileMode(420), modTime: time.Unix(1561268261, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -95,7 +95,7 @@ func staticCssBootstrapMinCssMap() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/css/bootstrap.min.css.map", size: 563340, mode: os.FileMode(420), modTime: time.Unix(1541086086, 0)}
info := bindataFileInfo{name: "static/css/bootstrap.min.css.map", size: 563340, mode: os.FileMode(420), modTime: time.Unix(1561268261, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -112,7 +112,7 @@ func staticCssOpenIconicBootstrapMinCss() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/css/open-iconic-bootstrap.min.css", size: 9395, mode: os.FileMode(420), modTime: time.Unix(1541086086, 0)}
info := bindataFileInfo{name: "static/css/open-iconic-bootstrap.min.css", size: 9395, mode: os.FileMode(420), modTime: time.Unix(1561268261, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -129,7 +129,7 @@ func staticCssStyleCss() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/css/style.css", size: 0, mode: os.FileMode(420), modTime: time.Unix(1541086086, 0)}
info := bindataFileInfo{name: "static/css/style.css", size: 0, mode: os.FileMode(420), modTime: time.Unix(1561268261, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -146,7 +146,7 @@ func staticFontsOpenIconicEot() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/fonts/open-iconic.eot", size: 28196, mode: os.FileMode(420), modTime: time.Unix(1541086086, 0)}
info := bindataFileInfo{name: "static/fonts/open-iconic.eot", size: 28196, mode: os.FileMode(420), modTime: time.Unix(1561268261, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -163,7 +163,7 @@ func staticFontsOpenIconicOtf() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/fonts/open-iconic.otf", size: 20996, mode: os.FileMode(420), modTime: time.Unix(1541086086, 0)}
info := bindataFileInfo{name: "static/fonts/open-iconic.otf", size: 20996, mode: os.FileMode(420), modTime: time.Unix(1561268261, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -723,7 +723,7 @@ func staticFontsOpenIconicSvg() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/fonts/open-iconic.svg", size: 54789, mode: os.FileMode(420), modTime: time.Unix(1541086086, 0)}
info := bindataFileInfo{name: "static/fonts/open-iconic.svg", size: 54789, mode: os.FileMode(420), modTime: time.Unix(1561268261, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -740,7 +740,7 @@ func staticFontsOpenIconicTtf() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/fonts/open-iconic.ttf", size: 28028, mode: os.FileMode(420), modTime: time.Unix(1541086086, 0)}
info := bindataFileInfo{name: "static/fonts/open-iconic.ttf", size: 28028, mode: os.FileMode(420), modTime: time.Unix(1561268261, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -757,7 +757,7 @@ func staticFontsOpenIconicWoff() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/fonts/open-iconic.woff", size: 14984, mode: os.FileMode(420), modTime: time.Unix(1541086086, 0)}
info := bindataFileInfo{name: "static/fonts/open-iconic.woff", size: 14984, mode: os.FileMode(420), modTime: time.Unix(1561268261, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -780,7 +780,7 @@ func staticJsBootstrapBundleMinJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/js/bootstrap.bundle.min.js", size: 70966, mode: os.FileMode(420), modTime: time.Unix(1541086086, 0)}
info := bindataFileInfo{name: "static/js/bootstrap.bundle.min.js", size: 70966, mode: os.FileMode(420), modTime: time.Unix(1561268261, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -797,7 +797,7 @@ func staticJsBootstrapBundleMinJsMap() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/js/bootstrap.bundle.min.js.map", size: 294126, mode: os.FileMode(420), modTime: time.Unix(1541086086, 0)}
info := bindataFileInfo{name: "static/js/bootstrap.bundle.min.js.map", size: 294126, mode: os.FileMode(420), modTime: time.Unix(1561268261, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -816,7 +816,7 @@ func staticJsJquerySlimMinJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/js/jquery.slim.min.js", size: 69917, mode: os.FileMode(420), modTime: time.Unix(1541086086, 0)}
info := bindataFileInfo{name: "static/js/jquery.slim.min.js", size: 69917, mode: os.FileMode(420), modTime: time.Unix(1561268261, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -841,7 +841,7 @@ func templatesContainer_detailsHtml() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "templates/container_details.html", size: 211, mode: os.FileMode(420), modTime: time.Unix(1541086086, 0)}
info := bindataFileInfo{name: "templates/container_details.html", size: 211, mode: os.FileMode(420), modTime: time.Unix(1561268261, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -875,7 +875,7 @@ func templatesFooterHtml() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "templates/footer.html", size: 293, mode: os.FileMode(420), modTime: time.Unix(1541086086, 0)}
info := bindataFileInfo{name: "templates/footer.html", size: 293, mode: os.FileMode(420), modTime: time.Unix(1561268261, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -906,7 +906,7 @@ func templatesHeaderHtml() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "templates/header.html", size: 448, mode: os.FileMode(420), modTime: time.Unix(1541086086, 0)}
info := bindataFileInfo{name: "templates/header.html", size: 448, mode: os.FileMode(420), modTime: time.Unix(1561268261, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -922,6 +922,7 @@ var _templatesIndexHtml = []byte(`{{template "header" "float services"}}
<tr>
<th>Name</th>
<th>TLS</th>
<th>Hosts</th>
<th>Ports</th>
<th>URLs</th>
<th>Endpoints</th>
......@@ -930,9 +931,11 @@ var _templatesIndexHtml = []byte(`{{template "header" "float services"}}
</thead>
<tbody>
{{with $services := .Services}}
{{range $services.Keys}}
{{$svc := index $services .}}
{{with $serviceMap := .ServiceMap}}
{{range $serviceMap.ServiceNames}}
{{$svc := index $serviceMap.Services .}}
{{$svchosts := index $serviceMap.Assignments .}}
{{$svcmaster := index $serviceMap.Masters .}}
<tr>
<td>
{{.}}
......@@ -948,7 +951,24 @@ var _templatesIndexHtml = []byte(`{{template "header" "float services"}}
<span class="oi oi-key"></span>
{{end}}
</td>
<td>{{if $svc.Ports}}{{$svc.Ports}}{{end}}</td>
<td>
{{range $svchosts}}
{{.}}
{{if eq . $svcmaster}}
<span class="badge badge-pill badge-info">master</span>
{{end}}<br>
{{end}}
</td>
<td>
{{range $svc.AllPorts}}
{{.Port}}
{{if .Tags}}
{{range .Tags}}
<span class="badge badge-pill badge-info">{{.}}</span>
{{end}}
{{end}}<br>
{{end}}
</td>
<td>
{{range $i, $p := $svc.PublicEndpoints}}
......@@ -973,9 +993,6 @@ var _templatesIndexHtml = []byte(`{{template "header" "float services"}}
<a href="{{$m.TargetStatusURL}}">
<span class="oi oi-circle-check" title="health status"></span>
</a>
<a href="{{$m.ServiceDashboardURL}}">
<span class="oi oi-graph" title="health status"></span>
</a>
{{end}}
</td>
......@@ -986,6 +1003,18 @@ var _templatesIndexHtml = []byte(`{{template "header" "float services"}}
<a href="/service/{{$svc.Name}}/container/{{$c.Name}}/details">
<span class="oi oi-excerpt" title="details"></span>
</a>
{{$systemdUnitName := $c.SystemdServiceName $svc}}
<a href="{{$svc.SystemdServiceURL $systemdUnitName}}">
<span class="oi oi-graph" title="performance"></span>
</a>
{{end}}
{{range $i, $s := $svc.SystemdServices}}
<br>
{{$s}}
<a href="{{$svc.SystemdServiceURL $s}}">
<span class="oi oi-graph" title="performance"></span>
</a>
{{end}}
</td>
......@@ -1015,7 +1044,7 @@ func templatesIndexHtml() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "templates/index.html", size: 2178, mode: os.FileMode(420), modTime: time.Unix(1543434683, 0)}
info := bindataFileInfo{name: "templates/index.html", size: 3002, mode: os.FileMode(420), modTime: time.Unix(1561280744, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -1041,7 +1070,7 @@ func templatesService_detailsHtml() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "templates/service_details.html", size: 176, mode: os.FileMode(420), modTime: time.Unix(1541086086, 0)}
info := bindataFileInfo{name: "templates/service_details.html", size: 176, mode: os.FileMode(420), modTime: time.Unix(1561268261, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -21,11 +21,13 @@ import (
var (
addr = flag.String("addr", getenv("ADDR", ":3000"), "tcp `address` to listen on")
configPath = flag.String("config", getenv("CONFIG", "/etc/float/dashboard.yml"), "dashboard config `file`")
floatServicesPath = flag.String("services", getenv("SERVICES", "/etc/float/services.yml"), "float services `file`")
floatServicesPath = flag.String("services-file", getenv("SERVICES", "/etc/float/services.yml"), "float services `file`")
floatServiceAssignmentsPath = flag.String("service-assignments-file", getenv("SERVICES", "/etc/float/service_assignments.yml"), "float service assignments map `file`")
floatServiceMastersPath = flag.String("service-masters-file", getenv("SERVICES", "/etc/float/service_masters.yml"), "float service masters map `file`")
tpl *template.Template
config *Config
services ServiceMap
serviceMap *ServiceMap
)
func getenv(k, dflt string) string {
......@@ -61,15 +63,30 @@ func mustReadConfig() *Config {
}
// Read the configured services.
func mustReadServices() ServiceMap {
m := make(map[string]*Service)
if err := readYAML(*floatServicesPath, &m); err != nil {
func mustReadServiceMap() *ServiceMap {
// Read services.yml.
services := make(map[string]*Service)
if err := readYAML(*floatServicesPath, &services); err != nil {
log.Fatalf("error reading services file: %v", err)
}
for name, s := range m {
// Add the name to every service, so we can iterate over values easily.
for name, s := range services {
s.Name = name
}
return m
// Read service_assignments.yml.
assignments := make(map[string][]string)
if err := readYAML(*floatServiceAssignmentsPath, &assignments); err != nil {
log.Fatalf("error reading service assignments file: %v", err)
}
// Read service_masters.yml.
masters := make(map[string]string)
if err := readYAML(*floatServiceMastersPath, &masters); err != nil {
log.Fatalf("error reading service masters file: %v", err)
}
return newServiceMap(services, assignments, masters)
}
// Parse the templates that are embedded with the binary (in bindata.go).
......@@ -124,7 +141,7 @@ func renderTemplate(w io.Writer, name string, ctx map[string]interface{}) {
ctx = make(map[string]interface{})
}
ctx["Config"] = config
ctx["Services"] = services
ctx["ServiceMap"] = serviceMap
if err := tpl.ExecuteTemplate(w, name, ctx); err != nil {
log.Printf("template error: %v", err)
}
......@@ -141,7 +158,7 @@ func handleIndex(w http.ResponseWriter, r *http.Request) {
func handleServiceDetails(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
s, ok := services[vars["service"]]
s, ok := serviceMap.Services[vars["service"]]
if !ok {
http.NotFound(w, r)
return
......@@ -154,7 +171,7 @@ func handleServiceDetails(w http.ResponseWriter, r *http.Request) {
func handleContainerDetails(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
s, ok := services[vars["service"]]
s, ok := serviceMap.Services[vars["service"]]
if !ok {
http.NotFound(w, r)
return
......@@ -162,7 +179,7 @@ func handleContainerDetails(w http.ResponseWriter, r *http.Request) {
var c *Container
for _, cc := range s.Containers {
if cc.Name == vars["container"] {
c = &cc
c = cc
break
}
}
......@@ -198,7 +215,7 @@ func main() {
tpl = mustParseEmbeddedTemplates()
config = mustReadConfig()
services = mustReadServices()
serviceMap = mustReadServiceMap()
h := makeServer()
......
package main
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestDash(t *testing.T) {
tpl = mustParseEmbeddedTemplates()
config = &Config{
Domain: "investici.org",
PublicDomain: "autistici.org",
}
serviceMap = newServiceMap(
map[string]*Service{
"svc1": &Service{
Name: "svc1",
NumInstances: 1,
Ports: []int{4040},
PublicEndpoints: []*PublicEndpoint{
&PublicEndpoint{
Name: "svc1",
Port: 3232,
},
},
Containers: []*Container{
&Container{
Name: "http",
Port: 3232,
Image: "image",
},
},
SystemdServices: []string{"svc1-internal.service"},
},
},
map[string][]string{
"svc1": []string{"host1", "host2"},
},
map[string]string{},
)
srv := httptest.NewServer(makeServer())
resp, err := http.Get(srv.URL)
if err != nil {
t.Fatalf("http.Get(%s): %v", srv.URL, err)
}
if resp.StatusCode != 200 {
t.Fatalf("got error %s", resp.Status)
}
}
......@@ -6,27 +6,16 @@ import (
"sort"
)
// Constants for Kibana dashboard IDs.
const (
kibanaWebDashboardID = "AV9wDlJSJ4s36xPImL8m"
kibanaSyslogDashboardID = "dc753c20-5616-11e8-b43b-036ee7010221"
)
type MonitoringEndpoint struct {
JobName string `yaml:"job_name"`
Port int `yaml:"port"`
Scheme string `yaml:"scheme,omitempty"`
}
func (m MonitoringEndpoint) TargetStatusURL() string {
func (m *MonitoringEndpoint) TargetStatusURL() string {
return fmt.Sprintf("https://monitor.%s/targets#job-%s", config.PublicDomain, m.JobName)
}
func (m MonitoringEndpoint) ServiceDashboardURL() string {
//return fmt.Sprintf("https://grafana.%s/d/xfV2rd7ik/service-overview?refresh=5s&orgId=1&from=now-3h&to=now&var-service=%s&var-vhosts=%s", config.PublicDomain, m.JobName, serviceName, vhostName)
return fmt.Sprintf("https://grafana.%s/service-dashboard?service=%s", config.PublicDomain, m.JobName)
}
type PublicEndpoint struct {
Name string `yaml:"name"`
Domains []string `yaml:"domains,omitempty"`
......@@ -35,26 +24,22 @@ type PublicEndpoint struct {
EnableSSOProxy bool `yaml:"enable_sso_proxy,omitempty"`
}
func (p PublicEndpoint) FQDN() string {
func (p *PublicEndpoint) FQDN() string {
return fmt.Sprintf("%s.%s", p.Name, config.PublicDomain)
}
func (p PublicEndpoint) URL() string {
func (p *PublicEndpoint) URL() string {
return fmt.Sprintf("https://%s/", p.FQDN())
}
func (p PublicEndpoint) LogsURL() template.HTML {
// Woah.
return template.HTML(fmt.Sprintf(
"https://logs.%s/app/kibana#/dashboard/%s?_g=(refreshInterval:(display:Off,pause:!f,value:0),time:(from:now-12h,mode:quick,to:now))&_a=(description:'',filters:!(),query:(language:lucene,query:'vhost:%s*'))",
config.PublicDomain,
kibanaWebDashboardID,
p.FQDN(),
))
func (p *PublicEndpoint) LogsURL() template.HTML {
return template.HTML(
kibanaURL("web", fmt.Sprintf("vhost:%s*", p.FQDN())),
)
}
func (p PublicEndpoint) QpsGraphURL() string {
return fmt.Sprintf("https://grafana.%s/d/xCSUMFnmz/http?orgId=1&var-vhost=%s:443", config.PublicDomain, p.FQDN())
func (p *PublicEndpoint) QpsGraphURL() string {
return grafanaQpsURL(p.FQDN())
}
// Boolean fields that default to true are problematic, so we use
......@@ -87,6 +72,10 @@ type Container struct {
Env map[string]string `yaml:"env,omitempty"`
}
func (c *Container) SystemdServiceName(s *Service) string {
return fmt.Sprintf("docker-%s-%s.service", s.Name, c.Name)
}
type Service struct {
Name string
......@@ -95,26 +84,88 @@ type Service struct {
MasterElection bool `yaml:"master_election,omitempty"`
MasterSchedulingGroup string `yaml:"master_scheduling_group,omitempty"`
Ports []int `yaml:"ports,omitempty"`
ServiceCredentials []ServiceCredentials `yaml:"service_credentials,omitempty"`
ServiceCredentials []*ServiceCredentials `yaml:"service_credentials,omitempty"`
LDAPCredentials []struct {
Name string `yaml:"name"`
} `yaml:"ldap_credentials,omitempty"`
MonitoringEndpoints []MonitoringEndpoint `yaml:"monitoring_endpoints,omitempty"`
PublicEndpoints []PublicEndpoint `yaml:"public_endpoints,omitempty"`
Containers []Container `yaml:"containers,omitempty"`
MonitoringEndpoints []*MonitoringEndpoint `yaml:"monitoring_endpoints,omitempty"`
PublicEndpoints []*PublicEndpoint `yaml:"public_endpoints,omitempty"`
Containers []*Container `yaml:"containers,omitempty"`
SystemdServices []string `yaml:"systemd_services"`
}
type ServiceMap map[string]*Service
func (s *Service) SystemdServiceURL(systemdService string) string {
return grafanaSystemdServiceURL(systemdService)
}
func (m ServiceMap) Keys() []string {
func (s *Service) AllSystemdServices() []string {
var out []string
for k := range m {
out = append(out, k)
out = append(out, s.SystemdServices...)
for _, c := range s.Containers {
out = append(out, c.SystemdServiceName(s))
}
sort.Sort(sortedStringList(out))
return out
}
type TaggedPort struct {
Port int
Tags []string
}
func (s *Service) AllPorts() []TaggedPort {
tmp := make(map[int]*TaggedPort)
addTag := func(port int, tag string) {
tp, ok := tmp[port]
if !ok {
tp = &TaggedPort{Port: port}
tmp[port] = tp
}
if tag != "" {
tp.Tags = append(tp.Tags, tag)
}
}
for _, port := range s.Ports {
addTag(port, "")
}
for _, m := range s.MonitoringEndpoints {
addTag(m.Port, "monitoring")
}
for _, p := range s.PublicEndpoints {
addTag(p.Port, fmt.Sprintf("public:%s", p.Name))
}
for _, c := range s.Containers {
addTag(c.Port, fmt.Sprintf("docker:%s", c.Name))
}
out := make([]TaggedPort, 0, len(tmp))
for _, tp := range tmp {
out = append(out, *tp)
}
return out
}
type ServiceMap struct {
Services map[string]*Service
ServiceNames []string
Assignments map[string][]string
Masters map[string]string
}
func newServiceMap(svcs map[string]*Service, assignments map[string][]string, masters map[string]string) *ServiceMap {
m := &ServiceMap{
Services: svcs,
Assignments: assignments,
Masters: masters,
}
for name := range svcs {
m.ServiceNames = append(m.ServiceNames, name)
}
sort.Sort(sortedStringList(m.ServiceNames))
return m
}
func (s *Service) HasClientServiceCredentials() bool {
for _, c := range s.ServiceCredentials {
if c.HasClient() {
......
......@@ -6,11 +6,11 @@ import (
)
var sriMap = map[string]string{
"/static/css/bootstrap.min.css": "sha384-Smlep5jCw/wG7hdkwQ/Z5nLIefveQRIY9nfy6xoR1uRYBtpZgI6339F5dgvm/e9B",
"/static/css/open-iconic-bootstrap.min.css": "sha384-wWci3BOzr88l+HNsAtr3+e5bk9qh5KfjU6gl/rbzfTYdsAVHBEbxB33veLYmFg/a",
"/static/css/style.css": "sha384-OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb",
"/static/js/bootstrap.bundle.min.js": "sha384-CS0nxkpPy+xUkNGhObAISrkg/xjb3USVCwy+0/NMzd5VxgY4CMCyTkItmy5n0voC",
"/static/js/jquery.slim.min.js": "sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo",
"/static/css/bootstrap.min.css": "sha384-Smlep5jCw/wG7hdkwQ/Z5nLIefveQRIY9nfy6xoR1uRYBtpZgI6339F5dgvm/e9B",
}
// SRIScript returns a <script> tag with resource integrity attributes.
......
......@@ -9,6 +9,7 @@
<tr>
<th>Name</th>
<th>TLS</th>
<th>Hosts</th>
<th>Ports</th>
<th>URLs</th>
<th>Endpoints</th>
......@@ -17,9 +18,11 @@
</thead>
<tbody>
{{with $services := .Services}}
{{range $services.Keys}}
{{$svc := index $services .}}
{{with $serviceMap := .ServiceMap}}
{{range $serviceMap.ServiceNames}}
{{$svc := index $serviceMap.Services .}}
{{$svchosts := index $serviceMap.Assignments .}}
{{$svcmaster := index $serviceMap.Masters .}}
<tr>
<td>
{{.}}
......@@ -35,7 +38,24 @@
<span class="oi oi-key"></span>
{{end}}
</td>
<td>{{if $svc.Ports}}{{$svc.Ports}}{{end}}</td>
<td>
{{range $svchosts}}
{{.}}
{{if eq . $svcmaster}}
<span class="badge badge-pill badge-info">master</span>
{{end}}<br>
{{end}}
</td>
<td>
{{range $svc.AllPorts}}
{{.Port}}
{{if .Tags}}
{{range .Tags}}
<span class="badge badge-pill badge-info">{{.}}</span>
{{end}}
{{end}}<br>
{{end}}
</td>
<td>
{{range $i, $p := $svc.PublicEndpoints}}
......@@ -60,9 +80,6 @@
<a href="{{$m.TargetStatusURL}}">
<span class="oi oi-circle-check" title="health status"></span>
</a>
<a href="{{$m.ServiceDashboardURL}}">
<span class="oi oi-graph" title="health status"></span>
</a>
{{end}}
</td>
......@@ -73,6 +90,18 @@
<a href="/service/{{$svc.Name}}/container/{{$c.Name}}/details">
<span class="oi oi-excerpt" title="details"></span>
</a>
{{$systemdUnitName := $c.SystemdServiceName $svc}}
<a href="{{$svc.SystemdServiceURL $systemdUnitName}}">
<span class="oi oi-graph" title="performance"></span>
</a>
{{end}}
{{range $i, $s := $svc.SystemdServices}}
<br>
{{$s}}
<a href="{{$svc.SystemdServiceURL $s}}">
<span class="oi oi-graph" title="performance"></span>
</a>
{{end}}
</td>
......
package main
import (
"fmt"
"net/url"
"strings"
)
// Constants for Kibana dashboard IDs.
var (
kibanaDashboards = map[string]string{
"web": "AV9wDlJSJ4s36xPImL8m",
"syslog": "dc753c20-5616-11e8-b43b-036ee7010221",
}
grafanaDashboards = map[string]string{
"http": "xCSUMFnmz",
"service-overview": "xfV2rd7ik",
}
)
func grafanaURL(dashboardName string, vars map[string]string) string {
dashboardID := grafanaDashboards[dashboardName]
vals := make(url.Values)
vals.Set("orgId", "1")
for k, v := range vars {
vals.Set(k, v)
}
return fmt.Sprintf(
"https://grafana.%s/d/%s/%s?%s",
config.PublicDomain,
dashboardID,
dashboardName,
vals.Encode(),
)
}
func kibanaURL(dashboardName, query string) string {
// Woah.
dashboardID := kibanaDashboards[dashboardName]
return fmt.Sprintf(
"https://logs.%s/app/kibana#/dashboard/%s?_g=(refreshInterval:(display:Off,pause:!f,value:0),time:(from:now-12h,mode:quick,to:now))&_a=(description:'',filters:!(),query:(language:lucene,query:'%s'))",
config.PublicDomain,
dashboardID,
strings.Replace(query, "\"", "%32", -1),
)
}
func grafanaSystemdServiceURL(systemdService string) string {
return grafanaURL("service-overview", map[string]string{
"var-service": strings.TrimSuffix(systemdService, ".service"),
})
}
func grafanaQpsURL(vhost string) string {
return grafanaURL("http", map[string]string{
"var-vhost": fmt.Sprintf("%s:443", vhost),
})
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment