mirror of
https://github.com/minio/minio.git
synced 2024-12-26 23:25:54 -05:00
069432566f
Signed-off-by: Harshavardhana <harsha@minio.io>
268 lines
8.6 KiB
Go
268 lines
8.6 KiB
Go
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
//
|
|
// This file is part of MinIO Object Storage stack
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package main
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"time"
|
|
|
|
jwtgo "github.com/dgrijalva/jwt-go"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
const (
|
|
pass = "PASS" // Indicate that a test passed
|
|
fail = "FAIL" // Indicate that a test failed
|
|
livenessPath = "/minio/health/live"
|
|
readinessPath = "/minio/health/ready"
|
|
prometheusPath = "/minio/prometheus/metrics"
|
|
prometheusPathV2 = "/minio/v2/metrics/cluster"
|
|
timeout = time.Duration(30 * time.Second)
|
|
)
|
|
|
|
type mintJSONFormatter struct {
|
|
}
|
|
|
|
func (f *mintJSONFormatter) Format(entry *log.Entry) ([]byte, error) {
|
|
data := make(log.Fields, len(entry.Data))
|
|
for k, v := range entry.Data {
|
|
switch v := v.(type) {
|
|
case error:
|
|
// Otherwise errors are ignored by `encoding/json`
|
|
// https://github.com/sirupsen/logrus/issues/137
|
|
data[k] = v.Error()
|
|
default:
|
|
data[k] = v
|
|
}
|
|
}
|
|
|
|
serialized, err := json.Marshal(data)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to marshal fields to JSON, %w", err)
|
|
}
|
|
return append(serialized, '\n'), nil
|
|
}
|
|
|
|
// log successful test runs
|
|
func successLogger(function string, args map[string]interface{}, startTime time.Time) *log.Entry {
|
|
// calculate the test case duration
|
|
duration := time.Since(startTime)
|
|
// log with the fields as per mint
|
|
fields := log.Fields{"name": "healthcheck", "function": function, "args": args, "duration": duration.Nanoseconds() / 1000000, "status": pass}
|
|
return log.WithFields(fields)
|
|
}
|
|
|
|
// log failed test runs
|
|
func failureLog(function string, args map[string]interface{}, startTime time.Time, alert string, message string, err error) *log.Entry {
|
|
// calculate the test case duration
|
|
duration := time.Since(startTime)
|
|
var fields log.Fields
|
|
// log with the fields as per mint
|
|
if err != nil {
|
|
fields = log.Fields{"name": "healthcheck", "function": function, "args": args,
|
|
"duration": duration.Nanoseconds() / 1000000, "status": fail, "alert": alert, "message": message, "error": err}
|
|
} else {
|
|
fields = log.Fields{"name": "healthcheck", "function": function, "args": args,
|
|
"duration": duration.Nanoseconds() / 1000000, "status": fail, "alert": alert, "message": message}
|
|
}
|
|
return log.WithFields(fields)
|
|
}
|
|
|
|
func testLivenessEndpoint(endpoint string) {
|
|
startTime := time.Now()
|
|
function := "testLivenessEndpoint"
|
|
|
|
u, err := url.Parse(fmt.Sprintf("%s%s", endpoint, livenessPath))
|
|
if err != nil {
|
|
// Could not parse URL successfully
|
|
failureLog(function, nil, startTime, "", "URL Parsing for Healthcheck Liveness handler failed", err).Fatal()
|
|
}
|
|
|
|
tr := &http.Transport{
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: u.Scheme == "https"},
|
|
}
|
|
client := &http.Client{Transport: tr, Timeout: timeout}
|
|
resp, err := client.Get(u.String())
|
|
if err != nil {
|
|
// GET request errored
|
|
failureLog(function, nil, startTime, "", "GET request failed", err).Fatal()
|
|
}
|
|
if resp.StatusCode != http.StatusOK {
|
|
// Status not 200 OK
|
|
failureLog(function, nil, startTime, "", fmt.Sprintf("GET /minio/health/live returned %s", resp.Status), err).Fatal()
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
defer successLogger(function, nil, startTime).Info()
|
|
}
|
|
|
|
func testReadinessEndpoint(endpoint string) {
|
|
startTime := time.Now()
|
|
function := "testReadinessEndpoint"
|
|
|
|
u, err := url.Parse(fmt.Sprintf("%s%s", endpoint, readinessPath))
|
|
if err != nil {
|
|
// Could not parse URL successfully
|
|
failureLog(function, nil, startTime, "", "URL Parsing for Healthcheck Readiness handler failed", err).Fatal()
|
|
}
|
|
|
|
tr := &http.Transport{
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: u.Scheme == "https"},
|
|
}
|
|
client := &http.Client{Transport: tr, Timeout: timeout}
|
|
resp, err := client.Get(u.String())
|
|
if err != nil {
|
|
// GET request errored
|
|
failureLog(function, nil, startTime, "", "GET request to Readiness endpoint failed", err).Fatal()
|
|
}
|
|
if resp.StatusCode != http.StatusOK {
|
|
// Status not 200 OK
|
|
failureLog(function, nil, startTime, "", "GET /minio/health/ready returned non OK status", err).Fatal()
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
defer successLogger(function, nil, startTime).Info()
|
|
}
|
|
|
|
const (
|
|
defaultPrometheusJWTExpiry = 100 * 365 * 24 * time.Hour
|
|
)
|
|
|
|
func testPrometheusEndpoint(endpoint string) {
|
|
startTime := time.Now()
|
|
function := "testPrometheusEndpoint"
|
|
|
|
u, err := url.Parse(fmt.Sprintf("%s%s", endpoint, prometheusPath))
|
|
if err != nil {
|
|
// Could not parse URL successfully
|
|
failureLog(function, nil, startTime, "", "URL Parsing for Healthcheck Prometheus handler failed", err).Fatal()
|
|
}
|
|
|
|
jwt := jwtgo.NewWithClaims(jwtgo.SigningMethodHS512, jwtgo.StandardClaims{
|
|
ExpiresAt: time.Now().UTC().Add(defaultPrometheusJWTExpiry).Unix(),
|
|
Subject: os.Getenv("ACCESS_KEY"),
|
|
Issuer: "prometheus",
|
|
})
|
|
|
|
token, err := jwt.SignedString([]byte(os.Getenv("SECRET_KEY")))
|
|
if err != nil {
|
|
failureLog(function, nil, startTime, "", "jwt generation failed", err).Fatal()
|
|
}
|
|
|
|
tr := &http.Transport{
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: u.Scheme == "https"},
|
|
}
|
|
client := &http.Client{Transport: tr, Timeout: timeout}
|
|
|
|
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
|
if err != nil {
|
|
failureLog(function, nil, startTime, "", "Initializing GET request to Prometheus endpoint failed", err).Fatal()
|
|
}
|
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
// GET request errored
|
|
failureLog(function, nil, startTime, "", "GET request to Prometheus endpoint failed", err).Fatal()
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
// Status not 200 OK
|
|
failureLog(function, nil, startTime, "", "GET /minio/prometheus/metrics returned non OK status", err).Fatal()
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
defer successLogger(function, nil, startTime).Info()
|
|
}
|
|
|
|
func testPrometheusEndpointV2(endpoint string) {
|
|
startTime := time.Now()
|
|
function := "testPrometheusEndpoint"
|
|
|
|
u, err := url.Parse(fmt.Sprintf("%s%s", endpoint, prometheusPathV2))
|
|
if err != nil {
|
|
// Could not parse URL successfully
|
|
failureLog(function, nil, startTime, "", "URL Parsing for Healthcheck Prometheus handler failed", err).Fatal()
|
|
}
|
|
|
|
jwt := jwtgo.NewWithClaims(jwtgo.SigningMethodHS512, jwtgo.StandardClaims{
|
|
ExpiresAt: time.Now().UTC().Add(defaultPrometheusJWTExpiry).Unix(),
|
|
Subject: os.Getenv("ACCESS_KEY"),
|
|
Issuer: "prometheus",
|
|
})
|
|
|
|
token, err := jwt.SignedString([]byte(os.Getenv("SECRET_KEY")))
|
|
if err != nil {
|
|
failureLog(function, nil, startTime, "", "jwt generation failed", err).Fatal()
|
|
}
|
|
|
|
tr := &http.Transport{
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: u.Scheme == "https"},
|
|
}
|
|
client := &http.Client{Transport: tr, Timeout: timeout}
|
|
|
|
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
|
if err != nil {
|
|
failureLog(function, nil, startTime, "", "Initializing GET request to Prometheus endpoint failed", err).Fatal()
|
|
}
|
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
// GET request errored
|
|
failureLog(function, nil, startTime, "", "GET request to Prometheus endpoint failed", err).Fatal()
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
// Status not 200 OK
|
|
failureLog(function, nil, startTime, "", "GET /minio/prometheus/metrics returned non OK status", err).Fatal()
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
defer successLogger(function, nil, startTime).Info()
|
|
}
|
|
|
|
func main() {
|
|
endpoint := os.Getenv("SERVER_ENDPOINT")
|
|
secure := os.Getenv("ENABLE_HTTPS")
|
|
if secure == "1" {
|
|
endpoint = "https://" + endpoint
|
|
} else {
|
|
endpoint = "http://" + endpoint
|
|
}
|
|
|
|
// Output to stdout instead of the default stderr
|
|
log.SetOutput(os.Stdout)
|
|
// create custom formatter
|
|
mintFormatter := mintJSONFormatter{}
|
|
// set custom formatter
|
|
log.SetFormatter(&mintFormatter)
|
|
// log Info or above -- success cases are Info level, failures are Fatal level
|
|
log.SetLevel(log.InfoLevel)
|
|
// execute tests
|
|
testLivenessEndpoint(endpoint)
|
|
testReadinessEndpoint(endpoint)
|
|
testPrometheusEndpoint(endpoint)
|
|
testPrometheusEndpointV2(endpoint)
|
|
}
|