list_aws.go 3.9 KB

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