API version checking and linting.

This commit is contained in:
Stanislav Nikitin 2020-12-23 19:31:09 +05:00
parent 045769a292
commit 095aff540e
Signed by: pztrn
GPG Key ID: 1E944A0F0568B550
6 changed files with 46 additions and 29 deletions

View File

@ -14,19 +14,16 @@ import (
// Application is a thing that responsible for all application-related // Application is a thing that responsible for all application-related
// actions like data fetching, storing, etc. on higher level. // actions like data fetching, storing, etc. on higher level.
type Application struct { type Application struct {
config *Config
ctx context.Context ctx context.Context
storage storage.Metrics
config *Config
doneChan chan struct{} doneChan chan struct{}
logger *logger.Logger logger *logger.Logger
name string
storage storage.Metrics
storageDone chan struct{} storageDone chan struct{}
fetchIsRunning bool
fetchIsRunningMutex sync.RWMutex
httpClient *http.Client httpClient *http.Client
name string
fetchIsRunningMutex sync.RWMutex
fetchIsRunning bool
} }
// NewApplication creates new application. // NewApplication creates new application.

View File

@ -4,11 +4,11 @@ import "time"
// Config is a generic application configuration. // Config is a generic application configuration.
type Config struct { type Config struct {
// Headers is a list of headers that should be added to metrics request.
Headers map[string]string `yaml:"headers"`
// Endpoint is a remote application endpoint which should give us metrics // Endpoint is a remote application endpoint which should give us metrics
// in Prometheus format. // in Prometheus format.
Endpoint string `yaml:"endpoint"` Endpoint string `yaml:"endpoint"`
// Headers is a list of headers that should be added to metrics request.
Headers map[string]string `yaml:"headers"`
// TimeBetweenRequests is a minimal amount of time which should pass // TimeBetweenRequests is a minimal amount of time which should pass
// between requests. // between requests.
TimeBetweenRequests time.Duration `yaml:"time_between_requests"` TimeBetweenRequests time.Duration `yaml:"time_between_requests"`

View File

@ -29,6 +29,7 @@ func (a *Application) parse(body string) map[string]models.Metric {
a.logger.Debugln("Analyzing line:", line) a.logger.Debugln("Analyzing line:", line)
name = a.getMetricName(line) name = a.getMetricName(line)
metric, found := data[name] metric, found := data[name]
if !found { if !found {
metric = models.NewMetric(name, "", "", nil) metric = models.NewMetric(name, "", "", nil)
@ -53,9 +54,9 @@ func (a *Application) parse(body string) map[string]models.Metric {
data[name] = metric data[name] = metric
// According to https://github.com/Showmax/prometheus-docs/blob/master/content/docs/instrumenting/exposition_formats.md // According to docs HELP and TYPE lines should be printed before actual metric. Do not even
// HELP and TYPE lines should be printed before actual metric. Do not even
// report bugs regarding that! // report bugs regarding that!
// Docs: https://github.com/Showmax/prometheus-docs/blob/master/content/docs/instrumenting/exposition_formats.md
continue continue
} }
@ -76,12 +77,12 @@ func (a *Application) parse(body string) map[string]models.Metric {
metric.Value = a.getMetricValue(line) metric.Value = a.getMetricValue(line)
a.logger.Debugln("Got metric: %+v\n", metric) a.logger.Debugf("Got metric: %+v\n", metric)
data[name] = metric data[name] = metric
} }
a.logger.Debugln("Data parsed: %+v\n", data) a.logger.Debugf("Data parsed: %+v\n", data)
return data return data
} }
@ -138,7 +139,7 @@ func (a *Application) getParametersForPrometheusMetric(line string) []string {
} }
// Sometimes nestif causes questions, like here. Is code below is // Sometimes nestif causes questions, like here. Is code below is
// "deply nested"? I think not. So: // "deeply nested"? I think not. So:
// nolint:nestif // nolint:nestif
if !paramNameFinished { if !paramNameFinished {
if string(r) != "=" { if string(r) != "=" {

View File

@ -39,7 +39,6 @@ func (h *handler) getAppsList() ([]byte, error) {
appsList, err := json.Marshal(apps) appsList, err := json.Marshal(apps)
if err != nil { if err != nil {
// ToDo: log error // ToDo: log error
return nil, errNoAppsRegistered return nil, errNoAppsRegistered
} }
@ -70,23 +69,41 @@ func (h *handler) getRequestInfo(r *http.Request) (*models.RequestInfo, error) {
// Parse API version. // Parse API version.
apiVersionRaw := strings.TrimLeft(pathSplitted[2], "v") apiVersionRaw := strings.TrimLeft(pathSplitted[2], "v")
apiVersion, err := strconv.Atoi(apiVersionRaw) apiVersion, err := strconv.Atoi(apiVersionRaw)
if err != nil { if err != nil {
// ToDo: log error // ToDo: log error
return nil, errInvalidAPIVersion return nil, errInvalidAPIVersion
} }
// Check used API version.
var supportedAPIVersionUsed bool
for _, version := range supportedAPIVersions {
if apiVersion == version {
supportedAPIVersionUsed = true
break
}
}
if !supportedAPIVersionUsed {
return nil, errInvalidAPIVersion
}
// Get request type and key. // Get request type and key.
requestType = pathSplitted[3] requestType = pathSplitted[3]
if len(pathSplitted) >= 5 { if len(pathSplitted) >= 5 {
appName = pathSplitted[4] appName = pathSplitted[4]
} }
if len(pathSplitted) >= 6 { if len(pathSplitted) >= 6 {
metricName = strings.Join(pathSplitted[5:], "/") metricName = strings.Join(pathSplitted[5:], "/")
} }
reqInfo := &models.RequestInfo{ reqInfo := &models.RequestInfo{
ApiVersion: apiVersion, APIVersion: apiVersion,
Application: appName, Application: appName,
Metric: metricName, Metric: metricName,
RequestType: requestType, RequestType: requestType,
@ -103,8 +120,9 @@ func (h *handler) register(appName string, hndl common.HTTPHandlerFunc) {
// ServeHTTP handles every HTTP request. // ServeHTTP handles every HTTP request.
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
startTime := time.Now() startTime := time.Now()
defer func() { defer func() {
requestDuration := time.Now().Sub(startTime) requestDuration := time.Since(startTime)
log.Printf("[HTTP Request] from %s to %s, duration %.4fs\n", r.RemoteAddr, r.URL.Path, requestDuration.Seconds()) log.Printf("[HTTP Request] from %s to %s, duration %.4fs\n", r.RemoteAddr, r.URL.Path, requestDuration.Seconds())
}() }()

View File

@ -2,16 +2,16 @@ package models
// Metric is a generic metric structure. // Metric is a generic metric structure.
type Metric struct { type Metric struct {
// Metric name. // Name is a metric name.
Name string Name string
// HELP data, if present. // Description is a metric description from HELP line.
Description string Description string
// Additional parameters, data inside "{}". // Type is a metric type from TYPE line.
Params []string
// Type is a metric type.
Type string Type string
// Metric value. // Value is a metric value.
Value string Value string
// Params is an additional params which are placed inside "{}".
Params []string
} }
// NewMetric creates new structure for storing single metric data. // NewMetric creates new structure for storing single metric data.
@ -21,6 +21,7 @@ func NewMetric(name, mType, description string, params []string) Metric {
Description: description, Description: description,
Type: mType, Type: mType,
Params: params, Params: params,
Value: "",
} }
return m return m

View File

@ -2,8 +2,8 @@ package models
// RequestInfo is a parsed request information to throw into application's handler. // RequestInfo is a parsed request information to throw into application's handler.
type RequestInfo struct { type RequestInfo struct {
ApiVersion int
Application string Application string
Metric string Metric string
RequestType string RequestType string
APIVersion int
} }