From b82f42625f5ee80fad9701d1abd2e0b059aee46c Mon Sep 17 00:00:00 2001 From: Tolya Korniltsev Date: Fri, 29 Jul 2022 14:40:44 +0700 Subject: [PATCH] feat(api): Implement delete app functionality as an HTTP endpoint backend #1223 (#1239) --- go.mod | 2 +- go.sum | 2 - pkg/admin/admin_integration_test.go | 3 +- pkg/admin/client.go | 3 +- pkg/admin/controller.go | 52 +++------- pkg/admin/controller_test.go | 14 ++- pkg/admin/server.go | 12 ++- pkg/admin/service.go | 28 ------ pkg/cli/server.go | 5 +- pkg/server/apps.go | 78 +++++++++++++++ pkg/server/controller.go | 10 ++ pkg/server/controller_admin_test.go | 145 ++++++++++++++++++++++++++++ pkg/server/index.go | 6 +- pkg/storage/storage_labels.go | 11 +++ pkg/storage/types.go | 20 ++++ pkg/testing/config.go | 3 + 16 files changed, 305 insertions(+), 89 deletions(-) delete mode 100644 pkg/admin/service.go create mode 100644 pkg/server/apps.go create mode 100644 pkg/server/controller_admin_test.go diff --git a/go.mod b/go.mod index 82cd7d9708..6a790d6d6a 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/pyroscope-io/pyroscope -go 1.17 +go 1.18 require ( github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d diff --git a/go.sum b/go.sum index 06ee2b3c9a..e7638b339e 100644 --- a/go.sum +++ b/go.sum @@ -902,7 +902,6 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= @@ -1014,7 +1013,6 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/pkg/admin/admin_integration_test.go b/pkg/admin/admin_integration_test.go index 35ab936fb0..39d97580cd 100644 --- a/pkg/admin/admin_integration_test.go +++ b/pkg/admin/admin_integration_test.go @@ -40,8 +40,7 @@ var _ = Describe("integration", func() { httpServer, err := admin.NewUdsHTTPServer(socketAddr, http) Expect(err).ToNot(HaveOccurred()) - svc := admin.NewService(mockStorage{}) - ctrl := admin.NewController(logger, svc, mockUserService{}, mockStorageService{}) + ctrl := admin.NewController(logger, mockStorage{}, mockUserService{}, mockStorageService{}) s, err := admin.NewServer(logger, ctrl, httpServer) Expect(err).ToNot(HaveOccurred()) server = s diff --git a/pkg/admin/client.go b/pkg/admin/client.go index b73b5627a3..f731de6cc3 100644 --- a/pkg/admin/client.go +++ b/pkg/admin/client.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/pyroscope-io/pyroscope/pkg/server" "io" "net/http" "time" @@ -67,7 +68,7 @@ func (c *Client) GetAppsNames() (names AppNames, err error) { func (c *Client) DeleteApp(name string) (err error) { // we are kinda robbing here // since the server and client are defined in the same package - payload := DeleteAppInput{ + payload := server.DeleteAppInput{ Name: name, } diff --git a/pkg/admin/controller.go b/pkg/admin/controller.go index be3aad452e..361585c60e 100644 --- a/pkg/admin/controller.go +++ b/pkg/admin/controller.go @@ -6,6 +6,8 @@ import ( "context" "encoding/json" "fmt" + "github.com/pyroscope-io/pyroscope/pkg/server/httputils" + pstorage "github.com/pyroscope-io/pyroscope/pkg/storage" "net/http" "github.com/gorilla/mux" @@ -14,10 +16,16 @@ import ( "github.com/pyroscope-io/pyroscope/pkg/model" ) -type Controller struct { - log *logrus.Logger +type Storage interface { + pstorage.AppNameGetter + pstorage.AppGetter + pstorage.AppDeleter +} - adminService *AdminService +type Controller struct { + log *logrus.Logger + httpUtils httputils.Utils + storage Storage userService UserService storageService StorageService } @@ -32,52 +40,18 @@ type StorageService interface { func NewController( log *logrus.Logger, - adminService *AdminService, + storage Storage, userService UserService, storageService StorageService) *Controller { return &Controller{ log: log, - adminService: adminService, + storage: storage, userService: userService, storageService: storageService, } } -// HandleGetApps handles GET requests -func (ctrl *Controller) HandleGetApps(w http.ResponseWriter, _ *http.Request) { - appNames := ctrl.adminService.GetApps() - - w.WriteHeader(http.StatusOK) - ctrl.writeResponseJSON(w, appNames) -} - -type DeleteAppInput struct { - Name string `json:"name"` -} - -// HandleDeleteApp handles DELETE requests -func (ctrl *Controller) HandleDeleteApp(w http.ResponseWriter, r *http.Request) { - var payload DeleteAppInput - - err := json.NewDecoder(r.Body).Decode(&payload) - if err != nil { - ctrl.writeError(w, http.StatusBadRequest, err, "") - return - } - - err = ctrl.adminService.DeleteApp(payload.Name) - if err != nil { - // TODO how to distinguish - // it was a bad request - // or an internal server error - ctrl.writeError(w, http.StatusInternalServerError, err, "") - return - } - - w.WriteHeader(http.StatusOK) -} - type UpdateUserRequest struct { Password *string `json:"password"` IsDisabled *bool `json:"isDisabled"` diff --git a/pkg/admin/controller_test.go b/pkg/admin/controller_test.go index a3c6f114dd..adbc2d6968 100644 --- a/pkg/admin/controller_test.go +++ b/pkg/admin/controller_test.go @@ -5,6 +5,8 @@ import ( "context" "encoding/json" "fmt" + "github.com/pyroscope-io/pyroscope/pkg/server" + "github.com/pyroscope-io/pyroscope/pkg/storage" "io" "net/http" "net/http/httptest" @@ -19,6 +21,7 @@ import ( type mockStorage struct { getAppNamesResult []string + getAppsResult storage.GetAppsOutput deleteResult error } @@ -26,6 +29,10 @@ func (m mockStorage) GetAppNames(ctx context.Context) []string { return m.getAppNamesResult } +func (m mockStorage) GetApps(ctx context.Context) storage.GetAppsOutput { + return m.getAppsResult +} + func (m mockStorage) DeleteApp(ctx context.Context, appname string) error { return m.deleteResult } @@ -60,8 +67,7 @@ var _ = Describe("controller", func() { // create a null logger, since we aren't interested logger, _ := test.NewNullLogger() - svc := admin.NewService(storage) - ctrl := admin.NewController(logger, svc, mockUserService{}, mockStorageService{}) + ctrl := admin.NewController(logger, storage, mockUserService{}, mockStorageService{}) httpServer := &admin.UdsHTTPServer{} server, err := admin.NewServer(logger, ctrl, httpServer) @@ -94,7 +100,7 @@ var _ = Describe("controller", func() { It("returns StatusOK", func() { // we are kinda robbing here // since the server and client are defined in the same package - payload := admin.DeleteAppInput{Name: "myapp"} + payload := server.DeleteAppInput{Name: "myapp"} marshalledPayload, err := json.Marshal(payload) request, err := http.NewRequest(http.MethodDelete, "/v1/apps", bytes.NewBuffer(marshalledPayload)) Expect(err).ToNot(HaveOccurred()) @@ -125,7 +131,7 @@ var _ = Describe("controller", func() { It("returns InternalServerError", func() { // we are kinda robbing here // since the server and client are defined in the same package - payload := admin.DeleteAppInput{Name: "myapp"} + payload := server.DeleteAppInput{Name: "myapp"} marshalledPayload, err := json.Marshal(payload) request, err := http.NewRequest(http.MethodDelete, "/v1/apps", bytes.NewBuffer(marshalledPayload)) Expect(err).ToNot(HaveOccurred()) diff --git a/pkg/admin/server.go b/pkg/admin/server.go index 187ac51596..c74a64d91f 100644 --- a/pkg/admin/server.go +++ b/pkg/admin/server.go @@ -1,6 +1,8 @@ package admin import ( + "github.com/pyroscope-io/pyroscope/pkg/server" + "github.com/pyroscope-io/pyroscope/pkg/server/httputils" "net/http" "os" @@ -37,11 +39,13 @@ func NewServer(logger *logrus.Logger, ctrl *Controller, httpServer HTTPServer) ( as.Handler = r + httpUtils := httputils.NewDefaultHelper(logger) // Routes - r.HandleFunc("/v1/apps", as.ctrl.HandleGetApps).Methods("GET") - r.HandleFunc("/v1/apps", as.ctrl.HandleDeleteApp).Methods("DELETE") - r.HandleFunc("/v1/users/{username}", as.ctrl.UpdateUserHandler).Methods("PATCH") - r.HandleFunc("/v1/storage/cleanup", as.ctrl.StorageCleanupHandler).Methods("PUT") + + r.HandleFunc("/v1/apps", server.NewGetAppNamesHandler(ctrl.storage, httpUtils)).Methods("GET") + r.HandleFunc("/v1/apps", server.NewDeleteAppHandler(ctrl.storage, httpUtils)).Methods("DELETE") + r.HandleFunc("/v1/users/{username}", ctrl.UpdateUserHandler).Methods("PATCH") + r.HandleFunc("/v1/storage/cleanup", ctrl.StorageCleanupHandler).Methods("PUT") // Global middlewares r.Use(logginMiddleware) diff --git a/pkg/admin/service.go b/pkg/admin/service.go deleted file mode 100644 index ee2da1551d..0000000000 --- a/pkg/admin/service.go +++ /dev/null @@ -1,28 +0,0 @@ -package admin - -import "context" - -type AdminService struct { - storage Storage -} - -type Storage interface { - GetAppNames(context.Context) []string - DeleteApp(ctx context.Context, appname string) error -} - -func NewService(v Storage) *AdminService { - m := &AdminService{ - v, - } - - return m -} - -func (m *AdminService) GetApps() (appNames []string) { - return m.storage.GetAppNames(context.TODO()) -} - -func (m *AdminService) DeleteApp(appname string) error { - return m.storage.DeleteApp(context.TODO(), appname) -} diff --git a/pkg/cli/server.go b/pkg/cli/server.go index 3803303109..b9208fa9da 100644 --- a/pkg/cli/server.go +++ b/pkg/cli/server.go @@ -119,9 +119,8 @@ func newServerService(c *config.Server) (*serverService, error) { // this needs to happen after storage is initiated! if svc.config.EnableExperimentalAdmin { socketPath := svc.config.AdminSocketPath - adminService := admin.NewService(svc.storage) userService := service.NewUserService(svc.database.DB()) - adminCtrl := admin.NewController(svc.logger, adminService, userService, svc.storage) + adminController := admin.NewController(svc.logger, svc.storage, userService, svc.storage) httpClient, err := admin.NewHTTPOverUDSClient(socketPath) if err != nil { return nil, fmt.Errorf("admin: %w", err) @@ -134,7 +133,7 @@ func newServerService(c *config.Server) (*serverService, error) { svc.adminServer, err = admin.NewServer( svc.logger, - adminCtrl, + adminController, adminHTTPOverUDS, ) if err != nil { diff --git a/pkg/server/apps.go b/pkg/server/apps.go new file mode 100644 index 0000000000..131f0104c4 --- /dev/null +++ b/pkg/server/apps.go @@ -0,0 +1,78 @@ +package server + +import ( + "encoding/json" + "github.com/pyroscope-io/pyroscope/pkg/server/httputils" + "github.com/pyroscope-io/pyroscope/pkg/storage" + "net/http" +) + +type AppInfo struct { + Name string `json:"name"` +} + +type DeleteAppInput struct { + Name string `json:"name"` +} + +func (c *Controller) getAppsHandler() http.HandlerFunc { + return NewGetAppsHandler(c.storage, c.httpUtils) +} + +func NewGetAppsHandler(s storage.AppGetter, httpUtils httputils.Utils) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + apps, err := s.GetApps(r.Context()) + if err != nil { + httpUtils.WriteError(r, w, http.StatusInternalServerError, err, "") + return + } + res := make([]AppInfo, 0, len(apps.Apps)) + for _, app := range apps.Apps { + it := AppInfo{ + Name: app.Name, + } + res = append(res, it) + } + w.WriteHeader(http.StatusOK) + httpUtils.WriteResponseJSON(r, w, res) + } +} + +func (c *Controller) getAppNames() http.HandlerFunc { + return NewGetAppNamesHandler(c.storage, c.httpUtils) +} + +func NewGetAppNamesHandler(s storage.AppNameGetter, httpUtils httputils.Utils) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + appNames := s.GetAppNames(r.Context()) + w.WriteHeader(http.StatusOK) + httpUtils.WriteResponseJSON(r, w, appNames) + } +} + +func (c *Controller) deleteAppsHandler() http.HandlerFunc { + return NewDeleteAppHandler(c.storage, c.httpUtils) +} + +func NewDeleteAppHandler(s storage.AppDeleter, httpUtils httputils.Utils) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var payload DeleteAppInput + + err := json.NewDecoder(r.Body).Decode(&payload) + if err != nil { + httpUtils.WriteError(r, w, http.StatusBadRequest, err, "") + return + } + + err = s.DeleteApp(r.Context(), payload.Name) + if err != nil { + // TODO how to distinguish + // it was a bad request + // or an internal server error + httpUtils.WriteError(r, w, http.StatusInternalServerError, err, "") + return + } + + w.WriteHeader(http.StatusOK) + } +} diff --git a/pkg/server/controller.go b/pkg/server/controller.go index 918d5c7c23..0712afcd48 100644 --- a/pkg/server/controller.go +++ b/pkg/server/controller.go @@ -28,6 +28,7 @@ import ( "gorm.io/gorm" adhocserver "github.com/pyroscope-io/pyroscope/pkg/adhoc/server" + "github.com/pyroscope-io/pyroscope/pkg/api" "github.com/pyroscope-io/pyroscope/pkg/api/authz" "github.com/pyroscope-io/pyroscope/pkg/api/router" @@ -280,6 +281,15 @@ func (ctrl *Controller) serverMux() (http.Handler, error) { ctrl.drainMiddleware, ctrl.authMiddleware(nil)) + appsRouter := apiRouter.PathPrefix("/").Subrouter() + appsRouter.Use( + api.AuthMiddleware(nil, ctrl.authService, ctrl.httpUtils), + authz.NewAuthorizer(ctrl.log, httputils.NewDefaultHelper(ctrl.log)).RequireOneOf( + authz.Role(model.AdminRole), + )) + appsRouter.HandleFunc("/apps", ctrl.getAppsHandler()).Methods("GET") + appsRouter.HandleFunc("/apps", ctrl.deleteAppsHandler()).Methods("DELETE") + // TODO(kolesnikovae): // Refactor: move mux part to pkg/api/router. // Make prometheus middleware to support gorilla patterns. diff --git a/pkg/server/controller_admin_test.go b/pkg/server/controller_admin_test.go new file mode 100644 index 0000000000..7af9ed603b --- /dev/null +++ b/pkg/server/controller_admin_test.go @@ -0,0 +1,145 @@ +package server + +import ( + "bytes" + "context" + "encoding/json" + + "github.com/pyroscope-io/pyroscope/pkg/model" + "github.com/pyroscope-io/pyroscope/pkg/service" + "github.com/pyroscope-io/pyroscope/pkg/sqlstore" + "net/http" + "net/url" + "strconv" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/prometheus/client_golang/prometheus" + "github.com/sirupsen/logrus" + + "github.com/pyroscope-io/pyroscope/pkg/config" + "github.com/pyroscope-io/pyroscope/pkg/exporter" + "github.com/pyroscope-io/pyroscope/pkg/health" + "github.com/pyroscope-io/pyroscope/pkg/parser" + "github.com/pyroscope-io/pyroscope/pkg/storage" + "github.com/pyroscope-io/pyroscope/pkg/testing" +) + +var _ = Describe("server", func() { + type testServices struct { + s *storage.Storage + k service.APIKeyService + } + ingest := func(lines string, app string) { + u, _ := url.Parse("http://localhost:4040/ingest") + q := u.Query() + q.Add("name", app) + q.Add("from", strconv.Itoa(int(testing.ParseTime("2020-01-01-01:01:00").Unix()))) + q.Add("until", strconv.Itoa(int(testing.ParseTime("2020-01-01-01:01:10").Unix()))) + q.Add("format", "lines") + u.RawQuery = q.Encode() + req, err := http.NewRequest("POST", u.String(), bytes.NewBuffer([]byte(lines))) + Expect(err).ToNot(HaveOccurred()) + req.Header.Set("Content-Type", "text/plain") + res, err := http.DefaultClient.Do(req) + Expect(err).ToNot(HaveOccurred()) + Expect(res.StatusCode).To(Equal(200)) + } + deleteApp := func(app string, key string) *http.Response { + input := struct { + Name string `json:"name"` + }{app} + body, _ := json.Marshal(input) + req, err := http.NewRequest("DELETE", "http://localhost:4040/api/apps", bytes.NewBuffer(body)) + if key != "" { + req.Header.Set("Authorization", "Bearer "+key) + } + Expect(err).ToNot(HaveOccurred()) + res, err := http.DefaultClient.Do(req) + Expect(err).ToNot(HaveOccurred()) + return res + } + runServer := func(cfg **config.Config, cb func(s testServices)) { + defer GinkgoRecover() + s, err := storage.New(storage.NewConfig(&(*cfg).Server), logrus.StandardLogger(), prometheus.NewRegistry(), new(health.Controller)) + Expect(err).ToNot(HaveOccurred()) + e, _ := exporter.NewExporter(nil, nil) + sql, err := sqlstore.Open(&(*cfg).Server) + Expect(err).ToNot(HaveOccurred()) + + l := logrus.New() + + c, _ := New(Config{ + Configuration: &(*cfg).Server, + Storage: s, + Ingester: parser.New(logrus.StandardLogger(), s, e), + Logger: l, + MetricsRegisterer: prometheus.NewRegistry(), + ExportedMetricsRegistry: prometheus.NewRegistry(), + Notifier: mockNotifier{}, + Adhoc: mockAdhocServer{}, + DB: sql.DB(), + }) + + go c.Start() + defer c.Stop() + + // TODO: Wait for start .There's possibly a better way of doing this + time.Sleep(50 * time.Millisecond) + k := service.NewAPIKeyService(sql.DB()) + cb(testServices{s, k}) + } + createToken := func(s testServices, role model.Role) string { + params := model.CreateAPIKeyParams{Name: "t" + strconv.Itoa(int(role)), Role: role} + _, key, err := s.k.CreateAPIKey(context.TODO(), params) + Expect(err).ToNot(HaveOccurred()) + return key + } + checkRoleAccess := func(s testServices, role model.Role, expectAccess bool, expectStatus int) { + ingest("foo;bar\nfoo;bar\nfoo;baz\nfoo;baz\nfoo;baz\n", "test.app1") + ingest("foo;bar\nfoo;bar\nfoo;baz\nfoo;baz\nfoo;baz\n", "test.app2") + time.Sleep(100 * time.Millisecond) + Expect(s.s.GetAppNames(context.TODO())).To(Equal([]string{"test.app1", "test.app2"})) + key := "" + if role != model.InvalidRole { + key = createToken(s, role) + } + res := deleteApp("test.app2", key) + Expect(res.StatusCode).To(Equal(expectStatus)) + if expectAccess { + Expect(s.s.GetAppNames(context.TODO())).To(Equal([]string{"test.app1"})) + } else { + Expect(s.s.GetAppNames(context.TODO())).To(Equal([]string{"test.app1", "test.app2"})) + } + } + + testing.WithConfig(func(cfg **config.Config) { + Describe("http api admin controller", func() { + Context("with Auth enabled", func() { + It("should be accessible to only admin token", func() { + (*cfg).Server.Auth.Internal.Enabled = true + (*cfg).Server.EnableExperimentalAdmin = true + runServer(cfg, func(s testServices) { + checkRoleAccess(s, model.AdminRole, true, http.StatusOK) + checkRoleAccess(s, model.AgentRole, false, http.StatusForbidden) + checkRoleAccess(s, model.ReadOnlyRole, false, http.StatusForbidden) + checkRoleAccess(s, model.InvalidRole, false, http.StatusUnauthorized) + }) + }) + }) + Context("with Auth disabled", func() { + It("should be accessible to only admin token", func() { + (*cfg).Server.Auth.Internal.Enabled = false + (*cfg).Server.EnableExperimentalAdmin = true + runServer(cfg, func(s testServices) { + checkRoleAccess(s, model.AdminRole, true, http.StatusOK) + checkRoleAccess(s, model.AgentRole, false, http.StatusForbidden) + checkRoleAccess(s, model.ReadOnlyRole, false, http.StatusForbidden) + checkRoleAccess(s, model.InvalidRole, false, http.StatusUnauthorized) + }) + }) + }) + }) + }) +}) diff --git a/pkg/server/index.go b/pkg/server/index.go index 9864f38584..2675353f5c 100644 --- a/pkg/server/index.go +++ b/pkg/server/index.go @@ -10,7 +10,6 @@ import ( "github.com/pyroscope-io/pyroscope/pkg/build" "github.com/pyroscope-io/pyroscope/pkg/server/httputils" - "github.com/pyroscope-io/pyroscope/pkg/storage" "github.com/pyroscope-io/pyroscope/pkg/util/updates" ) @@ -32,7 +31,6 @@ type IndexHandlerConfig struct { type IndexHandler struct { log *logrus.Logger - storage storage.AppNameGetter dir http.FileSystem fs http.Handler stats StatsReceiver @@ -55,13 +53,12 @@ func (ctrl *Controller) indexHandler() http.HandlerFunc { IsAuthRequired: ctrl.isAuthRequired(), BaseURL: ctrl.config.BaseURL, } - return NewIndexHandler(ctrl.log, ctrl.storage, ctrl.dir, ctrl, ctrl.notifier, cfg, ctrl.httpUtils).ServeHTTP + return NewIndexHandler(ctrl.log, ctrl.dir, ctrl, ctrl.notifier, cfg, ctrl.httpUtils).ServeHTTP } //revive:disable:argument-limit TODO(petethepig): we will refactor this later func NewIndexHandler( log *logrus.Logger, - s storage.AppNameGetter, dir http.FileSystem, stats StatsReceiver, notifier Notifier, @@ -71,7 +68,6 @@ func NewIndexHandler( fs := http.FileServer(dir) return &IndexHandler{ log: log, - storage: s, dir: dir, fs: fs, stats: stats, diff --git a/pkg/storage/storage_labels.go b/pkg/storage/storage_labels.go index fcd9140d15..49d95aeb0c 100644 --- a/pkg/storage/storage_labels.go +++ b/pkg/storage/storage_labels.go @@ -98,3 +98,14 @@ func (s *Storage) GetAppNames(ctx context.Context) []string { return appNames } + +func (s *Storage) GetApps(ctx context.Context) (GetAppsOutput, error) { + r := GetAppsOutput{} + for _, appName := range s.GetAppNames(ctx) { + a := AppInfo{ + Name: appName, + } + r.Apps = append(r.Apps, a) + } + return r, nil +} diff --git a/pkg/storage/types.go b/pkg/storage/types.go index c49b537722..4d726ad79a 100644 --- a/pkg/storage/types.go +++ b/pkg/storage/types.go @@ -53,6 +53,18 @@ type GetLabelValuesByQueryOutput struct { Values []string } +type AppInfo struct { + Name string +} + +type GetAppsOutput struct { + Apps []AppInfo +} + +type DeleteAppInput struct { + Name string +} + type LabelValuesGetter interface { GetValues(ctx context.Context, key string, cb func(v string) bool) GetValuesByQuery(ctx context.Context, in GetLabelValuesByQueryInput) (GetLabelValuesByQueryOutput, error) @@ -62,6 +74,14 @@ type AppNameGetter interface { GetAppNames(ctx context.Context) []string } +type AppGetter interface { + GetApps(ctx context.Context) (GetAppsOutput, error) +} + +type AppDeleter interface { + DeleteApp(ctx context.Context, appName string) error +} + // Other functions from storage.Storage: // type Backend interface { // Put(ctx context.Context, pi *PutInput) error diff --git a/pkg/testing/config.go b/pkg/testing/config.go index 9e71fab258..6f5086acd4 100644 --- a/pkg/testing/config.go +++ b/pkg/testing/config.go @@ -21,6 +21,9 @@ func WithConfig(cb func(cfg **config.Config)) { MaxNodesSerialization: 2048, MaxNodesRender: 2048, + Database: config.Database{ + Type: "sqlite3", + }, }, } })