diff --git a/cmd/service/service.go b/cmd/service/service.go index bc820f625..355c498f0 100644 --- a/cmd/service/service.go +++ b/cmd/service/service.go @@ -6,31 +6,39 @@ import ( "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/api" - "helm.sh/helm/v3/pkg/api/list" "helm.sh/helm/v3/pkg/api/logger" "helm.sh/helm/v3/pkg/api/ping" "helm.sh/helm/v3/pkg/servercontext" ) func main() { - app := servercontext.NewApp() - startServer(app) + servercontext.NewApp() + startServer() } -func startServer(appconfig *servercontext.Application) { +func startServer() { router := http.NewServeMux() //TODO: use gorilla mux and add middleware to write content type and other headers app := servercontext.App() logger.Setup("debug") +<<<<<<< HEAD actionInstall := action.NewInstall(app.ActionConfig) actionUpgrade := action.NewUpgrade(app.ActionConfig) actionHistory := action.NewHistory(app.ActionConfig) service := api.NewService(app.Config, new(action.ChartPathOptions), api.NewInstaller(actionInstall), api.NewUpgrader(actionUpgrade), api.NewHistory(actionHistory)) +======= + + service := api.NewService(app.Config, + new(action.ChartPathOptions), + api.NewInstall(action.NewInstall(app.ActionConfig)), + api.NewList(action.NewList(app.ActionConfig))) + +>>>>>>> helm-service-refactor router.Handle("/ping", ping.Handler()) - router.Handle("/list", list.Handler()) + router.Handle("/list", api.List(service)) router.Handle("/install", api.Install(service)) router.Handle("/upgrade", api.Upgrade(service)) diff --git a/pkg/api/chartloader.go b/pkg/api/chartloader.go new file mode 100644 index 000000000..e935ca7c5 --- /dev/null +++ b/pkg/api/chartloader.go @@ -0,0 +1,9 @@ +package api + +import ( + "helm.sh/helm/v3/pkg/cli" +) + +type chartloader interface { + LocateChart(name string, settings *cli.EnvSettings) (string, error) +} diff --git a/pkg/api/install.go b/pkg/api/install.go index 455f3f01c..c33d31f4c 100644 --- a/pkg/api/install.go +++ b/pkg/api/install.go @@ -37,7 +37,7 @@ func Install(svc Service) http.Handler { respondError(w, "error while installing chart: %v", err) return } - response.Status = res.status + response.Status = res.Status if err := json.NewEncoder(w).Encode(&response); err != nil { logger.Errorf("[Install] error writing response %v", err) w.WriteHeader(http.StatusInternalServerError) diff --git a/pkg/api/install_api_test.go b/pkg/api/install_api_test.go index ed1d38bd0..213f41e2f 100644 --- a/pkg/api/install_api_test.go +++ b/pkg/api/install_api_test.go @@ -15,7 +15,6 @@ import ( "gotest.tools/assert" "helm.sh/helm/v3/pkg/api" "helm.sh/helm/v3/pkg/api/logger" - "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/release" ) @@ -24,8 +23,9 @@ type InstallerTestSuite struct { suite.Suite recorder *httptest.ResponseRecorder server *httptest.Server - mockInstaller *mockInstaller + mockInstall *mockInstall mockChartLoader *mockChartLoader + mockList *mockList appConfig *cli.EnvSettings } @@ -35,13 +35,14 @@ func (s *InstallerTestSuite) SetupSuite() { func (s *InstallerTestSuite) SetupTest() { s.recorder = httptest.NewRecorder() - s.mockInstaller = new(mockInstaller) + s.mockInstall = new(mockInstall) s.mockChartLoader = new(mockChartLoader) + s.mockList = new(mockList) s.appConfig = &cli.EnvSettings{ RepositoryConfig: "./testdata/helm", PluginsDirectory: "./testdata/helm/plugin", } - service := api.NewService(s.appConfig, s.mockChartLoader, s.mockInstaller, nil, nil) + service := api.NewService(s.appConfig, s.mockChartLoader,nil, s.mockInstall, nil, nil) handler := api.Install(service) s.server = httptest.NewServer(handler) } @@ -55,11 +56,11 @@ func (s *InstallerTestSuite) TestShouldReturnDeployedStatusOnSuccessfulInstall() req, _ := http.NewRequest("POST", fmt.Sprintf("%s/install", s.server.URL), strings.NewReader(body)) s.mockChartLoader.On("LocateChart", chartName, s.appConfig).Return("./testdata/albatross", nil) icfg := api.ReleaseConfig{ChartName: chartName, Name: "redis-v5", Namespace: "something"} - s.mockInstaller.On("SetConfig", icfg) + s.mockInstall.On("SetConfig", icfg) release := &release.Release{Info: &release.Info{Status: release.StatusDeployed}} var vals map[string]interface{} //TODO: pass chart object and verify values present testdata chart yml - s.mockInstaller.On("Run", mock.AnythingOfType("*chart.Chart"), vals).Return(release, nil) + s.mockInstall.On("Run", mock.AnythingOfType("*chart.Chart"), vals).Return(release, nil) resp, err := http.DefaultClient.Do(req) @@ -68,7 +69,7 @@ func (s *InstallerTestSuite) TestShouldReturnDeployedStatusOnSuccessfulInstall() respBody, _ := ioutil.ReadAll(resp.Body) assert.Equal(s.T(), expectedResponse, string(respBody)) require.NoError(s.T(), err) - s.mockInstaller.AssertExpectations(s.T()) + s.mockInstall.AssertExpectations(s.T()) s.mockChartLoader.AssertExpectations(s.T()) } @@ -88,7 +89,7 @@ func (s *InstallerTestSuite) TestShouldReturnInternalServerErrorOnFailure() { respBody, _ := ioutil.ReadAll(resp.Body) assert.Equal(s.T(), expectedResponse, string(respBody)) require.NoError(s.T(), err) - s.mockInstaller.AssertExpectations(s.T()) + s.mockInstall.AssertExpectations(s.T()) s.mockChartLoader.AssertExpectations(s.T()) } @@ -99,21 +100,3 @@ func (s *InstallerTestSuite) TearDownTest() { func TestInstallAPI(t *testing.T) { suite.Run(t, new(InstallerTestSuite)) } - -type mockInstaller struct{ mock.Mock } - -func (m *mockInstaller) SetConfig(cfg api.ReleaseConfig) { - m.Called(cfg) -} - -func (m *mockInstaller) Run(c *chart.Chart, vals map[string]interface{}) (*release.Release, error) { - args := m.Called(c, vals) - return args.Get(0).(*release.Release), args.Error(1) -} - -type mockChartLoader struct{ mock.Mock } - -func (m *mockChartLoader) LocateChart(name string, settings *cli.EnvSettings) (string, error) { - args := m.Called(name, settings) - return args.String(0), args.Error(1) -} diff --git a/pkg/api/installer.go b/pkg/api/installer.go index 2fee04b15..c6b7c82ed 100644 --- a/pkg/api/installer.go +++ b/pkg/api/installer.go @@ -6,19 +6,20 @@ import ( "helm.sh/helm/v3/pkg/release" ) -type Installer struct { +type install struct { *action.Install } -type installrunner interface { +type Installer interface { Run(*chart.Chart, map[string]interface{}) (*release.Release, error) + SetConfig(ReleaseConfig) } -func (i *Installer) SetConfig(cfg ReleaseConfig) { +func (i *install) SetConfig(cfg ReleaseConfig) { i.ReleaseName = cfg.Name i.Namespace = cfg.Namespace } -func NewInstaller(ai *action.Install) *Installer { - return &Installer{ai} +func NewInstall(ai *action.Install) *install { + return &install{ai} } diff --git a/pkg/api/list.go b/pkg/api/list.go new file mode 100644 index 000000000..8f15225cd --- /dev/null +++ b/pkg/api/list.go @@ -0,0 +1,66 @@ +package api + +import ( + "encoding/json" + "helm.sh/helm/v3/pkg/api/logger" + "net/http" +) + +type ListRequest struct { + NameSpace string `json:"namespace"` + ReleaseStatus string `json:"release_status"` +} + +type ListResponse struct { + Error string `json:"error,omitempty"` + Releases []Release +} + +type Release struct { + Name string `json:"release"` + Namespace string `json:"namespace"` +} + +func List(svc Service) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + + var response ListResponse + var request ListRequest + decoder := json.NewDecoder(r.Body) + if err := decoder.Decode(&request); err != nil { + logger.Errorf("[List] error decoding request: %v", err) + response.Error = err.Error() + payload, _ := json.Marshal(response) + w.WriteHeader(http.StatusBadRequest) + w.Write(payload) + return + } + defer r.Body.Close() + + helmReleases, err := svc.List(request.ReleaseStatus) + + if err != nil { + logger.Errorf("[List] error while installing chart: %v", err) + response.Error = err.Error() + payload, _ := json.Marshal(response) + w.WriteHeader(http.StatusInternalServerError) + w.Write(payload) + return + } + + response = ListResponse{"", helmReleases} + payload, err := json.Marshal(response) + if err != nil { + logger.Errorf("[List] error writing response %v", err) + response.Error = err.Error() + payload, _ := json.Marshal(response) + w.WriteHeader(http.StatusInternalServerError) + w.Write(payload) + return + } + + w.WriteHeader(http.StatusOK) + w.Write(payload) + }) +} \ No newline at end of file diff --git a/pkg/api/list/listcontract.go b/pkg/api/list/listcontract.go deleted file mode 100644 index 5cb22f5c2..000000000 --- a/pkg/api/list/listcontract.go +++ /dev/null @@ -1,15 +0,0 @@ -package list - -type ListRequest struct { - RequestID string -} - -type ListRespose struct { - Status bool - Data []HelmRelease -} - -type HelmRelease struct { - Release string `json:"release"` - Namespace string `json:"namespace"` -} diff --git a/pkg/api/list/listhandler.go b/pkg/api/list/listhandler.go deleted file mode 100644 index 4da9cddef..000000000 --- a/pkg/api/list/listhandler.go +++ /dev/null @@ -1,52 +0,0 @@ -package list - -import ( - "encoding/json" - "fmt" - "net/http" - - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/servercontext" -) - -func Handler() http.Handler { - return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - - res.Header().Set("Content-Type", "application/json") - defer req.Body.Close() - - var request ListRequest - decoder := json.NewDecoder(req.Body) - decoder.UseNumber() - - if err := decoder.Decode(&request); err != nil { - fmt.Println("error in request") - return - } - - request.RequestID = req.Header.Get("Request-Id") - - list := action.NewList(servercontext.App().ActionConfig) - - list.SetStateMask() - results, err := list.Run() - if err != nil { - fmt.Printf("error while running helm list %v", err) - } - - var helmReleases []HelmRelease - for _, res := range results { - r := HelmRelease{Release: res.Name, Namespace: res.Namespace} - helmReleases = append(helmReleases, r) - } - - response := ListRespose{Status: true, Data: helmReleases} - payload, err := json.Marshal(response) - if err != nil { - fmt.Println("error parsing response") - return - } - - res.Write(payload) - }) -} diff --git a/pkg/api/list_api_test.go b/pkg/api/list_api_test.go new file mode 100644 index 000000000..b09aac14c --- /dev/null +++ b/pkg/api/list_api_test.go @@ -0,0 +1,109 @@ +package api_test + +import ( + "fmt" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "gotest.tools/assert" + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/api" + "helm.sh/helm/v3/pkg/api/logger" + "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/release" + "io/ioutil" + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +type ListTestSuite struct { + suite.Suite + recorder *httptest.ResponseRecorder + server *httptest.Server + mockInstall *mockInstall + mockChartLoader *mockChartLoader + mockList *mockList + appConfig *cli.EnvSettings +} + +type mockList struct{ mock.Mock } + +func (m *mockList) Run() ([]*release.Release, error) { + args := m.Called() + return args.Get(0).([]*release.Release), args.Error(1) +} + +func (m *mockList) SetStateMask() { + m.Called() +} + +func (m *mockList) SetState(state action.ListStates) { + m.Called(state) +} + +func (s *ListTestSuite) SetupSuite() { + logger.Setup("default") +} + +func (s *ListTestSuite) SetupTest() { + s.recorder = httptest.NewRecorder() + s.mockList = new(mockList) + s.appConfig = &cli.EnvSettings{ + RepositoryConfig: "./testdata/helm", + PluginsDirectory: "./testdata/helm/plugin", + } + service := api.NewService(s.appConfig, nil, s.mockList, nil, nil, nil) + handler := api.List(service) + s.server = httptest.NewServer(handler) +} + +func (s *ListTestSuite) TestShouldReturnReleasesWhenSuccessfulAPICall() { + body := `{"release_status":"deployed"}` + req, _ := http.NewRequest("POST", fmt.Sprintf("%s/list", s.server.URL), strings.NewReader(body)) + + var releases []*release.Release + releases = append(releases, + &release.Release{Name: "test-release", + Namespace: "test-namespace", + Info: &release.Info{Status: release.StatusDeployed}}) + + s.mockList.On("SetStateMask") + s.mockList.On("SetState", action.ListDeployed) + s.mockList.On("Run").Return(releases, nil) + + resp, err := http.DefaultClient.Do(req) + + assert.Equal(s.T(), 200, resp.StatusCode) + + expectedResponse := `{"Releases":[{"release":"test-release","namespace":"test-namespace"}]}` + respBody, _ := ioutil.ReadAll(resp.Body) + assert.Equal(s.T(), expectedResponse, string(respBody)) + + require.NoError(s.T(), err) + s.mockList.AssertExpectations(s.T()) +} + +func (s *ListTestSuite) TestShouldReturnBadRequestErrorIfItHasInvalidCharacter() { + body := `{"request_id":"test-request-id""""}` + req, _ := http.NewRequest("POST", fmt.Sprintf("%s/list", s.server.URL), strings.NewReader(body)) + + + resp, err := http.DefaultClient.Do(req) + + assert.Equal(s.T(), 400, resp.StatusCode) + + expectedResponse := `{"error":"invalid character '\"' after object key:value pair","Releases":null}` + respBody, _ := ioutil.ReadAll(resp.Body) + assert.Equal(s.T(), expectedResponse, string(respBody)) + require.NoError(s.T(), err) +} + +func (s *ListTestSuite) TearDownTest() { + s.server.Close() +} + +func TestListAPI(t *testing.T) { + suite.Run(t, new(ListTestSuite)) +} \ No newline at end of file diff --git a/pkg/api/lister.go b/pkg/api/lister.go new file mode 100644 index 000000000..3e65eea27 --- /dev/null +++ b/pkg/api/lister.go @@ -0,0 +1,24 @@ +package api + +import ( + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/release" +) + +type list struct { + *action.List +} + +type lister interface { + Run() ([]*release.Release, error) + SetStateMask() + SetState(state action.ListStates) +} + +func NewList(action *action.List) *list{ + return &list{action} +} + +func (l *list) SetState(state action.ListStates){ + l.StateMask = state +} diff --git a/pkg/api/service.go b/pkg/api/service.go index 63b55798f..e830f7c0d 100644 --- a/pkg/api/service.go +++ b/pkg/api/service.go @@ -4,8 +4,12 @@ import ( "context" "errors" "fmt" + "strings" + "helm.sh/helm/v3/pkg/action" + + "helm.sh/helm/v3/pkg/api/logger" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart/loader" @@ -14,10 +18,6 @@ import ( _ "k8s.io/client-go/plugin/pkg/client/auth" ) -type installer interface { - SetConfig(ReleaseConfig) - installrunner -} type upgrader interface { SetConfig(ReleaseConfig) @@ -29,15 +29,12 @@ type history interface { historyrunner } -type chartloader interface { - LocateChart(name string, settings *cli.EnvSettings) (string, error) -} - type Service struct { settings *cli.EnvSettings - installer - upgrader chartloader + lister + Installer + upgrader history } @@ -47,13 +44,13 @@ type ReleaseConfig struct { ChartName string } -type chartValues map[string]interface{} +type ChartValues map[string]interface{} type ReleaseResult struct { - status string + Status string } -func (s Service) getValues(vals chartValues) (chartValues, error) { +func (s Service) getValues(vals ChartValues) (ChartValues, error) { // valueOpts := &values.Options{} //valueOpts.Values = append(valueOpts.Values, vals) //TODO: we need to make this as Provider, so it'll be able to merge @@ -61,7 +58,8 @@ func (s Service) getValues(vals chartValues) (chartValues, error) { return vals, nil } -func (s Service) Install(ctx context.Context, cfg ReleaseConfig, values chartValues) (*ReleaseResult, error) { + +func (s Service) Install(ctx context.Context, cfg ReleaseConfig, values ChartValues) (*ReleaseResult, error) { if err := s.validate(cfg, values); err != nil { return nil, fmt.Errorf("error request validation: %v", err) } @@ -76,7 +74,7 @@ func (s Service) Install(ctx context.Context, cfg ReleaseConfig, values chartVal return s.installChart(cfg, chart, vals) } -func (s Service) Upgrade(ctx context.Context, cfg ReleaseConfig, values chartValues) (*ReleaseResult, error) { +func (s Service) Upgrade(ctx context.Context, cfg ReleaseConfig, values ChartValues) (*ReleaseResult, error) { if err := s.validate(cfg, values); err != nil { return nil, fmt.Errorf("error request validation: %v", err) } @@ -112,20 +110,22 @@ func (s Service) loadChart(chartName string) (*chart.Chart, error) { return requestedChart, nil } -func (s Service) installChart(icfg ReleaseConfig, ch *chart.Chart, vals chartValues) (*ReleaseResult, error) { - s.installer.SetConfig(icfg) - release, err := s.installer.Run(ch, vals) + +func (s Service) installChart(icfg ReleaseConfig, ch *chart.Chart, vals ChartValues) (*ReleaseResult, error) { + s.Installer.SetConfig(icfg) + release, err := s.Installer.Run(ch, vals) if err != nil { return nil, fmt.Errorf("error in installing chart: %v", err) } result := new(ReleaseResult) if release.Info != nil { - result.status = release.Info.Status.String() + result.Status = release.Info.Status.String() } return result, nil } -func (s Service) upgradeRelease(ucfg ReleaseConfig, ch *chart.Chart, vals chartValues) (*ReleaseResult, error) { + +func (s Service) upgradeRelease(ucfg ReleaseConfig, ch *chart.Chart, vals ChartValues) (*ReleaseResult, error) { s.upgrader.SetConfig(ucfg) release, err := s.upgrader.Run(ucfg.Name, ch, vals) if err != nil { @@ -133,12 +133,12 @@ func (s Service) upgradeRelease(ucfg ReleaseConfig, ch *chart.Chart, vals chartV } result := new(ReleaseResult) if release.Info != nil { - result.status = release.Info.Status.String() + result.Status = release.Info.Status.String() } return result, nil } -func (s Service) validate(icfg ReleaseConfig, values chartValues) error { +func (s Service) validate(icfg ReleaseConfig, values ChartValues) error { if strings.HasPrefix(icfg.ChartName, ".") || strings.HasPrefix(icfg.ChartName, "/") { return errors.New("cannot refer local chart") @@ -146,12 +146,34 @@ func (s Service) validate(icfg ReleaseConfig, values chartValues) error { return nil } -func NewService(settings *cli.EnvSettings, cl chartloader, i installer, u upgrader, h history) Service { - return Service{ - settings: settings, - chartloader: cl, - installer: i, - upgrader: u, - history: h, +func (s Service) List(releaseStatus string) ([]Release, error) { + listStates := new(action.ListStates) + + state := action.ListAll + if releaseStatus != "" { + state = listStates.FromName(releaseStatus) + } + + if state == action.ListUnknown { + return nil, errors.New("invalid release status") + } + + s.lister.SetState(state) + s.lister.SetStateMask() + releases, err := s.lister.Run() + if err != nil { + return nil, err + } + + var helmReleases []Release + for _, eachRes := range releases { + r := Release{Name: eachRes.Name, Namespace: eachRes.Namespace} + helmReleases = append(helmReleases, r) } + + return helmReleases, nil +} + +func NewService(settings *cli.EnvSettings, cl chartloader, l lister, i Installer, u upgrader, h history) Service { + return Service{settings,cl, l,i, u, h} } diff --git a/pkg/api/service_test.go b/pkg/api/service_test.go index a3e116f16..ae3c7f4b4 100644 --- a/pkg/api/service_test.go +++ b/pkg/api/service_test.go @@ -1,8 +1,10 @@ -package api +package api_test import ( "context" "errors" + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/api" "testing" "github.com/stretchr/testify/assert" @@ -19,33 +21,35 @@ import ( type ServiceTestSuite struct { suite.Suite ctx context.Context - installer *mockInstaller upgrader *mockUpgrader history *mockHistory + installer *mockInstall chartloader *mockChartLoader - svc Service + lister *mockList + svc api.Service settings *cli.EnvSettings } func (s *ServiceTestSuite) SetupTest() { logger.Setup("") - s.ctx = context.Background() - s.installer = new(mockInstaller) + s.settings = &cli.EnvSettings{} + s.chartloader = new(mockChartLoader) + s.lister = new(mockList) + s.installer = new(mockInstall) s.upgrader = new(mockUpgrader) s.history = new(mockHistory) - s.chartloader = new(mockChartLoader) - s.settings = &cli.EnvSettings{} - s.svc = NewService(s.settings, s.chartloader, s.installer, s.upgrader, s.history) + s.ctx = context.Background() + s.svc = api.NewService(s.settings, s.chartloader, s.lister, s.installer, s.upgrader, s.history) } func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnInvalidChart() { chartName := "stable/invalid-chart" - cfg := ReleaseConfig{ + cfg := api.ReleaseConfig{ Name: "some-component", Namespace: "hermes", ChartName: chartName, } - var vals chartValues + var vals api.ChartValues s.chartloader.On("LocateChart", chartName, s.settings).Return("", errors.New("Unable to find chart")) res, err := s.svc.Install(s.ctx, cfg, vals) @@ -60,12 +64,12 @@ func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnInvalidChart() { func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnLocalChartReference() { chartName := "./some/local-chart" - cfg := ReleaseConfig{ + cfg := api.ReleaseConfig{ Name: "some-component", Namespace: "hermes", ChartName: chartName, } - var vals chartValues + var vals api.ChartValues res, err := s.svc.Install(s.ctx, cfg, vals) @@ -79,7 +83,7 @@ func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnLocalChartReference() { func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnFailedInstallRun() { chartName := "stable/valid-chart" - cfg := ReleaseConfig{ + cfg := api.ReleaseConfig{ Name: "some-component", Namespace: "hermes", ChartName: chartName, @@ -101,7 +105,7 @@ func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnFailedInstallRun() { func (s *ServiceTestSuite) TestInstallShouldReturnResultOnSuccess() { chartName := "stable/valid-chart" - cfg := ReleaseConfig{ + cfg := api.ReleaseConfig{ Name: "some-component", Namespace: "hermes", ChartName: chartName, @@ -117,14 +121,14 @@ func (s *ServiceTestSuite) TestInstallShouldReturnResultOnSuccess() { t := s.T() assert.NoError(t, err) require.NotNil(t, res) - assert.Equal(t, res.status, "deployed") + assert.Equal(t, res.Status, "deployed") s.chartloader.AssertExpectations(t) s.installer.AssertExpectations(t) } func (s *ServiceTestSuite) TestUpgradeInstallTrueShouldInstallChart() { chartName := "stable/valid-chart" - cfg := ReleaseConfig{ + cfg := api.ReleaseConfig{ Name: "some-component", Namespace: "hermes", ChartName: chartName, @@ -142,14 +146,14 @@ func (s *ServiceTestSuite) TestUpgradeInstallTrueShouldInstallChart() { t := s.T() assert.NoError(t, err) require.NotNil(t, res) - assert.Equal(t, res.status, "deployed") + assert.Equal(t, res.Status, "deployed") s.chartloader.AssertExpectations(t) s.installer.AssertExpectations(t) } func (s *ServiceTestSuite) TestUpgradeInstallFalseShouldNotInstallChart() { chartName := "stable/valid-chart" - cfg := ReleaseConfig{ + cfg := api.ReleaseConfig{ Name: "some-component", Namespace: "hermes", ChartName: chartName, @@ -168,14 +172,14 @@ func (s *ServiceTestSuite) TestUpgradeInstallFalseShouldNotInstallChart() { require.NotNil(t, res) s.installer.AssertNotCalled(t, "Run") s.history.AssertNotCalled(t, "Run") - assert.Equal(t, res.status, "deployed") + assert.Equal(t, res.Status, "deployed") s.chartloader.AssertExpectations(t) s.installer.AssertExpectations(t) } func (s *ServiceTestSuite) TestUpgradeShouldReturnErrorOnFailedUpgradeRun() { chartName := "stable/valid-chart" - cfg := ReleaseConfig{ + cfg := api.ReleaseConfig{ Name: "some-component", Namespace: "hermes", ChartName: chartName, @@ -197,7 +201,7 @@ func (s *ServiceTestSuite) TestUpgradeShouldReturnErrorOnFailedUpgradeRun() { func (s *ServiceTestSuite) TestUpgradeShouldReturnResultOnSuccess() { chartName := "stable/valid-chart" - cfg := ReleaseConfig{ + cfg := api.ReleaseConfig{ Name: "some-component", Namespace: "hermes", ChartName: chartName, @@ -213,52 +217,127 @@ func (s *ServiceTestSuite) TestUpgradeShouldReturnResultOnSuccess() { t := s.T() assert.NoError(t, err) require.NotNil(t, res) - assert.Equal(t, res.status, "deployed") + assert.Equal(t, res.Status, "deployed") s.chartloader.AssertExpectations(t) s.upgrader.AssertExpectations(t) } -func TestServiceSuite(t *testing.T) { - suite.Run(t, new(ServiceTestSuite)) + +func (s *ServiceTestSuite) TestListShouldReturnErrorOnFailureOfListRun() { + s.lister.On("SetState", action.ListDeployed) + s.lister.On("SetStateMask") + + var releases []*release.Release + + s.lister.On("Run").Return(releases, errors.New("cluster issue")) + + releaseStatus := "deployed" + res, err := s.svc.List(releaseStatus) + + t := s.T() + assert.Error(t, err, "cluster issue") + assert.Nil(t, res) + + s.lister.AssertExpectations(t) } -type mockChartLoader struct{ mock.Mock } +func (s *ServiceTestSuite) TestListShouldReturnAllReleasesIfNoFilterIsPassed() { + s.lister.On("SetState", action.ListAll) + s.lister.On("SetStateMask") -func (m *mockChartLoader) LocateChart(name string, settings *cli.EnvSettings) (string, error) { - args := m.Called(name, settings) - return args.String(0), args.Error(1) + var releases []*release.Release + releases = append(releases, + &release.Release{Name: "test-release", + Namespace: "test-namespace", + Info: &release.Info{Status: release.StatusDeployed}}) + + s.lister.On("Run").Return(releases, nil) + + releaseStatus := "" + res, err := s.svc.List(releaseStatus) + + t := s.T() + assert.NoError(t, err) + require.NotNil(t, res) + + var response []api.Release + response = append(response, api.Release{"test-release", "test-namespace"}) + + assert.Equal(t, len(res), 1) + assert.Equal(t, "test-release", response[0].Name) + assert.Equal(t, "test-namespace", response[0].Namespace) + + s.lister.AssertExpectations(t) } -type mockInstaller struct{ mock.Mock } +func (s *ServiceTestSuite) TestListShouldReturnErrorIfInvalidStatusIsPassedAsFilter() { + releaseStatus := "invalid" + _, err := s.svc.List(releaseStatus) -type mockUpgrader struct{ mock.Mock } + t := s.T() + assert.Error(t, err, "invalid release status") +} -type mockHistory struct{ mock.Mock } +func (s *ServiceTestSuite) TestListShouldReturnDeployedReleasesIfDeployedIsPassedAsFilter() { + s.lister.On("SetState", action.ListDeployed) + s.lister.On("SetStateMask") -func (m *mockInstaller) SetConfig(cfg ReleaseConfig) { + var releases []*release.Release + s.lister.On("Run").Return(releases, nil) + + releaseStatus := "deployed" + _, err := s.svc.List(releaseStatus) + + t := s.T() + assert.NoError(t, err) + s.lister.AssertExpectations(t) +} + + +type mockInstall struct{ mock.Mock } + +func (m *mockInstall) SetConfig(cfg api.ReleaseConfig) { m.Called(cfg) } -func (m *mockInstaller) Run(c *chart.Chart, vals map[string]interface{}) (*release.Release, error) { +func (m *mockInstall) Run(c *chart.Chart, vals map[string]interface{}) (*release.Release, error) { args := m.Called(c, vals) return args.Get(0).(*release.Release), args.Error(1) } -func (m *mockUpgrader) GetInstall() bool { - args := m.Called() - return args.Get(0).(bool) + +type mockChartLoader struct{ mock.Mock } + +func (m *mockChartLoader) LocateChart(name string, settings *cli.EnvSettings) (string, error) { + args := m.Called(name, settings) + return args.String(0), args.Error(1) } + +type mockUpgrader struct{ mock.Mock } + func (m *mockUpgrader) Run(name string, chart *chart.Chart, vals map[string]interface{}) (*release.Release, error) { args := m.Called(name, chart, vals) return args.Get(0).(*release.Release), args.Error(1) } -func (m *mockUpgrader) SetConfig(cfg ReleaseConfig) { +func (m *mockUpgrader) SetConfig(cfg api.ReleaseConfig) { _ = m.Called(cfg) } +func (m *mockUpgrader) GetInstall() bool { + args := m.Called() + return args.Get(0).(bool) +} + + +type mockHistory struct{ mock.Mock } + func (m *mockHistory) Run(name string) ([]*release.Release, error) { args := m.Called(name) return args.Get(0).([]*release.Release), args.Error(1) } + +func TestServiceSuite(t *testing.T) { + suite.Run(t, new(ServiceTestSuite)) +} diff --git a/pkg/api/upgrade.go b/pkg/api/upgrade.go index f789979d3..4c39b2fa5 100644 --- a/pkg/api/upgrade.go +++ b/pkg/api/upgrade.go @@ -30,6 +30,7 @@ func Upgrade(svc Service) http.Handler { w.WriteHeader(http.StatusBadRequest) return } + defer r.Body.Close() var response UpgradeResponse cfg := ReleaseConfig{ChartName: req.Chart, Name: req.Name, Namespace: req.Namespace} @@ -38,7 +39,7 @@ func Upgrade(svc Service) http.Handler { respondError(w, "error while upgrading chart: %v", err) return } - response.Status = res.status + response.Status = res.Status if err := json.NewEncoder(w).Encode(&response); err != nil { logger.Errorf("[Upgrade] error writing response %v", err) w.WriteHeader(http.StatusInternalServerError) diff --git a/pkg/api/upgrade_api_test.go b/pkg/api/upgrade_api_test.go index 00df1d036..8194b6b15 100644 --- a/pkg/api/upgrade_api_test.go +++ b/pkg/api/upgrade_api_test.go @@ -15,7 +15,6 @@ import ( "gotest.tools/assert" "helm.sh/helm/v3/pkg/api" "helm.sh/helm/v3/pkg/api/logger" - "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/release" ) @@ -43,7 +42,7 @@ func (s *UpgradeTestSuite) SetupTest() { RepositoryConfig: "./testdata/helm", PluginsDirectory: "./testdata/helm/plugin", } - service := api.NewService(s.appConfig, s.mockChartLoader, nil, s.mockUpgrader, s.mockHistory) + service := api.NewService(s.appConfig, s.mockChartLoader, nil, nil, s.mockUpgrader, s.mockHistory) handler := api.Upgrade(service) s.server = httptest.NewServer(handler) } @@ -105,26 +104,3 @@ func (s *UpgradeTestSuite) TearDownTest() { func TestUpgradeAPI(t *testing.T) { suite.Run(t, new(UpgradeTestSuite)) } - -type mockUpgrader struct{ mock.Mock } - -type mockHistory struct{ mock.Mock } - -func (m *mockUpgrader) Run(name string, chart *chart.Chart, vals map[string]interface{}) (*release.Release, error) { - args := m.Called(name, chart, vals) - return args.Get(0).(*release.Release), args.Error(1) -} - -func (m *mockUpgrader) GetInstall() bool { - args := m.Called() - return args.Get(0).(bool) -} - -func (m *mockUpgrader) SetConfig(cfg api.ReleaseConfig) { - _ = m.Called(cfg) -} - -func (m *mockHistory) Run(name string) ([]*release.Release, error) { - args := m.Called(name) - return args.Get(0).([]*release.Release), args.Error(1) -}