satellite/admin: extend admin API to allow setting and deleting geofence for projects
Issue: https://github.com/storj/storj-private/issues/357 Change-Id: Ib59319581641f1f5da71c629143e12f11eb04925
This commit is contained in:
parent
4cc167a6bd
commit
cc12a48c24
@ -20,6 +20,7 @@ import (
|
||||
|
||||
"storj.io/common/macaroon"
|
||||
"storj.io/common/memory"
|
||||
"storj.io/common/storj"
|
||||
"storj.io/common/uuid"
|
||||
"storj.io/storj/satellite/buckets"
|
||||
"storj.io/storj/satellite/console"
|
||||
@ -697,6 +698,55 @@ func (server *Server) checkUsage(ctx context.Context, w http.ResponseWriter, pro
|
||||
return server.checkInvoicing(ctx, w, projectID)
|
||||
}
|
||||
|
||||
func (server *Server) createGeofenceForProject(w http.ResponseWriter, r *http.Request) {
|
||||
placement, err := parsePlacementConstraint(r.URL.Query().Get("region"))
|
||||
if err != nil {
|
||||
sendJSONError(w, err.Error(), "available: EU, EEA, US, DE, NR", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
server.setGeofenceForProject(w, r, placement)
|
||||
}
|
||||
|
||||
func (server *Server) deleteGeofenceForProject(w http.ResponseWriter, r *http.Request) {
|
||||
server.setGeofenceForProject(w, r, storj.EveryCountry)
|
||||
}
|
||||
|
||||
func (server *Server) setGeofenceForProject(w http.ResponseWriter, r *http.Request, placement storj.PlacementConstraint) {
|
||||
ctx := r.Context()
|
||||
|
||||
vars := mux.Vars(r)
|
||||
projectUUIDString, ok := vars["project"]
|
||||
if !ok {
|
||||
sendJSONError(w, "project-uuid missing",
|
||||
"", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
projectUUID, err := uuid.FromString(projectUUIDString)
|
||||
if err != nil {
|
||||
sendJSONError(w, "invalid project-uuid",
|
||||
err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
project, err := server.db.Console().Projects().Get(ctx, projectUUID)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
sendJSONError(w, "project with specified uuid does not exist",
|
||||
"", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
project.DefaultPlacement = placement
|
||||
|
||||
err = server.db.Console().Projects().Update(ctx, project)
|
||||
if err != nil {
|
||||
sendJSONError(w, "unable to set geofence for project",
|
||||
err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func bucketNames(buckets []buckets.Bucket) []string {
|
||||
var xs []string
|
||||
for _, b := range buckets {
|
||||
|
99
satellite/admin/project_testplanet_test.go
Normal file
99
satellite/admin/project_testplanet_test.go
Normal file
@ -0,0 +1,99 @@
|
||||
// Copyright (C) 2023 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package admin_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"storj.io/common/storj"
|
||||
"storj.io/common/testcontext"
|
||||
"storj.io/common/uuid"
|
||||
"storj.io/storj/private/testplanet"
|
||||
"storj.io/storj/satellite"
|
||||
)
|
||||
|
||||
func TestAdminProjectGeofenceAPI(t *testing.T) {
|
||||
testplanet.Run(t, testplanet.Config{
|
||||
SatelliteCount: 1,
|
||||
StorageNodeCount: 0,
|
||||
UplinkCount: 1,
|
||||
Reconfigure: testplanet.Reconfigure{
|
||||
Satellite: func(_ *zap.Logger, _ int, config *satellite.Config) {
|
||||
config.Admin.Address = "127.0.0.1:0"
|
||||
},
|
||||
},
|
||||
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
||||
uplink := planet.Uplinks[0]
|
||||
sat := planet.Satellites[0]
|
||||
address := sat.Admin.Admin.Listener.Addr()
|
||||
project, err := sat.DB.Console().Projects().Get(ctx, uplink.Projects[0].ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// update project set default placement to EEA
|
||||
project.DefaultPlacement = storj.EEA
|
||||
require.NoError(t, sat.DB.Console().Projects().Update(ctx, project))
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
project uuid.UUID
|
||||
// expectations
|
||||
status int
|
||||
body string
|
||||
}{
|
||||
{
|
||||
name: "project does not exist",
|
||||
project: uuid.NullUUID{}.UUID,
|
||||
status: http.StatusNotFound,
|
||||
body: `{"error":"project with specified uuid does not exist","detail":""}`,
|
||||
},
|
||||
{
|
||||
name: "validated",
|
||||
project: project.ID,
|
||||
status: http.StatusOK,
|
||||
body: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
baseURL := fmt.Sprintf("http://%s/api/projects/%s", address, testCase.project)
|
||||
t.Log(baseURL)
|
||||
baseGeofenceURL := fmt.Sprintf("http://%s/api/projects/%s/geofence", address, testCase.project)
|
||||
t.Log(baseGeofenceURL)
|
||||
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
assertReq(ctx, t, baseGeofenceURL+"?region=EU", "POST", "", testCase.status, testCase.body, sat.Config.Console.AuthToken)
|
||||
|
||||
if testCase.status == http.StatusOK {
|
||||
|
||||
t.Run("Set", func(t *testing.T) {
|
||||
project, err := sat.DB.Console().Projects().Get(ctx, testCase.project)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, storj.EU, project.DefaultPlacement)
|
||||
|
||||
expected, err := json.Marshal(project)
|
||||
require.NoError(t, err, "failed to json encode expected bucket")
|
||||
|
||||
assertGet(ctx, t, baseURL, string(expected), sat.Config.Console.AuthToken)
|
||||
})
|
||||
t.Run("Delete", func(t *testing.T) {
|
||||
assertReq(ctx, t, baseGeofenceURL, "DELETE", "", testCase.status, testCase.body, sat.Config.Console.AuthToken)
|
||||
|
||||
project, err := sat.DB.Console().Projects().Get(ctx, testCase.project)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected, err := json.Marshal(project)
|
||||
require.NoError(t, err)
|
||||
assertGet(ctx, t, baseURL, string(expected), sat.Config.Console.AuthToken)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
@ -138,6 +138,8 @@ func NewServer(log *zap.Logger, listener net.Listener, db DB, buckets *buckets.S
|
||||
fullAccessAPI.HandleFunc("/projects/{project}/buckets/{bucket}/geofence", server.deleteGeofenceForBucket).Methods("DELETE")
|
||||
fullAccessAPI.HandleFunc("/projects/{project}/usage", server.checkProjectUsage).Methods("GET")
|
||||
fullAccessAPI.HandleFunc("/projects/{project}/useragent", server.updateProjectsUserAgent).Methods("PATCH")
|
||||
fullAccessAPI.HandleFunc("/projects/{project}/geofence", server.createGeofenceForProject).Methods("POST")
|
||||
fullAccessAPI.HandleFunc("/projects/{project}/geofence", server.deleteGeofenceForProject).Methods("DELETE")
|
||||
fullAccessAPI.HandleFunc("/apikeys/{apikey}", server.getAPIKey).Methods("GET")
|
||||
fullAccessAPI.HandleFunc("/apikeys/{apikey}", server.deleteAPIKey).Methods("DELETE")
|
||||
fullAccessAPI.HandleFunc("/restkeys/{useremail}", server.addRESTKey).Methods("POST")
|
||||
|
Loading…
Reference in New Issue
Block a user