Bläddra i källkod

add NewListReleasesHandler

Anukul Sangwan 4 år sedan
förälder
incheckning
5b635d0a4c

+ 44 - 0
api/server/authz/namespace.go

@@ -0,0 +1,44 @@
+package authz
+
+import (
+	"context"
+	"net/http"
+
+	"github.com/porter-dev/porter/api/server/authz/policy"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/types"
+)
+
+type NamespaceScopedFactory struct {
+	config *shared.Config
+}
+
+func NewNamespaceScopedFactory(
+	config *shared.Config,
+) *NamespaceScopedFactory {
+	return &NamespaceScopedFactory{config}
+}
+
+func (p *NamespaceScopedFactory) Middleware(next http.Handler) http.Handler {
+	return &NamespaceScopedMiddleware{next, p.config}
+}
+
+type NamespaceScopedMiddleware struct {
+	next   http.Handler
+	config *shared.Config
+}
+
+func (n *NamespaceScopedMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	// get the namespace from the URL param context
+	reqScopes, _ := r.Context().Value(RequestScopeCtxKey).(map[types.PermissionScope]*policy.RequestAction)
+
+	namespace := reqScopes[types.NamespaceScope].Resource.Name
+
+	ctx := NewNamespaceContext(r.Context(), namespace)
+	r = r.WithContext(ctx)
+	n.next.ServeHTTP(w, r)
+}
+
+func NewNamespaceContext(ctx context.Context, namespace string) context.Context {
+	return context.WithValue(ctx, types.NamespaceScope, namespace)
+}

+ 57 - 0
api/server/handlers/namespace/list_releases.go

@@ -0,0 +1,57 @@
+package namespace
+
+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/types"
+	"github.com/porter-dev/porter/internal/models"
+)
+
+type ListReleasesHandler struct {
+	handlers.PorterHandlerReadWriter
+	authz.KubernetesAgentGetter
+}
+
+func NewListReleasesHandler(
+	config *shared.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *ListReleasesHandler {
+	return &ListReleasesHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+		KubernetesAgentGetter:   authz.NewOutOfClusterAgentGetter(config),
+	}
+}
+
+func (c *ListReleasesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	request := &types.ListReleasesRequest{}
+
+	if ok := c.DecodeAndValidate(w, r, request); !ok {
+		return
+	}
+
+	namespace := r.Context().Value(types.NamespaceScope).(string)
+	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
+
+	helmAgent, err := c.GetHelmAgent(r, cluster)
+
+	if err != nil {
+		c.HandleAPIError(w, apierrors.NewErrInternal(err))
+		return
+	}
+
+	releases, err := helmAgent.ListReleases(namespace, request.ReleaseListFilter)
+
+	if err != nil {
+		c.HandleAPIError(w, apierrors.NewErrInternal(err))
+		return
+	}
+
+	var res types.ListReleasesResponse = releases
+
+	c.WriteResult(w, res)
+}

+ 32 - 0
api/server/router/namespace.go

@@ -2,6 +2,8 @@ package router
 
 import (
 	"github.com/go-chi/chi"
+
+	"github.com/porter-dev/porter/api/server/handlers/namespace"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/types"
 )
@@ -50,5 +52,35 @@ func getNamespaceRoutes(
 
 	routes := make([]*Route, 0)
 
+	// GET /api/projects/{project_id}/clusters/{cluster_id}/namespaces/{namespace}/releases -> namespace.NewListReleasesHandler
+	getEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbGet,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/releases",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.ClusterScope,
+				types.NamespaceScope,
+			},
+		},
+	)
+
+	getHandler := namespace.NewListReleasesHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &Route{
+		Endpoint: getEndpoint,
+		Handler:  getHandler,
+		Router:   r,
+	})
+
 	return routes, newPath
 }

+ 6 - 0
api/server/router/router.go

@@ -97,6 +97,10 @@ func registerRoutes(config *shared.Config, routes []*Route) {
 	// after authorization. Each subsequent http.Handler can lookup the cluster in context.
 	clusterFactory := authz.NewClusterScopedFactory(config)
 
+	// Create a new "namespace-scoped" factory which will create a new namespace-scoped request
+	// after authorization. Each subsequent http.Handler can lookup the namespace in context.
+	namespaceFactory := authz.NewNamespaceScopedFactory(config)
+
 	// Create a new "helmrepo-scoped" factory which will create a new helmrepo-scoped request
 	// after authorization. Each subsequent http.Handler can lookup the helm repo in context.
 	helmRepoFactory := authz.NewHelmRepoScopedFactory(config)
@@ -143,6 +147,8 @@ func registerRoutes(config *shared.Config, routes []*Route) {
 				atomicGroup.Use(projFactory.Middleware)
 			case types.ClusterScope:
 				atomicGroup.Use(clusterFactory.Middleware)
+			case types.NamespaceScope:
+				atomicGroup.Use(namespaceFactory.Middleware)
 			case types.HelmRepoScope:
 				atomicGroup.Use(helmRepoFactory.Middleware)
 			case types.RegistryScope:

+ 13 - 6
internal/helm/filter.go → api/types/namespace.go

@@ -1,12 +1,13 @@
-package helm
+package types
 
 import (
 	"helm.sh/helm/v3/pkg/action"
+	"helm.sh/helm/v3/pkg/release"
 )
 
-// ListFilter is a struct that represents the various filter options used for
+// ReleaseListFilter is a struct that represents the various filter options used for
 // retrieving the releases
-type ListFilter struct {
+type ReleaseListFilter struct {
 	Namespace    string   `json:"namespace"`
 	Limit        int      `json:"limit"`
 	Skip         int      `json:"skip"`
@@ -21,7 +22,7 @@ type ListFilter struct {
 //
 // It returns an action.ListStates to be used in an action.List as filters for
 // releases in a certain state.
-func (h *ListFilter) listStatesFromNames() action.ListStates {
+func (h *ReleaseListFilter) listStatesFromNames() action.ListStates {
 	var res action.ListStates = 0
 
 	for _, name := range h.StatusFilter {
@@ -31,8 +32,8 @@ func (h *ListFilter) listStatesFromNames() action.ListStates {
 	return res
 }
 
-// apply sets the ListFilter options for an action.List
-func (h *ListFilter) apply(list *action.List) {
+// apply sets the ReleaseListFilter options for an action.List
+func (h *ReleaseListFilter) Apply(list *action.List) {
 	if h.Namespace == "" {
 		list.AllNamespaces = true
 	}
@@ -46,3 +47,9 @@ func (h *ListFilter) apply(list *action.List) {
 		list.ByDate = true
 	}
 }
+
+type ListReleasesRequest struct {
+	*ReleaseListFilter
+}
+
+type ListReleasesResponse []*release.Release

+ 5 - 4
dashboard/src/main/home/cluster-dashboard/chart/ChartList.tsx

@@ -46,9 +46,6 @@ const ChartList: React.FunctionComponent<Props> = ({
       const res = await api.getCharts(
         "<token>",
         {
-          namespace: namespace,
-          cluster_id: currentCluster.id,
-          storage: StorageType.Secret,
           limit: 50,
           skip: 0,
           byDate: false,
@@ -63,7 +60,11 @@ const ChartList: React.FunctionComponent<Props> = ({
             "failed",
           ],
         },
-        { id: currentProject.id }
+        {
+          id: currentProject.id,
+          cluster_id: currentCluster.id,
+          namespace: namespace,
+        }
       );
       const charts = res.data || [];
 

+ 6 - 5
dashboard/src/shared/api.tsx

@@ -417,17 +417,18 @@ const getChart = baseApi<
 
 const getCharts = baseApi<
   {
-    namespace: string;
-    cluster_id: number;
-    storage: StorageType;
     limit: number;
     skip: number;
     byDate: boolean;
     statusFilter: string[];
   },
-  { id: number }
+  {
+    id: number;
+    cluster_id: number;
+    namespace: string;
+  }
 >("GET", (pathParams) => {
-  return `/api/projects/${pathParams.id}/releases`;
+  return `/api/projects/${pathParams.id}/clusters/${pathParams.cluster_id}/namespaces/${pathParams.namespace}/releases`;
 });
 
 const getChartComponents = baseApi<

+ 7 - 5
internal/helm/agent.go

@@ -4,14 +4,16 @@ import (
 	"fmt"
 
 	"github.com/pkg/errors"
-	"github.com/porter-dev/porter/internal/kubernetes"
-	"github.com/porter-dev/porter/internal/models"
-	"github.com/porter-dev/porter/internal/repository"
 	"golang.org/x/oauth2"
 	"helm.sh/helm/v3/pkg/action"
 	"helm.sh/helm/v3/pkg/chart"
 	"helm.sh/helm/v3/pkg/release"
 	"k8s.io/helm/pkg/chartutil"
+
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/kubernetes"
+	"github.com/porter-dev/porter/internal/models"
+	"github.com/porter-dev/porter/internal/repository"
 )
 
 // Agent is a Helm agent for performing helm operations
@@ -23,11 +25,11 @@ type Agent struct {
 // ListReleases lists releases based on a ListFilter
 func (a *Agent) ListReleases(
 	namespace string,
-	filter *ListFilter,
+	filter *types.ReleaseListFilter,
 ) ([]*release.Release, error) {
 	cmd := action.NewList(a.ActionConfig)
 
-	filter.apply(cmd)
+	filter.Apply(cmd)
 
 	return cmd.Run()
 }