Przeglądaj źródła

list chart impl

Alexander Belanger 5 lat temu
rodzic
commit
3a22ded7e3

+ 29 - 0
internal/forms/helm_repo.go

@@ -0,0 +1,29 @@
+package forms
+
+import (
+	"github.com/porter-dev/porter/internal/models"
+)
+
+// CreateHelmRepo represents the accepted values for creating a
+// helm repo
+type CreateHelmRepo struct {
+	Name      string `json:"name" form:"required"`
+	RepoURL   string `json:"repo_url" form:"required"`
+	ProjectID uint   `json:"project_id" form:"required"`
+
+	BasicIntegrationID uint `json:"basic_integration_id"`
+	GCPIntegrationID   uint `json:"gcp_integration_id"`
+	AWSIntegrationID   uint `json:"aws_integration_id"`
+}
+
+// ToHelmRepo converts the form to a gorm helm repo model
+func (ch *CreateHelmRepo) ToHelmRepo() (*models.HelmRepo, error) {
+	return &models.HelmRepo{
+		Name:                   ch.Name,
+		RepoURL:                ch.RepoURL,
+		ProjectID:              ch.ProjectID,
+		BasicAuthIntegrationID: ch.BasicIntegrationID,
+		GCPIntegrationID:       ch.GCPIntegrationID,
+		AWSIntegrationID:       ch.AWSIntegrationID,
+	}, nil
+}

+ 1 - 1
internal/models/helm_repo.go

@@ -19,7 +19,7 @@ type HelmRepo struct {
 	// RepoURL is the URL to the helm repo. This varies based on the integration
 	// type. For example, for AWS S3 this may be prefixed with s3://, or for
 	// GCS it may be gs://
-	RepoURL string `json:"repo_name"`
+	RepoURL string `json:"repo_url"`
 
 	// ------------------------------------------------------------------
 	// All fields below this line are encrypted before storage

+ 131 - 0
server/api/helm_repo_handler.go

@@ -0,0 +1,131 @@
+package api
+
+import (
+	"encoding/json"
+	"net/http"
+	"strconv"
+
+	"github.com/porter-dev/porter/internal/helm/repo"
+
+	"github.com/go-chi/chi"
+	"github.com/porter-dev/porter/internal/forms"
+	"github.com/porter-dev/porter/internal/models"
+)
+
+// HandleCreateHelmRepo creates a new helm repo for a project
+func (app *App) HandleCreateHelmRepo(w http.ResponseWriter, r *http.Request) {
+	projID, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
+
+	if err != nil || projID == 0 {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+
+	form := &forms.CreateHelmRepo{
+		ProjectID: uint(projID),
+	}
+
+	// decode from JSON to form value
+	if err := json.NewDecoder(r.Body).Decode(form); err != nil {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+
+	// validate the form
+	if err := app.validator.Struct(form); err != nil {
+		app.handleErrorFormValidation(err, ErrProjectValidateFields, w)
+		return
+	}
+
+	// convert the form to a registry
+	hr, err := form.ToHelmRepo()
+
+	if err != nil {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+
+	// handle write to the database
+	hr, err = app.repo.HelmRepo.CreateHelmRepo(hr)
+
+	if err != nil {
+		app.handleErrorDataWrite(err, w)
+		return
+	}
+
+	app.logger.Info().Msgf("New helm repo created: %d", hr.ID)
+
+	w.WriteHeader(http.StatusCreated)
+
+	hrExt := hr.Externalize()
+
+	if err := json.NewEncoder(w).Encode(hrExt); err != nil {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+}
+
+// HandleListProjectHelmRepos returns a list of helm repos for a project
+func (app *App) HandleListProjectHelmRepos(w http.ResponseWriter, r *http.Request) {
+	projID, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
+
+	if err != nil || projID == 0 {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+
+	hrs, err := app.repo.HelmRepo.ListHelmReposByProjectID(uint(projID))
+
+	if err != nil {
+		app.handleErrorRead(err, ErrProjectDataRead, w)
+		return
+	}
+
+	extHRs := make([]*models.HelmRepoExternal, 0)
+
+	for _, hr := range hrs {
+		extHRs = append(extHRs, hr.Externalize())
+	}
+
+	w.WriteHeader(http.StatusOK)
+
+	if err := json.NewEncoder(w).Encode(extHRs); err != nil {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+}
+
+// HandleListHelmRepoCharts lists the charts for a given linked helm repo
+func (app *App) HandleListHelmRepoCharts(w http.ResponseWriter, r *http.Request) {
+	helmID, err := strconv.ParseUint(chi.URLParam(r, "helm_id"), 0, 64)
+
+	if err != nil || helmID == 0 {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+
+	hr, err := app.repo.HelmRepo.ReadHelmRepo(uint(helmID))
+
+	if err != nil {
+		app.handleErrorRead(err, ErrProjectDataRead, w)
+		return
+	}
+
+	// cast to a registry from registry package
+	_hr := repo.HelmRepo(*hr)
+	hrAPI := &_hr
+
+	charts, err := hrAPI.ListCharts(*app.repo)
+
+	if err != nil {
+		app.handleErrorRead(err, ErrProjectDataRead, w)
+		return
+	}
+
+	w.WriteHeader(http.StatusOK)
+
+	if err := json.NewEncoder(w).Encode(charts); err != nil {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+}

+ 157 - 0
server/api/helm_repo_handler_test.go

@@ -0,0 +1,157 @@
+package api_test
+
+import (
+	"encoding/json"
+	"net/http"
+	"strings"
+	"testing"
+
+	"github.com/go-test/deep"
+	"github.com/porter-dev/porter/internal/models"
+)
+
+// ------------------------- TEST TYPES AND MAIN LOOP ------------------------- //
+
+type helmTest struct {
+	initializers []func(t *tester)
+	msg          string
+	method       string
+	endpoint     string
+	body         string
+	expStatus    int
+	expBody      string
+	useCookie    bool
+	validators   []func(c *helmTest, tester *tester, t *testing.T)
+}
+
+func testHelmRepoRequests(t *testing.T, tests []*helmTest, canQuery bool) {
+	for _, c := range tests {
+		// create a new tester
+		tester := newTester(canQuery)
+
+		// if there's an initializer, call it
+		for _, init := range c.initializers {
+			init(tester)
+		}
+
+		req, err := http.NewRequest(
+			c.method,
+			c.endpoint,
+			strings.NewReader(c.body),
+		)
+
+		tester.req = req
+
+		if c.useCookie {
+			req.AddCookie(tester.cookie)
+		}
+
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tester.execute()
+		rr := tester.rr
+
+		// first, check that the status matches
+		if status := rr.Code; status != c.expStatus {
+			t.Errorf("%s, handler returned wrong status code: got %v want %v",
+				c.msg, status, c.expStatus)
+		}
+
+		// if there's a validator, call it
+		for _, validate := range c.validators {
+			validate(c, tester, t)
+		}
+	}
+}
+
+// ------------------------- TEST FIXTURES AND FUNCTIONS  ------------------------- //
+
+var createHelmRepoTests = []*helmTest{
+	&helmTest{
+		initializers: []func(t *tester){
+			initUserDefault,
+			initProject,
+		},
+		msg:       "Create helm repo",
+		method:    "POST",
+		endpoint:  "/api/projects/1/helmrepos",
+		body:      `{"name":"helm-repo-test","basic_integration_id":1,"repo_url":"https://example-repo.com"}`,
+		expStatus: http.StatusCreated,
+		expBody:   `{"id":1,"project_id":1,"name":"helm-repo-test","repo_name":"https://example-repo.com","service":"helm"}`,
+		useCookie: true,
+		validators: []func(c *helmTest, tester *tester, t *testing.T){
+			helmRepoBodyValidator,
+		},
+	},
+}
+
+func TestHandleCreateHelmRepo(t *testing.T) {
+	testHelmRepoRequests(t, createHelmRepoTests, true)
+}
+
+var listHelmReposTest = []*helmTest{
+	&helmTest{
+		initializers: []func(t *tester){
+			initUserDefault,
+			initProject,
+			initHelmRepo,
+		},
+		msg:       "List helm repos",
+		method:    "GET",
+		endpoint:  "/api/projects/1/helmrepos",
+		body:      ``,
+		expStatus: http.StatusOK,
+		expBody:   `[{"id":1,"project_id":1,"name":"helm-repo-test","repo_name":"https://example-repo.com","service":"helm"}]`,
+		useCookie: true,
+		validators: []func(c *helmTest, tester *tester, t *testing.T){
+			hrsBodyValidator,
+		},
+	},
+}
+
+func TestHandleListHelmRepos(t *testing.T) {
+	testHelmRepoRequests(t, listHelmReposTest, true)
+}
+
+// ------------------------- INITIALIZERS AND VALIDATORS ------------------------- //
+
+func initHelmRepo(tester *tester) {
+	proj, _ := tester.repo.Project.ReadProject(1)
+
+	hr := &models.HelmRepo{
+		Name:                   "helm-repo-test",
+		ProjectID:              proj.Model.ID,
+		RepoURL:                "https://example-repo.com",
+		BasicAuthIntegrationID: 1,
+	}
+
+	tester.repo.HelmRepo.CreateHelmRepo(hr)
+}
+
+func helmRepoBodyValidator(c *helmTest, tester *tester, t *testing.T) {
+	gotBody := &models.HelmRepoExternal{}
+	expBody := &models.HelmRepoExternal{}
+
+	json.Unmarshal(tester.rr.Body.Bytes(), &gotBody)
+	json.Unmarshal([]byte(c.expBody), &expBody)
+
+	if diff := deep.Equal(gotBody, expBody); diff != nil {
+		t.Errorf("handler returned wrong body:\n")
+		t.Error(diff)
+	}
+}
+
+func hrsBodyValidator(c *helmTest, tester *tester, t *testing.T) {
+	gotBody := make([]*models.HelmRepoExternal, 0)
+	expBody := make([]*models.HelmRepoExternal, 0)
+
+	json.Unmarshal(tester.rr.Body.Bytes(), &gotBody)
+	json.Unmarshal([]byte(c.expBody), &expBody)
+
+	if diff := deep.Equal(gotBody, expBody); diff != nil {
+		t.Errorf("handler returned wrong body:\n")
+		t.Error(diff)
+	}
+}

+ 31 - 0
server/router/router.go

@@ -308,6 +308,37 @@ func New(
 			),
 		)
 
+		// /api/projects/{project_id}/helmrepos routes
+		r.Method(
+			"POST",
+			"/projects/{project_id}/helmrepos",
+			auth.DoesUserHaveProjectAccess(
+				requestlog.NewHandler(a.HandleCreateHelmRepo, l),
+				mw.URLParam,
+				mw.WriteAccess,
+			),
+		)
+
+		r.Method(
+			"GET",
+			"/projects/{project_id}/helmrepos",
+			auth.DoesUserHaveProjectAccess(
+				requestlog.NewHandler(a.HandleListProjectHelmRepos, l),
+				mw.URLParam,
+				mw.WriteAccess,
+			),
+		)
+
+		r.Method(
+			"GET",
+			"/projects/{project_id}/helmrepos/{helm_id}/charts",
+			auth.DoesUserHaveProjectAccess(
+				requestlog.NewHandler(a.HandleListHelmRepoCharts, l),
+				mw.URLParam,
+				mw.WriteAccess,
+			),
+		)
+
 		// /api/projects/{project_id}/registries routes
 		r.Method(
 			"POST",