| index 7fc82a8a7..922ce3f3e 100644 |
| --- a/internal/host/claimfiles_test.go |
| +++ b/internal/host/claimfiles_test.go |
| @@ -5,6 +5,7 @@ import ( |
| "errors" |
| "path/filepath" |
| "testing" |
| + "time" |
| |
| "github.com/carlosframework/platform/internal/hostbox" |
| "github.com/carlosframework/platform/internal/hostwrite" |
| @@ -85,11 +86,12 @@ func TestOpenClaimFilesManifestBindsUnderTheClaimAlone(t *testing.T) { |
| } |
| off := func(string) string { return "" } |
| |
| + pass := func(context.Context) error { return nil } |
| provider := &movesFiles{} |
| for name, getenv := range map[string]func(string) string{"claim alone": claimOn, "both": bothOn} { |
| src := &fakeFilesSource{files: provider} |
| var out ClaimOwnerBindings |
| - closeFn, err := openClaimFilesManifest(context.Background(), getenv, src, store, data, actor, &out) |
| + closeFn, err := openClaimFilesManifest(context.Background(), getenv, src, store, data, actor, &out, pass) |
| if err != nil || closeFn == nil { |
| t.Fatalf("%s: %v %v", name, err, closeFn != nil) |
| } |
| @@ -104,33 +106,96 @@ func TestOpenClaimFilesManifestBindsUnderTheClaimAlone(t *testing.T) { |
| // Claim off: untouched, not opened. |
| src := &fakeFilesSource{files: provider} |
| var out ClaimOwnerBindings |
| - closeFn, err := openClaimFilesManifest(context.Background(), off, src, store, data, actor, &out) |
| + closeFn, err := openClaimFilesManifest(context.Background(), off, src, store, data, actor, &out, pass) |
| if err != nil || closeFn != nil || out.Serving.Files != nil || src.opens != 0 { |
| t.Fatalf("claim off: %v %v %v %d", err, closeFn != nil, out.Serving.Files, src.opens) |
| } |
| // Nothing registered: untouched. |
| - if closeFn, err := openClaimFilesManifest(context.Background(), claimOn, nil, store, data, actor, &out); err != nil || closeFn != nil || out.Serving.Files != nil { |
| + if closeFn, err := openClaimFilesManifest(context.Background(), claimOn, nil, store, data, actor, &out, pass); err != nil || closeFn != nil || out.Serving.Files != nil { |
| t.Fatalf("nothing registered: %v %v %v", err, closeFn != nil, out.Serving.Files) |
| } |
| // Already bound by the moves source: refused, not replaced. |
| already := &movesFiles{Box: "other"} |
| out = ClaimOwnerBindings{Serving: ClaimServingSources{Files: already}} |
| src = &fakeFilesSource{files: provider} |
| - if _, err := openClaimFilesManifest(context.Background(), claimOn, src, store, data, actor, &out); !errors.Is(err, hostwrite.ErrUnavailable) || out.Serving.Files != ClaimFilesManifest(already) || src.opens != 0 { |
| + if _, err := openClaimFilesManifest(context.Background(), claimOn, src, store, data, actor, &out, pass); !errors.Is(err, hostwrite.ErrUnavailable) || out.Serving.Files != ClaimFilesManifest(already) || src.opens != 0 { |
| t.Fatalf("two providers: %v %v %d", err, out.Serving.Files, src.opens) |
| } |
| // An Open error refuses; a nil provider refuses. |
| out = ClaimOwnerBindings{} |
| src = &fakeFilesSource{err: errors.New("no bucket")} |
| - if _, err := openClaimFilesManifest(context.Background(), claimOn, src, store, data, actor, &out); err == nil || out.Serving.Files != nil { |
| + if _, err := openClaimFilesManifest(context.Background(), claimOn, src, store, data, actor, &out, pass); err == nil || out.Serving.Files != nil { |
| t.Fatalf("open error: %v %v", err, out.Serving.Files) |
| } |
| src = &fakeFilesSource{} |
| - if _, err := openClaimFilesManifest(context.Background(), claimOn, src, store, data, actor, &out); !errors.Is(err, hostwrite.ErrUnavailable) || out.Serving.Files != nil { |
| + if _, err := openClaimFilesManifest(context.Background(), claimOn, src, store, data, actor, &out, pass); !errors.Is(err, hostwrite.ErrUnavailable) || out.Serving.Files != nil { |
| t.Fatalf("nil provider: %v %v", err, out.Serving.Files) |
| } |
| } |
| |
| +// C8 reads the claim's data bucket as the serving and moves owners do |
| +// (whole-branch fixes, item 5): through hostbox.RequestStore, so every |
| +// version listing and exact-version read it makes is bounded by the |
| +// claim's request limit and preceded by the box's identity check, a |
| +// refused identity issuing no bucket call. An establishment under a |
| +// refused identity refuses with hostbox.ErrChanged and writes nothing. |
| +func TestClaimFilesReadTheBucketThroughTheRequestStore(t *testing.T) { |
| + inner := &mwBucket{VersionedBucket: leasetest.NewVersionedBucket()} |
| + inner.AddBucket("data") |
| + var refuse error |
| + checks := 0 |
| + check := func(context.Context) error { checks++; return refuse } |
| + src := &movesFilesSource{DataDir: t.TempDir(), newFiles: func(context.Context, string, string) (release.Store, error) { |
| + return casBucketStore{b: inner.VersionedBucket, bucket: "data"}, nil |
| + }} |
| + actor := hostbox.Actor{Box: "uk-box", Region: "uk", Bucket: "data", HomeRegion: "ie", Cloud: hostbox.CloudIdentity{Region: "eu-west-2"}} |
| + var out ClaimOwnerBindings |
| + out.Data = inner |
| + closeFn, err := openClaimFilesManifest(t.Context(), func(k string) string { return "1" }, src, release.NewDirStore(t.TempDir()), inner, actor, &out, check) |
| + if err != nil || closeFn == nil { |
| + t.Fatalf("open: %v", err) |
| + } |
| + f, ok := out.Serving.Files.(*movesFiles) |
| + if !ok { |
| + t.Fatalf("%T", out.Serving.Files) |
| + } |
| + if _, ok := f.Data.(hostbox.RequestStore); !ok { |
| + t.Fatalf("C8's bucket is %T, not the claim's request store", f.Data) |
| + } |
| + before := time.Now() |
| + if _, err := f.Data.ListVersions(t.Context(), "data", "instances/"); err != nil { |
| + t.Fatal(err) |
| + } |
| + if inner.lists != 1 || checks != 1 || inner.deadlines[0].IsZero() || inner.deadlines[0].After(time.Now().Add(hostbox.RequestLimit)) || inner.deadlines[0].Before(before.Add(hostbox.RequestLimit-time.Minute)) { |
| + t.Fatalf("the request's deadline %v (lists %d, checks %d), want the request limit", inner.deadlines, inner.lists, checks) |
| + } |
| + refuse = hostbox.ErrChanged |
| + if _, err := f.Data.ListVersions(t.Context(), "data", "instances/"); !errors.Is(err, hostbox.ErrChanged) || inner.lists != 1 { |
| + t.Fatalf("a refused identity: %v, %d bucket calls", err, inner.lists) |
| + } |
| + if _, err := f.Data.GetVersion(t.Context(), "data", "k", "v"); !errors.Is(err, hostbox.ErrChanged) { |
| + t.Fatalf("a refused identity's read: %v", err) |
| + } |
| + // An establishment under a refused identity: the chain's first listing |
| + // refuses, nothing is written. |
| + x := &filesFixture{t: t, bucket: inner.VersionedBucket, files: casBucketStore{b: inner.VersionedBucket, bucket: "data"}, dir: src.DataDir} |
| + x.f = f |
| + f.FilesDir = func(host string) (string, string) { |
| + return filepath.Join(x.dir, host), filepath.Join(x.dir, host+".env") |
| + } |
| + x.writeEnv("K=1\n") |
| + o := servingObserved("g2", "g1:uk:@root:uk", "g2:uk:g1:uk") |
| + if err := f.Establish(t.Context(), o, launchInputs("g2"), x); !errors.Is(err, hostbox.ErrChanged) { |
| + t.Fatalf("establish under a refused identity: %v", err) |
| + } |
| + if _, found := x.manifest("g2"); found { |
| + t.Fatal("a manifest was written under a refused identity") |
| + } |
| + if inner.lists != 1 { |
| + t.Fatalf("%d bucket calls, want none from the refused establishment", inner.lists) |
| + } |
| +} |
| + |
| // The production source opens a movesFiles over the verified actor: the |
| // data bucket by exact version, the files store for that bucket in its |
| // cloud region, this box's engine region and the home alias, and the |