diff --git a/src/mod/traefik/Dockerfile b/src/mod/traefik/Dockerfile new file mode 100644 index 0000000..2cda75e --- /dev/null +++ b/src/mod/traefik/Dockerfile @@ -0,0 +1,23 @@ +# syntax=docker/dockerfile:1 + +FROM golang:1.20 + +# Set destination for COPY +WORKDIR /app + +# Download Go modules +COPY . ./ +RUN go mod download + +# Build +RUN CGO_ENABLED=0 GOOS=linux go build -o /traefik . + +# Optional: +# To bind to a TCP port, runtime parameters must be supplied to the docker command. +# But we can document in the Dockerfile what ports +# the application is going to listen on by default. +# https://docs.docker.com/engine/reference/builder/#expose +EXPOSE 8080 + +# Run +CMD ["/traefik"] diff --git a/src/mod/traefik/go.mod b/src/mod/traefik/go.mod new file mode 100644 index 0000000..ffb3330 --- /dev/null +++ b/src/mod/traefik/go.mod @@ -0,0 +1,17 @@ +module localhost.com + +go 1.20 + +require github.com/labstack/echo/v4 v4.10.2 + +require ( + github.com/labstack/gommon v0.4.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + golang.org/x/crypto v0.6.0 // indirect + golang.org/x/net v0.7.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect +) diff --git a/src/mod/traefik/go.sum b/src/mod/traefik/go.sum new file mode 100644 index 0000000..9ac683a --- /dev/null +++ b/src/mod/traefik/go.sum @@ -0,0 +1,36 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M= +github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k= +github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= +github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/src/mod/traefik/traefik.go b/src/mod/traefik/traefik.go new file mode 100644 index 0000000..e1b7db6 --- /dev/null +++ b/src/mod/traefik/traefik.go @@ -0,0 +1,352 @@ +package main + +import ( + "net/http" + "os" + "fmt" + "encoding/json" + "io/ioutil" + //"math/rand" + //"strconv" + //"time" + "github.com/labstack/echo/v4" +) + +var traefikhost = os.Getenv("TRAEFIKHOST") + +type Stats struct { + Label string `json:"Label"` + Value uint64 `json:"Value"` +} + +type TableComponent struct { + Id int `json:"id"` + Data any `json:"Data"` +} + +type GraphComponent struct { + Id int `json:"id"` + Graph string `json:"graph,omitempty"` + Title string `json:"title,omitempty"` + Stats []Stats `json:"stats,omitempty"` +} + +type RouterIn struct { + Status string `json:"status"` + TLS struct { + Options string `json:"options"` + CertResolver string `json:"certResolver"` + } `json:"tls"` + Rule string `json:"rule"` + EntryPoints []string `json:"entryPoints"` + Name string `json:"name"` + Service string `json:"service"` + Provider string `json:"provider"` +} + +type RouterOut struct { + Status string `json:"status"` + TLS bool `json:"tls"` + Rule string `json:"rule"` + EntryPoints []string `json:"entryPoints"` + Name string `json:"name"` + Service string `json:"service"` + Provider string `json:"provider"` +} + +type ServiceIn struct { + Status string `json:"status"` + Name string `json:"name"` + Type string `json:"type"` + LoadBalancer struct { + Servers []struct { + URL string `json:"url"` + } `json:"servers"` + } `json:"loadBalancer"` + Provider string `json:"provider"` +} + +type ServiceOut struct { + Status string `json:"status"` + Name string `json:"name"` + Type string `json:"type"` + Servers int `json:"servers"` + Provider string `json:"provider"` +} + +type Middleware struct { + Status string `json:"status"` + Name string `json:"name"` + Type string `json:"type"` + Provider string `json:"provider"` +} + +type Overview struct { + HTTP struct { + Routers struct { + Total uint64 `json:"total"` + Warnings uint64 `json:"warnings"` + Errors uint64 `json:"errors"` + } `json:"routers"` + Services struct { + Total uint64 `json:"total"` + Warnings uint64 `json:"warnings"` + Errors uint64 `json:"errors"` + } `json:"services"` + Middlewares struct { + Total uint64 `json:"total"` + Warnings uint64 `json:"warnings"` + Errors uint64 `json:"errors"` + } `json:"middlewares"` + } `json:"http"` +} + +func getSchema(c echo.Context) error { + routerGraph := []Stats{ + {Label: "Success", Value: 0}, + {Label: "Warnings", Value: 0}, + {Label: "Errors", Value: 0}, + } + + serviceGraph := []Stats{ + {Label: "Success", Value: 0}, + {Label: "Warnings", Value: 0}, + {Label: "Errors", Value: 0}, + } + + middlewareGraph := []Stats{ + {Label: "Success", Value: 0}, + {Label: "Warnings", Value: 0}, + {Label: "Errors", Value: 0}, + } + + graphSchema := []GraphComponent{ + {Id: 1, Graph: "donut", Stats: routerGraph, Title: "Routers"}, + {Id: 2, Graph: "radial-bar", Stats: serviceGraph, Title: "Services"}, + {Id: 3, Graph: "pie", Stats: middlewareGraph, Title: "Middlewares"}, + } + + tableSchema := []TableComponent{ + {Id: 4, Data: getRouterTable()}, + {Id: 5, Data: getServiceTable()}, + {Id: 6, Data: getMiddlewareTable()}, + } + + response := map[string]interface{} { + "graphs": graphSchema, + "tables": tableSchema, + } + + return c.JSON(http.StatusOK, response) +} + +func getRouterGraph(c echo.Context) error { + nulstats := []Stats{ + {Label: "Success", Value: 0}, + {Label: "Warnings", Value: 0}, + {Label: "Errors", Value: 0}, + } + url := fmt.Sprintf("%s/api/overview", traefikhost) + resp, err := http.Get(url) + if err != nil { + fmt.Println("error 1") + return c.JSON(http.StatusOK, nulstats) + } + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Println("error 2") + return c.JSON(http.StatusOK, nulstats) + } + overview := Overview{} + err = json.Unmarshal(body, &overview) + if err != nil { + fmt.Println("error 3") + return c.JSON(http.StatusOK, nulstats) + } + routers := overview.HTTP.Routers + stats := []Stats{ + {Label: "Success", Value: routers.Total - (routers.Warnings + routers.Errors)}, + {Label: "Warnings", Value: routers.Warnings}, + {Label: "Errors", Value: routers.Errors}, + } + response := GraphComponent{Id: 1, Graph: "donut", Stats: stats, Title: "Routers"} + return c.JSON(http.StatusOK, response) +} + +func getServiceGraph(c echo.Context) error { + nulstats := []Stats{ + {Label: "Success", Value: 0}, + {Label: "Warnings", Value: 0}, + {Label: "Errors", Value: 0}, + } + url := fmt.Sprintf("%s/api/overview", traefikhost) + resp, err := http.Get(url) + if err != nil { + fmt.Println("error 1") + return c.JSON(http.StatusOK, nulstats) + } + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Println("error 2") + return c.JSON(http.StatusOK, nulstats) + } + overview := Overview{} + err = json.Unmarshal(body, &overview) + if err != nil { + fmt.Println("error 3") + return c.JSON(http.StatusOK, nulstats) + } + services := overview.HTTP.Services + stats := []Stats{ + {Label: "Success", Value: services.Total - (services.Warnings + services.Errors)}, + {Label: "Warnings", Value: services.Warnings}, + {Label: "Errors", Value: services.Errors}, + } + response := GraphComponent{Id: 2, Graph: "radial-bar", Stats: stats, Title: "Routers"} + return c.JSON(http.StatusOK, response) +} + +func getMiddlewareGraph(c echo.Context) error { + nulstats := []Stats{ + {Label: "Success", Value: 0}, + {Label: "Warnings", Value: 0}, + {Label: "Errors", Value: 0}, + } + url := fmt.Sprintf("%s/api/overview", traefikhost) + resp, err := http.Get(url) + if err != nil { + fmt.Println("error 1") + return c.JSON(http.StatusOK, nulstats) + } + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Println("error 2") + return c.JSON(http.StatusOK, nulstats) + } + overview := Overview{} + err = json.Unmarshal(body, &overview) + if err != nil { + fmt.Println("error 3") + return c.JSON(http.StatusOK, nulstats) + } + middlewares := overview.HTTP.Middlewares + stats := []Stats{ + {Label: "Success", Value: middlewares.Total - (middlewares.Warnings + middlewares.Errors)}, + {Label: "Warnings", Value: middlewares.Warnings}, + {Label: "Errors", Value: middlewares.Errors}, + } + response := GraphComponent{Id: 3, Graph: "pie", Stats: stats, Title: "Routers"} + return c.JSON(http.StatusOK, response) +} + +func getRouterTable() []RouterOut { + response := []RouterIn{} + response2 := []RouterOut{} + + url := fmt.Sprintf("%s/api/http/routers", traefikhost) + resp, err := http.Get(url) + if err != nil { + fmt.Println(err) + return response2 + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Println(err) + return response2 + } + + err = json.Unmarshal(body, &response) + if err != nil { + fmt.Println(err) + return response2 + } + + for _, element := range response { + tmp := RouterOut{} + tmp.Status = element.Status + tmp.Rule = element.Rule + tmp.EntryPoints = element.EntryPoints + tmp.Name = element.Name + tmp.Service = element.Service + tmp.Provider = element.Provider + if element.TLS.CertResolver != "" { + tmp.TLS = true + } else { + tmp.TLS = false + } + response2 = append(response2, tmp) + } + + return response2 +} + +func getServiceTable() []ServiceOut { + response := []ServiceIn{} + response2 := []ServiceOut{} + + url := fmt.Sprintf("%s/api/http/services", traefikhost) + resp, err := http.Get(url) + if err != nil { + fmt.Println(err) + return response2 + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Println(err) + return response2 + } + + err = json.Unmarshal(body, &response) + if err != nil { + fmt.Println(err) + return response2 + } + + for _, element := range response { + tmp := ServiceOut{} + tmp.Status = element.Status + tmp.Name = element.Name + tmp.Type = element.Type + tmp.Provider = element.Provider + tmp.Servers = len(element.LoadBalancer.Servers) + response2 = append(response2, tmp) + } + + return response2 +} + +func getMiddlewareTable() []Middleware { + response := []Middleware{} + + url := fmt.Sprintf("%s/api/http/middlewares", traefikhost) + resp, err := http.Get(url) + if err != nil { + fmt.Println(err) + return response + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Println(err) + return response + } + + err = json.Unmarshal(body, &response) + if err != nil { + fmt.Println(err) + return response + } + + return response +} + +func main() { + e := echo.New() + e.GET("/schema", getSchema) + e.GET("/component/1", getRouterGraph) + e.GET("/component/2", getServiceGraph) + e.GET("/component/3", getMiddlewareGraph) + e.Logger.Fatal(e.Start(":8080")) +}