Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some refactor around license. #149

Merged
merged 7 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
Loading