Mohammed Nafees vor 4 Jahren
Ursprung
Commit
acfa1b12fc

+ 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)
+}

+ 179 - 0
api/server/router/cluster_v1.go

@@ -0,0 +1,179 @@
+package router
+
+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/types"
+)
+
+func NewV1ClusterScopedRegisterer(children ...*Registerer) *Registerer {
+	return &Registerer{
+		GetRoutes: GetClusterScopedRoutes,
+		Children:  children,
+	}
+}
+
+func GetV1ClusterScopedRoutes(
+	r chi.Router,
+	config *config.Config,
+	basePath *types.Path,
+	factory shared.APIEndpointFactory,
+	children ...*Registerer,
+) []*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,
+) ([]*Route, *types.Path) {
+	relPath := "/clusters/{cluster_id}"
+
+	newPath := &types.Path{
+		Parent:       basePath,
+		RelativePath: relPath,
+	}
+
+	var routes []*Route
+
+	// ----------------
+	// NAMESPACES BEGIN
+	// ----------------
+
+	// 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, &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, &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, &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, &Route{
+		Endpoint: deleteNamespaceEndpoint,
+		Handler:  deleteNamespaceHandler,
+		Router:   r,
+	})
+
+	// ----------------
+	// NAMESPACES END
+	// ----------------
+
+	return routes, newPath
+}

+ 55 - 0
api/server/router/namespace_v1.go

@@ -0,0 +1,55 @@
+package router
+
+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/types"
+)
+
+func NewV1NamespaceScopedRegisterer(children ...*Registerer) *Registerer {
+	return &Registerer{
+		GetRoutes: GetNamespaceScopedRoutes,
+		Children:  children,
+	}
+}
+
+func GetV1NamespaceScopedRoutes(
+	r chi.Router,
+	config *config.Config,
+	basePath *types.Path,
+	factory shared.APIEndpointFactory,
+	children ...*Registerer,
+) []*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,
+) ([]*Route, *types.Path) {
+	relPath := "/namespaces/{namespace}"
+
+	newPath := &types.Path{
+		Parent:       basePath,
+		RelativePath: relPath,
+	}
+
+	var routes []*Route
+
+	return routes, newPath
+}

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

@@ -104,6 +104,29 @@ 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 []*Route
+
+		v1NamespaceRoutes := NewV1ClusterScopedRegisterer().GetRoutes(
+			r,
+			config,
+			&types.Path{
+				RelativePath: "",
+			},
+			endpointFactory,
+		)
+
+		allRoutes = append(allRoutes, v1NamespaceRoutes...)
+
+		registerRoutes(config, allRoutes)
+	})
+
 	staticFilePath := config.ServerConf.StaticFilePath
 	fs := http.FileServer(http.Dir(staticFilePath))
 

+ 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
 }

+ 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