Skip to content

Commit

Permalink
Refactor code (#36)
Browse files Browse the repository at this point in the history
* Move the common functions to helpers

* simplify the exporter config

* simplify user/group metrics fuynctions

* Move NewExporter function to exporter.go

* simplify non OSS  metrics

* Simplify storageInfo metrics

* Add better API error msg

* Add Artifactory Client

* add custom artifactory error types

* Switch to using Artifactory client

* Add BuildInfo metric

* Change the Dockerfile to include buildinfo
  • Loading branch information
peimanja authored Apr 20, 2020
1 parent c6f9da0 commit 635311a
Show file tree
Hide file tree
Showing 22 changed files with 768 additions and 506 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ jobs:
github_token: ${{ secrets.GITHUB_TOKEN }}
goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }}
ldflags: |
-X github.com/prometheus/common/version.Version=${{ github.event.release.tag_name }}
-X github.com/prometheus/common/version.Revision=${{ github.sha }}
-X github.com/prometheus/common/version.Branch=${{ github.ref }}
-X github.com/prometheus/common/version.BuildDate=${{ github.event.release.created_at }}
10 changes: 9 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@ ADD . /go/artifactory_exporter

RUN go get -d -v ./...

RUN GOOS=linux GOARCH=amd64 go build -o /go/bin/artifactory_exporter
ARG SOURCE_COMMIT
ARG SOURCE_BRANCH
ARG BUILD_DATE

RUN GOOS=linux GOARCH=amd64 go build -o /go/bin/artifactory_exporter -ldflags " \
-X github.com/prometheus/common/version.Version=${SOURCE_BRANCH} \
-X github.com/prometheus/common/version.Revision=${SOURCE_COMMIT} \
-X github.com/prometheus/common/version.Branch=${SOURCE_BRANCH} \
-X github.com/prometheus/common/version.BuildDate=${BUILD_DATE}"

FROM gcr.io/distroless/base-debian10
COPY --from=build /go/bin/artifactory_exporter /
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ Some metrics are not available with Artifactory OSS license. The exporter return
| Metric | Description | Labels | OSS support |
| ------ | ----------- | ------ | ------ |
| artifactory_up | Was the last scrape of Artifactory successful. | | ✅ |
| artifactory_exporter_build_info | Exporter build information. | `version`, `revision`, `branch`, `goversion` | ✅ |
| artifactory_exporter_total_scrapes | Current total artifactory scrapes. | | ✅ |
| artifactory_exporter_total_api_errors | Current total Artifactory API errors when scraping for stats. | | ✅ |
| artifactory_exporter_json_parse_failures |Number of errors while parsing Json. | | ✅ |
| artifactory_replication_enabled | Replication status for an Artifactory repository (1 = enabled). | `name`, `type`, `cron_exp` | |
| artifactory_security_groups | Number of Artifactory groups. | | |
Expand Down
34 changes: 34 additions & 0 deletions artifactory/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package artifactory

import (
"crypto/tls"
"net/http"

"github.com/go-kit/kit/log"
"github.com/peimanja/artifactory_exporter/config"
)

// Client represents Artifactory HTTP Client
type Client struct {
URI string
authMethod string
cred config.Credentials
client *http.Client
logger log.Logger
}

// NewClient returns an initialized Artifactory HTTP Client.
func NewClient(conf *config.Config) *Client {
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: !conf.ArtiSSLVerify}}
client := &http.Client{
Timeout: conf.ArtiTimeout,
Transport: tr,
}
return &Client{
URI: conf.ArtiScrapeURI,
authMethod: conf.Credentials.AuthMethod,
cred: *conf.Credentials,
client: client,
logger: conf.Logger,
}
}
34 changes: 34 additions & 0 deletions artifactory/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package artifactory

//UnmarshalError is a custom Error type for unmarshal API respond body error
type UnmarshalError struct {
message string
endpoint string
}

func (e *UnmarshalError) Error() string {
return e.message
}

func (e *UnmarshalError) apiEndpoint() string {
return e.endpoint
}

//APIError is a custom Error type for API error
type APIError struct {
message string
endpoint string
status int
}

func (e *APIError) Error() string {
return e.message
}

func (e *APIError) apiEndpoint() string {
return e.endpoint
}

func (e *APIError) apiStatus() int {
return e.status
}
42 changes: 42 additions & 0 deletions artifactory/replication.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package artifactory

import (
"encoding/json"

"github.com/go-kit/kit/log/level"
)

const replicationEndpoint = "replications"

// Replication represents single element of API respond from replication endpoint
type Replication struct {
ReplicationType string `json:"replicationType"`
Enabled bool `json:"enabled"`
CronExp string `json:"cronExp"`
SyncDeletes bool `json:"syncDeletes"`
SyncProperties bool `json:"syncProperties"`
PathPrefix string `json:"pathPrefix"`
RepoKey string `json:"repoKey"`
URL string `json:"url"`
EnableEventReplication bool `json:"enableEventReplication"`
CheckBinaryExistenceInFilestore bool `json:"checkBinaryExistenceInFilestore"`
SyncStatistics bool `json:"syncStatistics"`
}

// FetchReplications makes the API call to replication endpoint and returns []Replication
func (c *Client) FetchReplications() ([]Replication, error) {
var replications []Replication
level.Debug(c.logger).Log("msg", "Fetching replications stats")
resp, err := c.FetchHTTP(replicationEndpoint)
if err != nil {
return nil, err
}
if err := json.Unmarshal(resp, &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(),
endpoint: replicationEndpoint,
}
}
return replications, nil
}
61 changes: 61 additions & 0 deletions artifactory/security.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package artifactory

import (
"encoding/json"

"github.com/go-kit/kit/log/level"
)

const (
usersEndpoint = "security/users"
groupsEndpoint = "security/groups"
)

// User represents single element of API respond from users endpoint
type User struct {
Name string `json:"name"`
Realm string `json:"realm"`
}

// FetchUsers makes the API call to users endpoint and returns []User
func (c *Client) FetchUsers() ([]User, error) {
var users []User
level.Debug(c.logger).Log("msg", "Fetching users stats")
resp, err := c.FetchHTTP(usersEndpoint)
if err != nil {
return nil, err
}
if err := json.Unmarshal(resp, &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(),
endpoint: usersEndpoint,
}
}
return users, nil
}

// Group represents single element of API respond from groups endpoint
type Group struct {
Name string `json:"name"`
Realm string `json:"uri"`
}

// FetchGroups makes the API call to groups endpoint and returns []Group
func (c *Client) FetchGroups() ([]Group, error) {
var groups []Group
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 {
level.Error(c.logger).Log("msg", "There was an issue when try to unmarshal groups respond")
return groups, &UnmarshalError{
message: err.Error(),
endpoint: groupsEndpoint,
}
}

return groups, nil
}
58 changes: 58 additions & 0 deletions artifactory/storageinfo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package artifactory

import (
"encoding/json"

"github.com/go-kit/kit/log/level"
)

const (
storageInfoEndpoint = "storageinfo"
)

// StorageInfo represents API respond from license storageinfo
type StorageInfo struct {
BinariesSummary struct {
BinariesCount string `json:"binariesCount"`
BinariesSize string `json:"binariesSize"`
ArtifactsSize string `json:"artifactsSize"`
Optimization string `json:"optimization"`
ItemsCount string `json:""`
ArtifactsCount string `json:"artifactsCount"`
} `json:"binariesSummary"`
FileStoreSummary struct {
StorageType string `json:"storageType"`
StorageDirectory string `json:"storageDirectory"`
TotalSpace string `json:"totalSpace"`
UsedSpace string `json:"usedSpace"`
FreeSpace string `json:"freeSpace"`
} `json:"fileStoreSummary"`
RepositoriesSummaryList []struct {
RepoKey string `json:"repoKey"`
RepoType string `json:"repoType"`
FoldersCount int `json:"foldersCount"`
FilesCount int `json:"filesCount"`
UsedSpace string `json:"usedSpace"`
ItemsCount int `json:"itemsCount"`
PackageType string `json:"packageType"`
Percentage string `json:"percentage"`
} `json:"repositoriesSummaryList"`
}

// FetchStorageInfo makes the API call to storageinfo endpoint and returns StorageInfo
func (c *Client) FetchStorageInfo() (StorageInfo, error) {
var storageInfo StorageInfo
level.Debug(c.logger).Log("msg", "Fetching storage info stats")
resp, err := c.FetchHTTP(storageInfoEndpoint)
if err != nil {
return storageInfo, err
}
if err := json.Unmarshal(resp, &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(),
endpoint: storageInfoEndpoint,
}
}
return storageInfo, nil
}
79 changes: 79 additions & 0 deletions artifactory/system.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package artifactory

import (
"encoding/json"

"github.com/go-kit/kit/log/level"
)

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

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

// BuildInfo represents API respond from version endpoint
type BuildInfo struct {
Version string `json:"version"`
Revision string `json:"revision"`
Addons []string `json:"addons"`
License string `json:"license"`
}

// FetchBuildInfo makes the API call to version endpoint and returns BuildInfo
func (c *Client) FetchBuildInfo() (BuildInfo, error) {
var buildInfo BuildInfo
level.Debug(c.logger).Log("msg", "Fetching build stats")
resp, err := c.FetchHTTP(versionEndpoint)
if err != nil {
return buildInfo, err
}
if err := json.Unmarshal(resp, &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(),
endpoint: versionEndpoint,
}
}
return buildInfo, nil
}

// LicenseInfo represents API respond from license endpoint
type LicenseInfo struct {
Type string `json:"type"`
ValidThrough string `json:"validThrough"`
LicensedTo string `json:"licensedTo"`
}

// FetchLicense makes the API call to license endpoint and returns LicenseInfo
func (c *Client) FetchLicense() (LicenseInfo, error) {
var licenseInfo LicenseInfo
level.Debug(c.logger).Log("msg", "Fetching license stats")
resp, err := c.FetchHTTP(licenseEndpoint)
if err != nil {
return licenseInfo, err
}
if err := json.Unmarshal(resp, &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(),
endpoint: licenseEndpoint,
}
}
return licenseInfo, nil
}
Loading

0 comments on commit 635311a

Please sign in to comment.