mirror of
https://github.com/jangraefen/hcloud-pricing-exporter
synced 2024-11-10 05:54:15 +00:00
Add price provider
This commit is contained in:
parent
6056d7ba39
commit
816a6d02fc
10 changed files with 122 additions and 54 deletions
|
@ -23,6 +23,7 @@ type Fetcher interface {
|
|||
}
|
||||
|
||||
type baseFetcher struct {
|
||||
pricing *PriceProvider
|
||||
hourly *prometheus.GaugeVec
|
||||
monthly *prometheus.GaugeVec
|
||||
}
|
||||
|
@ -35,7 +36,7 @@ func (fetcher baseFetcher) GetMonthly() prometheus.Collector {
|
|||
return fetcher.monthly
|
||||
}
|
||||
|
||||
func newBase(resource string, additionalLabels ...string) *baseFetcher {
|
||||
func newBase(pricing *PriceProvider, resource string, additionalLabels ...string) *baseFetcher {
|
||||
labels := []string{"name"}
|
||||
labels = append(labels, additionalLabels...)
|
||||
|
||||
|
@ -53,6 +54,7 @@ func newBase(resource string, additionalLabels ...string) *baseFetcher {
|
|||
}
|
||||
|
||||
return &baseFetcher{
|
||||
pricing: pricing,
|
||||
hourly: prometheus.NewGaugeVec(hourlyGaugeOpts, labels),
|
||||
monthly: prometheus.NewGaugeVec(monthlyGaugeOpts, labels),
|
||||
}
|
||||
|
|
|
@ -4,15 +4,11 @@ import (
|
|||
"github.com/hetznercloud/hcloud-go/hcloud"
|
||||
)
|
||||
|
||||
const (
|
||||
floatingIPPrice = float64(1.19)
|
||||
)
|
||||
|
||||
var _ Fetcher = &floatingIP{}
|
||||
|
||||
// NewFloatingIP creates a new fetcher that will collect pricing information on floating IPs.
|
||||
func NewFloatingIP() Fetcher {
|
||||
return &floatingIP{newBase("floatingip", "location")}
|
||||
func NewFloatingIP(pricing *PriceProvider) Fetcher {
|
||||
return &floatingIP{newBase(pricing, "floatingip", "location")}
|
||||
}
|
||||
|
||||
type floatingIP struct {
|
||||
|
@ -28,7 +24,7 @@ func (floatingIP floatingIP) Run(client *hcloud.Client) error {
|
|||
for _, f := range floatingIPs {
|
||||
location := f.HomeLocation
|
||||
|
||||
monthlyPrice := floatingIPPrice
|
||||
monthlyPrice := floatingIP.pricing.FloatingIP()
|
||||
hourlyPrice := pricingPerHour(monthlyPrice)
|
||||
|
||||
floatingIP.hourly.WithLabelValues(f.Name, location.Name).Set(hourlyPrice)
|
||||
|
|
|
@ -9,8 +9,8 @@ import (
|
|||
var _ Fetcher = &loadBalancer{}
|
||||
|
||||
// NewLoadbalancer creates a new fetcher that will collect pricing information on load balancers.
|
||||
func NewLoadbalancer() Fetcher {
|
||||
return &loadBalancer{newBase("loadbalancer", "location", "type")}
|
||||
func NewLoadbalancer(pricing *PriceProvider) Fetcher {
|
||||
return &loadBalancer{newBase(pricing, "loadbalancer", "location", "type")}
|
||||
}
|
||||
|
||||
type loadBalancer struct {
|
||||
|
|
82
fetcher/prices.go
Normal file
82
fetcher/prices.go
Normal file
|
@ -0,0 +1,82 @@
|
|||
package fetcher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/hetznercloud/hcloud-go/hcloud"
|
||||
)
|
||||
|
||||
// PriceProvider provides easy access to current HCloud prices
|
||||
type PriceProvider struct {
|
||||
Client *hcloud.Client
|
||||
pricing *hcloud.Pricing
|
||||
pricingLock sync.RWMutex
|
||||
}
|
||||
|
||||
// FloatingIP returns the current price for a floating IP per month.
|
||||
func (provider *PriceProvider) FloatingIP() float64 {
|
||||
provider.pricingLock.RLock()
|
||||
defer provider.pricingLock.RUnlock()
|
||||
|
||||
return parsePrice(provider.getPricing().FloatingIP.Monthly.Gross)
|
||||
}
|
||||
|
||||
// Image returns the current price for an image per GB per month.
|
||||
func (provider *PriceProvider) Image() float64 {
|
||||
provider.pricingLock.RLock()
|
||||
defer provider.pricingLock.RUnlock()
|
||||
|
||||
return parsePrice(provider.getPricing().Image.PerGBMonth.Gross)
|
||||
}
|
||||
|
||||
// Traffic returns the current price for a TB of extra traffic per month.
|
||||
func (provider *PriceProvider) Traffic() float64 {
|
||||
provider.pricingLock.RLock()
|
||||
defer provider.pricingLock.RUnlock()
|
||||
|
||||
return parsePrice(provider.getPricing().Traffic.PerTB.Gross)
|
||||
}
|
||||
|
||||
// ServerBackup returns the percentage of base price increase for server backups per month.
|
||||
func (provider *PriceProvider) ServerBackup() float64 {
|
||||
provider.pricingLock.RLock()
|
||||
defer provider.pricingLock.RUnlock()
|
||||
|
||||
return parsePrice(provider.getPricing().ServerBackup.Percentage)
|
||||
}
|
||||
|
||||
// Volume returns the current price for a volume per GB per month.
|
||||
func (provider *PriceProvider) Volume() float64 {
|
||||
return 0.0476
|
||||
}
|
||||
|
||||
// Sync forces the provider to re-fetch prices from the HCloud API.
|
||||
func (provider *PriceProvider) Sync() {
|
||||
provider.pricingLock.Lock()
|
||||
defer provider.pricingLock.Unlock()
|
||||
|
||||
provider.pricing = nil
|
||||
}
|
||||
|
||||
func (provider *PriceProvider) getPricing() *hcloud.Pricing {
|
||||
if provider.pricing == nil {
|
||||
pricing, _, err := provider.Client.Pricing.Get(context.Background())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
provider.pricing = &pricing
|
||||
}
|
||||
|
||||
return provider.pricing
|
||||
}
|
||||
|
||||
func parsePrice(rawPrice string) float64 {
|
||||
if price, err := strconv.ParseFloat(rawPrice, 32); err == nil {
|
||||
return price
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
|
@ -9,8 +9,8 @@ import (
|
|||
var _ Fetcher = &server{}
|
||||
|
||||
// NewServer creates a new fetcher that will collect pricing information on servers.
|
||||
func NewServer() Fetcher {
|
||||
return &server{newBase("server", "location")}
|
||||
func NewServer(pricing *PriceProvider) Fetcher {
|
||||
return &server{newBase(pricing, "server", "location")}
|
||||
}
|
||||
|
||||
type server struct {
|
||||
|
|
|
@ -6,15 +6,11 @@ import (
|
|||
"github.com/hetznercloud/hcloud-go/hcloud"
|
||||
)
|
||||
|
||||
const (
|
||||
backupPriceMultiplier = 0.2
|
||||
)
|
||||
|
||||
var _ Fetcher = &server{}
|
||||
|
||||
// NewServerBackup creates a new fetcher that will collect pricing information on server backups.
|
||||
func NewServerBackup() Fetcher {
|
||||
return &serverBackup{newBase("server_backup", "location", "type")}
|
||||
func NewServerBackup(pricing *PriceProvider) Fetcher {
|
||||
return &serverBackup{newBase(pricing, "server_backup", "location", "type")}
|
||||
}
|
||||
|
||||
type serverBackup struct {
|
||||
|
@ -36,8 +32,8 @@ func (serverBackup serverBackup) Run(client *hcloud.Client) error {
|
|||
return err
|
||||
}
|
||||
|
||||
hourlyPrice := toBackupPrice(serverPrice.Hourly.Gross)
|
||||
monthlyPrice := toBackupPrice(serverPrice.Monthly.Gross)
|
||||
hourlyPrice := serverBackup.toBackupPrice(serverPrice.Hourly.Gross)
|
||||
monthlyPrice := serverBackup.toBackupPrice(serverPrice.Monthly.Gross)
|
||||
|
||||
serverBackup.hourly.WithLabelValues(s.Name, location.Name, s.ServerType.Name).Set(hourlyPrice)
|
||||
serverBackup.monthly.WithLabelValues(s.Name, location.Name, s.ServerType.Name).Set(monthlyPrice)
|
||||
|
@ -47,11 +43,11 @@ func (serverBackup serverBackup) Run(client *hcloud.Client) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func toBackupPrice(rawServerPrice string) float64 {
|
||||
serverPrice, err := strconv.Atoi(rawServerPrice)
|
||||
func (serverBackup serverBackup) toBackupPrice(rawServerPrice string) float64 {
|
||||
serverPrice, err := strconv.ParseFloat(rawServerPrice, 32)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return float64(serverPrice) * backupPriceMultiplier
|
||||
return serverPrice * serverBackup.pricing.ServerBackup()
|
||||
}
|
||||
|
|
|
@ -6,15 +6,11 @@ import (
|
|||
"github.com/hetznercloud/hcloud-go/hcloud"
|
||||
)
|
||||
|
||||
const (
|
||||
trafficPrice = 1.19
|
||||
)
|
||||
|
||||
var _ Fetcher = &serverTraffic{}
|
||||
|
||||
// NewServerTraffic creates a new fetcher that will collect pricing information on server traffic.
|
||||
func NewServerTraffic() Fetcher {
|
||||
return &serverTraffic{newBase("server_traffic", "location", "type")}
|
||||
func NewServerTraffic(pricing *PriceProvider) Fetcher {
|
||||
return &serverTraffic{newBase(pricing, "server_traffic", "location", "type")}
|
||||
}
|
||||
|
||||
type serverTraffic struct {
|
||||
|
@ -37,7 +33,7 @@ func (serverTraffic serverTraffic) Run(client *hcloud.Client) error {
|
|||
break
|
||||
}
|
||||
|
||||
monthlyPrice := math.Ceil(float64(additionalTraffic)/sizeTB) * trafficPrice
|
||||
monthlyPrice := math.Ceil(float64(additionalTraffic)/sizeTB) * serverTraffic.pricing.Traffic()
|
||||
hourlyPrice := pricingPerHour(monthlyPrice)
|
||||
|
||||
serverTraffic.hourly.WithLabelValues(s.Name, location.Name, s.ServerType.Name).Set(hourlyPrice)
|
||||
|
|
|
@ -6,15 +6,11 @@ import (
|
|||
"github.com/hetznercloud/hcloud-go/hcloud"
|
||||
)
|
||||
|
||||
const (
|
||||
imagePrice = 0.0119
|
||||
)
|
||||
|
||||
var _ Fetcher = &snapshot{}
|
||||
|
||||
// NewSnapshot creates a new fetcher that will collect pricing information on server snapshots.
|
||||
func NewSnapshot() Fetcher {
|
||||
return &snapshot{newBase("snapshot")}
|
||||
func NewSnapshot(pricing *PriceProvider) Fetcher {
|
||||
return &snapshot{newBase(pricing, "snapshot")}
|
||||
}
|
||||
|
||||
type snapshot struct {
|
||||
|
@ -29,7 +25,7 @@ func (snapshot snapshot) Run(client *hcloud.Client) error {
|
|||
|
||||
for _, i := range images {
|
||||
if i.Type == "snapshot" {
|
||||
monthlyPrice := math.Ceil(float64(i.ImageSize)/sizeGB) * imagePrice
|
||||
monthlyPrice := math.Ceil(float64(i.ImageSize)/sizeGB) * snapshot.pricing.Image()
|
||||
hourlyPrice := pricingPerHour(monthlyPrice)
|
||||
|
||||
snapshot.hourly.WithLabelValues(i.Name).Set(hourlyPrice)
|
||||
|
|
|
@ -7,15 +7,11 @@ import (
|
|||
"github.com/hetznercloud/hcloud-go/hcloud"
|
||||
)
|
||||
|
||||
const (
|
||||
volumePrice = float64(0.0476)
|
||||
)
|
||||
|
||||
var _ Fetcher = &volume{}
|
||||
|
||||
// NewVolume creates a new fetcher that will collect pricing information on volumes.
|
||||
func NewVolume() Fetcher {
|
||||
return &server{newBase("volume", "location", "bytes")}
|
||||
func NewVolume(pricing *PriceProvider) Fetcher {
|
||||
return &server{newBase(pricing, "volume", "location", "bytes")}
|
||||
}
|
||||
|
||||
type volume struct {
|
||||
|
@ -29,7 +25,7 @@ func (volume volume) Run(client *hcloud.Client) error {
|
|||
}
|
||||
|
||||
for _, v := range volumes {
|
||||
monthlyPrice := math.Ceil(float64(v.Size/sizeGB)) * volumePrice
|
||||
monthlyPrice := math.Ceil(float64(v.Size/sizeGB)) * volume.pricing.Volume()
|
||||
hourlyPrice := pricingPerHour(monthlyPrice)
|
||||
|
||||
volume.hourly.WithLabelValues(v.Name, v.Location.Name, strconv.Itoa(v.Size)).Set(hourlyPrice)
|
||||
|
|
24
main.go
24
main.go
|
@ -19,16 +19,6 @@ const (
|
|||
defaultFetchInterval = 1 * time.Minute
|
||||
)
|
||||
|
||||
var (
|
||||
floatingIP = fetcher.NewFloatingIP()
|
||||
loadBalancer = fetcher.NewLoadbalancer()
|
||||
server = fetcher.NewServer()
|
||||
serverBackup = fetcher.NewServerBackup()
|
||||
serverTraffic = fetcher.NewServerTraffic()
|
||||
snapshot = fetcher.NewSnapshot()
|
||||
volume = fetcher.NewVolume()
|
||||
)
|
||||
|
||||
func toScheduler(client *hcloud.Client, f func(*hcloud.Client) error) func() {
|
||||
return func() {
|
||||
if err := f(client); err != nil {
|
||||
|
@ -59,6 +49,18 @@ func main() {
|
|||
}
|
||||
|
||||
client := hcloud.NewClient(hcloud.WithToken(hcloudAPIToken))
|
||||
priceRepository := &fetcher.PriceProvider{Client: client}
|
||||
|
||||
var (
|
||||
floatingIP = fetcher.NewFloatingIP(priceRepository)
|
||||
loadBalancer = fetcher.NewLoadbalancer(priceRepository)
|
||||
server = fetcher.NewServer(priceRepository)
|
||||
serverBackup = fetcher.NewServerBackup(priceRepository)
|
||||
serverTraffic = fetcher.NewServerTraffic(priceRepository)
|
||||
snapshot = fetcher.NewSnapshot(priceRepository)
|
||||
volume = fetcher.NewVolume(priceRepository)
|
||||
)
|
||||
|
||||
scheduler.RunTaskAtInterval(toScheduler(client, floatingIP.Run), fetchInterval, 0)
|
||||
scheduler.RunTaskAtInterval(toScheduler(client, loadBalancer.Run), fetchInterval, 0)
|
||||
scheduler.RunTaskAtInterval(toScheduler(client, server.Run), fetchInterval, 0)
|
||||
|
@ -67,6 +69,8 @@ func main() {
|
|||
scheduler.RunTaskAtInterval(toScheduler(client, snapshot.Run), fetchInterval, 0)
|
||||
scheduler.RunTaskAtInterval(toScheduler(client, volume.Run), fetchInterval, 0)
|
||||
|
||||
scheduler.RunTaskAtInterval(priceRepository.Sync, 10*fetchInterval, 10*fetchInterval)
|
||||
|
||||
registry := prometheus.NewRegistry()
|
||||
registry.MustRegister(
|
||||
floatingIP.GetHourly(),
|
||||
|
|
Loading…
Reference in a new issue