athenaconfiguration.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. package aws
  2. import (
  3. "fmt"
  4. "github.com/opencost/opencost/core/pkg/opencost"
  5. "github.com/opencost/opencost/core/pkg/util/json"
  6. "github.com/opencost/opencost/pkg/cloud"
  7. )
  8. // AthenaConfiguration
  9. type AthenaConfiguration struct {
  10. Bucket string `json:"bucket"`
  11. Region string `json:"region"`
  12. Database string `json:"database"`
  13. Catalog string `json:"catalog"`
  14. Table string `json:"table"`
  15. Workgroup string `json:"workgroup"`
  16. Account string `json:"account"`
  17. Authorizer Authorizer `json:"authorizer"`
  18. CURVersion string `json:"curVersion,omitempty"` // "1.0" or "2.0", defaults to "2.0" if not specified
  19. }
  20. func (ac *AthenaConfiguration) Validate() error {
  21. // Validate Authorizer
  22. if ac.Authorizer == nil {
  23. return fmt.Errorf("AthenaConfiguration: missing Authorizer")
  24. }
  25. err := ac.Authorizer.Validate()
  26. if err != nil {
  27. return fmt.Errorf("AthenaConfiguration: %s", err)
  28. }
  29. // Validate base properties
  30. if ac.Bucket == "" {
  31. return fmt.Errorf("AthenaConfiguration: missing bucket")
  32. }
  33. if ac.Region == "" {
  34. return fmt.Errorf("AthenaConfiguration: missing region")
  35. }
  36. if ac.Database == "" {
  37. return fmt.Errorf("AthenaConfiguration: missing database")
  38. }
  39. if ac.Table == "" {
  40. return fmt.Errorf("AthenaConfiguration: missing table")
  41. }
  42. if ac.Account == "" {
  43. return fmt.Errorf("AthenaConfiguration: missing account")
  44. }
  45. // Validate CURVersion if specified
  46. if ac.CURVersion != "" && ac.CURVersion != "1.0" && ac.CURVersion != "2.0" {
  47. return fmt.Errorf("AthenaConfiguration: invalid CURVersion '%s', must be '1.0' or '2.0'", ac.CURVersion)
  48. }
  49. return nil
  50. }
  51. func (ac *AthenaConfiguration) Equals(config cloud.Config) bool {
  52. if config == nil {
  53. return false
  54. }
  55. thatConfig, ok := config.(*AthenaConfiguration)
  56. if !ok {
  57. return false
  58. }
  59. if ac.Authorizer != nil {
  60. if !ac.Authorizer.Equals(thatConfig.Authorizer) {
  61. return false
  62. }
  63. } else {
  64. if thatConfig.Authorizer != nil {
  65. return false
  66. }
  67. }
  68. if ac.Bucket != thatConfig.Bucket {
  69. return false
  70. }
  71. if ac.Region != thatConfig.Region {
  72. return false
  73. }
  74. if ac.Database != thatConfig.Database {
  75. return false
  76. }
  77. if ac.Catalog != thatConfig.Catalog {
  78. return false
  79. }
  80. if ac.Table != thatConfig.Table {
  81. return false
  82. }
  83. if ac.Workgroup != thatConfig.Workgroup {
  84. return false
  85. }
  86. if ac.Account != thatConfig.Account {
  87. return false
  88. }
  89. if ac.CURVersion != thatConfig.CURVersion {
  90. return false
  91. }
  92. return true
  93. }
  94. func (ac *AthenaConfiguration) Sanitize() cloud.Config {
  95. return &AthenaConfiguration{
  96. Bucket: ac.Bucket,
  97. Region: ac.Region,
  98. Database: ac.Database,
  99. Catalog: ac.Catalog,
  100. Table: ac.Table,
  101. Workgroup: ac.Workgroup,
  102. Account: ac.Account,
  103. Authorizer: ac.Authorizer.Sanitize().(Authorizer),
  104. CURVersion: ac.CURVersion,
  105. }
  106. }
  107. func (ac *AthenaConfiguration) Key() string {
  108. return fmt.Sprintf("%s/%s", ac.Account, ac.Bucket)
  109. }
  110. func (ac *AthenaConfiguration) Provider() string {
  111. return opencost.AWSProvider
  112. }
  113. func (ac *AthenaConfiguration) UnmarshalJSON(b []byte) error {
  114. var f interface{}
  115. err := json.Unmarshal(b, &f)
  116. if err != nil {
  117. return err
  118. }
  119. fmap := f.(map[string]interface{})
  120. bucket, err := cloud.GetInterfaceValue[string](fmap, "bucket")
  121. if err != nil {
  122. return fmt.Errorf("AthenaConfiguration: UnmarshalJSON: %w", err)
  123. }
  124. ac.Bucket = bucket
  125. region, err := cloud.GetInterfaceValue[string](fmap, "region")
  126. if err != nil {
  127. return fmt.Errorf("AthenaConfiguration: UnmarshalJSON: %w", err)
  128. }
  129. ac.Region = region
  130. database, err := cloud.GetInterfaceValue[string](fmap, "database")
  131. if err != nil {
  132. return fmt.Errorf("AthenaConfiguration: UnmarshalJSON: %w", err)
  133. }
  134. ac.Database = database
  135. if _, ok := fmap["catalog"]; ok {
  136. catalog, err := cloud.GetInterfaceValue[string](fmap, "catalog")
  137. if err != nil {
  138. return fmt.Errorf("AthenaConfiguration: UnmarshalJSON: %w", err)
  139. }
  140. ac.Catalog = catalog
  141. }
  142. table, err := cloud.GetInterfaceValue[string](fmap, "table")
  143. if err != nil {
  144. return fmt.Errorf("AthenaConfiguration: UnmarshalJSON: %w", err)
  145. }
  146. ac.Table = table
  147. workgroup, err := cloud.GetInterfaceValue[string](fmap, "workgroup")
  148. if err != nil {
  149. return fmt.Errorf("AthenaConfiguration: UnmarshalJSON: %w", err)
  150. }
  151. ac.Workgroup = workgroup
  152. account, err := cloud.GetInterfaceValue[string](fmap, "account")
  153. if err != nil {
  154. return fmt.Errorf("AthenaConfiguration: UnmarshalJSON: %w", err)
  155. }
  156. ac.Account = account
  157. authAny, ok := fmap["authorizer"]
  158. if !ok {
  159. return fmt.Errorf("AthenaConfiguration: UnmarshalJSON: missing authorizer")
  160. }
  161. authorizer, err := cloud.AuthorizerFromInterface(authAny, SelectAuthorizerByType)
  162. if err != nil {
  163. return fmt.Errorf("AthenaConfiguration: UnmarshalJSON: %w", err)
  164. }
  165. ac.Authorizer = authorizer
  166. // Parse CURVersion if present (optional field)
  167. if _, ok := fmap["curVersion"]; ok {
  168. curVersion, err := cloud.GetInterfaceValue[string](fmap, "curVersion")
  169. if err != nil {
  170. return fmt.Errorf("AthenaConfiguration: UnmarshalJSON: %w", err)
  171. }
  172. ac.CURVersion = curVersion
  173. } else {
  174. // Default to 2.0 if not specified
  175. ac.CURVersion = "2.0"
  176. }
  177. return nil
  178. }
  179. // ConvertAwsAthenaInfoToConfig takes a legacy config and generates a Config based on the presence of properties to match
  180. // legacy behavior
  181. func ConvertAwsAthenaInfoToConfig(aai AwsAthenaInfo) cloud.KeyedConfig {
  182. if aai.IsEmpty() {
  183. return nil
  184. }
  185. var authorizer Authorizer
  186. if aai.ServiceKeyName == "" && aai.ServiceKeySecret == "" {
  187. authorizer = &ServiceAccount{}
  188. } else {
  189. authorizer = &AccessKey{
  190. ID: aai.ServiceKeyName,
  191. Secret: aai.ServiceKeySecret,
  192. }
  193. }
  194. // Wrap Authorizer with AssumeRole if MasterPayerArn is set
  195. if aai.MasterPayerARN != "" {
  196. authorizer = &AssumeRole{
  197. Authorizer: authorizer,
  198. RoleARN: aai.MasterPayerARN,
  199. }
  200. }
  201. var config cloud.KeyedConfig
  202. if aai.AthenaTable != "" || aai.AthenaDatabase != "" {
  203. // Use CURVersion from config if specified, otherwise default to 2.0
  204. curVersion := aai.CURVersion
  205. if curVersion == "" {
  206. curVersion = "2.0"
  207. }
  208. config = &AthenaConfiguration{
  209. Bucket: aai.AthenaBucketName,
  210. Region: aai.AthenaRegion,
  211. Catalog: aai.AthenaCatalog,
  212. Database: aai.AthenaDatabase,
  213. Table: aai.AthenaTable,
  214. Workgroup: aai.AthenaWorkgroup,
  215. Account: aai.AccountID,
  216. Authorizer: authorizer,
  217. CURVersion: curVersion,
  218. }
  219. } else {
  220. config = &S3Configuration{
  221. Bucket: aai.AthenaBucketName,
  222. Region: aai.AthenaRegion,
  223. Account: aai.AccountID,
  224. Authorizer: authorizer,
  225. }
  226. }
  227. return config
  228. }