Skip to content

Commit

Permalink
Add metric to monitor all licenses in a HA setup (#153)
Browse files Browse the repository at this point in the history
* Add metric and scraping for licenses endpoint

* Fix build issues
  • Loading branch information
MMichel authored Dec 18, 2024
1 parent 95826d8 commit 1f6df27
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 8 deletions.
40 changes: 35 additions & 5 deletions artifactory/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import (
)

const (
pingEndpoint = "system/ping"
versionEndpoint = "system/version"
licenseEndpoint = "system/license"
pingEndpoint = "system/ping"
versionEndpoint = "system/version"
licenseEndpoint = "system/license"
licensesEndpoint = "system/licenses"
)

type HealthStatus struct {
Expand Down Expand Up @@ -65,7 +66,7 @@ func (c *Client) FetchBuildInfo() (BuildInfo, error) {
return buildInfo, nil
}

// LicenseInfo represents API respond from license endpoint
// LicenseInfo represents API response from license endpoint
type LicenseInfo struct {
Type string `json:"type"`
ValidThrough string `json:"validThrough"`
Expand Down Expand Up @@ -117,11 +118,40 @@ func (c *Client) FetchLicense() (LicenseInfo, error) {
}
licenseInfo.NodeId = resp.NodeId
if err := json.Unmarshal(resp.Body, &licenseInfo); err != nil {
c.logger.Error("There was an issue when try to unmarshal licenseInfo respond")
c.logger.Error("There was an issue when trying to unmarshal licenseInfo response")
return licenseInfo, &UnmarshalError{
message: err.Error(),
endpoint: licenseEndpoint,
}
}
return licenseInfo, nil
}

// LicensesInfo represents API response from licenses endpoint
type LicensesInfo struct {
Licenses []struct {
LicenseInfo
NodeId string `json:"nodeId"`
NodeUrl string `json:"nodeUrl"`
LicenseHash string `json:"licenseHash"`
Expired bool `json:"expired"`
} `json:"licenses"`
}

// FetchLicenses makes the API call to licenses endpoint and returns LicensesInfo
func (c *Client) FetchLicenses() (LicensesInfo, error) {
var licensesInfo LicensesInfo
c.logger.Debug("Fetching HA licenses stats")
resp, err := c.FetchHTTP(licensesEndpoint)
if err != nil {
return licensesInfo, err
}
if err := json.Unmarshal(resp.Body, &licensesInfo); err != nil {
c.logger.Error("There was an issue when trying to unmarshal licensesInfo response")
return licensesInfo, &UnmarshalError{
message: err.Error(),
endpoint: licensesEndpoint,
}
}
return licensesInfo, nil
}
12 changes: 9 additions & 3 deletions collector/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,10 @@ var (
}

systemMetrics = metrics{
"healthy": newMetric("healthy", "system", "Is Artifactory working properly (1 = healthy).", defaultLabelNames),
"version": newMetric("version", "system", "Version and revision of Artifactory as labels.", append([]string{"version", "revision"}, defaultLabelNames...)),
"license": newMetric("license", "system", "License type and expiry as labels, seconds to expiration as value", append([]string{"type", "licensed_to", "expires"}, defaultLabelNames...)),
"healthy": newMetric("healthy", "system", "Is Artifactory working properly (1 = healthy).", defaultLabelNames),
"version": newMetric("version", "system", "Version and revision of Artifactory as labels.", append([]string{"version", "revision"}, defaultLabelNames...)),
"license": newMetric("license", "system", "License type and expiry as labels, seconds to expiration as value", append([]string{"type", "licensed_to", "expires"}, defaultLabelNames...)),
"licenses": newMetric("licenses", "system", "License type and expiry as labels, seconds to expiration as value", append([]string{"type", "valid_through", "licensed_to", "node_url", "license_hash", "expires"}, defaultLabelNames...)),
}

artifactsMetrics = metrics{
Expand Down Expand Up @@ -145,6 +146,11 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) (up float64) {
return 0
}

// Collect and export system HA licenses metrics
if err := e.exportSystemHALicenses(ch); err != nil {
return 0
}

// Fetch Storage Info stats and register them
storageInfo, err := e.client.FetchStorageInfo()
if err != nil {
Expand Down
39 changes: 39 additions & 0 deletions collector/system.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package collector

import (
"strconv"

"github.com/prometheus/client_golang/prometheus"
)

Expand Down Expand Up @@ -79,3 +81,40 @@ func (e *Exporter) exportSystem(ch chan<- prometheus.Metric) error {

return nil
}

func (e *Exporter) exportSystemHALicenses(ch chan<- prometheus.Metric) error {
licensesInfo, err := e.client.FetchLicenses()
if err != nil {
e.logger.Error(
"Couldn't scrape Artifactory when fetching system/licenses",
"err", err.Error(),
)
e.totalAPIErrors.Inc()
return err
}

for _, licenseInfo := range licensesInfo.Licenses {
licenseValSec, err := licenseInfo.ValidSeconds()
if err != nil {
e.logger.Warn(
"Couldn't get Artifactory license validity",
"err", err.Error(),
) // To preserve the operation, we do nothing but log the event,
}
metric := systemMetrics["licenses"]
ch <- prometheus.MustNewConstMetric(
metric,
prometheus.GaugeValue,
float64(licenseValSec), // Prometheus expects a float type.
licenseInfo.TypeNormalized(),
licenseInfo.ValidThrough,
licenseInfo.LicensedTo,
licenseInfo.NodeUrl,
licenseInfo.LicenseHash,
strconv.FormatBool(licenseInfo.Expired),
licenseInfo.NodeId,
)
}

return nil
}

0 comments on commit 1f6df27

Please sign in to comment.