satellite/admin: add delete project endpoint (#3888)
This commit is contained in:
parent
1ecbfc453f
commit
70502658c8
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user