bigqueryconfiguration.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. package gcp
  2. import (
  3. "context"
  4. "fmt"
  5. "strings"
  6. "cloud.google.com/go/bigquery"
  7. "github.com/opencost/opencost/core/pkg/opencost"
  8. "github.com/opencost/opencost/core/pkg/util/json"
  9. "github.com/opencost/opencost/pkg/cloud"
  10. )
  11. type BigQueryConfiguration struct {
  12. ProjectID string `json:"projectID"`
  13. Dataset string `json:"dataset"`
  14. Table string `json:"table"`
  15. ExcludePartitionTime bool `json:"excludePartitionTime"`
  16. Location string `json:"location"`
  17. Authorizer Authorizer `json:"authorizer"`
  18. }
  19. func (bqc *BigQueryConfiguration) Validate() error {
  20. if bqc.Authorizer == nil {
  21. return fmt.Errorf("BigQueryConfig: missing configurer")
  22. }
  23. err := bqc.Authorizer.Validate()
  24. if err != nil {
  25. return fmt.Errorf("BigQueryConfig: issue with GCP Authorizer: %s", err.Error())
  26. }
  27. if bqc.ProjectID == "" {
  28. return fmt.Errorf("BigQueryConfig: missing ProjectID")
  29. }
  30. if bqc.Dataset == "" {
  31. return fmt.Errorf("BigQueryConfig: missing Dataset")
  32. }
  33. if bqc.Table == "" {
  34. return fmt.Errorf("BigQueryConfig: missing Table")
  35. }
  36. return nil
  37. }
  38. func (bqc *BigQueryConfiguration) Equals(config cloud.Config) bool {
  39. if config == nil {
  40. return false
  41. }
  42. thatConfig, ok := config.(*BigQueryConfiguration)
  43. if !ok {
  44. return false
  45. }
  46. if bqc.Authorizer != nil {
  47. if !bqc.Authorizer.Equals(thatConfig.Authorizer) {
  48. return false
  49. }
  50. } else {
  51. if thatConfig.Authorizer != nil {
  52. return false
  53. }
  54. }
  55. if bqc.ProjectID != thatConfig.ProjectID {
  56. return false
  57. }
  58. if bqc.Dataset != thatConfig.Dataset {
  59. return false
  60. }
  61. if bqc.Table != thatConfig.Table {
  62. return false
  63. }
  64. if bqc.Location != thatConfig.Location {
  65. return false
  66. }
  67. return true
  68. }
  69. func (bqc *BigQueryConfiguration) Sanitize() cloud.Config {
  70. return &BigQueryConfiguration{
  71. ProjectID: bqc.ProjectID,
  72. Dataset: bqc.Dataset,
  73. Table: bqc.Table,
  74. Location: bqc.Location,
  75. Authorizer: bqc.Authorizer.Sanitize().(Authorizer),
  76. }
  77. }
  78. // Key uses the Usage Project Id as the Provider Key for GCP
  79. func (bqc *BigQueryConfiguration) Key() string {
  80. return fmt.Sprintf("%s/%s", bqc.ProjectID, bqc.GetBillingDataDataset())
  81. }
  82. func (bqc *BigQueryConfiguration) Provider() string {
  83. return opencost.GCPProvider
  84. }
  85. func (bqc *BigQueryConfiguration) GetBillingDataDataset() string {
  86. return fmt.Sprintf("%s.%s", bqc.Dataset, bqc.Table)
  87. }
  88. func (bqc *BigQueryConfiguration) GetBigQueryClient(ctx context.Context) (*bigquery.Client, error) {
  89. clientOpts, err := bqc.Authorizer.CreateGCPClientOptions()
  90. if err != nil {
  91. return nil, err
  92. }
  93. client, err := bigquery.NewClient(ctx, bqc.ProjectID, clientOpts...)
  94. if err != nil {
  95. return nil, err
  96. }
  97. client.Location = bqc.Location
  98. return client, nil
  99. }
  100. // UnmarshalJSON assumes data is save as an BigQueryConfigurationDTO
  101. func (bqc *BigQueryConfiguration) UnmarshalJSON(b []byte) error {
  102. var f interface{}
  103. err := json.Unmarshal(b, &f)
  104. if err != nil {
  105. return err
  106. }
  107. fmap := f.(map[string]interface{})
  108. projectID, err := cloud.GetInterfaceValue[string](fmap, "projectID")
  109. if err != nil {
  110. return fmt.Errorf("BigQueryConfiguration: FromInterface: %s", err.Error())
  111. }
  112. bqc.ProjectID = projectID
  113. dataset, err := cloud.GetInterfaceValue[string](fmap, "dataset")
  114. if err != nil {
  115. return fmt.Errorf("BigQueryConfiguration: FromInterface: %s", err.Error())
  116. }
  117. bqc.Dataset = dataset
  118. table, err := cloud.GetInterfaceValue[string](fmap, "table")
  119. if err != nil {
  120. return fmt.Errorf("BigQueryConfiguration: FromInterface: %s", err.Error())
  121. }
  122. bqc.Table = table
  123. if _, ok := fmap["location"]; ok {
  124. location, err := cloud.GetInterfaceValue[string](fmap, "location")
  125. if err != nil {
  126. return fmt.Errorf("BigQueryConfiguration: FromInterface: %s", err.Error())
  127. }
  128. bqc.Location = location
  129. }
  130. authAny, ok := fmap["authorizer"]
  131. if !ok {
  132. return fmt.Errorf("StorageConfiguration: UnmarshalJSON: missing authorizer")
  133. }
  134. authorizer, err := cloud.AuthorizerFromInterface(authAny, SelectAuthorizerByType)
  135. if err != nil {
  136. return fmt.Errorf("StorageConfiguration: UnmarshalJSON: %s", err.Error())
  137. }
  138. bqc.Authorizer = authorizer
  139. return nil
  140. }
  141. func ConvertBigQueryConfigToConfig(bqc BigQueryConfig) cloud.KeyedConfig {
  142. if bqc.IsEmpty() {
  143. return nil
  144. }
  145. BillingDataDataset := strings.Split(bqc.BillingDataDataset, ".")
  146. dataset := BillingDataDataset[0]
  147. var table string
  148. if len(BillingDataDataset) > 1 {
  149. table = BillingDataDataset[1]
  150. }
  151. bigQueryConfiguration := &BigQueryConfiguration{
  152. ProjectID: bqc.ProjectID,
  153. Dataset: dataset,
  154. Table: table,
  155. Authorizer: &WorkloadIdentity{}, // Default to WorkloadIdentity
  156. }
  157. if len(bqc.Key) != 0 {
  158. bigQueryConfiguration.Authorizer = &ServiceAccountKey{
  159. Key: bqc.Key,
  160. }
  161. }
  162. return bigQueryConfiguration
  163. }