package instancectl import ( "context" "errors" "os" "path/filepath" "strings" "testing" ) func TestPlan14BoundUnitPlacement(t *testing.T) { for _, name := range []string{"console.service", "woodstar-atbridge.service", "carlos-app@shop.example.service"} { t.Run(name, func(t *testing.T) { u, ctl, in, _ := claimUnitFixture(t) u.BoundHost, u.BoundUnit = in.Host, name ctl.group = "/" + BoundClaimUnitControlGroup(in.Host, name) ctl.events = filepath.Join(u.CgroupRoot, strings.TrimPrefix(ctl.group, "/")) if err := os.MkdirAll(ctl.events, 0755); err != nil { t.Fatal(err) } ctl.setPopulated(false) // A bound unit must never be started by the unreserved entrypoint. if _, err := u.Start(t.Context(), in); err == nil { t.Fatal("unreserved named launch") } if strings.Contains(ctl.joined(), "start ") { t.Fatal(ctl.joined()) } if err := u.Gone(t.Context(), in.Host, ""); err != nil { t.Fatal(err) } ctl.state, ctl.invocation = "active", "replacement" ctl.setPopulated(true) if err := u.Gone(t.Context(), in.Host, "old"); !errors.Is(err, ErrClaimUnitBusy) { t.Fatalf("replacement with populated tree accepted: %v", err) } ctl.state = "inactive" if err := u.Gone(t.Context(), in.Host, "old"); !errors.Is(err, ErrClaimUnitBusy) { t.Fatalf("descendants accepted: %v", err) } ctl.setPopulated(false) if err := u.Gone(t.Context(), in.Host, "old"); err != nil { t.Fatal(err) } before := ctl.joined() if err := u.Stop(context.Background(), "sibling.example"); err == nil { t.Fatal("binding crossed hosts") } if ctl.joined() != before { t.Fatal("foreign host reached systemd") } }) } } func TestPlan14BoundUnitNames(t *testing.T) { for _, name := range []string{"../console.service", "console.service/child", "x\n.service", "x@.service", "x.socket", "--x.service", "carlos-instance-control.service"} { u, ctl, in, _ := claimUnitFixture(t) u.BoundHost, u.BoundUnit = in.Host, name if err := u.Gone(t.Context(), in.Host, ""); err == nil { t.Fatalf("accepted %q", name) } if ctl.joined() != "" { t.Fatal("invalid binding reached systemd") } } } func TestPlan14BoundUnitExactStop(t *testing.T) { u, ctl, in, _ := claimUnitFixture(t) u.BoundHost, u.BoundUnit = in.Host, "console.service" ctl.group = "/" + BoundClaimUnitControlGroup(in.Host, u.BoundUnit) ctl.invocation, ctl.state = "replacement", "active" if err := u.StopInvocation(t.Context(), in.Host, "old"); err == nil { t.Fatal("stopped replacement") } if strings.Contains(ctl.joined(), "stop ") { t.Fatal(ctl.joined()) } if err := u.StopInvocation(t.Context(), in.Host, ""); err == nil { t.Fatal("stopped unknown invocation") } ctl.invocation = "old" if err := u.StopInvocation(t.Context(), in.Host, "old"); err != nil { t.Fatal(err) } if !strings.Contains(ctl.joined(), "stop console.service") { t.Fatal(ctl.joined()) } } func TestPlan14BoundStopReportsInvocationRace(t *testing.T) { for _, changed := range []string{"invocation", "group"} { t.Run(changed, func(t *testing.T) { u, ctl, in, _ := claimUnitFixture(t) u.BoundHost, u.BoundUnit = in.Host, "console.service" ctl.group = "/" + BoundClaimUnitControlGroup(in.Host, u.BoundUnit) ctl.invocation, ctl.state = "old", "active" u.Systemctl = func(ctx context.Context, args ...string) (string, error) { if args[0] == "stop" { if changed == "invocation" { ctl.invocation = "replacement" } else { ctl.group = "/sibling.slice/console.service" } } return ctl.run(ctx, args...) } if err := u.StopInvocation(t.Context(), in.Host, "old"); !errors.Is(err, ErrClaimUnitBusy) { t.Fatal("uncertain stop reported success", err) } }) } }