providers.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. package handlers
  2. import (
  3. // Keep context for future use in actual cloud calls
  4. "fmt"
  5. "net/http"
  6. "git.linuxforward.com/byop/byop-engine/cloud"
  7. "git.linuxforward.com/byop/byop-engine/models"
  8. "github.com/gin-gonic/gin"
  9. )
  10. // ProviderHandler handles provider-related operations
  11. type ProviderHandler struct {
  12. // No specific dependencies for now, but a ProviderService might be added later
  13. // for more complex logic or if providers are stored in a DB.
  14. }
  15. // NewProviderHandler creates a new ProviderHandler
  16. func NewProviderHandler() *ProviderHandler {
  17. return &ProviderHandler{}
  18. }
  19. // RegisterRoutes registers routes for provider operations
  20. func (h *ProviderHandler) RegisterRoutes(r *gin.RouterGroup) {
  21. r.GET("/", h.ListProviders)
  22. // r.POST("/", h.CreateProvider) // Assuming CreateProvider would involve DB interaction
  23. r.GET("/:id", h.GetProvider)
  24. // r.PUT("/:id", h.UpdateProvider) // Assuming UpdateProvider would involve DB interaction
  25. // r.DELETE("/:id", h.DeleteProvider) // Assuming DeleteProvider would involve DB interaction
  26. r.GET("/:id/regions", h.GetProviderRegions)
  27. r.POST("/:id/validate", h.ValidateProviderCredentials)
  28. }
  29. // ListProviders returns all supported providers
  30. func (h *ProviderHandler) ListProviders(c *gin.Context) {
  31. providers := cloud.GetSupportedProviders() // This returns []string
  32. if len(providers) == 0 {
  33. appErr := models.NewErrNotFound("no_providers_found", fmt.Errorf("No supported providers found"))
  34. models.RespondWithError(c, appErr)
  35. return
  36. }
  37. c.JSON(http.StatusOK, providers) // Returns a JSON array of strings
  38. }
  39. // GetProvider returns a specific provider by its ID (name)
  40. func (h *ProviderHandler) GetProvider(c *gin.Context) {
  41. ctx := c.Request.Context() // Get context, though not used for current static implementation
  42. _ = ctx // Avoid unused variable error if not used yet in this specific function
  43. providerName := c.Param("id")
  44. supportedProviders := cloud.GetSupportedProviders()
  45. found := false
  46. for _, sp := range supportedProviders {
  47. if sp == providerName {
  48. found = true
  49. break
  50. }
  51. }
  52. if !found {
  53. appErr := models.NewErrNotFound("provider_not_found", fmt.Errorf("Provider with name '%s' not found or not supported", providerName))
  54. models.RespondWithError(c, appErr)
  55. return
  56. }
  57. // Return basic information about the provider
  58. c.JSON(http.StatusOK, gin.H{"name": providerName, "status": "supported"})
  59. }
  60. // GetProviderRegions returns available regions for a provider
  61. func (h *ProviderHandler) GetProviderRegions(c *gin.Context) {
  62. ctx := c.Request.Context() // Get context, to be used for actual cloud calls
  63. providerName := c.Param("id")
  64. // Validate if provider is supported
  65. supportedProviders := cloud.GetSupportedProviders()
  66. isSupported := false
  67. for _, sp := range supportedProviders {
  68. if sp == providerName {
  69. isSupported = true
  70. break
  71. }
  72. }
  73. if !isSupported {
  74. appErr := models.NewErrNotFound("provider_not_supported", fmt.Errorf("Provider '%s' is not supported", providerName))
  75. models.RespondWithError(c, appErr)
  76. return
  77. }
  78. // Placeholder: Replace with actual calls to cloud.Provider.ListRegions(ctx)
  79. // For now, using static list. ctx is available for when real calls are made.
  80. _ = ctx // Explicitly use ctx to avoid "unused" error if no cloud calls yet
  81. var regions []string
  82. switch providerName {
  83. case "aws":
  84. regions = []string{"us-east-1", "us-west-2", "eu-west-1"} // Example static regions
  85. case "digitalocean":
  86. regions = []string{"nyc1", "sfo3", "lon1"} // Example static regions
  87. case "ovh":
  88. regions = []string{"GRA", "SBG", "BHS"} // Example static regions
  89. default:
  90. // This case should ideally not be hit if validation above is correct
  91. // and the switch covers all supported providers from cloud.GetSupportedProviders().
  92. appErr := models.NewErrInternalServer("provider_regions_not_implemented", fmt.Errorf("Regions not implemented for provider '%s'", providerName))
  93. models.RespondWithError(c, appErr)
  94. return
  95. }
  96. c.JSON(http.StatusOK, regions)
  97. }
  98. // ValidateProviderCredentials validates provider credentials
  99. func (h *ProviderHandler) ValidateProviderCredentials(c *gin.Context) {
  100. ctx := c.Request.Context() // Get context, to be used for actual cloud calls
  101. providerName := c.Param("id")
  102. // Validate if provider is supported
  103. supportedProviders := cloud.GetSupportedProviders()
  104. isSupported := false
  105. for _, sp := range supportedProviders {
  106. if sp == providerName {
  107. isSupported = true
  108. break
  109. }
  110. }
  111. if !isSupported {
  112. appErr := models.NewErrNotFound("provider_not_supported_for_validation", fmt.Errorf("Provider '%s' is not supported for credential validation", providerName))
  113. models.RespondWithError(c, appErr)
  114. return
  115. }
  116. var credsBody struct {
  117. AccessKeyID string `json:"access_key_id"`
  118. SecretAccessKey string `json:"secret_access_key"`
  119. Token string `json:"token"` // For AWS STS, DO API token, OVH tokens etc.
  120. ProjectID string `json:"project_id"` // For GCP, OVH
  121. Region string `json:"region"` // Optional, might be needed for some validation endpoints
  122. }
  123. if err := c.ShouldBindJSON(&credsBody); err != nil {
  124. appErr := models.NewErrValidation("invalid_credentials_format", map[string]string{"body": "Invalid request body for credentials"}, err)
  125. models.RespondWithError(c, appErr)
  126. return
  127. }
  128. // Placeholder: Replace with actual calls to a provider method like provider.ValidateCredentials(ctx, creds)
  129. // ctx is available for when real calls are made.
  130. _ = ctx // Explicitly use ctx to avoid "unused" error if no cloud calls yet
  131. var isValid bool
  132. var validationError error // To store error from actual validation logic
  133. switch providerName {
  134. case "aws":
  135. // e.g., isValid, validationError = cloud.ValidateAWSCredentials(ctx, credsBody.AccessKeyID, credsBody.SecretAccessKey, credsBody.Token, credsBody.Region)
  136. isValid = true // Assume valid for now
  137. case "digitalocean":
  138. // e.g., isValid, validationError = cloud.ValidateDOCredentials(ctx, credsBody.Token)
  139. isValid = true // Assume valid for now
  140. case "ovh":
  141. // e.g., isValid, validationError = cloud.ValidateOVHCredentials(ctx, ...)
  142. isValid = true // Assume valid for now
  143. default:
  144. // This case should ideally not be hit if validation above is correct.
  145. appErr := models.NewErrInternalServer("provider_validation_not_implemented", fmt.Errorf("Credential validation not implemented for provider '%s'", providerName))
  146. models.RespondWithError(c, appErr)
  147. return
  148. }
  149. if validationError != nil {
  150. appErr := models.NewErrInternalServer("credential_validation_error", fmt.Errorf("Error validating credentials for %s: %w", providerName, validationError))
  151. models.RespondWithError(c, appErr)
  152. return
  153. }
  154. if !isValid {
  155. appErr := models.NewErrUnauthorized("invalid_provider_credentials", fmt.Errorf("Credentials for provider %s are invalid", providerName))
  156. models.RespondWithError(c, appErr)
  157. return
  158. }
  159. c.JSON(http.StatusOK, gin.H{"valid": true, "message": "Credentials validated successfully"})
  160. }