فهرست منبع

context merge conflict resolved

jusrhee 5 سال پیش
والد
کامیت
70b885b1aa

+ 1 - 1
.air.toml

@@ -15,7 +15,7 @@ full_bin = "tmp/migrate; tmp/app"
 # Watch these filename extensions.
 include_ext = ["go", "mod", "sum", "html"]
 # Ignore these filename extensions or directories.
-exclude_dir = ["tmp"]
+exclude_dir = ["tmp", "dashboard"]
 # Watch these directories if you specified.
 include_dir = []
 # Exclude files.

+ 4 - 4
dashboard/src/main/home/Home.tsx

@@ -19,10 +19,11 @@ export default class Home extends Component<PropsType, StateType> {
 
   renderDashboard = () => {
     if (this.context.currentCluster) {
-      return <Dashboard />
+      return <DashboardWrapper><Dashboard /></DashboardWrapper>
     }
 
     return (
+      <DashboardWrapper>
         <Placeholder>
           <Bold>Porter - Getting Started</Bold><br /><br />
           1. Navigate to <A onClick={() => {this.context.setCurrentModal('ClusterConfigModal')}}>+ Add a Cluster</A> and provide a kubeconfig. *<br /><br />
@@ -34,6 +35,7 @@ export default class Home extends Component<PropsType, StateType> {
           
           * Make sure all fields are explicitly declared (e.g., certs and keys).
         </Placeholder>
+      </DashboardWrapper>
     );
   }
 
@@ -52,9 +54,7 @@ export default class Home extends Component<PropsType, StateType> {
 
         <Sidebar logOut={this.props.logOut} />
         <StyledDashboard>
-          <DashboardWrapper>
-            {this.renderDashboard()}
-          </DashboardWrapper>
+          {this.renderDashboard()}
         </StyledDashboard>
       </StyledHome>
     );

+ 23 - 0
dashboard/src/shared/types.tsx

@@ -6,6 +6,29 @@ export interface KubeContextConfig {
   user: string
 }
 
+export interface Chart {
+  name: string,
+  info: {
+    last_deployed: string,
+    deleted: string,
+    description: string,
+    status: string
+  },
+  chart: {
+    metadata: {
+      name: string,
+      home: string,
+      sources: string,
+      version: string,
+      description: string,
+      icon: string,
+      apiVersion: string
+    },
+  },
+  version: number,
+  namespace: string
+}
+
 export enum StorageType {
   Secret = 'secret',
   ConfigMap = 'configmap',

+ 23 - 40
docs/API.md

@@ -406,31 +406,22 @@ User{
 
 **URL parameters:** N/A
 
-**Query parameters:** N/A
-
-**Request Body**:
+**Query parameters:** 
 
 ```js
 {
-  "user_id": Number,
-  "helm": {
-    // The namespace of the cluster to be used
-    "namespace": String,
-    // The name of the context in the kubeconfig being used
-    "context": String,
-    // The Helm storage option to use
-    "storage": String("secret"|"configmap"|"memory")
-  },
-  "filter": {
-    "namespace": String,
-    "limit": Number,
-    "skip": Number,
-    "byDate": Boolean,
-    "statusFilter": []String
-  }
+  "namespace": String,
+  "context": String,
+  "storage": String("secret"|"configmap"|"memory"),
+  "limit": Number,
+  "skip": Number,
+  "byDate": Boolean,
+  "statusFilter": []String("unknown"|"deployed"|"uninstalled"|"superseded"|"failed"|"uninstalling"|"pending-install"|"pending-upgrade"|"pending-rollback")
 }
 ```
 
+**Request Body**: N/A
+
 **Successful Response Body**: the full body is determined by the [release specification](https://pkg.go.dev/helm.sh/helm/v3@v3.3.4/pkg/release#Release): listed here is a subset of fields deemed to be most relevant. Note that all of the top-level fields are optional.
 
 ```js
@@ -498,24 +489,21 @@ User{
 - `name` The name of the release.
 - `revision` The number of the release (set to `0` for the latest deployed release).
 
-**Query parameters:** N/A
-
-**Request Body**:
+**Query parameters:** 
 
 ```js
 {
-  "user_id": Number,
-  "helm": {
-    // The namespace of the cluster to be used
-    "namespace": String,
-    // The name of the context in the kubeconfig being used
-    "context": String,
-    // The Helm storage option to use
-    "storage": String("secret"|"configmap"|"memory")
-  }
+  // The namespace of the cluster to be used
+  "namespace": String,
+  // The name of the context in the kubeconfig being used
+  "context": String,
+  // The Helm storage option to use
+  "storage": String("secret"|"configmap"|"memory")
 }
 ```
 
+**Request Body**: N/A
+
 **Successful Response Body**: the full body is determined by the [release specification](https://pkg.go.dev/helm.sh/helm/v3@v3.3.4/pkg/release#Release): listed here is a subset of fields deemed to be most relevant. Note that all of the top-level fields are optional.
 
 ```js
@@ -584,18 +572,13 @@ Chart{
 
 **Query parameters:** N/A
 
-**Request Body**: 
-
 ```js
-{
-  "user_id": Number,
-  "k8s": {
-    // The name of the context in the kubeconfig being used
-    "context": String,
-  }
-}
+// The name of the context in the kubeconfig being used
+"context": String,
 ```
 
+**Request Body**: N/A
+
 **Successful Response Body**: the full body is determined by the [namespace specification](https://pkg.go.dev/k8s.io/api/core/v1#NamespaceList), but we're primarily only interested in namespace `name`:
 
 ```js

+ 63 - 11
internal/forms/chart.go

@@ -1,39 +1,91 @@
 package forms
 
 import (
+	"fmt"
+	"net/url"
+	"strconv"
+
 	"github.com/porter-dev/porter/internal/helm"
 	"github.com/porter-dev/porter/internal/repository"
 )
 
 // ChartForm is the generic base type for CRUD operations on charts
 type ChartForm struct {
-	HelmOptions *helm.Form `json:"helm" form:"required"`
-	UserID      uint       `json:"user_id"`
+	*helm.Form
+}
+
+// PopulateHelmOptionsFromQueryParams populates fields in the ChartForm using the passed
+// url.Values (the parsed query params)
+func (cf *ChartForm) PopulateHelmOptionsFromQueryParams(vals url.Values) {
+	fmt.Println("HI THERE", vals, vals["context"])
+
+	if context, ok := vals["context"]; ok && len(context) == 1 {
+		fmt.Println("SETTING CONTEXT", context[0])
+
+		cf.Context = context[0]
+	}
+
+	if namespace, ok := vals["namespace"]; ok && len(namespace) == 1 {
+		cf.Namespace = namespace[0]
+	}
+
+	if storage, ok := vals["storage"]; ok && len(storage) == 1 {
+		cf.Storage = storage[0]
+	}
 }
 
-// PopulateHelmOptions uses the passed user ID to populate the HelmOptions object
-func (cf *ChartForm) PopulateHelmOptions(repo repository.UserRepository) error {
-	user, err := repo.ReadUser(cf.UserID)
+// PopulateHelmOptionsFromUserID uses the passed user ID to populate the HelmOptions object
+func (cf *ChartForm) PopulateHelmOptionsFromUserID(userID uint, repo repository.UserRepository) error {
+	user, err := repo.ReadUser(userID)
 
 	if err != nil {
 		return err
 	}
 
-	cf.HelmOptions.AllowedContexts = user.ContextToSlice()
-	cf.HelmOptions.KubeConfig = user.RawKubeConfig
+	cf.AllowedContexts = user.ContextToSlice()
+	cf.KubeConfig = user.RawKubeConfig
 
 	return nil
 }
 
 // ListChartForm represents the accepted values for listing Helm charts
 type ListChartForm struct {
-	ChartForm
-	ListFilter *helm.ListFilter `json:"filter" form:"required"`
+	*ChartForm
+	*helm.ListFilter
+}
+
+// PopulateListFromQueryParams populates fields in the ListChartForm using the passed
+// url.Values (the parsed query params). It calls the underlying
+// PopulateHelmOptionsFromQueryParams
+func (lcf *ListChartForm) PopulateListFromQueryParams(vals url.Values) {
+	lcf.PopulateHelmOptionsFromQueryParams(vals)
+
+	if limit, ok := vals["limit"]; ok && len(limit) == 1 {
+		if limitInt, err := strconv.ParseInt(limit[0], 10, 64); err == nil {
+			lcf.ListFilter.Limit = int(limitInt)
+		}
+	}
+
+	if skip, ok := vals["skip"]; ok && len(skip) == 1 {
+		if skipInt, err := strconv.ParseInt(skip[0], 10, 64); err == nil {
+			lcf.ListFilter.Skip = int(skipInt)
+		}
+	}
+
+	if byDate, ok := vals["byDate"]; ok && len(byDate) == 1 {
+		if byDateBool, err := strconv.ParseBool(byDate[0]); err == nil {
+			lcf.ListFilter.ByDate = byDateBool
+		}
+	}
+
+	if statusFilter, ok := vals["statusFilter"]; ok {
+		lcf.ListFilter.StatusFilter = statusFilter
+	}
 }
 
 // GetChartForm represents the accepted values for getting a single Helm chart
 type GetChartForm struct {
-	ChartForm
+	*ChartForm
 	Name     string `json:"name" form:"required"`
-	Revision int    `json:"release"`
+	Revision int    `json:"revision"`
 }

+ 16 - 7
internal/forms/k8s.go

@@ -1,26 +1,35 @@
 package forms
 
 import (
+	"net/url"
+
 	"github.com/porter-dev/porter/internal/kubernetes"
 	"github.com/porter-dev/porter/internal/repository"
 )
 
 // K8sForm is the generic base type for CRUD operations on k8s objects
 type K8sForm struct {
-	K8sOptions *kubernetes.OutOfClusterConfig `json:"k8s" form:"required"`
-	UserID     uint                           `json:"user_id"`
+	*kubernetes.OutOfClusterConfig
+}
+
+// PopulateK8sOptionsFromQueryParams populates fields in the ChartForm using the passed
+// url.Values (the parsed query params)
+func (kf *K8sForm) PopulateK8sOptionsFromQueryParams(vals url.Values) {
+	if context, ok := vals["context"]; ok && len(context) == 1 {
+		kf.Context = context[0]
+	}
 }
 
-// PopulateK8sOptions uses the passed user ID to populate the HelmOptions object
-func (kf *K8sForm) PopulateK8sOptions(repo repository.UserRepository) error {
-	user, err := repo.ReadUser(kf.UserID)
+// PopulateK8sOptionsFromUserID uses the passed userID to populate the HelmOptions object
+func (kf *K8sForm) PopulateK8sOptionsFromUserID(userID uint, repo repository.UserRepository) error {
+	user, err := repo.ReadUser(userID)
 
 	if err != nil {
 		return err
 	}
 
-	kf.K8sOptions.AllowedContexts = user.ContextToSlice()
-	kf.K8sOptions.KubeConfig = user.RawKubeConfig
+	kf.AllowedContexts = user.ContextToSlice()
+	kf.KubeConfig = user.RawKubeConfig
 
 	return nil
 }

+ 40 - 15
server/api/chart_handler.go

@@ -3,6 +3,7 @@ package api
 import (
 	"encoding/json"
 	"net/http"
+	"net/url"
 	"strconv"
 
 	"github.com/go-chi/chi"
@@ -18,16 +19,32 @@ const (
 
 // HandleListCharts retrieves a list of charts with various filter options
 func (app *App) HandleListCharts(w http.ResponseWriter, r *http.Request) {
-	// get the filter options
-	form := &forms.ListChartForm{}
+	session, err := app.store.Get(r, app.cookieName)
 
-	// decode from JSON to form value
-	if err := json.NewDecoder(r.Body).Decode(form); err != nil {
+	if err != nil {
 		app.handleErrorFormDecoding(err, ErrChartDecode, w)
 		return
 	}
 
-	form.PopulateHelmOptions(app.repo.User)
+	vals, err := url.ParseQuery(r.URL.RawQuery)
+
+	if err != nil {
+		app.handleErrorFormDecoding(err, ErrChartDecode, w)
+		return
+	}
+
+	// get the filter options
+	form := &forms.ListChartForm{
+		ChartForm: &forms.ChartForm{
+			Form: &helm.Form{},
+		},
+		ListFilter: &helm.ListFilter{},
+	}
+	form.PopulateListFromQueryParams(vals)
+
+	if sessID, ok := session.Values["user_id"].(uint); ok {
+		form.PopulateHelmOptionsFromUserID(sessID, app.repo.User)
+	}
 
 	// validate the form
 	if err := app.validator.Struct(form); err != nil {
@@ -37,15 +54,14 @@ func (app *App) HandleListCharts(w http.ResponseWriter, r *http.Request) {
 
 	// create a new agent
 	var agent *helm.Agent
-	var err error
 
 	if app.testing {
 		agent = app.TestAgents.HelmAgent
 	} else {
-		agent, err = helm.GetAgentOutOfClusterConfig(form.HelmOptions, app.logger)
+		agent, err = helm.GetAgentOutOfClusterConfig(form.ChartForm.Form, app.logger)
 	}
 
-	releases, err := agent.ListReleases(form.HelmOptions.Namespace, form.ListFilter)
+	releases, err := agent.ListReleases(form.Namespace, form.ListFilter)
 
 	if err != nil {
 		app.handleErrorFormValidation(err, ErrChartValidateFields, w)
@@ -60,28 +76,37 @@ func (app *App) HandleListCharts(w http.ResponseWriter, r *http.Request) {
 
 // HandleGetChart retrieves a single chart based on a name and revision
 func (app *App) HandleGetChart(w http.ResponseWriter, r *http.Request) {
-	name := chi.URLParam(r, "name")
-	revision, err := strconv.ParseUint(chi.URLParam(r, "revision"), 0, 64)
+	session, err := app.store.Get(r, app.cookieName)
 
-	// decode from JSON to form value
 	if err != nil {
 		app.handleErrorFormDecoding(err, ErrChartDecode, w)
 		return
 	}
 
+	name := chi.URLParam(r, "name")
+	revision, err := strconv.ParseUint(chi.URLParam(r, "revision"), 0, 64)
+
 	// get the filter options
 	form := &forms.GetChartForm{
+		ChartForm: &forms.ChartForm{
+			Form: &helm.Form{},
+		},
 		Name:     name,
 		Revision: int(revision),
 	}
 
-	// decode from JSON to form value
-	if err := json.NewDecoder(r.Body).Decode(form); err != nil {
+	vals, err := url.ParseQuery(r.URL.RawQuery)
+
+	if err != nil {
 		app.handleErrorFormDecoding(err, ErrChartDecode, w)
 		return
 	}
 
-	form.PopulateHelmOptions(app.repo.User)
+	form.PopulateHelmOptionsFromQueryParams(vals)
+
+	if sessID, ok := session.Values["user_id"].(uint); ok {
+		form.PopulateHelmOptionsFromUserID(sessID, app.repo.User)
+	}
 
 	// validate the form
 	if err := app.validator.Struct(form); err != nil {
@@ -95,7 +120,7 @@ func (app *App) HandleGetChart(w http.ResponseWriter, r *http.Request) {
 	if app.testing {
 		agent = app.TestAgents.HelmAgent
 	} else {
-		agent, err = helm.GetAgentOutOfClusterConfig(form.HelmOptions, app.logger)
+		agent, err = helm.GetAgentOutOfClusterConfig(form.ChartForm.Form, app.logger)
 	}
 
 	release, err := agent.GetRelease(form.Name, form.Revision)

+ 21 - 29
server/api/chart_handler_test.go

@@ -3,6 +3,7 @@ package api_test
 import (
 	"encoding/json"
 	"net/http"
+	"net/url"
 	"reflect"
 	"strings"
 	"testing"
@@ -85,24 +86,18 @@ var listChartsTests = []*chartTest{
 		initializers: []func(tester *tester){
 			initDefaultCharts,
 		},
-		msg:      "List charts",
-		method:   "GET",
-		endpoint: "/api/charts",
-		body: `{
-			"user_id": 1,
-			"helm": {
-				"namespace": "",
-				"context": "context-test",
-				"storage": "memory"
-			},
-			"filter": {
-				"namespace": "",
-				"limit": 20,
-				"skip": 0,
-				"byDate": false,
-				"statusFilter": ["deployed"]
-			}
-		}`,
+		msg:    "List charts",
+		method: "GET",
+		endpoint: "/api/charts?" + url.Values{
+			"namespace":    []string{""},
+			"context":      []string{"context-test"},
+			"storage":      []string{"memory"},
+			"limit":        []string{"20"},
+			"skip":         []string{"0"},
+			"byDate":       []string{"false"},
+			"statusFilter": []string{"deployed"},
+		}.Encode(),
+		body:      "",
 		expStatus: http.StatusOK,
 		expBody:   releaseStubsToChartJSON(sampleReleaseStubs),
 		useCookie: true,
@@ -121,17 +116,14 @@ var getChartTests = []*chartTest{
 		initializers: []func(tester *tester){
 			initDefaultCharts,
 		},
-		msg:      "Get charts",
-		method:   "GET",
-		endpoint: "/api/charts/airwatch/0",
-		body: `{
-			"user_id": 1,
-			"helm": {
-				"namespace": "",
-				"context": "context-test",
-				"storage": "memory"
-			}
-		}`,
+		msg:    "Get charts",
+		method: "GET",
+		endpoint: "/api/charts/airwatch/0?" + url.Values{
+			"namespace": []string{""},
+			"context":   []string{"context-test"},
+			"storage":   []string{"memory"},
+		}.Encode(),
+		body:      "",
 		expStatus: http.StatusOK,
 		expBody:   releaseStubToChartJSON(sampleReleaseStubs[0]),
 		useCookie: true,

+ 21 - 7
server/api/k8s_handler.go

@@ -3,6 +3,7 @@ package api
 import (
 	"encoding/json"
 	"net/http"
+	"net/url"
 
 	"github.com/porter-dev/porter/internal/kubernetes"
 
@@ -17,15 +18,29 @@ const (
 
 // HandleListNamespaces retrieves a list of namespaces
 func (app *App) HandleListNamespaces(w http.ResponseWriter, r *http.Request) {
-	form := &forms.K8sForm{}
+	session, err := app.store.Get(r, app.cookieName)
 
-	// decode from JSON to form value
-	if err := json.NewDecoder(r.Body).Decode(form); err != nil {
-		app.handleErrorFormDecoding(err, ErrK8sDecode, w)
+	if err != nil {
+		app.handleErrorFormDecoding(err, ErrChartDecode, w)
+		return
+	}
+
+	vals, err := url.ParseQuery(r.URL.RawQuery)
+
+	if err != nil {
+		app.handleErrorFormDecoding(err, ErrChartDecode, w)
 		return
 	}
 
-	form.PopulateK8sOptions(app.repo.User)
+	// get the filter options
+	form := &forms.K8sForm{
+		OutOfClusterConfig: &kubernetes.OutOfClusterConfig{},
+	}
+	form.PopulateK8sOptionsFromQueryParams(vals)
+
+	if sessID, ok := session.Values["user_id"].(uint); ok {
+		form.PopulateK8sOptionsFromUserID(sessID, app.repo.User)
+	}
 
 	// validate the form
 	if err := app.validator.Struct(form); err != nil {
@@ -35,12 +50,11 @@ func (app *App) HandleListNamespaces(w http.ResponseWriter, r *http.Request) {
 
 	// create a new agent
 	var agent *kubernetes.Agent
-	var err error
 
 	if app.testing {
 		agent = app.TestAgents.K8sAgent
 	} else {
-		agent, err = kubernetes.GetAgentOutOfClusterConfig(form.K8sOptions)
+		agent, err = kubernetes.GetAgentOutOfClusterConfig(form.OutOfClusterConfig)
 	}
 
 	namespaces, err := agent.ListNamespaces()

+ 7 - 10
server/api/k8s_handler_test.go

@@ -3,6 +3,7 @@ package api_test
 import (
 	"encoding/json"
 	"net/http"
+	"net/url"
 	"reflect"
 	"strings"
 	"testing"
@@ -76,16 +77,12 @@ var listNamespacesTests = []*k8sTest{
 		initializers: []func(tester *tester){
 			initDefaultK8s,
 		},
-		msg:      "List namespaces",
-		method:   "GET",
-		endpoint: "/api/k8s/namespaces",
-		body: `{
-			"user_id": 1,
-			"k8s": {
-				"namespace": "",
-				"context": "context-test"
-			}
-		}`,
+		msg:    "List namespaces",
+		method: "GET",
+		endpoint: "/api/k8s/namespaces?" + url.Values{
+			"context": []string{"context-test"},
+		}.Encode(),
+		body:      "",
 		expStatus: http.StatusOK,
 		expBody:   objectsToJSON(defaultObjects),
 		useCookie: true,

+ 3 - 3
server/router/router.go

@@ -28,11 +28,11 @@ func New(a *api.App, store sessions.Store, cookieName string) *chi.Mux {
 		r.Method("POST", "/logout", auth.BasicAuthenticate(requestlog.NewHandler(a.HandleLogoutUser, l)))
 
 		// /api/charts routes
-		r.Method("GET", "/charts", auth.DoesUserIDMatch(requestlog.NewHandler(a.HandleListCharts, l), mw.BodyParam))
-		r.Method("GET", "/charts/{name}/{revision}", auth.DoesUserIDMatch(requestlog.NewHandler(a.HandleGetChart, l), mw.BodyParam))
+		r.Method("GET", "/charts", auth.BasicAuthenticate(requestlog.NewHandler(a.HandleListCharts, l)))
+		r.Method("GET", "/charts/{name}/{revision}", auth.BasicAuthenticate(requestlog.NewHandler(a.HandleGetChart, l)))
 
 		// /api/k8s routes
-		r.Method("GET", "/k8s/namespaces", auth.DoesUserIDMatch(requestlog.NewHandler(a.HandleListNamespaces, l), mw.BodyParam))
+		r.Method("GET", "/k8s/namespaces", auth.BasicAuthenticate(requestlog.NewHandler(a.HandleListNamespaces, l)))
 	})
 
 	return r