Răsfoiți Sursa

Merge pull request #115 from porter-dev/beta.2.integration-backend

Beta.2.integration backend
abelanger5 5 ani în urmă
părinte
comite
cfd9b27e62

+ 21 - 0
internal/forms/cluster.go

@@ -69,6 +69,27 @@ func (ccf *CreateClusterForm) ToCluster() (*models.Cluster, error) {
 	}, nil
 }
 
+// UpdateClusterForm represents the accepted values for updating a
+// cluster (only name for now)
+type UpdateClusterForm struct {
+	ID uint
+
+	Name string `json:"name" form:"required"`
+}
+
+// ToCluster converts the form to a cluster
+func (ucf *UpdateClusterForm) ToCluster(repo repository.ClusterRepository) (*models.Cluster, error) {
+	cluster, err := repo.ReadCluster(ucf.ID)
+
+	if err != nil {
+		return nil, err
+	}
+
+	cluster.Name = ucf.Name
+
+	return cluster, nil
+}
+
 // ResolveClusterForm will resolve a cluster candidate and create a new cluster
 type ResolveClusterForm struct {
 	Resolver *models.ClusterResolverAll `form:"required"`

+ 22 - 0
internal/forms/registry.go

@@ -2,6 +2,7 @@ package forms
 
 import (
 	"github.com/porter-dev/porter/internal/models"
+	"github.com/porter-dev/porter/internal/repository"
 )
 
 // CreateRegistry represents the accepted values for creating a
@@ -22,3 +23,24 @@ func (cr *CreateRegistry) ToRegistry() (*models.Registry, error) {
 		AWSIntegrationID: cr.AWSIntegrationID,
 	}, nil
 }
+
+// UpdateRegistryForm represents the accepted values for updating a
+// registry (only name for now)
+type UpdateRegistryForm struct {
+	ID uint
+
+	Name string `json:"name" form:"required"`
+}
+
+// ToRegistry converts the form to a cluster
+func (urf *UpdateRegistryForm) ToRegistry(repo repository.RegistryRepository) (*models.Registry, error) {
+	registry, err := repo.ReadRegistry(urf.ID)
+
+	if err != nil {
+		return nil, err
+	}
+
+	registry.Name = urf.Name
+
+	return registry, nil
+}

+ 1 - 0
internal/repository/cluster.go

@@ -16,6 +16,7 @@ type ClusterRepository interface {
 	CreateCluster(cluster *models.Cluster) (*models.Cluster, error)
 	ReadCluster(id uint) (*models.Cluster, error)
 	ListClustersByProjectID(projectID uint) ([]*models.Cluster, error)
+	UpdateCluster(cluster *models.Cluster) (*models.Cluster, error)
 	UpdateClusterTokenCache(tokenCache *ints.TokenCache) (*models.Cluster, error)
 	DeleteCluster(cluster *models.Cluster) error
 }

+ 11 - 0
internal/repository/gorm/cluster.go

@@ -193,6 +193,17 @@ func (repo *ClusterRepository) ListClustersByProjectID(
 	return clusters, nil
 }
 
+// UpdateCluster modifies an existing Cluster in the database
+func (repo *ClusterRepository) UpdateCluster(
+	cluster *models.Cluster,
+) (*models.Cluster, error) {
+	if err := repo.db.Save(cluster).Error; err != nil {
+		return nil, err
+	}
+
+	return cluster, nil
+}
+
 // UpdateClusterTokenCache updates the token cache for a cluster
 func (repo *ClusterRepository) UpdateClusterTokenCache(
 	tokenCache *ints.TokenCache,

+ 42 - 0
internal/repository/gorm/cluster_test.go

@@ -299,6 +299,48 @@ func TestListClustersByProjectID(t *testing.T) {
 	}
 }
 
+func TestUpdateCluster(t *testing.T) {
+	tester := &tester{
+		dbFileName: "./porter_update_cluster.db",
+	}
+
+	setupTestEnv(tester, t)
+	initProject(tester, t)
+	initCluster(tester, t)
+	defer cleanup(tester, t)
+
+	cluster := tester.initClusters[0]
+
+	cluster.Name = "cluster-new-name"
+
+	cluster, err := tester.repo.Cluster.UpdateCluster(
+		cluster,
+	)
+
+	if err != nil {
+		t.Fatalf("%v\n", err)
+	}
+
+	cluster, err = tester.repo.Cluster.ReadCluster(tester.initClusters[0].ID)
+
+	// make sure data is correct
+	expCluster := models.Cluster{
+		ProjectID:                tester.initProjects[0].ID,
+		Name:                     "cluster-new-name",
+		Server:                   "https://localhost",
+		KubeIntegrationID:        tester.initKIs[0].ID,
+		CertificateAuthorityData: []byte("-----BEGIN"),
+	}
+
+	// reset fields for reflect.DeepEqual
+	cluster.Model = orm.Model{}
+
+	if diff := deep.Equal(expCluster, *cluster); diff != nil {
+		t.Errorf("incorrect cluster")
+		t.Error(diff)
+	}
+}
+
 func TestUpdateClusterToken(t *testing.T) {
 	tester := &tester{
 		dbFileName: "./porter_test_update_cluster_token.db",

+ 11 - 0
internal/repository/gorm/registry.go

@@ -94,6 +94,17 @@ func (repo *RegistryRepository) ListRegistriesByProjectID(
 	return regs, nil
 }
 
+// UpdateRegistry modifies an existing Registry in the database
+func (repo *RegistryRepository) UpdateRegistry(
+	reg *models.Registry,
+) (*models.Registry, error) {
+	if err := repo.db.Save(reg).Error; err != nil {
+		return nil, err
+	}
+
+	return reg, nil
+}
+
 // UpdateRegistryTokenCache updates the token cache for a registry
 func (repo *RegistryRepository) UpdateRegistryTokenCache(
 	tokenCache *ints.RegTokenCache,

+ 39 - 0
internal/repository/gorm/registry_test.go

@@ -86,6 +86,45 @@ func TestListRegistriesByProjectID(t *testing.T) {
 	}
 }
 
+func TestUpdateRegistry(t *testing.T) {
+	tester := &tester{
+		dbFileName: "./porter_update_registry.db",
+	}
+
+	setupTestEnv(tester, t)
+	initProject(tester, t)
+	initRegistry(tester, t)
+	defer cleanup(tester, t)
+
+	reg := tester.initRegs[0]
+
+	reg.Name = "registry-new-name"
+
+	reg, err := tester.repo.Registry.UpdateRegistry(
+		reg,
+	)
+
+	if err != nil {
+		t.Fatalf("%v\n", err)
+	}
+
+	reg, err = tester.repo.Registry.ReadRegistry(tester.initRegs[0].ID)
+
+	// make sure data is correct
+	expRegistry := models.Registry{
+		ProjectID: tester.initProjects[0].ID,
+		Name:      "registry-new-name",
+	}
+
+	// reset fields for reflect.DeepEqual
+	reg.Model = orm.Model{}
+
+	if diff := deep.Equal(expRegistry, *reg); diff != nil {
+		t.Errorf("incorrect registry")
+		t.Error(diff)
+	}
+}
+
 func TestUpdateRegistryToken(t *testing.T) {
 	tester := &tester{
 		dbFileName: "./porter_test_update_registry_token.db",

+ 1 - 0
internal/repository/registry.go

@@ -10,6 +10,7 @@ type RegistryRepository interface {
 	CreateRegistry(reg *models.Registry) (*models.Registry, error)
 	ReadRegistry(id uint) (*models.Registry, error)
 	ListRegistriesByProjectID(projectID uint) ([]*models.Registry, error)
+	UpdateRegistry(reg *models.Registry) (*models.Registry, error)
 	UpdateRegistryTokenCache(tokenCache *ints.RegTokenCache) (*models.Registry, error)
 	DeleteRegistry(reg *models.Registry) error
 }

+ 18 - 0
internal/repository/test/cluster.go

@@ -144,6 +144,24 @@ func (repo *ClusterRepository) ListClustersByProjectID(
 	return res, nil
 }
 
+// UpdateCluster modifies an existing Cluster in the database
+func (repo *ClusterRepository) UpdateCluster(
+	cluster *models.Cluster,
+) (*models.Cluster, error) {
+	if !repo.canQuery {
+		return nil, errors.New("Cannot write database")
+	}
+
+	if int(cluster.ID-1) >= len(repo.clusters) || repo.clusters[cluster.ID-1] == nil {
+		return nil, gorm.ErrRecordNotFound
+	}
+
+	index := int(cluster.ID - 1)
+	repo.clusters[index] = cluster
+
+	return cluster, nil
+}
+
 // UpdateClusterTokenCache updates the token cache for a cluster
 func (repo *ClusterRepository) UpdateClusterTokenCache(
 	tokenCache *ints.TokenCache,

+ 18 - 0
internal/repository/test/registry.go

@@ -73,6 +73,24 @@ func (repo *RegistryRepository) ListRegistriesByProjectID(
 	return res, nil
 }
 
+// UpdateRegistry modifies an existing Registry in the database
+func (repo *RegistryRepository) UpdateRegistry(
+	reg *models.Registry,
+) (*models.Registry, error) {
+	if !repo.canQuery {
+		return nil, errors.New("Cannot write database")
+	}
+
+	if int(reg.ID-1) >= len(repo.registries) || repo.registries[reg.ID-1] == nil {
+		return nil, gorm.ErrRecordNotFound
+	}
+
+	index := int(reg.ID - 1)
+	repo.registries[index] = reg
+
+	return reg, nil
+}
+
 // UpdateRegistryTokenCache updates the token cache for a registry
 func (repo *RegistryRepository) UpdateRegistryTokenCache(
 	tokenCache *ints.RegTokenCache,

+ 58 - 0
server/api/cluster_handler.go

@@ -119,6 +119,64 @@ func (app *App) HandleListProjectClusters(w http.ResponseWriter, r *http.Request
 	}
 }
 
+// HandleUpdateProjectCluster updates a project's cluster
+func (app *App) HandleUpdateProjectCluster(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
+	}
+
+	clusterID, err := strconv.ParseUint(chi.URLParam(r, "cluster_id"), 0, 64)
+
+	if err != nil || clusterID == 0 {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+
+	form := &forms.UpdateClusterForm{
+		ID: uint(clusterID),
+	}
+
+	// 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
+	cluster, err := form.ToCluster(app.repo.Cluster)
+
+	if err != nil {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+
+	// handle write to the database
+	cluster, err = app.repo.Cluster.UpdateCluster(cluster)
+
+	if err != nil {
+		app.handleErrorDataWrite(err, w)
+		return
+	}
+
+	w.WriteHeader(http.StatusOK)
+
+	clusterExt := cluster.Externalize()
+
+	if err := json.NewEncoder(w).Encode(clusterExt); err != nil {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+}
+
 // HandleDeleteProjectCluster handles the deletion of a Cluster via the cluster ID
 func (app *App) HandleDeleteProjectCluster(w http.ResponseWriter, r *http.Request) {
 	id, err := strconv.ParseUint(chi.URLParam(r, "cluster_id"), 0, 64)

+ 24 - 0
server/api/cluster_handler_test.go

@@ -145,6 +145,30 @@ func TestHandleListProjectClusters(t *testing.T) {
 	testClusterRequests(t, listProjectClustersTest, true)
 }
 
+var updateClusterTests = []*clusterTest{
+	&clusterTest{
+		initializers: []func(t *tester){
+			initUserDefault,
+			initProject,
+			initProjectClusterDefault,
+		},
+		msg:       "Update cluster name",
+		method:    "POST",
+		endpoint:  "/api/projects/1/clusters/1",
+		body:      `{"name":"cluster-new-name"}`,
+		expStatus: http.StatusOK,
+		expBody:   `{"id":1,"project_id":1,"name":"cluster-new-name","server":"https://10.10.10.10","service":"kube"}`,
+		useCookie: true,
+		validators: []func(c *clusterTest, tester *tester, t *testing.T){
+			projectClusterBodyValidator,
+		},
+	},
+}
+
+func TestHandleUpdateCluster(t *testing.T) {
+	testClusterRequests(t, updateClusterTests, true)
+}
+
 var deleteClusterTests = []*clusterTest{
 	&clusterTest{
 		initializers: []func(t *tester){

+ 58 - 0
server/api/registry_handler.go

@@ -95,6 +95,64 @@ func (app *App) HandleListProjectRegistries(w http.ResponseWriter, r *http.Reque
 	}
 }
 
+// HandleUpdateProjectRegistry updates a registry
+func (app *App) HandleUpdateProjectRegistry(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
+	}
+
+	registryID, err := strconv.ParseUint(chi.URLParam(r, "registry_id"), 0, 64)
+
+	if err != nil || registryID == 0 {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+
+	form := &forms.UpdateRegistryForm{
+		ID: uint(registryID),
+	}
+
+	// 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
+	registry, err := form.ToRegistry(app.repo.Registry)
+
+	if err != nil {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+
+	// handle write to the database
+	registry, err = app.repo.Registry.UpdateRegistry(registry)
+
+	if err != nil {
+		app.handleErrorDataWrite(err, w)
+		return
+	}
+
+	w.WriteHeader(http.StatusOK)
+
+	regExt := registry.Externalize()
+
+	if err := json.NewEncoder(w).Encode(regExt); err != nil {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+}
+
 // HandleDeleteProjectRegistry handles the deletion of a Registry via the registry ID
 func (app *App) HandleDeleteProjectRegistry(w http.ResponseWriter, r *http.Request) {
 	id, err := strconv.ParseUint(chi.URLParam(r, "registry_id"), 0, 64)

+ 24 - 0
server/api/registry_handler_test.go

@@ -171,6 +171,30 @@ func TestHandleListRegistries(t *testing.T) {
 	testRegistryRequests(t, listRegistryTests, true)
 }
 
+var updateRegistryTests = []*regTest{
+	&regTest{
+		initializers: []func(t *tester){
+			initUserDefault,
+			initProject,
+			initRegistry,
+		},
+		msg:       "Update registry name",
+		method:    "POST",
+		endpoint:  "/api/projects/1/registries/1",
+		body:      `{"name":"registry-new-name"}`,
+		expStatus: http.StatusOK,
+		expBody:   `{"id":1,"name":"registry-new-name","project_id":1,"service":"ecr"}`,
+		useCookie: true,
+		validators: []func(c *regTest, tester *tester, t *testing.T){
+			regBodyValidator,
+		},
+	},
+}
+
+func TestHandleUpdateRegistry(t *testing.T) {
+	testRegistryRequests(t, updateRegistryTests, true)
+}
+
 var deleteRegTests = []*regTest{
 	&regTest{
 		initializers: []func(t *tester){

+ 28 - 0
server/router/router.go

@@ -202,6 +202,20 @@ func New(
 			),
 		)
 
+		r.Method(
+			"POST",
+			"/projects/{project_id}/clusters/{cluster_id}",
+			auth.DoesUserHaveProjectAccess(
+				auth.DoesUserHaveClusterAccess(
+					requestlog.NewHandler(a.HandleUpdateProjectCluster, l),
+					mw.URLParam,
+					mw.URLParam,
+				),
+				mw.URLParam,
+				mw.WriteAccess,
+			),
+		)
+
 		r.Method(
 			"DELETE",
 			"/projects/{project_id}/clusters/{cluster_id}",
@@ -289,6 +303,20 @@ func New(
 			),
 		)
 
+		r.Method(
+			"POST",
+			"/projects/{project_id}/registries/{registry_id}",
+			auth.DoesUserHaveProjectAccess(
+				auth.DoesUserHaveRegistryAccess(
+					requestlog.NewHandler(a.HandleUpdateProjectRegistry, l),
+					mw.URLParam,
+					mw.URLParam,
+				),
+				mw.URLParam,
+				mw.WriteAccess,
+			),
+		)
+
 		r.Method(
 			"DELETE",
 			"/projects/{project_id}/registries/{registry_id}",