package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"html/template"
"net/http"
"os"
"os/exec"
"strings"
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
"golang.org/x/text/language"
"golang.org/x/text/message"
)
const page = `
{{ .PageTitle }}
{{ .ServiceName }} |
{{ .Status }} |
{{ .SubStatus }} |
`
// Context for rendering template
type Context struct {
PageTitle string
CardTitle string
Details string
ServiceName string
Status string
SubStatus string
}
type Item struct {
Name string `json:"name"`
State string `json:"active_state"`
SubState string `json:"sub_state"`
}
type Response struct {
Status string `json:"status"`
Reason string `json:"reason"`
Items []Item `json:"items"`
}
func (response Response) Send(w http.ResponseWriter) {
bytes, err := json.Marshal(response)
if err != nil {
log.Errorf("Can't serialize stat: %v\n", err.Error())
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(bytes)
}
const (
StatusOK = "ok"
StatusErr = "error"
)
func checkService(w http.ResponseWriter, r *http.Request) {
var response Response
items, err := checkServices()
if err != nil {
log.Errorf("Can't get status: %v\n", err.Error())
response.Status = StatusErr
response.Reason = err.Error()
response.Send(w)
return
}
response.Status = StatusOK
response.Items = items
response.Send(w)
}
func renderPage(w http.ResponseWriter, r *http.Request) {
t, _, err := language.ParseAcceptLanguage(r.Header.Get("Accept-Language"))
if err != nil {
log.Errorf("Got error parsing Accept-Language header: %v", err.Error())
}
log.Infof("Got languages: %v", t)
templ, err := template.New("page").Parse(page)
if err != nil {
log.Errorf("Can't parse template: %v", err.Error())
w.WriteHeader(http.StatusInternalServerError)
return
}
var print *message.Printer
if len(t) > 0 {
for _, cur := range t {
if fmt.Sprintf("%v", cur)[:2] == fmt.Sprintf("%v", language.Russian) {
log.Debugf("Creating printer for lang %v", cur)
print = message.NewPrinter(cur)
break
}
}
}
if print == nil {
log.Infof("Create default printer")
print = message.NewPrinter(language.English)
}
context := Context{
PageTitle: print.Sprintf("PageTitle", "Loading"),
CardTitle: print.Sprintf("CardTitle", "Please wait, services are loading"),
Details: print.Sprintf("Details", "Show details"),
ServiceName: print.Sprintf("ServiceName", "Service name"),
Status: print.Sprintf("Status", "Status"),
SubStatus: print.Sprintf("SubStatus", "Sub status"),
}
var buf bytes.Buffer
err = templ.Execute(&buf, context)
if err != nil {
log.Errorf("Can't render template: %v", err.Error())
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Write(buf.Bytes())
}
func httpMatch(r *http.Request, rm *mux.RouteMatch) bool {
// This check prevent non localhost requests
if r.RemoteAddr[:5] == "[::1]" {
return true
} else if r.RemoteAddr[:9] == "127.0.0.1" {
return true
} else if r.RemoteAddr[:9] == "localhost" {
return true
}
log.Error("Bad remote addr")
return false
}
func init() {
message.SetString(language.Russian, "PageTitle", "Загрузка")
message.SetString(language.Russian, "CardTitle", "Пожалуйста, подождите, сервисы загружаются")
message.SetString(language.Russian, "Details", "Подробности")
message.SetString(language.Russian, "ServiceName", "Сервис")
message.SetString(language.Russian, "Status", "Статус")
message.SetString(language.Russian, "SubStatus", "Подстатус")
message.SetString(language.English, "PageTitle", "Loading")
message.SetString(language.English, "CardTitle", "Please wait, services are loading")
message.SetString(language.English, "Details", "Show details")
message.SetString(language.English, "ServiceName", "Service name")
message.SetString(language.English, "Status", "Status")
message.SetString(language.English, "SubStatus", "Sub status")
}
// Services to check it's state
var services = [...]string{"amccore.service", "amccelery.service", "amccelerybeat.service", "amccorrelator.service", "amcclient.service", "elasticsearch.service", "amcvector.service"}
func checkServices() ([]Item, error) {
result := make([]Item, 0)
for _, service := range services {
// Get service status
var out1 bytes.Buffer
cmd1 := exec.Command("systemctl", "show", "-p", "ActiveState", "--value", service)
cmd1.Stdout = &out1
err := cmd1.Run()
if err != nil {
log.Errorf("Can't get service active state: %v", err.Error())
return nil, err
}
var out2 bytes.Buffer
cmd2 := exec.Command("systemctl", "show", "-p", "SubState", "--value", service)
cmd2.Stdout = &out2
err = cmd2.Run()
if err != nil {
log.Errorf("Can't get service sub state: %v", err.Error())
return nil, err
}
result = append(result, Item{
Name: service,
State: strings.Replace(out1.String(), "\n", "", 1),
SubState: strings.Replace(out2.String(), "\n", "", 1),
})
}
return result, nil
}
func main() {
log.SetFormatter(&log.TextFormatter{})
log.SetOutput(os.Stdout)
port := flag.Int("port", 9080, "Port for work")
flag.Parse()
log.Info("Starting")
router := mux.NewRouter()
router.HandleFunc("/", checkService).Methods("GET").MatcherFunc(httpMatch)
router.HandleFunc("/page", renderPage).Methods("GET").MatcherFunc(httpMatch)
http.ListenAndServe(fmt.Sprintf(":%v", *port), router)
}