provider.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882
  1. package cloud
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "log"
  9. "math"
  10. "net/http"
  11. "net/url"
  12. "os"
  13. "strconv"
  14. "strings"
  15. "time"
  16. "github.com/aws/aws-sdk-go/aws/awserr"
  17. "cloud.google.com/go/compute/metadata"
  18. "golang.org/x/oauth2"
  19. "golang.org/x/oauth2/google"
  20. compute "google.golang.org/api/compute/v1"
  21. "github.com/aws/aws-sdk-go/aws"
  22. "github.com/aws/aws-sdk-go/aws/credentials"
  23. "github.com/aws/aws-sdk-go/aws/session"
  24. "github.com/aws/aws-sdk-go/service/athena"
  25. "github.com/aws/aws-sdk-go/service/ec2"
  26. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  27. "k8s.io/client-go/kubernetes"
  28. )
  29. type Provider interface {
  30. ClusterName() ([]byte, error)
  31. AddServiceKey(url.Values) error
  32. GetDisks() ([]byte, error)
  33. NodePricing(string) (*Node, error)
  34. AllNodePricing() (interface{}, error)
  35. DownloadPricingData() error
  36. GetKey(map[string]string) string
  37. QuerySQL(string) ([]byte, error)
  38. }
  39. func GetDefaultPricingData() (*CustomPricing, error) {
  40. jsonFile, err := os.Open("/models/default.json")
  41. if err != nil {
  42. return nil, err
  43. }
  44. defer jsonFile.Close()
  45. byteValue, err := ioutil.ReadAll(jsonFile)
  46. if err != nil {
  47. return nil, err
  48. }
  49. var customPricing *CustomPricing = &CustomPricing{}
  50. err = json.Unmarshal([]byte(byteValue), customPricing)
  51. if err != nil {
  52. return nil, err
  53. }
  54. return customPricing, nil
  55. }
  56. type CustomPricing struct {
  57. Provider string `json:"provider"`
  58. Description string `json:"description"`
  59. CPU string `json:"CPU"`
  60. SpotCPU string `json:"spotCPU"`
  61. RAM string `json:"RAM"`
  62. SpotRAM string `json:"spotRAM"`
  63. SpotLabel string `json:"spotLabel,omitempty"`
  64. SpotLabelValue string `json:"spotLabel,omitempty"`
  65. }
  66. type NodePrice struct {
  67. CPU string
  68. RAM string
  69. }
  70. type CustomProvider struct {
  71. Clientset *kubernetes.Clientset
  72. Pricing map[string]*NodePrice
  73. SpotLabel string
  74. SpotLabelValue string
  75. }
  76. func (*CustomProvider) ClusterName() ([]byte, error) {
  77. return nil, nil
  78. }
  79. func (*CustomProvider) AddServiceKey(url.Values) error {
  80. return nil
  81. }
  82. func (*CustomProvider) GetDisks() ([]byte, error) {
  83. return nil, nil
  84. }
  85. func (c *CustomProvider) AllNodePricing() (interface{}, error) {
  86. return c.Pricing, nil
  87. }
  88. func (c *CustomProvider) NodePricing(key string) (*Node, error) {
  89. if _, ok := c.Pricing[key]; !ok {
  90. key = "default"
  91. }
  92. return &Node{
  93. VCPUCost: c.Pricing[key].CPU,
  94. RAMCost: c.Pricing[key].RAM,
  95. }, nil
  96. }
  97. func (c *CustomProvider) DownloadPricingData() error {
  98. if c.Pricing == nil {
  99. m := make(map[string]*NodePrice)
  100. c.Pricing = m
  101. }
  102. p, err := GetDefaultPricingData()
  103. if err != nil {
  104. return err
  105. }
  106. c.Pricing["default"] = &NodePrice{
  107. CPU: p.CPU,
  108. RAM: p.RAM,
  109. }
  110. c.Pricing["default,spot"] = &NodePrice{
  111. CPU: p.SpotCPU,
  112. RAM: p.SpotRAM,
  113. }
  114. return nil
  115. }
  116. func (c *CustomProvider) GetKey(labels map[string]string) string {
  117. if labels[c.SpotLabel] != "" && labels[c.SpotLabel] == c.SpotLabelValue {
  118. return "default,spot"
  119. }
  120. return "default" // TODO: multiple custom pricing support.
  121. }
  122. func (*CustomProvider) QuerySQL(query string) ([]byte, error) {
  123. return nil, nil
  124. }
  125. type Node struct {
  126. Cost string `json:"hourlyCost"`
  127. VCPU string `json:"CPU"`
  128. VCPUCost string `json:"CPUHourlyCost"`
  129. RAM string `json:"RAM"`
  130. RAMCost string `json:"RAMGBHourlyCost"`
  131. Storage string `json:"storage"`
  132. StorageCost string `json:"storageHourlyCost"`
  133. UsesBaseCPUPrice bool `json:"usesDefaultPrice"`
  134. BaseCPUPrice string `json:"baseCPUPrice"` // Used to compute an implicit RAM GB/Hr price when RAM pricing is not provided.
  135. }
  136. func NewProvider(clientset *kubernetes.Clientset, apiKey string) (Provider, error) {
  137. if metadata.OnGCE() {
  138. if apiKey == "" {
  139. return nil, fmt.Errorf("Supply a GCP Key to start getting data")
  140. }
  141. return &GCP{
  142. Clientset: clientset,
  143. ApiKey: apiKey,
  144. }, nil
  145. } else {
  146. nodes, err := clientset.CoreV1().Nodes().List(metav1.ListOptions{})
  147. if err != nil {
  148. return nil, err
  149. }
  150. provider := strings.ToLower(nodes.Items[0].Spec.ProviderID)
  151. if strings.HasPrefix(provider, "aws") {
  152. return &AWS{
  153. Clientset: clientset,
  154. }, nil
  155. } else {
  156. log.Printf("Unsupported provider, falling back to default")
  157. return &CustomProvider{
  158. Clientset: clientset,
  159. }, nil
  160. }
  161. }
  162. }
  163. type userAgentTransport struct {
  164. userAgent string
  165. base http.RoundTripper
  166. }
  167. func (t userAgentTransport) RoundTrip(req *http.Request) (*http.Response, error) {
  168. req.Header.Set("User-Agent", t.userAgent)
  169. return t.base.RoundTrip(req)
  170. }
  171. type GCP struct {
  172. Pricing map[string]*GCPPricing
  173. Clientset *kubernetes.Clientset
  174. ApiKey string
  175. BaseCPUPrice string
  176. }
  177. func (*GCP) QuerySQL(query string) ([]byte, error) {
  178. return nil, nil
  179. }
  180. func (*GCP) ClusterName() ([]byte, error) {
  181. metadataClient := metadata.NewClient(&http.Client{Transport: userAgentTransport{
  182. userAgent: "kubecost",
  183. base: http.DefaultTransport,
  184. }})
  185. attribute, err := metadataClient.InstanceAttributeValue("cluster-name")
  186. if err != nil {
  187. return nil, err
  188. }
  189. m := make(map[string]string)
  190. m["name"] = attribute
  191. m["provider"] = "GCP"
  192. return json.Marshal(m)
  193. }
  194. func (*GCP) AddServiceKey(formValues url.Values) error {
  195. key := formValues.Get("key")
  196. k := []byte(key)
  197. return ioutil.WriteFile("/var/configs/key.json", k, 0644)
  198. }
  199. func (*GCP) GetDisks() ([]byte, error) {
  200. // metadata API setup
  201. metadataClient := metadata.NewClient(&http.Client{Transport: userAgentTransport{
  202. userAgent: "kubecost",
  203. base: http.DefaultTransport,
  204. }})
  205. projID, err := metadataClient.ProjectID()
  206. if err != nil {
  207. return nil, err
  208. }
  209. client, err := google.DefaultClient(oauth2.NoContext,
  210. "https://www.googleapis.com/auth/compute.readonly")
  211. if err != nil {
  212. return nil, err
  213. }
  214. svc, err := compute.New(client)
  215. if err != nil {
  216. return nil, err
  217. }
  218. res, err := svc.Disks.AggregatedList(projID).Do()
  219. if err != nil {
  220. return nil, err
  221. }
  222. return json.Marshal(res)
  223. }
  224. type GCPPricing struct {
  225. Name string `json:"name"`
  226. SKUID string `json:"skuId"`
  227. Description string `json:"description"`
  228. Category *GCPResourceInfo `json:"category"`
  229. ServiceRegions []string `json:"serviceRegions"`
  230. PricingInfo []*PricingInfo `json:"pricingInfo"`
  231. ServiceProviderName string `json:"serviceProviderName"`
  232. Node *Node `json:"node"`
  233. }
  234. type PricingInfo struct {
  235. Summary string `json:"summary"`
  236. PricingExpression *PricingExpression `json:"pricingExpression"`
  237. CurrencyConversionRate int `json:"currencyConversionRate"`
  238. EffectiveTime string `json:""`
  239. }
  240. type PricingExpression struct {
  241. UsageUnit string `json:"usageUnit"`
  242. UsageUnitDescription string `json:"usageUnitDescription"`
  243. BaseUnit string `json:"baseUnit"`
  244. BaseUnitConversionFactor int64 `json:"-"`
  245. DisplayQuantity int `json:"displayQuantity"`
  246. TieredRates []*TieredRates `json:"tieredRates"`
  247. }
  248. type TieredRates struct {
  249. StartUsageAmount int `json:"startUsageAmount"`
  250. UnitPrice *UnitPriceInfo `json:"unitPrice"`
  251. }
  252. type UnitPriceInfo struct {
  253. CurrencyCode string `json:"currencyCode"`
  254. Units string `json:"units"`
  255. Nanos float64 `json:"nanos"`
  256. }
  257. type GCPResourceInfo struct {
  258. ServiceDisplayName string `json:"serviceDisplayName"`
  259. ResourceFamily string `json:"resourceFamily"`
  260. ResourceGroup string `json:"resourceGroup"`
  261. UsageType string `json:"usageType"`
  262. }
  263. func (gcp *GCP) parsePage(r io.Reader, inputKeys map[string]bool) (map[string]*GCPPricing, string) {
  264. gcpPricingList := make(map[string]*GCPPricing)
  265. var nextPageToken string
  266. dec := json.NewDecoder(r)
  267. for {
  268. t, err := dec.Token()
  269. if err == io.EOF {
  270. break
  271. }
  272. //fmt.Printf("%v \n", t)
  273. if t == "skus" {
  274. dec.Token() // [
  275. for dec.More() {
  276. product := &GCPPricing{}
  277. err := dec.Decode(&product)
  278. if err != nil {
  279. fmt.Printf("Error: " + err.Error())
  280. break
  281. }
  282. usageType := strings.ToLower(product.Category.UsageType)
  283. instanceType := strings.ToLower(product.Category.ResourceGroup)
  284. if (instanceType == "ram" || instanceType == "cpu") && strings.Contains(strings.ToUpper(product.Description), "CUSTOM") {
  285. instanceType = "custom"
  286. }
  287. // instance.toLowerCase() === “f1micro”
  288. var partialCPU float64
  289. if strings.ToLower(instanceType) == "f1micro" {
  290. partialCPU = 0.2
  291. } else if strings.ToLower(instanceType) == "g1small" {
  292. partialCPU = 0.5
  293. }
  294. for _, sr := range product.ServiceRegions {
  295. region := sr
  296. candidateKey := region + "," + instanceType + "," + usageType
  297. if _, ok := inputKeys[candidateKey]; ok {
  298. lastRateIndex := len(product.PricingInfo[0].PricingExpression.TieredRates) - 1
  299. var nanos float64
  300. if len(product.PricingInfo) > 0 {
  301. nanos = product.PricingInfo[0].PricingExpression.TieredRates[lastRateIndex].UnitPrice.Nanos
  302. } else {
  303. continue
  304. }
  305. hourlyPrice := nanos * math.Pow10(-9)
  306. if hourlyPrice == 0 {
  307. continue
  308. } else if strings.Contains(strings.ToUpper(product.Description), "RAM") {
  309. if instanceType == "custom" {
  310. log.Printf("RAM custom sku is: " + product.Name)
  311. }
  312. if _, ok := gcpPricingList[candidateKey]; ok {
  313. gcpPricingList[candidateKey].Node.RAMCost = strconv.FormatFloat(hourlyPrice, 'f', -1, 64)
  314. } else {
  315. product.Node = &Node{
  316. RAMCost: strconv.FormatFloat(hourlyPrice, 'f', -1, 64),
  317. }
  318. if partialCPU != 0 {
  319. product.Node.VCPU = fmt.Sprintf("%f", partialCPU)
  320. }
  321. gcpPricingList[candidateKey] = product
  322. }
  323. break
  324. } else {
  325. if _, ok := gcpPricingList[candidateKey]; ok {
  326. gcpPricingList[candidateKey].Node.VCPUCost = strconv.FormatFloat(hourlyPrice, 'f', -1, 64)
  327. } else {
  328. product.Node = &Node{
  329. VCPUCost: strconv.FormatFloat(hourlyPrice, 'f', -1, 64),
  330. }
  331. if partialCPU != 0 {
  332. product.Node.VCPU = fmt.Sprintf("%f", partialCPU)
  333. }
  334. gcpPricingList[candidateKey] = product
  335. }
  336. break
  337. }
  338. }
  339. }
  340. }
  341. }
  342. if t == "nextPageToken" {
  343. pageToken, err := dec.Token()
  344. if err != nil {
  345. log.Printf("Error parsing nextpage token: " + err.Error())
  346. break
  347. }
  348. if pageToken.(string) != "" {
  349. nextPageToken = pageToken.(string)
  350. } else {
  351. nextPageToken = "done"
  352. }
  353. }
  354. }
  355. return gcpPricingList, nextPageToken
  356. }
  357. func (gcp *GCP) parsePages(inputKeys map[string]bool) (map[string]*GCPPricing, error) {
  358. var pages []map[string]*GCPPricing
  359. url := "https://cloudbilling.googleapis.com/v1/services/6F81-5844-456A/skus?key=" + gcp.ApiKey //AIzaSyDXQPG_MHUEy9neR7stolq6l0ujXmjJlvk
  360. log.Printf("URL: %s", url)
  361. var parsePagesHelper func(string) error
  362. parsePagesHelper = func(pageToken string) error {
  363. if pageToken == "done" {
  364. return nil
  365. } else if pageToken != "" {
  366. url = url + "&pageToken=" + pageToken
  367. }
  368. resp, err := http.Get(url)
  369. if err != nil {
  370. return err
  371. }
  372. page, token := gcp.parsePage(resp.Body, inputKeys)
  373. pages = append(pages, page)
  374. return parsePagesHelper(token)
  375. }
  376. err := parsePagesHelper("")
  377. returnPages := make(map[string]*GCPPricing)
  378. for _, page := range pages {
  379. for k, v := range page {
  380. if val, ok := returnPages[k]; ok { //keys may need to be merged
  381. if val.Node.RAMCost != "" && val.Node.VCPUCost == "" {
  382. val.Node.VCPUCost = v.Node.VCPUCost
  383. } else if val.Node.VCPUCost != "" && val.Node.RAMCost == "" {
  384. val.Node.RAMCost = v.Node.RAMCost
  385. } else {
  386. returnPages[k] = v
  387. }
  388. } else {
  389. returnPages[k] = v
  390. }
  391. }
  392. }
  393. return returnPages, err
  394. }
  395. func (gcp *GCP) DownloadPricingData() error {
  396. nodeList, err := gcp.Clientset.CoreV1().Nodes().List(metav1.ListOptions{})
  397. if err != nil {
  398. return err
  399. }
  400. inputkeys := make(map[string]bool)
  401. for _, n := range nodeList.Items {
  402. labels := n.GetObjectMeta().GetLabels()
  403. key := gcp.GetKey(labels)
  404. inputkeys[key] = true
  405. }
  406. pages, err := gcp.parsePages(inputkeys)
  407. if err != nil {
  408. return err
  409. }
  410. gcp.Pricing = pages
  411. c, err := GetDefaultPricingData()
  412. if err != nil {
  413. log.Printf("Error downloading default pricing data: %s", err.Error())
  414. }
  415. gcp.BaseCPUPrice = c.CPU
  416. return nil
  417. }
  418. func (gcp *GCP) GetKey(labels map[string]string) string {
  419. instanceType := strings.ToLower(strings.Join(strings.Split(labels["beta.kubernetes.io/instance-type"], "-")[:2], ""))
  420. if instanceType == "n1highmem" || instanceType == "n1highcpu" {
  421. instanceType = "n1standard" // These are priced the same. TODO: support n1ultrahighmem
  422. } else if strings.HasPrefix(instanceType, "custom") {
  423. instanceType = "custom" // The suffix of custom does not matter
  424. }
  425. region := strings.ToLower(labels["failure-domain.beta.kubernetes.io/region"])
  426. var usageType string
  427. if t, ok := labels["cloud.google.com/gke-preemptible"]; ok && t == "true" {
  428. usageType = "preemptible"
  429. } else {
  430. usageType = "ondemand"
  431. }
  432. return region + "," + instanceType + "," + usageType
  433. }
  434. func (gcp *GCP) AllNodePricing() (interface{}, error) {
  435. return gcp.Pricing, nil
  436. }
  437. func (gcp *GCP) NodePricing(key string) (*Node, error) {
  438. if n, ok := gcp.Pricing[key]; ok {
  439. log.Printf("Returning pricing for node %s: %+v from SKU %s", key, n.Node, n.Name)
  440. n.Node.BaseCPUPrice = gcp.BaseCPUPrice
  441. return n.Node, nil
  442. } else {
  443. log.Printf("Warning: no pricing data found for %s", key)
  444. return nil, fmt.Errorf("Warning: no pricing data found for %s", key)
  445. }
  446. }
  447. type AWS struct {
  448. Pricing map[string]*AWSProductTerms
  449. ValidPricingKeys map[string]bool
  450. Clientset *kubernetes.Clientset
  451. BaseCPUPrice string
  452. }
  453. type AWSPricing struct {
  454. Products map[string]*AWSProduct `json:"products"`
  455. Terms AWSPricingTerms `json:"terms"`
  456. }
  457. type AWSProduct struct {
  458. Sku string `json:"sku"`
  459. Attributes AWSProductAttributes `json:"attributes"`
  460. }
  461. type AWSProductAttributes struct {
  462. Location string `json:"location"`
  463. InstanceType string `json:"instanceType"`
  464. Memory string `json:"memory"`
  465. Storage string `json:"storage"`
  466. VCpu string `json:"vcpu"`
  467. UsageType string `json:"usagetype"`
  468. OperatingSystem string `json:"operatingSystem"`
  469. PreInstalledSw string `json:"preInstalledSw"`
  470. }
  471. type AWSPricingTerms struct {
  472. OnDemand map[string]map[string]*AWSOfferTerm `json:"OnDemand"`
  473. Reserved map[string]map[string]*AWSOfferTerm `json:"Reserved"`
  474. }
  475. type AWSOfferTerm struct {
  476. Sku string `json:"sku"`
  477. PriceDimensions map[string]*AWSRateCode `json:"priceDimensions"`
  478. }
  479. type AWSRateCode struct {
  480. Unit string `json:"unit"`
  481. PricePerUnit AWSCurrencyCode `json:"pricePerUnit"`
  482. }
  483. type AWSCurrencyCode struct {
  484. USD string `json:"USD"`
  485. }
  486. type AWSProductTerms struct {
  487. Sku string `json:"sku"`
  488. OnDemand *AWSOfferTerm `json:"OnDemand"`
  489. Reserved *AWSOfferTerm `json:"Reserved"`
  490. Memory string `json:"memory"`
  491. Storage string `json:"storage"`
  492. VCpu string `json:"vcpu"`
  493. }
  494. const OnDemandRateCode = ".JRTCKXETXF"
  495. const ReservedRateCode = ".38NPMPTW36"
  496. const HourlyRateCode = ".6YS6EN2CT7"
  497. func (aws *AWS) KubeAttrConversion(location, instanceType, operatingSystem string) string {
  498. locationToRegion := map[string]string{
  499. "US East (Ohio)": "us-east-2",
  500. "US East (N. Virginia)": "us-east-1",
  501. "US West (N. California)": "us-west-1",
  502. "US West (Oregon)": "us-west-2",
  503. "Asia Pacific (Mumbai)": "ap-south-1",
  504. "Asia Pacific (Osaka-Local)": "ap-northeast-3",
  505. "Asia Pacific (Seoul)": "ap-northeast-2",
  506. "Asia Pacific (Singapore)": "ap-southeast-1",
  507. "Asia Pacific (Sydney)": "ap-southeast-2",
  508. "Asia Pacific (Tokyo)": "ap-northeast-1",
  509. "Canada (Central)": "ca-central-1",
  510. "China (Beijing)": "cn-north-1",
  511. "China (Ningxia)": "cn-northwest-1",
  512. "EU (Frankfurt)": "eu-central-1",
  513. "EU (Ireland)": "eu-west-1",
  514. "EU (London)": "eu-west-2",
  515. "EU (Paris)": "eu-west-3",
  516. "EU (Stockholm)": "eu-north-1",
  517. "South America (São Paulo)": "sa-east-1",
  518. "AWS GovCloud (US-East)": "us-gov-east-1",
  519. "AWS GovCloud (US)": "us-gov-west-1",
  520. }
  521. operatingSystem = strings.ToLower(operatingSystem)
  522. region := locationToRegion[location]
  523. return region + "," + instanceType + "," + operatingSystem
  524. }
  525. func (aws *AWS) GetKey(labels map[string]string) string {
  526. instanceType := labels["beta.kubernetes.io/instance-type"]
  527. operatingSystem := labels["beta.kubernetes.io/os"]
  528. region := labels["failure-domain.beta.kubernetes.io/region"]
  529. return region + "," + instanceType + "," + operatingSystem
  530. }
  531. func (aws *AWS) DownloadPricingData() error {
  532. nodeList, err := aws.Clientset.CoreV1().Nodes().List(metav1.ListOptions{})
  533. if err != nil {
  534. return err
  535. }
  536. inputkeys := make(map[string]bool)
  537. for _, n := range nodeList.Items {
  538. labels := n.GetObjectMeta().GetLabels()
  539. key := aws.GetKey(labels)
  540. inputkeys[key] = true
  541. }
  542. aws.Pricing = make(map[string]*AWSProductTerms)
  543. aws.ValidPricingKeys = make(map[string]bool)
  544. skusToKeys := make(map[string]string)
  545. resp, err := http.Get("https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonEC2/current/index.json")
  546. if err != nil {
  547. return err
  548. }
  549. dec := json.NewDecoder(resp.Body)
  550. for {
  551. t, err := dec.Token()
  552. if err == io.EOF {
  553. fmt.Printf("done \n")
  554. break
  555. }
  556. if t == "products" {
  557. dec.Token() //{
  558. for dec.More() {
  559. dec.Token() // the sku token
  560. product := &AWSProduct{}
  561. err := dec.Decode(&product)
  562. if err != nil {
  563. fmt.Printf("Error: " + err.Error())
  564. break
  565. }
  566. if product.Attributes.PreInstalledSw == "NA" &&
  567. (strings.HasPrefix(product.Attributes.UsageType, "BoxUsage") || strings.Contains(product.Attributes.UsageType, "-BoxUsage")) {
  568. key := aws.KubeAttrConversion(product.Attributes.Location, product.Attributes.InstanceType, product.Attributes.OperatingSystem)
  569. if inputkeys[key] {
  570. aws.Pricing[key] = &AWSProductTerms{
  571. Sku: product.Sku,
  572. Memory: product.Attributes.Memory,
  573. Storage: product.Attributes.Storage,
  574. VCpu: product.Attributes.VCpu,
  575. }
  576. skusToKeys[product.Sku] = key
  577. }
  578. aws.ValidPricingKeys[key] = true
  579. }
  580. }
  581. }
  582. if t == "terms" {
  583. dec.Token()
  584. termType, _ := dec.Token()
  585. if termType == "OnDemand" {
  586. dec.Token() // {
  587. for dec.More() {
  588. sku, _ := dec.Token()
  589. dec.Token()
  590. skuOnDemand, _ := dec.Token()
  591. offerTerm := &AWSOfferTerm{}
  592. err := dec.Decode(&offerTerm)
  593. if err != nil {
  594. fmt.Printf("Error: " + err.Error())
  595. }
  596. if sku.(string)+OnDemandRateCode == skuOnDemand {
  597. key, ok := skusToKeys[sku.(string)]
  598. if ok {
  599. aws.Pricing[key].OnDemand = offerTerm
  600. }
  601. }
  602. dec.Token()
  603. }
  604. dec.Token()
  605. }
  606. }
  607. }
  608. if err != nil {
  609. return err
  610. }
  611. c, err := GetDefaultPricingData()
  612. if err != nil {
  613. log.Printf("Error downloading default pricing data: %s", err.Error())
  614. }
  615. aws.BaseCPUPrice = c.CPU
  616. return nil
  617. }
  618. func (aws *AWS) AllNodePricing() (interface{}, error) {
  619. return aws.Pricing, nil
  620. }
  621. func (aws *AWS) NodePricing(key string) (*Node, error) {
  622. //return json.Marshal(aws.Pricing[key])
  623. terms, ok := aws.Pricing[key]
  624. if ok {
  625. cost := terms.OnDemand.PriceDimensions[terms.Sku+OnDemandRateCode+HourlyRateCode].PricePerUnit.USD
  626. return &Node{
  627. Cost: cost,
  628. VCPU: terms.VCpu,
  629. RAM: terms.Memory,
  630. Storage: terms.Storage,
  631. }, nil
  632. } else if _, ok := aws.ValidPricingKeys[key]; ok {
  633. err := aws.DownloadPricingData()
  634. if err != nil {
  635. return nil, err
  636. }
  637. terms := aws.Pricing[key]
  638. cost := terms.OnDemand.PriceDimensions[terms.Sku+OnDemandRateCode+HourlyRateCode].PricePerUnit.USD
  639. return &Node{
  640. Cost: cost,
  641. VCPU: terms.VCpu,
  642. RAM: terms.Memory,
  643. Storage: terms.Storage,
  644. BaseCPUPrice: aws.BaseCPUPrice,
  645. }, nil
  646. } else {
  647. return nil, errors.New("Invalid Pricing Key: " + key + "\n")
  648. }
  649. }
  650. func (*AWS) ClusterName() ([]byte, error) {
  651. attribute := "AWS Cluster #1"
  652. m := make(map[string]string)
  653. m["name"] = attribute
  654. m["provider"] = "AWS"
  655. return json.Marshal(m)
  656. }
  657. func (*AWS) AddServiceKey(formValues url.Values) error {
  658. keyID := formValues.Get("access_key_ID")
  659. key := formValues.Get("secret_access_key")
  660. m := make(map[string]string)
  661. m["access_key_ID"] = keyID
  662. m["secret_access_key"] = key
  663. json, err := json.Marshal(m)
  664. if err != nil {
  665. return err
  666. }
  667. return ioutil.WriteFile("/var/configs/key.json", json, 0644)
  668. }
  669. func (*AWS) GetDisks() ([]byte, error) {
  670. jsonFile, err := os.Open("/var/configs/key.json")
  671. if err == nil {
  672. byteValue, _ := ioutil.ReadAll(jsonFile)
  673. var result map[string]string
  674. json.Unmarshal([]byte(byteValue), &result)
  675. os.Setenv("AWS_ACCESS_KEY_ID", result["access_key_ID"])
  676. os.Setenv("AWS_SECRET_ACCESS_KEY", result["secret_access_key"])
  677. } else if os.IsNotExist(err) {
  678. log.Printf("Using Default Credentials")
  679. } else {
  680. return nil, err
  681. }
  682. defer jsonFile.Close()
  683. byteValue, _ := ioutil.ReadAll(jsonFile)
  684. var result map[string]string
  685. json.Unmarshal([]byte(byteValue), &result)
  686. os.Setenv("AWS_ACCESS_KEY_ID", result["access_key_ID"])
  687. os.Setenv("AWS_SECRET_ACCESS_KEY", result["secret_access_key"])
  688. c := &aws.Config{
  689. Region: aws.String("us-east-1"),
  690. Credentials: credentials.NewEnvCredentials(),
  691. }
  692. s := session.Must(session.NewSession(c))
  693. ec2Svc := ec2.New(s)
  694. input := &ec2.DescribeVolumesInput{}
  695. volumeResult, err := ec2Svc.DescribeVolumes(input)
  696. if err != nil {
  697. if aerr, ok := err.(awserr.Error); ok {
  698. switch aerr.Code() {
  699. default:
  700. return nil, aerr
  701. }
  702. } else {
  703. return nil, err
  704. }
  705. }
  706. return json.Marshal(volumeResult)
  707. }
  708. func (*AWS) QuerySQL(query string) ([]byte, error) {
  709. jsonFile, err := os.Open("/var/configs/key.json")
  710. if err == nil {
  711. byteValue, _ := ioutil.ReadAll(jsonFile)
  712. var result map[string]string
  713. json.Unmarshal([]byte(byteValue), &result)
  714. os.Setenv("AWS_ACCESS_KEY_ID", result["access_key_ID"])
  715. os.Setenv("AWS_SECRET_ACCESS_KEY", result["secret_access_key"])
  716. } else if os.IsNotExist(err) {
  717. log.Printf("Using Default Credentials")
  718. } else {
  719. return nil, err
  720. }
  721. defer jsonFile.Close()
  722. athenaConfigs, err := os.Open("/var/configs/athena.json")
  723. if err != nil {
  724. return nil, err
  725. }
  726. defer athenaConfigs.Close()
  727. bytes, _ := ioutil.ReadAll(athenaConfigs)
  728. var athenaConf map[string]string
  729. json.Unmarshal([]byte(bytes), &athenaConf)
  730. region := aws.String(athenaConf["region"])
  731. resultsBucket := athenaConf["output"]
  732. database := athenaConf["database"]
  733. c := &aws.Config{
  734. Region: region,
  735. Credentials: credentials.NewEnvCredentials(),
  736. }
  737. s := session.Must(session.NewSession(c))
  738. svc := athena.New(s)
  739. var e athena.StartQueryExecutionInput
  740. var r athena.ResultConfiguration
  741. r.SetOutputLocation(resultsBucket)
  742. e.SetResultConfiguration(&r)
  743. e.SetQueryString(query)
  744. var q athena.QueryExecutionContext
  745. q.SetDatabase(database)
  746. e.SetQueryExecutionContext(&q)
  747. res, err := svc.StartQueryExecution(&e)
  748. if err != nil {
  749. return nil, err
  750. }
  751. fmt.Println("StartQueryExecution result:")
  752. fmt.Println(res.GoString())
  753. var qri athena.GetQueryExecutionInput
  754. qri.SetQueryExecutionId(*res.QueryExecutionId)
  755. var qrop *athena.GetQueryExecutionOutput
  756. duration := time.Duration(2) * time.Second // Pause for 2 seconds
  757. for {
  758. qrop, err = svc.GetQueryExecution(&qri)
  759. if err != nil {
  760. return nil, err
  761. }
  762. if *qrop.QueryExecution.Status.State != "RUNNING" {
  763. break
  764. }
  765. time.Sleep(duration)
  766. }
  767. if *qrop.QueryExecution.Status.State == "SUCCEEDED" {
  768. var ip athena.GetQueryResultsInput
  769. ip.SetQueryExecutionId(*res.QueryExecutionId)
  770. op, err := svc.GetQueryResults(&ip)
  771. if err != nil {
  772. return nil, err
  773. }
  774. bytes, err := json.Marshal(op.ResultSet)
  775. if err != nil {
  776. return nil, err
  777. }
  778. return bytes, nil
  779. } else {
  780. return nil, fmt.Errorf("Error getting query results : %s", *qrop.QueryExecution.Status.State)
  781. }
  782. }