Sfoglia il codice sorgente

Merge branch '0.8.0-api-cleanup' of https://github.com/porter-dev/porter into 0.8.0-api-cleanup

merge w/ remote
Alexander Belanger 4 anni fa
parent
commit
a86e84321c

+ 2 - 1
.gitignore

@@ -23,6 +23,7 @@ bin
 .terraform.lock.hcl
 
 *kubeconfig*
+!*kubeconfig*.go
 
 # .tfstate files
 *.tfstate
@@ -57,4 +58,4 @@ override.tf.json
 .terraformrc
 terraform.rc
 
-tmp
+tmp

+ 1 - 5
api/client/k8s.go

@@ -60,13 +60,9 @@ func (c *Client) GetKubeconfig(
 	projectID uint,
 	clusterID uint,
 ) (*GetKubeconfigResponse, error) {
-	cl := fmt.Sprintf("%d", clusterID)
-
 	req, err := http.NewRequest(
 		"GET",
-		fmt.Sprintf("%s/projects/%d/k8s/kubeconfig?"+url.Values{
-			"cluster_id": []string{cl},
-		}.Encode(), c.BaseURL, projectID),
+		fmt.Sprintf("%s/projects/%d/clusters/%d/kubeconfig", c.BaseURL, projectID, clusterID),
 		nil,
 	)
 

+ 7 - 2
api/server/handlers/cluster/agent.go

@@ -7,6 +7,7 @@ import (
 )
 
 type KubernetesAgentGetter interface {
+	GetOutOfClusterConfig(cluster *models.Cluster) *kubernetes.OutOfClusterConfig
 	GetAgent(cluster *models.Cluster) (*kubernetes.Agent, error)
 }
 
@@ -18,12 +19,16 @@ func NewDefaultKubernetesAgentGetter(config *shared.Config) KubernetesAgentGette
 	return &DefaultKubernetesAgentGetter{config}
 }
 
-func (d *DefaultKubernetesAgentGetter) GetAgent(cluster *models.Cluster) (*kubernetes.Agent, error) {
-	ooc := &kubernetes.OutOfClusterConfig{
+func (d *DefaultKubernetesAgentGetter) GetOutOfClusterConfig(cluster *models.Cluster) *kubernetes.OutOfClusterConfig {
+	return &kubernetes.OutOfClusterConfig{
 		Repo:              d.config.Repo,
 		DigitalOceanOAuth: d.config.DOConf,
 		Cluster:           cluster,
 	}
+}
+
+func (d *DefaultKubernetesAgentGetter) GetAgent(cluster *models.Cluster) (*kubernetes.Agent, error) {
+	ooc := d.GetOutOfClusterConfig(cluster)
 
 	return kubernetes.GetAgentOutOfClusterConfig(ooc)
 }

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

@@ -0,0 +1,57 @@
+package cluster
+
+import (
+	"net/http"
+
+	"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 CreateNamespaceHandler struct {
+	handlers.PorterHandlerReadWriter
+	KubernetesAgentGetter
+}
+
+func NewCreateNamespaceHandler(
+	config *shared.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *CreateNamespaceHandler {
+	return &CreateNamespaceHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+		KubernetesAgentGetter:   NewDefaultKubernetesAgentGetter(config),
+	}
+}
+
+func (c *CreateNamespaceHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	request := &types.CreateNamespaceRequest{}
+
+	if ok := c.DecodeAndValidate(w, r, request); !ok {
+		return
+	}
+
+	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
+
+	agent, err := c.GetAgent(cluster)
+
+	if err != nil {
+		c.HandleAPIError(w, apierrors.NewErrInternal(err))
+		return
+	}
+
+	namespace, err := agent.CreateNamespace(request.Name)
+
+	if err != nil {
+		c.HandleAPIError(w, apierrors.NewErrInternal(err))
+		return
+	}
+
+	res := types.CreateNamespaceResponse{
+		Namespace: namespace,
+	}
+
+	c.WriteResult(w, res)
+}

+ 50 - 0
api/server/handlers/cluster/delete_namespace.go

@@ -0,0 +1,50 @@
+package cluster
+
+import (
+	"net/http"
+
+	"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 DeleteNamespaceHandler struct {
+	handlers.PorterHandlerReader
+	KubernetesAgentGetter
+}
+
+func NewDeleteNamespaceHandler(
+	config *shared.Config,
+	decoderValidator shared.RequestDecoderValidator,
+) *DeleteNamespaceHandler {
+	return &DeleteNamespaceHandler{
+		PorterHandlerReader:   handlers.NewDefaultPorterHandler(config, decoderValidator, nil),
+		KubernetesAgentGetter: NewDefaultKubernetesAgentGetter(config),
+	}
+}
+
+func (c *DeleteNamespaceHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	request := &types.DeleteNamespaceRequest{}
+
+	if ok := c.DecodeAndValidate(w, r, request); !ok {
+		return
+	}
+
+	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
+
+	agent, err := c.GetAgent(cluster)
+
+	if err != nil {
+		c.HandleAPIError(w, apierrors.NewErrInternal(err))
+		return
+	}
+
+	if err := agent.DeleteNamespace(request.Name); err != nil {
+		c.HandleAPIError(w, apierrors.NewErrInternal(err))
+		return
+	}
+
+	w.WriteHeader(http.StatusOK)
+}

+ 47 - 0
api/server/handlers/cluster/detect_prometheus_installed.go

@@ -0,0 +1,47 @@
+package cluster
+
+import (
+	"net/http"
+
+	"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/kubernetes/prometheus"
+	"github.com/porter-dev/porter/internal/models"
+)
+
+type DetectPrometheusInstalledHandler struct {
+	handlers.PorterHandler
+	KubernetesAgentGetter
+}
+
+func NewDetectPrometheusInstalledHandler(
+	config *shared.Config,
+) *DetectPrometheusInstalledHandler {
+	return &DetectPrometheusInstalledHandler{
+		PorterHandler:         handlers.NewDefaultPorterHandler(config, nil, nil),
+		KubernetesAgentGetter: NewDefaultKubernetesAgentGetter(config),
+	}
+}
+
+func (c *DetectPrometheusInstalledHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
+
+	agent, err := c.GetAgent(cluster)
+
+	if err != nil {
+		c.HandleAPIError(w, apierrors.NewErrInternal(err))
+		return
+	}
+
+	if _, found, err := prometheus.GetPrometheusService(agent.Clientset); err != nil {
+		c.HandleAPIError(w, apierrors.NewErrInternal(err))
+		return
+	} else if !found {
+		http.NotFound(w, r)
+		return
+	}
+
+	w.WriteHeader(http.StatusOK)
+}

+ 53 - 0
api/server/handlers/cluster/get_kubeconfig.go

@@ -0,0 +1,53 @@
+package cluster
+
+import (
+	"net/http"
+
+	"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"
+	"k8s.io/client-go/tools/clientcmd"
+)
+
+type GetTemporaryKubeconfigHandler struct {
+	handlers.PorterHandlerWriter
+	KubernetesAgentGetter
+}
+
+func NewGetTemporaryKubeconfigHandler(
+	config *shared.Config,
+	writer shared.ResultWriter,
+) *GetTemporaryKubeconfigHandler {
+	return &GetTemporaryKubeconfigHandler{
+		PorterHandlerWriter:   handlers.NewDefaultPorterHandler(config, nil, writer),
+		KubernetesAgentGetter: NewDefaultKubernetesAgentGetter(config),
+	}
+}
+
+func (c *GetTemporaryKubeconfigHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
+
+	outOfClusterConfig := c.GetOutOfClusterConfig(cluster)
+
+	kubeconfig, err := outOfClusterConfig.CreateRawConfigFromCluster()
+
+	if err != nil {
+		c.HandleAPIError(w, apierrors.NewErrInternal(err))
+		return
+	}
+
+	kubeconfigBytes, err := clientcmd.Write(*kubeconfig)
+
+	if err != nil {
+		c.HandleAPIError(w, apierrors.NewErrInternal(err))
+		return
+	}
+
+	res := &types.GetTemporaryKubeconfigResponse{
+		Kubeconfig: kubeconfigBytes,
+	}
+
+	c.WriteResult(w, res)
+}

+ 66 - 0
api/server/handlers/cluster/get_pod_metrics.go

@@ -0,0 +1,66 @@
+package cluster
+
+import (
+	"github.com/porter-dev/porter/internal/kubernetes/prometheus"
+	"net/http"
+
+	"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 GetPodMetricsHandler struct {
+	handlers.PorterHandlerReadWriter
+	KubernetesAgentGetter
+}
+
+func NewGetPodMetricsHandler(
+	config *shared.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *GetPodMetricsHandler {
+	return &GetPodMetricsHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+		KubernetesAgentGetter:   NewDefaultKubernetesAgentGetter(config),
+	}
+}
+
+func (c *GetPodMetricsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	request := &types.GetPodMetricsRequest{}
+
+	if ok := c.DecodeAndValidate(w, r, request); !ok {
+		return
+	}
+
+	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
+
+	agent, err := c.GetAgent(cluster)
+
+	if err != nil {
+		c.HandleAPIError(w, apierrors.NewErrInternal(err))
+		return
+	}
+
+	// get prometheus service
+	promSvc, found, err := prometheus.GetPrometheusService(agent.Clientset)
+
+	if err != nil || !found {
+		c.HandleAPIError(w, apierrors.NewErrInternal(err))
+		return
+	}
+
+	rawQuery, err := prometheus.QueryPrometheus(agent.Clientset, promSvc, &request.QueryOpts)
+
+	if err != nil {
+		c.HandleAPIError(w, apierrors.NewErrInternal(err))
+		return
+	}
+
+	s := string(rawQuery)
+
+	var res types.GetPodMetricsResponse = &s
+
+	c.WriteResult(w, res)
+}

+ 6 - 6
api/server/handlers/cluster/list_namespaces.go

@@ -10,22 +10,22 @@ import (
 	"github.com/porter-dev/porter/internal/models"
 )
 
-type ClusterListNamespacesHandler struct {
+type ListNamespacesHandler struct {
 	handlers.PorterHandlerWriter
 	KubernetesAgentGetter
 }
 
-func NewClusterListNamespacesHandler(
+func NewListNamespacesHandler(
 	config *shared.Config,
 	writer shared.ResultWriter,
-) *ClusterListNamespacesHandler {
-	return &ClusterListNamespacesHandler{
+) *ListNamespacesHandler {
+	return &ListNamespacesHandler{
 		PorterHandlerWriter:   handlers.NewDefaultPorterHandler(config, nil, writer),
 		KubernetesAgentGetter: NewDefaultKubernetesAgentGetter(config),
 	}
 }
 
-func (c *ClusterListNamespacesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+func (c *ListNamespacesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
 
 	agent, err := c.GetAgent(cluster)
@@ -42,7 +42,7 @@ func (c *ClusterListNamespacesHandler) ServeHTTP(w http.ResponseWriter, r *http.
 		return
 	}
 
-	res := types.ClusterListNamespacesResponse{
+	res := types.ListNamespacesResponse{
 		NamespaceList: namespaceList,
 	}
 

+ 49 - 0
api/server/handlers/cluster/list_nginx_ingresses.go

@@ -0,0 +1,49 @@
+package cluster
+
+import (
+	"net/http"
+
+	"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/kubernetes/prometheus"
+	"github.com/porter-dev/porter/internal/models"
+)
+
+type ListNGINXIngressesHandler struct {
+	handlers.PorterHandlerWriter
+	KubernetesAgentGetter
+}
+
+func NewListNGINXIngressesHandler(
+	config *shared.Config,
+	writer shared.ResultWriter,
+) *ListNGINXIngressesHandler {
+	return &ListNGINXIngressesHandler{
+		PorterHandlerWriter:   handlers.NewDefaultPorterHandler(config, nil, writer),
+		KubernetesAgentGetter: NewDefaultKubernetesAgentGetter(config),
+	}
+}
+
+func (c *ListNGINXIngressesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
+
+	agent, err := c.GetAgent(cluster)
+
+	if err != nil {
+		c.HandleAPIError(w, apierrors.NewErrInternal(err))
+		return
+	}
+
+	ingresses, err := prometheus.GetIngressesWithNGINXAnnotation(agent.Clientset)
+
+	if err != nil {
+		c.HandleAPIError(w, apierrors.NewErrInternal(err))
+		return
+	}
+
+	var res types.ListNGINXIngressesResponse = ingresses
+
+	c.WriteResult(w, res)
+}

+ 168 - 1
api/server/router/cluster.go

@@ -96,7 +96,7 @@ func getClusterRoutes(
 		},
 	)
 
-	listNamespacesHandler := cluster.NewClusterListNamespacesHandler(
+	listNamespacesHandler := cluster.NewListNamespacesHandler(
 		config,
 		factory.GetResultWriter(),
 	)
@@ -107,5 +107,172 @@ func getClusterRoutes(
 		Router:   r,
 	})
 
+	// POST /api/projects/{project_id}/clusters/{cluster_id}/namespaces/create -> cluster.NewCreateNamespaceHandler
+	createNamespaceEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbCreate,
+			Method: types.HTTPVerbPost,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/namespaces/create",
+			},
+			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,
+	})
+
+	// DELETE /api/projects/{project_id}/clusters/{cluster_id}/namespaces/delete -> cluster.NewDeleteNamespaceHandler
+	deleteNamespaceEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbDelete,
+			Method: types.HTTPVerbDelete,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/namespaces/delete",
+			},
+			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,
+	})
+
+	// GET /api/projects/{project_id}/clusters/{cluster_id}/kubeconfig -> cluster.NewGetTemporaryKubeconfigHandler
+	getTemporaryKubeconfigEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbGet,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/kubeconfig",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.ClusterScope,
+			},
+		},
+	)
+
+	getTemporaryKubeconfigHandler := cluster.NewGetTemporaryKubeconfigHandler(
+		config,
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &Route{
+		Endpoint: getTemporaryKubeconfigEndpoint,
+		Handler:  getTemporaryKubeconfigHandler,
+		Router:   r,
+	})
+
+	// GET /api/projects/{project_id}/clusters/{cluster_id}/prometheus/detect -> cluster.NewDetectPrometheusInstalledHandler
+	detectPrometheusInstalledEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbGet,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/prometheus/detect",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.ClusterScope,
+			},
+		},
+	)
+
+	detectPrometheusInstalledHandler := cluster.NewDetectPrometheusInstalledHandler(config)
+
+	routes = append(routes, &Route{
+		Endpoint: detectPrometheusInstalledEndpoint,
+		Handler:  detectPrometheusInstalledHandler,
+		Router:   r,
+	})
+
+	// GET /api/projects/{project_id}/clusters/{cluster_id}/prometheus/ingresses -> cluster.NewListNGINXIngressesHandler
+	listNGINXIngressesEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbGet,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/prometheus/ingresses",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.ClusterScope,
+			},
+		},
+	)
+
+	listNGINXIngressesHandler := cluster.NewListNGINXIngressesHandler(
+		config,
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &Route{
+		Endpoint: listNGINXIngressesEndpoint,
+		Handler:  listNGINXIngressesHandler,
+		Router:   r,
+	})
+
+	// GET /api/projects/{project_id}/clusters/{cluster_id}/metrics -> cluster.NewGetPodMetricsHandler
+	getPodMetricsEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbGet,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/metrics",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.ClusterScope,
+			},
+		},
+	)
+
+	getPodMetricsHandler := cluster.NewGetPodMetricsHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &Route{
+		Endpoint: getPodMetricsEndpoint,
+		Handler:  getPodMetricsHandler,
+		Router:   r,
+	})
+
 	return routes, newPath
 }

+ 29 - 2
api/types/cluster.go

@@ -1,6 +1,9 @@
 package types
 
-import v1 "k8s.io/api/core/v1"
+import (
+	"github.com/porter-dev/porter/internal/kubernetes/prometheus"
+	v1 "k8s.io/api/core/v1"
+)
 
 type Cluster struct {
 	ID uint `json:"id"`
@@ -43,6 +46,30 @@ const (
 	Kube ClusterService = "kube"
 )
 
-type ClusterListNamespacesResponse struct {
+type ListNamespacesResponse struct {
 	*v1.NamespaceList
 }
+
+type CreateNamespaceRequest struct {
+	Name string `json:"name" form:"required"`
+}
+
+type CreateNamespaceResponse struct {
+	*v1.Namespace
+}
+
+type DeleteNamespaceRequest struct {
+	Name string `json:"name" form:"required"`
+}
+
+type GetTemporaryKubeconfigResponse struct {
+	Kubeconfig []byte `json:"kubeconfig"`
+}
+
+type ListNGINXIngressesResponse []prometheus.SimpleIngress
+
+type GetPodMetricsRequest struct {
+	prometheus.QueryOpts
+}
+
+type GetPodMetricsResponse *string

+ 8 - 4
dashboard/src/main/home/cluster-dashboard/ClusterDashboard.tsx

@@ -6,7 +6,12 @@ import { Route, Switch } from "react-router-dom";
 
 import { Context } from "shared/Context";
 import { ChartType, ClusterType } from "shared/types";
-import { getQueryParam, PorterUrl, pushFiltered, pushQueryParams } from "shared/routing";
+import {
+  getQueryParam,
+  PorterUrl,
+  pushFiltered,
+  pushQueryParams,
+} from "shared/routing";
 
 import DashboardHeader from "./DashboardHeader";
 import ChartList from "./chart/ChartList";
@@ -57,11 +62,10 @@ class ClusterDashboard extends Component<PropsType, StateType> {
     api
       .getPrometheusIsInstalled(
         "<token>",
-        {
-          cluster_id: currentCluster.id,
-        },
+        {},
         {
           id: currentProject.id,
+          cluster_id: currentCluster.id,
         }
       )
       .then((res) => {

+ 3 - 2
dashboard/src/main/home/cluster-dashboard/NamespaceSelector.tsx

@@ -29,10 +29,11 @@ export default class NamespaceSelector extends Component<PropsType, StateType> {
     api
       .getNamespaces(
         "<token>",
+        {},
         {
+          id: currentProject.id,
           cluster_id: currentCluster.id,
-        },
-        { id: currentProject.id }
+        }
       )
       .then((res) => {
         if (this._isMounted) {

+ 5 - 7
dashboard/src/main/home/cluster-dashboard/dashboard/Metrics.tsx

@@ -58,11 +58,10 @@ const Metrics: React.FC = () => {
       ),
       api.getPrometheusIsInstalled(
         "<token>",
-        {
-          cluster_id: currentCluster.id,
-        },
+        {},
         {
           id: currentProject.id,
+          cluster_id: currentCluster.id,
         }
       ),
     ])
@@ -73,11 +72,10 @@ const Metrics: React.FC = () => {
         api
           .getNGINXIngresses(
             "<token>",
-            {
-              cluster_id: currentCluster.id,
-            },
+            {},
             {
               id: currentProject.id,
+              cluster_id: currentCluster.id,
             }
           )
           .then((res) => {
@@ -218,7 +216,6 @@ const Metrics: React.FC = () => {
       const res = await api.getMetrics(
         "<token>",
         {
-          cluster_id: currentCluster.id,
           metric: selectedMetric,
           shouldsum: false,
           kind: "Ingress",
@@ -235,6 +232,7 @@ const Metrics: React.FC = () => {
         },
         {
           id: currentProject.id,
+          cluster_id: currentCluster.id,
         }
       );
 

+ 3 - 2
dashboard/src/main/home/cluster-dashboard/env-groups/CreateEnvGroup.tsx

@@ -113,10 +113,11 @@ export default class CreateEnvGroup extends Component<PropsType, StateType> {
     api
       .getNamespaces(
         "<token>",
+        {},
         {
+          id: currentProject.id,
           cluster_id: this.props.currentCluster.id,
-        },
-        { id: currentProject.id }
+        }
       )
       .then((res) => {
         if (res.data) {

+ 4 - 5
dashboard/src/main/home/cluster-dashboard/expanded-chart/metrics/MetricsSection.tsx

@@ -98,11 +98,10 @@ const MetricsSection: React.FunctionComponent<PropsType> = ({
       api
         .getNGINXIngresses(
           "<token>",
-          {
-            cluster_id: currentCluster.id,
-          },
+          {},
           {
             id: currentProject.id,
+            cluster_id: currentCluster.id,
           }
         )
         .then((res) => {
@@ -238,7 +237,6 @@ const MetricsSection: React.FunctionComponent<PropsType> = ({
       const res = await api.getMetrics(
         "<token>",
         {
-          cluster_id: currentCluster.id,
           metric: metricType,
           shouldsum: shouldsum,
           kind: selectedController?.kind,
@@ -251,6 +249,7 @@ const MetricsSection: React.FunctionComponent<PropsType> = ({
         },
         {
           id: currentProject.id,
+          cluster_id: currentCluster.id,
         }
       );
 
@@ -298,7 +297,6 @@ const MetricsSection: React.FunctionComponent<PropsType> = ({
       const res = await api.getMetrics(
         "<token>",
         {
-          cluster_id: currentCluster.id,
           metric: selectedMetric,
           shouldsum: shouldsum,
           kind: selectedController?.kind,
@@ -311,6 +309,7 @@ const MetricsSection: React.FunctionComponent<PropsType> = ({
         },
         {
           id: currentProject.id,
+          cluster_id: currentCluster.id,
         }
       );
 

+ 3 - 2
dashboard/src/main/home/launch/launch-flow/SettingsPage.tsx

@@ -91,10 +91,11 @@ class SettingsPage extends Component<PropsType, StateType> {
     api
       .getNamespaces(
         "<token>",
+        {},
         {
+          id: currentProject.id,
           cluster_id: id,
-        },
-        { id: currentProject.id }
+        }
       )
       .then((res) => {
         if (res.data) {

+ 2 - 1
dashboard/src/main/home/modals/DeleteNamespaceModal.tsx

@@ -27,9 +27,10 @@ const DeleteNamespaceModal = () => {
     api
       .deleteNamespace(
         "<token>",
-        { name: currentModalData.metadata.name, cluster_id: currentCluster.id },
+        { name: currentModalData.metadata.name },
         {
           id: currentProject.id,
+          cluster_id: currentCluster.id,
         }
       )
       .then((res) => {

+ 24 - 17
dashboard/src/shared/api.tsx

@@ -593,7 +593,6 @@ const getMatchingPods = baseApi<
 
 const getMetrics = baseApi<
   {
-    cluster_id: number;
     metric: string;
     shouldsum: boolean;
     pods?: string[];
@@ -605,27 +604,32 @@ const getMetrics = baseApi<
     endrange: number;
     resolution: string;
   },
-  { id: number }
+  {
+    id: number;
+    cluster_id: number;
+  }
 >("GET", (pathParams) => {
-  return `/api/projects/${pathParams.id}/k8s/metrics`;
+  return `/api/projects/${pathParams.id}/clusters/${pathParams.cluster_id}/metrics`;
 });
 
 const getNamespaces = baseApi<
+  {},
   {
+    id: number;
     cluster_id: number;
-  },
-  { id: number }
+  }
 >("GET", (pathParams) => {
-  return `/api/projects/${pathParams.id}/k8s/namespaces`;
+  return `/api/projects/${pathParams.id}/clusters/${pathParams.cluster_id}/namespaces`;
 });
 
 const getNGINXIngresses = baseApi<
+  {},
   {
+    id: number;
     cluster_id: number;
-  },
-  { id: number }
+  }
 >("GET", (pathParams) => {
-  return `/api/projects/${pathParams.id}/k8s/prometheus/ingresses`;
+  return `/api/projects/${pathParams.id}/clusters/${pathParams.cluster_id}/prometheus/ingresses`;
 });
 
 const getOAuthIds = baseApi<
@@ -655,12 +659,13 @@ const getProjectRepos = baseApi<{}, { id: number }>("GET", (pathParams) => {
 const getProjects = baseApi("GET", "/api/projects");
 
 const getPrometheusIsInstalled = baseApi<
+  {},
   {
+    id: number;
     cluster_id: number;
-  },
-  { id: number }
+  }
 >("GET", (pathParams) => {
-  return `/api/projects/${pathParams.id}/k8s/prometheus/detect`;
+  return `/api/projects/${pathParams.id}/clusters/${pathParams.cluster_id}/prometheus/detect`;
 });
 
 const getRegistryIntegrations = baseApi("GET", "/api/integrations/registry");
@@ -946,18 +951,20 @@ const createNamespace = baseApi<
   { id: number; cluster_id: number }
 >("POST", (pathParams) => {
   let { id, cluster_id } = pathParams;
-  return `/api/projects/${id}/k8s/namespaces/create?cluster_id=${cluster_id}`;
+  return `/api/projects/${id}/clusters/${cluster_id}/namespaces/create`;
 });
 
 const deleteNamespace = baseApi<
   {
     name: string;
-    cluster_id: number;
   },
-  { id: number }
+  {
+    id: number;
+    cluster_id: number;
+  }
 >("DELETE", (pathParams) => {
-  let { id } = pathParams;
-  return `/api/projects/${id}/k8s/namespaces/delete`;
+  let { id, cluster_id } = pathParams;
+  return `/api/projects/${id}/clusters/${cluster_id}/namespaces/delete`;
 });
 
 const deleteJob = baseApi<