Przeglądaj źródła

public integration lists

Alexander Belanger 5 lat temu
rodzic
commit
51df7e5fd0

+ 14 - 0
internal/models/integrations/aws.go

@@ -73,6 +73,20 @@ func (a *AWSIntegration) Externalize() *AWSIntegrationExternal {
 	}
 }
 
+// ToProjectIntegration converts an aws integration to a project integration
+func (a *AWSIntegration) ToProjectIntegration(
+	category string,
+	service IntegrationService,
+) *ProjectIntegration {
+	return &ProjectIntegration{
+		ID:            a.ID,
+		ProjectID:     a.ProjectID,
+		AuthMechanism: "aws",
+		Category:      category,
+		Service:       service,
+	}
+}
+
 // GetBearerToken retrieves a bearer token for an AWS account
 func (a *AWSIntegration) GetBearerToken(
 	getTokenCache GetTokenCacheFunc,

+ 14 - 0
internal/models/integrations/gcp.go

@@ -60,6 +60,20 @@ func (g *GCPIntegration) Externalize() *GCPIntegrationExternal {
 	}
 }
 
+// ToProjectIntegration converts a gcp integration to a project integration
+func (g *GCPIntegration) ToProjectIntegration(
+	category string,
+	service IntegrationService,
+) *ProjectIntegration {
+	return &ProjectIntegration{
+		ID:            g.ID,
+		ProjectID:     g.ProjectID,
+		AuthMechanism: "gcp",
+		Category:      category,
+		Service:       service,
+	}
+}
+
 // GetBearerToken retrieves a bearer token for a GCP account
 func (g *GCPIntegration) GetBearerToken(
 	getTokenCache GetTokenCacheFunc,

+ 96 - 0
internal/models/integrations/integration.go

@@ -0,0 +1,96 @@
+package integrations
+
+// IntegrationService is the name of a third-party service
+type IntegrationService string
+
+// The list of supported third-party services
+const (
+	GKE    IntegrationService = "gke"
+	EKS                       = "eks"
+	Kube                      = "kube"
+	GCR                       = "gcr"
+	ECR                       = "ecr"
+	Github                    = "github"
+	Docker                    = "docker"
+)
+
+// PorterIntegration is a supported integration service, specifying an auth
+// mechanism and the category of integration. A single service can have multiple
+// auth mechanisms. For example, a GKE integration can have both an "oauth" mechanism
+// and a "gcp" mechanism:
+//
+// PorterIntegration{
+// 	AuthMechanism: "oauth",
+// 	Category: "cluster",
+// 	Service: GKE,
+// }
+//
+// PorterIntegration{
+// 	AuthMechanism: "gcp",
+// 	Category: "cluster",
+// 	Service: GKE,
+// }
+type PorterIntegration struct {
+	AuthMechanism string             `json:"auth_mechanism"`
+	Category      string             `json:"category"`
+	Service       IntegrationService `json:"service"`
+}
+
+// PorterClusterIntegrations are the supported cluster integrations
+var PorterClusterIntegrations = []PorterIntegration{
+	PorterIntegration{
+		AuthMechanism: "gcp",
+		Category:      "cluster",
+		Service:       GKE,
+	},
+	PorterIntegration{
+		AuthMechanism: "aws",
+		Category:      "cluster",
+		Service:       EKS,
+	},
+	PorterIntegration{
+		AuthMechanism: "kube",
+		Category:      "cluster",
+		Service:       Kube,
+	},
+}
+
+// PorterRegistryIntegrations are the supported registry integrations
+var PorterRegistryIntegrations = []PorterIntegration{
+	PorterIntegration{
+		AuthMechanism: "gcp",
+		Category:      "registry",
+		Service:       GCR,
+	},
+	PorterIntegration{
+		AuthMechanism: "aws",
+		Category:      "registry",
+		Service:       ECR,
+	},
+	PorterIntegration{
+		AuthMechanism: "oauth",
+		Category:      "registry",
+		Service:       Docker,
+	},
+}
+
+// PorterRepoIntegrations are the supported repo integrations
+var PorterRepoIntegrations = []PorterIntegration{
+	PorterIntegration{
+		AuthMechanism: "oauth",
+		Category:      "repo",
+		Service:       Github,
+	},
+}
+
+// ProjectIntegration is the top-level integration object for various integrations.
+// Although the integrations are stored in the DB by auth mechanism, the integrations
+// are cast to a ProjectIntegration for consolidation before passing on to the client.
+type ProjectIntegration struct {
+	ID        uint `json:"id"`
+	ProjectID uint `json:"project_id"`
+
+	AuthMechanism string             `json:"auth_mechanism"`
+	Category      string             `json:"category"`
+	Service       IntegrationService `json:"service"`
+}

+ 14 - 0
internal/models/integrations/kube.go

@@ -69,3 +69,17 @@ func (k *KubeIntegration) Externalize() *KubeIntegrationExternal {
 		ProjectID: k.ProjectID,
 	}
 }
+
+// ToProjectIntegration converts a gcp integration to a project integration
+func (k *KubeIntegration) ToProjectIntegration(
+	category string,
+	service IntegrationService,
+) *ProjectIntegration {
+	return &ProjectIntegration{
+		ID:            k.ID,
+		ProjectID:     k.ProjectID,
+		AuthMechanism: "kube",
+		Category:      category,
+		Service:       service,
+	}
+}

+ 14 - 0
internal/models/integrations/oauth.go

@@ -61,3 +61,17 @@ func (o *OAuthIntegration) Externalize() *OAuthIntegrationExternal {
 		ProjectID: o.ProjectID,
 	}
 }
+
+// ToProjectIntegration converts a gcp integration to a project integration
+func (o *OAuthIntegration) ToProjectIntegration(
+	category string,
+	service IntegrationService,
+) *ProjectIntegration {
+	return &ProjectIntegration{
+		ID:            o.ID,
+		ProjectID:     o.ProjectID,
+		AuthMechanism: "oauth",
+		Category:      category,
+		Service:       service,
+	}
+}

+ 14 - 0
internal/models/integrations/oidc.go

@@ -74,3 +74,17 @@ func (o *OIDCIntegration) Externalize() *OIDCIntegrationExternal {
 		ProjectID: o.ProjectID,
 	}
 }
+
+// ToProjectIntegration converts a gcp integration to a project integration
+func (o *OIDCIntegration) ToProjectIntegration(
+	category string,
+	service IntegrationService,
+) *ProjectIntegration {
+	return &ProjectIntegration{
+		ID:            o.ID,
+		ProjectID:     o.ProjectID,
+		AuthMechanism: "oidc",
+		Category:      category,
+		Service:       service,
+	}
+}

+ 47 - 0
server/api/integration_handler.go

@@ -0,0 +1,47 @@
+package api
+
+import (
+	"encoding/json"
+	"net/http"
+
+	ints "github.com/porter-dev/porter/internal/models/integrations"
+)
+
+// HandleListClusterIntegrations lists the cluster integrations available to the
+// instance
+func (app *App) HandleListClusterIntegrations(w http.ResponseWriter, r *http.Request) {
+	clusters := ints.PorterClusterIntegrations
+
+	w.WriteHeader(http.StatusOK)
+
+	if err := json.NewEncoder(w).Encode(&clusters); err != nil {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+}
+
+// HandleListRegistryIntegrations lists the image registry integrations available to the
+// instance
+func (app *App) HandleListRegistryIntegrations(w http.ResponseWriter, r *http.Request) {
+	registries := ints.PorterRegistryIntegrations
+
+	w.WriteHeader(http.StatusOK)
+
+	if err := json.NewEncoder(w).Encode(&registries); err != nil {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+}
+
+// HandleListRepoIntegrations lists the repo integrations available to the
+// instance
+func (app *App) HandleListRepoIntegrations(w http.ResponseWriter, r *http.Request) {
+	repos := ints.PorterRepoIntegrations
+
+	w.WriteHeader(http.StatusOK)
+
+	if err := json.NewEncoder(w).Encode(&repos); err != nil {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+}

+ 150 - 0
server/api/integration_handler_test.go

@@ -0,0 +1,150 @@
+package api_test
+
+import (
+	"encoding/json"
+	"net/http"
+	"strings"
+	"testing"
+
+	"github.com/go-test/deep"
+	ints "github.com/porter-dev/porter/internal/models/integrations"
+)
+
+// ------------------------- TEST TYPES AND MAIN LOOP ------------------------- //
+
+type publicIntTest struct {
+	initializers []func(t *tester)
+	msg          string
+	method       string
+	endpoint     string
+	body         string
+	expStatus    int
+	expBody      string
+	useCookie    bool
+	validators   []func(c *publicIntTest, tester *tester, t *testing.T)
+}
+
+func testPublicIntegrationRequests(t *testing.T, tests []*publicIntTest, canQuery bool) {
+	for _, c := range tests {
+		// create a new tester
+		tester := newTester(canQuery)
+
+		// if there's an initializer, call it
+		for _, init := range c.initializers {
+			init(tester)
+		}
+
+		req, err := http.NewRequest(
+			c.method,
+			c.endpoint,
+			strings.NewReader(c.body),
+		)
+
+		tester.req = req
+
+		if c.useCookie {
+			req.AddCookie(tester.cookie)
+		}
+
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tester.execute()
+		rr := tester.rr
+
+		// first, check that the status matches
+		if status := rr.Code; status != c.expStatus {
+			t.Errorf("%s, handler returned wrong status code: got %v want %v",
+				c.msg, status, c.expStatus)
+		}
+
+		// if there's a validator, call it
+		for _, validate := range c.validators {
+			validate(c, tester, t)
+		}
+	}
+}
+
+// ------------------------- TEST FIXTURES AND FUNCTIONS  ------------------------- //
+
+var listClusterIntegrationsTests = []*publicIntTest{
+	&publicIntTest{
+		initializers: []func(t *tester){
+			initUserDefault,
+		},
+		msg:       "List cluster integrations",
+		method:    "GET",
+		endpoint:  "/api/integrations/cluster",
+		body:      ``,
+		expStatus: http.StatusOK,
+		expBody:   `[{"auth_mechanism":"gcp","category":"cluster","service":"gke"},{"auth_mechanism":"aws","category":"cluster","service":"eks"},{"auth_mechanism":"kube","category":"cluster","service":"kube"}]`,
+		useCookie: true,
+		validators: []func(c *publicIntTest, tester *tester, t *testing.T){
+			publicIntBodyValidator,
+		},
+	},
+}
+
+func TestHandleListClusterIntegrations(t *testing.T) {
+	testPublicIntegrationRequests(t, listClusterIntegrationsTests, true)
+}
+
+var listRegistryIntegrationsTests = []*publicIntTest{
+	&publicIntTest{
+		initializers: []func(t *tester){
+			initUserDefault,
+		},
+		msg:       "List registry integrations",
+		method:    "GET",
+		endpoint:  "/api/integrations/registry",
+		body:      ``,
+		expStatus: http.StatusOK,
+		expBody:   `[{"auth_mechanism":"gcp","category":"registry","service":"gcr"},{"auth_mechanism":"aws","category":"registry","service":"ecr"},{"auth_mechanism":"oauth","category":"registry","service":"docker"}]`,
+		useCookie: true,
+		validators: []func(c *publicIntTest, tester *tester, t *testing.T){
+			publicIntBodyValidator,
+		},
+	},
+}
+
+func TestHandleListRegistryIntegrations(t *testing.T) {
+	testPublicIntegrationRequests(t, listRegistryIntegrationsTests, true)
+}
+
+var listRepoIntegrationsTests = []*publicIntTest{
+	&publicIntTest{
+		initializers: []func(t *tester){
+			initUserDefault,
+		},
+		msg:       "List repo integrations",
+		method:    "GET",
+		endpoint:  "/api/integrations/repo",
+		body:      ``,
+		expStatus: http.StatusOK,
+		expBody:   `[{"auth_mechanism":"oauth","category":"repo","service":"github"}]`,
+		useCookie: true,
+		validators: []func(c *publicIntTest, tester *tester, t *testing.T){
+			publicIntBodyValidator,
+		},
+	},
+}
+
+func TestHandleListRepoIntegrations(t *testing.T) {
+	testPublicIntegrationRequests(t, listRepoIntegrationsTests, true)
+}
+
+// ------------------------- INITIALIZERS AND VALIDATORS ------------------------- //
+
+func publicIntBodyValidator(c *publicIntTest, tester *tester, t *testing.T) {
+	gotBody := make([]*ints.PorterIntegration, 0)
+	expBody := make([]*ints.PorterIntegration, 0)
+
+	json.Unmarshal(tester.rr.Body.Bytes(), &gotBody)
+	json.Unmarshal([]byte(c.expBody), &expBody)
+
+	if diff := deep.Equal(gotBody, expBody); diff != nil {
+		t.Errorf("handler returned wrong body:\n")
+		t.Error(diff)
+	}
+}

+ 25 - 0
server/router/router.go

@@ -40,6 +40,31 @@ func New(
 		r.Method("GET", "/auth/check", auth.BasicAuthenticate(requestlog.NewHandler(a.HandleAuthCheck, l)))
 		r.Method("POST", "/logout", auth.BasicAuthenticate(requestlog.NewHandler(a.HandleLogoutUser, l)))
 
+		// /integrations routes
+		r.Method(
+			"GET",
+			"/integrations/cluster",
+			auth.BasicAuthenticate(
+				requestlog.NewHandler(a.HandleListClusterIntegrations, l),
+			),
+		)
+
+		r.Method(
+			"GET",
+			"/integrations/registry",
+			auth.BasicAuthenticate(
+				requestlog.NewHandler(a.HandleListRegistryIntegrations, l),
+			),
+		)
+
+		r.Method(
+			"GET",
+			"/integrations/repo",
+			auth.BasicAuthenticate(
+				requestlog.NewHandler(a.HandleListRepoIntegrations, l),
+			),
+		)
+
 		// /api/oauth routes
 		// r.Method(
 		// 	"GET",