|
|
@@ -1,194 +0,0 @@
|
|
|
-package costmodel_test
|
|
|
-
|
|
|
-import (
|
|
|
- "context"
|
|
|
- "encoding/json"
|
|
|
- "fmt"
|
|
|
-
|
|
|
- // "math"
|
|
|
- // "net"
|
|
|
- "net/http"
|
|
|
- "strconv"
|
|
|
-
|
|
|
- // "testing"
|
|
|
- // "time"
|
|
|
-
|
|
|
- // "gotest.tools/assert"
|
|
|
-
|
|
|
- prometheusClient "github.com/prometheus/client_golang/api"
|
|
|
-
|
|
|
- // v1 "k8s.io/api/core/v1"
|
|
|
- // metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
-
|
|
|
- _ "k8s.io/client-go/plugin/pkg/client/auth"
|
|
|
-
|
|
|
- "log"
|
|
|
-)
|
|
|
-
|
|
|
-const apiPrefix = "/api/v1"
|
|
|
-
|
|
|
-const epQuery = apiPrefix + "/query"
|
|
|
-
|
|
|
-// The integration test assumes a GKE cluster in us-central-1 or an AWS cluster in us-east-2, with the following instance types
|
|
|
-// and storage classes.
|
|
|
-var prices = map[string]float64{
|
|
|
- "n1standardRAM": 0.004237,
|
|
|
- "n1standardCPU": 0.031611,
|
|
|
- "t2.medium": 0.0464,
|
|
|
- "t2.small": 0.023,
|
|
|
- "t2.micro": 0.0116,
|
|
|
- "c4.large": 0.1,
|
|
|
- "gp2": 0.000137,
|
|
|
- "ssd": 0.170,
|
|
|
- "Standard_DS2_v2": 0.252,
|
|
|
- "g1smallCPU": 0.025643,
|
|
|
- "g1smallRAM": 0.000034,
|
|
|
- "n1-highmem-2": 0.1171,
|
|
|
-}
|
|
|
-
|
|
|
-func parseQuery(qr interface{}) (float64, error) {
|
|
|
- data, ok := qr.(map[string]interface{})["data"]
|
|
|
- if !ok {
|
|
|
- return 0, fmt.Errorf("Improperly formatted response from prometheus, response %+v has no data field", data)
|
|
|
- }
|
|
|
- r, ok := data.(map[string]interface{})["result"]
|
|
|
- if !ok {
|
|
|
- return 0, fmt.Errorf("Improperly formatted data from prometheus, data has no result field")
|
|
|
- }
|
|
|
- results, ok := r.([]interface{})
|
|
|
- if !ok {
|
|
|
- return 0, fmt.Errorf("Improperly formatted results from prometheus, result field is not a slice")
|
|
|
- }
|
|
|
- val, ok := results[0].(map[string]interface{})["value"]
|
|
|
- if !ok {
|
|
|
- return 0, fmt.Errorf("Improperly formatted results from prometheus, value is not a field in the vector")
|
|
|
- }
|
|
|
- dataPoint, ok := val.([]interface{})
|
|
|
- if !ok || len(dataPoint) != 2 {
|
|
|
- return 0, fmt.Errorf("Improperly formatted datapoint from Prometheus")
|
|
|
- }
|
|
|
-
|
|
|
- return strconv.ParseFloat(dataPoint[1].(string), 64)
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-func query(cli prometheusClient.Client, query string) (interface{}, error) {
|
|
|
- u := cli.URL(epQuery, nil)
|
|
|
- q := u.Query()
|
|
|
- q.Set("query", query)
|
|
|
- u.RawQuery = q.Encode()
|
|
|
-
|
|
|
- req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- _, body, _, err := cli.Do(context.Background(), req)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
- var toReturn interface{}
|
|
|
- err = json.Unmarshal(body, &toReturn)
|
|
|
- if err != nil {
|
|
|
- log.Printf("ERROR" + err.Error())
|
|
|
- }
|
|
|
- return toReturn, err
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-func TestKubernetesPVCosts(t *testing.T) {
|
|
|
- cli, err := getKubernetesClient()
|
|
|
- if err != nil {
|
|
|
- panic(err)
|
|
|
- }
|
|
|
- var LongTimeoutRoundTripper http.RoundTripper = &http.Transport{ // may be necessary for long prometheus queries. TODO: make this configurable
|
|
|
- Proxy: http.ProxyFromEnvironment,
|
|
|
- DialContext: (&net.Dialer{
|
|
|
- Timeout: 120 * time.Second,
|
|
|
- KeepAlive: 120 * time.Second,
|
|
|
- }).DialContext,
|
|
|
- TLSHandshakeTimeout: 10 * time.Second,
|
|
|
- }
|
|
|
-
|
|
|
- pc := prometheusClient.Config{
|
|
|
- Address: address,
|
|
|
- RoundTripper: LongTimeoutRoundTripper,
|
|
|
- }
|
|
|
- promCli, err := prometheusClient.NewClient(pc)
|
|
|
- if err != nil {
|
|
|
- panic(err)
|
|
|
- }
|
|
|
-
|
|
|
- pvs, err := cli.CoreV1().PersistentVolumes().List(metav1.ListOptions{})
|
|
|
- if err != nil {
|
|
|
- panic(err)
|
|
|
- }
|
|
|
- for _, pv := range pvs.Items {
|
|
|
- name := pv.Name
|
|
|
- class := pv.Spec.StorageClassName
|
|
|
-
|
|
|
- q := fmt.Sprintf(`pv_hourly_cost{persistentvolume="%s"}`, name)
|
|
|
- qt, err := query(promCli, q)
|
|
|
- total, err := parseQuery(qt)
|
|
|
- if err != nil {
|
|
|
- log.Printf(err.Error())
|
|
|
- }
|
|
|
- if price, ok := prices[class]; ok {
|
|
|
- assert.Equal(t, math.Round(total*1000000)/1000000, price)
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-func TestKubernetesClusterCosts(t *testing.T) {
|
|
|
- prices["n1-standard-1"] = math.Round((prices["n1standardCPU"]+3.61219*prices["n1standardRAM"])*10000) / 10000
|
|
|
- prices["g1-small"] = math.Round(((prices["g1smallCPU"] + 0.216998*prices["g1smallRAM"]) * 10000)) / 10000
|
|
|
- cli, err := getKubernetesClient()
|
|
|
- if err != nil {
|
|
|
- panic(err)
|
|
|
- }
|
|
|
- var LongTimeoutRoundTripper http.RoundTripper = &http.Transport{ // may be necessary for long prometheus queries. TODO: make this configurable
|
|
|
- Proxy: http.ProxyFromEnvironment,
|
|
|
- DialContext: (&net.Dialer{
|
|
|
- Timeout: 120 * time.Second,
|
|
|
- KeepAlive: 120 * time.Second,
|
|
|
- }).DialContext,
|
|
|
- TLSHandshakeTimeout: 10 * time.Second,
|
|
|
- }
|
|
|
-
|
|
|
- pc := prometheusClient.Config{
|
|
|
- Address: address,
|
|
|
- RoundTripper: LongTimeoutRoundTripper,
|
|
|
- }
|
|
|
- promCli, err := prometheusClient.NewClient(pc)
|
|
|
- if err != nil {
|
|
|
- panic(err)
|
|
|
- }
|
|
|
-
|
|
|
- nodes, err := cli.CoreV1().Nodes().List(metav1.ListOptions{})
|
|
|
- if err != nil {
|
|
|
- panic(err)
|
|
|
- }
|
|
|
- for _, n := range nodes.Items {
|
|
|
- name := n.GetObjectMeta().GetName()
|
|
|
- q := fmt.Sprintf(`node_total_hourly_cost{instance="%s"}`, name)
|
|
|
- labels := n.GetObjectMeta().GetLabels()
|
|
|
- instanceType := labels[v1.LabelInstanceType]
|
|
|
-
|
|
|
- qt, err := query(promCli, q)
|
|
|
- if err != nil {
|
|
|
- panic(err)
|
|
|
- }
|
|
|
- total, err := parseQuery(qt)
|
|
|
- if err != nil {
|
|
|
- panic(err)
|
|
|
- }
|
|
|
-
|
|
|
- if price, ok := prices[instanceType]; ok {
|
|
|
- assert.Equal(t, math.Round(total*10000)/10000, price)
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-}
|
|
|
-*/
|