merge branch helm-list-refactor

pull/8423/head
ys.achinta 5 years ago
commit 5d510c6cae

@ -6,31 +6,39 @@ import (
"helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/api" "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/logger"
"helm.sh/helm/v3/pkg/api/ping" "helm.sh/helm/v3/pkg/api/ping"
"helm.sh/helm/v3/pkg/servercontext" "helm.sh/helm/v3/pkg/servercontext"
) )
func main() { func main() {
app := servercontext.NewApp() servercontext.NewApp()
startServer(app) startServer()
} }
func startServer(appconfig *servercontext.Application) { func startServer() {
router := http.NewServeMux() router := http.NewServeMux()
//TODO: use gorilla mux and add middleware to write content type and other headers //TODO: use gorilla mux and add middleware to write content type and other headers
app := servercontext.App() app := servercontext.App()
logger.Setup("debug") logger.Setup("debug")
<<<<<<< HEAD
actionInstall := action.NewInstall(app.ActionConfig) actionInstall := action.NewInstall(app.ActionConfig)
actionUpgrade := action.NewUpgrade(app.ActionConfig) actionUpgrade := action.NewUpgrade(app.ActionConfig)
actionHistory := action.NewHistory(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.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("/ping", ping.Handler())
router.Handle("/list", list.Handler()) router.Handle("/list", api.List(service))
router.Handle("/install", api.Install(service)) router.Handle("/install", api.Install(service))
router.Handle("/upgrade", api.Upgrade(service)) router.Handle("/upgrade", api.Upgrade(service))

@ -0,0 +1,9 @@
package api
import (
"helm.sh/helm/v3/pkg/cli"
)
type chartloader interface {
LocateChart(name string, settings *cli.EnvSettings) (string, error)
}

@ -37,7 +37,7 @@ func Install(svc Service) http.Handler {
respondError(w, "error while installing chart: %v", err) respondError(w, "error while installing chart: %v", err)
return return
} }
response.Status = res.status response.Status = res.Status
if err := json.NewEncoder(w).Encode(&response); err != nil { if err := json.NewEncoder(w).Encode(&response); err != nil {
logger.Errorf("[Install] error writing response %v", err) logger.Errorf("[Install] error writing response %v", err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)

@ -15,7 +15,6 @@ import (
"gotest.tools/assert" "gotest.tools/assert"
"helm.sh/helm/v3/pkg/api" "helm.sh/helm/v3/pkg/api"
"helm.sh/helm/v3/pkg/api/logger" "helm.sh/helm/v3/pkg/api/logger"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/release"
) )
@ -24,8 +23,9 @@ type InstallerTestSuite struct {
suite.Suite suite.Suite
recorder *httptest.ResponseRecorder recorder *httptest.ResponseRecorder
server *httptest.Server server *httptest.Server
mockInstaller *mockInstaller mockInstall *mockInstall
mockChartLoader *mockChartLoader mockChartLoader *mockChartLoader
mockList *mockList
appConfig *cli.EnvSettings appConfig *cli.EnvSettings
} }
@ -35,13 +35,14 @@ func (s *InstallerTestSuite) SetupSuite() {
func (s *InstallerTestSuite) SetupTest() { func (s *InstallerTestSuite) SetupTest() {
s.recorder = httptest.NewRecorder() s.recorder = httptest.NewRecorder()
s.mockInstaller = new(mockInstaller) s.mockInstall = new(mockInstall)
s.mockChartLoader = new(mockChartLoader) s.mockChartLoader = new(mockChartLoader)
s.mockList = new(mockList)
s.appConfig = &cli.EnvSettings{ s.appConfig = &cli.EnvSettings{
RepositoryConfig: "./testdata/helm", RepositoryConfig: "./testdata/helm",
PluginsDirectory: "./testdata/helm/plugin", 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) handler := api.Install(service)
s.server = httptest.NewServer(handler) 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)) 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) s.mockChartLoader.On("LocateChart", chartName, s.appConfig).Return("./testdata/albatross", nil)
icfg := api.ReleaseConfig{ChartName: chartName, Name: "redis-v5", Namespace: "something"} 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}} release := &release.Release{Info: &release.Info{Status: release.StatusDeployed}}
var vals map[string]interface{} var vals map[string]interface{}
//TODO: pass chart object and verify values present testdata chart yml //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) resp, err := http.DefaultClient.Do(req)
@ -68,7 +69,7 @@ func (s *InstallerTestSuite) TestShouldReturnDeployedStatusOnSuccessfulInstall()
respBody, _ := ioutil.ReadAll(resp.Body) respBody, _ := ioutil.ReadAll(resp.Body)
assert.Equal(s.T(), expectedResponse, string(respBody)) assert.Equal(s.T(), expectedResponse, string(respBody))
require.NoError(s.T(), err) require.NoError(s.T(), err)
s.mockInstaller.AssertExpectations(s.T()) s.mockInstall.AssertExpectations(s.T())
s.mockChartLoader.AssertExpectations(s.T()) s.mockChartLoader.AssertExpectations(s.T())
} }
@ -88,7 +89,7 @@ func (s *InstallerTestSuite) TestShouldReturnInternalServerErrorOnFailure() {
respBody, _ := ioutil.ReadAll(resp.Body) respBody, _ := ioutil.ReadAll(resp.Body)
assert.Equal(s.T(), expectedResponse, string(respBody)) assert.Equal(s.T(), expectedResponse, string(respBody))
require.NoError(s.T(), err) require.NoError(s.T(), err)
s.mockInstaller.AssertExpectations(s.T()) s.mockInstall.AssertExpectations(s.T())
s.mockChartLoader.AssertExpectations(s.T()) s.mockChartLoader.AssertExpectations(s.T())
} }
@ -99,21 +100,3 @@ func (s *InstallerTestSuite) TearDownTest() {
func TestInstallAPI(t *testing.T) { func TestInstallAPI(t *testing.T) {
suite.Run(t, new(InstallerTestSuite)) 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)
}

@ -6,19 +6,20 @@ import (
"helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/release"
) )
type Installer struct { type install struct {
*action.Install *action.Install
} }
type installrunner interface { type Installer interface {
Run(*chart.Chart, map[string]interface{}) (*release.Release, error) 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.ReleaseName = cfg.Name
i.Namespace = cfg.Namespace i.Namespace = cfg.Namespace
} }
func NewInstaller(ai *action.Install) *Installer { func NewInstall(ai *action.Install) *install {
return &Installer{ai} return &install{ai}
} }

@ -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)
})
}

@ -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"`
}

@ -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)
})
}

@ -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))
}

@ -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
}

@ -4,8 +4,12 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/api/logger" "helm.sh/helm/v3/pkg/api/logger"
"helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/chart/loader" "helm.sh/helm/v3/pkg/chart/loader"
@ -14,10 +18,6 @@ import (
_ "k8s.io/client-go/plugin/pkg/client/auth" _ "k8s.io/client-go/plugin/pkg/client/auth"
) )
type installer interface {
SetConfig(ReleaseConfig)
installrunner
}
type upgrader interface { type upgrader interface {
SetConfig(ReleaseConfig) SetConfig(ReleaseConfig)
@ -29,15 +29,12 @@ type history interface {
historyrunner historyrunner
} }
type chartloader interface {
LocateChart(name string, settings *cli.EnvSettings) (string, error)
}
type Service struct { type Service struct {
settings *cli.EnvSettings settings *cli.EnvSettings
installer
upgrader
chartloader chartloader
lister
Installer
upgrader
history history
} }
@ -47,13 +44,13 @@ type ReleaseConfig struct {
ChartName string ChartName string
} }
type chartValues map[string]interface{} type ChartValues map[string]interface{}
type ReleaseResult struct { 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.Options{}
//valueOpts.Values = append(valueOpts.Values, vals) //valueOpts.Values = append(valueOpts.Values, vals)
//TODO: we need to make this as Provider, so it'll be able to merge //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 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 { if err := s.validate(cfg, values); err != nil {
return nil, fmt.Errorf("error request validation: %v", err) 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) 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 { if err := s.validate(cfg, values); err != nil {
return nil, fmt.Errorf("error request validation: %v", err) 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 return requestedChart, nil
} }
func (s Service) installChart(icfg ReleaseConfig, ch *chart.Chart, vals chartValues) (*ReleaseResult, error) {
s.installer.SetConfig(icfg) func (s Service) installChart(icfg ReleaseConfig, ch *chart.Chart, vals ChartValues) (*ReleaseResult, error) {
release, err := s.installer.Run(ch, vals) s.Installer.SetConfig(icfg)
release, err := s.Installer.Run(ch, vals)
if err != nil { if err != nil {
return nil, fmt.Errorf("error in installing chart: %v", err) return nil, fmt.Errorf("error in installing chart: %v", err)
} }
result := new(ReleaseResult) result := new(ReleaseResult)
if release.Info != nil { if release.Info != nil {
result.status = release.Info.Status.String() result.Status = release.Info.Status.String()
} }
return result, nil 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) s.upgrader.SetConfig(ucfg)
release, err := s.upgrader.Run(ucfg.Name, ch, vals) release, err := s.upgrader.Run(ucfg.Name, ch, vals)
if err != nil { if err != nil {
@ -133,12 +133,12 @@ func (s Service) upgradeRelease(ucfg ReleaseConfig, ch *chart.Chart, vals chartV
} }
result := new(ReleaseResult) result := new(ReleaseResult)
if release.Info != nil { if release.Info != nil {
result.status = release.Info.Status.String() result.Status = release.Info.Status.String()
} }
return result, nil 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, ".") || if strings.HasPrefix(icfg.ChartName, ".") ||
strings.HasPrefix(icfg.ChartName, "/") { strings.HasPrefix(icfg.ChartName, "/") {
return errors.New("cannot refer local chart") return errors.New("cannot refer local chart")
@ -146,12 +146,34 @@ func (s Service) validate(icfg ReleaseConfig, values chartValues) error {
return nil return nil
} }
func NewService(settings *cli.EnvSettings, cl chartloader, i installer, u upgrader, h history) Service { func (s Service) List(releaseStatus string) ([]Release, error) {
return Service{ listStates := new(action.ListStates)
settings: settings,
chartloader: cl, state := action.ListAll
installer: i, if releaseStatus != "" {
upgrader: u, state = listStates.FromName(releaseStatus)
history: h, }
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}
} }

@ -1,8 +1,10 @@
package api package api_test
import ( import (
"context" "context"
"errors" "errors"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/api"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -19,33 +21,35 @@ import (
type ServiceTestSuite struct { type ServiceTestSuite struct {
suite.Suite suite.Suite
ctx context.Context ctx context.Context
installer *mockInstaller
upgrader *mockUpgrader upgrader *mockUpgrader
history *mockHistory history *mockHistory
installer *mockInstall
chartloader *mockChartLoader chartloader *mockChartLoader
svc Service lister *mockList
svc api.Service
settings *cli.EnvSettings settings *cli.EnvSettings
} }
func (s *ServiceTestSuite) SetupTest() { func (s *ServiceTestSuite) SetupTest() {
logger.Setup("") logger.Setup("")
s.ctx = context.Background() s.settings = &cli.EnvSettings{}
s.installer = new(mockInstaller) s.chartloader = new(mockChartLoader)
s.lister = new(mockList)
s.installer = new(mockInstall)
s.upgrader = new(mockUpgrader) s.upgrader = new(mockUpgrader)
s.history = new(mockHistory) s.history = new(mockHistory)
s.chartloader = new(mockChartLoader) s.ctx = context.Background()
s.settings = &cli.EnvSettings{} s.svc = api.NewService(s.settings, s.chartloader, s.lister, s.installer, s.upgrader, s.history)
s.svc = NewService(s.settings, s.chartloader, s.installer, s.upgrader, s.history)
} }
func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnInvalidChart() { func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnInvalidChart() {
chartName := "stable/invalid-chart" chartName := "stable/invalid-chart"
cfg := ReleaseConfig{ cfg := api.ReleaseConfig{
Name: "some-component", Name: "some-component",
Namespace: "hermes", Namespace: "hermes",
ChartName: chartName, ChartName: chartName,
} }
var vals chartValues var vals api.ChartValues
s.chartloader.On("LocateChart", chartName, s.settings).Return("", errors.New("Unable to find chart")) s.chartloader.On("LocateChart", chartName, s.settings).Return("", errors.New("Unable to find chart"))
res, err := s.svc.Install(s.ctx, cfg, vals) res, err := s.svc.Install(s.ctx, cfg, vals)
@ -60,12 +64,12 @@ func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnInvalidChart() {
func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnLocalChartReference() { func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnLocalChartReference() {
chartName := "./some/local-chart" chartName := "./some/local-chart"
cfg := ReleaseConfig{ cfg := api.ReleaseConfig{
Name: "some-component", Name: "some-component",
Namespace: "hermes", Namespace: "hermes",
ChartName: chartName, ChartName: chartName,
} }
var vals chartValues var vals api.ChartValues
res, err := s.svc.Install(s.ctx, cfg, vals) res, err := s.svc.Install(s.ctx, cfg, vals)
@ -79,7 +83,7 @@ func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnLocalChartReference() {
func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnFailedInstallRun() { func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnFailedInstallRun() {
chartName := "stable/valid-chart" chartName := "stable/valid-chart"
cfg := ReleaseConfig{ cfg := api.ReleaseConfig{
Name: "some-component", Name: "some-component",
Namespace: "hermes", Namespace: "hermes",
ChartName: chartName, ChartName: chartName,
@ -101,7 +105,7 @@ func (s *ServiceTestSuite) TestInstallShouldReturnErrorOnFailedInstallRun() {
func (s *ServiceTestSuite) TestInstallShouldReturnResultOnSuccess() { func (s *ServiceTestSuite) TestInstallShouldReturnResultOnSuccess() {
chartName := "stable/valid-chart" chartName := "stable/valid-chart"
cfg := ReleaseConfig{ cfg := api.ReleaseConfig{
Name: "some-component", Name: "some-component",
Namespace: "hermes", Namespace: "hermes",
ChartName: chartName, ChartName: chartName,
@ -117,14 +121,14 @@ func (s *ServiceTestSuite) TestInstallShouldReturnResultOnSuccess() {
t := s.T() t := s.T()
assert.NoError(t, err) assert.NoError(t, err)
require.NotNil(t, res) require.NotNil(t, res)
assert.Equal(t, res.status, "deployed") assert.Equal(t, res.Status, "deployed")
s.chartloader.AssertExpectations(t) s.chartloader.AssertExpectations(t)
s.installer.AssertExpectations(t) s.installer.AssertExpectations(t)
} }
func (s *ServiceTestSuite) TestUpgradeInstallTrueShouldInstallChart() { func (s *ServiceTestSuite) TestUpgradeInstallTrueShouldInstallChart() {
chartName := "stable/valid-chart" chartName := "stable/valid-chart"
cfg := ReleaseConfig{ cfg := api.ReleaseConfig{
Name: "some-component", Name: "some-component",
Namespace: "hermes", Namespace: "hermes",
ChartName: chartName, ChartName: chartName,
@ -142,14 +146,14 @@ func (s *ServiceTestSuite) TestUpgradeInstallTrueShouldInstallChart() {
t := s.T() t := s.T()
assert.NoError(t, err) assert.NoError(t, err)
require.NotNil(t, res) require.NotNil(t, res)
assert.Equal(t, res.status, "deployed") assert.Equal(t, res.Status, "deployed")
s.chartloader.AssertExpectations(t) s.chartloader.AssertExpectations(t)
s.installer.AssertExpectations(t) s.installer.AssertExpectations(t)
} }
func (s *ServiceTestSuite) TestUpgradeInstallFalseShouldNotInstallChart() { func (s *ServiceTestSuite) TestUpgradeInstallFalseShouldNotInstallChart() {
chartName := "stable/valid-chart" chartName := "stable/valid-chart"
cfg := ReleaseConfig{ cfg := api.ReleaseConfig{
Name: "some-component", Name: "some-component",
Namespace: "hermes", Namespace: "hermes",
ChartName: chartName, ChartName: chartName,
@ -168,14 +172,14 @@ func (s *ServiceTestSuite) TestUpgradeInstallFalseShouldNotInstallChart() {
require.NotNil(t, res) require.NotNil(t, res)
s.installer.AssertNotCalled(t, "Run") s.installer.AssertNotCalled(t, "Run")
s.history.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.chartloader.AssertExpectations(t)
s.installer.AssertExpectations(t) s.installer.AssertExpectations(t)
} }
func (s *ServiceTestSuite) TestUpgradeShouldReturnErrorOnFailedUpgradeRun() { func (s *ServiceTestSuite) TestUpgradeShouldReturnErrorOnFailedUpgradeRun() {
chartName := "stable/valid-chart" chartName := "stable/valid-chart"
cfg := ReleaseConfig{ cfg := api.ReleaseConfig{
Name: "some-component", Name: "some-component",
Namespace: "hermes", Namespace: "hermes",
ChartName: chartName, ChartName: chartName,
@ -197,7 +201,7 @@ func (s *ServiceTestSuite) TestUpgradeShouldReturnErrorOnFailedUpgradeRun() {
func (s *ServiceTestSuite) TestUpgradeShouldReturnResultOnSuccess() { func (s *ServiceTestSuite) TestUpgradeShouldReturnResultOnSuccess() {
chartName := "stable/valid-chart" chartName := "stable/valid-chart"
cfg := ReleaseConfig{ cfg := api.ReleaseConfig{
Name: "some-component", Name: "some-component",
Namespace: "hermes", Namespace: "hermes",
ChartName: chartName, ChartName: chartName,
@ -213,52 +217,127 @@ func (s *ServiceTestSuite) TestUpgradeShouldReturnResultOnSuccess() {
t := s.T() t := s.T()
assert.NoError(t, err) assert.NoError(t, err)
require.NotNil(t, res) require.NotNil(t, res)
assert.Equal(t, res.status, "deployed") assert.Equal(t, res.Status, "deployed")
s.chartloader.AssertExpectations(t) s.chartloader.AssertExpectations(t)
s.upgrader.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) { var releases []*release.Release
args := m.Called(name, settings) releases = append(releases,
return args.String(0), args.Error(1) &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) 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) args := m.Called(c, vals)
return args.Get(0).(*release.Release), args.Error(1) return args.Get(0).(*release.Release), args.Error(1)
} }
func (m *mockUpgrader) GetInstall() bool {
args := m.Called() type mockChartLoader struct{ mock.Mock }
return args.Get(0).(bool)
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) { func (m *mockUpgrader) Run(name string, chart *chart.Chart, vals map[string]interface{}) (*release.Release, error) {
args := m.Called(name, chart, vals) args := m.Called(name, chart, vals)
return args.Get(0).(*release.Release), args.Error(1) 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) _ = 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) { func (m *mockHistory) Run(name string) ([]*release.Release, error) {
args := m.Called(name) args := m.Called(name)
return args.Get(0).([]*release.Release), args.Error(1) return args.Get(0).([]*release.Release), args.Error(1)
} }
func TestServiceSuite(t *testing.T) {
suite.Run(t, new(ServiceTestSuite))
}

@ -30,6 +30,7 @@ func Upgrade(svc Service) http.Handler {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return return
} }
defer r.Body.Close() defer r.Body.Close()
var response UpgradeResponse var response UpgradeResponse
cfg := ReleaseConfig{ChartName: req.Chart, Name: req.Name, Namespace: req.Namespace} 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) respondError(w, "error while upgrading chart: %v", err)
return return
} }
response.Status = res.status response.Status = res.Status
if err := json.NewEncoder(w).Encode(&response); err != nil { if err := json.NewEncoder(w).Encode(&response); err != nil {
logger.Errorf("[Upgrade] error writing response %v", err) logger.Errorf("[Upgrade] error writing response %v", err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)

@ -15,7 +15,6 @@ import (
"gotest.tools/assert" "gotest.tools/assert"
"helm.sh/helm/v3/pkg/api" "helm.sh/helm/v3/pkg/api"
"helm.sh/helm/v3/pkg/api/logger" "helm.sh/helm/v3/pkg/api/logger"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/release"
) )
@ -43,7 +42,7 @@ func (s *UpgradeTestSuite) SetupTest() {
RepositoryConfig: "./testdata/helm", RepositoryConfig: "./testdata/helm",
PluginsDirectory: "./testdata/helm/plugin", 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) handler := api.Upgrade(service)
s.server = httptest.NewServer(handler) s.server = httptest.NewServer(handler)
} }
@ -105,26 +104,3 @@ func (s *UpgradeTestSuite) TearDownTest() {
func TestUpgradeAPI(t *testing.T) { func TestUpgradeAPI(t *testing.T) {
suite.Run(t, new(UpgradeTestSuite)) 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)
}

Loading…
Cancel
Save