| 1 | package host |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "os" |
| 6 | "strings" |
| 7 | "testing" |
| 8 | |
| 9 | "github.com/carlosframework/platform/internal/hostguard" |
| 10 | "github.com/carlosframework/platform/internal/rootstate" |
| 11 | ) |
| 12 | |
| 13 | func TestPlan14UnitRetirementExactOwner(t *testing.T) { |
| 14 | for _, attack := range []string{"none", "successor", "running", "lost replace", "lost final write"} { |
| 15 | t.Run(attack, func(t *testing.T) { |
| 16 | b, in, shape := plan14UnitBindingFixture(t, "console.service", "", "") |
| 17 | shape.Program = strings.Repeat("a", 64) |
| 18 | b.canonical = func(context.Context, string) (claimUnitShape, error) { return shape, nil } |
| 19 | captured, err := b.establish(t.Context(), in.Host) |
| 20 | if err != nil { |
| 21 | t.Fatal(err) |
| 22 | } |
| 23 | files := &plan14UnitFiles{} |
| 24 | ms := &plan14UnitBindingFault{Store: &rollbackMemory{files: map[string][]byte{}}} |
| 25 | material := claimUnitMaterializer{idle: func(string) error { return nil }, state: ms, read: files.read, create: files.create, replace: files.replace} |
| 26 | if err = material.install(t.Context(), captured); err != nil { |
| 27 | t.Fatal(err) |
| 28 | } |
| 29 | target := hostguard.FenceTarget{Host: in.Host, Incarnation: in.Incarnation, Stamp: in.Stamp, Box: in.Box, Enrollment: in.Enrollment} |
| 30 | if attack == "successor" { |
| 31 | target.Stamp++ |
| 32 | } |
| 33 | gone := func(context.Context, string, string) error { |
| 34 | if attack == "running" { |
| 35 | return os.ErrPermission |
| 36 | } |
| 37 | return nil |
| 38 | } |
| 39 | original := material.replace |
| 40 | material.replace = func(unit string, id rootstate.FileIdentity, body []byte) error { |
| 41 | if err := original(unit, id, body); err != nil { |
| 42 | return err |
| 43 | } |
| 44 | if attack == "lost replace" { |
| 45 | return os.ErrInvalid |
| 46 | } |
| 47 | if attack == "lost final write" { |
| 48 | ms.fail = true |
| 49 | } |
| 50 | return nil |
| 51 | } |
| 52 | err = retireClaimBoundUnit(t.Context(), target, b, material, gone) |
| 53 | if attack == "none" { |
| 54 | if err != nil { |
| 55 | t.Fatal(err) |
| 56 | } |
| 57 | } else if err == nil { |
| 58 | t.Fatal("attack accepted") |
| 59 | } |
| 60 | if attack == "successor" || attack == "running" { |
| 61 | if files.retires != 0 { |
| 62 | t.Fatal("foreign or running retired") |
| 63 | } |
| 64 | return |
| 65 | } |
| 66 | material.replace = original |
| 67 | ms.fail = false |
| 68 | if err = retireClaimBoundUnit(t.Context(), target, b, material, gone); err != nil { |
| 69 | t.Fatal("recover", err) |
| 70 | } |
| 71 | retired, err := b.read(in.Host) |
| 72 | if err != nil || !retired.Retired || files.retires != 1 { |
| 73 | t.Fatal(retired, err, files.retires) |
| 74 | } |
| 75 | }) |
| 76 | } |
| 77 | } |
| 78 | |