Skip to content
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
9 changes: 4 additions & 5 deletions cmd/bot/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,10 @@ type config struct {

GitHub github.Config

IsCI bool `envconfig:"CI"`
PR int `envconfig:"GITHUB_PULL_REQUEST" required:"true"`
Event string `envconfig:"GITHUB_EVENT_NAME"`
LogLevel string `envconfig:"LOG_LEVEL" default:"info"`
UseCloudCostExporterMetrics bool `envconfig:"USE_CLOUD_COST_EXPORTER" default:"false"`
IsCI bool `envconfig:"CI"`
PR int `envconfig:"GITHUB_PULL_REQUEST" required:"true"`
Event string `envconfig:"GITHUB_EVENT_NAME"`
LogLevel string `envconfig:"LOG_LEVEL" default:"info"`
}

const pullRequestEvent = "pull_request"
Expand Down
18 changes: 8 additions & 10 deletions cmd/bot/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,16 @@ func realMain(ctx context.Context) error {

prometheusClients, err := costmodel.NewClients(
&costmodel.ClientConfig{
Address: cfg.Prometheus.Prod.Address,
HTTPConfigFile: cfg.Prometheus.Prod.HTTPConfigFile,
Username: cfg.Prometheus.Prod.Username,
Password: cfg.Prometheus.Prod.Password,
UseCloudCostExporterMetrics: cfg.UseCloudCostExporterMetrics,
Address: cfg.Prometheus.Prod.Address,
HTTPConfigFile: cfg.Prometheus.Prod.HTTPConfigFile,
Username: cfg.Prometheus.Prod.Username,
Password: cfg.Prometheus.Prod.Password,
},
&costmodel.ClientConfig{
Address: cfg.Prometheus.Dev.Address,
HTTPConfigFile: cfg.Prometheus.Dev.HTTPConfigFile,
Username: cfg.Prometheus.Dev.Username,
Password: cfg.Prometheus.Dev.Password,
UseCloudCostExporterMetrics: cfg.UseCloudCostExporterMetrics,
Address: cfg.Prometheus.Dev.Address,
HTTPConfigFile: cfg.Prometheus.Dev.HTTPConfigFile,
Username: cfg.Prometheus.Dev.Username,
Password: cfg.Prometheus.Dev.Password,
})
if err != nil {
return fmt.Errorf("creating cost model client: %w", err)
Expand Down
15 changes: 6 additions & 9 deletions cmd/estimator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,25 @@ import (

func main() {
var fromFile, toFile, prometheusAddress, httpConfigFile, reportType, username, password string
var useCloudCostExporterMetrics bool
flag.StringVar(&fromFile, "from", "", "The file to compare from")
flag.StringVar(&toFile, "to", "", "The file to compare to")
flag.StringVar(&prometheusAddress, "prometheus.address", "http://localhost:9093/prometheus", "The Address of the prometheus server")
flag.StringVar(&httpConfigFile, "http.config.file", "", "The path to the http config file")
flag.StringVar(&username, "username", "", "Mimir username")
flag.StringVar(&password, "password", "", "Mimir password")
flag.StringVar(&reportType, "report.type", "table", "The type of report to generate. Options are: table, summary")
flag.BoolVar(&useCloudCostExporterMetrics, "use.cloud.cost.exporter.metrics", false, "Whether to use the cloud cost exporter metrics")
flag.Parse()

clusters := flag.Args()

ctx := context.Background()
if err := run(ctx, fromFile, toFile, prometheusAddress, httpConfigFile, reportType, username, password, clusters, useCloudCostExporterMetrics); err != nil {
if err := run(ctx, fromFile, toFile, prometheusAddress, httpConfigFile, reportType, username, password, clusters); err != nil {
fmt.Printf("Could not run: %s\n", err)
os.Exit(1)
}
}

func run(ctx context.Context, fromFile, toFile, address, httpConfigFile, reportType, username, password string, clusters []string, useCloudCostExporterMetrics bool) error {
func run(ctx context.Context, fromFile, toFile, address, httpConfigFile, reportType, username, password string, clusters []string) error {
from, err := os.ReadFile(fromFile)
if err != nil {
return fmt.Errorf("could not read file: %s", err)
Expand All @@ -44,11 +42,10 @@ func run(ctx context.Context, fromFile, toFile, address, httpConfigFile, reportT
}

client, err := costmodel.NewClient(&costmodel.ClientConfig{
Address: address,
HTTPConfigFile: httpConfigFile,
Username: username,
Password: password,
UseCloudCostExporterMetrics: useCloudCostExporterMetrics,
Address: address,
HTTPConfigFile: httpConfigFile,
Username: username,
Password: password,
})

if err != nil {
Expand Down
78 changes: 12 additions & 66 deletions pkg/costmodel/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,7 @@ import (
)

const (
queryCostPerCPU = `
avg by (spot) (node_cpu_hourly_cost{cluster="%s"}
* on (cluster, node) group_left(spot)
group by (cluster, node, spot) (
label_replace(
label_join(kubecost_node_is_spot == 1, "node", "", "exported_instance")
,"spot", "true", "", ""
)
or on (cluster, node)
label_replace(
label_join(kubecost_node_is_spot == 0, "node", "", "exported_instance")
,"spot", "false", "", ""
)
)
)
`
cloudcostExporterQueryCostPerCpu = `
queryCostPerCpu = `
avg by (price_tier) (
cloudcost_aws_ec2_instance_cpu_usd_per_core_hour{cluster_name="%s"}
or
Expand All @@ -41,24 +25,7 @@ avg by (spot) (node_cpu_hourly_cost{cluster="%s"}
cloudcost_gcp_gke_instance_cpu_usd_per_core_hour{cluster_name="%s"}
)
`

queryMemoryCost = `
avg by (spot) (node_ram_hourly_cost{cluster="%s"}
* on (cluster, node) group_left(spot)
group by (cluster, node, spot) (
label_replace(
label_join(kubecost_node_is_spot == 1, "node", "", "exported_instance")
,"spot", "true", "", ""
)
or on (cluster, node)
label_replace(
label_join(kubecost_node_is_spot == 0, "node", "", "exported_instance")
,"spot", "false", "", ""
)
)
)
`
cloudcostExporterQueryMemoryCost = `
avg by (price_tier) (
cloudcost_aws_ec2_instance_memory_usd_per_gib_hour{cluster_name="%s"}
or
Expand All @@ -67,13 +34,9 @@ avg by (spot) (node_ram_hourly_cost{cluster="%s"}
cloudcost_gcp_gke_instance_memory_usd_per_gib_hour{cluster_name="%s"}
)
`

// TODO(@Pokom): update this query with azure's PVC's cost once https://github.com/grafana/cloudcost-exporter/issues/236 is merged in
queryPersistentVolumeCost = `
avg_over_time(
avg(
pv_hourly_cost{cluster="%s"}
)[24h:1m]
)`
cloudcostQueryPersistentVolumeCost = `
avg(
cloudcost_aws_ec2_persistent_volume_usd_per_hour{persistentvolume!="", state="in-use"}
/ on (persistentvolume) group_left() (
Expand Down Expand Up @@ -112,8 +75,7 @@ var (

// Client is a client for the cost model.
type Client struct {
client api.Client
useCloudCostExporterMetrics bool
client api.Client
}

// Clients bundles the dev and prod client in one struct.
Expand All @@ -124,11 +86,10 @@ type Clients struct {

// ClientConfig is the configuration for the cost model client.
type ClientConfig struct {
Address string
HTTPConfigFile string
Username string
Password string
UseCloudCostExporterMetrics bool
Address string
HTTPConfigFile string
Username string
Password string
}

// NewClient creates a new cost model client with the given configuration.
Expand Down Expand Up @@ -168,8 +129,7 @@ func NewClient(config *ClientConfig) (*Client, error) {
return nil, err
}
return &Client{
client: client,
useCloudCostExporterMetrics: config.UseCloudCostExporterMetrics,
client: client,
}, nil
}

Expand All @@ -189,12 +149,7 @@ func NewClients(prodConfig, devConfig *ClientConfig) (*Clients, error) {

// GetCostPerCPU returns the average cost per CPU for a given cluster.
func (c *Client) GetCostPerCPU(ctx context.Context, cluster string) (Cost, error) {
query := fmt.Sprintf(queryCostPerCPU, cluster)
// TODO: Remove this once we've removed support for OpenCost
if c.useCloudCostExporterMetrics {
slog.Info("GetMemoryCost", "cluster", cluster, "message", "using cloudcost exporter metrics")
query = fmt.Sprintf(cloudcostExporterQueryCostPerCpu, cluster, cluster, cluster)
}
query := fmt.Sprintf(queryCostPerCpu, cluster, cluster, cluster)
results, err := c.query(ctx, query)
if err != nil {
return Cost{}, err
Expand All @@ -204,12 +159,7 @@ func (c *Client) GetCostPerCPU(ctx context.Context, cluster string) (Cost, error

// GetMemoryCost returns the cost per memory for a given cluster
func (c *Client) GetMemoryCost(ctx context.Context, cluster string) (Cost, error) {
query := fmt.Sprintf(queryMemoryCost, cluster)
// TODO: Remove this once we've removed support for OpenCost
if c.useCloudCostExporterMetrics {
slog.Info("GetMemoryCost", "cluster", cluster, "message", "using cloudcost exporter metrics")
query = fmt.Sprintf(cloudcostExporterQueryMemoryCost, cluster, cluster, cluster)
}
query := fmt.Sprintf(queryMemoryCost, cluster, cluster, cluster)
results, err := c.query(ctx, query)
if err != nil {
return Cost{}, err
Expand All @@ -235,11 +185,7 @@ func (c *Client) GetNodeCount(ctx context.Context, cluster string) (int, error)

// GetCostForPersistentVolume returns the average cost per persistent volume for a given cluster
func (c *Client) GetCostForPersistentVolume(ctx context.Context, cluster string) (Cost, error) {
query := fmt.Sprintf(queryPersistentVolumeCost, cluster)
if c.useCloudCostExporterMetrics {
slog.Info("GetCostForPersistentVolume", "cluster", cluster, "message", "using cloudcost exporter metrics")
query = fmt.Sprintf(cloudcostQueryPersistentVolumeCost, cluster, cluster, cluster, cluster)
}
query := fmt.Sprintf(queryPersistentVolumeCost, cluster, cluster, cluster, cluster)
results, err := c.query(ctx, query)
if err != nil {
return Cost{}, err
Expand Down
Loading