cluster.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. package forms
  2. import (
  3. "encoding/base64"
  4. "errors"
  5. "net/url"
  6. "strings"
  7. "github.com/porter-dev/porter/internal/kubernetes"
  8. "github.com/porter-dev/porter/internal/models"
  9. "github.com/porter-dev/porter/internal/repository"
  10. "k8s.io/client-go/tools/clientcmd/api"
  11. ints "github.com/porter-dev/porter/internal/models/integrations"
  12. )
  13. // ResolveClusterForm will resolve a cluster candidate and create a new cluster
  14. type ResolveClusterForm struct {
  15. Resolver *models.ClusterResolverAll `form:"required"`
  16. ClusterCandidateID uint `json:"cluster_candidate_id" form:"required"`
  17. ProjectID uint `json:"project_id" form:"required"`
  18. UserID uint `json:"user_id" form:"required"`
  19. // populated during the ResolveIntegration step
  20. IntegrationID uint
  21. ClusterCandidate *models.ClusterCandidate
  22. RawConf *api.Config
  23. }
  24. // ResolveIntegration creates an integration in the DB
  25. func (rcf *ResolveClusterForm) ResolveIntegration(
  26. repo repository.Repository,
  27. ) error {
  28. cc, err := repo.Cluster.ReadClusterCandidate(rcf.ClusterCandidateID)
  29. if err != nil {
  30. return err
  31. }
  32. rcf.ClusterCandidate = cc
  33. rawConf, err := kubernetes.GetRawConfigFromBytes(cc.Kubeconfig)
  34. if err != nil {
  35. return err
  36. }
  37. rcf.RawConf = rawConf
  38. context := rawConf.Contexts[rawConf.CurrentContext]
  39. authInfoName := context.AuthInfo
  40. authInfo := rawConf.AuthInfos[authInfoName]
  41. // iterate through the resolvers, and use the ClusterResolverAll to populate
  42. // the required fields
  43. var id uint
  44. switch cc.AuthMechanism {
  45. case models.X509:
  46. id, err = rcf.resolveX509(repo, authInfo)
  47. case models.Bearer:
  48. id, err = rcf.resolveToken(repo, authInfo)
  49. case models.Basic:
  50. id, err = rcf.resolveBasic(repo, authInfo)
  51. case models.Local:
  52. id, err = rcf.resolveLocal(repo, authInfo)
  53. case models.OIDC:
  54. id, err = rcf.resolveOIDC(repo, authInfo)
  55. case models.GCP:
  56. id, err = rcf.resolveGCP(repo, authInfo)
  57. case models.AWS:
  58. id, err = rcf.resolveAWS(repo, authInfo)
  59. }
  60. if err != nil {
  61. return err
  62. }
  63. rcf.IntegrationID = id
  64. return nil
  65. }
  66. func (rcf *ResolveClusterForm) resolveX509(
  67. repo repository.Repository,
  68. authInfo *api.AuthInfo,
  69. ) (uint, error) {
  70. ki := &ints.KubeIntegration{
  71. Mechanism: ints.KubeX509,
  72. UserID: rcf.UserID,
  73. ProjectID: rcf.ProjectID,
  74. }
  75. // attempt to construct cert and key from raw config
  76. if len(authInfo.ClientCertificateData) > 0 {
  77. ki.ClientCertificateData = authInfo.ClientCertificateData
  78. }
  79. if len(authInfo.ClientKeyData) > 0 {
  80. ki.ClientKeyData = authInfo.ClientKeyData
  81. }
  82. // override with resolver
  83. if rcf.Resolver.ClientCertData != "" {
  84. decoded, err := base64.StdEncoding.DecodeString(rcf.Resolver.ClientCertData)
  85. if err != nil {
  86. return 0, err
  87. }
  88. ki.ClientCertificateData = decoded
  89. }
  90. if rcf.Resolver.ClientKeyData != "" {
  91. decoded, err := base64.StdEncoding.DecodeString(rcf.Resolver.ClientKeyData)
  92. if err != nil {
  93. return 0, err
  94. }
  95. ki.ClientKeyData = decoded
  96. }
  97. // if resolvable, write kube integration to repo
  98. if len(ki.ClientCertificateData) == 0 || len(ki.ClientKeyData) == 0 {
  99. return 0, errors.New("could not resolve kube integration (x509)")
  100. }
  101. // return integration id if exists
  102. ki, err := repo.KubeIntegration.CreateKubeIntegration(ki)
  103. if err != nil {
  104. return 0, err
  105. }
  106. return ki.Model.ID, nil
  107. }
  108. func (rcf *ResolveClusterForm) resolveToken(
  109. repo repository.Repository,
  110. authInfo *api.AuthInfo,
  111. ) (uint, error) {
  112. ki := &ints.KubeIntegration{
  113. Mechanism: ints.KubeBearer,
  114. UserID: rcf.UserID,
  115. ProjectID: rcf.ProjectID,
  116. }
  117. // attempt to construct token from raw config
  118. if len(authInfo.Token) > 0 {
  119. ki.Token = []byte(authInfo.Token)
  120. }
  121. // supplement with resolver
  122. if rcf.Resolver.TokenData != "" {
  123. ki.Token = []byte(rcf.Resolver.TokenData)
  124. }
  125. // if resolvable, write kube integration to repo
  126. if len(ki.Token) == 0 {
  127. return 0, errors.New("could not resolve kube integration (token)")
  128. }
  129. // return integration id if exists
  130. ki, err := repo.KubeIntegration.CreateKubeIntegration(ki)
  131. if err != nil {
  132. return 0, err
  133. }
  134. return ki.Model.ID, nil
  135. }
  136. func (rcf *ResolveClusterForm) resolveBasic(
  137. repo repository.Repository,
  138. authInfo *api.AuthInfo,
  139. ) (uint, error) {
  140. ki := &ints.KubeIntegration{
  141. Mechanism: ints.KubeBasic,
  142. UserID: rcf.UserID,
  143. ProjectID: rcf.ProjectID,
  144. }
  145. if len(authInfo.Username) > 0 {
  146. ki.Username = []byte(authInfo.Username)
  147. }
  148. if len(authInfo.Password) > 0 {
  149. ki.Password = []byte(authInfo.Password)
  150. }
  151. if len(ki.Username) == 0 || len(ki.Password) == 0 {
  152. return 0, errors.New("could not resolve kube integration (basic)")
  153. }
  154. // return integration id if exists
  155. ki, err := repo.KubeIntegration.CreateKubeIntegration(ki)
  156. if err != nil {
  157. return 0, err
  158. }
  159. return ki.Model.ID, nil
  160. }
  161. func (rcf *ResolveClusterForm) resolveLocal(
  162. repo repository.Repository,
  163. authInfo *api.AuthInfo,
  164. ) (uint, error) {
  165. ki := &ints.KubeIntegration{
  166. Mechanism: ints.KubeLocal,
  167. UserID: rcf.UserID,
  168. ProjectID: rcf.ProjectID,
  169. Kubeconfig: rcf.ClusterCandidate.Kubeconfig,
  170. }
  171. // return integration id if exists
  172. ki, err := repo.KubeIntegration.CreateKubeIntegration(ki)
  173. if err != nil {
  174. return 0, err
  175. }
  176. return ki.Model.ID, nil
  177. }
  178. func (rcf *ResolveClusterForm) resolveOIDC(
  179. repo repository.Repository,
  180. authInfo *api.AuthInfo,
  181. ) (uint, error) {
  182. oidc := &ints.OIDCIntegration{
  183. Client: ints.OIDCKube,
  184. UserID: rcf.UserID,
  185. ProjectID: rcf.ProjectID,
  186. }
  187. if url, ok := authInfo.AuthProvider.Config["idp-issuer-url"]; ok {
  188. oidc.IssuerURL = []byte(url)
  189. }
  190. if clientID, ok := authInfo.AuthProvider.Config["client-id"]; ok {
  191. oidc.ClientID = []byte(clientID)
  192. }
  193. if clientSecret, ok := authInfo.AuthProvider.Config["client-secret"]; ok {
  194. oidc.ClientSecret = []byte(clientSecret)
  195. }
  196. if caData, ok := authInfo.AuthProvider.Config["idp-certificate-authority-data"]; ok {
  197. // based on the implementation, the oidc plugin expects the data to be base64 encoded,
  198. // which means we will not decode it here
  199. // reference: https://github.com/kubernetes/kubernetes/blob/9dfb4c876bfca7a5ae84259fae2bc337ed90c2d7/staging/src/k8s.io/client-go/plugin/pkg/client/auth/oidc/oidc.go#L135
  200. oidc.CertificateAuthorityData = []byte(caData)
  201. }
  202. if idToken, ok := authInfo.AuthProvider.Config["id-token"]; ok {
  203. oidc.IDToken = []byte(idToken)
  204. }
  205. if refreshToken, ok := authInfo.AuthProvider.Config["refresh-token"]; ok {
  206. oidc.RefreshToken = []byte(refreshToken)
  207. }
  208. // override with resolver
  209. if rcf.Resolver.OIDCIssuerCAData != "" {
  210. // based on the implementation, the oidc plugin expects the data to be base64 encoded,
  211. // which means we will not decode it here
  212. // reference: https://github.com/kubernetes/kubernetes/blob/9dfb4c876bfca7a5ae84259fae2bc337ed90c2d7/staging/src/k8s.io/client-go/plugin/pkg/client/auth/oidc/oidc.go#L135
  213. oidc.CertificateAuthorityData = []byte(rcf.Resolver.OIDCIssuerCAData)
  214. }
  215. // return integration id if exists
  216. oidc, err := repo.OIDCIntegration.CreateOIDCIntegration(oidc)
  217. if err != nil {
  218. return 0, err
  219. }
  220. return oidc.Model.ID, nil
  221. }
  222. func (rcf *ResolveClusterForm) resolveGCP(
  223. repo repository.Repository,
  224. authInfo *api.AuthInfo,
  225. ) (uint, error) {
  226. // TODO -- add GCP project ID and GCP email so that source is trackable
  227. gcp := &ints.GCPIntegration{
  228. UserID: rcf.UserID,
  229. ProjectID: rcf.ProjectID,
  230. }
  231. // supplement with resolver
  232. if rcf.Resolver.GCPKeyData != "" {
  233. gcp.GCPKeyData = []byte(rcf.Resolver.GCPKeyData)
  234. }
  235. // throw error if no data
  236. if len(gcp.GCPKeyData) == 0 {
  237. return 0, errors.New("could not resolve gcp integration")
  238. }
  239. // return integration id if exists
  240. gcp, err := repo.GCPIntegration.CreateGCPIntegration(gcp)
  241. if err != nil {
  242. return 0, err
  243. }
  244. return gcp.Model.ID, nil
  245. }
  246. func (rcf *ResolveClusterForm) resolveAWS(
  247. repo repository.Repository,
  248. authInfo *api.AuthInfo,
  249. ) (uint, error) {
  250. // TODO -- add AWS session token as an optional param
  251. // TODO -- add AWS entity and user ARN
  252. aws := &ints.AWSIntegration{
  253. UserID: rcf.UserID,
  254. ProjectID: rcf.ProjectID,
  255. }
  256. // override with resolver
  257. if rcf.Resolver.AWSClusterID != "" {
  258. aws.AWSClusterID = []byte(rcf.Resolver.AWSClusterID)
  259. }
  260. if rcf.Resolver.AWSAccessKeyID != "" {
  261. aws.AWSAccessKeyID = []byte(rcf.Resolver.AWSAccessKeyID)
  262. }
  263. if rcf.Resolver.AWSSecretAccessKey != "" {
  264. aws.AWSSecretAccessKey = []byte(rcf.Resolver.AWSSecretAccessKey)
  265. }
  266. // throw error if no data
  267. if len(aws.AWSClusterID) == 0 || len(aws.AWSAccessKeyID) == 0 || len(aws.AWSSecretAccessKey) == 0 {
  268. return 0, errors.New("could not resolve aws integration")
  269. }
  270. // return integration id if exists
  271. aws, err := repo.AWSIntegration.CreateAWSIntegration(aws)
  272. if err != nil {
  273. return 0, err
  274. }
  275. return aws.Model.ID, nil
  276. }
  277. // ResolveCluster writes a new cluster to the DB -- this must be called after
  278. // rcf.ResolveIntegration, since it relies on the previously created integration.
  279. func (rcf *ResolveClusterForm) ResolveCluster(
  280. repo repository.Repository,
  281. ) (*models.Cluster, error) {
  282. // build a cluster from the candidate
  283. cluster, err := rcf.buildCluster()
  284. if err != nil {
  285. return nil, err
  286. }
  287. // save cluster to db
  288. return repo.Cluster.CreateCluster(cluster)
  289. }
  290. func (rcf *ResolveClusterForm) buildCluster() (*models.Cluster, error) {
  291. rawConf := rcf.RawConf
  292. kcContext := rawConf.Contexts[rawConf.CurrentContext]
  293. kcAuthInfoName := kcContext.AuthInfo
  294. kcAuthInfo := rawConf.AuthInfos[kcAuthInfoName]
  295. kcClusterName := kcContext.Cluster
  296. kcCluster := rawConf.Clusters[kcClusterName]
  297. cc := rcf.ClusterCandidate
  298. cluster := &models.Cluster{
  299. AuthMechanism: cc.AuthMechanism,
  300. ProjectID: cc.ProjectID,
  301. Name: cc.Name,
  302. Server: cc.Server,
  303. ClusterLocationOfOrigin: kcCluster.LocationOfOrigin,
  304. TLSServerName: kcCluster.TLSServerName,
  305. InsecureSkipTLSVerify: kcCluster.InsecureSkipTLSVerify,
  306. UserLocationOfOrigin: kcAuthInfo.LocationOfOrigin,
  307. UserImpersonate: kcAuthInfo.Impersonate,
  308. }
  309. if len(kcAuthInfo.ImpersonateGroups) > 0 {
  310. cluster.UserImpersonateGroups = strings.Join(kcAuthInfo.ImpersonateGroups, ",")
  311. }
  312. if len(kcCluster.CertificateAuthorityData) > 0 {
  313. cluster.CertificateAuthorityData = kcCluster.CertificateAuthorityData
  314. }
  315. if rcf.Resolver.ClusterCAData != "" {
  316. decoded, err := base64.StdEncoding.DecodeString(rcf.Resolver.ClusterCAData)
  317. // skip if decoding error
  318. if err != nil {
  319. return nil, err
  320. }
  321. cluster.CertificateAuthorityData = decoded
  322. }
  323. if rcf.Resolver.ClusterHostname != "" {
  324. serverURL, err := url.Parse(cluster.Server)
  325. if err != nil {
  326. return nil, err
  327. }
  328. if serverURL.Port() == "" {
  329. serverURL.Host = rcf.Resolver.ClusterHostname
  330. } else {
  331. serverURL.Host = rcf.Resolver.ClusterHostname + ":" + serverURL.Port()
  332. }
  333. cluster.Server = serverURL.String()
  334. }
  335. switch cc.AuthMechanism {
  336. case models.X509, models.Bearer, models.Basic, models.Local:
  337. cluster.KubeIntegrationID = rcf.IntegrationID
  338. case models.OIDC:
  339. cluster.OIDCIntegrationID = rcf.IntegrationID
  340. case models.GCP:
  341. cluster.GCPIntegrationID = rcf.IntegrationID
  342. case models.AWS:
  343. cluster.AWSIntegrationID = rcf.IntegrationID
  344. }
  345. return cluster, nil
  346. }
  347. // // Resolver exposes an interface for resolving a ClusterCandidate.
  348. // // So that actions can be chained together, a pointer to a cluster can be
  349. // // used -- if this points to nil, a new cluster is created
  350. // type Resolver interface {
  351. // PopulateServiceAccount(repo repository.ClusterRepository) error
  352. // }
  353. // // ClusterResolver is the base type for resolving a ClusterCandidate
  354. // type ClusterResolver struct {
  355. // ClusterCandidateID uint `json:"cluster_candidate_id" form:"required"`
  356. // ProjectID uint `json:"project_id" form:"required"`
  357. // Cluster *models.Cluster
  358. // ClusterCandidate *models.ClusterCandidate
  359. // }
  360. // // PopulateServiceAccount will create a service account if it does not exist,
  361. // // or will append a new cluster given by a ServiceAccountCandidate to the
  362. // // ServiceAccount
  363. // func (cr *ClusterResolver) PopulateServiceAccount(
  364. // repo repository.ClusterRepository,
  365. // ) error {
  366. // var err error
  367. // id := cr.ClusterCandidateID
  368. // if cr.ClusterCandidate == nil {
  369. // cr.ClusterCandidate, err = repo.ReadClusterCandidate(id)
  370. // if err != nil {
  371. // return err
  372. // }
  373. // }
  374. // rawConf, err := kubernetes.GetRawConfigFromBytes(cr.ClusterCandidate.Kubeconfig)
  375. // if err != nil {
  376. // return err
  377. // }
  378. // context := rawConf.Contexts[rawConf.CurrentContext]
  379. // authInfoName := context.AuthInfo
  380. // authInfo := rawConf.AuthInfos[authInfoName]
  381. // clusterName := context.Cluster
  382. // cluster := rawConf.Clusters[clusterName]
  383. // cr.Cluster = models.Cluster{
  384. // ProjectID: cr.ProjectID,
  385. // Name: clusterName,
  386. // Server: cluster.Server,
  387. // LocationOfOrigin: cluster.ClusterLocationOfOrigin,
  388. // TLSServerName: cluster.TLSServerName,
  389. // InsecureSkipTLSVerify: cluster.InsecureSkipTLSVerify,
  390. // }
  391. // if len(cr.Cluster.CertificateAuthorityData) > 0 {
  392. // cr.Cluster.CertificateAuthorityData = cluster.CertificateAuthorityData
  393. // }
  394. // // if auth mechanism is local, just write the kubeconfig and return: rest of config is
  395. // // unnecessary
  396. // if sar.SACandidate.Integration == models.Local && len(sar.SACandidate.Kubeconfig) > 0 {
  397. // sar.SA.Kubeconfig = sar.SACandidate.Kubeconfig
  398. // return nil
  399. // }
  400. // if len(authInfo.ClientCertificateData) > 0 {
  401. // sar.SA.ClientCertificateData = authInfo.ClientCertificateData
  402. // }
  403. // if len(authInfo.ClientKeyData) > 0 {
  404. // sar.SA.ClientKeyData = authInfo.ClientKeyData
  405. // }
  406. // if len(authInfo.Token) > 0 {
  407. // sar.SA.Token = []byte(authInfo.Token)
  408. // }
  409. // if len(authInfo.Username) > 0 {
  410. // sar.SA.Username = []byte(authInfo.Username)
  411. // }
  412. // if len(authInfo.Password) > 0 {
  413. // sar.SA.Password = []byte(authInfo.Password)
  414. // }
  415. // if authInfo.AuthProvider != nil && authInfo.AuthProvider.Name == "oidc" {
  416. // if url, ok := authInfo.AuthProvider.Config["idp-issuer-url"]; ok {
  417. // sar.SA.OIDCIssuerURL = []byte(url)
  418. // }
  419. // if clientID, ok := authInfo.AuthProvider.Config["client-id"]; ok {
  420. // sar.SA.OIDCClientID = []byte(clientID)
  421. // }
  422. // if clientSecret, ok := authInfo.AuthProvider.Config["client-secret"]; ok {
  423. // sar.SA.OIDCClientSecret = []byte(clientSecret)
  424. // }
  425. // if caData, ok := authInfo.AuthProvider.Config["idp-certificate-authority-data"]; ok {
  426. // // based on the implementation, the oidc plugin expects the data to be base64 encoded,
  427. // // which means we will not decode it here
  428. // // reference: https://github.com/kubernetes/kubernetes/blob/9dfb4c876bfca7a5ae84259fae2bc337ed90c2d7/staging/src/k8s.io/client-go/plugin/pkg/client/auth/oidc/oidc.go#L135
  429. // sar.SA.OIDCCertificateAuthorityData = []byte(caData)
  430. // }
  431. // if idToken, ok := authInfo.AuthProvider.Config["id-token"]; ok {
  432. // sar.SA.OIDCIDToken = []byte(idToken)
  433. // }
  434. // if refreshToken, ok := authInfo.AuthProvider.Config["refresh-token"]; ok {
  435. // sar.SA.OIDCRefreshToken = []byte(refreshToken)
  436. // }
  437. // }
  438. // return nil
  439. // }
  440. // // ClientKeyDataAction contains the base64 encoded cluster key data
  441. // type ClientKeyDataAction struct {
  442. // *ClusterResolver
  443. // ClientKeyData string `json:"client_key_data" form:"required"`
  444. // }
  445. // // OIDCIssuerDataAction contains the base64 encoded IDP issuer CA data
  446. // type OIDCIssuerDataAction struct {
  447. // *ClusterResolver
  448. // OIDCIssuerCAData string `json:"oidc_idp_issuer_ca_data" form:"required"`
  449. // }
  450. // // PopulateServiceAccount will add OIDC issuer CA data to a ServiceAccount
  451. // func (oida *OIDCIssuerDataAction) PopulateServiceAccount(
  452. // repo repository.ClusterRepository,
  453. // ) error {
  454. // err := oida.ServiceAccountActionResolver.PopulateServiceAccount(repo)
  455. // if err != nil {
  456. // return err
  457. // }
  458. // // based on the implementation, the oidc plugin expects the data to be base64 encoded,
  459. // // which means we will not decode it here
  460. // // reference: https://github.com/kubernetes/kubernetes/blob/9dfb4c876bfca7a5ae84259fae2bc337ed90c2d7/staging/src/k8s.io/client-go/plugin/pkg/client/auth/oidc/oidc.go#L135
  461. // oida.ServiceAccountActionResolver.SA.OIDCCertificateAuthorityData = []byte(oida.OIDCIssuerCAData)
  462. // return nil
  463. // }
  464. // // GCPKeyDataAction contains the GCP key data
  465. // type GCPKeyDataAction struct {
  466. // *ClusterResolver
  467. // GCPKeyData string `json:"gcp_key_data" form:"required"`
  468. // }
  469. // // PopulateServiceAccount will add GCP key data to a ServiceAccount
  470. // func (gkda *GCPKeyDataAction) PopulateServiceAccount(
  471. // repo repository.ClusterRepository,
  472. // ) error {
  473. // err := gkda.ServiceAccountActionResolver.PopulateServiceAccount(repo)
  474. // if err != nil {
  475. // return err
  476. // }
  477. // gkda.ServiceAccountActionResolver.SA.GCPKeyData = []byte(gkda.GCPKeyData)
  478. // return nil
  479. // }
  480. // // AWSDataAction contains the AWS data (access id, key)
  481. // type AWSDataAction struct {
  482. // *ClusterResolver
  483. // AWSAccessKeyID string `json:"aws_access_key_id" form:"required"`
  484. // AWSSecretAccessKey string `json:"aws_secret_access_key" form:"required"`
  485. // AWSClusterID string `json:"aws_cluster_id" form:"required"`
  486. // }
  487. // // PopulateServiceAccount will add GCP key data to a ServiceAccount
  488. // func (akda *AWSDataAction) PopulateServiceAccount(
  489. // repo repository.ClusterRepository,
  490. // ) error {
  491. // err := akda.ServiceAccountActionResolver.PopulateServiceAccount(repo)
  492. // if err != nil {
  493. // return err
  494. // }
  495. // akda.ServiceAccountActionResolver.SA.AWSAccessKeyID = []byte(akda.AWSAccessKeyID)
  496. // akda.ServiceAccountActionResolver.SA.AWSSecretAccessKey = []byte(akda.AWSSecretAccessKey)
  497. // akda.ServiceAccountActionResolver.SA.AWSClusterID = []byte(akda.AWSClusterID)
  498. // return nil
  499. // }