Merge branch 'adrien/toGolang' of Production/xavierSrv into master

Release
master BIN_V1.0.0
adrien 2 years ago committed by Gitea
commit 473a00bde0
  1. 19
      .gitignore
  2. 68
      Makefile
  3. 2
      README.md
  4. 0
      bin/.keep
  5. 4
      bin/url.list
  6. 69
      bin/xavier.sh
  7. 43
      config/process.go
  8. 14
      config/struct_check.go
  9. 19
      config/struct_config.go
  10. 21
      config/struct_error.go
  11. 0
      examples/.keep
  12. 17
      examples/etc/xavier-srv/check.toml
  13. 13
      examples/etc/xavier-srv/config.toml
  14. 31
      examples/etc/xavier-srv/errors.toml
  15. 169
      main.go
  16. 0
      result.log
  17. 57
      tools/loging.go
  18. 76
      tools/reporting.go
  19. 47
      tools/various.go

19
.gitignore vendored

@ -1,3 +1,22 @@
status.html
*.back
*.backup
docs/public*
/.idea
*.test
*.prof
nohup.out
cover.out
*.swp
*.swo
*~
vendor/*/
*.bench
*.debug
coverage*.out
GoBuilds
dist
vendor
/.vscode

@ -0,0 +1,68 @@
GO ?= go
GOFMT ?= gofmt -s
GOPATH ?= $($(GO) env GOPATH)
BIN ?= /usr/local/bin
UDEV ?= /etc/udev/rules.d
DASH ?= $(if $(GOOS),_,)
GOOS ?=
GOARCH ?=
EXEC := xavierSrv$(DASH)$(GOOS)$(DASH)$(GOARCH)
GOOUT := $(shell pwd)/bin
GOFILES := main.go
.PHONY: help
help:
@echo "Make Routines:"
@echo " - \"\" print Make routines list"
@echo " - build creates the binary, Cross-Compiling ex : 'make build GOOS='linux' GOARCH='arm''"
@echo " - release Build for Linux 64/32/arm and OpenBSD 64/32"
@echo " - clean delete build files"
@echo " - install install the binary and service"
@echo " - remove remove binary and service"
@echo " - upgrade build and update installed binary"
.PHONY: install
install: build
sudo cp $(GOOUT)/$(EXEC) $(BIN)/$(EXEC)
# Add service
make clean
.PHONY: remove
remove:
sudo rm -rf $(BIN)/$(EXEC)
# Remove service
.PHONY: upgrade
upgrade: build
sudo cp $(GOOUT)/$(EXEC) $(BIN)/$(EXEC)
# Restart service
make clean
.PHONY: fmt
fmt:
$(GOFMT) -e -w $(GOFILES)
.PHONY: godep
godep:
$(GO) get github.com/BurntSushi/toml
$(GO) get github.com/xhit/go-simple-mail
.PHONY: build
build: godep
GOARCH=$(GOARCH) GOOS=$(GOOS) $(GO) build -o $(GOOUT)/$(EXEC) -a
.PHONY: buildall
buildall:
GOARCH=amd64 GOOS=linux make build
GOARCH=amd64 GOOS=openbsd make build
GOARCH=arm GOOS=linux make build
GOARCH=arm GOOS=openbsd make build
GOARCH=386 GOOS=linux make build
.PHONY: clean
clean:
$(GO) clean
rm -rf $(GOOUT)/*

@ -22,4 +22,4 @@ Each line is a tested url, composed like `STATUS:URL[:PORT]`:
Except all lines prefixed by `#` **OR **`%`
- `#` is for comments
- `%` is for commands when a check fail
- `%` is for commands when a check fail

@ -1,4 +0,0 @@
#%COMMANDE
%echo -e \"ma commande et les erreur:\\n~outS~\"
#HTTP_status_codes:URL[:PORT]
200:https://iglou.eu

@ -1,69 +0,0 @@
#!/bin/bash
trap "exit 0" 2 3
time=60
cerebro () {
cmdR=()
local _buff=""
while read line ; do
[ -z "$line" ] && continue
[ "${line:0:1}" == "#" ] && continue
[ "${line:0:1}" == "%" ] && cmdR+=("${line:1}") && continue
url=${line#*:}
status=${line%%:*}
rq=$(curl -I -s "$url" -o /dev/null -w "%{http_code}\n")
[ $rq -eq $status ] || _buff="${_buff}${rq} ${url#*//}\n"
done < "${urls}"
if [ -z "$_buff" ]; then
outC="OK!\n$(date +"%d/%m/%Y %T")"
elif [ "$_buff" != "$outC" ]; then
outC="$_buff"
xPsy
fi
echo -e "$outC"
echo -e "$outC" > "$html"
}
xPsy () {
local _buff=""
for ((i = 0; i < ${#cmdR[@]}; i++))
do
_buff="${cmdR[$1]}"
_buff="${_buff/~outC~/$outC}"
_buff="$(eval "$_buff" 2>&1)"
echo "$_buff"
done
}
root="$(cd "$(dirname "$0")"; pwd)"
html="${1:-${root}/status.html}"
urls="${2:-${root}/url.list}"
outC=""
cmdR=()
if [ "$1" == "-h" ]; then
echo -e "usage : $(basename "$0") [<html output path> [<url list path>]]\n"
exit 0
fi
if [ ! -w "${html%/*}" ]; then
echo "Unable to write ${html}"
exit 1
fi
if [ ! -r "${urls}" ]; then
echo "Unable to read ${urls}"
exit 1
fi
while true; do
cerebro
sleep $time
done

@ -0,0 +1,43 @@
package config
import (
"fmt"
"os"
"git.iglou.eu/xavierSrv/tools"
"github.com/BurntSushi/toml"
)
// Global : External global var
var Global Config
// Check : External global var
var Check CheckList
// Error : External global var
var Error ErrorList
// Init : for init config and log
func (co *Config) Init(ch *CheckList, er *ErrorList) {
if _, err := toml.DecodeFile("./examples/etc/xavier-srv/config.toml", &co); err != nil {
panic(err)
}
if _, err := toml.DecodeFile(co.Overall.CheckListFile, &ch); err != nil {
panic(err)
}
if _, err := toml.DecodeFile(co.Overall.ErrorListFile, &er); err != nil {
panic(err)
}
if f, err := os.Create(co.Overall.LogFile); err != nil {
panic(err)
} else {
f.Close()
}
tools.PushToLog(-1, fmt.Errorf(
"=>\n--- [Xavier] ---\nLog file: %v\nApp list file: %v\nErr list file: %v\n---",
co.Overall.LogFile,
co.Overall.CheckListFile,
co.Overall.ErrorListFile,
))
}

@ -0,0 +1,14 @@
package config
// CheckList : Define struct for check list configuration file
type CheckList struct {
Team []struct {
Enable bool `toml:"enable"`
Name string `toml:"name"`
App []struct {
Name string `toml:"name"`
URL string `toml:"url"`
Response int `toml:"response"`
} `toml:"app"`
} `toml:"team"`
}

@ -0,0 +1,19 @@
package config
// Config : Define struct for master configuration file
type Config struct {
Overall struct {
LoopWait int `toml:"loopWait"`
LogFile string `toml:"logFile"`
LogVerbose bool `toml:"logVerbose"`
CheckListFile string `toml:"checkListFile"`
ErrorListFile string `toml:"errorListFile"`
} `toml:"overall"`
HTTP struct {
MaxWait int `toml:"maxWait"`
} `toml:"http"`
Reporting struct {
Local bool `toml:"local"`
Dir string `toml:"dir"`
} `toml:"reporting"`
}

@ -0,0 +1,21 @@
package config
// ErrorList : Define struct for error list configuration file
type ErrorList struct {
Team map[string]struct {
Report []struct {
Process string `toml:"process"`
Encrypt string `toml:"encrypt"`
Host string `toml:"host,omitempty"`
From string `toml:"from,omitempty"`
User string `toml:"user,omitempty"`
Passwd string `toml:"passwd,omitempty"`
Recipients []string `toml:"recipients,omitempty"`
Subject string `toml:"subject,omitempty"`
Body string `toml:"body"`
Methods string `toml:"methods,omitempty"`
URL string `toml:"url,omitempty"`
ContentType string `toml:"Content-Type,omitempty"`
} `toml:"report"`
} `toml:"team"`
}

@ -0,0 +1,17 @@
[[team]]
enable = true
name = 'IglouEu'
[[team.app]]
name = 'iglou web site'
url = 'https://iglou.eu/'
response = 200
[[team.app]]
name = 'iglou false site'
url = 'http://iglou.eu/'
response = 200
[[team]]
enable = false
name = 'test'

@ -0,0 +1,13 @@
[overall]
loopWait = 10 # Second before retry evry thing
logFile = './result.log'
logVerbose = false
checkListFile = './examples/etc/xavier-srv/check.toml'
errorListFile = './examples/etc/xavier-srv/errors.toml'
[http]
maxWait = 120 # Max Microsecond between each request
[reporting]
local = true
dir = './examples/var/www/'

@ -0,0 +1,31 @@
[team.IglouEu]
[[team.IglouEu.report]]
process = 'smtp'
encrypt = 'tls'
host = 'mail.gandi.net:587'
from = 'Professeur Xavier <pxavier@iglou.eu>'
user = ''
passwd = ''
recipients = ['garbage@yopmail.com', 'garbage2@yopmail.com']
subject = '🤯 [WARNING][[%TEAM]] Error on webapp'
body = '''
Some WebAPP from team '[%TEAM]' have failed.
[%ERRORS]
'''
[[team.IglouEu.report]]
process = 'http'
methods = 'POST'
url = 'https://tchat.fr/hooks/'
body = '{"text": "Some WebAPP from `[%TEAM]` have failed.\n\n[%ERRORS]"}'
[[team.IglouEu.report]]
process = 'http'
methods = 'GET'
url = 'https://smsapi.free-mobile.fr/sendmsg?user=&pass=&msg='
body = '''
Some WebAPP from team '[%TEAM]' have failed.
[%ERRORS]
'''

@ -0,0 +1,169 @@
package main
import (
"errors"
"math/rand"
"strings"
"sync"
"time"
"git.iglou.eu/xavierSrv/config"
"git.iglou.eu/xavierSrv/tools"
)
var wg sync.WaitGroup
func main() {
//init:
rand.Seed(42)
config.Global.Init(&config.Check, &config.Error)
tools.InitLogConf(
config.Global.Overall.LogFile,
config.Global.Overall.LogVerbose,
)
//loop:
for index, team := range config.Check.Team {
if team.Enable {
wg.Add(1)
go xTeam(index)
}
}
wg.Wait()
time.Sleep(time.Duration(config.Global.Overall.LoopWait))
//goto loop
}
func xTeam(teamID int) bool {
team := &config.Check.Team[teamID]
var failures [][]string
for _, app := range team.App {
tools.PushToLog(6, errors.New("Checking '"+app.Name+"' ..."))
if _, err := cerebro(app.URL, app.Response); err != nil {
returned := []string{app.Name, err.Error()}
failures = append(failures, returned)
tools.PushToLog(6, err)
}
}
if failures != nil {
xTeamReport(team.Name, failures)
}
defer wg.Done()
return true
}
func xTeamReport(team string, failures [][]string) (bool, error) {
//var message string {"name":"","error":""}
if config.Global.Reporting.Local {
//report.LocalSave(team, report.LocalBuild(failures))
}
// Build message
reportMessage := ""
for i := range failures {
reportMessage += "[" + failures[i][0] + "]" + "\n" +
failures[i][1] + "\n"
}
// for reporting
for _, reportProcess := range config.Error.Team[team].Report {
switch reportProcess.Process {
case "smtp":
subject := strings.ReplaceAll(reportProcess.Subject, "[%TEAM]", team)
message := strings.ReplaceAll(reportProcess.Body, "[%TEAM]", team)
message = strings.ReplaceAll(message, "[%ERRORS]", reportMessage)
tools.SmtpReport(
reportProcess.Host,
reportProcess.Encrypt,
reportProcess.From,
reportProcess.User,
reportProcess.Passwd,
reportProcess.Recipients,
subject, message,
)
case "http":
message := strings.ReplaceAll(reportProcess.Body, "[%TEAM]", team)
if reportProcess.Methods == "POST" {
message = strings.ReplaceAll(message, "[%ERRORS]", tools.JsonEscapeString(reportMessage))
} else {
message = strings.ReplaceAll(message, "[%ERRORS]", reportMessage)
}
tools.HttpReport(reportProcess.Methods, reportProcess.URL, message)
default:
tools.PushToLog(3, errors.New("Unknow '"+reportProcess.Process+"' reporting process"))
}
}
// exec type
/*if config.Global.Reporting.ReportJSON {
jsonFile := "{\"status\":\"error\",\"date\":\""
jsonFile += time.Now().Format(time.RFC3339)
jsonFile += "\",\"listing\":["
i, l := 0, len(failures)
for {
if i%2 == 0 {
jsonFile += "{\"name\":\"" + failures[i] + "\""
} else {
jsonFile += ",\"error\":\"" + failures[i] + "\"},"
}
i++
if i == l {
jsonFile = jsonFile[:len(jsonFile)-1]
break
}
}
jsonFile += "]}"
fmt.Println(jsonFile)
}*/
/*
Si on a une erreur
- on construit le json de la team
- on formate le message de sortie
On boucle sur les reports
- pour les reports smtp on envoi le message
- pour les retapi on export les info format json puis send
Ne pas oublier de replace les balises [%***]
*/
return true, nil
}
func cerebro(url string, need int) (bool, error) {
xPsyAgatha, errAgatha := tools.HttpStatus(url, need, config.Global.HTTP.MaxWait)
xPsyArthur, errArthur := tools.HttpStatus(url, need, config.Global.HTTP.MaxWait)
xPsyDash, errDash := tools.HttpStatus(url, need, config.Global.HTTP.MaxWait)
if (xPsyAgatha && xPsyArthur) ||
(xPsyAgatha && xPsyDash) ||
(xPsyArthur && xPsyDash) {
return true, nil
}
err := errors.New("Inconsistent error returned")
if errAgatha.Error() == errArthur.Error() {
err = errAgatha
} else if errArthur.Error() == errDash.Error() {
err = errArthur
} else if errDash.Error() == errAgatha.Error() {
err = errDash
}
return false, err
}

@ -0,0 +1,57 @@
package tools
import (
"fmt"
"time"
)
var logFile string
var logVerbose bool
// InitLogConf : For init local logFile var
func InitLogConf(f string, v bool) {
logFile = f
logVerbose = v
}
// PushToLog : Log manager
func PushToLog(lvlID int, message error) {
if !logVerbose && lvlID == 6 {
return
}
msgout := ""
lvlName := "??"
switch lvlID {
case -1:
lvlName = "Start"
case 6:
lvlName = "Info"
case 3:
fallthrough
default:
lvlName = "Error"
}
msgout = fmt.Sprintf("[%s] (%s) %s", time.Now().Format(time.RFC3339), lvlName, message)
//logToFile(msgout)
fmt.Println(msgout)
}
/*func logToFile(m string) {
f, err := os.OpenFile(logFile, os.O_APPEND|os.O_WRONLY, 0644)
//err != nil {
// panic(err)
//}
if _, err := fmt.Fprintln(f, m); err != nil {
f.Close()
panic(err)
}
if err := f.Close(); err != nil {
panic(err)
}
}*/

@ -0,0 +1,76 @@
package tools
import (
"bytes"
"errors"
"net/http"
"net/url"
"strconv"
"strings"
"time"
mail "github.com/xhit/go-simple-mail"
)
func HttpReport(m string, u string, d string) {
var res *http.Response
var err error
if m == "POST" {
res, err = http.Post(u, "application/json", bytes.NewBufferString(d))
} else {
res, err = http.Get(u + url.QueryEscape(d))
}
if err != nil {
PushToLog(3, err)
}
if res.StatusCode != 200 {
PushToLog(3, errors.New("Request to `"+u+url.QueryEscape(d)+"` has failed: "+res.Status))
}
}
func SmtpReport(host string, encrypt string, from string, user string, passwd string, recip []string, subject string, message string) {
ht := strings.Split(host, ":")
pt, _ := strconv.Atoi(ht[1])
server := mail.NewSMTPClient()
//SMTP Server
server.Host = ht[0]
server.Port = pt
server.Username = user
server.Password = passwd
server.Authentication = mail.AuthPlain
server.KeepAlive = true
server.ConnectTimeout = 10 * time.Second
server.SendTimeout = 10 * time.Second
smtpClient, err := server.Connect()
switch encrypt {
case "tls":
server.Encryption = mail.EncryptionTLS
case "ssl":
server.Encryption = mail.EncryptionSSL
default:
server.Encryption = mail.EncryptionNone
}
if err != nil {
PushToLog(3, err)
}
for _, to := range recip {
email := mail.NewMSG()
email.SetFrom(from).
AddTo(to).
SetSubject(subject)
email.SetBody(mail.TextPlain, message)
if err = email.Send(smtpClient); err != nil {
PushToLog(3, err)
}
}
}

@ -0,0 +1,47 @@
package tools
import (
"errors"
"math/rand"
"net/http"
"strconv"
"strings"
"time"
)
func JsonEscapeString(d string) string {
r := strings.NewReplacer(
"\\", "\\\\",
"/", "\\/",
"\"", "\\\"",
"\n", "\\n",
"\r", "\\r",
"\t", "\\t",
"\x08", "\\f",
"\x0c", "\\b",
)
return r.Replace(d)
}
func HttpStatus(url string, need int, mwait int) (bool, error) {
time.Sleep(time.Millisecond * time.Duration(rand.Intn(mwait)))
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}}
resp, err := client.Head(url)
if err == nil {
statusCode := (need == resp.StatusCode)
if statusCode {
return true, nil
}
return false, errors.New("Head " + url + ": Response " + strconv.Itoa(resp.StatusCode) + ": Expected status code " + strconv.Itoa(need))
}
return false, err
}
Loading…
Cancel
Save