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 }}
` // 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) }