Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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.

57 changes: 57 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.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
104 changes: 20 additions & 84 deletions pkg/api/v1/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@ const (
)

// SecretsRoutes defines the routes for the secrets API.
type SecretsRoutes struct{}
type SecretsRoutes struct {
provider secrets.Provider
}

// SecretsRouter creates a new router for the secrets API.
func SecretsRouter() http.Handler {
routes := SecretsRoutes{}
func SecretsRouter(secretsProvider secrets.Provider) http.Handler {
routes := SecretsRoutes{
provider: secretsProvider,
}

r := chi.NewRouter()

Expand Down Expand Up @@ -214,15 +218,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 @@ -255,24 +251,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 @@ -324,34 +310,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 @@ -404,34 +379,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 @@ -467,25 +431,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 @@ -499,23 +452,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 (*SecretsRoutes) getSecretsManager() (secrets.Provider, error) {
cfg := config.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
Loading