Skip to content

Commit

Permalink
feat: add node_id label to all exported metrics (#80)
Browse files Browse the repository at this point in the history
Co-authored-by: Peiman Jafari <18074432+peimanja@users.noreply.github.com>
  • Loading branch information
iNoahNothing and peimanja authored Sep 28, 2022
1 parent 69bc681 commit 7eb9f40
Show file tree
Hide file tree
Showing 11 changed files with 139 additions and 98 deletions.
14 changes: 10 additions & 4 deletions artifactory/replication.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,25 @@ type Replication struct {
CheckBinaryExistenceInFilestore bool `json:"checkBinaryExistenceInFilestore"`
SyncStatistics bool `json:"syncStatistics"`
}
type Replications struct {
Replications []Replication
NodeId string
}

// FetchReplications makes the API call to replication endpoint and returns []Replication
func (c *Client) FetchReplications() ([]Replication, error) {
var replications []Replication
func (c *Client) FetchReplications() (Replications, error) {
var replications Replications
level.Debug(c.logger).Log("msg", "Fetching replications stats")
resp, err := c.FetchHTTP(replicationEndpoint)
if err != nil {
if err.(*APIError).status == 404 {
return replications, nil
}
return nil, err
return replications, err
}
if err := json.Unmarshal(resp, &replications); err != nil {
replications.NodeId = resp.NodeId

if err := json.Unmarshal(resp.Body, &replications.Replications); err != nil {
level.Error(c.logger).Log("msg", "There was an issue when try to unmarshal replication respond")
return replications, &UnmarshalError{
message: err.Error(),
Expand Down
24 changes: 17 additions & 7 deletions artifactory/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,21 @@ type User struct {
Name string `json:"name"`
Realm string `json:"realm"`
}
type Users struct {
Users []User
NodeId string
}

// FetchUsers makes the API call to users endpoint and returns []User
func (c *Client) FetchUsers() ([]User, error) {
var users []User
func (c *Client) FetchUsers() (Users, error) {
var users Users
level.Debug(c.logger).Log("msg", "Fetching users stats")
resp, err := c.FetchHTTP(usersEndpoint)
if err != nil {
return nil, err
return users, err
}
if err := json.Unmarshal(resp, &users); err != nil {
users.NodeId = resp.NodeId
if err := json.Unmarshal(resp.Body, &users.Users); err != nil {
level.Error(c.logger).Log("msg", "There was an issue when try to unmarshal users respond")
return users, &UnmarshalError{
message: err.Error(),
Expand All @@ -40,16 +45,21 @@ type Group struct {
Name string `json:"name"`
Realm string `json:"uri"`
}
type Groups struct {
Groups []Group
NodeId string
}

// FetchGroups makes the API call to groups endpoint and returns []Group
func (c *Client) FetchGroups() ([]Group, error) {
var groups []Group
func (c *Client) FetchGroups() (Groups, error) {
var groups Groups
level.Debug(c.logger).Log("msg", "Fetching groups stats")
resp, err := c.FetchHTTP(groupsEndpoint)
if err != nil {
return groups, err
}
if err := json.Unmarshal(resp, &groups); err != nil {
groups.NodeId = resp.NodeId
if err := json.Unmarshal(resp.Body, &groups.Groups); err != nil {
level.Error(c.logger).Log("msg", "There was an issue when try to unmarshal groups respond")
return groups, &UnmarshalError{
message: err.Error(),
Expand Down
4 changes: 3 additions & 1 deletion artifactory/storageinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type StorageInfo struct {
PackageType string `json:"packageType"`
Percentage string `json:"percentage"`
} `json:"repositoriesSummaryList"`
NodeId string
}

// FetchStorageInfo makes the API call to storageinfo endpoint and returns StorageInfo
Expand All @@ -47,7 +48,8 @@ func (c *Client) FetchStorageInfo() (StorageInfo, error) {
if err != nil {
return storageInfo, err
}
if err := json.Unmarshal(resp, &storageInfo); err != nil {
storageInfo.NodeId = resp.NodeId
if err := json.Unmarshal(resp.Body, &storageInfo); err != nil {
level.Error(c.logger).Log("msg", "There was an issue when try to unmarshal storageInfo respond")
return storageInfo, &UnmarshalError{
message: err.Error(),
Expand Down
26 changes: 19 additions & 7 deletions artifactory/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,27 @@ const (
licenseEndpoint = "system/license"
)

type HealthStatus struct {
Healthy bool
NodeId string
}

// FetchHealth returns true if the ping endpoint returns "OK"
func (c *Client) FetchHealth() (bool, error) {
func (c *Client) FetchHealth() (HealthStatus, error) {
health := HealthStatus{Healthy: false}
level.Debug(c.logger).Log("msg", "Fetching health stats")
resp, err := c.FetchHTTP(pingEndpoint)
if err != nil {
return false, err
return health, err
}
bodyString := string(resp)
health.NodeId = resp.NodeId
bodyString := string(resp.Body)
if bodyString == "OK" {
level.Debug(c.logger).Log("msg", "System ping returned OK")
return true, nil
health.Healthy = true
return health, nil
}
return false, err
return health, err
}

// BuildInfo represents API respond from version endpoint
Expand All @@ -33,6 +41,7 @@ type BuildInfo struct {
Revision string `json:"revision"`
Addons []string `json:"addons"`
License string `json:"license"`
NodeId string
}

// FetchBuildInfo makes the API call to version endpoint and returns BuildInfo
Expand All @@ -43,7 +52,8 @@ func (c *Client) FetchBuildInfo() (BuildInfo, error) {
if err != nil {
return buildInfo, err
}
if err := json.Unmarshal(resp, &buildInfo); err != nil {
buildInfo.NodeId = resp.NodeId
if err := json.Unmarshal(resp.Body, &buildInfo); err != nil {
level.Error(c.logger).Log("msg", "There was an issue when try to unmarshal buildInfo respond")
return buildInfo, &UnmarshalError{
message: err.Error(),
Expand All @@ -58,6 +68,7 @@ type LicenseInfo struct {
Type string `json:"type"`
ValidThrough string `json:"validThrough"`
LicensedTo string `json:"licensedTo"`
NodeId string
}

// FetchLicense makes the API call to license endpoint and returns LicenseInfo
Expand All @@ -68,7 +79,8 @@ func (c *Client) FetchLicense() (LicenseInfo, error) {
if err != nil {
return licenseInfo, err
}
if err := json.Unmarshal(resp, &licenseInfo); err != nil {
licenseInfo.NodeId = resp.NodeId
if err := json.Unmarshal(resp.Body, &licenseInfo); err != nil {
level.Error(c.logger).Log("msg", "There was an issue when try to unmarshal licenseInfo respond")
return licenseInfo, &UnmarshalError{
message: err.Error(),
Expand Down
52 changes: 26 additions & 26 deletions artifactory/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ type APIErrors struct {
Errors interface{}
}

// FetchHTTP is a wrapper function for making all Get API calls
func (c *Client) FetchHTTP(path string) ([]byte, error) {
fullPath := fmt.Sprintf("%s/api/%s", c.URI, path)
level.Debug(c.logger).Log("msg", "Fetching http", "path", fullPath)
req, err := http.NewRequest("GET", fullPath, nil)
type ApiResponse struct {
Body []byte
NodeId string
}

func (c *Client) makeRequest(method string, path string, body []byte) (*http.Response, error) {
req, err := http.NewRequest(method, path, bytes.NewBuffer(body))
if err != nil {
level.Error(c.logger).Log("msg", "There was an error creating request", "err", err.Error())
return nil, err
Expand All @@ -30,13 +32,22 @@ func (c *Client) FetchHTTP(path string) ([]byte, error) {
case "accessToken":
req.Header.Add("Authorization", "Bearer "+c.cred.AccessToken)
default:
return nil, fmt.Errorf("Artifactory Auth method is not supported")
return nil, fmt.Errorf("Artifactory Auth (%s) method is not supported", c.authMethod)
}
resp, err := c.client.Do(req)
return c.client.Do(req)
}

// FetchHTTP is a wrapper function for making all Get API calls
func (c *Client) FetchHTTP(path string) (*ApiResponse, error) {
var response ApiResponse
fullPath := fmt.Sprintf("%s/api/%s", c.URI, path)
level.Debug(c.logger).Log("msg", "Fetching http", "path", fullPath)
resp, err := c.makeRequest("GET", fullPath, nil)
if err != nil {
level.Error(c.logger).Log("msg", "There was an error making API call", "endpoint", fullPath, "err", err.Error())
return nil, err
}
response.NodeId = resp.Header.Get("x-artifactory-node-id")
defer resp.Body.Close()

if resp.StatusCode == 404 {
Expand Down Expand Up @@ -79,34 +90,22 @@ func (c *Client) FetchHTTP(path string) ([]byte, error) {
level.Error(c.logger).Log("msg", "There was an error reading response body", "err", err.Error())
return nil, err
}
response.Body = bodyBytes

return bodyBytes, nil
return &response, nil
}

// QueryAQL is a wrapper function for making an query to AQL endpoint
func (c *Client) QueryAQL(query []byte) ([]byte, error) {
func (c *Client) QueryAQL(query []byte) (*ApiResponse, error) {
var response ApiResponse
fullPath := fmt.Sprintf("%s/api/search/aql", c.URI)
level.Debug(c.logger).Log("msg", "Running AQL query", "path", fullPath)
req, err := http.NewRequest("POST", fullPath, bytes.NewBuffer(query))
req.Header = http.Header{"Content-Type": {"text/plain"}}
if err != nil {
level.Error(c.logger).Log("msg", "There was an error creating request", "err", err.Error())
return nil, err
}
switch c.authMethod {
case "userPass":
req.SetBasicAuth(c.cred.Username, c.cred.Password)
case "accessToken":
req.Header.Add("Authorization", "Bearer "+c.cred.AccessToken)
default:
return nil, fmt.Errorf("Artifactory Auth method is not supported")
}

resp, err := c.client.Do(req)
resp, err := c.makeRequest("POST", fullPath, query)
if err != nil {
level.Error(c.logger).Log("msg", "There was an error making API call", "endpoint", fullPath, "err", err.Error())
return nil, err
}
response.NodeId = resp.Header.Get("x-artifactory-node-id")
defer resp.Body.Close()
if !(resp.StatusCode >= 200 && resp.StatusCode < 300) {
var apiErrors APIErrors
Expand All @@ -129,5 +128,6 @@ func (c *Client) QueryAQL(query []byte) ([]byte, error) {
level.Error(c.logger).Log("msg", "There was an error reading response body", "err", err.Error())
return nil, err
}
return bodyBytes, nil
response.Body = bodyBytes
return &response, nil
}
22 changes: 15 additions & 7 deletions collector/artifacts.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type artifact struct {

type artifactQueryResult struct {
Results []artifact `json:"results,omitempty"`
NodeId string
}

func (e *Exporter) findArtifacts(period string, queryType string) (artifactQueryResult, error) {
Expand All @@ -35,7 +36,8 @@ func (e *Exporter) findArtifacts(period string, queryType string) (artifactQuery
e.totalAPIErrors.Inc()
return artifacts, err
}
if err := json.Unmarshal(resp, &artifacts); err != nil {
artifacts.NodeId = resp.NodeId
if err := json.Unmarshal(resp.Body, &artifacts); err != nil {
level.Warn(e.logger).Log("msg", "There was an error when trying to unmarshal AQL response", "queryType", queryType, "period", period, "error", err)
e.jsonParseFailures.Inc()
return artifacts, err
Expand Down Expand Up @@ -72,31 +74,37 @@ func (e *Exporter) getTotalArtifacts(r []repoSummary) ([]repoSummary, error) {
repoSummaries := r
for i := range repoSummaries {
for _, k := range created1m.Results {
repoSummaries[i].NodeId = created1m.NodeId
if repoSummaries[i].Name == k.Repo {
repoSummaries[i].TotalCreate1m++
}
}
for _, k := range created5m.Results {
repoSummaries[i].NodeId = created1m.NodeId
if repoSummaries[i].Name == k.Repo {
repoSummaries[i].TotalCreated5m++
}
}
for _, k := range created15m.Results {
repoSummaries[i].NodeId = created1m.NodeId
if repoSummaries[i].Name == k.Repo {
repoSummaries[i].TotalCreated15m++
}
}
for _, k := range downloaded1m.Results {
repoSummaries[i].NodeId = created1m.NodeId
if repoSummaries[i].Name == k.Repo {
repoSummaries[i].TotalDownloaded1m++
}
}
for _, k := range downloaded5m.Results {
repoSummaries[i].NodeId = created1m.NodeId
if repoSummaries[i].Name == k.Repo {
repoSummaries[i].TotalDownloaded5m++
}
}
for _, k := range downloaded15m.Results {
repoSummaries[i].NodeId = created1m.NodeId
if repoSummaries[i].Name == k.Repo {
repoSummaries[i].TotalDownloaded15m++
}
Expand All @@ -111,22 +119,22 @@ func (e *Exporter) exportArtifacts(repoSummaries []repoSummary, ch chan<- promet
switch metricName {
case "created1m":
level.Debug(e.logger).Log("msg", "Registering metric", "metric", metricName, "repo", repoSummary.Name, "type", repoSummary.Type, "package_type", repoSummary.PackageType, "value", repoSummary.TotalCreate1m)
ch <- prometheus.MustNewConstMetric(metric, prometheus.GaugeValue, repoSummary.TotalCreate1m, repoSummary.Name, repoSummary.Type, repoSummary.PackageType)
ch <- prometheus.MustNewConstMetric(metric, prometheus.GaugeValue, repoSummary.TotalCreate1m, repoSummary.Name, repoSummary.Type, repoSummary.PackageType, repoSummary.NodeId)
case "created5m":
level.Debug(e.logger).Log("msg", "Registering metric", "metric", metricName, "repo", repoSummary.Name, "type", repoSummary.Type, "package_type", repoSummary.PackageType, "value", repoSummary.TotalCreated5m)
ch <- prometheus.MustNewConstMetric(metric, prometheus.GaugeValue, repoSummary.TotalCreated5m, repoSummary.Name, repoSummary.Type, repoSummary.PackageType)
ch <- prometheus.MustNewConstMetric(metric, prometheus.GaugeValue, repoSummary.TotalCreated5m, repoSummary.Name, repoSummary.Type, repoSummary.PackageType, repoSummary.NodeId)
case "created15m":
level.Debug(e.logger).Log("msg", "Registering metric", "metric", metricName, "repo", repoSummary.Name, "type", repoSummary.Type, "package_type", repoSummary.PackageType, "value", repoSummary.TotalCreated15m)
ch <- prometheus.MustNewConstMetric(metric, prometheus.GaugeValue, repoSummary.TotalCreated15m, repoSummary.Name, repoSummary.Type, repoSummary.PackageType)
ch <- prometheus.MustNewConstMetric(metric, prometheus.GaugeValue, repoSummary.TotalCreated15m, repoSummary.Name, repoSummary.Type, repoSummary.PackageType, repoSummary.NodeId)
case "downloaded1m":
level.Debug(e.logger).Log("msg", "Registering metric", "metric", metricName, "repo", repoSummary.Name, "type", repoSummary.Type, "package_type", repoSummary.PackageType, "value", repoSummary.TotalDownloaded1m)
ch <- prometheus.MustNewConstMetric(metric, prometheus.GaugeValue, repoSummary.TotalDownloaded1m, repoSummary.Name, repoSummary.Type, repoSummary.PackageType)
ch <- prometheus.MustNewConstMetric(metric, prometheus.GaugeValue, repoSummary.TotalDownloaded1m, repoSummary.Name, repoSummary.Type, repoSummary.PackageType, repoSummary.NodeId)
case "downloaded5m":
level.Debug(e.logger).Log("msg", "Registering metric", "metric", metricName, "repo", repoSummary.Name, "type", repoSummary.Type, "package_type", repoSummary.PackageType, "value", repoSummary.TotalDownloaded5m)
ch <- prometheus.MustNewConstMetric(metric, prometheus.GaugeValue, repoSummary.TotalDownloaded5m, repoSummary.Name, repoSummary.Type, repoSummary.PackageType)
ch <- prometheus.MustNewConstMetric(metric, prometheus.GaugeValue, repoSummary.TotalDownloaded5m, repoSummary.Name, repoSummary.Type, repoSummary.PackageType, repoSummary.NodeId)
case "downloaded15m":
level.Debug(e.logger).Log("msg", "Registering metric", "metric", metricName, "repo", repoSummary.Name, "type", repoSummary.Type, "package_type", repoSummary.PackageType, "value", repoSummary.TotalDownloaded15m)
ch <- prometheus.MustNewConstMetric(metric, prometheus.GaugeValue, repoSummary.TotalDownloaded15m, repoSummary.Name, repoSummary.Type, repoSummary.PackageType)
ch <- prometheus.MustNewConstMetric(metric, prometheus.GaugeValue, repoSummary.TotalDownloaded15m, repoSummary.Name, repoSummary.Type, repoSummary.PackageType, repoSummary.NodeId)
}
}
}
Expand Down
25 changes: 13 additions & 12 deletions collector/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ const (
)

var (
filestoreLabelNames = []string{"storage_type", "storage_dir"}
repoLabelNames = []string{"name", "type", "package_type"}
replicationLabelNames = []string{"name", "type", "url", "cron_exp"}
defaultLabelNames = []string{"node_id"}
filestoreLabelNames = append([]string{"storage_type", "storage_dir"}, defaultLabelNames...)
repoLabelNames = append([]string{"name", "type", "package_type"}, defaultLabelNames...)
replicationLabelNames = append([]string{"name", "type", "url", "cron_exp"}, defaultLabelNames...)
)

func newMetric(metricName string, subsystem string, docString string, labelNames []string) *prometheus.Desc {
Expand All @@ -29,15 +30,15 @@ var (
}

securityMetrics = metrics{
"users": newMetric("users", "security", "Number of Artifactory users for each realm.", []string{"realm"}),
"groups": newMetric("groups", "security", "Number of Artifactory groups", nil),
"users": newMetric("users", "security", "Number of Artifactory users for each realm.", append([]string{"realm"}, defaultLabelNames...)),
"groups": newMetric("groups", "security", "Number of Artifactory groups", defaultLabelNames),
}

storageMetrics = metrics{
"artifacts": newMetric("artifacts", "storage", "Total artifacts count stored in Artifactory.", nil),
"artifactsSize": newMetric("artifacts_size_bytes", "storage", "Total artifacts Size stored in Artifactory in bytes.", nil),
"binaries": newMetric("binaries", "storage", "Total binaries count stored in Artifactory.", nil),
"binariesSize": newMetric("binaries_size_bytes", "storage", "Total binaries Size stored in Artifactory in bytes.", nil),
"artifacts": newMetric("artifacts", "storage", "Total artifacts count stored in Artifactory.", defaultLabelNames),
"artifactsSize": newMetric("artifacts_size_bytes", "storage", "Total artifacts Size stored in Artifactory in bytes.", defaultLabelNames),
"binaries": newMetric("binaries", "storage", "Total binaries count stored in Artifactory.", defaultLabelNames),
"binariesSize": newMetric("binaries_size_bytes", "storage", "Total binaries Size stored in Artifactory in bytes.", defaultLabelNames),
"filestore": newMetric("filestore_bytes", "storage", "Total available space in the file store in bytes.", filestoreLabelNames),
"filestoreUsed": newMetric("filestore_used_bytes", "storage", "Used space in the file store in bytes.", filestoreLabelNames),
"filestoreFree": newMetric("filestore_free_bytes", "storage", "Free space in the file store in bytes.", filestoreLabelNames),
Expand All @@ -49,9 +50,9 @@ var (
}

systemMetrics = metrics{
"healthy": newMetric("healthy", "system", "Is Artifactory working properly (1 = healthy).", nil),
"version": newMetric("version", "system", "Version and revision of Artifactory as labels.", []string{"version", "revision"}),
"license": newMetric("license", "system", "License type and expiry as labels, seconds to expiration as value", []string{"type", "licensed_to", "expires"}),
"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...)),
}

artifactsMetrics = metrics{
Expand Down
Loading

0 comments on commit 7eb9f40

Please sign in to comment.