366 lines
11 KiB
Go
366 lines
11 KiB
Go
|
/*
|
||
|
Copyright 2015 OpsGenie. All rights reserved.
|
||
|
Use of this source code is governed by a Apache Software
|
||
|
license that can be found in the LICENSE file.
|
||
|
*/
|
||
|
|
||
|
//Package client provides clients for using the OpsGenie Web API. Also prepares and sends requests.
|
||
|
//API user first creates a OpsGenieClient instance.
|
||
|
//
|
||
|
//cli := new(ogcli.OpsGenieClient)
|
||
|
//
|
||
|
//Following that he/she can set APIKey and some configurations for HTTP communication layer by setting
|
||
|
//a proxy definition and/or transport layer options.
|
||
|
//
|
||
|
//cli.SetAPIKey(constants.APIKey)
|
||
|
//
|
||
|
//Then create the client of the API type that he/she wants to use.
|
||
|
//
|
||
|
//alertCli, cliErr := cli.Alert()
|
||
|
//
|
||
|
//if cliErr != nil {
|
||
|
//panic(cliErr)
|
||
|
//}
|
||
|
//
|
||
|
//The most fundamental and general use case is being able to access the
|
||
|
//OpsGenie Web API by coding a Go program.
|
||
|
//The program -by mean of a client application- can send OpsGenie Web API
|
||
|
//the requests using the 'client' package in a higher level. For the programmer
|
||
|
//of the client application, that reduces the number of LoCs.
|
||
|
//Besides it will result a less error-prone application and reduce
|
||
|
//the complexity by hiding the low-level networking, error-handling and
|
||
|
//byte-processing calls.
|
||
|
package client
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"runtime"
|
||
|
"time"
|
||
|
|
||
|
"github.com/franela/goreq"
|
||
|
goquery "github.com/google/go-querystring/query"
|
||
|
"github.com/opsgenie/opsgenie-go-sdk/logging"
|
||
|
)
|
||
|
|
||
|
// endpointURL is the base URL of OpsGenie Web API.
|
||
|
var endpointURL = "https://api.opsgenie.com"
|
||
|
|
||
|
const (
|
||
|
defaultConnectionTimeout time.Duration = 30 * time.Second
|
||
|
defaultRequestTimeout time.Duration = 60 * time.Second
|
||
|
defaultMaxRetryAttempts int = 5
|
||
|
timeSleepBetweenRequests time.Duration = 500 * time.Millisecond
|
||
|
)
|
||
|
|
||
|
// RequestHeaderUserAgent contains User-Agent values tool/version (OS;GO_Version;language).
|
||
|
type requestHeaderUserAgent struct {
|
||
|
sdkName string
|
||
|
version string
|
||
|
os string
|
||
|
goVersion string
|
||
|
timezone string
|
||
|
}
|
||
|
|
||
|
// ToString formats and returns RequestHeaderUserAgent type's fields as string.
|
||
|
func (p requestHeaderUserAgent) ToString() string {
|
||
|
return fmt.Sprintf("%s/%s (%s;%s;%s)", p.sdkName, p.version, p.os, p.goVersion, p.timezone)
|
||
|
}
|
||
|
|
||
|
var userAgentParam requestHeaderUserAgent
|
||
|
|
||
|
/*
|
||
|
OpsGenieClient is a general data type used for:
|
||
|
- authenticating callers through their API keys and
|
||
|
- instantiating "alert", "heartbeat", "integration" and "policy" clients
|
||
|
- setting HTTP transport layer configurations
|
||
|
- setting Proxy configurations
|
||
|
*/
|
||
|
type OpsGenieClient struct {
|
||
|
proxy *ProxyConfiguration
|
||
|
httpTransportSettings *HTTPTransportSettings
|
||
|
apiKey string
|
||
|
opsGenieAPIURL string
|
||
|
}
|
||
|
|
||
|
// SetProxyConfiguration sets proxy configurations of the OpsGenieClient.
|
||
|
func (cli *OpsGenieClient) SetProxyConfiguration(conf *ProxyConfiguration) {
|
||
|
cli.proxy = conf
|
||
|
}
|
||
|
|
||
|
// SetHTTPTransportSettings sets HTTP transport layer configurations of the OpsGenieClient.
|
||
|
func (cli *OpsGenieClient) SetHTTPTransportSettings(settings *HTTPTransportSettings) {
|
||
|
cli.httpTransportSettings = settings
|
||
|
}
|
||
|
|
||
|
// SetAPIKey sets API Key of the OpsGenieClient and authenticates callers through the API Key at OpsGenie.
|
||
|
func (cli *OpsGenieClient) SetAPIKey(key string) {
|
||
|
cli.apiKey = key
|
||
|
}
|
||
|
|
||
|
// SetOpsGenieAPIUrl sets the endpoint(base URL) that requests will send. It can be used for testing purpose.
|
||
|
func (cli *OpsGenieClient) SetOpsGenieAPIUrl(url string) {
|
||
|
if url != "" {
|
||
|
cli.opsGenieAPIURL = url
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// OpsGenieAPIUrl returns the current endpoint(base URL) that requests will send.
|
||
|
func (cli *OpsGenieClient) OpsGenieAPIUrl() string {
|
||
|
if cli.opsGenieAPIURL == "" {
|
||
|
cli.opsGenieAPIURL = endpointURL
|
||
|
}
|
||
|
return cli.opsGenieAPIURL
|
||
|
}
|
||
|
|
||
|
// APIKey returns the API Key value that OpsGenieClient uses to authenticate at OpsGenie.
|
||
|
func (cli *OpsGenieClient) APIKey() string {
|
||
|
return cli.apiKey
|
||
|
}
|
||
|
|
||
|
// makeHTTPTransportSettings internal method to set default values of HTTP transport layer configuration if necessary.
|
||
|
func (cli *OpsGenieClient) makeHTTPTransportSettings() {
|
||
|
if cli.httpTransportSettings != nil {
|
||
|
if cli.httpTransportSettings.MaxRetryAttempts <= 0 {
|
||
|
cli.httpTransportSettings.MaxRetryAttempts = defaultMaxRetryAttempts
|
||
|
}
|
||
|
if cli.httpTransportSettings.ConnectionTimeout <= 0 {
|
||
|
cli.httpTransportSettings.ConnectionTimeout = defaultConnectionTimeout
|
||
|
}
|
||
|
if cli.httpTransportSettings.RequestTimeout <= 0 {
|
||
|
cli.httpTransportSettings.RequestTimeout = defaultRequestTimeout
|
||
|
}
|
||
|
} else {
|
||
|
cli.httpTransportSettings = &HTTPTransportSettings{MaxRetryAttempts: defaultMaxRetryAttempts, ConnectionTimeout: defaultConnectionTimeout, RequestTimeout: defaultRequestTimeout}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Alert instantiates a new OpsGenieAlertClient.
|
||
|
func (cli *OpsGenieClient) Alert() (*OpsGenieAlertClient, error) {
|
||
|
cli.makeHTTPTransportSettings()
|
||
|
|
||
|
alertClient := new(OpsGenieAlertClient)
|
||
|
alertClient.SetOpsGenieClient(*cli)
|
||
|
|
||
|
if cli.opsGenieAPIURL == "" {
|
||
|
alertClient.SetOpsGenieAPIUrl(endpointURL)
|
||
|
}
|
||
|
|
||
|
return alertClient, nil
|
||
|
}
|
||
|
|
||
|
// Heartbeat instantiates a new OpsGenieHeartbeatClient.
|
||
|
func (cli *OpsGenieClient) Heartbeat() (*OpsGenieHeartbeatClient, error) {
|
||
|
cli.makeHTTPTransportSettings()
|
||
|
|
||
|
heartbeatClient := new(OpsGenieHeartbeatClient)
|
||
|
heartbeatClient.SetOpsGenieClient(*cli)
|
||
|
|
||
|
if cli.opsGenieAPIURL == "" {
|
||
|
heartbeatClient.SetOpsGenieAPIUrl(endpointURL)
|
||
|
}
|
||
|
|
||
|
return heartbeatClient, nil
|
||
|
}
|
||
|
|
||
|
// Integration instantiates a new OpsGenieIntegrationClient.
|
||
|
func (cli *OpsGenieClient) Integration() (*OpsGenieIntegrationClient, error) {
|
||
|
cli.makeHTTPTransportSettings()
|
||
|
|
||
|
integrationClient := new(OpsGenieIntegrationClient)
|
||
|
integrationClient.SetOpsGenieClient(*cli)
|
||
|
|
||
|
if cli.opsGenieAPIURL == "" {
|
||
|
integrationClient.SetOpsGenieAPIUrl(endpointURL)
|
||
|
}
|
||
|
|
||
|
return integrationClient, nil
|
||
|
}
|
||
|
|
||
|
// Policy instantiates a new OpsGeniePolicyClient.
|
||
|
func (cli *OpsGenieClient) Policy() (*OpsGeniePolicyClient, error) {
|
||
|
cli.makeHTTPTransportSettings()
|
||
|
|
||
|
policyClient := new(OpsGeniePolicyClient)
|
||
|
policyClient.SetOpsGenieClient(*cli)
|
||
|
|
||
|
if cli.opsGenieAPIURL == "" {
|
||
|
policyClient.SetOpsGenieAPIUrl(endpointURL)
|
||
|
}
|
||
|
|
||
|
return policyClient, nil
|
||
|
}
|
||
|
|
||
|
// Team instantiates a new OpsGenieTeamClient.
|
||
|
func (cli *OpsGenieClient) Team() (*OpsGenieTeamClient, error) {
|
||
|
cli.makeHTTPTransportSettings()
|
||
|
|
||
|
teamClient := new(OpsGenieTeamClient)
|
||
|
teamClient.SetOpsGenieClient(*cli)
|
||
|
|
||
|
if cli.opsGenieAPIURL == "" {
|
||
|
teamClient.SetOpsGenieAPIUrl(endpointURL)
|
||
|
}
|
||
|
|
||
|
return teamClient, nil
|
||
|
}
|
||
|
|
||
|
// Escalation instantiates a new OpsGenieEscalationClient.
|
||
|
func (cli *OpsGenieClient) Escalation() (*OpsGenieEscalationClient, error) {
|
||
|
cli.makeHTTPTransportSettings()
|
||
|
|
||
|
escalationClient := new(OpsGenieEscalationClient)
|
||
|
escalationClient.SetOpsGenieClient(*cli)
|
||
|
|
||
|
if cli.opsGenieAPIURL == "" {
|
||
|
escalationClient.SetOpsGenieAPIUrl(endpointURL)
|
||
|
}
|
||
|
|
||
|
return escalationClient, nil
|
||
|
}
|
||
|
|
||
|
// Schedule instantiates a new OpsGenieScheduleClient.
|
||
|
func (cli *OpsGenieClient) Schedule() (*OpsGenieScheduleClient, error) {
|
||
|
cli.makeHTTPTransportSettings()
|
||
|
|
||
|
scheduleClient := new(OpsGenieScheduleClient)
|
||
|
scheduleClient.SetOpsGenieClient(*cli)
|
||
|
|
||
|
if cli.opsGenieAPIURL == "" {
|
||
|
scheduleClient.SetOpsGenieAPIUrl(endpointURL)
|
||
|
}
|
||
|
|
||
|
return scheduleClient, nil
|
||
|
}
|
||
|
|
||
|
// User instantiates a new OpsGenieUserClient.
|
||
|
func (cli *OpsGenieClient) User() (*OpsGenieUserClient, error) {
|
||
|
cli.makeHTTPTransportSettings()
|
||
|
|
||
|
userClient := new(OpsGenieUserClient)
|
||
|
userClient.SetOpsGenieClient(*cli)
|
||
|
|
||
|
if cli.opsGenieAPIURL == "" {
|
||
|
userClient.SetOpsGenieAPIUrl(endpointURL)
|
||
|
}
|
||
|
|
||
|
return userClient, nil
|
||
|
}
|
||
|
|
||
|
// buildCommonRequestProps is an internal method to set common properties of requests that will send to OpsGenie.
|
||
|
func (cli *OpsGenieClient) buildCommonRequestProps() goreq.Request {
|
||
|
if cli.httpTransportSettings == nil {
|
||
|
cli.makeHTTPTransportSettings()
|
||
|
}
|
||
|
goreq.SetConnectTimeout(cli.httpTransportSettings.ConnectionTimeout)
|
||
|
req := goreq.Request{}
|
||
|
if cli.proxy != nil {
|
||
|
req.Proxy = cli.proxy.toString()
|
||
|
}
|
||
|
req.UserAgent = userAgentParam.ToString()
|
||
|
req.Timeout = cli.httpTransportSettings.RequestTimeout
|
||
|
req.Insecure = true
|
||
|
|
||
|
return req
|
||
|
}
|
||
|
|
||
|
// buildGetRequest is an internal method to prepare a "GET" request that will send to OpsGenie.
|
||
|
func (cli *OpsGenieClient) buildGetRequest(uri string, request interface{}) goreq.Request {
|
||
|
req := cli.buildCommonRequestProps()
|
||
|
req.Method = "GET"
|
||
|
req.ContentType = "application/x-www-form-urlencoded; charset=UTF-8"
|
||
|
uri = cli.OpsGenieAPIUrl() + uri
|
||
|
if request != nil {
|
||
|
v, _ := goquery.Values(request)
|
||
|
req.Uri = uri + "?" + v.Encode()
|
||
|
} else {
|
||
|
req.Uri = uri
|
||
|
}
|
||
|
logging.Logger().Info("Executing OpsGenie request to ["+uri+"] with parameters: ")
|
||
|
return req
|
||
|
}
|
||
|
|
||
|
// buildPostRequest is an internal method to prepare a "POST" request that will send to OpsGenie.
|
||
|
func (cli *OpsGenieClient) buildPostRequest(uri string, request interface{}) goreq.Request {
|
||
|
req := cli.buildCommonRequestProps()
|
||
|
req.Method = "POST"
|
||
|
req.ContentType = "application/json; charset=utf-8"
|
||
|
req.Uri = cli.OpsGenieAPIUrl() + uri
|
||
|
req.Body = request
|
||
|
j, _ := json.Marshal(request)
|
||
|
logging.Logger().Info("Executing OpsGenie request to ["+req.Uri+"] with content parameters: ", string(j))
|
||
|
|
||
|
return req
|
||
|
}
|
||
|
|
||
|
// buildDeleteRequest is an internal method to prepare a "DELETE" request that will send to OpsGenie.
|
||
|
func (cli *OpsGenieClient) buildDeleteRequest(uri string, request interface{}) goreq.Request {
|
||
|
req := cli.buildGetRequest(uri, request)
|
||
|
req.Method = "DELETE"
|
||
|
return req
|
||
|
}
|
||
|
|
||
|
// sendRequest is an internal method to send the prepared requests to OpsGenie.
|
||
|
func (cli *OpsGenieClient) sendRequest(req goreq.Request) (*goreq.Response, error) {
|
||
|
// send the request
|
||
|
var resp *goreq.Response
|
||
|
var err error
|
||
|
for i := 0; i < cli.httpTransportSettings.MaxRetryAttempts; i++ {
|
||
|
resp, err = req.Do()
|
||
|
if err == nil && resp.StatusCode < 500 {
|
||
|
break
|
||
|
}
|
||
|
if resp != nil {
|
||
|
defer resp.Body.Close()
|
||
|
logging.Logger().Info(fmt.Sprintf("Retrying request [%s] ResponseCode:[%d]. RetryCount: %d", req.Uri, resp.StatusCode, (i + 1)))
|
||
|
} else {
|
||
|
logging.Logger().Info(fmt.Sprintf("Retrying request [%s] Reason:[%s]. RetryCount: %d", req.Uri, err.Error(), (i + 1)))
|
||
|
}
|
||
|
time.Sleep(timeSleepBetweenRequests * time.Duration(i+1))
|
||
|
}
|
||
|
if err != nil {
|
||
|
message := "Unable to send the request " + err.Error()
|
||
|
logging.Logger().Warn(message)
|
||
|
return nil, errors.New(message)
|
||
|
}
|
||
|
// check for the returning http status
|
||
|
statusCode := resp.StatusCode
|
||
|
if statusCode >= 400 {
|
||
|
body, err := resp.Body.ToString()
|
||
|
if err != nil {
|
||
|
message := "Server response with error can not be parsed " + err.Error()
|
||
|
logging.Logger().Warn(message)
|
||
|
return nil, errors.New(message)
|
||
|
}
|
||
|
return nil, errorMessage(statusCode, body)
|
||
|
}
|
||
|
return resp, nil
|
||
|
}
|
||
|
|
||
|
// errorMessage is an internal method to return formatted error message according to HTTP status code of the response.
|
||
|
func errorMessage(httpStatusCode int, responseBody string) error {
|
||
|
if httpStatusCode >= 400 && httpStatusCode < 500 {
|
||
|
message := fmt.Sprintf("Client error occurred; Response Code: %d, Response Body: %s", httpStatusCode, responseBody)
|
||
|
logging.Logger().Warn(message)
|
||
|
return errors.New(message)
|
||
|
}
|
||
|
if httpStatusCode >= 500 {
|
||
|
message := fmt.Sprintf("Server error occurred; Response Code: %d, Response Body: %s", httpStatusCode, responseBody)
|
||
|
logging.Logger().Info(message)
|
||
|
return errors.New(message)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Initializer for the package client
|
||
|
// Initializes the User-Agent parameter of the requests.
|
||
|
// TODO version information must be read from a MANIFEST file
|
||
|
func init() {
|
||
|
userAgentParam.sdkName = "opsgenie-go-sdk"
|
||
|
userAgentParam.version = "1.0.0"
|
||
|
userAgentParam.os = runtime.GOOS
|
||
|
userAgentParam.goVersion = runtime.Version()
|
||
|
userAgentParam.timezone = time.Local.String()
|
||
|
}
|