From 3f1325faf83287f32fa1bebbe8eb2bc6fc508fa7 Mon Sep 17 00:00:00 2001 From: "ys.achinta" Date: Mon, 22 Jun 2020 19:06:42 +0530 Subject: [PATCH 1/6] add list under an interface for easy testing --- cmd/service/service.go | 13 ++++--- pkg/api/chartloader.go | 18 ++++++++++ pkg/api/install_api_test.go | 37 ++++++-------------- pkg/api/installer.go | 23 ++++++++++--- pkg/api/list.go | 68 +++++++++++++++++++++++++++++++++++++ pkg/api/lister.go | 28 +++++++++++++++ pkg/api/service.go | 26 +++++--------- 7 files changed, 159 insertions(+), 54 deletions(-) create mode 100644 pkg/api/chartloader.go create mode 100644 pkg/api/list.go create mode 100644 pkg/api/lister.go diff --git a/cmd/service/service.go b/cmd/service/service.go index 375f9c4be..4221bf38d 100644 --- a/cmd/service/service.go +++ b/cmd/service/service.go @@ -13,19 +13,22 @@ import ( ) 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") - actionInstall := action.NewInstall(app.ActionConfig) - service := api.NewService(app.Config, new(action.ChartPathOptions), api.NewInstaller(actionInstall)) + service := api.NewService(app.Config, + new(action.ChartPathOptions), + api.NewInstall(action.NewInstall(app.ActionConfig)), + api.NewList(action.NewList(app.ActionConfig))) + router.Handle("/ping", ping.Handler()) router.Handle("/list", list.Handler()) router.Handle("/install", api.Install(service)) diff --git a/pkg/api/chartloader.go b/pkg/api/chartloader.go new file mode 100644 index 000000000..7887e1ed8 --- /dev/null +++ b/pkg/api/chartloader.go @@ -0,0 +1,18 @@ +package api + +import ( + "github.com/stretchr/testify/mock" + "helm.sh/helm/v3/pkg/cli" +) + +type chartloader interface { + LocateChart(name string, settings *cli.EnvSettings) (string, error) +} + +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/install_api_test.go b/pkg/api/install_api_test.go index 72961e8c2..0ccafa053 100644 --- a/pkg/api/install_api_test.go +++ b/pkg/api/install_api_test.go @@ -14,7 +14,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" ) @@ -23,8 +22,9 @@ type InstallerTestSuite struct { suite.Suite recorder *httptest.ResponseRecorder server *httptest.Server - mockInstaller *mockInstaller - mockChartLoader *mockChartLoader + mockInstall *api.MockInstall + mockChartLoader *api.MockChartLoader + mockList *api.MockList appConfig *cli.EnvSettings } @@ -34,13 +34,14 @@ func (s *InstallerTestSuite) SetupSuite() { func (s *InstallerTestSuite) SetupTest() { s.recorder = httptest.NewRecorder() - s.mockInstaller = new(mockInstaller) - s.mockChartLoader = new(mockChartLoader) + s.mockInstall = new(api.MockInstall) + s.mockChartLoader = new(api.MockChartLoader) + s.mockList = new(api.MockList) s.appConfig = &cli.EnvSettings{ RepositoryConfig: "./testdata/helm", PluginsDirectory: "./testdata/helm/plugin", } - service := api.NewService(s.appConfig, s.mockChartLoader, s.mockInstaller) + service := api.NewService(s.appConfig, s.mockChartLoader, s.mockInstall, s.mockList) handler := api.Install(service) s.server = httptest.NewServer(handler) } @@ -54,11 +55,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.InstallConfig{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) @@ -67,7 +68,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()) } @@ -78,21 +79,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.InstallConfig) { - 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 07a6402c1..1cf3eb6ca 100644 --- a/pkg/api/installer.go +++ b/pkg/api/installer.go @@ -1,24 +1,37 @@ package api import ( + "github.com/stretchr/testify/mock" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/release" ) -type Installer struct { +type install struct { *action.Install } -type runner interface { +type Installer interface { Run(*chart.Chart, map[string]interface{}) (*release.Release, error) + SetConfig(InstallConfig) } -func (i *Installer) SetConfig(cfg InstallConfig) { +func (i *install) SetConfig(cfg InstallConfig) { 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} +} + +type MockInstall struct{ mock.Mock } + +func (m *MockInstall) SetConfig(cfg InstallConfig) { + m.Called(cfg) +} + +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) } diff --git a/pkg/api/list.go b/pkg/api/list.go new file mode 100644 index 000000000..782cde97c --- /dev/null +++ b/pkg/api/list.go @@ -0,0 +1,68 @@ +package api + +import ( + "encoding/json" + "helm.sh/helm/v3/pkg/api/logger" + "net/http" +) + +type ListRequest struct { + RequestID string +} + +type ListResponse struct { + Error string `json:"error,omitempty"` + Data []HelmRelease +} + +type HelmRelease struct { + Release string `json:"release"` + Namespace string `json:"namespace"` +} + +func Handler(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) + decoder.UseNumber() + if err := decoder.Decode(&request); err != nil { + response.Error = err.Error() + logger.Errorf("[List] error decoding request: %v", err) + w.WriteHeader(http.StatusBadRequest) + return + } + defer r.Body.Close() + + request.RequestID = r.Header.Get("Request-Id") + + svc.lister.SetStateMask() + res, err := svc.lister.Run() + + if err != nil { + response.Error = err.Error() + logger.Errorf("[List] error while installing chart: %v", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + var helmReleases []HelmRelease + for _, eachRes := range res { + r := HelmRelease{Release: eachRes.Name, Namespace: eachRes.Namespace} + helmReleases = append(helmReleases, r) + } + response = ListResponse{"", helmReleases} + payload, err := json.Marshal(response) + if err != nil { + response.Error = err.Error() + logger.Errorf("[List] error writing response %v", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusOK) + w.Write(payload) + }) +} \ No newline at end of file diff --git a/pkg/api/lister.go b/pkg/api/lister.go new file mode 100644 index 000000000..1abad4542 --- /dev/null +++ b/pkg/api/lister.go @@ -0,0 +1,28 @@ +package api + +import ( + "github.com/stretchr/testify/mock" + "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() +} + +func NewList(action *action.List) *list{ + return &list{action} +} + + +type MockList struct{ mock.Mock } + +func (m *MockList) Run() ([]*release.Release, error) { + args := m.Called() + return args.Get(0).([]*release.Release), args.Error(1) +} \ No newline at end of file diff --git a/pkg/api/service.go b/pkg/api/service.go index 3b58b6cd8..93241a8b0 100644 --- a/pkg/api/service.go +++ b/pkg/api/service.go @@ -8,22 +8,13 @@ import ( "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart/loader" "helm.sh/helm/v3/pkg/cli" - "helm.sh/helm/v3/pkg/release" _ "k8s.io/client-go/plugin/pkg/client/auth" ) -type installer interface { - SetConfig(InstallConfig) - Run(*chart.Chart, map[string]interface{}) (*release.Release, error) -} - -type chartloader interface { - LocateChart(name string, settings *cli.EnvSettings) (string, error) -} - type Service struct { settings *cli.EnvSettings - installer + Installer + lister chartloader } @@ -73,8 +64,8 @@ func (s Service) loadChart(chartName string) (*chart.Chart, error) { } func (s Service) installChart(icfg InstallConfig, ch *chart.Chart, vals chartValues) (*installResult, error) { - s.installer.SetConfig(icfg) - release, err := s.installer.Run(ch, vals) + s.Installer.SetConfig(icfg) + release, err := s.Installer.Run(ch, vals) if err != nil { return nil, fmt.Errorf("error in installing chart: %v", err) } @@ -85,10 +76,11 @@ func (s Service) installChart(icfg InstallConfig, ch *chart.Chart, vals chartVal return result, nil } -func NewService(settings *cli.EnvSettings, cl chartloader, i installer) Service { +func NewService(settings *cli.EnvSettings, cl chartloader, i Installer, l lister) Service { return Service{ - settings: settings, - chartloader: cl, - installer: i, + settings, + i, + l, + cl, } } From 9503263ac53c3e06409dbcc3ea8a041e1c28048e Mon Sep 17 00:00:00 2001 From: "ys.achinta" Date: Thu, 25 Jun 2020 10:33:29 +0530 Subject: [PATCH 2/6] test for list api --- cmd/service/service.go | 3 +- pkg/api/list.go | 14 +++++-- pkg/api/list_api_test.go | 86 ++++++++++++++++++++++++++++++++++++++++ pkg/api/lister.go | 4 ++ pkg/api/service_test.go | 33 ++++----------- 5 files changed, 109 insertions(+), 31 deletions(-) create mode 100644 pkg/api/list_api_test.go diff --git a/cmd/service/service.go b/cmd/service/service.go index 4221bf38d..eca782416 100644 --- a/cmd/service/service.go +++ b/cmd/service/service.go @@ -6,7 +6,6 @@ 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" @@ -30,7 +29,7 @@ func startServer() { api.NewList(action.NewList(app.ActionConfig))) 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/list.go b/pkg/api/list.go index 782cde97c..496a28711 100644 --- a/pkg/api/list.go +++ b/pkg/api/list.go @@ -20,7 +20,7 @@ type HelmRelease struct { Namespace string `json:"namespace"` } -func Handler(svc Service) http.Handler { +func List(svc Service) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -29,9 +29,11 @@ func Handler(svc Service) http.Handler { decoder := json.NewDecoder(r.Body) decoder.UseNumber() if err := decoder.Decode(&request); err != nil { - response.Error = err.Error() 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() @@ -42,9 +44,11 @@ func Handler(svc Service) http.Handler { res, err := svc.lister.Run() if err != nil { - response.Error = err.Error() 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 } @@ -56,9 +60,11 @@ func Handler(svc Service) http.Handler { response = ListResponse{"", helmReleases} payload, err := json.Marshal(response) if err != nil { - response.Error = err.Error() logger.Errorf("[List] error writing response %v", err) + response.Error = err.Error() + payload, _ := json.Marshal(response) w.WriteHeader(http.StatusInternalServerError) + w.Write(payload) return } diff --git a/pkg/api/list_api_test.go b/pkg/api/list_api_test.go new file mode 100644 index 000000000..69db484a6 --- /dev/null +++ b/pkg/api/list_api_test.go @@ -0,0 +1,86 @@ +package api_test + +import ( + "fmt" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "gotest.tools/assert" + "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 *api.MockInstall + mockChartLoader *api.MockChartLoader + mockList *api.MockList + appConfig *cli.EnvSettings +} + +func (s *ListTestSuite) SetupSuite() { + logger.Setup("default") +} + +func (s *ListTestSuite) SetupTest() { + s.recorder = httptest.NewRecorder() + s.mockInstall = new(api.MockInstall) + s.mockChartLoader = new(api.MockChartLoader) + s.mockList = new(api.MockList) + s.appConfig = &cli.EnvSettings{ + RepositoryConfig: "./testdata/helm", + PluginsDirectory: "./testdata/helm/plugin", + } + service := api.NewService(s.appConfig, s.mockChartLoader, s.mockInstall, s.mockList) + handler := api.List(service) + s.server = httptest.NewServer(handler) +} + +func (s *ListTestSuite) TestShouldReturnReleasesWhenSuccessfulAPICall() { + body := `{"request_id":"test-request-id"}` + 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("Run").Return(releases, nil) + + resp, err := http.DefaultClient.Do(req) + + assert.Equal(s.T(), 200, resp.StatusCode) + expectedResponse := `{"Data":[{"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) + 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 index 1abad4542..8530e6852 100644 --- a/pkg/api/lister.go +++ b/pkg/api/lister.go @@ -25,4 +25,8 @@ 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() } \ No newline at end of file diff --git a/pkg/api/service_test.go b/pkg/api/service_test.go index 728f94287..b2be76aba 100644 --- a/pkg/api/service_test.go +++ b/pkg/api/service_test.go @@ -10,7 +10,6 @@ import ( "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "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" ) @@ -18,8 +17,9 @@ import ( type ServiceTestSuite struct { suite.Suite ctx context.Context - installer *mockInstaller - chartloader *mockChartLoader + installer *MockInstall + chartloader *MockChartLoader + lister *MockList svc Service settings *cli.EnvSettings } @@ -27,10 +27,11 @@ type ServiceTestSuite struct { func (s *ServiceTestSuite) SetupTest() { logger.Setup("") s.ctx = context.Background() - s.installer = new(mockInstaller) - s.chartloader = new(mockChartLoader) + s.installer = new(MockInstall) + s.lister = new(MockList) + s.chartloader = new(MockChartLoader) s.settings = &cli.EnvSettings{} - s.svc = NewService(s.settings, s.chartloader, s.installer) + s.svc = NewService(s.settings, s.chartloader, s.installer, s.lister) } func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnInvalidChart() { @@ -100,22 +101,4 @@ func (s *ServiceTestSuite) TestInstallShouldReturnResultOnSuccess() { func TestServiceSuite(t *testing.T) { suite.Run(t, new(ServiceTestSuite)) -} - -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 mockInstaller struct{ mock.Mock } - -func (m *mockInstaller) SetConfig(cfg InstallConfig) { - 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) -} +} \ No newline at end of file From 34c6d043437b46ecb233fe31edf9695e22e96fff Mon Sep 17 00:00:00 2001 From: "ys.achinta" Date: Tue, 30 Jun 2020 09:15:49 +0530 Subject: [PATCH 3/6] Add Code Review Changes --- pkg/api/chartloader.go | 9 --------- pkg/api/install.go | 4 ++-- pkg/api/install_api_test.go | 33 ++++++++++++++++++++++++++------- pkg/api/installer.go | 12 ------------ pkg/api/list.go | 18 +++++------------- pkg/api/list_api_test.go | 32 +++++++++++++++++++++++++------- pkg/api/lister.go | 14 +++----------- pkg/api/service.go | 31 +++++++++++++++++++++++++------ pkg/api/service_test.go | 29 +++++++++++++++-------------- pkg/api/upgrade.go | 2 +- 10 files changed, 102 insertions(+), 82 deletions(-) diff --git a/pkg/api/chartloader.go b/pkg/api/chartloader.go index 7887e1ed8..e935ca7c5 100644 --- a/pkg/api/chartloader.go +++ b/pkg/api/chartloader.go @@ -1,18 +1,9 @@ package api import ( - "github.com/stretchr/testify/mock" "helm.sh/helm/v3/pkg/cli" ) type chartloader interface { LocateChart(name string, settings *cli.EnvSettings) (string, error) } - -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/install.go b/pkg/api/install.go index 5d465383a..df51db5ab 100644 --- a/pkg/api/install.go +++ b/pkg/api/install.go @@ -16,7 +16,7 @@ type InstallRequest struct { type InstallResponse struct { Error string `json:"error,omitempty"` - Status string `json:"status"` + Status string `json:"Status"` } // RODO: we could use interface as well if everything's in same package @@ -39,7 +39,7 @@ func Install(svc Service) http.Handler { w.WriteHeader(http.StatusInternalServerError) 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 0ccafa053..3321eb872 100644 --- a/pkg/api/install_api_test.go +++ b/pkg/api/install_api_test.go @@ -2,6 +2,7 @@ package api_test import ( "fmt" + "helm.sh/helm/v3/pkg/chart" "io/ioutil" "net/http" "net/http/httptest" @@ -22,21 +23,39 @@ type InstallerTestSuite struct { suite.Suite recorder *httptest.ResponseRecorder server *httptest.Server - mockInstall *api.MockInstall - mockChartLoader *api.MockChartLoader - mockList *api.MockList + mockInstall *mockInstall + mockChartLoader *mockChartLoader + mockList *mockList appConfig *cli.EnvSettings } +type mockInstall struct{ mock.Mock } + +func (m *mockInstall) SetConfig(cfg api.InstallConfig) { + m.Called(cfg) +} + +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) +} + +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) +} + func (s *InstallerTestSuite) SetupSuite() { logger.Setup("default") } func (s *InstallerTestSuite) SetupTest() { s.recorder = httptest.NewRecorder() - s.mockInstall = new(api.MockInstall) - s.mockChartLoader = new(api.MockChartLoader) - s.mockList = new(api.MockList) + s.mockInstall = new(mockInstall) + s.mockChartLoader = new(mockChartLoader) + s.mockList = new(mockList) s.appConfig = &cli.EnvSettings{ RepositoryConfig: "./testdata/helm", PluginsDirectory: "./testdata/helm/plugin", @@ -64,7 +83,7 @@ func (s *InstallerTestSuite) TestShouldReturnDeployedStatusOnSuccessfulInstall() resp, err := http.DefaultClient.Do(req) assert.Equal(s.T(), 200, resp.StatusCode) - expectedResponse := `{"status":"deployed"}` + "\n" + expectedResponse := `{"Status":"deployed"}` + "\n" respBody, _ := ioutil.ReadAll(resp.Body) assert.Equal(s.T(), expectedResponse, string(respBody)) require.NoError(s.T(), err) diff --git a/pkg/api/installer.go b/pkg/api/installer.go index 1cf3eb6ca..2c8e1b7f3 100644 --- a/pkg/api/installer.go +++ b/pkg/api/installer.go @@ -1,7 +1,6 @@ package api import ( - "github.com/stretchr/testify/mock" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/release" @@ -24,14 +23,3 @@ func (i *install) SetConfig(cfg InstallConfig) { func NewInstall(ai *action.Install) *install { return &install{ai} } - -type MockInstall struct{ mock.Mock } - -func (m *MockInstall) SetConfig(cfg InstallConfig) { - m.Called(cfg) -} - -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) -} diff --git a/pkg/api/list.go b/pkg/api/list.go index 496a28711..befa6362e 100644 --- a/pkg/api/list.go +++ b/pkg/api/list.go @@ -7,15 +7,16 @@ import ( ) type ListRequest struct { - RequestID string + NameSpace string `json:"namespace"` + ReleaseStatus string `json:"release_status"` } type ListResponse struct { Error string `json:"error,omitempty"` - Data []HelmRelease + Data []Releases } -type HelmRelease struct { +type Releases struct { Release string `json:"release"` Namespace string `json:"namespace"` } @@ -27,7 +28,6 @@ func List(svc Service) http.Handler { var response ListResponse var request ListRequest decoder := json.NewDecoder(r.Body) - decoder.UseNumber() if err := decoder.Decode(&request); err != nil { logger.Errorf("[List] error decoding request: %v", err) response.Error = err.Error() @@ -38,10 +38,7 @@ func List(svc Service) http.Handler { } defer r.Body.Close() - request.RequestID = r.Header.Get("Request-Id") - - svc.lister.SetStateMask() - res, err := svc.lister.Run() + helmReleases, err := svc.List(request.ReleaseStatus) if err != nil { logger.Errorf("[List] error while installing chart: %v", err) @@ -52,11 +49,6 @@ func List(svc Service) http.Handler { return } - var helmReleases []HelmRelease - for _, eachRes := range res { - r := HelmRelease{Release: eachRes.Name, Namespace: eachRes.Namespace} - helmReleases = append(helmReleases, r) - } response = ListResponse{"", helmReleases} payload, err := json.Marshal(response) if err != nil { diff --git a/pkg/api/list_api_test.go b/pkg/api/list_api_test.go index 69db484a6..0797ff2c6 100644 --- a/pkg/api/list_api_test.go +++ b/pkg/api/list_api_test.go @@ -2,9 +2,11 @@ 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" @@ -20,21 +22,36 @@ type ListTestSuite struct { suite.Suite recorder *httptest.ResponseRecorder server *httptest.Server - mockInstall *api.MockInstall - mockChartLoader *api.MockChartLoader - mockList *api.MockList + 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.mockInstall = new(api.MockInstall) - s.mockChartLoader = new(api.MockChartLoader) - s.mockList = new(api.MockList) + s.mockInstall = new(mockInstall) + s.mockChartLoader = new(mockChartLoader) + s.mockList = new(mockList) s.appConfig = &cli.EnvSettings{ RepositoryConfig: "./testdata/helm", PluginsDirectory: "./testdata/helm/plugin", @@ -45,7 +62,7 @@ func (s *ListTestSuite) SetupTest() { } func (s *ListTestSuite) TestShouldReturnReleasesWhenSuccessfulAPICall() { - body := `{"request_id":"test-request-id"}` + body := `{"release_status":"deployed"}` req, _ := http.NewRequest("POST", fmt.Sprintf("%s/list", s.server.URL), strings.NewReader(body)) var releases []*release.Release @@ -55,6 +72,7 @@ func (s *ListTestSuite) TestShouldReturnReleasesWhenSuccessfulAPICall() { 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) diff --git a/pkg/api/lister.go b/pkg/api/lister.go index 8530e6852..3e65eea27 100644 --- a/pkg/api/lister.go +++ b/pkg/api/lister.go @@ -1,7 +1,6 @@ package api import ( - "github.com/stretchr/testify/mock" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/release" ) @@ -13,20 +12,13 @@ type list struct { type lister interface { Run() ([]*release.Release, error) SetStateMask() + SetState(state action.ListStates) } func NewList(action *action.List) *list{ return &list{action} } - -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 (l *list) SetState(state action.ListStates){ + l.StateMask = state } - -func (m *MockList) SetStateMask() { - m.Called() -} \ No newline at end of file diff --git a/pkg/api/service.go b/pkg/api/service.go index 93241a8b0..653ae0224 100644 --- a/pkg/api/service.go +++ b/pkg/api/service.go @@ -3,6 +3,7 @@ package api import ( "context" "fmt" + "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/api/logger" "helm.sh/helm/v3/pkg/chart" @@ -24,13 +25,13 @@ type InstallConfig struct { ChartName string } -type chartValues map[string]interface{} +type ChartValues map[string]interface{} type installResult 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 @@ -38,7 +39,7 @@ func (s Service) getValues(vals chartValues) (chartValues, error) { return vals, nil } -func (s Service) Install(ctx context.Context, cfg InstallConfig, values chartValues) (*installResult, error) { +func (s Service) Install(ctx context.Context, cfg InstallConfig, values ChartValues) (*installResult, error) { chart, err := s.loadChart(cfg.ChartName) if err != nil { return nil, err @@ -63,7 +64,7 @@ func (s Service) loadChart(chartName string) (*chart.Chart, error) { return requestedChart, nil } -func (s Service) installChart(icfg InstallConfig, ch *chart.Chart, vals chartValues) (*installResult, error) { +func (s Service) installChart(icfg InstallConfig, ch *chart.Chart, vals ChartValues) (*installResult, error) { s.Installer.SetConfig(icfg) release, err := s.Installer.Run(ch, vals) if err != nil { @@ -71,11 +72,29 @@ func (s Service) installChart(icfg InstallConfig, ch *chart.Chart, vals chartVal } result := new(installResult) if release.Info != nil { - result.status = release.Info.Status.String() + result.Status = release.Info.Status.String() } return result, nil } +func (s Service) List(releaseStatus string) ([]Releases, error) { + listStates := new(action.ListStates) + s.lister.SetState(listStates.FromName(releaseStatus)) + s.lister.SetStateMask() + releases, err := s.lister.Run() + if err != nil { + return nil, err + } + + var helmReleases []Releases + for _, eachRes := range releases { + r := Releases{Release: eachRes.Name, Namespace: eachRes.Namespace} + helmReleases = append(helmReleases, r) + } + + return helmReleases, nil +} + func NewService(settings *cli.EnvSettings, cl chartloader, i Installer, l lister) Service { return Service{ settings, diff --git a/pkg/api/service_test.go b/pkg/api/service_test.go index b2be76aba..03f2d330f 100644 --- a/pkg/api/service_test.go +++ b/pkg/api/service_test.go @@ -1,8 +1,9 @@ -package api +package api_test import ( "context" "errors" + "helm.sh/helm/v3/pkg/api" "testing" "github.com/stretchr/testify/assert" @@ -17,31 +18,31 @@ import ( type ServiceTestSuite struct { suite.Suite ctx context.Context - installer *MockInstall - chartloader *MockChartLoader - lister *MockList - svc Service + installer *mockInstall + chartloader *mockChartLoader + lister *mockList + svc api.Service settings *cli.EnvSettings } func (s *ServiceTestSuite) SetupTest() { logger.Setup("") s.ctx = context.Background() - s.installer = new(MockInstall) - s.lister = new(MockList) - s.chartloader = new(MockChartLoader) + s.installer = new(mockInstall) + s.lister = new(mockList) + s.chartloader = new(mockChartLoader) s.settings = &cli.EnvSettings{} - s.svc = NewService(s.settings, s.chartloader, s.installer, s.lister) + s.svc = api.NewService(s.settings, s.chartloader, s.installer, s.lister) } func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnInvalidChart() { chartName := "stable/invalid-chart" - cfg := InstallConfig{ + cfg := api.InstallConfig{ 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) @@ -56,7 +57,7 @@ func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnInvalidChart() { func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnFailedIntallRun() { chartName := "stable/valid-chart" - cfg := InstallConfig{ + cfg := api.InstallConfig{ Name: "some-component", Namespace: "hermes", ChartName: chartName, @@ -78,7 +79,7 @@ func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnFailedIntallRun() { func (s *ServiceTestSuite) TestInstallShouldReturnResultOnSuccess() { chartName := "stable/valid-chart" - cfg := InstallConfig{ + cfg := api.InstallConfig{ Name: "some-component", Namespace: "hermes", ChartName: chartName, @@ -94,7 +95,7 @@ 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) } diff --git a/pkg/api/upgrade.go b/pkg/api/upgrade.go index fba5639f7..8dac31c14 100644 --- a/pkg/api/upgrade.go +++ b/pkg/api/upgrade.go @@ -99,7 +99,7 @@ func (h UpgradeHandler) UpgradeRelease(releaseName, releaseNamespace, chartPath fmt.Printf("error in request: %v", err) return false, "", err } - return true, release.status, nil + return true, release.Status, nil } } From d2e3ac6c047daad92eea2db0cd7dcd1e849cf4ba Mon Sep 17 00:00:00 2001 From: "ys.achinta" Date: Tue, 30 Jun 2020 09:20:52 +0530 Subject: [PATCH 4/6] capture error response --- pkg/api/list_api_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/api/list_api_test.go b/pkg/api/list_api_test.go index 0797ff2c6..40ba27116 100644 --- a/pkg/api/list_api_test.go +++ b/pkg/api/list_api_test.go @@ -78,9 +78,11 @@ func (s *ListTestSuite) TestShouldReturnReleasesWhenSuccessfulAPICall() { resp, err := http.DefaultClient.Do(req) assert.Equal(s.T(), 200, resp.StatusCode) + expectedResponse := `{"Data":[{"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()) } @@ -89,9 +91,14 @@ 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","Data":null}` + respBody, _ := ioutil.ReadAll(resp.Body) + assert.Equal(s.T(), expectedResponse, string(respBody)) require.NoError(s.T(), err) } From 0dbd397759337333e7be6a529352ed3db7764bdc Mon Sep 17 00:00:00 2001 From: "ys.achinta" Date: Tue, 30 Jun 2020 10:16:00 +0530 Subject: [PATCH 5/6] add service level tests --- pkg/api/list.go | 8 ++--- pkg/api/list_api_test.go | 4 +-- pkg/api/service.go | 19 ++++++++--- pkg/api/service_test.go | 74 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 94 insertions(+), 11 deletions(-) diff --git a/pkg/api/list.go b/pkg/api/list.go index befa6362e..8f15225cd 100644 --- a/pkg/api/list.go +++ b/pkg/api/list.go @@ -12,12 +12,12 @@ type ListRequest struct { } type ListResponse struct { - Error string `json:"error,omitempty"` - Data []Releases + Error string `json:"error,omitempty"` + Releases []Release } -type Releases struct { - Release string `json:"release"` +type Release struct { + Name string `json:"release"` Namespace string `json:"namespace"` } diff --git a/pkg/api/list_api_test.go b/pkg/api/list_api_test.go index 40ba27116..b4428237e 100644 --- a/pkg/api/list_api_test.go +++ b/pkg/api/list_api_test.go @@ -79,7 +79,7 @@ func (s *ListTestSuite) TestShouldReturnReleasesWhenSuccessfulAPICall() { assert.Equal(s.T(), 200, resp.StatusCode) - expectedResponse := `{"Data":[{"release":"test-release","namespace":"test-namespace"}]}` + expectedResponse := `{"Releases":[{"release":"test-release","namespace":"test-namespace"}]}` respBody, _ := ioutil.ReadAll(resp.Body) assert.Equal(s.T(), expectedResponse, string(respBody)) @@ -96,7 +96,7 @@ func (s *ListTestSuite) TestShouldReturnBadRequestErrorIfItHasInvalidCharacter() assert.Equal(s.T(), 400, resp.StatusCode) - expectedResponse := `{"error":"invalid character '\"' after object key:value pair","Data":null}` + 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) diff --git a/pkg/api/service.go b/pkg/api/service.go index 653ae0224..b76c2a931 100644 --- a/pkg/api/service.go +++ b/pkg/api/service.go @@ -2,6 +2,7 @@ package api import ( "context" + "errors" "fmt" "helm.sh/helm/v3/pkg/action" @@ -77,18 +78,28 @@ func (s Service) installChart(icfg InstallConfig, ch *chart.Chart, vals ChartVal return result, nil } -func (s Service) List(releaseStatus string) ([]Releases, error) { +func (s Service) List(releaseStatus string) ([]Release, error) { listStates := new(action.ListStates) - s.lister.SetState(listStates.FromName(releaseStatus)) + + 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 []Releases + var helmReleases []Release for _, eachRes := range releases { - r := Releases{Release: eachRes.Name, Namespace: eachRes.Namespace} + r := Release{Name: eachRes.Name, Namespace: eachRes.Namespace} helmReleases = append(helmReleases, r) } diff --git a/pkg/api/service_test.go b/pkg/api/service_test.go index 03f2d330f..a25bd965c 100644 --- a/pkg/api/service_test.go +++ b/pkg/api/service_test.go @@ -3,6 +3,7 @@ package api_test import ( "context" "errors" + "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/api" "testing" @@ -55,7 +56,7 @@ func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnInvalidChart() { s.installer.AssertNotCalled(t, "Run") } -func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnFailedIntallRun() { +func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnFailedInstallRun() { chartName := "stable/valid-chart" cfg := api.InstallConfig{ Name: "some-component", @@ -100,6 +101,77 @@ func (s *ServiceTestSuite) TestInstallShouldReturnResultOnSuccess() { s.installer.AssertExpectations(t) } +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) +} + +func (s *ServiceTestSuite) TestListShouldReturnAllReleasesIfNoFilterIsPassed() { + s.lister.On("SetState", action.ListAll) + s.lister.On("SetStateMask") + + 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) +} + +func (s *ServiceTestSuite) TestListShouldReturnDeployedReleasesIfDeployedIsPassedAsFilter() { + s.lister.On("SetState", action.ListDeployed) + s.lister.On("SetStateMask") + + 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) +} + +func (s *ServiceTestSuite) TestListShouldReturnErrorIfInvalidStatusIsPassedAsFilter() { + releaseStatus := "invalid" + _, err := s.svc.List(releaseStatus) + + t := s.T() + assert.Error(t, err, "invalid release status") +} + func TestServiceSuite(t *testing.T) { suite.Run(t, new(ServiceTestSuite)) } \ No newline at end of file From 74295c3b4ac7545de4fed0d767b79be3027fe4dc Mon Sep 17 00:00:00 2001 From: "ys.achinta" Date: Tue, 30 Jun 2020 10:23:52 +0530 Subject: [PATCH 6/6] fix some linting --- pkg/api/list/listcontract.go | 15 ----------- pkg/api/list/listhandler.go | 52 ------------------------------------ pkg/api/service.go | 8 +++--- 3 files changed, 4 insertions(+), 71 deletions(-) delete mode 100644 pkg/api/list/listcontract.go delete mode 100644 pkg/api/list/listhandler.go 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/service.go b/pkg/api/service.go index b76c2a931..64279f116 100644 --- a/pkg/api/service.go +++ b/pkg/api/service.go @@ -28,7 +28,7 @@ type InstallConfig struct { type ChartValues map[string]interface{} -type installResult struct { +type InstallResult struct { Status string } @@ -40,7 +40,7 @@ func (s Service) getValues(vals ChartValues) (ChartValues, error) { return vals, nil } -func (s Service) Install(ctx context.Context, cfg InstallConfig, values ChartValues) (*installResult, error) { +func (s Service) Install(ctx context.Context, cfg InstallConfig, values ChartValues) (*InstallResult, error) { chart, err := s.loadChart(cfg.ChartName) if err != nil { return nil, err @@ -65,13 +65,13 @@ func (s Service) loadChart(chartName string) (*chart.Chart, error) { return requestedChart, nil } -func (s Service) installChart(icfg InstallConfig, ch *chart.Chart, vals ChartValues) (*installResult, error) { +func (s Service) installChart(icfg InstallConfig, ch *chart.Chart, vals ChartValues) (*InstallResult, 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(installResult) + result := new(InstallResult) if release.Info != nil { result.Status = release.Info.Status.String() }