Skip to content

Add sandboxed e2e tests for host resources. - #2959

Open
dxapd wants to merge 1 commit into
google:mainfrom
dxapd:net-host-test
Open

Add sandboxed e2e tests for host resources.#2959
dxapd wants to merge 1 commit into
google:mainfrom
dxapd:net-host-test

Conversation

@dxapd

@dxapd dxapd commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Add a way to run tests for resource allocation. This will be adapted for cvdalloc soon.

Currently, I don't run the code in the new directory in kokoro. I plan to try and do so if possible w/ the cvdalloc stuff, but the main use of this right now is for local testing.

@dxapd
dxapd force-pushed the net-host-test branch 8 times, most recently from 7b7ea00 to 6d09f82 Compare August 4, 2026 23:07
@dxapd dxapd added the kokoro:force-run Trigger a presubmit build unconditionally. label Aug 4, 2026
@GoogleCuttlefishTesterBot GoogleCuttlefishTesterBot removed the kokoro:force-run Trigger a presubmit build unconditionally. label Aug 4, 2026
@dxapd
dxapd requested a review from Databean August 5, 2026 21:18
@dxapd
dxapd marked this pull request as ready for review August 5, 2026 21:19
Comment on lines +38 to +50
func ensurePath() {
parts := strings.Split(os.Getenv("PATH"), ":")
have := map[string]bool{}
for _, p := range parts {
have[p] = true
}
for _, e := range []string{"/usr/sbin", "/usr/bin", "/sbin", "/bin"} {
if !have[e] {
parts = append(parts, e)
}
}
os.Setenv("PATH", strings.Join(parts, ":"))
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the callers to these executables use absolute paths rather than setting up PATH? Are the locations of these executables unpredictable?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file is called network_ns.go, which gives me the impression that it is creating the network namespace, but if I understand correctly it is doing the initial setup inside the namespace. Maybe inside_network_ns.go to be less ambiguous?

}

func NewSandbox(t *testing.T) *Sandbox {
t.Helper()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Helper() in these files seems like it will make failures harder to understand.

When printing file and line information, that function will be skipped.

s.pid = cmd.Process.Pid

if err := s.waitReady(); err != nil {
s.Close()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If t.Cleanup(s.Close) is moved before this block, would all these error handlers need to call s.Close() manually?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file is called filesystem_ns.go, which gives me the impression that it is creating the filesystem namespace, but if I understand correctly it is doing the initial setup inside the namespace. Maybe inside_filesystem_ns.go to be less ambiguous?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense, though i think the best prefix here would be init or something similar

cmd.Stdout = &outBuf
cmd.Stderr = &errBuf
log.Printf("[sandbox] running: %s", strings.Join(args, " "))
err := cmd.Run()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this use the context from the test environment so that the commands are halted if the test exits?

keeper *exec.Cmd
pid int
tempdir string
initScript string

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, this refers to the /etc/init.d/cuttlefish-host-resources script. This isn't a great separation of concerns: the Sandbox type is aware both of namespacing concerns and is involved in managing the host-resources script directly. It would be easier to consider the Sandbox type purely as managing namespaces, and it should expose enough operations that the test can use it to start operations on the init script inside.

Comment on lines +69 to +74
cmd := exec.Command("unshare", "--user", "--map-root-user", "--net", "--mount", "sleep", "infinity")
if err := cmd.Start(); err != nil {
t.Fatalf("cannot create rootless user+net namespace: %v", err)
}
s.keeper = cmd
s.pid = cmd.Process.Pid

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than using unshare, is it possible to use SysProcAttr with flags like CLONE_NEWNET?

It doesn't look like there is a convenient alternative for nsenter though. A kind of awkward way to avoid nsenter would be to have bash as the root process of the namespace and keep file descriptors open to run commands inside by writing to the shell's stdin and reading results from its stdout, but that seems more convoluted.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it appears that that is mostly what unshare does, plus a bunch of setup:

https://github.com/util-linux/util-linux/blob/master/sys-utils/unshare.c#L939

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The functions in this file that are member functions of Sandbox feel like they could just as easily be free functions that accept a Sandbox, as they don't interact with any of its private members.

http://go/UnhealthyIdioms#violation-of-single-responsibility-principle

It doesn't make much difference in go semantics since visibility is package-level so even non-member functions can access private struct members, but as a reader it draws clearer distinctions about the meaning of the Sandbox type.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file defines some parse* functions that internally define more complex structures with json annotations and return simpler types with easily accessible public fields.

What do you think instead of defining the public types around the json schema, keeping the fields private, and then defining accessor methods to produce the values the callers need to access? It would make it easier to connect the code to the command output.

The downside is that it makes it harder to describe the ideal state in test cases like TestStaticResourcesInit which also rely on being able to construct the same types. Maybe a hybrid of the two approaches is to try to extract the json schema types out of the parse* methods and implement conversions between the two types of structures.

There's also UnmarshalerFrom and with jsontext.Decoder but that looks like a bigger hassle because of how low-level it is.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my thinking was actually to let this file fall on the messier side so the test files are super clear, but let me see if i can straighten things out in here. i'll try a few things

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants