mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-11-03 18:47:41 +01:00
* s3tables: bind table and view writes to the record they were authorized against * s3tables: bind UpdateNamespace to the namespace it was authorized against UpdateNamespace writes back the whole record it read, owner and creation time included. A namespace deleted and created again before the write took the first one's owner and CreatedAt, which also undoes the CreatedAt binding creates now rely on. Refuse the write with 409 when the namespace at the path has another CreatedAt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Chris Lu <chrislusf@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
90 lines
3.3 KiB
Go
90 lines
3.3 KiB
Go
package s3tables
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3tables/s3tablestest"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// claimRecord puts a delete claim on an existing catalog entry, the state a
|
|
// record is in while another operation owns it.
|
|
func claimRecord(t *testing.T, fs *s3tablestest.MemFiler, dir, name string) {
|
|
t.Helper()
|
|
entry := fs.Get(dir, name)
|
|
require.NotNil(t, entry)
|
|
claimed := map[string][]byte{}
|
|
for k, v := range entry.Extended {
|
|
claimed[k] = v
|
|
}
|
|
claimed[ExtendedKeyDeleting] = []byte("delete")
|
|
fs.Put(dir, name, claimed)
|
|
}
|
|
|
|
// A write landing on a record another operation claimed is an expected
|
|
// conflict, not a server fault: the caller must see 409.
|
|
func TestUpdateViewClaimedRecordConflicts(t *testing.T) {
|
|
fs, m := startRenameManager(t)
|
|
seedView(t, fs, "v")
|
|
claimRecord(t, fs, GetNamespacePath(renameTestBucket, "ns"), "v")
|
|
|
|
status, resp := runHandler(t, m, fs.Client, "UpdateView", &UpdateViewRequest{
|
|
TableBucketARN: mustBucketARN(t),
|
|
Namespace: []string{"ns"},
|
|
Name: "v",
|
|
MetadataLocation: "s3://" + renameTestBucket + "/ns/v/metadata/v2.metadata.json",
|
|
})
|
|
assert.Equal(t, http.StatusConflict, status, "%+v", resp)
|
|
assert.Equal(t, ErrCodeConflict, resp.Type)
|
|
}
|
|
|
|
func TestTagResourceClaimedRecordConflicts(t *testing.T) {
|
|
fs, m := startRenameManager(t)
|
|
claimRecord(t, fs, GetNamespacePath(renameTestBucket, "ns"), "t")
|
|
|
|
tableARN := fmt.Sprintf("arn:aws:s3tables:%s:%s:bucket/%s/table/ns/t", DefaultRegion, DefaultAccountID, renameTestBucket)
|
|
status, resp := runHandler(t, m, fs.Client, "TagResource", &TagResourceRequest{
|
|
ResourceARN: tableARN,
|
|
Tags: map[string]string{"k": "v"},
|
|
})
|
|
assert.Equal(t, http.StatusConflict, status, "%+v", resp)
|
|
assert.Equal(t, ErrCodeConflict, resp.Type)
|
|
}
|
|
|
|
func TestUntagResourceClaimedRecordConflicts(t *testing.T) {
|
|
fs, m := startRenameManager(t)
|
|
claimRecord(t, fs, GetNamespacePath(renameTestBucket, "ns"), "t")
|
|
|
|
tableARN := fmt.Sprintf("arn:aws:s3tables:%s:%s:bucket/%s/table/ns/t", DefaultRegion, DefaultAccountID, renameTestBucket)
|
|
status, resp := runHandler(t, m, fs.Client, "UntagResource", &UntagResourceRequest{
|
|
ResourceARN: tableARN,
|
|
TagKeys: []string{"k"},
|
|
})
|
|
assert.Equal(t, http.StatusConflict, status, "%+v", resp)
|
|
assert.Equal(t, ErrCodeConflict, resp.Type)
|
|
}
|
|
|
|
// DeleteTablePolicy writes like every other mutation of a table record, so a
|
|
// record another operation claimed refuses it with 409 instead of losing the
|
|
// policy under the claim's owner.
|
|
func TestDeleteTablePolicyClaimedRecordConflicts(t *testing.T) {
|
|
fs, m := startTableT(t, map[string][]byte{ExtendedKeyPolicy: []byte(bindTestPolicy)})
|
|
nsPath := GetNamespacePath(renameTestBucket, "ns")
|
|
claimRecord(t, fs, nsPath, "t")
|
|
|
|
status, resp := runHandler(t, m, fs.Client, "DeleteTablePolicy", &DeleteTablePolicyRequest{
|
|
TableBucketARN: mustBucketARN(t),
|
|
Namespace: []string{"ns"},
|
|
Name: "t",
|
|
})
|
|
assert.Equal(t, http.StatusConflict, status, "%+v", resp)
|
|
assert.Equal(t, ErrCodeConflict, resp.Type, "%+v", resp)
|
|
extended := fs.Get(nsPath, "t").Extended
|
|
assert.Equal(t, []byte(bindTestPolicy), extended[ExtendedKeyPolicy], "the refused delete keeps the policy")
|
|
assert.Equal(t, []byte("delete"), extended[ExtendedKeyDeleting], "and leaves the claim to its owner")
|
|
}
|