Skip to content

Commit

Permalink
Add E2E testing for usb (#1214)
Browse files Browse the repository at this point in the history
* e2e test usb mounting

* no poweroff

* no start

* drive usb via sync server since its up

sudo santactl status

sudo?

* revert nostart/nopoweroff

* bump VMCLI minimum os version
  • Loading branch information
kallsyms authored Nov 1, 2023
1 parent 64bb34b commit 7530b8f
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 21 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ jobs:
run: ./Testing/integration/test_config_changes.sh
- name: Test sync server changes
run: ./Testing/integration/test_sync_changes.sh
- name: Test USB blocking
run: ./Testing/integration/test_usb.sh
- name: Poweroff
if: ${{ always() }}
run: sudo shutdown -h +1
5 changes: 5 additions & 0 deletions Testing/integration/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,8 @@ run_command(
name = "dismiss_santa_popup",
cmd = "osascript $${BUILD_WORKSPACE_DIRECTORY}/Testing/integration/dismiss_santa_popup.scpt",
)

run_command(
name = "dismiss_usb_popup",
cmd = "osascript $${BUILD_WORKSPACE_DIRECTORY}/Testing/integration/dismiss_usb_popup.scpt",
)
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
(NSString *)bundleDir;
+ (VZVirtualMachine *)createVirtualMachineWithBundleDir:(NSString *)bundleDir;
+ (VZVirtualMachine *)createVirtualMachineWithBundleDir:(NSString *)bundleDir
roDisk:(NSString *)roDisk;
roDisk:(NSString *)roDisk
usbDisk:(NSString *)usbDisk;

@end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,21 @@ + (VZVirtioBlockDeviceConfiguration *)createBlockDeviceConfigurationForDisk:(NSU
return disk;
}

+ (VZUSBMassStorageDeviceConfiguration *)createUSBDeviceConfigurationForDisk:(NSURL *)diskURL
readOnly:(BOOL)ro {
NSError *error;
VZDiskImageStorageDeviceAttachment *diskAttachment =
[[VZDiskImageStorageDeviceAttachment alloc] initWithURL:diskURL readOnly:ro error:&error];
if (!diskAttachment) {
NSLog(@"Failed to create VZDiskImageStorageDeviceAttachment: %@", error.localizedDescription);
exit(-1);
}
VZUSBMassStorageDeviceConfiguration *disk =
[[VZUSBMassStorageDeviceConfiguration alloc] initWithAttachment:diskAttachment];

return disk;
}

+ (VZVirtioNetworkDeviceConfiguration *)createNetworkDeviceConfiguration {
VZNATNetworkDeviceAttachment *natAttachment = [[VZNATNetworkDeviceAttachment alloc] init];
VZVirtioNetworkDeviceConfiguration *networkConfiguration =
Expand Down Expand Up @@ -189,15 +204,22 @@ + (VZVirtualMachineConfiguration *)createBaseVirtualMachineConfigurationWithBund
}

+ (VZVirtualMachine *)createVirtualMachineWithBundleDir:(NSString *)bundleDir
roDisk:(NSString *)roDisk {
roDisk:(NSString *)roDisk
usbDisk:(NSString*)usbDisk {
VZVirtualMachineConfiguration *configuration =
[self createBaseVirtualMachineConfigurationWithBundleDir:bundleDir];
if (roDisk) {
if (roDisk && ![roDisk isEqualToString:@""]) {
configuration.storageDevices = [configuration.storageDevices
arrayByAddingObject:[self createBlockDeviceConfigurationForDisk:[[NSURL alloc]
initFileURLWithPath:roDisk]
readOnly:YES]];
}
if (usbDisk && ![usbDisk isEqualToString:@""]) {
configuration.storageDevices = [configuration.storageDevices
arrayByAddingObject:[self createUSBDeviceConfigurationForDisk:[[NSURL alloc]
initFileURLWithPath:usbDisk]
readOnly:NO]];
}
NSError *error;
if (![configuration validateWithError:&error]) {
NSLog(@"Failed to validate configuration: %@", error.localizedDescription);
Expand All @@ -208,7 +230,7 @@ + (VZVirtualMachine *)createVirtualMachineWithBundleDir:(NSString *)bundleDir
}

+ (VZVirtualMachine *)createVirtualMachineWithBundleDir:(NSString *)bundleDir {
return [self createVirtualMachineWithBundleDir:bundleDir roDisk:nil];
return [self createVirtualMachineWithBundleDir:bundleDir roDisk:nil usbDisk:nil];
}

@end
2 changes: 1 addition & 1 deletion Testing/integration/VM/VMCLI/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ macos_application(
bundle_id = "com.google.santa.e2e.vmcli",
entitlements = "//Testing/integration/VM/Common:entitlements",
infoplists = ["//Testing/integration/VM/Common:plist"],
minimum_os_version = "12.0",
minimum_os_version = "13.0",
deps = [
":vmcli_lib",
],
Expand Down
11 changes: 8 additions & 3 deletions Testing/integration/VM/VMCLI/main.m
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ - (void)guestDidStopVirtualMachine:(VZVirtualMachine *)virtualMachine {
@end

int main(int argc, const char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s bundle_path", argv[0]);
if (argc < 2) {
fprintf(stderr, "Usage: %s bundle_path [usb_disk]", argv[0]);
exit(-1);
}

Expand All @@ -50,8 +50,13 @@ int main(int argc, const char *argv[]) {
bundleDir = [bundleDir stringByAppendingString:@"/"];
}

NSString *usbDisk;
if (argc > 2) {
usbDisk = @(argv[2]);
}

VZVirtualMachine *vm =
[MacOSVirtualMachineConfigurationHelper createVirtualMachineWithBundleDir:bundleDir];
[MacOSVirtualMachineConfigurationHelper createVirtualMachineWithBundleDir:bundleDir roDisk:nil usbDisk:usbDisk];

MacOSVirtualMachineDelegate *delegate = [MacOSVirtualMachineDelegate new];
vm.delegate = delegate;
Expand Down
34 changes: 22 additions & 12 deletions Testing/integration/VM/VMGUI/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -47,28 +47,37 @@ @implementation AppDelegate {
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
#ifdef __arm64__
dispatch_async(dispatch_get_main_queue(), ^{
NSArray *args = [[NSProcessInfo processInfo] arguments];
NSMutableArray *args = [NSMutableArray arrayWithArray:[[NSProcessInfo processInfo] arguments]];

VZMacOSVirtualMachineStartOptions *options = [VZMacOSVirtualMachineStartOptions new];
NSString *bundleDir;
NSString *roDisk;
NSString *usbDisk;

if (args.count < 2) {
abortWithErrorMessage(@"Usage: VMGUI [-recovery] bundle_path [ro_disk]");
[args removeObjectAtIndex:0];

if (args.count == 0) {
abortWithErrorMessage(@"Usage: VMGUI [-recovery] bundle_path [ro_disk] [usb_disk]");
}

int bundleArg = 1;
if ([args[1] isEqualToString:@"-recovery"]) {
if ([args[0] isEqualToString:@"-recovery"]) {
options.startUpFromMacOSRecovery = YES;
if (args.count < 3) {
abortWithErrorMessage(@"Usage: VMGUI [-recovery] bundle_path [ro_disk]");
[args removeObjectAtIndex:0];
if (args.count == 0) {
abortWithErrorMessage(@"Usage: VMGUI [-recovery] bundle_path [ro_disk] [usb_disk]");
}
bundleArg = 2;
}

bundleDir = args[bundleArg];
if (args.count > bundleArg + 1) {
roDisk = args[bundleArg + 1];
bundleDir = args[0];
[args removeObjectAtIndex:0];
if (args.count) {
roDisk = args[0];
[args removeObjectAtIndex:0];
}

if (args.count) {
usbDisk = args[0];
[args removeObjectAtIndex:0];
}

if (![bundleDir hasSuffix:@"/"]) {
Expand All @@ -77,7 +86,8 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {

VZVirtualMachine *vm =
[MacOSVirtualMachineConfigurationHelper createVirtualMachineWithBundleDir:bundleDir
roDisk:roDisk];
roDisk:roDisk
usbDisk:usbDisk];
self->_virtualMachine = vm;

self->_delegate = [MacOSVirtualMachineDelegate new];
Expand Down
5 changes: 4 additions & 1 deletion Testing/integration/actions/start_vm.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,12 @@
print(f"Snapshot: {snapshot_dir}")
# COW copy the image to this tempdir
subprocess.check_output(["cp", "-rc", extracted_path, snapshot_dir])
# Create a disk image for USB testing
usb_dmg = pathlib.Path(snapshot_dir) / "usb.dmg"
subprocess.check_output(["hdiutil", "create", "-size", "100M", "-fs", "ExFAT", "-volname", "USB", usb_dmg])
try:
subprocess.check_output(
[VMCLI, pathlib.Path(snapshot_dir) / extracted_path.name],
[VMCLI, pathlib.Path(snapshot_dir) / extracted_path.name, usb_dmg],
timeout=TIMEOUT,
)
except subprocess.TimeoutExpired:
Expand Down
10 changes: 10 additions & 0 deletions Testing/integration/dismiss_usb_popup.scpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-- Dismiss the "disk remounted" popup from Santa.
-- This is run inside test VMs.

on run argv
tell application "System Events"
tell process "Santa"
click button 1 of group 1 of window 1
end tell
end tell
end run
55 changes: 55 additions & 0 deletions Testing/integration/test_usb.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/bin/bash
set -xe

bazel run //Testing/integration:install_profile -- Testing/integration/configs/default.mobileconfig
sudo diskutil unmount force USB || true

killall moroz
/tmp/moroz -configs="$GITHUB_WORKSPACE/Testing/integration/configs/moroz_default/global.toml" -use-tls=false &
sudo santactl sync --debug
if [[ "$(sudo santactl status --json | jq .daemon.block_usb)" != "false" ]]; then
echo "USB blocking enabled with minimal config" >&2
exit 1
fi

sudo diskutil mount USB
echo test > /Volumes/USB/test
sync
sudo diskutil unmount force USB

killall moroz
/tmp/moroz -configs="$GITHUB_WORKSPACE/Testing/integration/configs/moroz_changed/global.toml" -use-tls=false &
sudo santactl sync --debug
if [[ "$(sudo santactl status --json | jq .daemon.block_usb)" != "true" ]]; then
echo "USB blocking config change didnt take effect" >&2
exit 1
fi

set +e
sudo diskutil mount USB
blocked=$?
set -e

if [[ $blocked == 0 ]]; then
echo "R/W mount succeeded with USB blocking enabled" >&2
exit 1
fi

sleep 5

# Santa should have remounted the disk RO for us. Check that it did.
bazel run //Testing/integration:dismiss_usb_popup
cat /Volumes/USB/test

sudo diskutil unmount force USB

# Ensure things can still be normally mounted if mount flags match remount opts.
set +e
sudo diskutil mount -mountOptions ro,noexec USB
blocked=$?
set -e

if [[ $blocked != 0 ]]; then
echo "RO+noexec mount failed with USB blocking enabled" >&2
exit 1
fi

0 comments on commit 7530b8f

Please sign in to comment.