Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/server/docs.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/server/swagger.json

Large diffs are not rendered by default.

61 changes: 61 additions & 0 deletions docs/server/swagger.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 42 additions & 4 deletions pkg/api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ import (
v1 "github.com/stacklok/toolhive/pkg/api/v1"
"github.com/stacklok/toolhive/pkg/auth"
"github.com/stacklok/toolhive/pkg/client"
"github.com/stacklok/toolhive/pkg/config"
"github.com/stacklok/toolhive/pkg/container"
"github.com/stacklok/toolhive/pkg/container/runtime"
"github.com/stacklok/toolhive/pkg/groups"
"github.com/stacklok/toolhive/pkg/logger"
"github.com/stacklok/toolhive/pkg/secrets"
"github.com/stacklok/toolhive/pkg/updates"
"github.com/stacklok/toolhive/pkg/workloads"
)
Expand All @@ -57,6 +59,7 @@ type ServerBuilder struct {
clientManager client.Manager
workloadManager workloads.Manager
groupManager groups.Manager
secretsProvider secrets.Provider
}

// NewServerBuilder creates a new ServerBuilder with default configuration
Expand Down Expand Up @@ -133,6 +136,12 @@ func (b *ServerBuilder) WithGroupManager(manager groups.Manager) *ServerBuilder
return b
}

// WithSecretsProvider sets the secrets provider
func (b *ServerBuilder) WithSecretsProvider(provider secrets.Provider) *ServerBuilder {
b.secretsProvider = provider
return b
}

// Build creates and configures the HTTP router
func (b *ServerBuilder) Build(ctx context.Context) (*chi.Mux, error) {
r := chi.NewRouter()
Expand Down Expand Up @@ -207,20 +216,49 @@ func (b *ServerBuilder) createDefaultManagers(ctx context.Context) error {
return fmt.Errorf("failed to create group manager: %v", err)
}
}
if b.secretsProvider == nil {
b.secretsProvider, err = getSecretsManager()
if err != nil {
return fmt.Errorf("failed to create secrets provider: %v", err)
}
}

return nil
}

// getSecretsManager is a helper function to get the secrets manager
func getSecretsManager() (secrets.Provider, error) {
cfg := config.NewDefaultProvider().GetConfig()

// Check if secrets setup has been completed
if !cfg.Secrets.SetupCompleted {
return nil, secrets.ErrSecretsNotSetup
}

providerType, err := cfg.Secrets.GetProviderType()
if err != nil {
return nil, err
}

return secrets.CreateSecretProvider(providerType)
}

// setupDefaultRoutes sets up the default API routes
func (b *ServerBuilder) setupDefaultRoutes(r *chi.Mux) {
routers := map[string]http.Handler{
"/health": v1.HealthcheckRouter(b.containerRuntime),
"/api/v1beta/version": v1.VersionRouter(),
"/api/v1beta/workloads": v1.WorkloadRouter(b.workloadManager, b.containerRuntime, b.groupManager, b.debugMode),
"/health": v1.HealthcheckRouter(b.containerRuntime),
"/api/v1beta/version": v1.VersionRouter(),
"/api/v1beta/workloads": v1.WorkloadRouter(
b.workloadManager,
b.containerRuntime,
b.groupManager,
b.secretsProvider,
b.debugMode,
),
"/api/v1beta/registry": v1.RegistryRouter(),
"/api/v1beta/discovery": v1.DiscoveryRouter(),
"/api/v1beta/clients": v1.ClientRouter(b.clientManager, b.workloadManager, b.groupManager),
"/api/v1beta/secrets": v1.SecretsRouter(),
"/api/v1beta/secrets": v1.SecretsRouter(b.secretsProvider),
"/api/v1beta/groups": v1.GroupsRouter(b.groupManager, b.workloadManager, b.clientManager),
}

Expand Down
102 changes: 18 additions & 84 deletions pkg/api/v1/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ const (
// SecretsRoutes defines the routes for the secrets API.
type SecretsRoutes struct {
configProvider config.Provider
provider secrets.Provider
}

// NewSecretsRoutes creates a new SecretsRoutes with the default config provider
func NewSecretsRoutes() *SecretsRoutes {
func NewSecretsRoutes(provider secrets.Provider) *SecretsRoutes {
return &SecretsRoutes{
configProvider: config.NewDefaultProvider(),
provider: provider,
}
}

Expand All @@ -39,8 +41,8 @@ func NewSecretsRoutesWithProvider(provider config.Provider) *SecretsRoutes {
}

// SecretsRouter creates a new router for the secrets API.
func SecretsRouter() http.Handler {
routes := NewSecretsRoutes()
func SecretsRouter(provider secrets.Provider) http.Handler {
routes := NewSecretsRoutes(provider)
return secretsRouterWithRoutes(routes)
}

Expand Down Expand Up @@ -233,15 +235,7 @@ func (s *SecretsRoutes) getSecretsProvider(w http.ResponseWriter, _ *http.Reques
return
}

// Get provider capabilities
provider, err := s.getSecretsManager()
if err != nil {
logger.Errorf("Failed to create secrets provider: %v", err)
http.Error(w, "Failed to access secrets provider", http.StatusInternalServerError)
return
}

capabilities := provider.Capabilities()
capabilities := s.provider.Capabilities()

w.Header().Set("Content-Type", "application/json")
resp := getSecretsProviderResponse{
Expand Down Expand Up @@ -274,24 +268,14 @@ func (s *SecretsRoutes) getSecretsProvider(w http.ResponseWriter, _ *http.Reques
// @Failure 500 {string} string "Internal Server Error"
// @Router /api/v1beta/secrets/default/keys [get]
func (s *SecretsRoutes) listSecrets(w http.ResponseWriter, r *http.Request) {
provider, err := s.getSecretsManager()
if err != nil {
if errors.Is(err, secrets.ErrSecretsNotSetup) {
http.Error(w, "Secrets provider not setup", http.StatusNotFound)
return
}
logger.Errorf("Failed to get secrets manager: %v", err)
http.Error(w, "Failed to access secrets provider", http.StatusInternalServerError)
return
}

// Check if provider supports listing
if !provider.Capabilities().CanList {
if !s.provider.Capabilities().CanList {
http.Error(w, "Secrets provider does not support listing keys", http.StatusMethodNotAllowed)
return
}

secretDescriptions, err := provider.ListSecrets(r.Context())
secretDescriptions, err := s.provider.ListSecrets(r.Context())
if err != nil {
logger.Errorf("Failed to list secrets: %v", err)
http.Error(w, "Failed to list secrets", http.StatusInternalServerError)
Expand Down Expand Up @@ -343,34 +327,23 @@ func (s *SecretsRoutes) createSecret(w http.ResponseWriter, r *http.Request) {
return
}

provider, err := s.getSecretsManager()
if err != nil {
if errors.Is(err, secrets.ErrSecretsNotSetup) {
http.Error(w, "Secrets provider not setup", http.StatusNotFound)
return
}
logger.Errorf("Failed to get secrets manager: %v", err)
http.Error(w, "Failed to access secrets provider", http.StatusInternalServerError)
return
}

// Check if provider supports writing
if !provider.Capabilities().CanWrite {
if !s.provider.Capabilities().CanWrite {
http.Error(w, "Secrets provider does not support creating secrets", http.StatusMethodNotAllowed)
return
}

// Check if secret already exists (if provider supports reading)
if provider.Capabilities().CanRead {
_, err := provider.GetSecret(r.Context(), req.Key)
if s.provider.Capabilities().CanRead {
_, err := s.provider.GetSecret(r.Context(), req.Key)
if err == nil {
http.Error(w, "Secret already exists", http.StatusConflict)
return
}
}

// Create the secret
if err := provider.SetSecret(r.Context(), req.Key, req.Value); err != nil {
if err := s.provider.SetSecret(r.Context(), req.Key, req.Value); err != nil {
logger.Errorf("Failed to create secret: %v", err)
http.Error(w, "Failed to create secret", http.StatusInternalServerError)
return
Expand Down Expand Up @@ -423,34 +396,23 @@ func (s *SecretsRoutes) updateSecret(w http.ResponseWriter, r *http.Request) {
return
}

provider, err := s.getSecretsManager()
if err != nil {
if errors.Is(err, secrets.ErrSecretsNotSetup) {
http.Error(w, "Secrets provider not setup", http.StatusNotFound)
return
}
logger.Errorf("Failed to get secrets manager: %v", err)
http.Error(w, "Failed to access secrets provider", http.StatusInternalServerError)
return
}

// Check if provider supports writing
if !provider.Capabilities().CanWrite {
if !s.provider.Capabilities().CanWrite {
http.Error(w, "Secrets provider does not support updating secrets", http.StatusMethodNotAllowed)
return
}

// Check if secret exists (if provider supports reading)
if provider.Capabilities().CanRead {
_, err := provider.GetSecret(r.Context(), key)
if s.provider.Capabilities().CanRead {
_, err := s.provider.GetSecret(r.Context(), key)
if err != nil {
http.Error(w, "Secret not found", http.StatusNotFound)
return
}
}

// Update the secret
if err := provider.SetSecret(r.Context(), key, req.Value); err != nil {
if err := s.provider.SetSecret(r.Context(), key, req.Value); err != nil {
logger.Errorf("Failed to update secret: %v", err)
http.Error(w, "Failed to update secret", http.StatusInternalServerError)
return
Expand Down Expand Up @@ -486,25 +448,14 @@ func (s *SecretsRoutes) deleteSecret(w http.ResponseWriter, r *http.Request) {
return
}

provider, err := s.getSecretsManager()
if err != nil {
if errors.Is(err, secrets.ErrSecretsNotSetup) {
http.Error(w, "Secrets provider not setup", http.StatusNotFound)
return
}
logger.Errorf("Failed to get secrets manager: %v", err)
http.Error(w, "Failed to access secrets provider", http.StatusInternalServerError)
return
}

// Check if provider supports deletion
if !provider.Capabilities().CanDelete {
if !s.provider.Capabilities().CanDelete {
http.Error(w, "Secrets provider does not support deleting secrets", http.StatusMethodNotAllowed)
return
}

// Delete the secret
if err := provider.DeleteSecret(r.Context(), key); err != nil {
if err := s.provider.DeleteSecret(r.Context(), key); err != nil {
logger.Errorf("Failed to delete secret: %v", err)
// Check if it's a "not found" error
if err.Error() == "cannot delete non-existent secret: "+key {
Expand All @@ -518,23 +469,6 @@ func (s *SecretsRoutes) deleteSecret(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
}

// getSecretsManager is a helper function to get the secrets manager
func (s *SecretsRoutes) getSecretsManager() (secrets.Provider, error) {
cfg := s.configProvider.GetConfig()

// Check if secrets setup has been completed
if !cfg.Secrets.SetupCompleted {
return nil, secrets.ErrSecretsNotSetup
}

providerType, err := cfg.Secrets.GetProviderType()
if err != nil {
return nil, err
}

return secrets.CreateSecretProvider(providerType)
}

// Request and response type definitions

// setupSecretsRequest represents the request for initializing a secrets provider
Expand Down
Loading