فهرست منبع

Resolve merge conflicts

Niko Kovacevic 6 سال پیش
والد
کامیت
df3a5533fb
10فایلهای تغییر یافته به همراه988 افزوده شده و 2148 حذف شده
  1. 0 5
      Dockerfile
  2. 984 0
      costmodel/router.go
  3. 2 4
      go.mod
  4. 2 978
      main.go
  5. 0 35
      test/costmodel_test.go
  6. 0 17
      test/mocks/go.mod
  7. 0 212
      test/mocks/go.sum
  8. 0 66
      test/mocks/mock_client.go
  9. 0 588
      test/mocks/mock_kubernetes.go
  10. 0 243
      test/mocks/mock_provider.go

+ 0 - 5
Dockerfile

@@ -5,11 +5,6 @@ WORKDIR /app
 COPY go.mod .
 COPY go.sum .
 
-RUN mkdir ./test
-RUN mkdir ./test/mocks
-COPY ./test/mocks/go.mod ./test/mocks/go.mod
-COPY ./test/mocks/go.sum ./test/mocks/go.sum
-
 # Get dependencies - will also be cached if we won't change mod/sum
 RUN go mod download
 # COPY the source code as the last step

+ 984 - 0
costmodel/router.go

@@ -0,0 +1,984 @@
+package costmodel
+
+import (
+	"context"
+	"encoding/json"
+	"flag"
+	"fmt"
+	"net"
+	"net/http"
+	"os"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+
+	"k8s.io/klog"
+
+	"github.com/julienschmidt/httprouter"
+	costAnalyzerCloud "github.com/kubecost/cost-model/cloud"
+	"github.com/patrickmn/go-cache"
+	prometheusClient "github.com/prometheus/client_golang/api"
+	prometheusAPI "github.com/prometheus/client_golang/api/prometheus/v1"
+	"github.com/prometheus/client_golang/prometheus"
+	v1 "k8s.io/api/core/v1"
+
+	"k8s.io/client-go/kubernetes"
+	"k8s.io/client-go/rest"
+)
+
+const (
+	prometheusServerEndpointEnvVar = "PROMETHEUS_SERVER_ENDPOINT"
+	prometheusTroubleshootingEp    = "http://docs.kubecost.com/custom-prom#troubleshoot"
+)
+
+var (
+	// gitCommit is set by the build system
+	gitCommit string
+)
+
+var Router = httprouter.New()
+var A Accesses
+
+type Accesses struct {
+	PrometheusClient              prometheusClient.Client
+	KubeClientSet                 kubernetes.Interface
+	Cloud                         costAnalyzerCloud.Provider
+	CPUPriceRecorder              *prometheus.GaugeVec
+	RAMPriceRecorder              *prometheus.GaugeVec
+	PersistentVolumePriceRecorder *prometheus.GaugeVec
+	GPUPriceRecorder              *prometheus.GaugeVec
+	NodeTotalPriceRecorder        *prometheus.GaugeVec
+	RAMAllocationRecorder         *prometheus.GaugeVec
+	CPUAllocationRecorder         *prometheus.GaugeVec
+	GPUAllocationRecorder         *prometheus.GaugeVec
+	PVAllocationRecorder          *prometheus.GaugeVec
+	ContainerUptimeRecorder       *prometheus.GaugeVec
+	NetworkZoneEgressRecorder     prometheus.Gauge
+	NetworkRegionEgressRecorder   prometheus.Gauge
+	NetworkInternetEgressRecorder prometheus.Gauge
+	ServiceSelectorRecorder       *prometheus.GaugeVec
+	DeploymentSelectorRecorder    *prometheus.GaugeVec
+	Model                         *CostModel
+	Cache                         *cache.Cache
+}
+
+type DataEnvelope struct {
+	Code    int         `json:"code"`
+	Status  string      `json:"status"`
+	Data    interface{} `json:"data"`
+	Message string      `json:"message,omitempty"`
+}
+
+func normalizeTimeParam(param string) (string, error) {
+	// convert days to hours
+	if param[len(param)-1:] == "d" {
+		count := param[:len(param)-1]
+		val, err := strconv.ParseInt(count, 10, 64)
+		if err != nil {
+			return "", err
+		}
+		val = val * 24
+		param = fmt.Sprintf("%dh", val)
+	}
+
+	return param, nil
+}
+
+func wrapDataWithMessage(data interface{}, err error, message string) []byte {
+	var resp []byte
+
+	if err != nil {
+		klog.V(1).Infof("Error returned to client: %s", err.Error())
+		resp, _ = json.Marshal(&DataEnvelope{
+			Code:    http.StatusInternalServerError,
+			Status:  "error",
+			Message: err.Error(),
+			Data:    data,
+		})
+	} else {
+		resp, _ = json.Marshal(&DataEnvelope{
+			Code:    http.StatusOK,
+			Status:  "success",
+			Data:    data,
+			Message: message,
+		})
+
+	}
+
+	return resp
+}
+
+func wrapData(data interface{}, err error) []byte {
+	var resp []byte
+
+	if err != nil {
+		klog.V(1).Infof("Error returned to client: %s", err.Error())
+		resp, _ = json.Marshal(&DataEnvelope{
+			Code:    http.StatusInternalServerError,
+			Status:  "error",
+			Message: err.Error(),
+			Data:    data,
+		})
+	} else {
+		resp, _ = json.Marshal(&DataEnvelope{
+			Code:   http.StatusOK,
+			Status: "success",
+			Data:   data,
+		})
+
+	}
+
+	return resp
+}
+
+// RefreshPricingData needs to be called when a new node joins the fleet, since we cache the relevant subsets of pricing data to avoid storing the whole thing.
+func (a *Accesses) RefreshPricingData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+
+	err := a.Cloud.DownloadPricingData()
+
+	w.Write(wrapData(nil, err))
+}
+
+func filterFields(fields string, data map[string]*CostData) map[string]CostData {
+	fs := strings.Split(fields, ",")
+	fmap := make(map[string]bool)
+	for _, f := range fs {
+		fieldNameLower := strings.ToLower(f) // convert to go struct name by uppercasing first letter
+		klog.V(1).Infof("to delete: %s", fieldNameLower)
+		fmap[fieldNameLower] = true
+	}
+	filteredData := make(map[string]CostData)
+	for cname, costdata := range data {
+		s := reflect.TypeOf(*costdata)
+		val := reflect.ValueOf(*costdata)
+		costdata2 := CostData{}
+		cd2 := reflect.New(reflect.Indirect(reflect.ValueOf(costdata2)).Type()).Elem()
+		n := s.NumField()
+		for i := 0; i < n; i++ {
+			field := s.Field(i)
+			value := val.Field(i)
+			value2 := cd2.Field(i)
+			if _, ok := fmap[strings.ToLower(field.Name)]; !ok {
+				value2.Set(reflect.Value(value))
+			}
+		}
+		filteredData[cname] = cd2.Interface().(CostData)
+	}
+	return filteredData
+}
+
+func (a *Accesses) CostDataModel(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+
+	window := r.URL.Query().Get("timeWindow")
+	offset := r.URL.Query().Get("offset")
+	fields := r.URL.Query().Get("filterFields")
+	namespace := r.URL.Query().Get("namespace")
+	aggregationField := r.URL.Query().Get("aggregation")
+	aggregationSubField := r.URL.Query().Get("aggregationSubfield")
+
+	if offset != "" {
+		offset = "offset " + offset
+	}
+
+	data, err := a.Model.ComputeCostData(a.PrometheusClient, a.KubeClientSet, a.Cloud, window, offset, namespace)
+	if aggregationField != "" {
+		c, err := a.Cloud.GetConfig()
+		if err != nil {
+			w.Write(wrapData(nil, err))
+		}
+		discount, err := strconv.ParseFloat(c.Discount[:len(c.Discount)-1], 64)
+		if err != nil {
+			w.Write(wrapData(nil, err))
+		}
+		discount = discount * 0.01
+		agg := AggregateCostModel(data, aggregationField, aggregationSubField, false, discount, 1.0, nil)
+		w.Write(wrapData(agg, nil))
+	} else {
+		if fields != "" {
+			filteredData := filterFields(fields, data)
+			w.Write(wrapData(filteredData, err))
+		} else {
+			w.Write(wrapData(data, err))
+		}
+	}
+}
+
+func (a *Accesses) ClusterCosts(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+
+	window := r.URL.Query().Get("window")
+	offset := r.URL.Query().Get("offset")
+
+	if offset != "" {
+		offset = "offset " + offset
+	}
+
+	data, err := ClusterCosts(a.PrometheusClient, a.Cloud, window, offset)
+	w.Write(wrapData(data, err))
+}
+
+func (a *Accesses) ClusterCostsOverTime(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+
+	start := r.URL.Query().Get("start")
+	end := r.URL.Query().Get("end")
+	window := r.URL.Query().Get("window")
+	offset := r.URL.Query().Get("offset")
+
+	if offset != "" {
+		offset = "offset " + offset
+	}
+
+	data, err := ClusterCostsOverTime(a.PrometheusClient, a.Cloud, start, end, window, offset)
+	w.Write(wrapData(data, err))
+}
+
+// AggregateCostModel handles HTTP requests to the aggregated cost model API, which can be parametrized
+// by time period using window and offset, aggregation field using field and subfield (in cases like
+// field=label, subfield=app for grouping by label.app), and filtered by namespace.
+func (a *Accesses) AggregateCostModel(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+
+	window := r.URL.Query().Get("window")
+	offset := r.URL.Query().Get("offset")
+	namespace := r.URL.Query().Get("namespace")
+	cluster := r.URL.Query().Get("cluster")
+	field := r.URL.Query().Get("aggregation")
+	subfield := r.URL.Query().Get("aggregationSubfield")
+	allocateIdle := r.URL.Query().Get("allocateIdle")
+	sharedNamespaces := r.URL.Query().Get("sharedNamespaces")
+	sharedLabelNames := r.URL.Query().Get("sharedLabelNames")
+	sharedLabelValues := r.URL.Query().Get("sharedLabelValues")
+	remote := r.URL.Query().Get("remote")
+
+	// timeSeries == true maintains the time series dimension of the data,
+	// which by default gets summed over the entire interval
+	timeSeries := r.URL.Query().Get("timeSeries") == "true"
+
+	// disableCache, if set to "true", tells this function to recompute and
+	// cache the requested data
+	disableCache := r.URL.Query().Get("disableCache") == "true"
+
+	// clearCache, if set to "true", tells this function to flush the cache,
+	// then recompute and cache the requested data
+	clearCache := r.URL.Query().Get("clearCache") == "true"
+
+	// aggregation field is required
+	if field == "" {
+		w.WriteHeader(http.StatusBadRequest)
+		w.Write(wrapData(nil, fmt.Errorf("Missing aggregation field parameter")))
+		return
+	}
+
+	// aggregation subfield is required when aggregation field is "label"
+	if field == "label" && subfield == "" {
+		w.WriteHeader(http.StatusBadRequest)
+		w.Write(wrapData(nil, fmt.Errorf("Missing aggregation subfield parameter for aggregation by label")))
+		return
+	}
+
+	// endTime defaults to the current time, unless an offset is explicity declared,
+	// in which case it shifts endTime back by given duration
+	endTime := time.Now()
+	if offset != "" {
+		o, err := time.ParseDuration(offset)
+		if err != nil {
+			w.Write(wrapData(nil, err))
+			return
+		}
+
+		endTime = endTime.Add(-1 * o)
+	}
+
+	// if window or offset are defined in terms of days, convert to hours
+	// e.g. convert "2d" to "48h"
+	window, err := normalizeTimeParam(window)
+	if err != nil {
+		w.Write(wrapData(nil, err))
+		return
+	}
+
+	offset, err = normalizeTimeParam(offset)
+	if err != nil {
+		w.Write(wrapData(nil, err))
+		return
+	}
+
+	// convert time window into start and end times, formatted
+	// as ISO datetime strings
+	d, err := time.ParseDuration(window)
+	if err != nil {
+		w.Write(wrapData(nil, err))
+		return
+	}
+
+	startTime := endTime.Add(-1 * d)
+	layout := "2006-01-02T15:04:05.000Z"
+	start := startTime.Format(layout)
+	end := endTime.Format(layout)
+
+	// clear cache prior to checking the cache so that a clearCache=true
+	// request always returns a freshly computed value
+	if clearCache {
+		a.Cache.Flush()
+	}
+
+	aggKey := fmt.Sprintf("aggregate:%s:%s:%s:%s:%s:%t", window, offset, namespace, field, subfield, timeSeries)
+
+	// check the cache for aggregated response; if cache is hit and not disabled, return response
+	if result, found := a.Cache.Get(aggKey); found && !disableCache {
+		w.Write(wrapDataWithMessage(result, nil, fmt.Sprintf("cache hit: %s", aggKey)))
+		return
+	}
+
+	remoteAvailable := os.Getenv(remoteEnabled)
+	remoteEnabled := false
+	if remoteAvailable == "true" && remote != "false" {
+		remoteEnabled = true
+	}
+	klog.Infof("REMOTE ENABLED: %t", remoteEnabled)
+
+	data, err := a.Model.ComputeCostDataRange(a.PrometheusClient, a.KubeClientSet, a.Cloud, start, end, "1h", namespace, cluster, remoteEnabled)
+	if err != nil {
+		w.Write(wrapData(nil, err))
+		return
+	}
+
+	c, err := a.Cloud.GetConfig()
+	if err != nil {
+		w.Write(wrapData(nil, err))
+		return
+	}
+	discount, err := strconv.ParseFloat(c.Discount[:len(c.Discount)-1], 64)
+	if err != nil {
+		w.Write(wrapData(nil, err))
+		return
+	}
+	discount = discount * 0.01
+
+	idleCoefficient := 1.0
+	if allocateIdle == "true" {
+		idleCoefficient, err = ComputeIdleCoefficient(data, a.PrometheusClient, a.Cloud, discount, fmt.Sprintf("%dh", int(d.Hours())), offset)
+		if err != nil {
+			w.Write(wrapData(nil, err))
+		}
+	}
+
+	sn := []string{}
+	sln := []string{}
+	slv := []string{}
+	if sharedNamespaces != "" {
+		sn = strings.Split(sharedNamespaces, ",")
+	}
+	if sharedLabelNames != "" {
+		sln = strings.Split(sharedLabelNames, ",")
+		slv = strings.Split(sharedLabelValues, ",")
+		if len(sln) != len(slv) || slv[0] == "" {
+			w.Write(wrapData(nil, fmt.Errorf("Supply exacly one label value per label name")))
+			return
+		}
+	}
+	var sr *SharedResourceInfo
+	if len(sn) > 0 || len(sln) > 0 {
+		sr = NewSharedResourceInfo(true, sn, sln, slv)
+	}
+
+	// aggregate cost model data by given fields and cache the result for the default expiration
+	result := AggregateCostModel(data, field, subfield, timeSeries, discount, idleCoefficient, sr)
+	a.Cache.Set(aggKey, result, cache.DefaultExpiration)
+
+	w.Write(wrapDataWithMessage(result, nil, fmt.Sprintf("cache miss: %s", aggKey)))
+}
+
+func (a *Accesses) CostDataModelRange(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+
+	start := r.URL.Query().Get("start")
+	end := r.URL.Query().Get("end")
+	window := r.URL.Query().Get("window")
+	fields := r.URL.Query().Get("filterFields")
+	namespace := r.URL.Query().Get("namespace")
+	cluster := r.URL.Query().Get("cluster")
+	aggregationField := r.URL.Query().Get("aggregation")
+	aggregationSubField := r.URL.Query().Get("aggregationSubfield")
+	remote := r.URL.Query().Get("remote")
+
+	remoteAvailable := os.Getenv(remoteEnabled)
+	remoteEnabled := false
+	if remoteAvailable == "true" && remote != "false" {
+		remoteEnabled = true
+	}
+	data, err := a.Model.ComputeCostDataRange(a.PrometheusClient, a.KubeClientSet, a.Cloud, start, end, window, namespace, cluster, remoteEnabled)
+	if err != nil {
+		w.Write(wrapData(nil, err))
+	}
+	if aggregationField != "" {
+		c, err := a.Cloud.GetConfig()
+		if err != nil {
+			w.Write(wrapData(nil, err))
+		}
+		discount, err := strconv.ParseFloat(c.Discount[:len(c.Discount)-1], 64)
+		if err != nil {
+			w.Write(wrapData(nil, err))
+		}
+		discount = discount * 0.01
+		agg := AggregateCostModel(data, aggregationField, aggregationSubField, false, discount, 1.0, nil)
+		w.Write(wrapData(agg, nil))
+	} else {
+		if fields != "" {
+			filteredData := filterFields(fields, data)
+			w.Write(wrapData(filteredData, err))
+		} else {
+			w.Write(wrapData(data, err))
+		}
+	}
+}
+
+// CostDataModelRangeLarge is experimental multi-cluster and long-term data storage in SQL support.
+func (a *Accesses) CostDataModelRangeLarge(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+
+	startString := r.URL.Query().Get("start")
+	endString := r.URL.Query().Get("end")
+	windowString := r.URL.Query().Get("window")
+
+	layout := "2006-01-02T15:04:05.000Z"
+
+	var start time.Time
+	var end time.Time
+	var err error
+
+	if windowString == "" {
+		windowString = "1h"
+	}
+	if startString != "" {
+		start, err = time.Parse(layout, startString)
+		if err != nil {
+			klog.V(1).Infof("Error parsing time " + startString + ". Error: " + err.Error())
+			w.Write(wrapData(nil, err))
+		}
+	} else {
+		window, err := time.ParseDuration(windowString)
+		if err != nil {
+			w.Write(wrapData(nil, fmt.Errorf("Invalid duration '%s'", windowString)))
+
+		}
+		start = time.Now().Add(-2 * window)
+	}
+	if endString != "" {
+		end, err = time.Parse(layout, endString)
+		if err != nil {
+			klog.V(1).Infof("Error parsing time " + endString + ". Error: " + err.Error())
+			w.Write(wrapData(nil, err))
+		}
+	} else {
+		end = time.Now()
+	}
+
+	remoteLayout := "2006-01-02T15:04:05Z"
+	remoteStartStr := start.Format(remoteLayout)
+	remoteEndStr := end.Format(remoteLayout)
+	klog.V(1).Infof("Using remote database for query from %s to %s with window %s", startString, endString, windowString)
+
+	data, err := CostDataRangeFromSQL("", "", windowString, remoteStartStr, remoteEndStr)
+	w.Write(wrapData(data, err))
+}
+
+func (a *Accesses) OutofClusterCosts(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+
+	start := r.URL.Query().Get("start")
+	end := r.URL.Query().Get("end")
+	aggregator := r.URL.Query().Get("aggregator")
+
+	data, err := a.Cloud.ExternalAllocations(start, end, aggregator)
+	w.Write(wrapData(data, err))
+}
+
+func (p *Accesses) GetAllNodePricing(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+
+	data, err := p.Cloud.AllNodePricing()
+	w.Write(wrapData(data, err))
+}
+
+func (p *Accesses) GetConfigs(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+	data, err := p.Cloud.GetConfig()
+	w.Write(wrapData(data, err))
+}
+
+func (p *Accesses) UpdateSpotInfoConfigs(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+	data, err := p.Cloud.UpdateConfig(r.Body, costAnalyzerCloud.SpotInfoUpdateType)
+	if err != nil {
+		w.Write(wrapData(data, err))
+		return
+	}
+	w.Write(wrapData(data, err))
+	err = p.Cloud.DownloadPricingData()
+	if err != nil {
+		klog.V(1).Infof("Error redownloading data on config update: %s", err.Error())
+	}
+	return
+}
+
+func (p *Accesses) UpdateAthenaInfoConfigs(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+	data, err := p.Cloud.UpdateConfig(r.Body, costAnalyzerCloud.AthenaInfoUpdateType)
+	if err != nil {
+		w.Write(wrapData(data, err))
+		return
+	}
+	w.Write(wrapData(data, err))
+	return
+}
+
+func (p *Accesses) UpdateBigQueryInfoConfigs(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+	data, err := p.Cloud.UpdateConfig(r.Body, costAnalyzerCloud.BigqueryUpdateType)
+	if err != nil {
+		w.Write(wrapData(data, err))
+		return
+	}
+	w.Write(wrapData(data, err))
+	return
+}
+
+func (p *Accesses) UpdateConfigByKey(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+	data, err := p.Cloud.UpdateConfig(r.Body, "")
+	if err != nil {
+		w.Write(wrapData(data, err))
+		return
+	}
+	w.Write(wrapData(data, err))
+	return
+}
+
+func (p *Accesses) ManagementPlatform(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+
+	data, err := p.Cloud.GetManagementPlatform()
+	if err != nil {
+		w.Write(wrapData(data, err))
+		return
+	}
+	w.Write(wrapData(data, err))
+	return
+}
+
+func (p *Accesses) ClusterInfo(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+
+	data, err := p.Cloud.ClusterInfo()
+	w.Write(wrapData(data, err))
+
+}
+
+func Healthz(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
+	w.WriteHeader(200)
+	w.Header().Set("Content-Length", "0")
+	w.Header().Set("Content-Type", "text/plain")
+}
+
+func (p *Accesses) GetPrometheusMetadata(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+	w.Write(wrapData(ValidatePrometheus(p.PrometheusClient)))
+}
+
+func (p *Accesses) ContainerUptimes(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+	res, err := ComputeUptimes(p.PrometheusClient)
+	w.Write(wrapData(res, err))
+}
+
+func (a *Accesses) recordPrices() {
+	go func() {
+		containerSeen := make(map[string]bool)
+		nodeSeen := make(map[string]bool)
+		pvSeen := make(map[string]bool)
+		pvcSeen := make(map[string]bool)
+
+		getKeyFromLabelStrings := func(labels ...string) string {
+			return strings.Join(labels, ",")
+		}
+		getLabelStringsFromKey := func(key string) []string {
+			return strings.Split(key, ",")
+		}
+
+		for {
+			klog.V(4).Info("Recording prices...")
+			podlist := a.Model.Cache.GetAllPods()
+			podStatus := make(map[string]v1.PodPhase)
+			for _, pod := range podlist {
+				podStatus[pod.Name] = pod.Status.Phase
+			}
+
+			// Record network pricing at global scope
+			networkCosts, err := a.Cloud.NetworkPricing()
+			if err != nil {
+				klog.V(4).Infof("Failed to retrieve network costs: %s", err.Error())
+			} else {
+				a.NetworkZoneEgressRecorder.Set(networkCosts.ZoneNetworkEgressCost)
+				a.NetworkRegionEgressRecorder.Set(networkCosts.RegionNetworkEgressCost)
+				a.NetworkInternetEgressRecorder.Set(networkCosts.InternetNetworkEgressCost)
+			}
+
+			data, err := a.Model.ComputeCostData(a.PrometheusClient, a.KubeClientSet, a.Cloud, "2m", "", "")
+			if err != nil {
+				klog.V(1).Info("Error in price recording: " + err.Error())
+				// zero the for loop so the time.Sleep will still work
+				data = map[string]*CostData{}
+			}
+
+			for _, costs := range data {
+				nodeName := costs.NodeName
+				node := costs.NodeData
+				if node == nil {
+					klog.V(4).Infof("Skipping Node \"%s\" due to missing Node Data costs", nodeName)
+					continue
+				}
+				cpuCost, _ := strconv.ParseFloat(node.VCPUCost, 64)
+				cpu, _ := strconv.ParseFloat(node.VCPU, 64)
+				ramCost, _ := strconv.ParseFloat(node.RAMCost, 64)
+				ram, _ := strconv.ParseFloat(node.RAMBytes, 64)
+				gpu, _ := strconv.ParseFloat(node.GPU, 64)
+				gpuCost, _ := strconv.ParseFloat(node.GPUCost, 64)
+
+				totalCost := cpu*cpuCost + ramCost*(ram/1024/1024/1024) + gpu*gpuCost
+
+				namespace := costs.Namespace
+				podName := costs.PodName
+				containerName := costs.Name
+
+				if costs.PVCData != nil {
+					for _, pvc := range costs.PVCData {
+						if pvc.Volume != nil {
+							a.PVAllocationRecorder.WithLabelValues(namespace, podName, pvc.Claim, pvc.VolumeName).Set(pvc.Values[0].Value)
+							labelKey := getKeyFromLabelStrings(namespace, podName, pvc.Claim, pvc.VolumeName)
+							pvcSeen[labelKey] = true
+						}
+					}
+				}
+
+				a.CPUPriceRecorder.WithLabelValues(nodeName, nodeName).Set(cpuCost)
+				a.RAMPriceRecorder.WithLabelValues(nodeName, nodeName).Set(ramCost)
+				a.GPUPriceRecorder.WithLabelValues(nodeName, nodeName).Set(gpuCost)
+				a.NodeTotalPriceRecorder.WithLabelValues(nodeName, nodeName).Set(totalCost)
+				labelKey := getKeyFromLabelStrings(nodeName, nodeName)
+				nodeSeen[labelKey] = true
+
+				if len(costs.RAMAllocation) > 0 {
+					a.RAMAllocationRecorder.WithLabelValues(namespace, podName, containerName, nodeName, nodeName).Set(costs.RAMAllocation[0].Value)
+				}
+				if len(costs.CPUAllocation) > 0 {
+					a.CPUAllocationRecorder.WithLabelValues(namespace, podName, containerName, nodeName, nodeName).Set(costs.CPUAllocation[0].Value)
+				}
+				if len(costs.GPUReq) > 0 {
+					// allocation here is set to the request because shared GPU usage not yet supported.
+					a.GPUAllocationRecorder.WithLabelValues(namespace, podName, containerName, nodeName, nodeName).Set(costs.GPUReq[0].Value)
+				}
+				labelKey = getKeyFromLabelStrings(namespace, podName, containerName, nodeName, nodeName)
+				if podStatus[podName] == v1.PodRunning { // Only report data for current pods
+					containerSeen[labelKey] = true
+				} else {
+					containerSeen[labelKey] = false
+				}
+
+				storageClasses := a.Model.Cache.GetAllStorageClasses()
+				storageClassMap := make(map[string]map[string]string)
+				for _, storageClass := range storageClasses {
+					params := storageClass.Parameters
+					storageClassMap[storageClass.ObjectMeta.Name] = params
+					if storageClass.GetAnnotations()["storageclass.kubernetes.io/is-default-class"] == "true" || storageClass.GetAnnotations()["storageclass.beta.kubernetes.io/is-default-class"] == "true" {
+						storageClassMap["default"] = params
+						storageClassMap[""] = params
+					}
+				}
+
+				pvs := a.Model.Cache.GetAllPersistentVolumes()
+				for _, pv := range pvs {
+					parameters, ok := storageClassMap[pv.Spec.StorageClassName]
+					if !ok {
+						klog.V(4).Infof("Unable to find parameters for storage class \"%s\". Does pv \"%s\" have a storageClassName?", pv.Spec.StorageClassName, pv.Name)
+					}
+					cacPv := &costAnalyzerCloud.PV{
+						Class:      pv.Spec.StorageClassName,
+						Region:     pv.Labels[v1.LabelZoneRegion],
+						Parameters: parameters,
+					}
+					GetPVCost(cacPv, pv, a.Cloud)
+					c, _ := strconv.ParseFloat(cacPv.Cost, 64)
+					a.PersistentVolumePriceRecorder.WithLabelValues(pv.Name, pv.Name).Set(c)
+					labelKey := getKeyFromLabelStrings(pv.Name, pv.Name)
+					pvSeen[labelKey] = true
+				}
+				containerUptime, _ := ComputeUptimes(a.PrometheusClient)
+				for key, uptime := range containerUptime {
+					container, _ := NewContainerMetricFromKey(key)
+					a.ContainerUptimeRecorder.WithLabelValues(container.Namespace, container.PodName, container.ContainerName).Set(uptime)
+				}
+			}
+			for labelString, seen := range nodeSeen {
+				if !seen {
+					labels := getLabelStringsFromKey(labelString)
+					a.NodeTotalPriceRecorder.DeleteLabelValues(labels...)
+					a.CPUPriceRecorder.DeleteLabelValues(labels...)
+					a.GPUPriceRecorder.DeleteLabelValues(labels...)
+					a.RAMPriceRecorder.DeleteLabelValues(labels...)
+					delete(nodeSeen, labelString)
+				}
+				nodeSeen[labelString] = false
+			}
+			for labelString, seen := range containerSeen {
+				if !seen {
+					labels := getLabelStringsFromKey(labelString)
+					a.RAMAllocationRecorder.DeleteLabelValues(labels...)
+					a.CPUAllocationRecorder.DeleteLabelValues(labels...)
+					a.GPUAllocationRecorder.DeleteLabelValues(labels...)
+					a.ContainerUptimeRecorder.DeleteLabelValues(labels...)
+					delete(containerSeen, labelString)
+				}
+				containerSeen[labelString] = false
+			}
+			for labelString, seen := range pvSeen {
+				if !seen {
+					labels := getLabelStringsFromKey(labelString)
+					a.PersistentVolumePriceRecorder.DeleteLabelValues(labels...)
+					delete(pvSeen, labelString)
+				}
+				pvSeen[labelString] = false
+			}
+			for labelString, seen := range pvcSeen {
+				if !seen {
+					labels := getLabelStringsFromKey(labelString)
+					a.PVAllocationRecorder.DeleteLabelValues(labels...)
+					delete(pvcSeen, labelString)
+				}
+				pvcSeen[labelString] = false
+			}
+			time.Sleep(time.Minute)
+		}
+	}()
+}
+
+func init() {
+	klog.InitFlags(nil)
+	flag.Set("v", "3")
+	flag.Parse()
+	klog.V(1).Infof("Starting cost-model (git commit \"%s\")", gitCommit)
+
+	address := os.Getenv(prometheusServerEndpointEnvVar)
+	if address == "" {
+		klog.Fatalf("No address for prometheus set in $%s. Aborting.", prometheusServerEndpointEnvVar)
+	}
+
+	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, _ := prometheusClient.NewClient(pc)
+
+	api := prometheusAPI.NewAPI(promCli)
+	_, err := api.Config(context.Background())
+	if err != nil {
+		klog.Fatalf("No valid prometheus config file at %s. Error: %s . Troubleshooting help available at: %s", address, err.Error(), prometheusTroubleshootingEp)
+	}
+	klog.V(1).Info("Success: retrieved a prometheus config file from: " + address)
+
+	_, err = ValidatePrometheus(promCli)
+	if err != nil {
+		klog.Fatalf("Failed to query prometheus at %s. Error: %s . Troubleshooting help available at: %s", address, err.Error(), prometheusTroubleshootingEp)
+	}
+	klog.V(1).Info("Success: retrieved the 'up' query against prometheus at: " + address)
+
+	// Kubernetes API setup
+	kc, err := rest.InClusterConfig()
+	if err != nil {
+		panic(err.Error())
+	}
+	kubeClientset, err := kubernetes.NewForConfig(kc)
+	if err != nil {
+		panic(err.Error())
+	}
+
+	cloudProviderKey := os.Getenv("CLOUD_PROVIDER_API_KEY")
+	cloudProvider, err := costAnalyzerCloud.NewProvider(kubeClientset, cloudProviderKey)
+	if err != nil {
+		panic(err.Error())
+	}
+
+	cpuGv := prometheus.NewGaugeVec(prometheus.GaugeOpts{
+		Name: "node_cpu_hourly_cost",
+		Help: "node_cpu_hourly_cost hourly cost for each cpu on this node",
+	}, []string{"instance", "node"})
+
+	ramGv := prometheus.NewGaugeVec(prometheus.GaugeOpts{
+		Name: "node_ram_hourly_cost",
+		Help: "node_ram_hourly_cost hourly cost for each gb of ram on this node",
+	}, []string{"instance", "node"})
+
+	gpuGv := prometheus.NewGaugeVec(prometheus.GaugeOpts{
+		Name: "node_gpu_hourly_cost",
+		Help: "node_gpu_hourly_cost hourly cost for each gpu on this node",
+	}, []string{"instance", "node"})
+
+	totalGv := prometheus.NewGaugeVec(prometheus.GaugeOpts{
+		Name: "node_total_hourly_cost",
+		Help: "node_total_hourly_cost Total node cost per hour",
+	}, []string{"instance", "node"})
+
+	pvGv := prometheus.NewGaugeVec(prometheus.GaugeOpts{
+		Name: "pv_hourly_cost",
+		Help: "pv_hourly_cost Cost per GB per hour on a persistent disk",
+	}, []string{"volumename", "persistentvolume"})
+
+	RAMAllocation := prometheus.NewGaugeVec(prometheus.GaugeOpts{
+		Name: "container_memory_allocation_bytes",
+		Help: "container_memory_allocation_bytes Bytes of RAM used",
+	}, []string{"namespace", "pod", "container", "instance", "node"})
+
+	CPUAllocation := prometheus.NewGaugeVec(prometheus.GaugeOpts{
+		Name: "container_cpu_allocation",
+		Help: "container_cpu_allocation Percent of a single CPU used in a minute",
+	}, []string{"namespace", "pod", "container", "instance", "node"})
+
+	GPUAllocation := prometheus.NewGaugeVec(prometheus.GaugeOpts{
+		Name: "container_gpu_allocation",
+		Help: "container_gpu_allocation GPU used",
+	}, []string{"namespace", "pod", "container", "instance", "node"})
+	PVAllocation := prometheus.NewGaugeVec(prometheus.GaugeOpts{
+		Name: "pod_pvc_allocation",
+		Help: "pod_pvc_allocation Bytes used by a PVC attached to a pod",
+	}, []string{"namespace", "pod", "persistentvolumeclaim", "persistentvolume"})
+
+	ContainerUptimeRecorder := prometheus.NewGaugeVec(prometheus.GaugeOpts{
+		Name: "container_uptime_seconds",
+		Help: "container_uptime_seconds Seconds a container has been running",
+	}, []string{"namespace", "pod", "container"})
+
+	NetworkZoneEgressRecorder := prometheus.NewGauge(prometheus.GaugeOpts{
+		Name: "kubecost_network_zone_egress_cost",
+		Help: "kubecost_network_zone_egress_cost Total cost per GB egress across zones",
+	})
+	NetworkRegionEgressRecorder := prometheus.NewGauge(prometheus.GaugeOpts{
+		Name: "kubecost_network_region_egress_cost",
+		Help: "kubecost_network_region_egress_cost Total cost per GB egress across regions",
+	})
+	NetworkInternetEgressRecorder := prometheus.NewGauge(prometheus.GaugeOpts{
+		Name: "kubecost_network_internet_egress_cost",
+		Help: "kubecost_network_internet_egress_cost Total cost per GB of internet egress.",
+	})
+
+	prometheus.MustRegister(cpuGv)
+	prometheus.MustRegister(ramGv)
+	prometheus.MustRegister(gpuGv)
+	prometheus.MustRegister(totalGv)
+	prometheus.MustRegister(pvGv)
+	prometheus.MustRegister(RAMAllocation)
+	prometheus.MustRegister(CPUAllocation)
+	prometheus.MustRegister(ContainerUptimeRecorder)
+	prometheus.MustRegister(PVAllocation)
+	prometheus.MustRegister(NetworkZoneEgressRecorder, NetworkRegionEgressRecorder, NetworkInternetEgressRecorder)
+	prometheus.MustRegister(ServiceCollector{
+		KubeClientSet: kubeClientset,
+	})
+	prometheus.MustRegister(DeploymentCollector{
+		KubeClientSet: kubeClientset,
+	})
+
+	// cache responses from model for a default of 2 minutes; clear expired responses every 10 minutes
+	modelCache := cache.New(time.Minute*2, time.Minute*10)
+
+	A = Accesses{
+		PrometheusClient:              promCli,
+		KubeClientSet:                 kubeClientset,
+		Cloud:                         cloudProvider,
+		CPUPriceRecorder:              cpuGv,
+		RAMPriceRecorder:              ramGv,
+		GPUPriceRecorder:              gpuGv,
+		NodeTotalPriceRecorder:        totalGv,
+		RAMAllocationRecorder:         RAMAllocation,
+		CPUAllocationRecorder:         CPUAllocation,
+		GPUAllocationRecorder:         GPUAllocation,
+		PVAllocationRecorder:          PVAllocation,
+		ContainerUptimeRecorder:       ContainerUptimeRecorder,
+		NetworkZoneEgressRecorder:     NetworkZoneEgressRecorder,
+		NetworkRegionEgressRecorder:   NetworkRegionEgressRecorder,
+		NetworkInternetEgressRecorder: NetworkInternetEgressRecorder,
+		PersistentVolumePriceRecorder: pvGv,
+		Model:                         NewCostModel(kubeClientset),
+		Cache:                         modelCache,
+	}
+
+	remoteEnabled := os.Getenv(remoteEnabled)
+	if remoteEnabled == "true" {
+		info, err := cloudProvider.ClusterInfo()
+		klog.Infof("Saving cluster  with id:'%s', and name:'%s' to durable storage", info["id"], info["name"])
+		if err != nil {
+			klog.Infof("Error saving cluster id %s", err.Error())
+		}
+		_, _, err = costAnalyzerCloud.GetOrCreateClusterMeta(info["id"], info["name"])
+		if err != nil {
+			klog.Infof("Unable to set cluster id '%s' for cluster '%s', %s", info["id"], info["name"], err.Error())
+		}
+	}
+
+	err = A.Cloud.DownloadPricingData()
+	if err != nil {
+		klog.V(1).Info("Failed to download pricing data: " + err.Error())
+	}
+
+	A.recordPrices()
+
+	Router.GET("/costDataModel", A.CostDataModel)
+	Router.GET("/costDataModelRange", A.CostDataModelRange)
+	Router.GET("/costDataModelRangeLarge", A.CostDataModelRangeLarge)
+	Router.GET("/outOfClusterCosts", A.OutofClusterCosts)
+	Router.GET("/allNodePricing", A.GetAllNodePricing)
+	Router.GET("/healthz", Healthz)
+	Router.GET("/getConfigs", A.GetConfigs)
+	Router.POST("/refreshPricing", A.RefreshPricingData)
+	Router.POST("/updateSpotInfoConfigs", A.UpdateSpotInfoConfigs)
+	Router.POST("/updateAthenaInfoConfigs", A.UpdateAthenaInfoConfigs)
+	Router.POST("/updateBigQueryInfoConfigs", A.UpdateBigQueryInfoConfigs)
+	Router.POST("/updateConfigByKey", A.UpdateConfigByKey)
+	Router.GET("/clusterCostsOverTime", A.ClusterCostsOverTime)
+	Router.GET("/clusterCosts", A.ClusterCosts)
+	Router.GET("/validatePrometheus", A.GetPrometheusMetadata)
+	Router.GET("/managementPlatform", A.ManagementPlatform)
+	Router.GET("/clusterInfo", A.ClusterInfo)
+	Router.GET("/containerUptimes", A.ContainerUptimes)
+	Router.GET("/aggregatedCostModel", A.AggregateCostModel)
+}

+ 2 - 4
go.mod

@@ -1,9 +1,5 @@
 module github.com/kubecost/cost-model
 
-require github.com/kubecost/test/mocks v0.0.0
-
-replace github.com/kubecost/test/mocks v0.0.0 => ./test/mocks
-
 replace github.com/golang/lint => golang.org/x/lint v0.0.0-20180702182130-06c8688daad7
 
 require (
@@ -40,3 +36,5 @@ require (
 	k8s.io/client-go v0.0.0-20190620085101-78d2af792bab
 	k8s.io/klog v0.4.0
 )
+
+go 1.13

+ 2 - 978
main.go

@@ -1,992 +1,16 @@
 package main
 
 import (
-	"context"
-	"encoding/json"
-	"flag"
-	"fmt"
-	"net"
 	"net/http"
-	"os"
-	"reflect"
-	"strconv"
-	"strings"
-	"time"
 
-	"k8s.io/klog"
-
-	"github.com/julienschmidt/httprouter"
-	costAnalyzerCloud "github.com/kubecost/cost-model/cloud"
 	costModel "github.com/kubecost/cost-model/costmodel"
-	"github.com/patrickmn/go-cache"
-	prometheusClient "github.com/prometheus/client_golang/api"
-	prometheusAPI "github.com/prometheus/client_golang/api/prometheus/v1"
-	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
-	v1 "k8s.io/api/core/v1"
-
-	"k8s.io/client-go/kubernetes"
-	"k8s.io/client-go/rest"
-)
-
-const (
-	prometheusServerEndpointEnvVar = "PROMETHEUS_SERVER_ENDPOINT"
-	prometheusTroubleshootingEp    = "http://docs.kubecost.com/custom-prom#troubleshoot"
-	remoteEnabled                  = "REMOTE_WRITE_ENABLED"
-)
-
-var (
-	// gitCommit is set by the build system
-	gitCommit string
+	"k8s.io/klog"
 )
 
-var Router = httprouter.New()
-
-type Accesses struct {
-	PrometheusClient              prometheusClient.Client
-	KubeClientSet                 kubernetes.Interface
-	Cloud                         costAnalyzerCloud.Provider
-	CPUPriceRecorder              *prometheus.GaugeVec
-	RAMPriceRecorder              *prometheus.GaugeVec
-	PersistentVolumePriceRecorder *prometheus.GaugeVec
-	GPUPriceRecorder              *prometheus.GaugeVec
-	NodeTotalPriceRecorder        *prometheus.GaugeVec
-	RAMAllocationRecorder         *prometheus.GaugeVec
-	CPUAllocationRecorder         *prometheus.GaugeVec
-	GPUAllocationRecorder         *prometheus.GaugeVec
-	PVAllocationRecorder          *prometheus.GaugeVec
-	ContainerUptimeRecorder       *prometheus.GaugeVec
-	NetworkZoneEgressRecorder     prometheus.Gauge
-	NetworkRegionEgressRecorder   prometheus.Gauge
-	NetworkInternetEgressRecorder prometheus.Gauge
-	ServiceSelectorRecorder       *prometheus.GaugeVec
-	DeploymentSelectorRecorder    *prometheus.GaugeVec
-	Model                         *costModel.CostModel
-	Cache                         *cache.Cache
-}
-
-type DataEnvelope struct {
-	Code    int         `json:"code"`
-	Status  string      `json:"status"`
-	Data    interface{} `json:"data"`
-	Message string      `json:"message,omitempty"`
-}
-
-func normalizeTimeParam(param string) (string, error) {
-	// convert days to hours
-	if param[len(param)-1:] == "d" {
-		count := param[:len(param)-1]
-		val, err := strconv.ParseInt(count, 10, 64)
-		if err != nil {
-			return "", err
-		}
-		val = val * 24
-		param = fmt.Sprintf("%dh", val)
-	}
-
-	return param, nil
-}
-
-func wrapDataWithMessage(data interface{}, err error, message string) []byte {
-	var resp []byte
-
-	if err != nil {
-		klog.V(1).Infof("Error returned to client: %s", err.Error())
-		resp, _ = json.Marshal(&DataEnvelope{
-			Code:    http.StatusInternalServerError,
-			Status:  "error",
-			Message: err.Error(),
-			Data:    data,
-		})
-	} else {
-		resp, _ = json.Marshal(&DataEnvelope{
-			Code:    http.StatusOK,
-			Status:  "success",
-			Data:    data,
-			Message: message,
-		})
-
-	}
-
-	return resp
-}
-
-func wrapData(data interface{}, err error) []byte {
-	var resp []byte
-
-	if err != nil {
-		klog.V(1).Infof("Error returned to client: %s", err.Error())
-		resp, _ = json.Marshal(&DataEnvelope{
-			Code:    http.StatusInternalServerError,
-			Status:  "error",
-			Message: err.Error(),
-			Data:    data,
-		})
-	} else {
-		resp, _ = json.Marshal(&DataEnvelope{
-			Code:   http.StatusOK,
-			Status: "success",
-			Data:   data,
-		})
-
-	}
-
-	return resp
-}
-
-// RefreshPricingData needs to be called when a new node joins the fleet, since we cache the relevant subsets of pricing data to avoid storing the whole thing.
-func (a *Accesses) RefreshPricingData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Access-Control-Allow-Origin", "*")
-
-	err := a.Cloud.DownloadPricingData()
-
-	w.Write(wrapData(nil, err))
-}
-
-func filterFields(fields string, data map[string]*costModel.CostData) map[string]costModel.CostData {
-	fs := strings.Split(fields, ",")
-	fmap := make(map[string]bool)
-	for _, f := range fs {
-		fieldNameLower := strings.ToLower(f) // convert to go struct name by uppercasing first letter
-		klog.V(1).Infof("to delete: %s", fieldNameLower)
-		fmap[fieldNameLower] = true
-	}
-	filteredData := make(map[string]costModel.CostData)
-	for cname, costdata := range data {
-		s := reflect.TypeOf(*costdata)
-		val := reflect.ValueOf(*costdata)
-		costdata2 := costModel.CostData{}
-		cd2 := reflect.New(reflect.Indirect(reflect.ValueOf(costdata2)).Type()).Elem()
-		n := s.NumField()
-		for i := 0; i < n; i++ {
-			field := s.Field(i)
-			value := val.Field(i)
-			value2 := cd2.Field(i)
-			if _, ok := fmap[strings.ToLower(field.Name)]; !ok {
-				value2.Set(reflect.Value(value))
-			}
-		}
-		filteredData[cname] = cd2.Interface().(costModel.CostData)
-	}
-	return filteredData
-}
-
-func (a *Accesses) CostDataModel(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Access-Control-Allow-Origin", "*")
-
-	window := r.URL.Query().Get("timeWindow")
-	offset := r.URL.Query().Get("offset")
-	fields := r.URL.Query().Get("filterFields")
-	namespace := r.URL.Query().Get("namespace")
-	aggregationField := r.URL.Query().Get("aggregation")
-	aggregationSubField := r.URL.Query().Get("aggregationSubfield")
-
-	if offset != "" {
-		offset = "offset " + offset
-	}
-
-	data, err := a.Model.ComputeCostData(a.PrometheusClient, a.KubeClientSet, a.Cloud, window, offset, namespace)
-	if aggregationField != "" {
-		c, err := a.Cloud.GetConfig()
-		if err != nil {
-			w.Write(wrapData(nil, err))
-		}
-		discount, err := strconv.ParseFloat(c.Discount[:len(c.Discount)-1], 64)
-		if err != nil {
-			w.Write(wrapData(nil, err))
-		}
-		discount = discount * 0.01
-		agg := costModel.AggregateCostModel(data, aggregationField, aggregationSubField, false, discount, 1.0, nil)
-		w.Write(wrapData(agg, nil))
-	} else {
-		if fields != "" {
-			filteredData := filterFields(fields, data)
-			w.Write(wrapData(filteredData, err))
-		} else {
-			w.Write(wrapData(data, err))
-		}
-	}
-}
-
-func (a *Accesses) ClusterCosts(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Access-Control-Allow-Origin", "*")
-
-	window := r.URL.Query().Get("window")
-	offset := r.URL.Query().Get("offset")
-
-	if offset != "" {
-		offset = "offset " + offset
-	}
-
-	data, err := costModel.ClusterCosts(a.PrometheusClient, a.Cloud, window, offset)
-	w.Write(wrapData(data, err))
-}
-
-func (a *Accesses) ClusterCostsOverTime(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Access-Control-Allow-Origin", "*")
-
-	start := r.URL.Query().Get("start")
-	end := r.URL.Query().Get("end")
-	window := r.URL.Query().Get("window")
-	offset := r.URL.Query().Get("offset")
-
-	if offset != "" {
-		offset = "offset " + offset
-	}
-
-	data, err := costModel.ClusterCostsOverTime(a.PrometheusClient, a.Cloud, start, end, window, offset)
-	w.Write(wrapData(data, err))
-}
-
-// AggregateCostModel handles HTTP requests to the aggregated cost model API, which can be parametrized
-// by time period using window and offset, aggregation field using field and subfield (in cases like
-// field=label, subfield=app for grouping by label.app), and filtered by namespace.
-func (a *Accesses) AggregateCostModel(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Access-Control-Allow-Origin", "*")
-
-	window := r.URL.Query().Get("window")
-	offset := r.URL.Query().Get("offset")
-	namespace := r.URL.Query().Get("namespace")
-	cluster := r.URL.Query().Get("cluster")
-	field := r.URL.Query().Get("aggregation")
-	subfield := r.URL.Query().Get("aggregationSubfield")
-	allocateIdle := r.URL.Query().Get("allocateIdle")
-	sharedNamespaces := r.URL.Query().Get("sharedNamespaces")
-	sharedLabelNames := r.URL.Query().Get("sharedLabelNames")
-	sharedLabelValues := r.URL.Query().Get("sharedLabelValues")
-	remote := r.URL.Query().Get("remote")
-
-	// timeSeries == true maintains the time series dimension of the data,
-	// which by default gets summed over the entire interval
-	timeSeries := r.URL.Query().Get("timeSeries") == "true"
-
-	// disableCache, if set to "true", tells this function to recompute and
-	// cache the requested data
-	disableCache := r.URL.Query().Get("disableCache") == "true"
-
-	// clearCache, if set to "true", tells this function to flush the cache,
-	// then recompute and cache the requested data
-	clearCache := r.URL.Query().Get("clearCache") == "true"
-
-	// aggregation field is required
-	if field == "" {
-		w.WriteHeader(http.StatusBadRequest)
-		w.Write(wrapData(nil, fmt.Errorf("Missing aggregation field parameter")))
-		return
-	}
-
-	// aggregation subfield is required when aggregation field is "label"
-	if field == "label" && subfield == "" {
-		w.WriteHeader(http.StatusBadRequest)
-		w.Write(wrapData(nil, fmt.Errorf("Missing aggregation subfield parameter for aggregation by label")))
-		return
-	}
-
-	// endTime defaults to the current time, unless an offset is explicity declared,
-	// in which case it shifts endTime back by given duration
-	endTime := time.Now()
-	if offset != "" {
-		o, err := time.ParseDuration(offset)
-		if err != nil {
-			w.Write(wrapData(nil, err))
-			return
-		}
-
-		endTime = endTime.Add(-1 * o)
-	}
-
-	// if window or offset are defined in terms of days, convert to hours
-	// e.g. convert "2d" to "48h"
-	window, err := normalizeTimeParam(window)
-	if err != nil {
-		w.Write(wrapData(nil, err))
-		return
-	}
-
-	offset, err = normalizeTimeParam(offset)
-	if err != nil {
-		w.Write(wrapData(nil, err))
-		return
-	}
-
-	// convert time window into start and end times, formatted
-	// as ISO datetime strings
-	d, err := time.ParseDuration(window)
-	if err != nil {
-		w.Write(wrapData(nil, err))
-		return
-	}
-
-	startTime := endTime.Add(-1 * d)
-	layout := "2006-01-02T15:04:05.000Z"
-	start := startTime.Format(layout)
-	end := endTime.Format(layout)
-
-	// clear cache prior to checking the cache so that a clearCache=true
-	// request always returns a freshly computed value
-	if clearCache {
-		a.Cache.Flush()
-	}
-
-	aggKey := fmt.Sprintf("aggregate:%s:%s:%s:%s:%s:%t", window, offset, namespace, field, subfield, timeSeries)
-
-	// check the cache for aggregated response; if cache is hit and not disabled, return response
-	if result, found := a.Cache.Get(aggKey); found && !disableCache {
-		w.Write(wrapDataWithMessage(result, nil, fmt.Sprintf("cache hit: %s", aggKey)))
-		return
-	}
-
-	remoteAvailable := os.Getenv(remoteEnabled)
-	remoteEnabled := false
-	if remoteAvailable == "true" && remote != "false" {
-		remoteEnabled = true
-	}
-	klog.Infof("REMOTE ENABLED: %t", remoteEnabled)
-
-	data, err := a.Model.ComputeCostDataRange(a.PrometheusClient, a.KubeClientSet, a.Cloud, start, end, "1h", namespace, cluster, remoteEnabled)
-	if err != nil {
-		w.Write(wrapData(nil, err))
-		return
-	}
-
-	c, err := a.Cloud.GetConfig()
-	if err != nil {
-		w.Write(wrapData(nil, err))
-		return
-	}
-	discount, err := strconv.ParseFloat(c.Discount[:len(c.Discount)-1], 64)
-	if err != nil {
-		w.Write(wrapData(nil, err))
-		return
-	}
-	discount = discount * 0.01
-
-	idleCoefficient := 1.0
-	if allocateIdle == "true" {
-		idleCoefficient, err = costModel.ComputeIdleCoefficient(data, a.PrometheusClient, a.Cloud, discount, fmt.Sprintf("%dh", int(d.Hours())), offset)
-		if err != nil {
-			w.Write(wrapData(nil, err))
-		}
-	}
-
-	sn := []string{}
-	sln := []string{}
-	slv := []string{}
-	if sharedNamespaces != "" {
-		sn = strings.Split(sharedNamespaces, ",")
-	}
-	if sharedLabelNames != "" {
-		sln = strings.Split(sharedLabelNames, ",")
-		slv = strings.Split(sharedLabelValues, ",")
-		if len(sln) != len(slv) || slv[0] == "" {
-			w.Write(wrapData(nil, fmt.Errorf("Supply exacly one label value per label name")))
-			return
-		}
-	}
-	var sr *costModel.SharedResourceInfo
-	if len(sn) > 0 || len(sln) > 0 {
-		sr = costModel.NewSharedResourceInfo(true, sn, sln, slv)
-	}
-
-	// aggregate cost model data by given fields and cache the result for the default expiration
-	result := costModel.AggregateCostModel(data, field, subfield, timeSeries, discount, idleCoefficient, sr)
-	a.Cache.Set(aggKey, result, cache.DefaultExpiration)
-
-	w.Write(wrapDataWithMessage(result, nil, fmt.Sprintf("cache miss: %s", aggKey)))
-}
-
-func (a *Accesses) CostDataModelRange(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Access-Control-Allow-Origin", "*")
-
-	start := r.URL.Query().Get("start")
-	end := r.URL.Query().Get("end")
-	window := r.URL.Query().Get("window")
-	fields := r.URL.Query().Get("filterFields")
-	namespace := r.URL.Query().Get("namespace")
-	cluster := r.URL.Query().Get("cluster")
-	aggregationField := r.URL.Query().Get("aggregation")
-	aggregationSubField := r.URL.Query().Get("aggregationSubfield")
-	remote := r.URL.Query().Get("remote")
-
-	remoteAvailable := os.Getenv(remoteEnabled)
-	remoteEnabled := false
-	if remoteAvailable == "true" && remote != "false" {
-		remoteEnabled = true
-	}
-	data, err := a.Model.ComputeCostDataRange(a.PrometheusClient, a.KubeClientSet, a.Cloud, start, end, window, namespace, cluster, remoteEnabled)
-	if err != nil {
-		w.Write(wrapData(nil, err))
-	}
-	if aggregationField != "" {
-		c, err := a.Cloud.GetConfig()
-		if err != nil {
-			w.Write(wrapData(nil, err))
-		}
-		discount, err := strconv.ParseFloat(c.Discount[:len(c.Discount)-1], 64)
-		if err != nil {
-			w.Write(wrapData(nil, err))
-		}
-		discount = discount * 0.01
-		agg := costModel.AggregateCostModel(data, aggregationField, aggregationSubField, false, discount, 1.0, nil)
-		w.Write(wrapData(agg, nil))
-	} else {
-		if fields != "" {
-			filteredData := filterFields(fields, data)
-			w.Write(wrapData(filteredData, err))
-		} else {
-			w.Write(wrapData(data, err))
-		}
-	}
-}
-
-// CostDataModelRangeLarge is experimental multi-cluster and long-term data storage in SQL support.
-func (a *Accesses) CostDataModelRangeLarge(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Access-Control-Allow-Origin", "*")
-
-	startString := r.URL.Query().Get("start")
-	endString := r.URL.Query().Get("end")
-	windowString := r.URL.Query().Get("window")
-
-	layout := "2006-01-02T15:04:05.000Z"
-
-	var start time.Time
-	var end time.Time
-	var err error
-
-	if windowString == "" {
-		windowString = "1h"
-	}
-	if startString != "" {
-		start, err = time.Parse(layout, startString)
-		if err != nil {
-			klog.V(1).Infof("Error parsing time " + startString + ". Error: " + err.Error())
-			w.Write(wrapData(nil, err))
-		}
-	} else {
-		window, err := time.ParseDuration(windowString)
-		if err != nil {
-			w.Write(wrapData(nil, fmt.Errorf("Invalid duration '%s'", windowString)))
-
-		}
-		start = time.Now().Add(-2 * window)
-	}
-	if endString != "" {
-		end, err = time.Parse(layout, endString)
-		if err != nil {
-			klog.V(1).Infof("Error parsing time " + endString + ". Error: " + err.Error())
-			w.Write(wrapData(nil, err))
-		}
-	} else {
-		end = time.Now()
-	}
-
-	remoteLayout := "2006-01-02T15:04:05Z"
-	remoteStartStr := start.Format(remoteLayout)
-	remoteEndStr := end.Format(remoteLayout)
-	klog.V(1).Infof("Using remote database for query from %s to %s with window %s", startString, endString, windowString)
-
-	data, err := costModel.CostDataRangeFromSQL("", "", windowString, remoteStartStr, remoteEndStr)
-	w.Write(wrapData(data, err))
-}
-
-func (a *Accesses) OutofClusterCosts(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Access-Control-Allow-Origin", "*")
-
-	start := r.URL.Query().Get("start")
-	end := r.URL.Query().Get("end")
-	aggregator := r.URL.Query().Get("aggregator")
-
-	data, err := a.Cloud.ExternalAllocations(start, end, aggregator)
-	w.Write(wrapData(data, err))
-}
-
-func (p *Accesses) GetAllNodePricing(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Access-Control-Allow-Origin", "*")
-
-	data, err := p.Cloud.AllNodePricing()
-	w.Write(wrapData(data, err))
-}
-
-func (p *Accesses) GetConfigs(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Access-Control-Allow-Origin", "*")
-	data, err := p.Cloud.GetConfig()
-	w.Write(wrapData(data, err))
-}
-
-func (p *Accesses) UpdateSpotInfoConfigs(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Access-Control-Allow-Origin", "*")
-	data, err := p.Cloud.UpdateConfig(r.Body, costAnalyzerCloud.SpotInfoUpdateType)
-	if err != nil {
-		w.Write(wrapData(data, err))
-		return
-	}
-	w.Write(wrapData(data, err))
-	err = p.Cloud.DownloadPricingData()
-	if err != nil {
-		klog.V(1).Infof("Error redownloading data on config update: %s", err.Error())
-	}
-	return
-}
-
-func (p *Accesses) UpdateAthenaInfoConfigs(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Access-Control-Allow-Origin", "*")
-	data, err := p.Cloud.UpdateConfig(r.Body, costAnalyzerCloud.AthenaInfoUpdateType)
-	if err != nil {
-		w.Write(wrapData(data, err))
-		return
-	}
-	w.Write(wrapData(data, err))
-	return
-}
-
-func (p *Accesses) UpdateBigQueryInfoConfigs(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Access-Control-Allow-Origin", "*")
-	data, err := p.Cloud.UpdateConfig(r.Body, costAnalyzerCloud.BigqueryUpdateType)
-	if err != nil {
-		w.Write(wrapData(data, err))
-		return
-	}
-	w.Write(wrapData(data, err))
-	return
-}
-
-func (p *Accesses) UpdateConfigByKey(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Access-Control-Allow-Origin", "*")
-	data, err := p.Cloud.UpdateConfig(r.Body, "")
-	if err != nil {
-		w.Write(wrapData(data, err))
-		return
-	}
-	w.Write(wrapData(data, err))
-	return
-}
-
-func (p *Accesses) ManagementPlatform(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Access-Control-Allow-Origin", "*")
-
-	data, err := p.Cloud.GetManagementPlatform()
-	if err != nil {
-		w.Write(wrapData(data, err))
-		return
-	}
-	w.Write(wrapData(data, err))
-	return
-}
-
-func (p *Accesses) ClusterInfo(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Access-Control-Allow-Origin", "*")
-
-	data, err := p.Cloud.ClusterInfo()
-	w.Write(wrapData(data, err))
-
-}
-
-func Healthz(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
-	w.WriteHeader(200)
-	w.Header().Set("Content-Length", "0")
-	w.Header().Set("Content-Type", "text/plain")
-}
-
-func (p *Accesses) GetPrometheusMetadata(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Access-Control-Allow-Origin", "*")
-	w.Write(wrapData(costModel.ValidatePrometheus(p.PrometheusClient)))
-}
-
-func (p *Accesses) ContainerUptimes(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Access-Control-Allow-Origin", "*")
-	res, err := costModel.ComputeUptimes(p.PrometheusClient)
-	w.Write(wrapData(res, err))
-}
-
-func (a *Accesses) recordPrices() {
-	go func() {
-		containerSeen := make(map[string]bool)
-		nodeSeen := make(map[string]bool)
-		pvSeen := make(map[string]bool)
-		pvcSeen := make(map[string]bool)
-
-		getKeyFromLabelStrings := func(labels ...string) string {
-			return strings.Join(labels, ",")
-		}
-		getLabelStringsFromKey := func(key string) []string {
-			return strings.Split(key, ",")
-		}
-
-		for {
-			klog.V(4).Info("Recording prices...")
-			podlist := a.Model.Cache.GetAllPods()
-			podStatus := make(map[string]v1.PodPhase)
-			for _, pod := range podlist {
-				podStatus[pod.Name] = pod.Status.Phase
-			}
-
-			// Record network pricing at global scope
-			networkCosts, err := a.Cloud.NetworkPricing()
-			if err != nil {
-				klog.V(4).Infof("Failed to retrieve network costs: %s", err.Error())
-			} else {
-				a.NetworkZoneEgressRecorder.Set(networkCosts.ZoneNetworkEgressCost)
-				a.NetworkRegionEgressRecorder.Set(networkCosts.RegionNetworkEgressCost)
-				a.NetworkInternetEgressRecorder.Set(networkCosts.InternetNetworkEgressCost)
-			}
-
-			data, err := a.Model.ComputeCostData(a.PrometheusClient, a.KubeClientSet, a.Cloud, "2m", "", "")
-			if err != nil {
-				klog.V(1).Info("Error in price recording: " + err.Error())
-				// zero the for loop so the time.Sleep will still work
-				data = map[string]*costModel.CostData{}
-			}
-
-			for _, costs := range data {
-				nodeName := costs.NodeName
-				node := costs.NodeData
-				if node == nil {
-					klog.V(4).Infof("Skipping Node \"%s\" due to missing Node Data costs", nodeName)
-					continue
-				}
-				cpuCost, _ := strconv.ParseFloat(node.VCPUCost, 64)
-				cpu, _ := strconv.ParseFloat(node.VCPU, 64)
-				ramCost, _ := strconv.ParseFloat(node.RAMCost, 64)
-				ram, _ := strconv.ParseFloat(node.RAMBytes, 64)
-				gpu, _ := strconv.ParseFloat(node.GPU, 64)
-				gpuCost, _ := strconv.ParseFloat(node.GPUCost, 64)
-
-				totalCost := cpu*cpuCost + ramCost*(ram/1024/1024/1024) + gpu*gpuCost
-
-				namespace := costs.Namespace
-				podName := costs.PodName
-				containerName := costs.Name
-
-				if costs.PVCData != nil {
-					for _, pvc := range costs.PVCData {
-						if pvc.Volume != nil {
-							a.PVAllocationRecorder.WithLabelValues(namespace, podName, pvc.Claim, pvc.VolumeName).Set(pvc.Values[0].Value)
-							labelKey := getKeyFromLabelStrings(namespace, podName, pvc.Claim, pvc.VolumeName)
-							pvcSeen[labelKey] = true
-						}
-					}
-				}
-
-				a.CPUPriceRecorder.WithLabelValues(nodeName, nodeName).Set(cpuCost)
-				a.RAMPriceRecorder.WithLabelValues(nodeName, nodeName).Set(ramCost)
-				a.GPUPriceRecorder.WithLabelValues(nodeName, nodeName).Set(gpuCost)
-				a.NodeTotalPriceRecorder.WithLabelValues(nodeName, nodeName).Set(totalCost)
-				labelKey := getKeyFromLabelStrings(nodeName, nodeName)
-				nodeSeen[labelKey] = true
-
-				if len(costs.RAMAllocation) > 0 {
-					a.RAMAllocationRecorder.WithLabelValues(namespace, podName, containerName, nodeName, nodeName).Set(costs.RAMAllocation[0].Value)
-				}
-				if len(costs.CPUAllocation) > 0 {
-					a.CPUAllocationRecorder.WithLabelValues(namespace, podName, containerName, nodeName, nodeName).Set(costs.CPUAllocation[0].Value)
-				}
-				if len(costs.GPUReq) > 0 {
-					// allocation here is set to the request because shared GPU usage not yet supported.
-					a.GPUAllocationRecorder.WithLabelValues(namespace, podName, containerName, nodeName, nodeName).Set(costs.GPUReq[0].Value)
-				}
-				labelKey = getKeyFromLabelStrings(namespace, podName, containerName, nodeName, nodeName)
-				if podStatus[podName] == v1.PodRunning { // Only report data for current pods
-					containerSeen[labelKey] = true
-				} else {
-					containerSeen[labelKey] = false
-				}
-
-				storageClasses := a.Model.Cache.GetAllStorageClasses()
-				storageClassMap := make(map[string]map[string]string)
-				for _, storageClass := range storageClasses {
-					params := storageClass.Parameters
-					storageClassMap[storageClass.ObjectMeta.Name] = params
-					if storageClass.GetAnnotations()["storageclass.kubernetes.io/is-default-class"] == "true" || storageClass.GetAnnotations()["storageclass.beta.kubernetes.io/is-default-class"] == "true" {
-						storageClassMap["default"] = params
-						storageClassMap[""] = params
-					}
-				}
-
-				pvs := a.Model.Cache.GetAllPersistentVolumes()
-				for _, pv := range pvs {
-					parameters, ok := storageClassMap[pv.Spec.StorageClassName]
-					if !ok {
-						klog.V(4).Infof("Unable to find parameters for storage class \"%s\". Does pv \"%s\" have a storageClassName?", pv.Spec.StorageClassName, pv.Name)
-					}
-					cacPv := &costAnalyzerCloud.PV{
-						Class:      pv.Spec.StorageClassName,
-						Region:     pv.Labels[v1.LabelZoneRegion],
-						Parameters: parameters,
-					}
-					costModel.GetPVCost(cacPv, pv, a.Cloud)
-					c, _ := strconv.ParseFloat(cacPv.Cost, 64)
-					a.PersistentVolumePriceRecorder.WithLabelValues(pv.Name, pv.Name).Set(c)
-					labelKey := getKeyFromLabelStrings(pv.Name, pv.Name)
-					pvSeen[labelKey] = true
-				}
-				containerUptime, _ := costModel.ComputeUptimes(a.PrometheusClient)
-				for key, uptime := range containerUptime {
-					container, _ := costModel.NewContainerMetricFromKey(key)
-					a.ContainerUptimeRecorder.WithLabelValues(container.Namespace, container.PodName, container.ContainerName).Set(uptime)
-				}
-			}
-			for labelString, seen := range nodeSeen {
-				if !seen {
-					labels := getLabelStringsFromKey(labelString)
-					a.NodeTotalPriceRecorder.DeleteLabelValues(labels...)
-					a.CPUPriceRecorder.DeleteLabelValues(labels...)
-					a.GPUPriceRecorder.DeleteLabelValues(labels...)
-					a.RAMPriceRecorder.DeleteLabelValues(labels...)
-					delete(nodeSeen, labelString)
-				}
-				nodeSeen[labelString] = false
-			}
-			for labelString, seen := range containerSeen {
-				if !seen {
-					labels := getLabelStringsFromKey(labelString)
-					a.RAMAllocationRecorder.DeleteLabelValues(labels...)
-					a.CPUAllocationRecorder.DeleteLabelValues(labels...)
-					a.GPUAllocationRecorder.DeleteLabelValues(labels...)
-					a.ContainerUptimeRecorder.DeleteLabelValues(labels...)
-					delete(containerSeen, labelString)
-				}
-				containerSeen[labelString] = false
-			}
-			for labelString, seen := range pvSeen {
-				if !seen {
-					labels := getLabelStringsFromKey(labelString)
-					a.PersistentVolumePriceRecorder.DeleteLabelValues(labels...)
-					delete(pvSeen, labelString)
-				}
-				pvSeen[labelString] = false
-			}
-			for labelString, seen := range pvcSeen {
-				if !seen {
-					labels := getLabelStringsFromKey(labelString)
-					a.PVAllocationRecorder.DeleteLabelValues(labels...)
-					delete(pvcSeen, labelString)
-				}
-				pvcSeen[labelString] = false
-			}
-			time.Sleep(time.Minute)
-		}
-	}()
-}
-
 func main() {
-	klog.InitFlags(nil)
-	flag.Set("v", "3")
-	flag.Parse()
-	klog.V(1).Infof("Starting cost-model (git commit \"%s\")", gitCommit)
-
-	address := os.Getenv(prometheusServerEndpointEnvVar)
-	if address == "" {
-		klog.Fatalf("No address for prometheus set in $%s. Aborting.", prometheusServerEndpointEnvVar)
-	}
-
-	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, _ := prometheusClient.NewClient(pc)
-
-	api := prometheusAPI.NewAPI(promCli)
-	_, err := api.Config(context.Background())
-	if err != nil {
-		klog.Fatalf("No valid prometheus config file at %s. Error: %s . Troubleshooting help available at: %s", address, err.Error(), prometheusTroubleshootingEp)
-	}
-	klog.V(1).Info("Success: retrieved a prometheus config file from: " + address)
-
-	_, err = costModel.ValidatePrometheus(promCli)
-	if err != nil {
-		klog.Fatalf("Failed to query prometheus at %s. Error: %s . Troubleshooting help available at: %s", address, err.Error(), prometheusTroubleshootingEp)
-	}
-	klog.V(1).Info("Success: retrieved the 'up' query against prometheus at: " + address)
-
-	// Kubernetes API setup
-	kc, err := rest.InClusterConfig()
-	if err != nil {
-		panic(err.Error())
-	}
-	kubeClientset, err := kubernetes.NewForConfig(kc)
-	if err != nil {
-		panic(err.Error())
-	}
-
-	cloudProviderKey := os.Getenv("CLOUD_PROVIDER_API_KEY")
-	cloudProvider, err := costAnalyzerCloud.NewProvider(kubeClientset, cloudProviderKey)
-	if err != nil {
-		panic(err.Error())
-	}
-
-	cpuGv := prometheus.NewGaugeVec(prometheus.GaugeOpts{
-		Name: "node_cpu_hourly_cost",
-		Help: "node_cpu_hourly_cost hourly cost for each cpu on this node",
-	}, []string{"instance", "node"})
-
-	ramGv := prometheus.NewGaugeVec(prometheus.GaugeOpts{
-		Name: "node_ram_hourly_cost",
-		Help: "node_ram_hourly_cost hourly cost for each gb of ram on this node",
-	}, []string{"instance", "node"})
-
-	gpuGv := prometheus.NewGaugeVec(prometheus.GaugeOpts{
-		Name: "node_gpu_hourly_cost",
-		Help: "node_gpu_hourly_cost hourly cost for each gpu on this node",
-	}, []string{"instance", "node"})
-
-	totalGv := prometheus.NewGaugeVec(prometheus.GaugeOpts{
-		Name: "node_total_hourly_cost",
-		Help: "node_total_hourly_cost Total node cost per hour",
-	}, []string{"instance", "node"})
-
-	pvGv := prometheus.NewGaugeVec(prometheus.GaugeOpts{
-		Name: "pv_hourly_cost",
-		Help: "pv_hourly_cost Cost per GB per hour on a persistent disk",
-	}, []string{"volumename", "persistentvolume"})
-
-	RAMAllocation := prometheus.NewGaugeVec(prometheus.GaugeOpts{
-		Name: "container_memory_allocation_bytes",
-		Help: "container_memory_allocation_bytes Bytes of RAM used",
-	}, []string{"namespace", "pod", "container", "instance", "node"})
-
-	CPUAllocation := prometheus.NewGaugeVec(prometheus.GaugeOpts{
-		Name: "container_cpu_allocation",
-		Help: "container_cpu_allocation Percent of a single CPU used in a minute",
-	}, []string{"namespace", "pod", "container", "instance", "node"})
-
-	GPUAllocation := prometheus.NewGaugeVec(prometheus.GaugeOpts{
-		Name: "container_gpu_allocation",
-		Help: "container_gpu_allocation GPU used",
-	}, []string{"namespace", "pod", "container", "instance", "node"})
-	PVAllocation := prometheus.NewGaugeVec(prometheus.GaugeOpts{
-		Name: "pod_pvc_allocation",
-		Help: "pod_pvc_allocation Bytes used by a PVC attached to a pod",
-	}, []string{"namespace", "pod", "persistentvolumeclaim", "persistentvolume"})
-
-	ContainerUptimeRecorder := prometheus.NewGaugeVec(prometheus.GaugeOpts{
-		Name: "container_uptime_seconds",
-		Help: "container_uptime_seconds Seconds a container has been running",
-	}, []string{"namespace", "pod", "container"})
-
-	NetworkZoneEgressRecorder := prometheus.NewGauge(prometheus.GaugeOpts{
-		Name: "kubecost_network_zone_egress_cost",
-		Help: "kubecost_network_zone_egress_cost Total cost per GB egress across zones",
-	})
-	NetworkRegionEgressRecorder := prometheus.NewGauge(prometheus.GaugeOpts{
-		Name: "kubecost_network_region_egress_cost",
-		Help: "kubecost_network_region_egress_cost Total cost per GB egress across regions",
-	})
-	NetworkInternetEgressRecorder := prometheus.NewGauge(prometheus.GaugeOpts{
-		Name: "kubecost_network_internet_egress_cost",
-		Help: "kubecost_network_internet_egress_cost Total cost per GB of internet egress.",
-	})
-
-	prometheus.MustRegister(cpuGv)
-	prometheus.MustRegister(ramGv)
-	prometheus.MustRegister(gpuGv)
-	prometheus.MustRegister(totalGv)
-	prometheus.MustRegister(pvGv)
-	prometheus.MustRegister(RAMAllocation)
-	prometheus.MustRegister(CPUAllocation)
-	prometheus.MustRegister(ContainerUptimeRecorder)
-	prometheus.MustRegister(PVAllocation)
-	prometheus.MustRegister(NetworkZoneEgressRecorder, NetworkRegionEgressRecorder, NetworkInternetEgressRecorder)
-	prometheus.MustRegister(costModel.ServiceCollector{
-		KubeClientSet: kubeClientset,
-	})
-	prometheus.MustRegister(costModel.DeploymentCollector{
-		KubeClientSet: kubeClientset,
-	})
-
-	// cache responses from model for a default of 2 minutes; clear expired responses every 10 minutes
-	modelCache := cache.New(time.Minute*2, time.Minute*10)
-
-	a := Accesses{
-		PrometheusClient:              promCli,
-		KubeClientSet:                 kubeClientset,
-		Cloud:                         cloudProvider,
-		CPUPriceRecorder:              cpuGv,
-		RAMPriceRecorder:              ramGv,
-		GPUPriceRecorder:              gpuGv,
-		NodeTotalPriceRecorder:        totalGv,
-		RAMAllocationRecorder:         RAMAllocation,
-		CPUAllocationRecorder:         CPUAllocation,
-		GPUAllocationRecorder:         GPUAllocation,
-		PVAllocationRecorder:          PVAllocation,
-		ContainerUptimeRecorder:       ContainerUptimeRecorder,
-		NetworkZoneEgressRecorder:     NetworkZoneEgressRecorder,
-		NetworkRegionEgressRecorder:   NetworkRegionEgressRecorder,
-		NetworkInternetEgressRecorder: NetworkInternetEgressRecorder,
-		PersistentVolumePriceRecorder: pvGv,
-		Model:                         costModel.NewCostModel(kubeClientset),
-		Cache:                         modelCache,
-	}
-
-	remoteEnabled := os.Getenv(remoteEnabled)
-	if remoteEnabled == "true" {
-		info, err := cloudProvider.ClusterInfo()
-		klog.Infof("Saving cluster  with id:'%s', and name:'%s' to durable storage", info["id"], info["name"])
-		if err != nil {
-			klog.Infof("Error saving cluster id %s", err.Error())
-		}
-		_, _, err = costAnalyzerCloud.GetOrCreateClusterMeta(info["id"], info["name"])
-		if err != nil {
-			klog.Infof("Unable to set cluster id '%s' for cluster '%s', %s", info["id"], info["name"], err.Error())
-		}
-	}
-
-	err = a.Cloud.DownloadPricingData()
-	if err != nil {
-		klog.V(1).Info("Failed to download pricing data: " + err.Error())
-	}
-
-	a.recordPrices()
-
-	Router.GET("/costDataModel", a.CostDataModel)
-	Router.GET("/costDataModelRange", a.CostDataModelRange)
-	Router.GET("/costDataModelRangeLarge", a.CostDataModelRangeLarge)
-	Router.GET("/outOfClusterCosts", a.OutofClusterCosts)
-	Router.GET("/allNodePricing", a.GetAllNodePricing)
-	Router.GET("/healthz", Healthz)
-	Router.GET("/getConfigs", a.GetConfigs)
-	Router.POST("/refreshPricing", a.RefreshPricingData)
-	Router.POST("/updateSpotInfoConfigs", a.UpdateSpotInfoConfigs)
-	Router.POST("/updateAthenaInfoConfigs", a.UpdateAthenaInfoConfigs)
-	Router.POST("/updateBigQueryInfoConfigs", a.UpdateBigQueryInfoConfigs)
-	Router.POST("/updateConfigByKey", a.UpdateConfigByKey)
-	Router.GET("/clusterCostsOverTime", a.ClusterCostsOverTime)
-	Router.GET("/clusterCosts", a.ClusterCosts)
-	Router.GET("/validatePrometheus", a.GetPrometheusMetadata)
-	Router.GET("/managementPlatform", a.ManagementPlatform)
-	Router.GET("/clusterInfo", a.ClusterInfo)
-	Router.GET("/containerUptimes", a.ContainerUptimes)
-	Router.GET("/aggregatedCostModel", a.AggregateCostModel)
-
 	rootMux := http.NewServeMux()
-	rootMux.Handle("/", Router)
+	rootMux.Handle("/", costModel.Router)
 	rootMux.Handle("/metrics", promhttp.Handler())
-
 	klog.Fatal(http.ListenAndServe(":9003", rootMux))
 }

+ 0 - 35
test/costmodel_test.go

@@ -1,35 +0,0 @@
-package costmodel_test
-
-// Mocks can be regenerated with something like GO111MODULE=on mockgen -destination ./test/mocks/mock_provider.go  -package mocks github.com/kubecost/cost-model/cloud Provider
-
-import (
-	"net/url"
-	"testing"
-	"time"
-
-	"github.com/golang/mock/gomock"
-	"github.com/kubecost/test/mocks"
-	v1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-
-	fakecontroller "k8s.io/client-go/tools/cache/testing"
-)
-
-func TestCostModel(t *testing.T) {
-	ctrl := gomock.NewController(t)
-
-	defer ctrl.Finish()
-
-	u, _ := url.Parse("http://localhost:9003")
-	cli := mocks.NewMockClient(ctrl)
-	cli.EXPECT().URL(gomock.Any(), gomock.Any()).AnyTimes().Return(u)
-	cli.EXPECT().Do(gomock.Any(), gomock.Any()).AnyTimes()
-
-	fc := fakecontroller.NewFakeControllerSource()
-	fc.Add(&v1.Pod{
-		ObjectMeta: metav1.ObjectMeta{
-			Name: "foo",
-		},
-	})
-	time.Sleep(100 * time.Millisecond)
-}

+ 0 - 17
test/mocks/go.mod

@@ -1,17 +0,0 @@
-module github.com/kubecost/test/mocks
-
-go 1.12
-
-replace git.apache.org/thrift.git@v0.12.0 => github.com/apache/thrift v0.12.0
-
-require (
-	github.com/golang/mock v1.2.0
-	github.com/golang/protobuf v1.3.1 // indirect
-	github.com/googleapis/gnostic v0.2.0 // indirect
-	github.com/kubecost/cost-model v0.0.0-20190415210323-992655b79eac
-	golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a // indirect
-	golang.org/x/net v0.0.0-20190415214537-1da14a5a36f2 // indirect
-	k8s.io/api v0.0.0-20190404065945-709cf190c7b7
-	k8s.io/client-go v0.0.0-20190404172613-2e1a3ed22ac5
-	k8s.io/klog v0.0.0-20190306015804-8e90cee79f82
-)

+ 0 - 212
test/mocks/go.sum

@@ -1,212 +0,0 @@
-cloud.google.com/go v0.0.0-20160913182117-3b1ae45394a2/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
-cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
-github.com/Azure/go-autorest v11.1.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
-github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
-github.com/aws/aws-sdk-go v1.19.10 h1:WHIaUrU98WsWIXxlxeMCmbuB5HowxuUnk8eBH4iGl/g=
-github.com/aws/aws-sdk-go v1.19.10/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
-github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
-github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
-github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
-github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
-github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
-github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
-github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI=
-github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
-github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
-github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
-github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
-github.com/google/uuid v0.0.0-20171113160352-8c31c18f31ed/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
-github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g=
-github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
-github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
-github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
-github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
-github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/grpc-ecosystem/grpc-gateway v1.6.2/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
-github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
-github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
-github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
-github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be h1:AHimNtVIpiBjPUhEF5KNCkrUyqTSA5zWUl8sQ2bfGBE=
-github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
-github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
-github.com/kubecost/cost-model v0.0.0-20190415210323-992655b79eac h1:bwwIrqghE8p64eC2i/LqPOLMUMwEI8AM/dwYnTcyeAY=
-github.com/kubecost/cost-model v0.0.0-20190415210323-992655b79eac/go.mod h1:NxiMjOpYdrBQBjo3bGcJOpB+KZd1NWpTbWaWlMq3f+Q=
-github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
-github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
-github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
-github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
-github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
-github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
-github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
-github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
-github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
-github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-go.opencensus.io v0.19.1/go.mod h1:gug0GbSHa8Pafr0d2urOSgoXHZ6x/RUlaiT0d9pqb4A=
-go.opencensus.io v0.19.2 h1:ZZpq6xI6kv/LuE/5s5UQvBU5vMjvRnPb8PvJrIntAnc=
-go.opencensus.io v0.19.2/go.mod h1:NO/8qkisMZLZ1FCsKNqtJPwc8/TaclWyY0B6wcYNg9M=
-golang.org/x/crypto v0.0.0-20180808211826-de0752318171/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a h1:Igim7XhdOpBnWPuYJ70XcNpq8q3BCACtVgNfoJxOV7g=
-golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190415214537-1da14a5a36f2 h1:iC0Y6EDq+rhnAePxGvJs2kzUAYcwESqdcGRPzEUfzTU=
-golang.org/x/net v0.0.0-20190415214537-1da14a5a36f2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/oauth2 v0.0.0-20170412232759-a6bd8cefa181/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA=
-golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e h1:nFYrTHrdrAOpShe27kaFHjsqYSEQ0KWqdWLu3xuZJts=
-golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU=
-golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
-golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
-google.golang.org/api v0.2.0/go.mod h1:IfRCZScioGtypHNTlz3gFk67J8uePVW7uDTBzXuIkhU=
-google.golang.org/api v0.3.0 h1:UIJY20OEo3+tK5MBlcdx37kmdH6EnRjGkW78mc6+EeA=
-google.golang.org/api v0.3.0/go.mod h1:IuvZyQh8jgscv8qWfQ4ABd8m7hEudgBFM/EdhA3BnXw=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20181219182458-5a97ab628bfb/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
-google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 h1:Lj2SnHtxkRGJDqnGaSjo+CCdIieEnwVazbOXILwQemk=
-google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
-google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
-google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
-gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o=
-gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-k8s.io/api v0.0.0-20190404065945-709cf190c7b7 h1:s6+su3184vqq9jlmBa1UHf/JXGlx6mR5/Wn1mgGgho0=
-k8s.io/api v0.0.0-20190404065945-709cf190c7b7/go.mod h1:qa6Gt7knwxwD/MXwV8iaEoTpniVksiix/r9hpLWYgTA=
-k8s.io/api v0.0.0-20190415132514-c2f1300cac21 h1:XGJCFakX0XXD6V2frIsafsr96sDU1q6Gcvm2JaFQVyM=
-k8s.io/api v0.0.0-20190415132514-c2f1300cac21/go.mod h1:5HMaKNcWJji8AGOBjOZxmFDwRutMItn4MrrXZcFANNo=
-k8s.io/apimachinery v0.0.0-20190404065847-4a4abcd45006 h1:Jiue4qiNBoiq1GxPx33Kjw7KOyqYVEcZyl4y4rIXXLU=
-k8s.io/apimachinery v0.0.0-20190404065847-4a4abcd45006/go.mod h1:65NCMCFo27j/Cv2DAQSfKd70SAtu/hwoqasuXFCDNvY=
-k8s.io/apimachinery v0.0.0-20190415132420-07d458fe0356 h1:IneIG23feOS594nIIsJVRUHK50ALz/g0/Co/3iML7RI=
-k8s.io/apimachinery v0.0.0-20190415132420-07d458fe0356/go.mod h1:jLjiXl596L+wVGdx8dRx6sSNnabzqnI2+nAJqqqyesQ=
-k8s.io/client-go v0.0.0-20190404172613-2e1a3ed22ac5 h1:BwY2C//EoWktJi74O6R2REBonrhsfhRI0qfVwOjOPp8=
-k8s.io/client-go v0.0.0-20190404172613-2e1a3ed22ac5/go.mod h1:bIEHXHbykaOlj+pgLllzLJ2RPGdzkjtqdk0Il07KPEM=
-k8s.io/client-go v11.0.0+incompatible h1:LBbX2+lOwY9flffWlJM7f1Ct8V2SRNiMRDFeiwnJo9o=
-k8s.io/client-go v11.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
-k8s.io/klog v0.0.0-20190306015804-8e90cee79f82 h1:SHucoAy7lRb+w5oC/hbXyZg+zX+Wftn6hD4tGzHCVqA=
-k8s.io/klog v0.0.0-20190306015804-8e90cee79f82/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
-k8s.io/klog v0.3.0 h1:0VPpR+sizsiivjIfIAQH/rl8tan6jvWkS7lU+0di3lE=
-k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
-k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
-k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
-k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7 h1:8r+l4bNWjRlsFYlQJnKJ2p7s1YQPj4XyXiJVqDHRx7c=
-k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
-sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
-sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

+ 0 - 66
test/mocks/mock_client.go

@@ -1,66 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: github.com/prometheus/client_golang/api (interfaces: Client)
-
-// Package mocks is a generated GoMock package.
-package mocks
-
-import (
-	context "context"
-	gomock "github.com/golang/mock/gomock"
-	http "net/http"
-	url "net/url"
-	reflect "reflect"
-)
-
-// MockClient is a mock of Client interface
-type MockClient struct {
-	ctrl     *gomock.Controller
-	recorder *MockClientMockRecorder
-}
-
-// MockClientMockRecorder is the mock recorder for MockClient
-type MockClientMockRecorder struct {
-	mock *MockClient
-}
-
-// NewMockClient creates a new mock instance
-func NewMockClient(ctrl *gomock.Controller) *MockClient {
-	mock := &MockClient{ctrl: ctrl}
-	mock.recorder = &MockClientMockRecorder{mock}
-	return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use
-func (m *MockClient) EXPECT() *MockClientMockRecorder {
-	return m.recorder
-}
-
-// Do mocks base method
-func (m *MockClient) Do(arg0 context.Context, arg1 *http.Request) (*http.Response, []byte, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "Do", arg0, arg1)
-	ret0, _ := ret[0].(*http.Response)
-	ret1, _ := ret[1].([]byte)
-	ret2, _ := ret[2].(error)
-	return ret0, ret1, ret2
-}
-
-// Do indicates an expected call of Do
-func (mr *MockClientMockRecorder) Do(arg0, arg1 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Do", reflect.TypeOf((*MockClient)(nil).Do), arg0, arg1)
-}
-
-// URL mocks base method
-func (m *MockClient) URL(arg0 string, arg1 map[string]string) *url.URL {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "URL", arg0, arg1)
-	ret0, _ := ret[0].(*url.URL)
-	return ret0
-}
-
-// URL indicates an expected call of URL
-func (mr *MockClientMockRecorder) URL(arg0, arg1 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "URL", reflect.TypeOf((*MockClient)(nil).URL), arg0, arg1)
-}

+ 0 - 588
test/mocks/mock_kubernetes.go

@@ -1,588 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: k8s.io/client-go/kubernetes (interfaces: Interface)
-
-// Package mocks is a generated GoMock package.
-package mocks
-
-import (
-	gomock "github.com/golang/mock/gomock"
-	discovery "k8s.io/client-go/discovery"
-	v1beta1 "k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1"
-	v1 "k8s.io/client-go/kubernetes/typed/apps/v1"
-	v1beta10 "k8s.io/client-go/kubernetes/typed/apps/v1beta1"
-	v1beta2 "k8s.io/client-go/kubernetes/typed/apps/v1beta2"
-	v1alpha1 "k8s.io/client-go/kubernetes/typed/auditregistration/v1alpha1"
-	v10 "k8s.io/client-go/kubernetes/typed/authentication/v1"
-	v1beta11 "k8s.io/client-go/kubernetes/typed/authentication/v1beta1"
-	v11 "k8s.io/client-go/kubernetes/typed/authorization/v1"
-	v1beta12 "k8s.io/client-go/kubernetes/typed/authorization/v1beta1"
-	v12 "k8s.io/client-go/kubernetes/typed/autoscaling/v1"
-	v2beta1 "k8s.io/client-go/kubernetes/typed/autoscaling/v2beta1"
-	v2beta2 "k8s.io/client-go/kubernetes/typed/autoscaling/v2beta2"
-	v13 "k8s.io/client-go/kubernetes/typed/batch/v1"
-	v1beta13 "k8s.io/client-go/kubernetes/typed/batch/v1beta1"
-	v2alpha1 "k8s.io/client-go/kubernetes/typed/batch/v2alpha1"
-	v1beta14 "k8s.io/client-go/kubernetes/typed/certificates/v1beta1"
-	v14 "k8s.io/client-go/kubernetes/typed/coordination/v1"
-	v1beta15 "k8s.io/client-go/kubernetes/typed/coordination/v1beta1"
-	v15 "k8s.io/client-go/kubernetes/typed/core/v1"
-	v1beta16 "k8s.io/client-go/kubernetes/typed/events/v1beta1"
-	v1beta17 "k8s.io/client-go/kubernetes/typed/extensions/v1beta1"
-	v16 "k8s.io/client-go/kubernetes/typed/networking/v1"
-	v1beta18 "k8s.io/client-go/kubernetes/typed/networking/v1beta1"
-	v1alpha10 "k8s.io/client-go/kubernetes/typed/node/v1alpha1"
-	v1beta19 "k8s.io/client-go/kubernetes/typed/node/v1beta1"
-	v1beta110 "k8s.io/client-go/kubernetes/typed/policy/v1beta1"
-	v17 "k8s.io/client-go/kubernetes/typed/rbac/v1"
-	v1alpha11 "k8s.io/client-go/kubernetes/typed/rbac/v1alpha1"
-	v1beta111 "k8s.io/client-go/kubernetes/typed/rbac/v1beta1"
-	v18 "k8s.io/client-go/kubernetes/typed/scheduling/v1"
-	v1alpha12 "k8s.io/client-go/kubernetes/typed/scheduling/v1alpha1"
-	v1beta112 "k8s.io/client-go/kubernetes/typed/scheduling/v1beta1"
-	v1alpha13 "k8s.io/client-go/kubernetes/typed/settings/v1alpha1"
-	v19 "k8s.io/client-go/kubernetes/typed/storage/v1"
-	v1alpha14 "k8s.io/client-go/kubernetes/typed/storage/v1alpha1"
-	v1beta113 "k8s.io/client-go/kubernetes/typed/storage/v1beta1"
-	reflect "reflect"
-)
-
-// MockInterface is a mock of Interface interface
-type MockInterface struct {
-	ctrl     *gomock.Controller
-	recorder *MockInterfaceMockRecorder
-}
-
-// MockInterfaceMockRecorder is the mock recorder for MockInterface
-type MockInterfaceMockRecorder struct {
-	mock *MockInterface
-}
-
-// NewMockInterface creates a new mock instance
-func NewMockInterface(ctrl *gomock.Controller) *MockInterface {
-	mock := &MockInterface{ctrl: ctrl}
-	mock.recorder = &MockInterfaceMockRecorder{mock}
-	return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use
-func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder {
-	return m.recorder
-}
-
-// AdmissionregistrationV1beta1 mocks base method
-func (m *MockInterface) AdmissionregistrationV1beta1() v1beta1.AdmissionregistrationV1beta1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "AdmissionregistrationV1beta1")
-	ret0, _ := ret[0].(v1beta1.AdmissionregistrationV1beta1Interface)
-	return ret0
-}
-
-// AdmissionregistrationV1beta1 indicates an expected call of AdmissionregistrationV1beta1
-func (mr *MockInterfaceMockRecorder) AdmissionregistrationV1beta1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AdmissionregistrationV1beta1", reflect.TypeOf((*MockInterface)(nil).AdmissionregistrationV1beta1))
-}
-
-// AppsV1 mocks base method
-func (m *MockInterface) AppsV1() v1.AppsV1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "AppsV1")
-	ret0, _ := ret[0].(v1.AppsV1Interface)
-	return ret0
-}
-
-// AppsV1 indicates an expected call of AppsV1
-func (mr *MockInterfaceMockRecorder) AppsV1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppsV1", reflect.TypeOf((*MockInterface)(nil).AppsV1))
-}
-
-// AppsV1beta1 mocks base method
-func (m *MockInterface) AppsV1beta1() v1beta10.AppsV1beta1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "AppsV1beta1")
-	ret0, _ := ret[0].(v1beta10.AppsV1beta1Interface)
-	return ret0
-}
-
-// AppsV1beta1 indicates an expected call of AppsV1beta1
-func (mr *MockInterfaceMockRecorder) AppsV1beta1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppsV1beta1", reflect.TypeOf((*MockInterface)(nil).AppsV1beta1))
-}
-
-// AppsV1beta2 mocks base method
-func (m *MockInterface) AppsV1beta2() v1beta2.AppsV1beta2Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "AppsV1beta2")
-	ret0, _ := ret[0].(v1beta2.AppsV1beta2Interface)
-	return ret0
-}
-
-// AppsV1beta2 indicates an expected call of AppsV1beta2
-func (mr *MockInterfaceMockRecorder) AppsV1beta2() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppsV1beta2", reflect.TypeOf((*MockInterface)(nil).AppsV1beta2))
-}
-
-// AuditregistrationV1alpha1 mocks base method
-func (m *MockInterface) AuditregistrationV1alpha1() v1alpha1.AuditregistrationV1alpha1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "AuditregistrationV1alpha1")
-	ret0, _ := ret[0].(v1alpha1.AuditregistrationV1alpha1Interface)
-	return ret0
-}
-
-// AuditregistrationV1alpha1 indicates an expected call of AuditregistrationV1alpha1
-func (mr *MockInterfaceMockRecorder) AuditregistrationV1alpha1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuditregistrationV1alpha1", reflect.TypeOf((*MockInterface)(nil).AuditregistrationV1alpha1))
-}
-
-// AuthenticationV1 mocks base method
-func (m *MockInterface) AuthenticationV1() v10.AuthenticationV1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "AuthenticationV1")
-	ret0, _ := ret[0].(v10.AuthenticationV1Interface)
-	return ret0
-}
-
-// AuthenticationV1 indicates an expected call of AuthenticationV1
-func (mr *MockInterfaceMockRecorder) AuthenticationV1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthenticationV1", reflect.TypeOf((*MockInterface)(nil).AuthenticationV1))
-}
-
-// AuthenticationV1beta1 mocks base method
-func (m *MockInterface) AuthenticationV1beta1() v1beta11.AuthenticationV1beta1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "AuthenticationV1beta1")
-	ret0, _ := ret[0].(v1beta11.AuthenticationV1beta1Interface)
-	return ret0
-}
-
-// AuthenticationV1beta1 indicates an expected call of AuthenticationV1beta1
-func (mr *MockInterfaceMockRecorder) AuthenticationV1beta1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthenticationV1beta1", reflect.TypeOf((*MockInterface)(nil).AuthenticationV1beta1))
-}
-
-// AuthorizationV1 mocks base method
-func (m *MockInterface) AuthorizationV1() v11.AuthorizationV1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "AuthorizationV1")
-	ret0, _ := ret[0].(v11.AuthorizationV1Interface)
-	return ret0
-}
-
-// AuthorizationV1 indicates an expected call of AuthorizationV1
-func (mr *MockInterfaceMockRecorder) AuthorizationV1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthorizationV1", reflect.TypeOf((*MockInterface)(nil).AuthorizationV1))
-}
-
-// AuthorizationV1beta1 mocks base method
-func (m *MockInterface) AuthorizationV1beta1() v1beta12.AuthorizationV1beta1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "AuthorizationV1beta1")
-	ret0, _ := ret[0].(v1beta12.AuthorizationV1beta1Interface)
-	return ret0
-}
-
-// AuthorizationV1beta1 indicates an expected call of AuthorizationV1beta1
-func (mr *MockInterfaceMockRecorder) AuthorizationV1beta1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthorizationV1beta1", reflect.TypeOf((*MockInterface)(nil).AuthorizationV1beta1))
-}
-
-// AutoscalingV1 mocks base method
-func (m *MockInterface) AutoscalingV1() v12.AutoscalingV1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "AutoscalingV1")
-	ret0, _ := ret[0].(v12.AutoscalingV1Interface)
-	return ret0
-}
-
-// AutoscalingV1 indicates an expected call of AutoscalingV1
-func (mr *MockInterfaceMockRecorder) AutoscalingV1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AutoscalingV1", reflect.TypeOf((*MockInterface)(nil).AutoscalingV1))
-}
-
-// AutoscalingV2beta1 mocks base method
-func (m *MockInterface) AutoscalingV2beta1() v2beta1.AutoscalingV2beta1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "AutoscalingV2beta1")
-	ret0, _ := ret[0].(v2beta1.AutoscalingV2beta1Interface)
-	return ret0
-}
-
-// AutoscalingV2beta1 indicates an expected call of AutoscalingV2beta1
-func (mr *MockInterfaceMockRecorder) AutoscalingV2beta1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AutoscalingV2beta1", reflect.TypeOf((*MockInterface)(nil).AutoscalingV2beta1))
-}
-
-// AutoscalingV2beta2 mocks base method
-func (m *MockInterface) AutoscalingV2beta2() v2beta2.AutoscalingV2beta2Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "AutoscalingV2beta2")
-	ret0, _ := ret[0].(v2beta2.AutoscalingV2beta2Interface)
-	return ret0
-}
-
-// AutoscalingV2beta2 indicates an expected call of AutoscalingV2beta2
-func (mr *MockInterfaceMockRecorder) AutoscalingV2beta2() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AutoscalingV2beta2", reflect.TypeOf((*MockInterface)(nil).AutoscalingV2beta2))
-}
-
-// BatchV1 mocks base method
-func (m *MockInterface) BatchV1() v13.BatchV1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "BatchV1")
-	ret0, _ := ret[0].(v13.BatchV1Interface)
-	return ret0
-}
-
-// BatchV1 indicates an expected call of BatchV1
-func (mr *MockInterfaceMockRecorder) BatchV1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BatchV1", reflect.TypeOf((*MockInterface)(nil).BatchV1))
-}
-
-// BatchV1beta1 mocks base method
-func (m *MockInterface) BatchV1beta1() v1beta13.BatchV1beta1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "BatchV1beta1")
-	ret0, _ := ret[0].(v1beta13.BatchV1beta1Interface)
-	return ret0
-}
-
-// BatchV1beta1 indicates an expected call of BatchV1beta1
-func (mr *MockInterfaceMockRecorder) BatchV1beta1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BatchV1beta1", reflect.TypeOf((*MockInterface)(nil).BatchV1beta1))
-}
-
-// BatchV2alpha1 mocks base method
-func (m *MockInterface) BatchV2alpha1() v2alpha1.BatchV2alpha1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "BatchV2alpha1")
-	ret0, _ := ret[0].(v2alpha1.BatchV2alpha1Interface)
-	return ret0
-}
-
-// BatchV2alpha1 indicates an expected call of BatchV2alpha1
-func (mr *MockInterfaceMockRecorder) BatchV2alpha1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BatchV2alpha1", reflect.TypeOf((*MockInterface)(nil).BatchV2alpha1))
-}
-
-// CertificatesV1beta1 mocks base method
-func (m *MockInterface) CertificatesV1beta1() v1beta14.CertificatesV1beta1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "CertificatesV1beta1")
-	ret0, _ := ret[0].(v1beta14.CertificatesV1beta1Interface)
-	return ret0
-}
-
-// CertificatesV1beta1 indicates an expected call of CertificatesV1beta1
-func (mr *MockInterfaceMockRecorder) CertificatesV1beta1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CertificatesV1beta1", reflect.TypeOf((*MockInterface)(nil).CertificatesV1beta1))
-}
-
-// CoordinationV1 mocks base method
-func (m *MockInterface) CoordinationV1() v14.CoordinationV1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "CoordinationV1")
-	ret0, _ := ret[0].(v14.CoordinationV1Interface)
-	return ret0
-}
-
-// CoordinationV1 indicates an expected call of CoordinationV1
-func (mr *MockInterfaceMockRecorder) CoordinationV1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CoordinationV1", reflect.TypeOf((*MockInterface)(nil).CoordinationV1))
-}
-
-// CoordinationV1beta1 mocks base method
-func (m *MockInterface) CoordinationV1beta1() v1beta15.CoordinationV1beta1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "CoordinationV1beta1")
-	ret0, _ := ret[0].(v1beta15.CoordinationV1beta1Interface)
-	return ret0
-}
-
-// CoordinationV1beta1 indicates an expected call of CoordinationV1beta1
-func (mr *MockInterfaceMockRecorder) CoordinationV1beta1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CoordinationV1beta1", reflect.TypeOf((*MockInterface)(nil).CoordinationV1beta1))
-}
-
-// CoreV1 mocks base method
-func (m *MockInterface) CoreV1() v15.CoreV1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "CoreV1")
-	ret0, _ := ret[0].(v15.CoreV1Interface)
-	return ret0
-}
-
-// CoreV1 indicates an expected call of CoreV1
-func (mr *MockInterfaceMockRecorder) CoreV1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CoreV1", reflect.TypeOf((*MockInterface)(nil).CoreV1))
-}
-
-// Discovery mocks base method
-func (m *MockInterface) Discovery() discovery.DiscoveryInterface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "Discovery")
-	ret0, _ := ret[0].(discovery.DiscoveryInterface)
-	return ret0
-}
-
-// Discovery indicates an expected call of Discovery
-func (mr *MockInterfaceMockRecorder) Discovery() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Discovery", reflect.TypeOf((*MockInterface)(nil).Discovery))
-}
-
-// EventsV1beta1 mocks base method
-func (m *MockInterface) EventsV1beta1() v1beta16.EventsV1beta1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "EventsV1beta1")
-	ret0, _ := ret[0].(v1beta16.EventsV1beta1Interface)
-	return ret0
-}
-
-// EventsV1beta1 indicates an expected call of EventsV1beta1
-func (mr *MockInterfaceMockRecorder) EventsV1beta1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EventsV1beta1", reflect.TypeOf((*MockInterface)(nil).EventsV1beta1))
-}
-
-// ExtensionsV1beta1 mocks base method
-func (m *MockInterface) ExtensionsV1beta1() v1beta17.ExtensionsV1beta1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "ExtensionsV1beta1")
-	ret0, _ := ret[0].(v1beta17.ExtensionsV1beta1Interface)
-	return ret0
-}
-
-// ExtensionsV1beta1 indicates an expected call of ExtensionsV1beta1
-func (mr *MockInterfaceMockRecorder) ExtensionsV1beta1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExtensionsV1beta1", reflect.TypeOf((*MockInterface)(nil).ExtensionsV1beta1))
-}
-
-// NetworkingV1 mocks base method
-func (m *MockInterface) NetworkingV1() v16.NetworkingV1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "NetworkingV1")
-	ret0, _ := ret[0].(v16.NetworkingV1Interface)
-	return ret0
-}
-
-// NetworkingV1 indicates an expected call of NetworkingV1
-func (mr *MockInterfaceMockRecorder) NetworkingV1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworkingV1", reflect.TypeOf((*MockInterface)(nil).NetworkingV1))
-}
-
-// NetworkingV1beta1 mocks base method
-func (m *MockInterface) NetworkingV1beta1() v1beta18.NetworkingV1beta1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "NetworkingV1beta1")
-	ret0, _ := ret[0].(v1beta18.NetworkingV1beta1Interface)
-	return ret0
-}
-
-// NetworkingV1beta1 indicates an expected call of NetworkingV1beta1
-func (mr *MockInterfaceMockRecorder) NetworkingV1beta1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworkingV1beta1", reflect.TypeOf((*MockInterface)(nil).NetworkingV1beta1))
-}
-
-// NodeV1alpha1 mocks base method
-func (m *MockInterface) NodeV1alpha1() v1alpha10.NodeV1alpha1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "NodeV1alpha1")
-	ret0, _ := ret[0].(v1alpha10.NodeV1alpha1Interface)
-	return ret0
-}
-
-// NodeV1alpha1 indicates an expected call of NodeV1alpha1
-func (mr *MockInterfaceMockRecorder) NodeV1alpha1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NodeV1alpha1", reflect.TypeOf((*MockInterface)(nil).NodeV1alpha1))
-}
-
-// NodeV1beta1 mocks base method
-func (m *MockInterface) NodeV1beta1() v1beta19.NodeV1beta1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "NodeV1beta1")
-	ret0, _ := ret[0].(v1beta19.NodeV1beta1Interface)
-	return ret0
-}
-
-// NodeV1beta1 indicates an expected call of NodeV1beta1
-func (mr *MockInterfaceMockRecorder) NodeV1beta1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NodeV1beta1", reflect.TypeOf((*MockInterface)(nil).NodeV1beta1))
-}
-
-// PolicyV1beta1 mocks base method
-func (m *MockInterface) PolicyV1beta1() v1beta110.PolicyV1beta1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "PolicyV1beta1")
-	ret0, _ := ret[0].(v1beta110.PolicyV1beta1Interface)
-	return ret0
-}
-
-// PolicyV1beta1 indicates an expected call of PolicyV1beta1
-func (mr *MockInterfaceMockRecorder) PolicyV1beta1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PolicyV1beta1", reflect.TypeOf((*MockInterface)(nil).PolicyV1beta1))
-}
-
-// RbacV1 mocks base method
-func (m *MockInterface) RbacV1() v17.RbacV1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "RbacV1")
-	ret0, _ := ret[0].(v17.RbacV1Interface)
-	return ret0
-}
-
-// RbacV1 indicates an expected call of RbacV1
-func (mr *MockInterfaceMockRecorder) RbacV1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RbacV1", reflect.TypeOf((*MockInterface)(nil).RbacV1))
-}
-
-// RbacV1alpha1 mocks base method
-func (m *MockInterface) RbacV1alpha1() v1alpha11.RbacV1alpha1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "RbacV1alpha1")
-	ret0, _ := ret[0].(v1alpha11.RbacV1alpha1Interface)
-	return ret0
-}
-
-// RbacV1alpha1 indicates an expected call of RbacV1alpha1
-func (mr *MockInterfaceMockRecorder) RbacV1alpha1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RbacV1alpha1", reflect.TypeOf((*MockInterface)(nil).RbacV1alpha1))
-}
-
-// RbacV1beta1 mocks base method
-func (m *MockInterface) RbacV1beta1() v1beta111.RbacV1beta1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "RbacV1beta1")
-	ret0, _ := ret[0].(v1beta111.RbacV1beta1Interface)
-	return ret0
-}
-
-// RbacV1beta1 indicates an expected call of RbacV1beta1
-func (mr *MockInterfaceMockRecorder) RbacV1beta1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RbacV1beta1", reflect.TypeOf((*MockInterface)(nil).RbacV1beta1))
-}
-
-// SchedulingV1 mocks base method
-func (m *MockInterface) SchedulingV1() v18.SchedulingV1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SchedulingV1")
-	ret0, _ := ret[0].(v18.SchedulingV1Interface)
-	return ret0
-}
-
-// SchedulingV1 indicates an expected call of SchedulingV1
-func (mr *MockInterfaceMockRecorder) SchedulingV1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchedulingV1", reflect.TypeOf((*MockInterface)(nil).SchedulingV1))
-}
-
-// SchedulingV1alpha1 mocks base method
-func (m *MockInterface) SchedulingV1alpha1() v1alpha12.SchedulingV1alpha1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SchedulingV1alpha1")
-	ret0, _ := ret[0].(v1alpha12.SchedulingV1alpha1Interface)
-	return ret0
-}
-
-// SchedulingV1alpha1 indicates an expected call of SchedulingV1alpha1
-func (mr *MockInterfaceMockRecorder) SchedulingV1alpha1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchedulingV1alpha1", reflect.TypeOf((*MockInterface)(nil).SchedulingV1alpha1))
-}
-
-// SchedulingV1beta1 mocks base method
-func (m *MockInterface) SchedulingV1beta1() v1beta112.SchedulingV1beta1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SchedulingV1beta1")
-	ret0, _ := ret[0].(v1beta112.SchedulingV1beta1Interface)
-	return ret0
-}
-
-// SchedulingV1beta1 indicates an expected call of SchedulingV1beta1
-func (mr *MockInterfaceMockRecorder) SchedulingV1beta1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchedulingV1beta1", reflect.TypeOf((*MockInterface)(nil).SchedulingV1beta1))
-}
-
-// SettingsV1alpha1 mocks base method
-func (m *MockInterface) SettingsV1alpha1() v1alpha13.SettingsV1alpha1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SettingsV1alpha1")
-	ret0, _ := ret[0].(v1alpha13.SettingsV1alpha1Interface)
-	return ret0
-}
-
-// SettingsV1alpha1 indicates an expected call of SettingsV1alpha1
-func (mr *MockInterfaceMockRecorder) SettingsV1alpha1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SettingsV1alpha1", reflect.TypeOf((*MockInterface)(nil).SettingsV1alpha1))
-}
-
-// StorageV1 mocks base method
-func (m *MockInterface) StorageV1() v19.StorageV1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "StorageV1")
-	ret0, _ := ret[0].(v19.StorageV1Interface)
-	return ret0
-}
-
-// StorageV1 indicates an expected call of StorageV1
-func (mr *MockInterfaceMockRecorder) StorageV1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StorageV1", reflect.TypeOf((*MockInterface)(nil).StorageV1))
-}
-
-// StorageV1alpha1 mocks base method
-func (m *MockInterface) StorageV1alpha1() v1alpha14.StorageV1alpha1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "StorageV1alpha1")
-	ret0, _ := ret[0].(v1alpha14.StorageV1alpha1Interface)
-	return ret0
-}
-
-// StorageV1alpha1 indicates an expected call of StorageV1alpha1
-func (mr *MockInterfaceMockRecorder) StorageV1alpha1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StorageV1alpha1", reflect.TypeOf((*MockInterface)(nil).StorageV1alpha1))
-}
-
-// StorageV1beta1 mocks base method
-func (m *MockInterface) StorageV1beta1() v1beta113.StorageV1beta1Interface {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "StorageV1beta1")
-	ret0, _ := ret[0].(v1beta113.StorageV1beta1Interface)
-	return ret0
-}
-
-// StorageV1beta1 indicates an expected call of StorageV1beta1
-func (mr *MockInterfaceMockRecorder) StorageV1beta1() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StorageV1beta1", reflect.TypeOf((*MockInterface)(nil).StorageV1beta1))
-}

+ 0 - 243
test/mocks/mock_provider.go

@@ -1,243 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: github.com/kubecost/cost-model/cloud (interfaces: Provider)
-
-// Package mocks is a generated GoMock package.
-package mocks
-
-import (
-	gomock "github.com/golang/mock/gomock"
-	cloud "github.com/kubecost/cost-model/cloud"
-	io "io"
-	v1 "k8s.io/api/core/v1"
-	url "net/url"
-	reflect "reflect"
-)
-
-// MockProvider is a mock of Provider interface
-type MockProvider struct {
-	ctrl     *gomock.Controller
-	recorder *MockProviderMockRecorder
-}
-
-// MockProviderMockRecorder is the mock recorder for MockProvider
-type MockProviderMockRecorder struct {
-	mock *MockProvider
-}
-
-// NewMockProvider creates a new mock instance
-func NewMockProvider(ctrl *gomock.Controller) *MockProvider {
-	mock := &MockProvider{ctrl: ctrl}
-	mock.recorder = &MockProviderMockRecorder{mock}
-	return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use
-func (m *MockProvider) EXPECT() *MockProviderMockRecorder {
-	return m.recorder
-}
-
-// AddServiceKey mocks base method
-func (m *MockProvider) AddServiceKey(arg0 url.Values) error {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "AddServiceKey", arg0)
-	ret0, _ := ret[0].(error)
-	return ret0
-}
-
-// AddServiceKey indicates an expected call of AddServiceKey
-func (mr *MockProviderMockRecorder) AddServiceKey(arg0 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddServiceKey", reflect.TypeOf((*MockProvider)(nil).AddServiceKey), arg0)
-}
-
-// AllNodePricing mocks base method
-func (m *MockProvider) AllNodePricing() (interface{}, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "AllNodePricing")
-	ret0, _ := ret[0].(interface{})
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// AllNodePricing indicates an expected call of AllNodePricing
-func (mr *MockProviderMockRecorder) AllNodePricing() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllNodePricing", reflect.TypeOf((*MockProvider)(nil).AllNodePricing))
-}
-
-// ClusterInfo mocks base method
-func (m *MockProvider) ClusterInfo() (map[string]string, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "ClusterInfo")
-	ret0, _ := ret[0].(map[string]string)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// ClusterInfo indicates an expected call of ClusterInfo
-func (mr *MockProviderMockRecorder) ClusterInfo() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClusterInfo", reflect.TypeOf((*MockProvider)(nil).ClusterInfo))
-}
-
-// DownloadPricingData mocks base method
-func (m *MockProvider) DownloadPricingData() error {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "DownloadPricingData")
-	ret0, _ := ret[0].(error)
-	return ret0
-}
-
-// DownloadPricingData indicates an expected call of DownloadPricingData
-func (mr *MockProviderMockRecorder) DownloadPricingData() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DownloadPricingData", reflect.TypeOf((*MockProvider)(nil).DownloadPricingData))
-}
-
-// ExternalAllocations mocks base method
-func (m *MockProvider) ExternalAllocations(arg0, arg1, arg2 string) ([]*cloud.OutOfClusterAllocation, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "ExternalAllocations", arg0, arg1, arg2)
-	ret0, _ := ret[0].([]*cloud.OutOfClusterAllocation)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// ExternalAllocations indicates an expected call of ExternalAllocations
-func (mr *MockProviderMockRecorder) ExternalAllocations(arg0, arg1, arg2 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExternalAllocations", reflect.TypeOf((*MockProvider)(nil).ExternalAllocations), arg0, arg1, arg2)
-}
-
-// GetConfig mocks base method
-func (m *MockProvider) GetConfig() (*cloud.CustomPricing, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetConfig")
-	ret0, _ := ret[0].(*cloud.CustomPricing)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// GetConfig indicates an expected call of GetConfig
-func (mr *MockProviderMockRecorder) GetConfig() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConfig", reflect.TypeOf((*MockProvider)(nil).GetConfig))
-}
-
-// GetDisks mocks base method
-func (m *MockProvider) GetDisks() ([]byte, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetDisks")
-	ret0, _ := ret[0].([]byte)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// GetDisks indicates an expected call of GetDisks
-func (mr *MockProviderMockRecorder) GetDisks() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDisks", reflect.TypeOf((*MockProvider)(nil).GetDisks))
-}
-
-// GetKey mocks base method
-func (m *MockProvider) GetKey(arg0 map[string]string) cloud.Key {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetKey", arg0)
-	ret0, _ := ret[0].(cloud.Key)
-	return ret0
-}
-
-// GetKey indicates an expected call of GetKey
-func (mr *MockProviderMockRecorder) GetKey(arg0 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetKey", reflect.TypeOf((*MockProvider)(nil).GetKey), arg0)
-}
-
-// GetLocalStorageQuery mocks base method
-func (m *MockProvider) GetLocalStorageQuery() (string, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetLocalStorageQuery")
-	ret0, _ := ret[0].(string)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// GetLocalStorageQuery indicates an expected call of GetLocalStorageQuery
-func (mr *MockProviderMockRecorder) GetLocalStorageQuery() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLocalStorageQuery", reflect.TypeOf((*MockProvider)(nil).GetLocalStorageQuery))
-}
-
-// GetManagementPlatform mocks base method
-func (m *MockProvider) GetManagementPlatform() (string, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetManagementPlatform")
-	ret0, _ := ret[0].(string)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// GetManagementPlatform indicates an expected call of GetManagementPlatform
-func (mr *MockProviderMockRecorder) GetManagementPlatform() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetManagementPlatform", reflect.TypeOf((*MockProvider)(nil).GetManagementPlatform))
-}
-
-// GetPVKey mocks base method
-func (m *MockProvider) GetPVKey(arg0 *v1.PersistentVolume, arg1 map[string]string) cloud.PVKey {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetPVKey", arg0, arg1)
-	ret0, _ := ret[0].(cloud.PVKey)
-	return ret0
-}
-
-// GetPVKey indicates an expected call of GetPVKey
-func (mr *MockProviderMockRecorder) GetPVKey(arg0, arg1 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPVKey", reflect.TypeOf((*MockProvider)(nil).GetPVKey), arg0, arg1)
-}
-
-// NodePricing mocks base method
-func (m *MockProvider) NodePricing(arg0 cloud.Key) (*cloud.Node, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "NodePricing", arg0)
-	ret0, _ := ret[0].(*cloud.Node)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// NodePricing indicates an expected call of NodePricing
-func (mr *MockProviderMockRecorder) NodePricing(arg0 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NodePricing", reflect.TypeOf((*MockProvider)(nil).NodePricing), arg0)
-}
-
-// PVPricing mocks base method
-func (m *MockProvider) PVPricing(arg0 cloud.PVKey) (*cloud.PV, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "PVPricing", arg0)
-	ret0, _ := ret[0].(*cloud.PV)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// PVPricing indicates an expected call of PVPricing
-func (mr *MockProviderMockRecorder) PVPricing(arg0 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PVPricing", reflect.TypeOf((*MockProvider)(nil).PVPricing), arg0)
-}
-
-// UpdateConfig mocks base method
-func (m *MockProvider) UpdateConfig(arg0 io.Reader, arg1 string) (*cloud.CustomPricing, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "UpdateConfig", arg0, arg1)
-	ret0, _ := ret[0].(*cloud.CustomPricing)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// UpdateConfig indicates an expected call of UpdateConfig
-func (mr *MockProviderMockRecorder) UpdateConfig(arg0, arg1 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateConfig", reflect.TypeOf((*MockProvider)(nil).UpdateConfig), arg0, arg1)
-}