package dashboard import ( "github.com/gin-gonic/gin" "github.com/komodorio/helm-dashboard/pkg/dashboard/handlers" "github.com/komodorio/helm-dashboard/pkg/dashboard/objects" log "github.com/sirupsen/logrus" "gotest.tools/v3/assert" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/cli" kubefake "helm.sh/helm/v3/pkg/kube/fake" "helm.sh/helm/v3/pkg/registry" "helm.sh/helm/v3/pkg/storage" "helm.sh/helm/v3/pkg/storage/driver" "io/ioutil" "net/http" "net/http/httptest" "net/url" "os" "path/filepath" "strings" "testing" ) var inMemStorage *storage.Storage var repoFile string func TestMain(m *testing.M) { // fixture to set logging level via env variable if os.Getenv("DEBUG") != "" { log.SetLevel(log.DebugLevel) log.Debugf("Set logging level") } inMemStorage = storage.Init(driver.NewMemory()) d, err := ioutil.TempDir("", "helm") if err != nil { panic(err) } repoFile = filepath.Join(d, "repositories.yaml") m.Run() inMemStorage = nil repoFile = "" } func GetTestGinContext(w *httptest.ResponseRecorder) *gin.Context { gin.SetMode(gin.TestMode) ctx, _ := gin.CreateTestContext(w) ctx.Request = &http.Request{ Header: make(http.Header), } return ctx } func TestNoCacheMiddleware(t *testing.T) { w := httptest.NewRecorder() con := GetTestGinContext(w) noCache(con) assert.Equal(t, w.Header().Get("Cache-Control"), "no-cache") } func TestEnableCacheControl(t *testing.T) { w := httptest.NewRecorder() con := GetTestGinContext(w) // Sets deafault policy to `no-cache` noCache(con) h := handlers.HelmHandler{ Contexted: &handlers.Contexted{ Data: &objects.DataLayer{}, }, } h.EnableClientCache(con) assert.Equal(t, w.Header().Get("Cache-Control"), "max-age=43200") } func TestConfigureStatic(t *testing.T) { w := httptest.NewRecorder() req, err := http.NewRequest("GET", "/", nil) if err != nil { t.Fatal(err) } // Create an API Engine api := gin.Default() // Configure static routes configureStatic(api) // Start the server api.ServeHTTP(w, req) assert.Equal(t, w.Code, http.StatusOK) } func TestConfigureRoutes(t *testing.T) { w := httptest.NewRecorder() req, err := http.NewRequest("GET", "/status", nil) if err != nil { t.Fatal(err) } // Create a API Engine api := gin.Default() // Required arguements for route configuration abortWeb := func() {} data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig) if err != nil { t.Fatal(err) } // Configure routes to API engine configureRoutes(abortWeb, data, api) // Start the server api.ServeHTTP(w, req) assert.Equal(t, w.Code, http.StatusOK) } func TestContextSetter(t *testing.T) { w := httptest.NewRecorder() con := GetTestGinContext(w) // Required arguements data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig) if err != nil { t.Fatal(err) } // Set the context ctxHandler := contextSetter(data) ctxHandler(con) appName, exists := con.Get("app") if !exists { t.Fatal("Value app doesn't exist in context") } tmp := handlers.Contexted{Data: data} assert.Equal(t, appName, tmp.GetApp(con)) } func TestNewRouter(t *testing.T) { w := httptest.NewRecorder() req, err := http.NewRequest("GET", "/status", nil) if err != nil { t.Fatal(err) } // Required arguemnets abortWeb := func() {} data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig) if err != nil { t.Fatal(err) } // Create a new router with the function newRouter := NewRouter(abortWeb, data, false) newRouter.ServeHTTP(w, req) assert.Equal(t, w.Code, http.StatusOK) } func TestConfigureScanners(t *testing.T) { w := httptest.NewRecorder() req, err := http.NewRequest("GET", "/api/scanners", nil) if err != nil { t.Fatal(err) } // Required arguemnets data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig) if err != nil { t.Fatal(err) } apiEngine := gin.Default() configureScanners(apiEngine.Group("/api/scanners"), data) apiEngine.ServeHTTP(w, req) assert.Equal(t, w.Code, http.StatusOK) } func TestConfigureKubectls(t *testing.T) { w := httptest.NewRecorder() req, err := http.NewRequest("GET", "/api/kube/contexts", nil) if err != nil { t.Fatal(err) } // Required arguemnets data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig) if err != nil { t.Fatal(err) } apiEngine := gin.Default() // Required middleware for kubectl api configuration apiEngine.Use(contextSetter(data)) configureKubectls(apiEngine.Group("/api/kube"), data) apiEngine.ServeHTTP(w, req) assert.Equal(t, w.Code, http.StatusOK) } func TestE2E(t *testing.T) { // Initialize data layer data, err := objects.NewDataLayer([]string{""}, "0.0.0-test", getFakeHelmConfig) assert.NilError(t, err) // Create a new router with the function abortWeb := func() {} newRouter := NewRouter(abortWeb, data, false) // initially, we don't have any releases w := httptest.NewRecorder() req, err := http.NewRequest("GET", "/api/helm/releases", nil) assert.NilError(t, err) newRouter.ServeHTTP(w, req) assert.Equal(t, w.Code, http.StatusOK) assert.Equal(t, w.Body.String(), "[]") // initially, we don't have any repositories w = httptest.NewRecorder() req, err = http.NewRequest("GET", "/api/helm/repositories", nil) assert.NilError(t, err) newRouter.ServeHTTP(w, req) assert.Equal(t, w.Code, http.StatusOK) assert.Equal(t, w.Body.String(), "[]") // then we add one repository w = httptest.NewRecorder() form := url.Values{} form.Add("name", "komodorio") form.Add("url", "https://helm-charts.komodor.io") req, err = http.NewRequest("POST", "/api/helm/repositories", strings.NewReader(form.Encode())) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") assert.NilError(t, err) newRouter.ServeHTTP(w, req) assert.Equal(t, w.Code, http.StatusNoContent) assert.Equal(t, w.Body.String(), "") // now, we have one repo w = httptest.NewRecorder() req, err = http.NewRequest("GET", "/api/helm/repositories", nil) assert.NilError(t, err) newRouter.ServeHTTP(w, req) assert.Equal(t, w.Code, http.StatusOK) assert.Equal(t, w.Body.String(), `[ { "name": "komodorio", "url": "https://helm-charts.komodor.io" } ]`) // what's the latest version of that chart w = httptest.NewRecorder() req, err = http.NewRequest("GET", "/api/helm/repositories/latestver?name=helm-dashboard", nil) assert.NilError(t, err) newRouter.ServeHTTP(w, req) assert.Equal(t, w.Code, http.StatusOK) // generate template for potential release w = httptest.NewRecorder() form = url.Values{} form.Add("preview", "true") form.Add("name", "release1") form.Add("chart", "komodorio/helm-dashboard") req, err = http.NewRequest("POST", "/api/helm/releases/test1", strings.NewReader(form.Encode())) assert.NilError(t, err) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") newRouter.ServeHTTP(w, req) assert.Equal(t, w.Code, http.StatusOK) // install the release w = httptest.NewRecorder() form = url.Values{} form.Add("name", "release1") form.Add("chart", "komodorio/helm-dashboard") req, err = http.NewRequest("POST", "/api/helm/releases/test1", strings.NewReader(form.Encode())) assert.NilError(t, err) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") newRouter.ServeHTTP(w, req) assert.Equal(t, w.Code, http.StatusAccepted) // get list of releases w = httptest.NewRecorder() req, err = http.NewRequest("GET", "/api/helm/releases", nil) assert.NilError(t, err) newRouter.ServeHTTP(w, req) assert.Equal(t, w.Code, http.StatusOK) t.Logf("Release: %s", w.Body.String()) //assert.Equal(t, w.Body.String(), "[]") // upgrade/reconfigure release w = httptest.NewRecorder() form = url.Values{} form.Add("chart", "komodorio/helm-dashboard") form.Add("values", "dashboard:\n allowWriteActions: true\n") req, err = http.NewRequest("POST", "/api/helm/releases/test1/release1", strings.NewReader(form.Encode())) assert.NilError(t, err) newRouter.ServeHTTP(w, req) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") assert.Equal(t, w.Code, http.StatusAccepted) // get history of revisions for release w = httptest.NewRecorder() req, err = http.NewRequest("GET", "/api/helm/releases/test1/release1/history", nil) assert.NilError(t, err) newRouter.ServeHTTP(w, req) assert.Equal(t, w.Code, http.StatusOK) t.Logf("Revs: %s", w.Body.String()) //assert.Equal(t, w.Body.String(), "[]") // get values for revision w = httptest.NewRecorder() req, err = http.NewRequest("GET", "/api/helm/releases/test1/release1/values?revision=2&userDefined=true", nil) assert.NilError(t, err) newRouter.ServeHTTP(w, req) assert.Equal(t, w.Code, http.StatusOK) //assert.Equal(t, w.Body.String(), "[]") // rollback w = httptest.NewRecorder() form = url.Values{} form.Add("revision", "1") req, err = http.NewRequest("POST", "/api/helm/releases/test1/release1/rollback", strings.NewReader(form.Encode())) assert.NilError(t, err) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") newRouter.ServeHTTP(w, req) assert.Equal(t, w.Code, http.StatusAccepted) // get manifest diff for release w = httptest.NewRecorder() req, err = http.NewRequest("GET", "/api/helm/releases/test1/release1/manifests?revision=1&revisionDiff=2", nil) assert.NilError(t, err) newRouter.ServeHTTP(w, req) assert.Equal(t, w.Code, http.StatusOK) //assert.Equal(t, w.Body.String(), "[]") // delete repo w = httptest.NewRecorder() req, err = http.NewRequest("DELETE", "/api/helm/repositories/komodorio", nil) assert.NilError(t, err) newRouter.ServeHTTP(w, req) assert.Equal(t, w.Code, http.StatusNoContent) // reconfigure release without repo connection w = httptest.NewRecorder() form = url.Values{} form.Add("chart", "komodorio/helm-dashboard") form.Add("values", "dashboard:\n allowWriteActions: false\n") req, err = http.NewRequest("POST", "/api/helm/releases/test1/release1", strings.NewReader(form.Encode())) assert.NilError(t, err) newRouter.ServeHTTP(w, req) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") assert.Equal(t, w.Code, http.StatusAccepted) t.Logf("Upgraded: %s", w.Body.String()) // uninstall w = httptest.NewRecorder() req, err = http.NewRequest("DELETE", "/api/helm/releases/test1/release1", nil) assert.NilError(t, err) newRouter.ServeHTTP(w, req) assert.Equal(t, w.Code, http.StatusAccepted) // check we don't have releases again w = httptest.NewRecorder() req, err = http.NewRequest("GET", "/api/helm/releases", nil) assert.NilError(t, err) newRouter.ServeHTTP(w, req) assert.Equal(t, w.Code, http.StatusOK) assert.Equal(t, w.Body.String(), "[]") } func getFakeHelmConfig(settings *cli.EnvSettings, _ string) (*action.Configuration, error) { settings.RepositoryConfig = repoFile registryClient, err := registry.NewClient() if err != nil { return nil, err } return &action.Configuration{ Releases: inMemStorage, KubeClient: &kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: os.Stderr}}, Capabilities: chartutil.DefaultCapabilities, RegistryClient: registryClient, Log: log.Infof, }, nil }