فهرست منبع

testing setup with agents

Alexander Belanger 5 سال پیش
والد
کامیت
1293b69ee6

+ 1 - 1
cmd/app/main.go

@@ -34,7 +34,7 @@ func main() {
 
 	validator := vr.New()
 
-	a := api.New(logger, repo, validator, store, &appConf.Helm, appConf.Server.CookieName)
+	a := api.New(logger, repo, validator, store, appConf.Server.CookieName, false)
 
 	appRouter := router.New(a, store, appConf.Server.CookieName)
 

+ 4 - 4
internal/config/config.go

@@ -12,7 +12,7 @@ type Conf struct {
 	Debug  bool `env:"DEBUG,default=false"`
 	Server ServerConf
 	Db     DBConf
-	Helm   HelmGlobalConf
+	K8s    K8sConf
 }
 
 // ServerConf is the server configuration
@@ -35,9 +35,9 @@ type DBConf struct {
 	DbName   string `env:"DB_NAME,default=porter"`
 }
 
-// HelmGlobalConf is the global configuration for the Helm agent
-type HelmGlobalConf struct {
-	IsTesting bool `env:"HELM_IS_TESTING,default=false"`
+// K8sConf is the global configuration for the k8s agents
+type K8sConf struct {
+	IsTesting bool `env:"K8S_IS_TESTING,default=false"`
 }
 
 // FromEnv generates a configuration from environment variables

+ 0 - 76
internal/helm/agent.go

@@ -1,20 +1,8 @@
 package helm
 
 import (
-	"errors"
-	"io/ioutil"
-
-	"github.com/porter-dev/porter/internal/config"
-	"github.com/porter-dev/porter/internal/kubernetes"
-	"github.com/porter-dev/porter/internal/logger"
 	"helm.sh/helm/v3/pkg/action"
-	"helm.sh/helm/v3/pkg/kube"
 	"helm.sh/helm/v3/pkg/release"
-	"helm.sh/helm/v3/pkg/storage"
-
-	"helm.sh/helm/v3/pkg/chartutil"
-	kubefake "helm.sh/helm/v3/pkg/kube/fake"
-	k8s "k8s.io/client-go/kubernetes"
 )
 
 // Agent is a Helm agent for performing helm operations
@@ -22,70 +10,6 @@ type Agent struct {
 	ActionConfig *action.Configuration
 }
 
-// Form represents the options for connecting to a cluster and
-// creating a Helm agent
-type Form struct {
-	KubeConfig      []byte
-	AllowedContexts []string
-	Context         string `json:"context" form:"required"`
-	Storage         string `json:"storage" form:"oneof=secret configmap memory"`
-	Namespace       string `json:"namespace"`
-}
-
-// ToAgent uses the Form to generate an agent. Setting testing=true will create
-// a test agent with in-memory storage
-func (h *Form) ToAgent(
-	l *logger.Logger,
-	helmConf *config.HelmGlobalConf,
-	storage *storage.Storage,
-) (*Agent, error) {
-	if helmConf.IsTesting {
-		testStorage := storage
-
-		if testStorage == nil {
-			testStorage = StorageMap["memory"](nil, h.Namespace, nil)
-		}
-
-		return &Agent{&action.Configuration{
-			Releases: testStorage,
-			KubeClient: &kubefake.FailingKubeClient{
-				PrintingKubeClient: kubefake.PrintingKubeClient{
-					Out: ioutil.Discard,
-				},
-			},
-			Capabilities: chartutil.DefaultCapabilities,
-			Log:          l.Printf,
-		}}, nil
-	}
-
-	// create a kubernetes agent
-	conf := &kubernetes.OutOfClusterConfig{
-		KubeConfig:      h.KubeConfig,
-		AllowedContexts: h.AllowedContexts,
-		Context:         h.Context,
-	}
-
-	k8sAgent, err := kubernetes.AgentFromOutOfClusterConfig(conf)
-
-	if err != nil {
-		return nil, err
-	}
-
-	clientset, ok := k8sAgent.Clientset.(*k8s.Clientset)
-
-	if !ok {
-		return nil, errors.New("Agent Clientset was not of type *(k8s.io/client-go/kubernetes).Clientset")
-	}
-
-	// use k8s agent to create Helm agent
-	return &Agent{&action.Configuration{
-		RESTClientGetter: k8sAgent.RESTClientGetter,
-		KubeClient:       kube.New(k8sAgent.RESTClientGetter),
-		Releases:         StorageMap[h.Storage](l, h.Namespace, clientset),
-		Log:              l.Printf,
-	}}, nil
-}
-
 // ListReleases lists releases based on a ListFilter
 func (a *Agent) ListReleases(
 	namespace string,

+ 3 - 7
internal/helm/agent_test.go

@@ -5,7 +5,6 @@ import (
 
 	"helm.sh/helm/v3/pkg/storage/driver"
 
-	"github.com/porter-dev/porter/internal/config"
 	"github.com/porter-dev/porter/internal/helm"
 	"github.com/porter-dev/porter/internal/logger"
 
@@ -17,15 +16,12 @@ func newAgentFixture(t *testing.T, namespace string) *helm.Agent {
 	t.Helper()
 
 	l := logger.NewConsole(true)
-	opts := &helm.Form{
+
+	form := &helm.Form{
 		Namespace: namespace,
 	}
 
-	agent, _ := opts.ToAgent(l, &config.HelmGlobalConf{
-		IsTesting: true,
-	}, nil)
-
-	return agent
+	return helm.GetAgentTesting(form, nil, l)
 }
 
 type releaseStub struct {

+ 101 - 0
internal/helm/config.go

@@ -0,0 +1,101 @@
+package helm
+
+import (
+	"errors"
+	"io/ioutil"
+
+	"github.com/porter-dev/porter/internal/kubernetes"
+	"github.com/porter-dev/porter/internal/logger"
+	"helm.sh/helm/v3/pkg/action"
+	"helm.sh/helm/v3/pkg/chartutil"
+	"helm.sh/helm/v3/pkg/kube"
+	kubefake "helm.sh/helm/v3/pkg/kube/fake"
+	"helm.sh/helm/v3/pkg/storage"
+	k8s "k8s.io/client-go/kubernetes"
+)
+
+// Form represents the options for connecting to a cluster and
+// creating a Helm agent
+type Form struct {
+	KubeConfig      []byte
+	AllowedContexts []string
+	Context         string `json:"context" form:"required"`
+	Storage         string `json:"storage" form:"oneof=secret configmap memory"`
+	Namespace       string `json:"namespace"`
+}
+
+// GetAgentOutOfClusterConfig creates a new Agent from outside the cluster using
+// the underlying kubernetes.GetAgentOutOfClusterConfig method
+func GetAgentOutOfClusterConfig(form *Form, l *logger.Logger) (*Agent, error) {
+	// create a kubernetes agent
+	conf := &kubernetes.OutOfClusterConfig{
+		KubeConfig:      form.KubeConfig,
+		AllowedContexts: form.AllowedContexts,
+		Context:         form.Context,
+	}
+
+	k8sAgent, err := kubernetes.GetAgentOutOfClusterConfig(conf)
+
+	if err != nil {
+		return nil, err
+	}
+
+	clientset, ok := k8sAgent.Clientset.(*k8s.Clientset)
+
+	if !ok {
+		return nil, errors.New("Agent Clientset was not of type *(k8s.io/client-go/kubernetes).Clientset")
+	}
+
+	// use k8s agent to create Helm agent
+	return &Agent{&action.Configuration{
+		RESTClientGetter: k8sAgent.RESTClientGetter,
+		KubeClient:       kube.New(k8sAgent.RESTClientGetter),
+		Releases:         StorageMap[form.Storage](l, form.Namespace, clientset),
+		Log:              l.Printf,
+	}}, nil
+}
+
+// GetAgentInClusterConfig creates a new Agent from inside the cluster using
+// the underlying kubernetes.GetAgentInClusterConfig method
+func GetAgentInClusterConfig(form *Form, l *logger.Logger) (*Agent, error) {
+	// create a kubernetes agent
+	k8sAgent, err := kubernetes.GetAgentInClusterConfig()
+
+	if err != nil {
+		return nil, err
+	}
+
+	clientset, ok := k8sAgent.Clientset.(*k8s.Clientset)
+
+	if !ok {
+		return nil, errors.New("Agent Clientset was not of type *(k8s.io/client-go/kubernetes).Clientset")
+	}
+
+	// use k8s agent to create Helm agent
+	return &Agent{&action.Configuration{
+		RESTClientGetter: k8sAgent.RESTClientGetter,
+		KubeClient:       kube.New(k8sAgent.RESTClientGetter),
+		Releases:         StorageMap[form.Storage](l, form.Namespace, clientset),
+		Log:              l.Printf,
+	}}, nil
+}
+
+// GetAgentTesting creates a new Agent using an optional existing storage class
+func GetAgentTesting(form *Form, storage *storage.Storage, l *logger.Logger) *Agent {
+	testStorage := storage
+
+	if testStorage == nil {
+		testStorage = StorageMap["memory"](nil, form.Namespace, nil)
+	}
+
+	return &Agent{&action.Configuration{
+		Releases: testStorage,
+		KubeClient: &kubefake.FailingKubeClient{
+			PrintingKubeClient: kubefake.PrintingKubeClient{
+				Out: ioutil.Discard,
+			},
+		},
+		Capabilities: chartutil.DefaultCapabilities,
+		Log:          l.Printf,
+	}}
+}

+ 0 - 0
internal/helm/driver.go → internal/helm/storage.go


+ 1 - 2
internal/kubernetes/agent_test.go

@@ -7,7 +7,6 @@ import (
 	"k8s.io/apimachinery/pkg/api/meta"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/client-go/discovery"
-	"k8s.io/client-go/kubernetes/fake"
 	"k8s.io/client-go/rest"
 	"k8s.io/client-go/tools/clientcmd"
 
@@ -36,7 +35,7 @@ func (f *fakeRESTClientGetter) ToRESTMapper() (meta.RESTMapper, error) {
 func newAgentFixture(t *testing.T, objects ...runtime.Object) *kubernetes.Agent {
 	t.Helper()
 
-	return &kubernetes.Agent{&fakeRESTClientGetter{}, fake.NewSimpleClientset(objects...)}
+	return kubernetes.GetAgentTesting(objects...)
 }
 
 func TestOutOfClusterConfig(t *testing.T) {

+ 32 - 7
internal/kubernetes/config.go

@@ -7,18 +7,20 @@ import (
 	"time"
 
 	"k8s.io/apimachinery/pkg/api/meta"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/cli-runtime/pkg/genericclioptions"
 	"k8s.io/client-go/discovery"
 	diskcached "k8s.io/client-go/discovery/cached/disk"
 	"k8s.io/client-go/kubernetes"
+	"k8s.io/client-go/kubernetes/fake"
 	"k8s.io/client-go/rest"
 	"k8s.io/client-go/restmapper"
 	"k8s.io/client-go/tools/clientcmd"
 	"k8s.io/client-go/util/homedir"
 )
 
-// AgentFromOutOfClusterConfig creates a new Agent using the OutOfClusterConfig
-func AgentFromOutOfClusterConfig(conf *OutOfClusterConfig) (*Agent, error) {
+// GetAgentOutOfClusterConfig creates a new Agent using the OutOfClusterConfig
+func GetAgentOutOfClusterConfig(conf *OutOfClusterConfig) (*Agent, error) {
 	restConf, err := conf.ToRESTConfig()
 
 	if err != nil {
@@ -34,21 +36,26 @@ func AgentFromOutOfClusterConfig(conf *OutOfClusterConfig) (*Agent, error) {
 	return &Agent{conf, clientset}, nil
 }
 
-// AgentFromInClusterConfig uses the service account that kubernetes
+// GetAgentInClusterConfig uses the service account that kubernetes
 // gives to pods to connect
-func AgentFromInClusterConfig() (*Agent, error) {
+func GetAgentInClusterConfig() (*Agent, error) {
 	conf, err := rest.InClusterConfig()
 
 	if err != nil {
 		return nil, err
 	}
 
-	restClientGetter := NewRESTClientGetterFromInClusterConfig(conf)
+	restClientGetter := newRESTClientGetterFromInClusterConfig(conf)
 	clientset, err := kubernetes.NewForConfig(conf)
 
 	return &Agent{restClientGetter, clientset}, nil
 }
 
+// GetAgentTesting creates a new Agent using an optional existing storage class
+func GetAgentTesting(objects ...runtime.Object) *Agent {
+	return &Agent{&fakeRESTClientGetter{}, fake.NewSimpleClientset(objects...)}
+}
+
 // OutOfClusterConfig is the set of parameters required for an out-of-cluster connection.
 // This implements RESTClientGetter
 type OutOfClusterConfig struct {
@@ -119,9 +126,9 @@ func (conf *OutOfClusterConfig) ToRESTMapper() (meta.RESTMapper, error) {
 	return expander, nil
 }
 
-// NewRESTClientGetterFromInClusterConfig returns a RESTClientGetter using
+// newRESTClientGetterFromInClusterConfig returns a RESTClientGetter using
 // default values set from the *rest.Config
-func NewRESTClientGetterFromInClusterConfig(conf *rest.Config) genericclioptions.RESTClientGetter {
+func newRESTClientGetterFromInClusterConfig(conf *rest.Config) genericclioptions.RESTClientGetter {
 	cfs := genericclioptions.NewConfigFlags(false)
 
 	cfs.ClusterName = &conf.ServerName
@@ -143,3 +150,21 @@ func NewRESTClientGetterFromInClusterConfig(conf *rest.Config) genericclioptions
 func stringptr(val string) *string {
 	return &val
 }
+
+type fakeRESTClientGetter struct{}
+
+func (f *fakeRESTClientGetter) ToRESTConfig() (*rest.Config, error) {
+	return nil, nil
+}
+
+func (f *fakeRESTClientGetter) ToRawKubeConfigLoader() clientcmd.ClientConfig {
+	return nil
+}
+
+func (f *fakeRESTClientGetter) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
+	return nil, nil
+}
+
+func (f *fakeRESTClientGetter) ToRESTMapper() (meta.RESTMapper, error) {
+	return nil, nil
+}

+ 27 - 8
server/api/api.go

@@ -5,12 +5,20 @@ import (
 	ut "github.com/go-playground/universal-translator"
 	"github.com/go-playground/validator/v10"
 	sessionstore "github.com/porter-dev/porter/internal/auth"
-	"github.com/porter-dev/porter/internal/config"
+	"github.com/porter-dev/porter/internal/helm"
+	"github.com/porter-dev/porter/internal/kubernetes"
 	lr "github.com/porter-dev/porter/internal/logger"
 	"github.com/porter-dev/porter/internal/repository"
 	"helm.sh/helm/v3/pkg/storage"
 )
 
+// TestAgents are the k8s agents used for testing
+type TestAgents struct {
+	HelmAgent             *helm.Agent
+	HelmTestStorageDriver *storage.Storage
+	K8sAgent              *kubernetes.Agent
+}
+
 // App represents an API instance with handler methods attached, a DB connection
 // and a logger instance
 type App struct {
@@ -19,11 +27,9 @@ type App struct {
 	validator  *validator.Validate
 	store      *sessionstore.PGStore
 	translator *ut.Translator
-	helmConf   *config.HelmGlobalConf
-	// HelmTestStorageDriver is used by testing libraries to query the in-memory
-	// Helm storage driver
-	HelmTestStorageDriver *storage.Storage
-	cookieName            string
+	cookieName string
+	testing    bool
+	TestAgents *TestAgents
 }
 
 // New returns a new App instance
@@ -32,8 +38,8 @@ func New(
 	repo *repository.Repository,
 	validator *validator.Validate,
 	store *sessionstore.PGStore,
-	helmConf *config.HelmGlobalConf,
 	cookieName string,
+	testing bool,
 ) *App {
 	// for now, will just support the english translator from the
 	// validator/translations package
@@ -41,14 +47,27 @@ func New(
 	uni := ut.New(en, en)
 	trans, _ := uni.GetTranslator("en")
 
+	var testAgents *TestAgents = nil
+
+	if testing {
+		memStorage := helm.StorageMap["memory"](nil, "", nil)
+
+		testAgents = &TestAgents{
+			HelmAgent:             helm.GetAgentTesting(&helm.Form{}, nil, logger),
+			HelmTestStorageDriver: memStorage,
+			K8sAgent:              kubernetes.GetAgentTesting(),
+		}
+	}
+
 	return &App{
 		logger:     logger,
 		repo:       repo,
 		validator:  validator,
 		store:      store,
 		translator: &trans,
-		helmConf:   helmConf,
 		cookieName: cookieName,
+		testing:    testing,
+		TestAgents: testAgents,
 	}
 }
 

+ 16 - 2
server/api/chart_handler.go

@@ -7,6 +7,7 @@ import (
 
 	"github.com/go-chi/chi"
 	"github.com/porter-dev/porter/internal/forms"
+	"github.com/porter-dev/porter/internal/helm"
 )
 
 // Enumeration of chart API error codes, represented as int64
@@ -35,7 +36,14 @@ func (app *App) HandleListCharts(w http.ResponseWriter, r *http.Request) {
 	}
 
 	// create a new agent
-	agent, err := form.HelmOptions.ToAgent(app.logger, app.helmConf, app.HelmTestStorageDriver)
+	var agent *helm.Agent
+	var err error
+
+	if app.testing {
+		agent = app.TestAgents.HelmAgent
+	} else {
+		agent, err = helm.GetAgentOutOfClusterConfig(form.HelmOptions, app.logger)
+	}
 
 	releases, err := agent.ListReleases(form.HelmOptions.Namespace, form.ListFilter)
 
@@ -82,7 +90,13 @@ func (app *App) HandleGetChart(w http.ResponseWriter, r *http.Request) {
 	}
 
 	// create a new agent
-	agent, err := form.HelmOptions.ToAgent(app.logger, app.helmConf, app.HelmTestStorageDriver)
+	var agent *helm.Agent
+
+	if app.testing {
+		agent = app.TestAgents.HelmAgent
+	} else {
+		agent, err = helm.GetAgentOutOfClusterConfig(form.HelmOptions, app.logger)
+	}
 
 	release, err := agent.GetRelease(form.Name, form.Revision)
 

+ 2 - 19
server/api/chart_handler_test.go

@@ -7,13 +7,10 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/porter-dev/porter/internal/config"
 	"github.com/porter-dev/porter/internal/helm"
-	"github.com/porter-dev/porter/internal/logger"
 
 	"helm.sh/helm/v3/pkg/chart"
 	"helm.sh/helm/v3/pkg/release"
-	"helm.sh/helm/v3/pkg/storage"
 	"helm.sh/helm/v3/pkg/storage/driver"
 )
 
@@ -42,8 +39,7 @@ type chartTest struct {
 func testChartRequests(t *testing.T, tests []*chartTest, canQuery bool) {
 	for _, c := range tests {
 		// create a new tester
-		storage := helm.StorageMap["memory"](nil, "", nil)
-		tester := newTester(canQuery, storage)
+		tester := newTester(canQuery)
 
 		// if there's an initializer, call it
 		for _, init := range c.initializers {
@@ -154,7 +150,7 @@ func TestHandleGetChart(t *testing.T) {
 func initDefaultCharts(tester *tester) {
 	initUserDefault(tester)
 
-	agent := newAgentFixture("default", tester.app.HelmTestStorageDriver)
+	agent := tester.app.TestAgents.HelmAgent
 
 	makeReleases(agent, sampleReleaseStubs)
 
@@ -163,19 +159,6 @@ func initDefaultCharts(tester *tester) {
 	agent.ActionConfig.Releases.Driver.(*driver.Memory).SetNamespace("")
 }
 
-func newAgentFixture(namespace string, storage *storage.Storage) *helm.Agent {
-	l := logger.NewConsole(true)
-	opts := &helm.Form{
-		Namespace: namespace,
-	}
-
-	agent, _ := opts.ToAgent(l, &config.HelmGlobalConf{
-		IsTesting: true,
-	}, storage)
-
-	return agent
-}
-
 var sampleReleaseStubs = []releaseStub{
 	releaseStub{"airwatch", "default", 1, "1.0.0", release.StatusDeployed},
 	releaseStub{"not-in-default-namespace", "other", 1, "1.0.1", release.StatusDeployed},

+ 3 - 5
server/api/helpers_test.go

@@ -13,7 +13,6 @@ import (
 	"github.com/porter-dev/porter/internal/repository/test"
 	"github.com/porter-dev/porter/server/api"
 	"github.com/porter-dev/porter/server/router"
-	"helm.sh/helm/v3/pkg/storage"
 
 	sessionstore "github.com/porter-dev/porter/internal/auth"
 	vr "github.com/porter-dev/porter/internal/validator"
@@ -55,7 +54,7 @@ func (t *tester) createUserSession(email string, pw string) {
 	t.reset()
 }
 
-func newTester(canQuery bool, storage *storage.Storage) *tester {
+func newTester(canQuery bool) *tester {
 	appConf := config.Conf{
 		Debug: true,
 		Server: config.ServerConf{
@@ -69,7 +68,7 @@ func newTester(canQuery bool, storage *storage.Storage) *tester {
 		// unimportant here
 		Db: config.DBConf{},
 		// set the helm config to testing
-		Helm: config.HelmGlobalConf{
+		K8s: config.K8sConf{
 			IsTesting: true,
 		},
 	}
@@ -80,8 +79,7 @@ func newTester(canQuery bool, storage *storage.Storage) *tester {
 	repo := test.NewRepository(canQuery)
 
 	store, _ := sessionstore.NewStore(repo, appConf.Server)
-	app := api.New(logger, repo, validator, store, &appConf.Helm, appConf.Server.CookieName)
-	app.HelmTestStorageDriver = storage
+	app := api.New(logger, repo, validator, store, appConf.Server.CookieName, true)
 	r := router.New(app, store, appConf.Server.CookieName)
 
 	return &tester{

+ 8 - 2
server/api/k8s_handler.go

@@ -17,7 +17,6 @@ const (
 
 // HandleListNamespaces retrieves a list of namespaces
 func (app *App) HandleListNamespaces(w http.ResponseWriter, r *http.Request) {
-	// get the filter options
 	form := &forms.K8sForm{}
 
 	// decode from JSON to form value
@@ -35,7 +34,14 @@ func (app *App) HandleListNamespaces(w http.ResponseWriter, r *http.Request) {
 	}
 
 	// create a new agent
-	agent, err := kubernetes.AgentFromOutOfClusterConfig(form.K8sOptions)
+	var agent *kubernetes.Agent
+	var err error
+
+	if app.testing {
+		agent = app.TestAgents.K8sAgent
+	} else {
+		agent, err = kubernetes.GetAgentOutOfClusterConfig(form.K8sOptions)
+	}
 
 	namespaces, err := agent.ListNamespaces()
 

+ 1 - 1
server/api/user_handler_test.go

@@ -29,7 +29,7 @@ type userTest struct {
 func testUserRequests(t *testing.T, tests []*userTest, canQuery bool) {
 	for _, c := range tests {
 		// create a new tester
-		tester := newTester(canQuery, nil)
+		tester := newTester(canQuery)
 
 		// if there's an initializer, call it
 		for _, init := range c.initializers {