list_aws.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. package cloud_provider
  2. import (
  3. "net/http"
  4. "github.com/aws/aws-sdk-go/aws/arn"
  5. "github.com/porter-dev/porter/api/server/authz"
  6. "github.com/porter-dev/porter/api/server/handlers"
  7. "github.com/porter-dev/porter/api/server/shared"
  8. "github.com/porter-dev/porter/api/server/shared/apierrors"
  9. "github.com/porter-dev/porter/api/server/shared/config"
  10. "github.com/porter-dev/porter/api/types"
  11. "github.com/porter-dev/porter/internal/models"
  12. "github.com/porter-dev/porter/internal/telemetry"
  13. )
  14. // ListAwsAccountsResponse describes an outbound response for listing aws accounts on
  15. // a given project.
  16. type ListAwsAccountsResponse struct {
  17. // Accounts is a list of aws account objects
  18. Accounts []AwsAccount `json:"accounts"`
  19. }
  20. // AwsAccount describes an outbound response for listing aws accounts on
  21. // a given project.
  22. //
  23. // The shape of the object is "generic" as there will be similar endpoints in
  24. // the future for other cloud providers.
  25. type AwsAccount struct {
  26. // CloudProviderID is the cloud provider id - for AWS, this is an account
  27. CloudProviderID string `json:"cloud_provider_id"`
  28. // ProjectID is the project the account is associated with
  29. ProjectID uint `json:"project_id"`
  30. }
  31. // ListAwsAccountsHandler is a struct for handling an aws cloud provider list request
  32. type ListAwsAccountsHandler struct {
  33. handlers.PorterHandlerWriter
  34. authz.KubernetesAgentGetter
  35. }
  36. // NewListAwsAccountsHandler constructs a ListAwsAccountsHandler
  37. func NewListAwsAccountsHandler(
  38. config *config.Config,
  39. writer shared.ResultWriter,
  40. ) *ListAwsAccountsHandler {
  41. return &ListAwsAccountsHandler{
  42. PorterHandlerWriter: handlers.NewDefaultPorterHandler(config, nil, writer),
  43. KubernetesAgentGetter: authz.NewOutOfClusterAgentGetter(config),
  44. }
  45. }
  46. // ServeHTTP returns a list of AWS Accounts
  47. //
  48. // todo: Move this logic down into CCP
  49. func (c *ListAwsAccountsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  50. ctx, span := telemetry.NewSpan(r.Context(), "serve-cloud-provider-list-aws")
  51. defer span.End()
  52. project, _ := ctx.Value(types.ProjectScope).(*models.Project)
  53. res := ListAwsAccountsResponse{
  54. Accounts: []AwsAccount{},
  55. }
  56. if !project.GetFeatureFlag(models.CapiProvisionerEnabled, c.Config().LaunchDarklyClient) {
  57. err := telemetry.Error(ctx, span, nil, "listing cloud providers not available on non-capi clusters")
  58. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  59. return
  60. }
  61. dblinks, err := c.Repo().AWSAssumeRoleChainer().List(ctx, project.ID)
  62. if err != nil {
  63. err := telemetry.Error(ctx, span, err, "unable to find assume role chain links")
  64. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  65. return
  66. }
  67. for _, link := range dblinks {
  68. targetArn, err := arn.Parse(link.TargetARN)
  69. if err != nil {
  70. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "err-target-arn", Value: link.TargetARN})
  71. err := telemetry.Error(ctx, span, err, "unable to parse target arn")
  72. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  73. return
  74. }
  75. account := AwsAccount{
  76. CloudProviderID: targetArn.AccountID,
  77. ProjectID: uint(link.ProjectID),
  78. }
  79. if contains(res.Accounts, account) {
  80. continue
  81. }
  82. res.Accounts = append(res.Accounts, account)
  83. }
  84. c.WriteResult(w, r, res)
  85. }
  86. // contains will check if the list of AwsAccounts contains the specified account
  87. // TODO: replace this with an upgrade to Go 1.21 in favor of slices.Contains()
  88. func contains(s []AwsAccount, e AwsAccount) bool {
  89. for _, a := range s {
  90. if a == e {
  91. return true
  92. }
  93. }
  94. return false
  95. }