Skip to content

Commit

Permalink
Some refactor around license. (#149)
Browse files Browse the repository at this point in the history
* Some refactor around license.

Trying to put responsibility in the right places.

* Minor amendments.

Shortcomings left over from rapid development.

* Sonnar issue

Cognitive Complexity.

* Time format.

To quote Oracle “Month name-Day-Year with no leading zeros”.

---------

Co-authored-by: Peiman Jafari <[email protected]>
  • Loading branch information
KacperPerschke and peimanja authored Dec 6, 2024
1 parent 149f078 commit 95826d8
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 68 deletions.
38 changes: 38 additions & 0 deletions artifactory/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ package artifactory

import (
"encoding/json"
"fmt"
"slices"
"strings"
"time"
)

const (
Expand Down Expand Up @@ -69,6 +73,40 @@ type LicenseInfo struct {
NodeId string
}

func (l LicenseInfo) IsOSS() bool {
var afOSSLicenseTypes = []string{
`community edition for c/c++`,
`jcr edition`,
`oss`,
}
return slices.Contains(
afOSSLicenseTypes,
l.TypeNormalized(),
)
}

func (l LicenseInfo) TypeNormalized() string {
return strings.ToLower(l.Type)
}

const USAFullDate = "Jan 2, 2006"

func (l LicenseInfo) ValidSeconds() (int64, error) {
if l.IsOSS() {
return 0, nil
}
validThroughTime, err := time.Parse(USAFullDate, l.ValidThrough)
if err != nil {
return 0, fmt.Errorf(
"unparsable ‘validThrough’ license field: %w",
err,
)
}
validThroughEpoch := validThroughTime.Unix()
timeNowEpoch := time.Now().Unix()
return validThroughEpoch - timeNowEpoch, nil
}

// FetchLicense makes the API call to license endpoint and returns LicenseInfo
func (c *Client) FetchLicense() (LicenseInfo, error) {
var licenseInfo LicenseInfo
Expand Down
44 changes: 3 additions & 41 deletions collector/collector.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package collector

import (
"strings"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/version"
)
Expand Down Expand Up @@ -134,52 +132,16 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
func (e *Exporter) scrape(ch chan<- prometheus.Metric) (up float64) {
e.totalScrapes.Inc()

// Collect License info
var licenseType string
license, err := e.client.FetchLicense()
if err != nil {
e.totalAPIErrors.Inc()
return 0
}
licenseType = strings.ToLower(license.Type)
// Some API endpoints are not available in OSS
if licenseType != "oss" && licenseType != "jcr edition" && licenseType != "community edition for c/c++" {
for metricName, metric := range securityMetrics {
switch metricName {
case "users":
err := e.exportUsersCount(metricName, metric, ch)
if err != nil {
return 0
}
case "groups":
err := e.exportGroups(metricName, metric, ch)
if err != nil {
return 0
}
case "certificates":
err := e.exportCertificates(metricName, metric, ch)
if err != nil {
return 0
}
}
}
err = e.exportReplications(ch)
if err != nil {
return 0
}
}

// Collect and export open metrics
if e.optionalMetrics.OpenMetrics {
err = e.exportOpenMetrics(ch)
err := e.exportOpenMetrics(ch)
if err != nil {
return 0
}
}

// Collect and export system metrics
err = e.exportSystem(license, ch)
if err != nil {
if err := e.exportSystem(ch); err != nil {
return 0
}

Expand All @@ -200,7 +162,7 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) (up float64) {

// Get Downloaded and Created items for all repo in the last 1 and 5 minutes and add it to repoSummaryList
if e.optionalMetrics.Artifacts {
repoSummaryList, err = e.getTotalArtifacts(repoSummaryList)
repoSummaryList, err := e.getTotalArtifacts(repoSummaryList)
if err != nil {
return 0
}
Expand Down
26 changes: 26 additions & 0 deletions collector/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,32 @@ func (e *Exporter) countUsersPerRealm(users []artifactory.User) realmUserCounts
return usersPerRealm
}

func (e *Exporter) exportAllSecurityMetrics(ch chan<- prometheus.Metric) error {
for metricName, metric := range securityMetrics {
switch metricName {
case "users":
err := e.exportUsersCount(metricName, metric, ch)
if err != nil {
return err
}
case "groups":
err := e.exportGroups(metricName, metric, ch)
if err != nil {
return err
}
case "certificates":
err := e.exportCertificates(metricName, metric, ch)
if err != nil {
return err
}
}
}
if err := e.exportReplications(ch); err != nil {
return err
}
return nil
}

func (e *Exporter) exportUsersCount(metricName string, metric *prometheus.Desc, ch chan<- prometheus.Metric) error {
// Fetch Artifactory Users
users, err := e.client.FetchUsers()
Expand Down
75 changes: 48 additions & 27 deletions collector/system.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
package collector

import (
"strings"
"time"

"github.com/prometheus/client_golang/prometheus"

"github.com/peimanja/artifactory_exporter/artifactory"
)

func (e *Exporter) exportSystem(license artifactory.LicenseInfo, ch chan<- prometheus.Metric) error {
health, err := e.client.FetchHealth()
func (e *Exporter) exportSystem(ch chan<- prometheus.Metric) error {
healthInfo, err := e.client.FetchHealth()
if err != nil {
e.logger.Error(
"Couldn't scrape Artifactory when fetching system/ping",
Expand All @@ -28,33 +23,59 @@ func (e *Exporter) exportSystem(license artifactory.LicenseInfo, ch chan<- prome
e.totalAPIErrors.Inc()
return err
}
licenseInfo, err := e.client.FetchLicense()
if err != nil {
e.logger.Error(
"Couldn't scrape Artifactory when fetching system/license",
"err", err.Error(),
)
e.totalAPIErrors.Inc()
return err
}
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,
}

licenseType := strings.ToLower(license.Type)
for metricName, metric := range systemMetrics {
switch metricName {
case "healthy":
ch <- prometheus.MustNewConstMetric(metric, prometheus.GaugeValue, convArtiToPromBool(health.Healthy), health.NodeId)
ch <- prometheus.MustNewConstMetric(
metric,
prometheus.GaugeValue,
convArtiToPromBool(healthInfo.Healthy),
healthInfo.NodeId,
)
case "version":
ch <- prometheus.MustNewConstMetric(metric, prometheus.GaugeValue, 1, buildInfo.Version, buildInfo.Revision, buildInfo.NodeId)
ch <- prometheus.MustNewConstMetric(
metric,
prometheus.GaugeValue,
1,
buildInfo.Version,
buildInfo.Revision,
buildInfo.NodeId,
)
case "license":
var validThrough float64
timeNow := float64(time.Now().Unix())
switch licenseType {
case "oss", "jcr edition", "community edition for c/c++":
validThrough = timeNow
default:
if validThroughTime, err := time.Parse("Jan 2, 2006", license.ValidThrough); err != nil {
e.logger.Warn(
"Couldn't parse Artifactory license ValidThrough",
"err", err.Error(),
)
validThrough = timeNow
} else {
validThrough = float64(validThroughTime.Unix())
}
}
ch <- prometheus.MustNewConstMetric(metric, prometheus.GaugeValue, validThrough-timeNow, licenseType, license.LicensedTo, license.ValidThrough, license.NodeId)
ch <- prometheus.MustNewConstMetric(
metric,
prometheus.GaugeValue,
float64(licenseValSec), // Prometheus expects a float type.
licenseInfo.TypeNormalized(),
licenseInfo.LicensedTo,
licenseInfo.ValidThrough,
licenseInfo.NodeId,
)
}
}
if !licenseInfo.IsOSS() { // Some endpoints are only available commercially.
err := e.exportAllSecurityMetrics(ch)
if err != nil {
return err
}
}

return nil
}

0 comments on commit 95826d8

Please sign in to comment.