satellite/admin: rework update user limits functionality

Fixed nil pointer dereference panic.
Updated naming conventions so that PUT request and GET response bodies are the same (bandwidth, storage and segment).
Allowed usage of notations like 150GB, 2TB for storage and bandwidth limits.
Updated tests.

Issue:
https://github.com/storj/storj/issues/5674

Change-Id: I7ac27c00721a9b4bf507afa34cb05c4475a809ad
This commit is contained in:
Vitalii 2023-04-20 13:33:48 +03:00 committed by Vitalii Shpital
parent 260b71e70c
commit 4d998970d4
3 changed files with 92 additions and 53 deletions

View File

@ -378,25 +378,31 @@ Blank fields will not be updated.`,
}
},
{
name: 'update user and project limits',
name: "update user's project limits",
desc: `Update limits for all of user's existing and future projects.
Blank fields will not be updated.`,
params: [
["current user's email", new InputText('email', true)],
['project storage limit (in bytes)', new InputText('number', false)],
['project bandwidth limit (in bytes)', new InputText('number', false)],
[
'project storage limit (in bytes or notations like 1GB, 2tb)',
new InputText('text', false)
],
[
'project bandwidth limit (in bytes or notations like 1GB, 2tb)',
new InputText('text', false)
],
['project segment limit (max number)', new InputText('number', false)]
],
func: async (
currentEmail: string,
projectStorageLimit?: number,
projectBandwidthLimit?: number,
projectSegmentLimit?: number
storage?: number,
bandwidth?: number,
segment?: number
): Promise<null> => {
return this.fetch('PUT', `users/${currentEmail}/limits`, null, {
projectStorageLimit,
projectBandwidthLimit,
projectSegmentLimit
storage,
bandwidth,
segment
}) as Promise<null>;
}
},

View File

@ -15,6 +15,7 @@ import (
"github.com/gorilla/mux"
"golang.org/x/crypto/bcrypt"
"storj.io/common/memory"
"storj.io/common/uuid"
"storj.io/storj/satellite/console"
)
@ -224,7 +225,7 @@ func (server *Server) userLimits(w http.ResponseWriter, r *http.Request) {
var limits struct {
Storage int64 `json:"storage"`
Bandwidth int64 `json:"bandwidth"`
Segment int64 `json:"maxSegments"`
Segment int64 `json:"segment"`
}
limits.Storage = user.ProjectStorageLimit
@ -362,12 +363,12 @@ func (server *Server) updateLimits(w http.ResponseWriter, r *http.Request) {
return
}
type User struct {
console.User
var input struct {
Storage memory.Size `json:"storage"`
Bandwidth memory.Size `json:"bandwidth"`
Segment int64 `json:"segment"`
}
var input User
err = json.Unmarshal(body, &input)
if err != nil {
sendJSONError(w, "failed to unmarshal request",
@ -375,38 +376,46 @@ func (server *Server) updateLimits(w http.ResponseWriter, r *http.Request) {
return
}
updateRequest := console.UpdateUserRequest{}
if input.ProjectStorageLimit > 0 {
updateRequest.ProjectStorageLimit = &input.ProjectStorageLimit
}
if input.ProjectBandwidthLimit > 0 {
updateRequest.ProjectBandwidthLimit = &input.ProjectBandwidthLimit
}
if input.ProjectSegmentLimit > 0 {
updateRequest.ProjectSegmentLimit = &input.ProjectSegmentLimit
newLimits := console.UsageLimits{
Storage: user.ProjectStorageLimit,
Bandwidth: user.ProjectBandwidthLimit,
Segment: user.ProjectSegmentLimit,
}
userLimits := console.UsageLimits{
Storage: *updateRequest.ProjectStorageLimit,
Bandwidth: *updateRequest.ProjectBandwidthLimit,
Segment: *updateRequest.ProjectSegmentLimit,
if input.Storage > 0 {
newLimits.Storage = input.Storage.Int64()
}
if input.Bandwidth > 0 {
newLimits.Bandwidth = input.Bandwidth.Int64()
}
if input.Segment > 0 {
newLimits.Segment = input.Segment
}
err = server.db.Console().Users().UpdateUserProjectLimits(ctx, user.ID, userLimits)
if newLimits.Storage == user.ProjectStorageLimit &&
newLimits.Bandwidth == user.ProjectBandwidthLimit &&
newLimits.Segment == user.ProjectSegmentLimit {
sendJSONError(w, "no limits to update",
"new values are equal to old ones", http.StatusBadRequest)
return
}
err = server.db.Console().Users().UpdateUserProjectLimits(ctx, user.ID, newLimits)
if err != nil {
sendJSONError(w, "failed to update user limits",
err.Error(), http.StatusInternalServerError)
return
}
userProjects, err := server.db.Console().Projects().GetOwn(ctx, user.ID)
if err != nil {
sendJSONError(w, "failed to get user's projects",
err.Error(), http.StatusInternalServerError)
return
}
for _, p := range userProjects {
err = server.db.Console().Projects().UpdateUsageLimits(ctx, p.ID, userLimits)
err = server.db.Console().Projects().UpdateUsageLimits(ctx, p.ID, newLimits)
if err != nil {
sendJSONError(w, "failed to update project limits",
err.Error(), http.StatusInternalServerError)

View File

@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"storj.io/common/memory"
"storj.io/common/testcontext"
"storj.io/storj/private/testplanet"
"storj.io/storj/satellite"
@ -204,32 +205,55 @@ func TestUserUpdate(t *testing.T) {
require.Equal(t, newUsageLimit, updatedUserStatusAndUsageLimits.ProjectBandwidthLimit)
require.Equal(t, newUsageLimit, updatedUserStatusAndUsageLimits.ProjectSegmentLimit)
// Update user limits and project limits (current and existing projects for a user).
link = "http://" + address.String() + "/api/users/alice+2@mail.test/limits"
newStorageLimit := int64(15000)
newBandwidthLimit := int64(25000)
newSegmentLimit := int64(35000)
body2 := fmt.Sprintf(`{"projectStorageLimit":%d, "projectBandwidthLimit":%d, "projectSegmentLimit":%d}`, newStorageLimit, newBandwidthLimit, newSegmentLimit)
responseBody = assertReq(ctx, t, link, http.MethodPut, body2, http.StatusOK, "", planet.Satellites[0].Config.Console.AuthToken)
require.Len(t, responseBody, 0)
var updateLimitsTests = []struct {
newStorageLimit memory.Size
newBandwidthLimit memory.Size
newSegmentLimit int64
useSizeString bool
}{
{
15000,
25000,
35000,
false,
},
{
50 * memory.KB,
75 * memory.KB,
40000,
true,
},
}
// Get user limits returns new updated limits
link2 := "http://" + address.String() + "/api/users/alice+2@mail.test/limits"
expectedBody := `{` +
fmt.Sprintf(`"storage":%d,"bandwidth":%d,"maxSegments":%d}`, newStorageLimit, newBandwidthLimit, newSegmentLimit)
assertReq(ctx, t, link2, http.MethodGet, "", http.StatusOK, expectedBody, planet.Satellites[0].Config.Console.AuthToken)
for _, tt := range updateLimitsTests {
// Update user limits and project limits (current and existing projects for a user).
link = "http://" + address.String() + "/api/users/alice+2@mail.test/limits"
jsonStr := `{"storage":"%d", "bandwidth":"%d", "segment":%d}`
if tt.useSizeString {
jsonStr = strings.Replace(jsonStr, "%d", "%v", 2)
}
body2 := fmt.Sprintf(jsonStr, tt.newStorageLimit, tt.newBandwidthLimit, tt.newSegmentLimit)
responseBody = assertReq(ctx, t, link, http.MethodPut, body2, http.StatusOK, "", planet.Satellites[0].Config.Console.AuthToken)
require.Len(t, responseBody, 0)
userUpdatedLimits, err := planet.Satellites[0].DB.Console().Users().Get(ctx, user.ID)
require.NoError(t, err)
require.Equal(t, newStorageLimit, userUpdatedLimits.ProjectStorageLimit)
require.Equal(t, newBandwidthLimit, userUpdatedLimits.ProjectBandwidthLimit)
require.Equal(t, newSegmentLimit, userUpdatedLimits.ProjectSegmentLimit)
// Get user limits returns new updated limits
link2 := "http://" + address.String() + "/api/users/alice+2@mail.test/limits"
expectedBody := `{` +
fmt.Sprintf(`"storage":%d,"bandwidth":%d,"segment":%d}`, tt.newStorageLimit, tt.newBandwidthLimit, tt.newSegmentLimit)
assertReq(ctx, t, link2, http.MethodGet, "", http.StatusOK, expectedBody, planet.Satellites[0].Config.Console.AuthToken)
projectUpdatedLimits, err := planet.Satellites[0].DB.Console().Projects().Get(ctx, planet.Uplinks[0].Projects[0].ID)
require.NoError(t, err)
require.Equal(t, newStorageLimit, projectUpdatedLimits.StorageLimit.Int64())
require.Equal(t, newBandwidthLimit, projectUpdatedLimits.BandwidthLimit.Int64())
require.Equal(t, newSegmentLimit, *projectUpdatedLimits.SegmentLimit)
userUpdatedLimits, err := planet.Satellites[0].DB.Console().Users().Get(ctx, user.ID)
require.NoError(t, err)
require.Equal(t, tt.newStorageLimit.Int64(), userUpdatedLimits.ProjectStorageLimit)
require.Equal(t, tt.newBandwidthLimit.Int64(), userUpdatedLimits.ProjectBandwidthLimit)
require.Equal(t, tt.newSegmentLimit, userUpdatedLimits.ProjectSegmentLimit)
projectUpdatedLimits, err := planet.Satellites[0].DB.Console().Projects().Get(ctx, planet.Uplinks[0].Projects[0].ID)
require.NoError(t, err)
require.Equal(t, tt.newStorageLimit, *projectUpdatedLimits.StorageLimit)
require.Equal(t, tt.newBandwidthLimit, *projectUpdatedLimits.BandwidthLimit)
require.Equal(t, tt.newSegmentLimit, *projectUpdatedLimits.SegmentLimit)
}
})
t.Run("Not found", func(t *testing.T) {