diff --git a/README.md b/README.md index 6914f86..ea38a17 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ ![Latest release](https://img.shields.io/github/v/release/QuokkaStake/cosmos-node-exporter) [![Actions Status](https://github.com/QuokkaStake/cosmos-node-exporter/workflows/test/badge.svg)](https://github.com/QuokkaStake/cosmos-node-exporter/actions) -[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FQuokkaStake%2Fcosmos-node-exporter.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FQuokkaStake%2Fcosmos-node-exporter?ref=badge_shield) cosmos-node-exporter is a Prometheus scraper that scrapes some data to monitor your node, specifically you can set up alerting if: - your app version does not match the latest on GitHub (can be useful to be notified on new releases) @@ -110,7 +109,3 @@ All configuration is done via `.toml` config. Check config.example.toml for refe ## How can I contribute? Bug reports and feature requests are always welcome! If you want to contribute, feel free to open issues or PRs. - - -## License -[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FQuokkaStake%2Fcosmos-node-exporter.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FQuokkaStake%2Fcosmos-node-exporter?ref=badge_large) \ No newline at end of file diff --git a/pkg/app.go b/pkg/app.go index 4ffdb5e..9b611ca 100644 --- a/pkg/app.go +++ b/pkg/app.go @@ -4,7 +4,7 @@ import ( "main/pkg/config" "main/pkg/constants" cosmovisorPkg "main/pkg/cosmovisor" - githubPkg "main/pkg/github" + git "main/pkg/git" "main/pkg/queriers/app" cosmovisorQuerierPkg "main/pkg/queriers/cosmovisor" nodeStats "main/pkg/queriers/node_stats" @@ -39,7 +39,6 @@ func NewApp( var tendermintRPC *tendermint.RPC var cosmovisor *cosmovisorPkg.Cosmovisor - var github *githubPkg.Github if config.TendermintConfig.Enabled.Bool { tendermintRPC = tendermint.NewRPC(config, logger) @@ -49,13 +48,11 @@ func NewApp( cosmovisor = cosmovisorPkg.NewCosmovisor(config, logger) } - if config.GithubConfig.Repository != "" { - github = githubPkg.NewGithub(config, logger) - } + gitClient := git.GetClient(config, logger) queriers := []types.Querier{ nodeStats.NewQuerier(logger, tendermintRPC), - versions.NewQuerier(logger, github, cosmovisor), + versions.NewQuerier(logger, gitClient, cosmovisor), upgrades.NewQuerier(config, logger, cosmovisor, tendermintRPC), cosmovisorQuerierPkg.NewQuerier(logger, cosmovisor), app.NewQuerier(version), diff --git a/pkg/config/config.go b/pkg/config/config.go index 322f70a..abd4bcf 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -21,7 +21,7 @@ type TendermintConfig struct { QueryUpgrades null.Bool `default:"true" toml:"query-upgrades"` } -type GithubConfig struct { +type GitConfig struct { Repository string `default:"" toml:"repository"` Token string `toml:"token"` } @@ -57,11 +57,11 @@ type Config struct { LogConfig LogConfig `toml:"log"` TendermintConfig TendermintConfig `toml:"tendermint"` CosmovisorConfig CosmovisorConfig `toml:"cosmovisor"` - GithubConfig GithubConfig `toml:"github"` + GitConfig GitConfig `toml:"git"` ListenAddress string `default:":9500" toml:"listen-address"` } -func (c *GithubConfig) Validate() error { +func (c *GitConfig) Validate() error { if c.Repository == "" { return nil } @@ -74,7 +74,7 @@ func (c *GithubConfig) Validate() error { } func (c *Config) Validate() error { - if err := c.GithubConfig.Validate(); err != nil { + if err := c.GitConfig.Validate(); err != nil { return fmt.Errorf("GitHub config is invalid: %s", err) } diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 5598ac6..d8e15a0 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -7,9 +7,10 @@ import ( const ( MetricsPrefix = "cosmos_node_exporter_" - UncachedGithubQueryTime = time.Hour + UncachedGithubQueryTime = 30 * time.Second ) var ( - GithubRegexp = regexp.MustCompile("https://github.com/(?P[a-zA-Z0-9-].*)/(?P[a-zA-Z0-9-].*)") + GithubRegexp = regexp.MustCompile("https://github.com/(?P[a-zA-Z0-9-].*)/(?P[a-zA-Z0-9-].*)") + GitopiaRegexp = regexp.MustCompile("gitopia://(?P[a-zA-Z0-9-].*)/(?P[a-zA-Z0-9-].*)") ) diff --git a/pkg/git/client.go b/pkg/git/client.go new file mode 100644 index 0000000..208353f --- /dev/null +++ b/pkg/git/client.go @@ -0,0 +1,19 @@ +package git + +import ( + configPkg "main/pkg/config" + + "github.com/rs/zerolog" +) + +type Client interface { + GetLatestRelease() (string, error) +} + +func GetClient(config *configPkg.Config, logger *zerolog.Logger) Client { + if config.GitConfig.Repository != "" { + return NewGithub(config, logger) + } + + return nil +} diff --git a/pkg/github/github.go b/pkg/git/github.go similarity index 75% rename from pkg/github/github.go rename to pkg/git/github.go index 1684b94..66c198f 100644 --- a/pkg/github/github.go +++ b/pkg/git/github.go @@ -1,4 +1,4 @@ -package github +package git import ( "encoding/json" @@ -18,24 +18,25 @@ type Github struct { Token string Logger zerolog.Logger LastModified time.Time - LastResult *types.ReleaseInfo + LastResult string } func NewGithub(config *config.Config, logger *zerolog.Logger) *Github { - value := constants.GithubRegexp.FindStringSubmatch(config.GithubConfig.Repository) + value := constants.GithubRegexp.FindStringSubmatch(config.GitConfig.Repository) return &Github{ Organization: value[1], Repository: value[2], - Token: config.GithubConfig.Token, - Logger: logger.With().Str("component", "github").Logger(), + Token: config.GitConfig.Token, + Logger: logger.With().Str("component", "git").Logger(), LastModified: time.Now(), + LastResult: "", } } func (g *Github) UseCache() bool { // If the last result is not present - do not use cache, for the first query. - if g.LastResult == nil { + if g.LastResult == "" { return false } @@ -45,7 +46,7 @@ func (g *Github) UseCache() bool { return diff < constants.UncachedGithubQueryTime } -func (g *Github) GetLatestRelease() (types.ReleaseInfo, error) { +func (g *Github) GetLatestRelease() (string, error) { latestReleaseUrl := fmt.Sprintf( "https://api.github.com/repos/%s/%s/releases/latest", g.Organization, @@ -58,7 +59,7 @@ func (g *Github) GetLatestRelease() (types.ReleaseInfo, error) { req, err := http.NewRequest(http.MethodGet, latestReleaseUrl, nil) if err != nil { - return types.ReleaseInfo{}, err + return "", err } useCache := g.UseCache() @@ -80,30 +81,29 @@ func (g *Github) GetLatestRelease() (types.ReleaseInfo, error) { res, err := client.Do(req) if err != nil { - return types.ReleaseInfo{}, err + return "", err } defer res.Body.Close() - if res.StatusCode == http.StatusNotModified && g.LastResult != nil { + if res.StatusCode == http.StatusNotModified && g.LastResult != "" { g.Logger.Trace().Msg("Github returned cached response") - g.LastModified = time.Now() - return *g.LastResult, nil + return g.LastResult, nil } releaseInfo := types.ReleaseInfo{} err = json.NewDecoder(res.Body).Decode(&releaseInfo) if err != nil { - return releaseInfo, err + return "", err } // GitHub returned error, probably rate-limiting if releaseInfo.Message != "" { - return releaseInfo, fmt.Errorf("got error from Github: %s", releaseInfo.Message) + return "", fmt.Errorf("got error from Github: %s", releaseInfo.Message) } g.LastModified = time.Now() - g.LastResult = &releaseInfo + g.LastResult = releaseInfo.TagName - return releaseInfo, err + return releaseInfo.TagName, err } diff --git a/pkg/queriers/versions/querier.go b/pkg/queriers/versions/querier.go index 51d65be..aa4986a 100644 --- a/pkg/queriers/versions/querier.go +++ b/pkg/queriers/versions/querier.go @@ -3,7 +3,7 @@ package versions import ( "main/pkg/constants" cosmovisorPkg "main/pkg/cosmovisor" - "main/pkg/github" + "main/pkg/git" "main/pkg/query_info" "main/pkg/types" "main/pkg/utils" @@ -16,24 +16,24 @@ import ( type Querier struct { Logger zerolog.Logger - Github *github.Github + GitClient git.Client Cosmovisor *cosmovisorPkg.Cosmovisor } func NewQuerier( logger *zerolog.Logger, - github *github.Github, + gitClient git.Client, cosmovisor *cosmovisorPkg.Cosmovisor, ) *Querier { return &Querier{ Logger: logger.With().Str("component", "versions_querier").Logger(), - Github: github, + GitClient: gitClient, Cosmovisor: cosmovisor, } } func (v *Querier) Enabled() bool { - return v.Github != nil || v.Cosmovisor != nil + return v.GitClient != nil || v.Cosmovisor != nil } func (v *Querier) Name() string { @@ -45,32 +45,27 @@ func (v *Querier) Get() ([]prometheus.Collector, []query_info.QueryInfo) { collectors := []prometheus.Collector{} var ( - releaseInfo types.ReleaseInfo - versionInfo types.VersionInfo - err error + latestVersion string + versionInfo types.VersionInfo + err error ) - if v.Github != nil { + if v.GitClient != nil { queriesInfo = append(queriesInfo, query_info.QueryInfo{ - Module: "github", + Module: "git", Action: "get_latest_release", Success: false, }) - releaseInfo, err = v.Github.GetLatestRelease() + latestVersion, err = v.GitClient.GetLatestRelease() if err != nil { - v.Logger.Err(err).Msg("Could not get latest Github version") - return []prometheus.Collector{}, queriesInfo - } - - if releaseInfo.TagName == "" { - v.Logger.Err(err).Msg("Malformed Github response when querying version") + v.Logger.Err(err).Msg("Could not get latest Git version") return []prometheus.Collector{}, queriesInfo } // stripping first "v" character: "v1.2.3" => "1.2.3" - if releaseInfo.TagName[0] == 'v' { - releaseInfo.TagName = releaseInfo.TagName[1:] + if latestVersion[0] == 'v' { + latestVersion = latestVersion[1:] } queriesInfo[len(queriesInfo)-1].Success = true @@ -84,7 +79,7 @@ func (v *Querier) Get() ([]prometheus.Collector, []query_info.QueryInfo) { ) remoteVersion. - With(prometheus.Labels{"version": releaseInfo.TagName}). + With(prometheus.Labels{"version": latestVersion}). Set(1) collectors = append(collectors, remoteVersion) @@ -120,14 +115,14 @@ func (v *Querier) Get() ([]prometheus.Collector, []query_info.QueryInfo) { collectors = append(collectors, localVersion) } - if v.Github != nil && v.Cosmovisor != nil { + if v.GitClient != nil && v.Cosmovisor != nil { semverLocal, err := semver.NewVersion(versionInfo.Version) if err != nil { v.Logger.Err(err).Msg("Could not get local app version") return collectors, queriesInfo } - semverRemote, err := semver.NewVersion(releaseInfo.TagName) + semverRemote, err := semver.NewVersion(latestVersion) if err != nil { v.Logger.Err(err).Msg("Could not get remote app version") return collectors, queriesInfo @@ -147,7 +142,7 @@ func (v *Querier) Get() ([]prometheus.Collector, []query_info.QueryInfo) { isUsingLatestVersion. With(prometheus.Labels{ "local_version": versionInfo.Version, - "remote_version": releaseInfo.TagName, + "remote_version": latestVersion, }). Set(utils.BoolToFloat64(isLatestOrSameVersion))