Skip to content

Commit a384737

Browse files
authored
Support running and managing remote server through APIs (#1693)
* support remote server through API * refactored secret provider func * generateed task docs * added tests * refactored * fixed unit tests and docs * fix swagger generated path issue * default trasnport for remote servers changed to streamable-http
1 parent d477b72 commit a384737

File tree

11 files changed

+1101
-367
lines changed

11 files changed

+1101
-367
lines changed

docs/server/docs.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/server/swagger.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/server/swagger.yaml

Lines changed: 61 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/api/server.go

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@ import (
2929
v1 "github.com/stacklok/toolhive/pkg/api/v1"
3030
"github.com/stacklok/toolhive/pkg/auth"
3131
"github.com/stacklok/toolhive/pkg/client"
32+
"github.com/stacklok/toolhive/pkg/config"
3233
"github.com/stacklok/toolhive/pkg/container"
3334
"github.com/stacklok/toolhive/pkg/container/runtime"
3435
"github.com/stacklok/toolhive/pkg/groups"
3536
"github.com/stacklok/toolhive/pkg/logger"
37+
"github.com/stacklok/toolhive/pkg/secrets"
3638
"github.com/stacklok/toolhive/pkg/updates"
3739
"github.com/stacklok/toolhive/pkg/workloads"
3840
)
@@ -57,6 +59,7 @@ type ServerBuilder struct {
5759
clientManager client.Manager
5860
workloadManager workloads.Manager
5961
groupManager groups.Manager
62+
secretsProvider secrets.Provider
6063
}
6164

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

139+
// WithSecretsProvider sets the secrets provider
140+
func (b *ServerBuilder) WithSecretsProvider(provider secrets.Provider) *ServerBuilder {
141+
b.secretsProvider = provider
142+
return b
143+
}
144+
136145
// Build creates and configures the HTTP router
137146
func (b *ServerBuilder) Build(ctx context.Context) (*chi.Mux, error) {
138147
r := chi.NewRouter()
@@ -207,20 +216,49 @@ func (b *ServerBuilder) createDefaultManagers(ctx context.Context) error {
207216
return fmt.Errorf("failed to create group manager: %v", err)
208217
}
209218
}
219+
if b.secretsProvider == nil {
220+
b.secretsProvider, err = getSecretsManager()
221+
if err != nil {
222+
return fmt.Errorf("failed to create secrets provider: %v", err)
223+
}
224+
}
210225

211226
return nil
212227
}
213228

229+
// getSecretsManager is a helper function to get the secrets manager
230+
func getSecretsManager() (secrets.Provider, error) {
231+
cfg := config.NewDefaultProvider().GetConfig()
232+
233+
// Check if secrets setup has been completed
234+
if !cfg.Secrets.SetupCompleted {
235+
return nil, secrets.ErrSecretsNotSetup
236+
}
237+
238+
providerType, err := cfg.Secrets.GetProviderType()
239+
if err != nil {
240+
return nil, err
241+
}
242+
243+
return secrets.CreateSecretProvider(providerType)
244+
}
245+
214246
// setupDefaultRoutes sets up the default API routes
215247
func (b *ServerBuilder) setupDefaultRoutes(r *chi.Mux) {
216248
routers := map[string]http.Handler{
217-
"/health": v1.HealthcheckRouter(b.containerRuntime),
218-
"/api/v1beta/version": v1.VersionRouter(),
219-
"/api/v1beta/workloads": v1.WorkloadRouter(b.workloadManager, b.containerRuntime, b.groupManager, b.debugMode),
249+
"/health": v1.HealthcheckRouter(b.containerRuntime),
250+
"/api/v1beta/version": v1.VersionRouter(),
251+
"/api/v1beta/workloads": v1.WorkloadRouter(
252+
b.workloadManager,
253+
b.containerRuntime,
254+
b.groupManager,
255+
b.secretsProvider,
256+
b.debugMode,
257+
),
220258
"/api/v1beta/registry": v1.RegistryRouter(),
221259
"/api/v1beta/discovery": v1.DiscoveryRouter(),
222260
"/api/v1beta/clients": v1.ClientRouter(b.clientManager, b.workloadManager, b.groupManager),
223-
"/api/v1beta/secrets": v1.SecretsRouter(),
261+
"/api/v1beta/secrets": v1.SecretsRouter(b.secretsProvider),
224262
"/api/v1beta/groups": v1.GroupsRouter(b.groupManager, b.workloadManager, b.clientManager),
225263
}
226264

pkg/api/v1/secrets.go

Lines changed: 18 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@ const (
2222
// SecretsRoutes defines the routes for the secrets API.
2323
type SecretsRoutes struct {
2424
configProvider config.Provider
25+
provider secrets.Provider
2526
}
2627

2728
// NewSecretsRoutes creates a new SecretsRoutes with the default config provider
28-
func NewSecretsRoutes() *SecretsRoutes {
29+
func NewSecretsRoutes(provider secrets.Provider) *SecretsRoutes {
2930
return &SecretsRoutes{
3031
configProvider: config.NewDefaultProvider(),
32+
provider: provider,
3133
}
3234
}
3335

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

4143
// SecretsRouter creates a new router for the secrets API.
42-
func SecretsRouter() http.Handler {
43-
routes := NewSecretsRoutes()
44+
func SecretsRouter(provider secrets.Provider) http.Handler {
45+
routes := NewSecretsRoutes(provider)
4446
return secretsRouterWithRoutes(routes)
4547
}
4648

@@ -233,15 +235,7 @@ func (s *SecretsRoutes) getSecretsProvider(w http.ResponseWriter, _ *http.Reques
233235
return
234236
}
235237

236-
// Get provider capabilities
237-
provider, err := s.getSecretsManager()
238-
if err != nil {
239-
logger.Errorf("Failed to create secrets provider: %v", err)
240-
http.Error(w, "Failed to access secrets provider", http.StatusInternalServerError)
241-
return
242-
}
243-
244-
capabilities := provider.Capabilities()
238+
capabilities := s.provider.Capabilities()
245239

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

288272
// Check if provider supports listing
289-
if !provider.Capabilities().CanList {
273+
if !s.provider.Capabilities().CanList {
290274
http.Error(w, "Secrets provider does not support listing keys", http.StatusMethodNotAllowed)
291275
return
292276
}
293277

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

346-
provider, err := s.getSecretsManager()
347-
if err != nil {
348-
if errors.Is(err, secrets.ErrSecretsNotSetup) {
349-
http.Error(w, "Secrets provider not setup", http.StatusNotFound)
350-
return
351-
}
352-
logger.Errorf("Failed to get secrets manager: %v", err)
353-
http.Error(w, "Failed to access secrets provider", http.StatusInternalServerError)
354-
return
355-
}
356-
357330
// Check if provider supports writing
358-
if !provider.Capabilities().CanWrite {
331+
if !s.provider.Capabilities().CanWrite {
359332
http.Error(w, "Secrets provider does not support creating secrets", http.StatusMethodNotAllowed)
360333
return
361334
}
362335

363336
// Check if secret already exists (if provider supports reading)
364-
if provider.Capabilities().CanRead {
365-
_, err := provider.GetSecret(r.Context(), req.Key)
337+
if s.provider.Capabilities().CanRead {
338+
_, err := s.provider.GetSecret(r.Context(), req.Key)
366339
if err == nil {
367340
http.Error(w, "Secret already exists", http.StatusConflict)
368341
return
369342
}
370343
}
371344

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

426-
provider, err := s.getSecretsManager()
427-
if err != nil {
428-
if errors.Is(err, secrets.ErrSecretsNotSetup) {
429-
http.Error(w, "Secrets provider not setup", http.StatusNotFound)
430-
return
431-
}
432-
logger.Errorf("Failed to get secrets manager: %v", err)
433-
http.Error(w, "Failed to access secrets provider", http.StatusInternalServerError)
434-
return
435-
}
436-
437399
// Check if provider supports writing
438-
if !provider.Capabilities().CanWrite {
400+
if !s.provider.Capabilities().CanWrite {
439401
http.Error(w, "Secrets provider does not support updating secrets", http.StatusMethodNotAllowed)
440402
return
441403
}
442404

443405
// Check if secret exists (if provider supports reading)
444-
if provider.Capabilities().CanRead {
445-
_, err := provider.GetSecret(r.Context(), key)
406+
if s.provider.Capabilities().CanRead {
407+
_, err := s.provider.GetSecret(r.Context(), key)
446408
if err != nil {
447409
http.Error(w, "Secret not found", http.StatusNotFound)
448410
return
449411
}
450412
}
451413

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

489-
provider, err := s.getSecretsManager()
490-
if err != nil {
491-
if errors.Is(err, secrets.ErrSecretsNotSetup) {
492-
http.Error(w, "Secrets provider not setup", http.StatusNotFound)
493-
return
494-
}
495-
logger.Errorf("Failed to get secrets manager: %v", err)
496-
http.Error(w, "Failed to access secrets provider", http.StatusInternalServerError)
497-
return
498-
}
499-
500451
// Check if provider supports deletion
501-
if !provider.Capabilities().CanDelete {
452+
if !s.provider.Capabilities().CanDelete {
502453
http.Error(w, "Secrets provider does not support deleting secrets", http.StatusMethodNotAllowed)
503454
return
504455
}
505456

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

521-
// getSecretsManager is a helper function to get the secrets manager
522-
func (s *SecretsRoutes) getSecretsManager() (secrets.Provider, error) {
523-
cfg := s.configProvider.GetConfig()
524-
525-
// Check if secrets setup has been completed
526-
if !cfg.Secrets.SetupCompleted {
527-
return nil, secrets.ErrSecretsNotSetup
528-
}
529-
530-
providerType, err := cfg.Secrets.GetProviderType()
531-
if err != nil {
532-
return nil, err
533-
}
534-
535-
return secrets.CreateSecretProvider(providerType)
536-
}
537-
538472
// Request and response type definitions
539473

540474
// setupSecretsRequest represents the request for initializing a secrets provider

0 commit comments

Comments
 (0)