satellite/admin: add delete project endpoint (#3888)

This commit is contained in:
Stefan Benten 2020-05-18 19:36:09 +02:00 committed by GitHub
parent 1ecbfc453f
commit 70502658c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 118 additions and 2 deletions

View File

@ -54,6 +54,10 @@ Updates usage limit for a project.
Updates rate limit for a project.
## DELETE /api/project/{project-id}
Deletes the project.
## POST /api/project
Adds a project for specific user.

View File

@ -14,7 +14,9 @@ import (
"github.com/gorilla/mux"
"github.com/gorilla/schema"
"storj.io/common/macaroon"
"storj.io/common/memory"
"storj.io/common/storj"
"storj.io/common/uuid"
"storj.io/storj/satellite/console"
)
@ -285,3 +287,51 @@ func (server *Server) addProject(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(data) // nothing to do with the error response, probably the client requesting disappeared
}
func (server *Server) deleteProject(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
vars := mux.Vars(r)
projectUUIDString, ok := vars["project"]
if !ok {
http.Error(w, "project-uuid missing", http.StatusBadRequest)
return
}
projectUUID, err := uuid.FromString(projectUUIDString)
if err != nil {
http.Error(w, fmt.Sprintf("invalid project-uuid: %v", err), http.StatusBadRequest)
return
}
if err := r.ParseForm(); err != nil {
http.Error(w, fmt.Sprintf("invalid form: %v", err), http.StatusBadRequest)
return
}
buckets, err := server.db.Buckets().ListBuckets(ctx, projectUUID, storj.BucketListOptions{Limit: 1, Direction: storj.Forward}, macaroon.AllowedBuckets{All: true})
if err != nil {
http.Error(w, fmt.Sprintf("unable to list buckets: %v", err), http.StatusInternalServerError)
return
}
if len(buckets.Items) > 0 {
http.Error(w, fmt.Sprintf("buckets still exist"), http.StatusConflict)
return
}
keys, err := server.db.Console().APIKeys().GetPagedByProjectID(ctx, projectUUID, console.APIKeyCursor{Limit: 1, Page: 1})
if err != nil {
http.Error(w, fmt.Sprintf("unable to list api-keys: %v", err), http.StatusInternalServerError)
return
}
if keys.TotalCount > 0 {
http.Error(w, fmt.Sprintf("api-keys still exist"), http.StatusConflict)
return
}
err = server.db.Console().Projects().Delete(ctx, projectUUID)
if err != nil {
http.Error(w, fmt.Sprintf("unable to delete project: %v", err), http.StatusInternalServerError)
return
}
}

View File

@ -15,10 +15,13 @@ import (
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"storj.io/common/macaroon"
"storj.io/common/storj"
"storj.io/common/testcontext"
"storj.io/common/uuid"
"storj.io/storj/private/testplanet"
"storj.io/storj/satellite"
"storj.io/storj/satellite/console"
)
func TestAPI(t *testing.T) {
@ -32,8 +35,8 @@ func TestAPI(t *testing.T) {
},
},
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
satellite := planet.Satellites[0]
address := satellite.Admin.Admin.Listener.Addr()
sat := planet.Satellites[0]
address := sat.Admin.Admin.Listener.Addr()
project := planet.Uplinks[0].Projects[0]
link := "http://" + address.String() + "/api/project/" + project.ID.String() + "/limit"
@ -144,6 +147,61 @@ func TestAddProject(t *testing.T) {
})
}
func TestDeleteProject(t *testing.T) {
testplanet.Run(t, testplanet.Config{
SatelliteCount: 1,
StorageNodeCount: 0,
UplinkCount: 1,
Reconfigure: testplanet.Reconfigure{
Satellite: func(log *zap.Logger, index int, config *satellite.Config) {
config.Admin.Address = "127.0.0.1:0"
},
},
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
address := planet.Satellites[0].Admin.Admin.Listener.Addr()
projectID := planet.Uplinks[0].Projects[0].ID
// Ensure there are no buckets left
buckets, err := planet.Satellites[0].DB.Buckets().ListBuckets(ctx, projectID, storj.BucketListOptions{Limit: 1, Direction: storj.Forward}, macaroon.AllowedBuckets{All: true})
require.NoError(t, err)
require.Len(t, buckets.Items, 0)
apikeys, err := planet.Satellites[0].DB.Console().APIKeys().GetPagedByProjectID(ctx, projectID, console.APIKeyCursor{
Page: 1,
Limit: 2,
Search: "",
})
require.NoError(t, err)
require.Len(t, apikeys.APIKeys, 1)
// the deletion with an existing API key should fail
req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("http://"+address.String()+"/api/project/%s", projectID), nil)
require.NoError(t, err)
req.Header.Set("Authorization", "very-secret-token")
response, err := http.DefaultClient.Do(req)
require.NoError(t, err)
require.NoError(t, response.Body.Close())
require.Equal(t, http.StatusConflict, response.StatusCode)
err = planet.Satellites[0].DB.Console().APIKeys().Delete(ctx, apikeys.APIKeys[0].ID)
require.NoError(t, err)
req, err = http.NewRequest(http.MethodDelete, fmt.Sprintf("http://"+address.String()+"/api/project/%s", projectID), nil)
require.NoError(t, err)
req.Header.Set("Authorization", "very-secret-token")
response, err = http.DefaultClient.Do(req)
require.NoError(t, err)
require.NoError(t, response.Body.Close())
require.Equal(t, http.StatusOK, response.StatusCode)
project, err := planet.Satellites[0].DB.Console().Projects().Get(ctx, projectID)
require.Error(t, err)
require.Nil(t, project)
})
}
func assertGet(t *testing.T, link string, expected string) {
t.Helper()

View File

@ -18,6 +18,7 @@ import (
"storj.io/common/errs2"
"storj.io/storj/satellite/accounting"
"storj.io/storj/satellite/console"
"storj.io/storj/satellite/metainfo"
)
// Config defines configuration for debug server.
@ -33,6 +34,8 @@ type DB interface {
ProjectAccounting() accounting.ProjectAccounting
// Console returns database for satellite console
Console() console.DB
// Buckets returns database for satellite buckets
Buckets() metainfo.BucketsDB
}
// Server provides endpoints for debugging.
@ -64,6 +67,7 @@ func NewServer(log *zap.Logger, listener net.Listener, db DB, config Config) *Se
server.mux.HandleFunc("/api/user/{useremail}", server.userInfo).Methods("GET")
server.mux.HandleFunc("/api/project/{project}/limit", server.getProjectLimit).Methods("GET")
server.mux.HandleFunc("/api/project/{project}/limit", server.putProjectLimit).Methods("PUT", "POST")
server.mux.HandleFunc("/api/project/{project}", server.deleteProject).Methods("DELETE")
server.mux.HandleFunc("/api/project", server.addProject).Methods("POST")
return server