| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882 |
- package cloud
- import (
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "math"
- "net/http"
- "net/url"
- "os"
- "strconv"
- "strings"
- "time"
- "github.com/aws/aws-sdk-go/aws/awserr"
- "cloud.google.com/go/compute/metadata"
- "golang.org/x/oauth2"
- "golang.org/x/oauth2/google"
- compute "google.golang.org/api/compute/v1"
- "github.com/aws/aws-sdk-go/aws"
- "github.com/aws/aws-sdk-go/aws/credentials"
- "github.com/aws/aws-sdk-go/aws/session"
- "github.com/aws/aws-sdk-go/service/athena"
- "github.com/aws/aws-sdk-go/service/ec2"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/client-go/kubernetes"
- )
- type Provider interface {
- ClusterName() ([]byte, error)
- AddServiceKey(url.Values) error
- GetDisks() ([]byte, error)
- NodePricing(string) (*Node, error)
- AllNodePricing() (interface{}, error)
- DownloadPricingData() error
- GetKey(map[string]string) string
- QuerySQL(string) ([]byte, error)
- }
- func GetDefaultPricingData() (*CustomPricing, error) {
- jsonFile, err := os.Open("/models/default.json")
- if err != nil {
- return nil, err
- }
- defer jsonFile.Close()
- byteValue, err := ioutil.ReadAll(jsonFile)
- if err != nil {
- return nil, err
- }
- var customPricing *CustomPricing = &CustomPricing{}
- err = json.Unmarshal([]byte(byteValue), customPricing)
- if err != nil {
- return nil, err
- }
- return customPricing, nil
- }
- type CustomPricing struct {
- Provider string `json:"provider"`
- Description string `json:"description"`
- CPU string `json:"CPU"`
- SpotCPU string `json:"spotCPU"`
- RAM string `json:"RAM"`
- SpotRAM string `json:"spotRAM"`
- SpotLabel string `json:"spotLabel,omitempty"`
- SpotLabelValue string `json:"spotLabel,omitempty"`
- }
- type NodePrice struct {
- CPU string
- RAM string
- }
- type CustomProvider struct {
- Clientset *kubernetes.Clientset
- Pricing map[string]*NodePrice
- SpotLabel string
- SpotLabelValue string
- }
- func (*CustomProvider) ClusterName() ([]byte, error) {
- return nil, nil
- }
- func (*CustomProvider) AddServiceKey(url.Values) error {
- return nil
- }
- func (*CustomProvider) GetDisks() ([]byte, error) {
- return nil, nil
- }
- func (c *CustomProvider) AllNodePricing() (interface{}, error) {
- return c.Pricing, nil
- }
- func (c *CustomProvider) NodePricing(key string) (*Node, error) {
- if _, ok := c.Pricing[key]; !ok {
- key = "default"
- }
- return &Node{
- VCPUCost: c.Pricing[key].CPU,
- RAMCost: c.Pricing[key].RAM,
- }, nil
- }
- func (c *CustomProvider) DownloadPricingData() error {
- if c.Pricing == nil {
- m := make(map[string]*NodePrice)
- c.Pricing = m
- }
- p, err := GetDefaultPricingData()
- if err != nil {
- return err
- }
- c.Pricing["default"] = &NodePrice{
- CPU: p.CPU,
- RAM: p.RAM,
- }
- c.Pricing["default,spot"] = &NodePrice{
- CPU: p.SpotCPU,
- RAM: p.SpotRAM,
- }
- return nil
- }
- func (c *CustomProvider) GetKey(labels map[string]string) string {
- if labels[c.SpotLabel] != "" && labels[c.SpotLabel] == c.SpotLabelValue {
- return "default,spot"
- }
- return "default" // TODO: multiple custom pricing support.
- }
- func (*CustomProvider) QuerySQL(query string) ([]byte, error) {
- return nil, nil
- }
- type Node struct {
- Cost string `json:"hourlyCost"`
- VCPU string `json:"CPU"`
- VCPUCost string `json:"CPUHourlyCost"`
- RAM string `json:"RAM"`
- RAMCost string `json:"RAMGBHourlyCost"`
- Storage string `json:"storage"`
- StorageCost string `json:"storageHourlyCost"`
- UsesBaseCPUPrice bool `json:"usesDefaultPrice"`
- BaseCPUPrice string `json:"baseCPUPrice"` // Used to compute an implicit RAM GB/Hr price when RAM pricing is not provided.
- }
- func NewProvider(clientset *kubernetes.Clientset, apiKey string) (Provider, error) {
- if metadata.OnGCE() {
- if apiKey == "" {
- return nil, fmt.Errorf("Supply a GCP Key to start getting data")
- }
- return &GCP{
- Clientset: clientset,
- ApiKey: apiKey,
- }, nil
- } else {
- nodes, err := clientset.CoreV1().Nodes().List(metav1.ListOptions{})
- if err != nil {
- return nil, err
- }
- provider := strings.ToLower(nodes.Items[0].Spec.ProviderID)
- if strings.HasPrefix(provider, "aws") {
- return &AWS{
- Clientset: clientset,
- }, nil
- } else {
- log.Printf("Unsupported provider, falling back to default")
- return &CustomProvider{
- Clientset: clientset,
- }, nil
- }
- }
- }
- type userAgentTransport struct {
- userAgent string
- base http.RoundTripper
- }
- func (t userAgentTransport) RoundTrip(req *http.Request) (*http.Response, error) {
- req.Header.Set("User-Agent", t.userAgent)
- return t.base.RoundTrip(req)
- }
- type GCP struct {
- Pricing map[string]*GCPPricing
- Clientset *kubernetes.Clientset
- ApiKey string
- BaseCPUPrice string
- }
- func (*GCP) QuerySQL(query string) ([]byte, error) {
- return nil, nil
- }
- func (*GCP) ClusterName() ([]byte, error) {
- metadataClient := metadata.NewClient(&http.Client{Transport: userAgentTransport{
- userAgent: "kubecost",
- base: http.DefaultTransport,
- }})
- attribute, err := metadataClient.InstanceAttributeValue("cluster-name")
- if err != nil {
- return nil, err
- }
- m := make(map[string]string)
- m["name"] = attribute
- m["provider"] = "GCP"
- return json.Marshal(m)
- }
- func (*GCP) AddServiceKey(formValues url.Values) error {
- key := formValues.Get("key")
- k := []byte(key)
- return ioutil.WriteFile("/var/configs/key.json", k, 0644)
- }
- func (*GCP) GetDisks() ([]byte, error) {
- // metadata API setup
- metadataClient := metadata.NewClient(&http.Client{Transport: userAgentTransport{
- userAgent: "kubecost",
- base: http.DefaultTransport,
- }})
- projID, err := metadataClient.ProjectID()
- if err != nil {
- return nil, err
- }
- client, err := google.DefaultClient(oauth2.NoContext,
- "https://www.googleapis.com/auth/compute.readonly")
- if err != nil {
- return nil, err
- }
- svc, err := compute.New(client)
- if err != nil {
- return nil, err
- }
- res, err := svc.Disks.AggregatedList(projID).Do()
- if err != nil {
- return nil, err
- }
- return json.Marshal(res)
- }
- type GCPPricing struct {
- Name string `json:"name"`
- SKUID string `json:"skuId"`
- Description string `json:"description"`
- Category *GCPResourceInfo `json:"category"`
- ServiceRegions []string `json:"serviceRegions"`
- PricingInfo []*PricingInfo `json:"pricingInfo"`
- ServiceProviderName string `json:"serviceProviderName"`
- Node *Node `json:"node"`
- }
- type PricingInfo struct {
- Summary string `json:"summary"`
- PricingExpression *PricingExpression `json:"pricingExpression"`
- CurrencyConversionRate int `json:"currencyConversionRate"`
- EffectiveTime string `json:""`
- }
- type PricingExpression struct {
- UsageUnit string `json:"usageUnit"`
- UsageUnitDescription string `json:"usageUnitDescription"`
- BaseUnit string `json:"baseUnit"`
- BaseUnitConversionFactor int64 `json:"-"`
- DisplayQuantity int `json:"displayQuantity"`
- TieredRates []*TieredRates `json:"tieredRates"`
- }
- type TieredRates struct {
- StartUsageAmount int `json:"startUsageAmount"`
- UnitPrice *UnitPriceInfo `json:"unitPrice"`
- }
- type UnitPriceInfo struct {
- CurrencyCode string `json:"currencyCode"`
- Units string `json:"units"`
- Nanos float64 `json:"nanos"`
- }
- type GCPResourceInfo struct {
- ServiceDisplayName string `json:"serviceDisplayName"`
- ResourceFamily string `json:"resourceFamily"`
- ResourceGroup string `json:"resourceGroup"`
- UsageType string `json:"usageType"`
- }
- func (gcp *GCP) parsePage(r io.Reader, inputKeys map[string]bool) (map[string]*GCPPricing, string) {
- gcpPricingList := make(map[string]*GCPPricing)
- var nextPageToken string
- dec := json.NewDecoder(r)
- for {
- t, err := dec.Token()
- if err == io.EOF {
- break
- }
- //fmt.Printf("%v \n", t)
- if t == "skus" {
- dec.Token() // [
- for dec.More() {
- product := &GCPPricing{}
- err := dec.Decode(&product)
- if err != nil {
- fmt.Printf("Error: " + err.Error())
- break
- }
- usageType := strings.ToLower(product.Category.UsageType)
- instanceType := strings.ToLower(product.Category.ResourceGroup)
- if (instanceType == "ram" || instanceType == "cpu") && strings.Contains(strings.ToUpper(product.Description), "CUSTOM") {
- instanceType = "custom"
- }
- // instance.toLowerCase() === “f1micro”
- var partialCPU float64
- if strings.ToLower(instanceType) == "f1micro" {
- partialCPU = 0.2
- } else if strings.ToLower(instanceType) == "g1small" {
- partialCPU = 0.5
- }
- for _, sr := range product.ServiceRegions {
- region := sr
- candidateKey := region + "," + instanceType + "," + usageType
- if _, ok := inputKeys[candidateKey]; ok {
- lastRateIndex := len(product.PricingInfo[0].PricingExpression.TieredRates) - 1
- var nanos float64
- if len(product.PricingInfo) > 0 {
- nanos = product.PricingInfo[0].PricingExpression.TieredRates[lastRateIndex].UnitPrice.Nanos
- } else {
- continue
- }
- hourlyPrice := nanos * math.Pow10(-9)
- if hourlyPrice == 0 {
- continue
- } else if strings.Contains(strings.ToUpper(product.Description), "RAM") {
- if instanceType == "custom" {
- log.Printf("RAM custom sku is: " + product.Name)
- }
- if _, ok := gcpPricingList[candidateKey]; ok {
- gcpPricingList[candidateKey].Node.RAMCost = strconv.FormatFloat(hourlyPrice, 'f', -1, 64)
- } else {
- product.Node = &Node{
- RAMCost: strconv.FormatFloat(hourlyPrice, 'f', -1, 64),
- }
- if partialCPU != 0 {
- product.Node.VCPU = fmt.Sprintf("%f", partialCPU)
- }
- gcpPricingList[candidateKey] = product
- }
- break
- } else {
- if _, ok := gcpPricingList[candidateKey]; ok {
- gcpPricingList[candidateKey].Node.VCPUCost = strconv.FormatFloat(hourlyPrice, 'f', -1, 64)
- } else {
- product.Node = &Node{
- VCPUCost: strconv.FormatFloat(hourlyPrice, 'f', -1, 64),
- }
- if partialCPU != 0 {
- product.Node.VCPU = fmt.Sprintf("%f", partialCPU)
- }
- gcpPricingList[candidateKey] = product
- }
- break
- }
- }
- }
- }
- }
- if t == "nextPageToken" {
- pageToken, err := dec.Token()
- if err != nil {
- log.Printf("Error parsing nextpage token: " + err.Error())
- break
- }
- if pageToken.(string) != "" {
- nextPageToken = pageToken.(string)
- } else {
- nextPageToken = "done"
- }
- }
- }
- return gcpPricingList, nextPageToken
- }
- func (gcp *GCP) parsePages(inputKeys map[string]bool) (map[string]*GCPPricing, error) {
- var pages []map[string]*GCPPricing
- url := "https://cloudbilling.googleapis.com/v1/services/6F81-5844-456A/skus?key=" + gcp.ApiKey //AIzaSyDXQPG_MHUEy9neR7stolq6l0ujXmjJlvk
- log.Printf("URL: %s", url)
- var parsePagesHelper func(string) error
- parsePagesHelper = func(pageToken string) error {
- if pageToken == "done" {
- return nil
- } else if pageToken != "" {
- url = url + "&pageToken=" + pageToken
- }
- resp, err := http.Get(url)
- if err != nil {
- return err
- }
- page, token := gcp.parsePage(resp.Body, inputKeys)
- pages = append(pages, page)
- return parsePagesHelper(token)
- }
- err := parsePagesHelper("")
- returnPages := make(map[string]*GCPPricing)
- for _, page := range pages {
- for k, v := range page {
- if val, ok := returnPages[k]; ok { //keys may need to be merged
- if val.Node.RAMCost != "" && val.Node.VCPUCost == "" {
- val.Node.VCPUCost = v.Node.VCPUCost
- } else if val.Node.VCPUCost != "" && val.Node.RAMCost == "" {
- val.Node.RAMCost = v.Node.RAMCost
- } else {
- returnPages[k] = v
- }
- } else {
- returnPages[k] = v
- }
- }
- }
- return returnPages, err
- }
- func (gcp *GCP) DownloadPricingData() error {
- nodeList, err := gcp.Clientset.CoreV1().Nodes().List(metav1.ListOptions{})
- if err != nil {
- return err
- }
- inputkeys := make(map[string]bool)
- for _, n := range nodeList.Items {
- labels := n.GetObjectMeta().GetLabels()
- key := gcp.GetKey(labels)
- inputkeys[key] = true
- }
- pages, err := gcp.parsePages(inputkeys)
- if err != nil {
- return err
- }
- gcp.Pricing = pages
- c, err := GetDefaultPricingData()
- if err != nil {
- log.Printf("Error downloading default pricing data: %s", err.Error())
- }
- gcp.BaseCPUPrice = c.CPU
- return nil
- }
- func (gcp *GCP) GetKey(labels map[string]string) string {
- instanceType := strings.ToLower(strings.Join(strings.Split(labels["beta.kubernetes.io/instance-type"], "-")[:2], ""))
- if instanceType == "n1highmem" || instanceType == "n1highcpu" {
- instanceType = "n1standard" // These are priced the same. TODO: support n1ultrahighmem
- } else if strings.HasPrefix(instanceType, "custom") {
- instanceType = "custom" // The suffix of custom does not matter
- }
- region := strings.ToLower(labels["failure-domain.beta.kubernetes.io/region"])
- var usageType string
- if t, ok := labels["cloud.google.com/gke-preemptible"]; ok && t == "true" {
- usageType = "preemptible"
- } else {
- usageType = "ondemand"
- }
- return region + "," + instanceType + "," + usageType
- }
- func (gcp *GCP) AllNodePricing() (interface{}, error) {
- return gcp.Pricing, nil
- }
- func (gcp *GCP) NodePricing(key string) (*Node, error) {
- if n, ok := gcp.Pricing[key]; ok {
- log.Printf("Returning pricing for node %s: %+v from SKU %s", key, n.Node, n.Name)
- n.Node.BaseCPUPrice = gcp.BaseCPUPrice
- return n.Node, nil
- } else {
- log.Printf("Warning: no pricing data found for %s", key)
- return nil, fmt.Errorf("Warning: no pricing data found for %s", key)
- }
- }
- type AWS struct {
- Pricing map[string]*AWSProductTerms
- ValidPricingKeys map[string]bool
- Clientset *kubernetes.Clientset
- BaseCPUPrice string
- }
- type AWSPricing struct {
- Products map[string]*AWSProduct `json:"products"`
- Terms AWSPricingTerms `json:"terms"`
- }
- type AWSProduct struct {
- Sku string `json:"sku"`
- Attributes AWSProductAttributes `json:"attributes"`
- }
- type AWSProductAttributes struct {
- Location string `json:"location"`
- InstanceType string `json:"instanceType"`
- Memory string `json:"memory"`
- Storage string `json:"storage"`
- VCpu string `json:"vcpu"`
- UsageType string `json:"usagetype"`
- OperatingSystem string `json:"operatingSystem"`
- PreInstalledSw string `json:"preInstalledSw"`
- }
- type AWSPricingTerms struct {
- OnDemand map[string]map[string]*AWSOfferTerm `json:"OnDemand"`
- Reserved map[string]map[string]*AWSOfferTerm `json:"Reserved"`
- }
- type AWSOfferTerm struct {
- Sku string `json:"sku"`
- PriceDimensions map[string]*AWSRateCode `json:"priceDimensions"`
- }
- type AWSRateCode struct {
- Unit string `json:"unit"`
- PricePerUnit AWSCurrencyCode `json:"pricePerUnit"`
- }
- type AWSCurrencyCode struct {
- USD string `json:"USD"`
- }
- type AWSProductTerms struct {
- Sku string `json:"sku"`
- OnDemand *AWSOfferTerm `json:"OnDemand"`
- Reserved *AWSOfferTerm `json:"Reserved"`
- Memory string `json:"memory"`
- Storage string `json:"storage"`
- VCpu string `json:"vcpu"`
- }
- const OnDemandRateCode = ".JRTCKXETXF"
- const ReservedRateCode = ".38NPMPTW36"
- const HourlyRateCode = ".6YS6EN2CT7"
- func (aws *AWS) KubeAttrConversion(location, instanceType, operatingSystem string) string {
- locationToRegion := map[string]string{
- "US East (Ohio)": "us-east-2",
- "US East (N. Virginia)": "us-east-1",
- "US West (N. California)": "us-west-1",
- "US West (Oregon)": "us-west-2",
- "Asia Pacific (Mumbai)": "ap-south-1",
- "Asia Pacific (Osaka-Local)": "ap-northeast-3",
- "Asia Pacific (Seoul)": "ap-northeast-2",
- "Asia Pacific (Singapore)": "ap-southeast-1",
- "Asia Pacific (Sydney)": "ap-southeast-2",
- "Asia Pacific (Tokyo)": "ap-northeast-1",
- "Canada (Central)": "ca-central-1",
- "China (Beijing)": "cn-north-1",
- "China (Ningxia)": "cn-northwest-1",
- "EU (Frankfurt)": "eu-central-1",
- "EU (Ireland)": "eu-west-1",
- "EU (London)": "eu-west-2",
- "EU (Paris)": "eu-west-3",
- "EU (Stockholm)": "eu-north-1",
- "South America (São Paulo)": "sa-east-1",
- "AWS GovCloud (US-East)": "us-gov-east-1",
- "AWS GovCloud (US)": "us-gov-west-1",
- }
- operatingSystem = strings.ToLower(operatingSystem)
- region := locationToRegion[location]
- return region + "," + instanceType + "," + operatingSystem
- }
- func (aws *AWS) GetKey(labels map[string]string) string {
- instanceType := labels["beta.kubernetes.io/instance-type"]
- operatingSystem := labels["beta.kubernetes.io/os"]
- region := labels["failure-domain.beta.kubernetes.io/region"]
- return region + "," + instanceType + "," + operatingSystem
- }
- func (aws *AWS) DownloadPricingData() error {
- nodeList, err := aws.Clientset.CoreV1().Nodes().List(metav1.ListOptions{})
- if err != nil {
- return err
- }
- inputkeys := make(map[string]bool)
- for _, n := range nodeList.Items {
- labels := n.GetObjectMeta().GetLabels()
- key := aws.GetKey(labels)
- inputkeys[key] = true
- }
- aws.Pricing = make(map[string]*AWSProductTerms)
- aws.ValidPricingKeys = make(map[string]bool)
- skusToKeys := make(map[string]string)
- resp, err := http.Get("https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonEC2/current/index.json")
- if err != nil {
- return err
- }
- dec := json.NewDecoder(resp.Body)
- for {
- t, err := dec.Token()
- if err == io.EOF {
- fmt.Printf("done \n")
- break
- }
- if t == "products" {
- dec.Token() //{
- for dec.More() {
- dec.Token() // the sku token
- product := &AWSProduct{}
- err := dec.Decode(&product)
- if err != nil {
- fmt.Printf("Error: " + err.Error())
- break
- }
- if product.Attributes.PreInstalledSw == "NA" &&
- (strings.HasPrefix(product.Attributes.UsageType, "BoxUsage") || strings.Contains(product.Attributes.UsageType, "-BoxUsage")) {
- key := aws.KubeAttrConversion(product.Attributes.Location, product.Attributes.InstanceType, product.Attributes.OperatingSystem)
- if inputkeys[key] {
- aws.Pricing[key] = &AWSProductTerms{
- Sku: product.Sku,
- Memory: product.Attributes.Memory,
- Storage: product.Attributes.Storage,
- VCpu: product.Attributes.VCpu,
- }
- skusToKeys[product.Sku] = key
- }
- aws.ValidPricingKeys[key] = true
- }
- }
- }
- if t == "terms" {
- dec.Token()
- termType, _ := dec.Token()
- if termType == "OnDemand" {
- dec.Token() // {
- for dec.More() {
- sku, _ := dec.Token()
- dec.Token()
- skuOnDemand, _ := dec.Token()
- offerTerm := &AWSOfferTerm{}
- err := dec.Decode(&offerTerm)
- if err != nil {
- fmt.Printf("Error: " + err.Error())
- }
- if sku.(string)+OnDemandRateCode == skuOnDemand {
- key, ok := skusToKeys[sku.(string)]
- if ok {
- aws.Pricing[key].OnDemand = offerTerm
- }
- }
- dec.Token()
- }
- dec.Token()
- }
- }
- }
- if err != nil {
- return err
- }
- c, err := GetDefaultPricingData()
- if err != nil {
- log.Printf("Error downloading default pricing data: %s", err.Error())
- }
- aws.BaseCPUPrice = c.CPU
- return nil
- }
- func (aws *AWS) AllNodePricing() (interface{}, error) {
- return aws.Pricing, nil
- }
- func (aws *AWS) NodePricing(key string) (*Node, error) {
- //return json.Marshal(aws.Pricing[key])
- terms, ok := aws.Pricing[key]
- if ok {
- cost := terms.OnDemand.PriceDimensions[terms.Sku+OnDemandRateCode+HourlyRateCode].PricePerUnit.USD
- return &Node{
- Cost: cost,
- VCPU: terms.VCpu,
- RAM: terms.Memory,
- Storage: terms.Storage,
- }, nil
- } else if _, ok := aws.ValidPricingKeys[key]; ok {
- err := aws.DownloadPricingData()
- if err != nil {
- return nil, err
- }
- terms := aws.Pricing[key]
- cost := terms.OnDemand.PriceDimensions[terms.Sku+OnDemandRateCode+HourlyRateCode].PricePerUnit.USD
- return &Node{
- Cost: cost,
- VCPU: terms.VCpu,
- RAM: terms.Memory,
- Storage: terms.Storage,
- BaseCPUPrice: aws.BaseCPUPrice,
- }, nil
- } else {
- return nil, errors.New("Invalid Pricing Key: " + key + "\n")
- }
- }
- func (*AWS) ClusterName() ([]byte, error) {
- attribute := "AWS Cluster #1"
- m := make(map[string]string)
- m["name"] = attribute
- m["provider"] = "AWS"
- return json.Marshal(m)
- }
- func (*AWS) AddServiceKey(formValues url.Values) error {
- keyID := formValues.Get("access_key_ID")
- key := formValues.Get("secret_access_key")
- m := make(map[string]string)
- m["access_key_ID"] = keyID
- m["secret_access_key"] = key
- json, err := json.Marshal(m)
- if err != nil {
- return err
- }
- return ioutil.WriteFile("/var/configs/key.json", json, 0644)
- }
- func (*AWS) GetDisks() ([]byte, error) {
- jsonFile, err := os.Open("/var/configs/key.json")
- if err == nil {
- byteValue, _ := ioutil.ReadAll(jsonFile)
- var result map[string]string
- json.Unmarshal([]byte(byteValue), &result)
- os.Setenv("AWS_ACCESS_KEY_ID", result["access_key_ID"])
- os.Setenv("AWS_SECRET_ACCESS_KEY", result["secret_access_key"])
- } else if os.IsNotExist(err) {
- log.Printf("Using Default Credentials")
- } else {
- return nil, err
- }
- defer jsonFile.Close()
- byteValue, _ := ioutil.ReadAll(jsonFile)
- var result map[string]string
- json.Unmarshal([]byte(byteValue), &result)
- os.Setenv("AWS_ACCESS_KEY_ID", result["access_key_ID"])
- os.Setenv("AWS_SECRET_ACCESS_KEY", result["secret_access_key"])
- c := &aws.Config{
- Region: aws.String("us-east-1"),
- Credentials: credentials.NewEnvCredentials(),
- }
- s := session.Must(session.NewSession(c))
- ec2Svc := ec2.New(s)
- input := &ec2.DescribeVolumesInput{}
- volumeResult, err := ec2Svc.DescribeVolumes(input)
- if err != nil {
- if aerr, ok := err.(awserr.Error); ok {
- switch aerr.Code() {
- default:
- return nil, aerr
- }
- } else {
- return nil, err
- }
- }
- return json.Marshal(volumeResult)
- }
- func (*AWS) QuerySQL(query string) ([]byte, error) {
- jsonFile, err := os.Open("/var/configs/key.json")
- if err == nil {
- byteValue, _ := ioutil.ReadAll(jsonFile)
- var result map[string]string
- json.Unmarshal([]byte(byteValue), &result)
- os.Setenv("AWS_ACCESS_KEY_ID", result["access_key_ID"])
- os.Setenv("AWS_SECRET_ACCESS_KEY", result["secret_access_key"])
- } else if os.IsNotExist(err) {
- log.Printf("Using Default Credentials")
- } else {
- return nil, err
- }
- defer jsonFile.Close()
- athenaConfigs, err := os.Open("/var/configs/athena.json")
- if err != nil {
- return nil, err
- }
- defer athenaConfigs.Close()
- bytes, _ := ioutil.ReadAll(athenaConfigs)
- var athenaConf map[string]string
- json.Unmarshal([]byte(bytes), &athenaConf)
- region := aws.String(athenaConf["region"])
- resultsBucket := athenaConf["output"]
- database := athenaConf["database"]
- c := &aws.Config{
- Region: region,
- Credentials: credentials.NewEnvCredentials(),
- }
- s := session.Must(session.NewSession(c))
- svc := athena.New(s)
- var e athena.StartQueryExecutionInput
- var r athena.ResultConfiguration
- r.SetOutputLocation(resultsBucket)
- e.SetResultConfiguration(&r)
- e.SetQueryString(query)
- var q athena.QueryExecutionContext
- q.SetDatabase(database)
- e.SetQueryExecutionContext(&q)
- res, err := svc.StartQueryExecution(&e)
- if err != nil {
- return nil, err
- }
- fmt.Println("StartQueryExecution result:")
- fmt.Println(res.GoString())
- var qri athena.GetQueryExecutionInput
- qri.SetQueryExecutionId(*res.QueryExecutionId)
- var qrop *athena.GetQueryExecutionOutput
- duration := time.Duration(2) * time.Second // Pause for 2 seconds
- for {
- qrop, err = svc.GetQueryExecution(&qri)
- if err != nil {
- return nil, err
- }
- if *qrop.QueryExecution.Status.State != "RUNNING" {
- break
- }
- time.Sleep(duration)
- }
- if *qrop.QueryExecution.Status.State == "SUCCEEDED" {
- var ip athena.GetQueryResultsInput
- ip.SetQueryExecutionId(*res.QueryExecutionId)
- op, err := svc.GetQueryResults(&ip)
- if err != nil {
- return nil, err
- }
- bytes, err := json.Marshal(op.ResultSet)
- if err != nil {
- return nil, err
- }
- return bytes, nil
- } else {
- return nil, fmt.Errorf("Error getting query results : %s", *qrop.QueryExecution.Status.State)
- }
- }
|