libs/go-kibana-rest/kbapi/api.kibana_synthetics.go (400 lines of code) (raw):
package kbapi
import (
"context"
"encoding/json"
"fmt"
"sync"
"time"
"github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
)
const (
basePathKibanaSynthetics = "/api/synthetics"
privateLocationsSuffix = "/private_locations"
monitorsSuffix = "/monitors"
Http MonitorType = "http"
Tcp MonitorType = "tcp"
Icmp MonitorType = "icmp"
Browser MonitorType = "browser"
Every1Minute MonitorSchedule = 1
Every2Minutes = 2
Every3Minutes = 3
Every5Minutes = 5
Every10Minutes = 10
Every15Minutes = 15
Every20Minutes = 20
Every30Minutes = 30
Every60Minutes = 60
Every120Minutes = 120
Every240Minutes = 240
Japan MonitorLocation = "japan"
India = "india"
Singapore = "singapore"
AustraliaEast = "australia_east"
UnitedKingdom = "united_kingdom"
Germany = "germany"
CanadaEast = "canada_east"
Brazil = "brazil"
USEast = "us_east"
USWest = "us_west"
ModeAll HttpMonitorMode = "all"
ModeAny = "any"
ScreenshotOn ScreenshotOption = "on"
ScreenshotOff = "off"
ScreenshotOnlyOfFailures = "only-on-failure"
)
var plMu sync.Mutex
type MonitorFields interface {
APIRequest(cfg SyntheticsMonitorConfig) interface{}
}
type KibanaError struct {
Code int `json:"statusCode,omitempty"`
Error string `json:"error,omitempty"`
Message string `json:"message,omitempty"`
}
type MonitorID string
type MonitorType string
type MonitorLocation string
type MonitorSchedule int
type HttpMonitorMode string
type ScreenshotOption string
type JsonObject map[string]interface{}
type KibanaSyntheticsMonitorAPI struct {
Add KibanaSyntheticsMonitorAdd
Delete KibanaSyntheticsMonitorDelete
Get KibanaSyntheticsMonitorGet
Update KibanaSyntheticsMonitorUpdate
}
type KibanaSyntheticsPrivateLocationAPI struct {
Create KibanaSyntheticsPrivateLocationCreate
Delete KibanaSyntheticsPrivateLocationDelete
Get KibanaSyntheticsPrivateLocationGet
}
type SyntheticsStatusConfig struct {
Enabled *bool `json:"enabled,omitempty"`
}
type MonitorAlertConfig struct {
Status *SyntheticsStatusConfig `json:"status,omitempty"`
Tls *SyntheticsStatusConfig `json:"tls,omitempty"`
}
type ICMPMonitorFields struct {
Host string `json:"host"`
Wait string `json:"wait,omitempty"`
}
type BrowserMonitorFields struct {
InlineScript string `json:"inline_script"`
Screenshots ScreenshotOption `json:"screenshots,omitempty"`
SyntheticsArgs []string `json:"synthetics_args,omitempty"`
IgnoreHttpsErrors *bool `json:"ignore_https_errors,omitempty"`
PlaywrightOptions JsonObject `json:"playwright_options,omitempty"`
}
type TCPMonitorFields struct {
Host string `json:"host"`
Ssl *SSLConfig `json:"ssl,omitempty"`
CheckSend string `json:"check.send,omitempty"`
CheckReceive string `json:"check.receive,omitempty"`
ProxyUrl string `json:"proxy_url,omitempty"`
ProxyUseLocalResolver *bool `json:"proxy_use_local_resolver,omitempty"`
}
type SSLConfig struct {
VerificationMode string `json:"verification_mode,omitempty"`
SupportedProtocols []string `json:"supported_protocols,omitempty"`
CertificateAuthorities []string `json:"certificate_authorities,omitempty"`
Certificate string `json:"certificate,omitempty"`
Key string `json:"key,omitempty"`
KeyPassphrase string `json:"key_passphrase,omitempty"`
}
type HTTPMonitorFields struct {
Url string `json:"url"`
Ssl *SSLConfig `json:"ssl,omitempty"`
MaxRedirects string `json:"max_redirects,omitempty"`
Mode HttpMonitorMode `json:"mode,omitempty"`
Ipv4 *bool `json:"ipv4,omitempty"`
Ipv6 *bool `json:"ipv6,omitempty"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
ProxyHeader JsonObject `json:"proxy_headers,omitempty"`
ProxyUrl string `json:"proxy_url,omitempty"`
Response JsonObject `json:"response,omitempty"`
Check JsonObject `json:"check,omitempty"`
}
type SyntheticsMonitorConfig struct {
Name string `json:"name"`
Schedule MonitorSchedule `json:"schedule,omitempty"`
Locations []MonitorLocation `json:"locations,omitempty"`
PrivateLocations []string `json:"private_locations,omitempty"`
Enabled *bool `json:"enabled,omitempty"`
Tags []string `json:"tags,omitempty"`
Alert *MonitorAlertConfig `json:"alert,omitempty"`
APMServiceName string `json:"service.name,omitempty"`
TimeoutSeconds int `json:"timeout,omitempty"`
Namespace string `json:"namespace,omitempty"`
Params JsonObject `json:"params,omitempty"`
RetestOnFailure *bool `json:"retest_on_failure,omitempty"`
}
type MonitorScheduleConfig struct {
Number string `json:"number"`
Unit string `json:"unit"`
}
type SyntheticGeoConfig struct {
Lat float64 `json:"lat"`
Lon float64 `json:"lon"`
}
type MonitorLocationConfig struct {
Id string `json:"id"`
Label string `json:"label"`
Geo *SyntheticGeoConfig `json:"geo,omitempty"`
IsServiceManaged bool `json:"isServiceManaged"`
}
type PrivateLocationConfig struct {
Label string `json:"label"`
AgentPolicyId string `json:"agentPolicyId"`
Tags []string `json:"tags,omitempty"`
Geo *SyntheticGeoConfig `json:"geo,omitempty"`
}
type PrivateLocation struct {
Id string `json:"id"`
Namespace string `json:"namespace,omitempty"`
PrivateLocationConfig
}
type MonitorDeleteStatus struct {
Id MonitorID `json:"id"`
Deleted bool `json:"deleted"`
}
type SyntheticsMonitor struct {
Name string `json:"name"`
Type MonitorType `json:"type"`
ConfigId MonitorID `json:"config_id"`
Id MonitorID `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Namespace string `json:"namespace"`
Enabled *bool `json:"enabled,omitempty"`
Alert *MonitorAlertConfig `json:"alert,omitempty"`
Schedule *MonitorScheduleConfig `json:"schedule,omitempty"`
Tags []string `json:"tags,omitempty"`
APMServiceName string `json:"service.name,omitempty"`
Timeout json.Number `json:"timeout,omitempty"`
Locations []MonitorLocationConfig `json:"locations,omitempty"`
Origin string `json:"origin,omitempty"`
Params JsonObject `json:"params,omitempty"`
MaxAttempts int `json:"max_attempts"`
Revision int `json:"revision,omitempty"`
Ui JsonObject `json:"__ui,omitempty"`
//http
Url string `json:"url,omitempty"`
Mode HttpMonitorMode `json:"mode"`
MaxRedirects string `json:"max_redirects"`
Ipv4 *bool `json:"ipv4,omitempty"`
Ipv6 *bool `json:"ipv6,omitempty"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
ProxyHeaders JsonObject `json:"proxy_headers,omitempty"`
CheckResponseBodyPositive []string `json:"check.response.body.positive,omitempty"`
CheckResponseStatus []string `json:"check.response.status,omitempty"`
ResponseIncludeBody string `json:"response.include_body,omitempty"`
ResponseIncludeHeaders bool `json:"response.include_headers,omitempty"`
ResponseIncludeBodyMaxBytes string `json:"response.include_body_max_bytes,omitempty"`
CheckRequestBody JsonObject `json:"check.request.body,omitempty"`
CheckRequestHeaders JsonObject `json:"check.request.headers,omitempty"`
CheckRequestMethod string `json:"check.request.method,omitempty"`
//http and tcp
ProxyUrl string `json:"proxy_url,omitempty"`
SslVerificationMode string `json:"ssl.verification_mode"`
SslSupportedProtocols []string `json:"ssl.supported_protocols"`
SslCertificateAuthorities []string `json:"ssl.certificate_authorities,omitempty"`
SslCertificate string `json:"ssl.certificate,omitempty"`
SslKey string `json:"ssl.key,omitempty"`
SslKeyPassphrase string `json:"ssl.key_passphrase,omitempty"`
//tcp and icmp
Host string `json:"host,omitempty"`
//tcp
ProxyUseLocalResolver *bool `json:"proxy_use_local_resolver,omitempty"`
CheckSend string `json:"check.send,omitempty"`
CheckReceive string `json:"check.receive,omitempty"`
//icmp
Wait json.Number `json:"wait,omitempty"`
//browser
Screenshots string `json:"screenshots,omitempty"`
IgnoreHttpsErrors *bool `json:"ignore_https_errors,omitempty"`
InlineScript string `json:"inline_script"`
SyntheticsArgs []string `json:"synthetics_args,omitempty"`
PlaywrightOptions JsonObject `json:"playwright_options,omitempty"`
}
type MonitorTypeConfig struct {
Type MonitorType `json:"type"`
}
func (f HTTPMonitorFields) APIRequest(config SyntheticsMonitorConfig) interface{} {
mType := MonitorTypeConfig{Type: Http}
return struct {
SyntheticsMonitorConfig
MonitorTypeConfig
HTTPMonitorFields
}{
config,
mType,
f,
}
}
func (f TCPMonitorFields) APIRequest(config SyntheticsMonitorConfig) interface{} {
mType := MonitorTypeConfig{Type: Tcp}
return struct {
SyntheticsMonitorConfig
MonitorTypeConfig
TCPMonitorFields
}{
config,
mType,
f,
}
}
func (f ICMPMonitorFields) APIRequest(config SyntheticsMonitorConfig) interface{} {
mType := MonitorTypeConfig{Type: Icmp}
return struct {
SyntheticsMonitorConfig
MonitorTypeConfig
ICMPMonitorFields
}{
config,
mType,
f,
}
}
func (f BrowserMonitorFields) APIRequest(config SyntheticsMonitorConfig) interface{} {
mType := MonitorTypeConfig{Type: Browser}
return struct {
SyntheticsMonitorConfig
MonitorTypeConfig
BrowserMonitorFields
}{
config,
mType,
f,
}
}
type KibanaSyntheticsMonitorAdd func(ctx context.Context, config SyntheticsMonitorConfig, fields MonitorFields, namespace string) (*SyntheticsMonitor, error)
type KibanaSyntheticsMonitorUpdate func(ctx context.Context, id MonitorID, config SyntheticsMonitorConfig, fields MonitorFields, namespace string) (*SyntheticsMonitor, error)
type KibanaSyntheticsMonitorGet func(ctx context.Context, id MonitorID, namespace string) (*SyntheticsMonitor, error)
type KibanaSyntheticsMonitorDelete func(ctx context.Context, namespace string, ids ...MonitorID) ([]MonitorDeleteStatus, error)
type KibanaSyntheticsPrivateLocationCreate func(ctx context.Context, pLoc PrivateLocationConfig) (*PrivateLocation, error)
type KibanaSyntheticsPrivateLocationGet func(ctx context.Context, idOrLabel string) (*PrivateLocation, error)
type KibanaSyntheticsPrivateLocationDelete func(ctx context.Context, id string) error
func newKibanaSyntheticsPrivateLocationGetFunc(c *resty.Client) KibanaSyntheticsPrivateLocationGet {
return func(ctx context.Context, idOrLabel string) (*PrivateLocation, error) {
if idOrLabel == "" {
return nil, APIError{
Code: 404,
Message: "Private location id or label is empty",
}
}
path := basePathWithId("", privateLocationsSuffix, idOrLabel)
log.Debugf("URL to get private locations: %s", path)
resp, err := c.R().SetContext(ctx).Get(path)
if err = handleKibanaError(err, resp); err != nil {
return nil, err
}
return unmarshal(resp, PrivateLocation{})
}
}
func newKibanaSyntheticsPrivateLocationCreateFunc(c *resty.Client) KibanaSyntheticsPrivateLocationCreate {
return func(ctx context.Context, pLoc PrivateLocationConfig) (*PrivateLocation, error) {
plMu.Lock()
defer plMu.Unlock()
path := basePath("", privateLocationsSuffix)
log.Debugf("URL to create private locations: %s", path)
resp, err := c.R().SetContext(ctx).SetBody(pLoc).Post(path)
if err = handleKibanaError(err, resp); err != nil {
return nil, err
}
return unmarshal(resp, PrivateLocation{})
}
}
func newKibanaSyntheticsPrivateLocationDeleteFunc(c *resty.Client) KibanaSyntheticsPrivateLocationDelete {
return func(ctx context.Context, id string) error {
plMu.Lock()
defer plMu.Unlock()
path := basePathWithId("", privateLocationsSuffix, id)
log.Debugf("URL to delete private locations: %s", path)
resp, err := c.R().SetContext(ctx).Delete(path)
err = handleKibanaError(err, resp)
return err
}
}
func newKibanaSyntheticsMonitorGetFunc(c *resty.Client) KibanaSyntheticsMonitorGet {
return func(ctx context.Context, id MonitorID, namespace string) (*SyntheticsMonitor, error) {
path := basePathWithId(namespace, monitorsSuffix, id)
log.Debugf("URL to get monitor: %s", path)
resp, err := c.R().SetContext(ctx).Get(path)
if err := handleKibanaError(err, resp); err != nil {
return nil, err
}
return unmarshal(resp, SyntheticsMonitor{})
}
}
func newKibanaSyntheticsMonitorDeleteFunc(c *resty.Client) KibanaSyntheticsMonitorDelete {
return func(ctx context.Context, namespace string, ids ...MonitorID) ([]MonitorDeleteStatus, error) {
path := basePath(namespace, monitorsSuffix)
log.Debugf("URL to delete monitors: %s", path)
resp, err := c.R().SetContext(ctx).SetBody(map[string]interface{}{
"ids": ids,
}).Delete(path)
if err = handleKibanaError(err, resp); err != nil {
return nil, err
}
result, err := unmarshal(resp, []MonitorDeleteStatus{})
return *result, err
}
}
func newKibanaSyntheticsMonitorUpdateFunc(c *resty.Client) KibanaSyntheticsMonitorUpdate {
return func(ctx context.Context, id MonitorID, config SyntheticsMonitorConfig, fields MonitorFields, namespace string) (*SyntheticsMonitor, error) {
path := basePathWithId(namespace, monitorsSuffix, id)
log.Debugf("URL to update monitor: %s", path)
data := fields.APIRequest(config)
resp, err := c.R().SetContext(ctx).SetBody(data).Put(path)
if err := handleKibanaError(err, resp); err != nil {
return nil, err
}
return unmarshal(resp, SyntheticsMonitor{})
}
}
func newKibanaSyntheticsMonitorAddFunc(c *resty.Client) KibanaSyntheticsMonitorAdd {
return func(ctx context.Context, config SyntheticsMonitorConfig, fields MonitorFields, namespace string) (*SyntheticsMonitor, error) {
path := basePath(namespace, monitorsSuffix)
log.Debugf("URL to create monitor: %s", path)
data := fields.APIRequest(config)
resp, err := c.R().SetContext(ctx).SetBody(data).Post(path)
if err := handleKibanaError(err, resp); err != nil {
return nil, err
}
return unmarshal(resp, SyntheticsMonitor{})
}
}
func unmarshal[T interface{}](resp *resty.Response, result T) (*T, error) {
respBody := resp.Body()
err := json.Unmarshal(respBody, &result)
if err != nil {
return nil, err
}
return &result, nil
}
func handleKibanaError(err error, resp *resty.Response) error {
if err != nil {
return err
}
log.Debug("Response: ", resp)
if resp.StatusCode() >= 300 {
kibanaErr := KibanaError{}
err := json.Unmarshal(resp.Body(), &kibanaErr)
if err != nil {
return NewAPIError(resp.StatusCode(), resp.Status(), err)
}
return NewAPIError(resp.StatusCode(), kibanaErr.Message, kibanaErr.Error)
}
return nil
}
func basePathWithId(namespace, suffix string, id any) string {
return fmt.Sprintf("%s/%s", basePath(namespace, suffix), id)
}
func basePath(namespace, suffix string) string {
return namespaceBasesPath(namespace, basePathKibanaSynthetics, suffix)
}
func namespaceBasesPath(namespace, basePath, suffix string) string {
if namespace == "" || namespace == "default" {
return fmt.Sprintf("%s%s", basePath, suffix)
}
return fmt.Sprintf("/s/%s%s%s", namespace, basePath, suffix)
}