Преглед изворни кода

Merge branch 'nafees/api-v1' into dev

Mohammed Nafees пре 4 година
родитељ
комит
7c56171d9b

+ 1 - 0
api/server/handlers/cluster/create_namespace.go

@@ -55,5 +55,6 @@ func (c *CreateNamespaceHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
 		Namespace: namespace,
 	}
 
+	w.WriteHeader(http.StatusCreated)
 	c.WriteResult(w, r, res)
 }

+ 11 - 4
api/server/handlers/cluster/delete_namespace.go

@@ -8,6 +8,7 @@ import (
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/requestutils"
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/internal/models"
 )
@@ -28,10 +29,16 @@ func NewDeleteNamespaceHandler(
 }
 
 func (c *DeleteNamespaceHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	request := &types.DeleteNamespaceRequest{}
+	namespace, _ := requestutils.GetURLParamString(r, types.URLParamNamespace)
 
-	if ok := c.DecodeAndValidate(w, r, request); !ok {
-		return
+	if namespace == "" {
+		request := &types.DeleteNamespaceRequest{}
+
+		if ok := c.DecodeAndValidate(w, r, request); !ok {
+			return
+		}
+
+		namespace = request.Name
 	}
 
 	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
@@ -43,7 +50,7 @@ func (c *DeleteNamespaceHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
 		return
 	}
 
-	if err := agent.DeleteNamespace(request.Name); err != nil {
+	if err := agent.DeleteNamespace(namespace); err != nil {
 		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
 		return
 	}

+ 61 - 0
api/server/handlers/cluster/get_namespace.go

@@ -0,0 +1,61 @@
+package cluster
+
+import (
+	"net/http"
+
+	"github.com/porter-dev/porter/api/server/authz"
+	"github.com/porter-dev/porter/api/server/handlers"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/apierrors"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/requestutils"
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/models"
+	"k8s.io/apimachinery/pkg/api/errors"
+)
+
+type GetNamespaceHandler struct {
+	handlers.PorterHandlerWriter
+	authz.KubernetesAgentGetter
+}
+
+func NewGetNamespaceHandler(
+	config *config.Config,
+	writer shared.ResultWriter,
+) *GetNamespaceHandler {
+	return &GetNamespaceHandler{
+		PorterHandlerWriter:   handlers.NewDefaultPorterHandler(config, nil, writer),
+		KubernetesAgentGetter: authz.NewOutOfClusterAgentGetter(config),
+	}
+}
+
+func (c *GetNamespaceHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	namespace, reqErr := requestutils.GetURLParamString(r, types.URLParamNamespace)
+
+	if reqErr != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(reqErr))
+	}
+
+	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
+
+	agent, err := c.GetAgent(r, cluster, "")
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	res, err := agent.GetNamespace(namespace)
+
+	if err != nil {
+		if errors.IsNotFound(err) {
+			c.HandleAPIError(w, r, apierrors.NewErrNotFound(err))
+			return
+		}
+
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	c.WriteResult(w, r, res)
+}

+ 28 - 27
api/server/router/base.go

@@ -14,11 +14,12 @@ import (
 	"github.com/porter-dev/porter/api/server/handlers/webhook"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/router"
 	"github.com/porter-dev/porter/api/types"
 )
 
-func NewBaseRegisterer(children ...*Registerer) *Registerer {
-	return &Registerer{
+func NewBaseRegisterer(children ...*router.Registerer) *router.Registerer {
+	return &router.Registerer{
 		GetRoutes: GetBaseRoutes,
 		Children:  children,
 	}
@@ -29,9 +30,9 @@ func GetBaseRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-	children ...*Registerer,
-) []*Route {
-	routes := make([]*Route, 0)
+	children ...*router.Registerer,
+) []*router.Route {
+	routes := make([]*router.Route, 0)
 
 	// GET /api/readyz -> healthcheck.NewReadyzHandler
 	getReadyzEndpoint := factory.NewAPIEndpoint(
@@ -51,7 +52,7 @@ func GetBaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getReadyzEndpoint,
 		Handler:  getReadyzHandler,
 		Router:   r,
@@ -75,7 +76,7 @@ func GetBaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getLivezEndpoint,
 		Handler:  getLivezHandler,
 		Router:   r,
@@ -98,7 +99,7 @@ func GetBaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getMetadataEndpoint,
 		Handler:  getMetadataHandler,
 		Router:   r,
@@ -121,7 +122,7 @@ func GetBaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listClusterIntsEndpoint,
 		Handler:  listClusterIntsHandler,
 		Router:   r,
@@ -144,7 +145,7 @@ func GetBaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listRegistryIntsEndpoint,
 		Handler:  listRegistryIntsHandler,
 		Router:   r,
@@ -167,7 +168,7 @@ func GetBaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listHelmRepoIntsEndpoint,
 		Handler:  listHelmRepoIntsHandler,
 		Router:   r,
@@ -191,7 +192,7 @@ func GetBaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: createUserEndpoint,
 		Handler:  createUserHandler,
 		Router:   r,
@@ -215,7 +216,7 @@ func GetBaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: loginUserEndpoint,
 		Handler:  loginUserHandler,
 		Router:   r,
@@ -239,7 +240,7 @@ func GetBaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: cliLoginExchangeEndpoint,
 		Handler:  cliLoginExchangeHandler,
 		Router:   r,
@@ -263,7 +264,7 @@ func GetBaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: passwordInitiateResetEndpoint,
 		Handler:  passwordInitiateResetHandler,
 		Router:   r,
@@ -287,7 +288,7 @@ func GetBaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: passwordVerifyResetEndpoint,
 		Handler:  passwordVerifyResetHandler,
 		Router:   r,
@@ -311,7 +312,7 @@ func GetBaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: passwordFinalizeResetEndpoint,
 		Handler:  passwordFinalizeResetHandler,
 		Router:   r,
@@ -336,7 +337,7 @@ func GetBaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: webhookEndpoint,
 		Handler:  webhookHandler,
 		Router:   r,
@@ -359,7 +360,7 @@ func GetBaseRoutes(
 		config,
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: githubAppInstallEndpoint,
 		Handler:  githubAppInstallHandler,
 		Router:   r,
@@ -384,7 +385,7 @@ func GetBaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: githubAppWebhookEndpoint,
 		Handler:  githubAppWebhookHandler,
 		Router:   r,
@@ -409,7 +410,7 @@ func GetBaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: githubLoginStartEndpoint,
 		Handler:  githubLoginStartHandler,
 		Router:   r,
@@ -434,7 +435,7 @@ func GetBaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: githubLoginCallbackEndpoint,
 		Handler:  githubLoginCallbackHandler,
 		Router:   r,
@@ -459,7 +460,7 @@ func GetBaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: googleLoginStartEndpoint,
 		Handler:  googleLoginStartHandler,
 		Router:   r,
@@ -484,7 +485,7 @@ func GetBaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: googleLoginCallbackEndpoint,
 		Handler:  googleLoginCallbackHandler,
 		Router:   r,
@@ -509,7 +510,7 @@ func GetBaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getCredentialsEndpoint,
 		Handler:  getCredentialsHandler,
 		Router:   r,
@@ -533,7 +534,7 @@ func GetBaseRoutes(
 		factory.GetDecoderValidator(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: addProjectBillingEndpoint,
 		Handler:  addProjectBillingHandler,
 		Router:   r,
@@ -559,7 +560,7 @@ func GetBaseRoutes(
 			factory.GetResultWriter(),
 		)
 
-		routes = append(routes, &Route{
+		routes = append(routes, &router.Route{
 			Endpoint: githubIncomingWebhookEndpoint,
 			Handler:  githubIncomingWebhookHandler,
 			Router:   r,

+ 46 - 45
api/server/router/cluster.go

@@ -10,11 +10,12 @@ import (
 	"github.com/porter-dev/porter/api/server/handlers/kube_events"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/router"
 	"github.com/porter-dev/porter/api/types"
 )
 
-func NewClusterScopedRegisterer(children ...*Registerer) *Registerer {
-	return &Registerer{
+func NewClusterScopedRegisterer(children ...*router.Registerer) *router.Registerer {
+	return &router.Registerer{
 		GetRoutes: GetClusterScopedRoutes,
 		Children:  children,
 	}
@@ -25,8 +26,8 @@ func GetClusterScopedRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-	children ...*Registerer,
-) []*Route {
+	children ...*router.Registerer,
+) []*router.Route {
 	routes, projPath := getClusterRoutes(r, config, basePath, factory)
 
 	if len(children) > 0 {
@@ -47,7 +48,7 @@ func getClusterRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-) ([]*Route, *types.Path) {
+) ([]*router.Route, *types.Path) {
 	relPath := "/clusters/{cluster_id}"
 
 	newPath := &types.Path{
@@ -55,7 +56,7 @@ func getClusterRoutes(
 		RelativePath: relPath,
 	}
 
-	routes := make([]*Route, 0)
+	routes := make([]*router.Route, 0)
 
 	// POST /api/projects/{project_id}/clusters -> project.NewCreateClusterManualHandler
 	createEndpoint := factory.NewAPIEndpoint(
@@ -79,7 +80,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: createEndpoint,
 		Handler:  createHandler,
 		Router:   r,
@@ -109,7 +110,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: createCandidateEndpoint,
 		Handler:  createCandidateHandler,
 		Router:   r,
@@ -136,7 +137,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listCandidatesEndpoint,
 		Handler:  listCandidatesHandler,
 		Router:   r,
@@ -169,7 +170,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: resolveCandidateEndpoint,
 		Handler:  resolveCandidateHandler,
 		Router:   r,
@@ -198,7 +199,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: updateClusterEndpoint,
 		Handler:  updateClusterHandler,
 		Router:   r,
@@ -226,7 +227,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: deleteClusterEndpoint,
 		Handler:  deleteClusterHandler,
 		Router:   r,
@@ -254,7 +255,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getEndpoint,
 		Handler:  getHandler,
 		Router:   r,
@@ -282,7 +283,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listDatabaseEndpoint,
 		Handler:  listDatabaseHandler,
 		Router:   r,
@@ -312,7 +313,7 @@ func getClusterRoutes(
 			factory.GetResultWriter(),
 		)
 
-		routes = append(routes, &Route{
+		routes = append(routes, &router.Route{
 			Endpoint: listEnvEndpoint,
 			Handler:  listEnvHandler,
 			Router:   r,
@@ -341,7 +342,7 @@ func getClusterRoutes(
 			factory.GetResultWriter(),
 		)
 
-		routes = append(routes, &Route{
+		routes = append(routes, &router.Route{
 			Endpoint: listDeploymentsEndpoint,
 			Handler:  listDeploymentsHandler,
 			Router:   r,
@@ -370,7 +371,7 @@ func getClusterRoutes(
 			factory.GetResultWriter(),
 		)
 
-		routes = append(routes, &Route{
+		routes = append(routes, &router.Route{
 			Endpoint: getDeploymentEndpoint,
 			Handler:  getDeploymentHandler,
 			Router:   r,
@@ -399,7 +400,7 @@ func getClusterRoutes(
 			factory.GetResultWriter(),
 		)
 
-		routes = append(routes, &Route{
+		routes = append(routes, &router.Route{
 			Endpoint: reenableDeploymentEndpoint,
 			Handler:  reenableDeploymentHandler,
 			Router:   r,
@@ -428,7 +429,7 @@ func getClusterRoutes(
 			factory.GetResultWriter(),
 		)
 
-		routes = append(routes, &Route{
+		routes = append(routes, &router.Route{
 			Endpoint: triggerDeploymentWorkflowEndpoint,
 			Handler:  triggerDeploymentWorkflowHandler,
 			Router:   r,
@@ -457,7 +458,7 @@ func getClusterRoutes(
 			factory.GetResultWriter(),
 		)
 
-		routes = append(routes, &Route{
+		routes = append(routes, &router.Route{
 			Endpoint: enablePullRequestEndpoint,
 			Handler:  enablePullRequestHandler,
 			Router:   r,
@@ -487,7 +488,7 @@ func getClusterRoutes(
 			factory.GetResultWriter(),
 		)
 
-		routes = append(routes, &Route{
+		routes = append(routes, &router.Route{
 			Endpoint: deleteDeploymentEndpoint,
 			Handler:  deleteDeploymentHandler,
 			Router:   r,
@@ -517,7 +518,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listNamespacesEndpoint,
 		Handler:  listNamespacesHandler,
 		Router:   r,
@@ -545,7 +546,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listNodesEndpoint,
 		Handler:  listNodesHandler,
 		Router:   r,
@@ -573,7 +574,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getNodeEndpoint,
 		Handler:  getNodeHandler,
 		Router:   r,
@@ -602,7 +603,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: createNamespaceEndpoint,
 		Handler:  createNamespaceHandler,
 		Router:   r,
@@ -630,7 +631,7 @@ func getClusterRoutes(
 		factory.GetDecoderValidator(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: deleteNamespaceEndpoint,
 		Handler:  deleteNamespaceHandler,
 		Router:   r,
@@ -658,7 +659,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getTemporaryKubeconfigEndpoint,
 		Handler:  getTemporaryKubeconfigHandler,
 		Router:   r,
@@ -683,7 +684,7 @@ func getClusterRoutes(
 
 	detectPrometheusInstalledHandler := cluster.NewDetectPrometheusInstalledHandler(config)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: detectPrometheusInstalledEndpoint,
 		Handler:  detectPrometheusInstalledHandler,
 		Router:   r,
@@ -708,7 +709,7 @@ func getClusterRoutes(
 
 	detectAgentInstalledHandler := cluster.NewDetectAgentInstalledHandler(config, factory.GetResultWriter())
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: detectAgentInstalledEndpoint,
 		Handler:  detectAgentInstalledHandler,
 		Router:   r,
@@ -737,7 +738,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: installAgentEndpoint,
 		Handler:  installAgentHandler,
 		Router:   r,
@@ -766,7 +767,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: upgradeAgentEndpoint,
 		Handler:  upgradeAgentHandler,
 		Router:   r,
@@ -795,7 +796,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listKubeEventsEndpoint,
 		Handler:  listKubeEventsHandler,
 		Router:   r,
@@ -824,7 +825,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getKubeEventEndpoint,
 		Handler:  getKubeEventHandler,
 		Router:   r,
@@ -853,7 +854,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getKubeEventLogsEndpoint,
 		Handler:  getKubeEventLogsHandler,
 		Router:   r,
@@ -882,7 +883,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getKubeEventLogBucketsEndpoint,
 		Handler:  getKubeEventLogBucketsHandler,
 		Router:   r,
@@ -911,7 +912,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: createKubeEventsEndpoint,
 		Handler:  createKubeEventsHandler,
 		Router:   r,
@@ -939,7 +940,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listNGINXIngressesEndpoint,
 		Handler:  listNGINXIngressesHandler,
 		Router:   r,
@@ -968,7 +969,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getPodMetricsEndpoint,
 		Handler:  getPodMetricsHandler,
 		Router:   r,
@@ -998,7 +999,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: streamHelmReleaseEndpoint,
 		Handler:  streamHelmReleaseHandler,
 		Router:   r,
@@ -1032,7 +1033,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: streamStatusEndpoint,
 		Handler:  streamStatusHandler,
 		Router:   r,
@@ -1061,7 +1062,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getPodsEndpoint,
 		Handler:  getPodsHandler,
 		Router:   r,
@@ -1090,7 +1091,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getIncidentsEndpoint,
 		Handler:  getIncidentsHandler,
 		Router:   r,
@@ -1119,7 +1120,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getIncidentEventLogsEndpoint,
 		Handler:  getIncidentEventLogsHandler,
 		Router:   r,
@@ -1148,7 +1149,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: notifyNewIncidentEndpoint,
 		Handler:  notifyNewIncidentHandler,
 		Router:   r,
@@ -1177,7 +1178,7 @@ func getClusterRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: notifyResolvedIncidentEndpoint,
 		Handler:  notifyResolvedIncidentHandler,
 		Router:   r,

+ 24 - 23
api/server/router/git_installation.go

@@ -8,11 +8,12 @@ import (
 	"github.com/porter-dev/porter/api/server/handlers/gitinstallation"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/router"
 	"github.com/porter-dev/porter/api/types"
 )
 
-func NewGitInstallationScopedRegisterer(children ...*Registerer) *Registerer {
-	return &Registerer{
+func NewGitInstallationScopedRegisterer(children ...*router.Registerer) *router.Registerer {
+	return &router.Registerer{
 		GetRoutes: GetGitInstallationScopedRoutes,
 		Children:  children,
 	}
@@ -23,8 +24,8 @@ func GetGitInstallationScopedRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-	children ...*Registerer,
-) []*Route {
+	children ...*router.Registerer,
+) []*router.Route {
 	routes, projPath := getGitInstallationRoutes(r, config, basePath, factory)
 
 	if len(children) > 0 {
@@ -45,7 +46,7 @@ func getGitInstallationRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-) ([]*Route, *types.Path) {
+) ([]*router.Route, *types.Path) {
 	relPath := "/gitrepos/{git_installation_id}"
 
 	newPath := &types.Path{
@@ -53,7 +54,7 @@ func getGitInstallationRoutes(
 		RelativePath: relPath,
 	}
 
-	routes := make([]*Route, 0)
+	routes := make([]*router.Route, 0)
 
 	// GET /api/projects/{project_id}/gitrepos/{git_installation_id} -> gitinstallation.NewGitInstallationGetHandler
 	getEndpoint := factory.NewAPIEndpoint(
@@ -77,7 +78,7 @@ func getGitInstallationRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getEndpoint,
 		Handler:  getHandler,
 		Router:   r,
@@ -106,7 +107,7 @@ func getGitInstallationRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getPermissionsEndpoint,
 		Handler:  getPermissionsHandler,
 		Router:   r,
@@ -144,7 +145,7 @@ func getGitInstallationRoutes(
 			factory.GetResultWriter(),
 		)
 
-		routes = append(routes, &Route{
+		routes = append(routes, &router.Route{
 			Endpoint: createEnvironmentEndpoint,
 			Handler:  createEnvironmentHandler,
 			Router:   r,
@@ -180,7 +181,7 @@ func getGitInstallationRoutes(
 			factory.GetResultWriter(),
 		)
 
-		routes = append(routes, &Route{
+		routes = append(routes, &router.Route{
 			Endpoint: createDeploymentEndpoint,
 			Handler:  createDeploymentHandler,
 			Router:   r,
@@ -216,7 +217,7 @@ func getGitInstallationRoutes(
 			factory.GetResultWriter(),
 		)
 
-		routes = append(routes, &Route{
+		routes = append(routes, &router.Route{
 			Endpoint: getDeploymentEndpoint,
 			Handler:  getDeploymentHandler,
 			Router:   r,
@@ -252,7 +253,7 @@ func getGitInstallationRoutes(
 			factory.GetResultWriter(),
 		)
 
-		routes = append(routes, &Route{
+		routes = append(routes, &router.Route{
 			Endpoint: listDeploymentsEndpoint,
 			Handler:  listDeploymentsHandler,
 			Router:   r,
@@ -288,7 +289,7 @@ func getGitInstallationRoutes(
 			factory.GetResultWriter(),
 		)
 
-		routes = append(routes, &Route{
+		routes = append(routes, &router.Route{
 			Endpoint: finalizeDeploymentEndpoint,
 			Handler:  finalizeDeploymentHandler,
 			Router:   r,
@@ -324,7 +325,7 @@ func getGitInstallationRoutes(
 			factory.GetResultWriter(),
 		)
 
-		routes = append(routes, &Route{
+		routes = append(routes, &router.Route{
 			Endpoint: updateDeploymentEndpoint,
 			Handler:  updateDeploymentHandler,
 			Router:   r,
@@ -360,7 +361,7 @@ func getGitInstallationRoutes(
 			factory.GetResultWriter(),
 		)
 
-		routes = append(routes, &Route{
+		routes = append(routes, &router.Route{
 			Endpoint: updateDeploymentStatusEndpoint,
 			Handler:  updateDeploymentStatusHandler,
 			Router:   r,
@@ -396,7 +397,7 @@ func getGitInstallationRoutes(
 			factory.GetResultWriter(),
 		)
 
-		routes = append(routes, &Route{
+		routes = append(routes, &router.Route{
 			Endpoint: deleteEnvironmentEndpoint,
 			Handler:  deleteEnvironmentHandler,
 			Router:   r,
@@ -427,7 +428,7 @@ func getGitInstallationRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listReposEndpoint,
 		Handler:  listReposHandler,
 		Router:   r,
@@ -462,7 +463,7 @@ func getGitInstallationRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listBranchesEndpoint,
 		Handler:  listBranchesHandler,
 		Router:   r,
@@ -499,7 +500,7 @@ func getGitInstallationRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getBuildpackEndpoint,
 		Handler:  getBuildpackHandler,
 		Router:   r,
@@ -536,7 +537,7 @@ func getGitInstallationRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getContentsEndpoint,
 		Handler:  getContentsHandler,
 		Router:   r,
@@ -573,7 +574,7 @@ func getGitInstallationRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getProcfileEndpoint,
 		Handler:  getProcfileHandler,
 		Router:   r,
@@ -610,7 +611,7 @@ func getGitInstallationRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getTarballURLEndpoint,
 		Handler:  getTarballURLHandler,
 		Router:   r,
@@ -646,7 +647,7 @@ func getGitInstallationRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: rerunWorkflowEndpoint,
 		Handler:  rerunWorkflowHandler,
 		Router:   r,

+ 10 - 9
api/server/router/helm_repo.go

@@ -5,11 +5,12 @@ import (
 	"github.com/porter-dev/porter/api/server/handlers/helmrepo"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/router"
 	"github.com/porter-dev/porter/api/types"
 )
 
-func NewHelmRepoScopedRegisterer(children ...*Registerer) *Registerer {
-	return &Registerer{
+func NewHelmRepoScopedRegisterer(children ...*router.Registerer) *router.Registerer {
+	return &router.Registerer{
 		GetRoutes: GetHelmRepoScopedRoutes,
 		Children:  children,
 	}
@@ -20,8 +21,8 @@ func GetHelmRepoScopedRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-	children ...*Registerer,
-) []*Route {
+	children ...*router.Registerer,
+) []*router.Route {
 	routes, projPath := getHelmRepoRoutes(r, config, basePath, factory)
 
 	if len(children) > 0 {
@@ -42,7 +43,7 @@ func getHelmRepoRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-) ([]*Route, *types.Path) {
+) ([]*router.Route, *types.Path) {
 	relPath := "/helmrepos/{helm_repo_id}"
 
 	newPath := &types.Path{
@@ -50,7 +51,7 @@ func getHelmRepoRoutes(
 		RelativePath: relPath,
 	}
 
-	routes := make([]*Route, 0)
+	routes := make([]*router.Route, 0)
 
 	// GET /api/projects/{project_id}/helmrepos/{helm_repo_id} -> registry.NewHelmRepoGetHandler
 	getEndpoint := factory.NewAPIEndpoint(
@@ -74,7 +75,7 @@ func getHelmRepoRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getEndpoint,
 		Handler:  getHandler,
 		Router:   r,
@@ -103,7 +104,7 @@ func getHelmRepoRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: hrListEndpoint,
 		Handler:  hrListHandler,
 		Router:   r,
@@ -132,7 +133,7 @@ func getHelmRepoRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: chartGetEndpoint,
 		Handler:  chartGetHandler,
 		Router:   r,

+ 20 - 19
api/server/router/infra.go

@@ -8,11 +8,12 @@ import (
 	"github.com/porter-dev/porter/api/server/handlers/infra"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/router"
 	"github.com/porter-dev/porter/api/types"
 )
 
-func NewInfraScopedRegisterer(children ...*Registerer) *Registerer {
-	return &Registerer{
+func NewInfraScopedRegisterer(children ...*router.Registerer) *router.Registerer {
+	return &router.Registerer{
 		GetRoutes: GetInfraScopedRoutes,
 		Children:  children,
 	}
@@ -23,8 +24,8 @@ func GetInfraScopedRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-	children ...*Registerer,
-) []*Route {
+	children ...*router.Registerer,
+) []*router.Route {
 	routes, projPath := getInfraRoutes(r, config, basePath, factory)
 
 	if len(children) > 0 {
@@ -45,7 +46,7 @@ func getInfraRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-) ([]*Route, *types.Path) {
+) ([]*router.Route, *types.Path) {
 	relPath := "/infras/{infra_id}"
 
 	newPath := &types.Path{
@@ -53,7 +54,7 @@ func getInfraRoutes(
 		RelativePath: relPath,
 	}
 
-	routes := make([]*Route, 0)
+	routes := make([]*router.Route, 0)
 
 	// GET /api/projects/{project_id}/infra -> project.NewInfraListHandler
 	listInfraEndpoint := factory.NewAPIEndpoint(
@@ -77,7 +78,7 @@ func getInfraRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listInfraEndpoint,
 		Handler:  listInfraHandler,
 		Router:   r,
@@ -105,7 +106,7 @@ func getInfraRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getEndpoint,
 		Handler:  getHandler,
 		Router:   r,
@@ -134,7 +135,7 @@ func getInfraRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: retryCreateEndpoint,
 		Handler:  retryCreateHandler,
 		Router:   r,
@@ -163,7 +164,7 @@ func getInfraRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: updateEndpoint,
 		Handler:  updateHandler,
 		Router:   r,
@@ -192,7 +193,7 @@ func getInfraRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: retryDeleteEndpoint,
 		Handler:  retryDeleteHandler,
 		Router:   r,
@@ -220,7 +221,7 @@ func getInfraRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listOperationsEndpoint,
 		Handler:  listOperationsHandler,
 		Router:   r,
@@ -249,7 +250,7 @@ func getInfraRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getOperationEndpoint,
 		Handler:  getOperationHandler,
 		Router:   r,
@@ -279,7 +280,7 @@ func getInfraRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: streamStateEndpoint,
 		Handler:  streamStateHandler,
 		Router:   r,
@@ -309,7 +310,7 @@ func getInfraRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: streamLogEndpoint,
 		Handler:  streamLogHandler,
 		Router:   r,
@@ -338,7 +339,7 @@ func getInfraRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getOperationLogsEndpoint,
 		Handler:  getOperationLogsHandler,
 		Router:   r,
@@ -366,7 +367,7 @@ func getInfraRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getStateEndpoint,
 		Handler:  getStateHandler,
 		Router:   r,
@@ -395,7 +396,7 @@ func getInfraRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: deleteEndpoint,
 		Handler:  deleteHandler,
 		Router:   r,
@@ -423,7 +424,7 @@ func getInfraRoutes(
 		factory.GetDecoderValidator(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: updateDBStatusEndpoint,
 		Handler:  updateDBStatusHandler,
 		Router:   r,

+ 12 - 11
api/server/router/invite.go

@@ -5,11 +5,12 @@ import (
 	"github.com/porter-dev/porter/api/server/handlers/invite"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/router"
 	"github.com/porter-dev/porter/api/types"
 )
 
-func NewInviteScopedRegisterer(children ...*Registerer) *Registerer {
-	return &Registerer{
+func NewInviteScopedRegisterer(children ...*router.Registerer) *router.Registerer {
+	return &router.Registerer{
 		GetRoutes: GetInviteScopedRoutes,
 		Children:  children,
 	}
@@ -20,8 +21,8 @@ func GetInviteScopedRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-	children ...*Registerer,
-) []*Route {
+	children ...*router.Registerer,
+) []*router.Route {
 	routes, projPath := getInviteRoutes(r, config, basePath, factory)
 
 	if len(children) > 0 {
@@ -42,7 +43,7 @@ func getInviteRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-) ([]*Route, *types.Path) {
+) ([]*router.Route, *types.Path) {
 	relPath := "/invites/{invite_id}"
 
 	newPath := &types.Path{
@@ -50,7 +51,7 @@ func getInviteRoutes(
 		RelativePath: relPath,
 	}
 
-	routes := make([]*Route, 0)
+	routes := make([]*router.Route, 0)
 
 	// GET /api/projects/{project_id}/invites -> invite.NewInvitesListHandler
 	listEndpoint := factory.NewAPIEndpoint(
@@ -74,7 +75,7 @@ func getInviteRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listEndpoint,
 		Handler:  listHandler,
 		Router:   r,
@@ -105,7 +106,7 @@ func getInviteRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: createEndpoint,
 		Handler:  createHandler,
 		Router:   r,
@@ -131,7 +132,7 @@ func getInviteRoutes(
 
 	acceptHandler := invite.NewInviteAcceptHandler(config)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: acceptEndpoint,
 		Handler:  acceptHandler,
 		Router:   r,
@@ -160,7 +161,7 @@ func getInviteRoutes(
 		factory.GetDecoderValidator(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: updateRoleEndpoint,
 		Handler:  updateRoleHandler,
 		Router:   r,
@@ -188,7 +189,7 @@ func getInviteRoutes(
 		config,
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: deleteEndpoint,
 		Handler:  deleteHandler,
 		Router:   r,

+ 28 - 27
api/server/router/namespace.go

@@ -9,11 +9,12 @@ import (
 	"github.com/porter-dev/porter/api/server/handlers/namespace"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/router"
 	"github.com/porter-dev/porter/api/types"
 )
 
-func NewNamespaceScopedRegisterer(children ...*Registerer) *Registerer {
-	return &Registerer{
+func NewNamespaceScopedRegisterer(children ...*router.Registerer) *router.Registerer {
+	return &router.Registerer{
 		GetRoutes: GetNamespaceScopedRoutes,
 		Children:  children,
 	}
@@ -24,8 +25,8 @@ func GetNamespaceScopedRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-	children ...*Registerer,
-) []*Route {
+	children ...*router.Registerer,
+) []*router.Route {
 	routes, projPath := getNamespaceRoutes(r, config, basePath, factory)
 
 	if len(children) > 0 {
@@ -46,7 +47,7 @@ func getNamespaceRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-) ([]*Route, *types.Path) {
+) ([]*router.Route, *types.Path) {
 	relPath := "/namespaces/{namespace}"
 
 	newPath := &types.Path{
@@ -54,7 +55,7 @@ func getNamespaceRoutes(
 		RelativePath: relPath,
 	}
 
-	routes := make([]*Route, 0)
+	routes := make([]*router.Route, 0)
 
 	// GET /api/projects/{project_id}/clusters/{cluster_id}/namespaces/{namespace}/envgroups/list -> namespace.NewListEnvGroupsHandler
 	listEnvGroupsEndpoint := factory.NewAPIEndpoint(
@@ -79,7 +80,7 @@ func getNamespaceRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listEnvGroupsEndpoint,
 		Handler:  listEnvGroupsHandler,
 		Router:   r,
@@ -109,7 +110,7 @@ func getNamespaceRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: cloneEnvGroupEndpoint,
 		Handler:  cloneEnvGroupHandler,
 		Router:   r,
@@ -139,7 +140,7 @@ func getNamespaceRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getEnvGroupEndpoint,
 		Handler:  getEnvGroupHandler,
 		Router:   r,
@@ -169,7 +170,7 @@ func getNamespaceRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getEnvGroupAllVersionsEndpoint,
 		Handler:  getEnvGroupAllVersionsHandler,
 		Router:   r,
@@ -199,7 +200,7 @@ func getNamespaceRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: createEnvGroupEndpoint,
 		Handler:  createEnvGroupHandler,
 		Router:   r,
@@ -229,7 +230,7 @@ func getNamespaceRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: updateEnvGroupAppsEndpoint,
 		Handler:  updateEnvGroupAppsHandler,
 		Router:   r,
@@ -259,7 +260,7 @@ func getNamespaceRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: removeEnvGroupAppEndpoint,
 		Handler:  removeEnvGroupAppHandler,
 		Router:   r,
@@ -289,7 +290,7 @@ func getNamespaceRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: deleteEnvGroupEndpoint,
 		Handler:  deleteEnvGroupHandler,
 		Router:   r,
@@ -319,7 +320,7 @@ func getNamespaceRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: updateConfigMapEndpoint,
 		Handler:  updateConfigMapHandler,
 		Router:   r,
@@ -348,7 +349,7 @@ func getNamespaceRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: deleteCRDEndpoint,
 		Handler:  deleteCRDHandler,
 		Router:   r,
@@ -378,7 +379,7 @@ func getNamespaceRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listReleasesEndpoint,
 		Handler:  listReleasesHandler,
 		Router:   r,
@@ -413,7 +414,7 @@ func getNamespaceRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: streamPodLogsEndpoint,
 		Handler:  streamPodLogsHandler,
 		Router:   r,
@@ -447,7 +448,7 @@ func getNamespaceRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: streamJobRunsEndpoint,
 		Handler:  streamJobRunsHandler,
 		Router:   r,
@@ -481,7 +482,7 @@ func getNamespaceRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getPreviousLogsEndpoint,
 		Handler:  getPreviousLogsHandler,
 		Router:   r,
@@ -514,7 +515,7 @@ func getNamespaceRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getJobPodsEndpoint,
 		Handler:  getJobPodsHandler,
 		Router:   r,
@@ -546,7 +547,7 @@ func getNamespaceRoutes(
 		config,
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: deleteJobEndpoint,
 		Handler:  deleteJobHandler,
 		Router:   r,
@@ -578,7 +579,7 @@ func getNamespaceRoutes(
 		config,
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: stopJobEndpoint,
 		Handler:  stopJobHandler,
 		Router:   r,
@@ -611,7 +612,7 @@ func getNamespaceRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getPodEndpoint,
 		Handler:  getPodHandler,
 		Router:   r,
@@ -643,7 +644,7 @@ func getNamespaceRoutes(
 		config,
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: deletePodEndpoint,
 		Handler:  deletePodHandler,
 		Router:   r,
@@ -676,7 +677,7 @@ func getNamespaceRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getPodEventsEndpoint,
 		Handler:  getPodEventsHandler,
 		Router:   r,
@@ -706,7 +707,7 @@ func getNamespaceRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getIngressEndpoint,
 		Handler:  getIngressHandler,
 		Router:   r,

+ 8 - 7
api/server/router/oauth_callback.go

@@ -5,11 +5,12 @@ import (
 	"github.com/porter-dev/porter/api/server/handlers/oauth_callback"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/router"
 	"github.com/porter-dev/porter/api/types"
 )
 
-func NewOAuthCallbackRegisterer(children ...*Registerer) *Registerer {
-	return &Registerer{
+func NewOAuthCallbackRegisterer(children ...*router.Registerer) *router.Registerer {
+	return &router.Registerer{
 		GetRoutes: GetOAuthCallbackRoutes,
 		Children:  children,
 	}
@@ -20,11 +21,11 @@ func GetOAuthCallbackRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-	children ...*Registerer,
-) []*Route {
+	children ...*router.Registerer,
+) []*router.Route {
 	relPath := "/oauth"
 
-	routes := make([]*Route, 0)
+	routes := make([]*router.Route, 0)
 
 	// GET /api/oauth/slack/callback -> oauth_callback.NewOAuthCallbackSlackHandler
 	slackEndpoint := factory.NewAPIEndpoint(
@@ -44,7 +45,7 @@ func GetOAuthCallbackRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: slackEndpoint,
 		Handler:  slackHandler,
 		Router:   r,
@@ -68,7 +69,7 @@ func GetOAuthCallbackRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: doEndpoint,
 		Handler:  doHandler,
 		Router:   r,

+ 42 - 41
api/server/router/project.go

@@ -13,11 +13,12 @@ import (
 	"github.com/porter-dev/porter/api/server/handlers/registry"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/router"
 	"github.com/porter-dev/porter/api/types"
 )
 
-func NewProjectScopedRegisterer(children ...*Registerer) *Registerer {
-	return &Registerer{
+func NewProjectScopedRegisterer(children ...*router.Registerer) *router.Registerer {
+	return &router.Registerer{
 		GetRoutes: GetProjectScopedRoutes,
 		Children:  children,
 	}
@@ -28,8 +29,8 @@ func GetProjectScopedRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-	children ...*Registerer,
-) []*Route {
+	children ...*router.Registerer,
+) []*router.Route {
 	routes, projPath := getProjectRoutes(r, config, basePath, factory)
 
 	if len(children) > 0 {
@@ -50,7 +51,7 @@ func getProjectRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-) ([]*Route, *types.Path) {
+) ([]*router.Route, *types.Path) {
 	relPath := "/projects/{project_id}"
 
 	newPath := &types.Path{
@@ -58,7 +59,7 @@ func getProjectRoutes(
 		RelativePath: relPath,
 	}
 
-	routes := make([]*Route, 0)
+	routes := make([]*router.Route, 0)
 
 	// GET /api/projects/{project_id} -> project.NewProjectGetHandler
 	getEndpoint := factory.NewAPIEndpoint(
@@ -81,7 +82,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getEndpoint,
 		Handler:  getHandler,
 		Router:   r,
@@ -108,7 +109,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: deleteEndpoint,
 		Handler:  deleteHandler,
 		Router:   r,
@@ -135,7 +136,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getPolicyEndpoint,
 		Handler:  getPolicyHandler,
 		Router:   r,
@@ -163,7 +164,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getOnboardingEndpoint,
 		Handler:  getOnboardingHandler,
 		Router:   r,
@@ -191,7 +192,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: updateOnboardingEndpoint,
 		Handler:  updateOnboardingHandler,
 		Router:   r,
@@ -218,7 +219,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getUsageEndpoint,
 		Handler:  getUsageHandler,
 		Router:   r,
@@ -245,7 +246,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getBillingEndpoint,
 		Handler:  getBillingHandler,
 		Router:   r,
@@ -274,7 +275,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getBillingTokenEndpoint,
 		Handler:  getBillingTokenHandler,
 		Router:   r,
@@ -298,7 +299,7 @@ func getProjectRoutes(
 		factory.GetDecoderValidator(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getBillingWebhookEndpoint,
 		Handler:  getBillingWebhookHandler,
 		Router:   r,
@@ -325,7 +326,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listClusterEndpoint,
 		Handler:  listClusterHandler,
 		Router:   r,
@@ -352,7 +353,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listGitReposEndpoint,
 		Handler:  listGitReposHandler,
 		Router:   r,
@@ -379,7 +380,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listCollaboratorsEndpoint,
 		Handler:  listCollaboratorsHandler,
 		Router:   r,
@@ -406,7 +407,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listRolesEndpoint,
 		Handler:  listRolesHandler,
 		Router:   r,
@@ -434,7 +435,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: updateRoleEndpoint,
 		Handler:  updateRoleHandler,
 		Router:   r,
@@ -462,7 +463,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: deleteRoleEndpoint,
 		Handler:  deleteRoleHandler,
 		Router:   r,
@@ -489,7 +490,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listRegistriesEndpoint,
 		Handler:  listRegistriesHandler,
 		Router:   r,
@@ -517,7 +518,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: createRegistryEndpoint,
 		Handler:  createRegistryHandler,
 		Router:   r,
@@ -545,7 +546,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getECRTokenEndpoint,
 		Handler:  getECRTokenHandler,
 		Router:   r,
@@ -573,7 +574,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getDOCRTokenEndpoint,
 		Handler:  getDOCRTokenHandler,
 		Router:   r,
@@ -601,7 +602,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getGCRTokenEndpoint,
 		Handler:  getGCRTokenHandler,
 		Router:   r,
@@ -629,7 +630,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getACRTokenEndpoint,
 		Handler:  getACRTokenHandler,
 		Router:   r,
@@ -657,7 +658,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getDockerhubTokenEndpoint,
 		Handler:  getDockerhubTokenHandler,
 		Router:   r,
@@ -685,7 +686,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: createInfraEndpoint,
 		Handler:  createInfraHandler,
 		Router:   r,
@@ -712,7 +713,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getTemplatesEndpoint,
 		Handler:  getTemplatesHandler,
 		Router:   r,
@@ -739,7 +740,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getTemplateEndpoint,
 		Handler:  getTemplateHandler,
 		Router:   r,
@@ -767,7 +768,7 @@ func getProjectRoutes(
 	// 	factory.GetResultWriter(),
 	// )
 
-	// routes = append(routes, &Route{
+	// routes = append(routes, &router.Route{
 	// 	Endpoint: provisionECREndpoint,
 	// 	Handler:  provisionECRHandler,
 	// 	Router:   r,
@@ -797,7 +798,7 @@ func getProjectRoutes(
 	// 	factory.GetResultWriter(),
 	// )
 
-	// routes = append(routes, &Route{
+	// routes = append(routes, &router.Route{
 	// 	Endpoint: provisionEKSEndpoint,
 	// 	Handler:  provisionEKSHandler,
 	// 	Router:   r,
@@ -825,7 +826,7 @@ func getProjectRoutes(
 	// 	factory.GetResultWriter(),
 	// )
 
-	// routes = append(routes, &Route{
+	// routes = append(routes, &router.Route{
 	// 	Endpoint: provisionDOCREndpoint,
 	// 	Handler:  provisionDOCRHandler,
 	// 	Router:   r,
@@ -855,7 +856,7 @@ func getProjectRoutes(
 	// 	factory.GetResultWriter(),
 	// )
 
-	// routes = append(routes, &Route{
+	// routes = append(routes, &router.Route{
 	// 	Endpoint: provisionDOKSEndpoint,
 	// 	Handler:  provisionDOKSHandler,
 	// 	Router:   r,
@@ -883,7 +884,7 @@ func getProjectRoutes(
 	// 	factory.GetResultWriter(),
 	// )
 
-	// routes = append(routes, &Route{
+	// routes = append(routes, &router.Route{
 	// 	Endpoint: provisionGCREndpoint,
 	// 	Handler:  provisionGCRHandler,
 	// 	Router:   r,
@@ -913,7 +914,7 @@ func getProjectRoutes(
 	// 	factory.GetResultWriter(),
 	// )
 
-	// routes = append(routes, &Route{
+	// routes = append(routes, &router.Route{
 	// 	Endpoint: provisionGKEEndpoint,
 	// 	Handler:  provisionGKEHandler,
 	// 	Router:   r,
@@ -941,7 +942,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: hrCreateEndpoint,
 		Handler:  hrCreateHandler,
 		Router:   r,
@@ -968,7 +969,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: hrListEndpoint,
 		Handler:  hrListHandler,
 		Router:   r,
@@ -995,7 +996,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getTagsEndpoint,
 		Handler:  getTagsHandler,
 		Router:   r,
@@ -1023,7 +1024,7 @@ func getProjectRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: createTagEndpoint,
 		Handler:  createTagHandler,
 		Router:   r,

+ 17 - 16
api/server/router/project_integration.go

@@ -5,11 +5,12 @@ import (
 	project_integration "github.com/porter-dev/porter/api/server/handlers/project_integration"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/router"
 	"github.com/porter-dev/porter/api/types"
 )
 
-func NewProjectIntegrationScopedRegisterer(children ...*Registerer) *Registerer {
-	return &Registerer{
+func NewProjectIntegrationScopedRegisterer(children ...*router.Registerer) *router.Registerer {
+	return &router.Registerer{
 		GetRoutes: GetProjectIntegrationScopedRoutes,
 		Children:  children,
 	}
@@ -20,8 +21,8 @@ func GetProjectIntegrationScopedRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-	children ...*Registerer,
-) []*Route {
+	children ...*router.Registerer,
+) []*router.Route {
 	routes, projPath := getProjectIntegrationRoutes(r, config, basePath, factory)
 
 	if len(children) > 0 {
@@ -42,7 +43,7 @@ func getProjectIntegrationRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-) ([]*Route, *types.Path) {
+) ([]*router.Route, *types.Path) {
 	relPath := "/integrations"
 
 	newPath := &types.Path{
@@ -50,7 +51,7 @@ func getProjectIntegrationRoutes(
 		RelativePath: relPath,
 	}
 
-	routes := make([]*Route, 0)
+	routes := make([]*router.Route, 0)
 
 	// GET /api/projects/{project_id}/integrations/oauth -> project_integration.NewListOAuthHandler
 	listOAuthEndpoint := factory.NewAPIEndpoint(
@@ -73,7 +74,7 @@ func getProjectIntegrationRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listOAuthEndpoint,
 		Handler:  listOAuthHandler,
 		Router:   r,
@@ -100,7 +101,7 @@ func getProjectIntegrationRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listDOEndpoint,
 		Handler:  listDOHandler,
 		Router:   r,
@@ -128,7 +129,7 @@ func getProjectIntegrationRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: createBasicEndpoint,
 		Handler:  createBasicHandler,
 		Router:   r,
@@ -156,7 +157,7 @@ func getProjectIntegrationRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: createAWSEndpoint,
 		Handler:  createAWSHandler,
 		Router:   r,
@@ -183,7 +184,7 @@ func getProjectIntegrationRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listAWSEndpoint,
 		Handler:  listAWSHandler,
 		Router:   r,
@@ -211,7 +212,7 @@ func getProjectIntegrationRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: overwriteAWSEndpoint,
 		Handler:  overwriteAWSHandler,
 		Router:   r,
@@ -238,7 +239,7 @@ func getProjectIntegrationRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listAzureEndpoint,
 		Handler:  listAzureHandler,
 		Router:   r,
@@ -266,7 +267,7 @@ func getProjectIntegrationRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: createGCPEndpoint,
 		Handler:  createGCPHandler,
 		Router:   r,
@@ -293,7 +294,7 @@ func getProjectIntegrationRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listGCPEndpoint,
 		Handler:  listGCPHandler,
 		Router:   r,
@@ -321,7 +322,7 @@ func getProjectIntegrationRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: createAzureEndpoint,
 		Handler:  createAzureHandler,
 		Router:   r,

+ 9 - 8
api/server/router/project_oauth.go

@@ -6,11 +6,12 @@ import (
 	"github.com/porter-dev/porter/api/server/handlers/project_oauth"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/router"
 	"github.com/porter-dev/porter/api/types"
 )
 
-func NewProjectOAuthScopedRegisterer(children ...*Registerer) *Registerer {
-	return &Registerer{
+func NewProjectOAuthScopedRegisterer(children ...*router.Registerer) *router.Registerer {
+	return &router.Registerer{
 		GetRoutes: GetProjectOAuthScopedRoutes,
 		Children:  children,
 	}
@@ -21,8 +22,8 @@ func GetProjectOAuthScopedRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-	children ...*Registerer,
-) []*Route {
+	children ...*router.Registerer,
+) []*router.Route {
 	routes, projPath := getProjectOAuthRoutes(r, config, basePath, factory)
 
 	if len(children) > 0 {
@@ -43,7 +44,7 @@ func getProjectOAuthRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-) ([]*Route, *types.Path) {
+) ([]*router.Route, *types.Path) {
 	relPath := "/oauth"
 
 	newPath := &types.Path{
@@ -51,7 +52,7 @@ func getProjectOAuthRoutes(
 		RelativePath: relPath,
 	}
 
-	routes := make([]*Route, 0)
+	routes := make([]*router.Route, 0)
 
 	// GET /api/projects/{project_id}/oauth/slack -> project_integration.NewProjectOAuthSlackHandler
 	slackEndpoint := factory.NewAPIEndpoint(
@@ -75,7 +76,7 @@ func getProjectOAuthRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: slackEndpoint,
 		Handler:  slackHandler,
 		Router:   r,
@@ -103,7 +104,7 @@ func getProjectOAuthRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: doEndpoint,
 		Handler:  doHandler,
 		Router:   r,

+ 13 - 12
api/server/router/registry.go

@@ -7,11 +7,12 @@ import (
 	"github.com/porter-dev/porter/api/server/handlers/registry"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/router"
 	"github.com/porter-dev/porter/api/types"
 )
 
-func NewRegistryScopedRegisterer(children ...*Registerer) *Registerer {
-	return &Registerer{
+func NewRegistryScopedRegisterer(children ...*router.Registerer) *router.Registerer {
+	return &router.Registerer{
 		GetRoutes: GetRegistryScopedRoutes,
 		Children:  children,
 	}
@@ -22,8 +23,8 @@ func GetRegistryScopedRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-	children ...*Registerer,
-) []*Route {
+	children ...*router.Registerer,
+) []*router.Route {
 	routes, projPath := getRegistryRoutes(r, config, basePath, factory)
 
 	if len(children) > 0 {
@@ -44,7 +45,7 @@ func getRegistryRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-) ([]*Route, *types.Path) {
+) ([]*router.Route, *types.Path) {
 	relPath := "/registries/{registry_id}"
 
 	newPath := &types.Path{
@@ -52,7 +53,7 @@ func getRegistryRoutes(
 		RelativePath: relPath,
 	}
 
-	routes := make([]*Route, 0)
+	routes := make([]*router.Route, 0)
 
 	// GET /api/projects/{project_id}/registries/{registry_id} -> registry.NewRegistryGetHandler
 	getEndpoint := factory.NewAPIEndpoint(
@@ -76,7 +77,7 @@ func getRegistryRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getEndpoint,
 		Handler:  getHandler,
 		Router:   r,
@@ -105,7 +106,7 @@ func getRegistryRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: updateEndpoint,
 		Handler:  updateHandler,
 		Router:   r,
@@ -133,7 +134,7 @@ func getRegistryRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: deleteEndpoint,
 		Handler:  deleteHandler,
 		Router:   r,
@@ -161,7 +162,7 @@ func getRegistryRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listRepositoriesEndpoint,
 		Handler:  listRepositoriesHandler,
 		Router:   r,
@@ -193,7 +194,7 @@ func getRegistryRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listImagesEndpoint,
 		Handler:  listImagesHandler,
 		Router:   r,
@@ -222,7 +223,7 @@ func getRegistryRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: createRepositoryEndpoint,
 		Handler:  createRepositoryHandler,
 		Router:   r,

+ 32 - 31
api/server/router/release.go

@@ -5,11 +5,12 @@ import (
 	"github.com/porter-dev/porter/api/server/handlers/release"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/router"
 	"github.com/porter-dev/porter/api/types"
 )
 
-func NewReleaseScopedRegisterer(children ...*Registerer) *Registerer {
-	return &Registerer{
+func NewReleaseScopedRegisterer(children ...*router.Registerer) *router.Registerer {
+	return &router.Registerer{
 		GetRoutes: GetReleaseScopedRoutes,
 		Children:  children,
 	}
@@ -20,8 +21,8 @@ func GetReleaseScopedRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-	children ...*Registerer,
-) []*Route {
+	children ...*router.Registerer,
+) []*router.Route {
 	routes, projPath := getReleaseRoutes(r, config, basePath, factory)
 
 	if len(children) > 0 {
@@ -42,7 +43,7 @@ func getReleaseRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-) ([]*Route, *types.Path) {
+) ([]*router.Route, *types.Path) {
 	relPath := "/releases/{name}/{version}"
 
 	newPath := &types.Path{
@@ -50,7 +51,7 @@ func getReleaseRoutes(
 		RelativePath: relPath,
 	}
 
-	routes := make([]*Route, 0)
+	routes := make([]*router.Route, 0)
 
 	// GET /api/projects/{project_id}/clusters/{cluster_id}/namespaces/{namespace}/releases/{name}/{version} -> release.NewReleaseGetHandler
 	getEndpoint := factory.NewAPIEndpoint(
@@ -76,7 +77,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getEndpoint,
 		Handler:  getHandler,
 		Router:   r,
@@ -108,7 +109,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: streamFormEndpoint,
 		Handler:  streamFormHandler,
 		Router:   r,
@@ -138,7 +139,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getControllersEndpoint,
 		Handler:  getControllersHandler,
 		Router:   r,
@@ -168,7 +169,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getComponentsEndpoint,
 		Handler:  getComponentsHandler,
 		Router:   r,
@@ -198,7 +199,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getHistoryEndpoint,
 		Handler:  getHistoryHandler,
 		Router:   r,
@@ -228,7 +229,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getAllPodsEndpoint,
 		Handler:  getAllPodsHandler,
 		Router:   r,
@@ -258,7 +259,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: updateNotifsEndpoint,
 		Handler:  updateNotifsHandler,
 		Router:   r,
@@ -287,7 +288,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getNotifsEndpoint,
 		Handler:  getNotifsHandler,
 		Router:   r,
@@ -317,7 +318,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: updateBuildConfigEndpoint,
 		Handler:  updateBuildConfigHandler,
 		Router:   r,
@@ -346,7 +347,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getWebhookEndpoint,
 		Handler:  getWebhookHandler,
 		Router:   r,
@@ -376,7 +377,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: createWebhookEndpoint,
 		Handler:  createWebhookHandler,
 		Router:   r,
@@ -406,7 +407,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getStepsEndpoint,
 		Handler:  getStepsHandler,
 		Router:   r,
@@ -436,7 +437,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: updateStepsEndpoint,
 		Handler:  updateStepsHandler,
 		Router:   r,
@@ -466,7 +467,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: createReleaseEndpoint,
 		Handler:  createReleaseHandler,
 		Router:   r,
@@ -496,7 +497,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: createAddonEndpoint,
 		Handler:  createAddonHandler,
 		Router:   r,
@@ -526,7 +527,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getGHATemplateEndpoint,
 		Handler:  getGHATemplateHandler,
 		Router:   r,
@@ -558,7 +559,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: rollbackEndpoint,
 		Handler:  rollbackHandler,
 		Router:   r,
@@ -590,7 +591,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: upgradeEndpoint,
 		Handler:  upgradeHandler,
 		Router:   r,
@@ -622,7 +623,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: deleteEndpoint,
 		Handler:  deleteHandler,
 		Router:   r,
@@ -653,7 +654,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: updateImageBatchEndpoint,
 		Handler:  updateImageBatchHandler,
 		Router:   r,
@@ -685,7 +686,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getJobsEndpoint,
 		Handler:  getJobsHandler,
 		Router:   r,
@@ -716,7 +717,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getLatestJobRunEndpoint,
 		Handler:  getLatestJobRunHandler,
 		Router:   r,
@@ -747,7 +748,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getJobsStatusEndpoint,
 		Handler:  getJobsStatusHandler,
 		Router:   r,
@@ -776,7 +777,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: createSubdomainEndpoint,
 		Handler:  createSubdomainHandler,
 		Router:   r,
@@ -808,7 +809,7 @@ func getReleaseRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: updateReleaseTagsEndpoint,
 		Handler:  updateReleaseTagsHandler,
 		Router:   r,

+ 37 - 21
api/server/router/router.go

@@ -12,8 +12,10 @@ import (
 	"github.com/porter-dev/porter/api/server/authz"
 	"github.com/porter-dev/porter/api/server/authz/policy"
 	"github.com/porter-dev/porter/api/server/router/middleware"
+	v1 "github.com/porter-dev/porter/api/server/router/v1"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/router"
 	"github.com/porter-dev/porter/api/types"
 )
 
@@ -90,13 +92,13 @@ func NewAPIRouter(config *config.Config) *chi.Mux {
 			userRegisterer.Children...,
 		)
 
-		routes := [][]*Route{
+		routes := [][]*router.Route{
 			baseRoutes,
 			userRoutes,
 			oauthCallbackRoutes,
 		}
 
-		var allRoutes []*Route
+		var allRoutes []*router.Route
 		for _, r := range routes {
 			allRoutes = append(allRoutes, r...)
 		}
@@ -104,6 +106,38 @@ func NewAPIRouter(config *config.Config) *chi.Mux {
 		registerRoutes(config, allRoutes)
 	})
 
+	r.Route("/api/v1", func(r chi.Router) {
+		// set panic middleware for all API endpoints to catch panics
+		r.Use(panicMW.Middleware)
+
+		// set the content type for all API endpoints and log all request info
+		r.Use(middleware.ContentTypeJSON)
+
+		var allRoutes []*router.Route
+
+		v1RegistryRegisterer := v1.NewV1RegistryScopedRegisterer()
+		v1ReleaseRegisterer := v1.NewV1ReleaseScopedRegisterer()
+		v1NamespaceRegisterer := v1.NewV1NamespaceScopedRegisterer(v1ReleaseRegisterer)
+		v1ClusterRegisterer := v1.NewV1ClusterScopedRegisterer(v1NamespaceRegisterer)
+		v1ProjRegisterer := v1.NewV1ProjectScopedRegisterer(
+			v1ClusterRegisterer,
+			v1RegistryRegisterer,
+		)
+
+		v1Routes := v1ProjRegisterer.GetRoutes(
+			r,
+			config,
+			&types.Path{
+				RelativePath: "",
+			},
+			endpointFactory,
+		)
+
+		allRoutes = append(allRoutes, v1Routes...)
+
+		registerRoutes(config, allRoutes)
+	})
+
 	staticFilePath := config.ServerConf.StaticFilePath
 	fs := http.FileServer(http.Dir(staticFilePath))
 
@@ -128,25 +162,7 @@ func NewAPIRouter(config *config.Config) *chi.Mux {
 	return r
 }
 
-type Route struct {
-	Endpoint *shared.APIEndpoint
-	Handler  http.Handler
-	Router   chi.Router
-}
-
-type Registerer struct {
-	GetRoutes func(
-		r chi.Router,
-		config *config.Config,
-		basePath *types.Path,
-		factory shared.APIEndpointFactory,
-		children ...*Registerer,
-	) []*Route
-
-	Children []*Registerer
-}
-
-func registerRoutes(config *config.Config, routes []*Route) {
+func registerRoutes(config *config.Config, routes []*router.Route) {
 	// Create a new "user-scoped" factory which will create a new user-scoped request
 	// after authentication. Each subsequent http.Handler can lookup the user in context.
 	authNFactory := authn.NewAuthNFactory(config)

+ 10 - 9
api/server/router/slack_integration.go

@@ -5,11 +5,12 @@ import (
 	"github.com/porter-dev/porter/api/server/handlers/slack_integration"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/router"
 	"github.com/porter-dev/porter/api/types"
 )
 
-func NewSlackIntegrationScopedRegisterer(children ...*Registerer) *Registerer {
-	return &Registerer{
+func NewSlackIntegrationScopedRegisterer(children ...*router.Registerer) *router.Registerer {
+	return &router.Registerer{
 		GetRoutes: GetSlackIntegrationScopedRoutes,
 		Children:  children,
 	}
@@ -20,8 +21,8 @@ func GetSlackIntegrationScopedRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-	children ...*Registerer,
-) []*Route {
+	children ...*router.Registerer,
+) []*router.Route {
 	routes, projPath := getSlackIntegrationRoutes(r, config, basePath, factory)
 
 	if len(children) > 0 {
@@ -42,7 +43,7 @@ func getSlackIntegrationRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-) ([]*Route, *types.Path) {
+) ([]*router.Route, *types.Path) {
 	relPath := "/slack_integrations"
 
 	newPath := &types.Path{
@@ -50,7 +51,7 @@ func getSlackIntegrationRoutes(
 		RelativePath: relPath,
 	}
 
-	routes := make([]*Route, 0)
+	routes := make([]*router.Route, 0)
 
 	// GET /api/projects/{project_id}/slack_integrations -> slack_integration.NewListHandler
 	listEndpoint := factory.NewAPIEndpoint(
@@ -73,7 +74,7 @@ func getSlackIntegrationRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listEndpoint,
 		Handler:  listHandler,
 		Router:   r,
@@ -97,7 +98,7 @@ func getSlackIntegrationRoutes(
 
 	existsHandler := slack_integration.NewSlackIntegrationExists(config)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: existsEndpoint,
 		Handler:  existsHandler,
 		Router:   r,
@@ -121,7 +122,7 @@ func getSlackIntegrationRoutes(
 
 	deleteHandler := slack_integration.NewSlackIntegrationDelete(config)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: deleteEndpoint,
 		Handler:  deleteHandler,
 		Router:   r,

+ 23 - 22
api/server/router/user.go

@@ -10,11 +10,12 @@ import (
 	"github.com/porter-dev/porter/api/server/handlers/user"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/router"
 	"github.com/porter-dev/porter/api/types"
 )
 
-func NewUserScopedRegisterer(children ...*Registerer) *Registerer {
-	return &Registerer{
+func NewUserScopedRegisterer(children ...*router.Registerer) *router.Registerer {
+	return &router.Registerer{
 		GetRoutes: GetUserScopedRoutes,
 		Children:  children,
 	}
@@ -25,8 +26,8 @@ func GetUserScopedRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-	children ...*Registerer,
-) []*Route {
+	children ...*router.Registerer,
+) []*router.Route {
 	routes := getUserRoutes(r, config, basePath, factory)
 
 	for _, child := range children {
@@ -45,8 +46,8 @@ func getUserRoutes(
 	config *config.Config,
 	basePath *types.Path,
 	factory shared.APIEndpointFactory,
-) []*Route {
-	routes := make([]*Route, 0)
+) []*router.Route {
+	routes := make([]*router.Route, 0)
 
 	// POST /api/welcome -> user.NewUserWelcomeHandler
 	welcomeEndpoint := factory.NewAPIEndpoint(
@@ -67,7 +68,7 @@ func getUserRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: welcomeEndpoint,
 		Handler:  welcomeHandler,
 		Router:   r,
@@ -93,7 +94,7 @@ func getUserRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: cliLoginUserEndpoint,
 		Handler:  cliLoginUserHandler,
 		Router:   r,
@@ -114,7 +115,7 @@ func getUserRoutes(
 
 	logoutUserHandler := user.NewUserLogoutHandler(config)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: logoutUserEndpoint,
 		Handler:  logoutUserHandler,
 		Router:   r,
@@ -138,7 +139,7 @@ func getUserRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: authCheckEndpoint,
 		Handler:  authCheckHandler,
 		Router:   r,
@@ -162,7 +163,7 @@ func getUserRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: deleteUserEndpoint,
 		Handler:  deleteUserHandler,
 		Router:   r,
@@ -187,7 +188,7 @@ func getUserRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: createEndpoint,
 		Handler:  createHandler,
 		Router:   r,
@@ -211,7 +212,7 @@ func getUserRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listEndpoint,
 		Handler:  listHandler,
 		Router:   r,
@@ -232,7 +233,7 @@ func getUserRoutes(
 
 	emailVerifyInitiateHandler := user.NewVerifyEmailInitiateHandler(config)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: emailVerifyInitiateEndpoint,
 		Handler:  emailVerifyInitiateHandler,
 		Router:   r,
@@ -257,7 +258,7 @@ func getUserRoutes(
 		factory.GetDecoderValidator(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: emailVerifyFinalizeEndpoint,
 		Handler:  emailVerifyFinalizeHandler,
 		Router:   r,
@@ -282,7 +283,7 @@ func getUserRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: listTemplatesEndpoint,
 		Handler:  listTemplatesRequest,
 		Router:   r,
@@ -311,7 +312,7 @@ func getUserRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getTemplateEndpoint,
 		Handler:  getTemplateRequest,
 		Router:   r,
@@ -340,7 +341,7 @@ func getUserRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: getTemplateUpgradeNotesEndpoint,
 		Handler:  getTemplateUpgradeNotesRequest,
 		Router:   r,
@@ -365,7 +366,7 @@ func getUserRoutes(
 		config,
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: githubAppOAuthStartEndpoint,
 		Handler:  githubAppOAuthStartHandler,
 		Router:   r,
@@ -390,7 +391,7 @@ func getUserRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: githubAppOAuthCallbackEndpoint,
 		Handler:  githubAppOAuthCallbackHandler,
 		Router:   r,
@@ -415,7 +416,7 @@ func getUserRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: githubAppAccountsEndpoint,
 		Handler:  githubAppAccountsHandler,
 		Router:   r,
@@ -440,7 +441,7 @@ func getUserRoutes(
 		factory.GetResultWriter(),
 	)
 
-	routes = append(routes, &Route{
+	routes = append(routes, &router.Route{
 		Endpoint: canCreateProjectEndpoint,
 		Handler:  canCreateProjectHandler,
 		Router:   r,

+ 172 - 0
api/server/router/v1/cluster.go

@@ -0,0 +1,172 @@
+package v1
+
+import (
+	"fmt"
+
+	"github.com/go-chi/chi"
+	"github.com/porter-dev/porter/api/server/handlers/cluster"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/router"
+	"github.com/porter-dev/porter/api/types"
+)
+
+func NewV1ClusterScopedRegisterer(children ...*router.Registerer) *router.Registerer {
+	return &router.Registerer{
+		GetRoutes: GetV1ClusterScopedRoutes,
+		Children:  children,
+	}
+}
+
+func GetV1ClusterScopedRoutes(
+	r chi.Router,
+	config *config.Config,
+	basePath *types.Path,
+	factory shared.APIEndpointFactory,
+	children ...*router.Registerer,
+) []*router.Route {
+	routes, projPath := getV1ClusterRoutes(r, config, basePath, factory)
+
+	if len(children) > 0 {
+		r.Route(projPath.RelativePath, func(r chi.Router) {
+			for _, child := range children {
+				childRoutes := child.GetRoutes(r, config, basePath, factory, child.Children...)
+
+				routes = append(routes, childRoutes...)
+			}
+		})
+	}
+
+	return routes
+}
+
+func getV1ClusterRoutes(
+	r chi.Router,
+	config *config.Config,
+	basePath *types.Path,
+	factory shared.APIEndpointFactory,
+) ([]*router.Route, *types.Path) {
+	relPath := "/clusters/{cluster_id}"
+
+	newPath := &types.Path{
+		Parent:       basePath,
+		RelativePath: relPath,
+	}
+
+	var routes []*router.Route
+
+	// POST /api/v1/projects/{project_id}/clusters/{cluster_id}/namespaces -> cluster.NewCreateNamespaceHandler
+	createNamespaceEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbCreate,
+			Method: types.HTTPVerbPost,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/namespaces",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.ClusterScope,
+			},
+		},
+	)
+
+	createNamespaceHandler := cluster.NewCreateNamespaceHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: createNamespaceEndpoint,
+		Handler:  createNamespaceHandler,
+		Router:   r,
+	})
+
+	// GET /api/v1/projects/{project_id}/clusters/{cluster_id}/namespaces/{namespace} -> cluster.NewGetNamespaceHandler
+	getNamespaceEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbGet,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: fmt.Sprintf("%s/namespaces/{%s}", relPath, types.URLParamNamespace),
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.ClusterScope,
+			},
+		},
+	)
+
+	getNamespaceHandler := cluster.NewGetNamespaceHandler(
+		config,
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: getNamespaceEndpoint,
+		Handler:  getNamespaceHandler,
+		Router:   r,
+	})
+
+	// GET /api/v1/projects/{project_id}/clusters/{cluster_id}/namespaces -> cluster.NewListNamespacesHandler
+	listNamespacesEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbGet,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/namespaces",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.ClusterScope,
+			},
+		},
+	)
+
+	listNamespacesHandler := cluster.NewListNamespacesHandler(
+		config,
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: listNamespacesEndpoint,
+		Handler:  listNamespacesHandler,
+		Router:   r,
+	})
+
+	// DELETE /api/v1/projects/{project_id}/clusters/{cluster_id}/namespaces/{namespace} -> cluster.NewDeleteNamespaceHandler
+	deleteNamespaceEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbDelete,
+			Method: types.HTTPVerbDelete,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: fmt.Sprintf("%s/namespaces/{%s}", relPath, types.URLParamNamespace),
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.ClusterScope,
+			},
+		},
+	)
+
+	deleteNamespaceHandler := cluster.NewDeleteNamespaceHandler(
+		config,
+		factory.GetDecoderValidator(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: deleteNamespaceEndpoint,
+		Handler:  deleteNamespaceHandler,
+		Router:   r,
+	})
+
+	return routes, newPath
+}

+ 56 - 0
api/server/router/v1/namespace.go

@@ -0,0 +1,56 @@
+package v1
+
+import (
+	"github.com/go-chi/chi"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/router"
+	"github.com/porter-dev/porter/api/types"
+)
+
+func NewV1NamespaceScopedRegisterer(children ...*router.Registerer) *router.Registerer {
+	return &router.Registerer{
+		GetRoutes: GetV1NamespaceScopedRoutes,
+		Children:  children,
+	}
+}
+
+func GetV1NamespaceScopedRoutes(
+	r chi.Router,
+	config *config.Config,
+	basePath *types.Path,
+	factory shared.APIEndpointFactory,
+	children ...*router.Registerer,
+) []*router.Route {
+	routes, projPath := getV1NamespaceRoutes(r, config, basePath, factory)
+
+	if len(children) > 0 {
+		r.Route(projPath.RelativePath, func(r chi.Router) {
+			for _, child := range children {
+				childRoutes := child.GetRoutes(r, config, basePath, factory, child.Children...)
+
+				routes = append(routes, childRoutes...)
+			}
+		})
+	}
+
+	return routes
+}
+
+func getV1NamespaceRoutes(
+	r chi.Router,
+	config *config.Config,
+	basePath *types.Path,
+	factory shared.APIEndpointFactory,
+) ([]*router.Route, *types.Path) {
+	relPath := "/namespaces/{namespace}"
+
+	newPath := &types.Path{
+		Parent:       basePath,
+		RelativePath: relPath,
+	}
+
+	var routes []*router.Route
+
+	return routes, newPath
+}

+ 56 - 0
api/server/router/v1/project.go

@@ -0,0 +1,56 @@
+package v1
+
+import (
+	"github.com/go-chi/chi"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/router"
+	"github.com/porter-dev/porter/api/types"
+)
+
+func NewV1ProjectScopedRegisterer(children ...*router.Registerer) *router.Registerer {
+	return &router.Registerer{
+		GetRoutes: GetV1ProjectScopedRoutes,
+		Children:  children,
+	}
+}
+
+func GetV1ProjectScopedRoutes(
+	r chi.Router,
+	config *config.Config,
+	basePath *types.Path,
+	factory shared.APIEndpointFactory,
+	children ...*router.Registerer,
+) []*router.Route {
+	routes, projPath := getV1ProjectRoutes(r, config, basePath, factory)
+
+	if len(children) > 0 {
+		r.Route(projPath.RelativePath, func(r chi.Router) {
+			for _, child := range children {
+				childRoutes := child.GetRoutes(r, config, basePath, factory, child.Children...)
+
+				routes = append(routes, childRoutes...)
+			}
+		})
+	}
+
+	return routes
+}
+
+func getV1ProjectRoutes(
+	r chi.Router,
+	config *config.Config,
+	basePath *types.Path,
+	factory shared.APIEndpointFactory,
+) ([]*router.Route, *types.Path) {
+	relPath := "/projects/{project_id}"
+
+	newPath := &types.Path{
+		Parent:       basePath,
+		RelativePath: relPath,
+	}
+
+	var routes []*router.Route
+
+	return routes, newPath
+}

+ 259 - 0
api/server/router/v1/registry.go

@@ -0,0 +1,259 @@
+package v1
+
+import (
+	"fmt"
+
+	"github.com/go-chi/chi"
+	"github.com/porter-dev/porter/api/server/handlers/registry"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/router"
+	"github.com/porter-dev/porter/api/types"
+)
+
+func NewV1RegistryScopedRegisterer(children ...*router.Registerer) *router.Registerer {
+	return &router.Registerer{
+		GetRoutes: GetV1RegistryScopedRoutes,
+		Children:  children,
+	}
+}
+
+func GetV1RegistryScopedRoutes(
+	r chi.Router,
+	config *config.Config,
+	basePath *types.Path,
+	factory shared.APIEndpointFactory,
+	children ...*router.Registerer,
+) []*router.Route {
+	routes, projPath := getV1RegistryRoutes(r, config, basePath, factory)
+
+	if len(children) > 0 {
+		r.Route(projPath.RelativePath, func(r chi.Router) {
+			for _, child := range children {
+				childRoutes := child.GetRoutes(r, config, basePath, factory, child.Children...)
+
+				routes = append(routes, childRoutes...)
+			}
+		})
+	}
+
+	return routes
+}
+
+func getV1RegistryRoutes(
+	r chi.Router,
+	config *config.Config,
+	basePath *types.Path,
+	factory shared.APIEndpointFactory,
+) ([]*router.Route, *types.Path) {
+	relPath := "/registries"
+
+	newPath := &types.Path{
+		Parent:       basePath,
+		RelativePath: relPath,
+	}
+
+	var routes []*router.Route
+
+	// POST /api/v1/projects/{project_id}/registries -> registry.NewRegistryCreateHandler
+	createRegistryEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbCreate,
+			Method: types.HTTPVerbPost,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath,
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+			},
+		},
+	)
+
+	createRegistryHandler := registry.NewRegistryCreateHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: createRegistryEndpoint,
+		Handler:  createRegistryHandler,
+		Router:   r,
+	})
+
+	// GET /api/v1/projects/{project_id}/registries/{registry_id} -> registry.NewRegistryGetHandler
+	getEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbGet,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/{registry_id}",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.RegistryScope,
+			},
+		},
+	)
+
+	getHandler := registry.NewRegistryGetHandler(
+		config,
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: getEndpoint,
+		Handler:  getHandler,
+		Router:   r,
+	})
+
+	// GET /api/v1/projects/{project_id}/registries -> registry.NewRegistryListHandler
+	listRegistriesEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbList,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath,
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+			},
+		},
+	)
+
+	listRegistriesHandler := registry.NewRegistryListHandler(
+		config,
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: listRegistriesEndpoint,
+		Handler:  listRegistriesHandler,
+		Router:   r,
+	})
+
+	// DELETE /api/v1/projects/{project_id}/registries/{registry_id} -> registry.NewRegistryDeleteHandler
+	deleteEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbDelete,
+			Method: types.HTTPVerbDelete,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/{registry_id}",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.RegistryScope,
+			},
+		},
+	)
+
+	deleteHandler := registry.NewRegistryDeleteHandler(
+		config,
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: deleteEndpoint,
+		Handler:  deleteHandler,
+		Router:   r,
+	})
+
+	// POST /api/v1/projects/{project_id}/registries/{registry_id}/repositories -> registry.NewRegistryCreateRepositoryHandler
+	createRepositoryEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbCreate,
+			Method: types.HTTPVerbPost,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/{registry_id}/repositories",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.RegistryScope,
+			},
+		},
+	)
+
+	createRepositoryHandler := registry.NewRegistryCreateRepositoryHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: createRepositoryEndpoint,
+		Handler:  createRepositoryHandler,
+		Router:   r,
+	})
+
+	// GET /api/v1/projects/{project_id}/registries/{registry_id}/repositories -> registry.NewRegistryListRepositoriesHandler
+	listRepositoriesEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbList,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/{registry_id}/repositories",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.RegistryScope,
+			},
+		},
+	)
+
+	listRepositoriesHandler := registry.NewRegistryListRepositoriesHandler(
+		config,
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: listRepositoriesEndpoint,
+		Handler:  listRepositoriesHandler,
+		Router:   r,
+	})
+
+	// GET /api/v1/projects/{project_id}/registries/{registry_id}/repositories/* -> registry.NewRegistryListImagesHandler
+	listImagesEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbList,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent: basePath,
+				RelativePath: fmt.Sprintf(
+					"%s/{registry_id}/repositories/%s",
+					relPath,
+					types.URLParamWildcard,
+				),
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.RegistryScope,
+			},
+		},
+	)
+
+	listImagesHandler := registry.NewRegistryListImagesHandler(
+		config,
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: listImagesEndpoint,
+		Handler:  listImagesHandler,
+		Router:   r,
+	})
+
+	return routes, newPath
+}

+ 212 - 0
api/server/router/v1/release.go

@@ -0,0 +1,212 @@
+package v1
+
+import (
+	"github.com/go-chi/chi"
+	"github.com/porter-dev/porter/api/server/handlers/namespace"
+	"github.com/porter-dev/porter/api/server/handlers/release"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/router"
+	"github.com/porter-dev/porter/api/types"
+)
+
+func NewV1ReleaseScopedRegisterer(children ...*router.Registerer) *router.Registerer {
+	return &router.Registerer{
+		GetRoutes: GetV1ReleaseScopedRoutes,
+		Children:  children,
+	}
+}
+
+func GetV1ReleaseScopedRoutes(
+	r chi.Router,
+	config *config.Config,
+	basePath *types.Path,
+	factory shared.APIEndpointFactory,
+	children ...*router.Registerer,
+) []*router.Route {
+	routes, projPath := getV1ReleaseRoutes(r, config, basePath, factory)
+
+	if len(children) > 0 {
+		r.Route(projPath.RelativePath, func(r chi.Router) {
+			for _, child := range children {
+				childRoutes := child.GetRoutes(r, config, basePath, factory, child.Children...)
+
+				routes = append(routes, childRoutes...)
+			}
+		})
+	}
+
+	return routes
+}
+
+func getV1ReleaseRoutes(
+	r chi.Router,
+	config *config.Config,
+	basePath *types.Path,
+	factory shared.APIEndpointFactory,
+) ([]*router.Route, *types.Path) {
+	relPath := "/releases"
+
+	newPath := &types.Path{
+		Parent:       basePath,
+		RelativePath: relPath,
+	}
+
+	var routes []*router.Route
+
+	// POST /api/v1/projects/{project_id}/clusters/{cluster_id}/namespaces/{namespace}/releases -> release.NewCreateReleaseHandler
+	createReleaseEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbCreate,
+			Method: types.HTTPVerbPost,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath,
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.ClusterScope,
+				types.NamespaceScope,
+			},
+		},
+	)
+
+	createReleaseHandler := release.NewCreateReleaseHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: createReleaseEndpoint,
+		Handler:  createReleaseHandler,
+		Router:   r,
+	})
+
+	// GET /api/v1/projects/{project_id}/clusters/{cluster_id}/namespaces/{namespace}/releases/{name}/{version} -> release.NewReleaseGetHandler
+	getEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbGet,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/{name}/{version}",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.ClusterScope,
+				types.NamespaceScope,
+				types.ReleaseScope,
+			},
+		},
+	)
+
+	getHandler := release.NewReleaseGetHandler(
+		config,
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: getEndpoint,
+		Handler:  getHandler,
+		Router:   r,
+	})
+
+	// GET /api/v1/projects/{project_id}/clusters/{cluster_id}/namespaces/{namespace}/releases -> namespace.NewListReleasesHandler
+	listReleasesEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbGet,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath,
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.ClusterScope,
+				types.NamespaceScope,
+			},
+		},
+	)
+
+	listReleasesHandler := namespace.NewListReleasesHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: listReleasesEndpoint,
+		Handler:  listReleasesHandler,
+		Router:   r,
+	})
+
+	// PATCH /api/v1/projects/{project_id}/clusters/{cluster_id}/namespaces/{namespace}/releases/{name}/{version} ->
+	// release.NewUpgradeReleaseHandler
+	upgradeEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbUpdate,
+			Method: types.HTTPVerbPatch,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/{name}/{version}",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.ClusterScope,
+				types.NamespaceScope,
+				types.ReleaseScope,
+			},
+		},
+	)
+
+	upgradeHandler := release.NewUpgradeReleaseHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: upgradeEndpoint,
+		Handler:  upgradeHandler,
+		Router:   r,
+	})
+
+	// DELETE /api/v1/projects/{project_id}/clusters/{cluster_id}/namespaces/{namespace}/releases/{name}/{version} ->
+	// release.NewDeleteReleaseHandler
+	deleteEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbDelete,
+			Method: types.HTTPVerbDelete,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath,
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.ClusterScope,
+				types.NamespaceScope,
+				types.ReleaseScope,
+			},
+		},
+	)
+
+	deleteHandler := release.NewDeleteReleaseHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: deleteEndpoint,
+		Handler:  deleteHandler,
+		Router:   r,
+	})
+
+	return routes, newPath
+}

+ 25 - 0
api/server/shared/apierrors/errors.go

@@ -92,6 +92,31 @@ func (e *ErrPassThroughToClient) GetStatusCode() int {
 	return e.statusCode
 }
 
+// errors that denote that a resource was not found
+type ErrNotFound struct {
+	err error
+}
+
+func NewErrNotFound(err error) RequestError {
+	return &ErrNotFound{err}
+}
+
+func (e *ErrNotFound) Error() string {
+	return e.err.Error()
+}
+
+func (e *ErrNotFound) InternalError() string {
+	return e.err.Error()
+}
+
+func (e *ErrNotFound) ExternalError() string {
+	return "Resource not found."
+}
+
+func (e *ErrNotFound) GetStatusCode() int {
+	return http.StatusNotFound
+}
+
 type ErrorOpts struct {
 	Code uint
 }

+ 28 - 0
api/server/shared/router/router.go

@@ -0,0 +1,28 @@
+package router
+
+import (
+	"net/http"
+
+	"github.com/go-chi/chi"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/types"
+)
+
+type Route struct {
+	Endpoint *shared.APIEndpoint
+	Handler  http.Handler
+	Router   chi.Router
+}
+
+type Registerer struct {
+	GetRoutes func(
+		r chi.Router,
+		config *config.Config,
+		basePath *types.Path,
+		factory shared.APIEndpointFactory,
+		children ...*Registerer,
+	) []*Route
+
+	Children []*Registerer
+}

+ 15 - 0
internal/kubernetes/agent.go

@@ -640,6 +640,21 @@ func (a *Agent) CreateNamespace(name string) (*v1.Namespace, error) {
 	)
 }
 
+// GetNamespace gets the namespace given the name
+func (a *Agent) GetNamespace(name string) (*v1.Namespace, error) {
+	ns, err := a.Clientset.CoreV1().Namespaces().Get(
+		context.Background(),
+		name,
+		metav1.GetOptions{},
+	)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return ns, nil
+}
+
 // DeleteNamespace deletes the namespace given the name.
 func (a *Agent) DeleteNamespace(name string) error {
 	// check if namespace exists