Adding api test for successful deployment of chart

pull/8330/head
devdinu 5 years ago
parent 87abc3b47f
commit cacf215d6c

@ -4,6 +4,7 @@ import (
"fmt"
"net/http"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/http/api"
"helm.sh/helm/v3/pkg/http/api/list"
"helm.sh/helm/v3/pkg/http/api/logger"
@ -22,7 +23,9 @@ func startServer(appconfig *servercontext.Application) {
//TODO: use gorilla mux and add middleware to write content type and other headers
app := servercontext.App()
logger.Setup("debug")
service := api.NewService(app.Config, app.ActionConfig)
actionInstall := action.NewInstall(app.ActionConfig)
service := api.NewService(app.Config, new(action.ChartPathOptions), api.NewInstaller(actionInstall))
router.Handle("/ping", ping.Handler())
router.Handle("/list", list.Handler())
router.Handle("/install", api.Install(service))

@ -53,6 +53,7 @@ require (
google.golang.org/appengine v1.6.5 // indirect
google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6 // indirect
google.golang.org/grpc v1.24.0 // indirect
gotest.tools v2.2.0+incompatible
k8s.io/api v0.17.1
k8s.io/apiextensions-apiserver v0.17.1
k8s.io/apimachinery v0.17.1

@ -469,6 +469,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=

@ -16,7 +16,7 @@ type InstallRequest struct {
type InstallResponse struct {
Error string `json:"error,omitempty"`
Status string
Status string `json:"status"`
}
// RODO: we could use interface as well if everything's in same package
@ -40,7 +40,7 @@ func Install(svc Service) http.Handler {
return
}
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)
w.WriteHeader(http.StatusInternalServerError)
return

@ -0,0 +1,98 @@
package api_test
import (
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"gotest.tools/assert"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/http/api"
"helm.sh/helm/v3/pkg/http/api/logger"
"helm.sh/helm/v3/pkg/release"
)
type InstallerTestSuite struct {
suite.Suite
recorder *httptest.ResponseRecorder
server *httptest.Server
mockInstaller *mockInstaller
mockChartLoader *mockChartLoader
appConfig *cli.EnvSettings
}
func (s *InstallerTestSuite) SetupSuite() {
logger.Setup("default")
}
func (s *InstallerTestSuite) SetupTest() {
s.recorder = httptest.NewRecorder()
s.mockInstaller = new(mockInstaller)
s.mockChartLoader = new(mockChartLoader)
s.appConfig = &cli.EnvSettings{
RepositoryConfig: "./testdata/helm",
PluginsDirectory: "./testdata/helm/plugin",
}
service := api.NewService(s.appConfig, s.mockChartLoader, s.mockInstaller)
handler := api.Install(service)
s.server = httptest.NewServer(handler)
}
func (s *InstallerTestSuite) TestShouldReturnDeployedStatusOnSuccessfulInstall() {
chartName := "stable/redis-ha"
body := fmt.Sprintf(`{
"chart":"%s",
"name": "redis-v5",
"namespace": "something"}`, chartName)
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)
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)
resp, err := http.DefaultClient.Do(req)
assert.Equal(s.T(), 200, resp.StatusCode)
expectedResponse := `{"status":"deployed"}` + "\n"
respBody, _ := ioutil.ReadAll(resp.Body)
assert.Equal(s.T(), expectedResponse, string(respBody))
require.NoError(s.T(), err)
s.mockInstaller.AssertExpectations(s.T())
s.mockChartLoader.AssertExpectations(s.T())
}
func (s *InstallerTestSuite) TearDownTest() {
s.server.Close()
}
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)
}

@ -0,0 +1,24 @@
package api
import (
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/release"
)
type Installer struct {
*action.Install
}
type runner interface {
Run(*chart.Chart, map[string]interface{}) (*release.Release, error)
}
func (i *Installer) SetConfig(cfg InstallConfig) {
i.ReleaseName = cfg.Name
i.Namespace = cfg.Namespace
}
func NewInstaller(ai *action.Install) *Installer {
return &Installer{ai}
}

@ -4,21 +4,27 @@ import (
"context"
"fmt"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/chart/loader"
"helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/cli/values"
"helm.sh/helm/v3/pkg/getter"
"helm.sh/helm/v3/pkg/http/api/logger"
"helm.sh/helm/v3/pkg/servercontext"
"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
actionConfig *action.Configuration
chartloader *action.ChartPathOptions
settings *cli.EnvSettings
installer
chartloader
}
type InstallConfig struct {
@ -34,10 +40,11 @@ type installResult struct {
}
func (s Service) getValues(vals chartValues) (chartValues, error) {
valueOpts := &values.Options{}
// valueOpts := &values.Options{}
//valueOpts.Values = append(valueOpts.Values, vals)
//TODO: we need to make this as Provider, so it'll be able to merge
return valueOpts.MergeValues(getter.All(servercontext.App().Config))
// why do we need getter.ALl?
return vals, nil
}
func (s Service) Install(ctx context.Context, cfg InstallConfig, values chartValues) (*installResult, error) {
@ -66,26 +73,22 @@ func (s Service) loadChart(chartName string) (*chart.Chart, error) {
}
func (s Service) installChart(icfg InstallConfig, ch *chart.Chart, vals chartValues) (*installResult, error) {
install := action.NewInstall(s.actionConfig)
install.Namespace = icfg.Namespace
install.ReleaseName = icfg.Name
release, err := install.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)
}
result := new(installResult)
fmt.Println(result)
if release.Info != nil {
result.status = release.Info.Status.String()
}
return result, nil
}
func NewService(settings *cli.EnvSettings, actionConfig *action.Configuration) Service {
func NewService(settings *cli.EnvSettings, cl chartloader, i installer) Service {
return Service{
settings: settings,
actionConfig: actionConfig,
chartloader: new(action.ChartPathOptions),
settings: settings,
chartloader: cl,
installer: i,
}
}

@ -0,0 +1,4 @@
name: albatross
description: A Helm chart for Kubernetes
version: 0.1.0
home: ""

@ -0,0 +1,4 @@
albatross: "true"
global:
author: Coleridge
Loading…
Cancel
Save