Skip to content

Commit 405664f

Browse files
committed
Replace json string parameter with file
Signed-off-by: Laura Loghin <[email protected]>
1 parent 6f1567b commit 405664f

File tree

11 files changed

+101
-91
lines changed

11 files changed

+101
-91
lines changed

CHANGELOG.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
# Changelog
22

3-
## Unreleased
3+
## [Unreleased]
44

55
### Added
66

7-
- New command-line parameter for both `firecracker` and `jailer`, named
8-
`vmm-config`, which represents a JSON that can be used for configuring and
9-
starting a microVM without sending any API requests.
7+
- New command-line parameter for `firecracker`, named `--config-file`, which
8+
represents the path to a file that contains a JSON which can be used for
9+
configuring and starting a microVM without sending any API requests.
10+
- The jailer adheres to the "end of command options" convention, meaning
11+
all parameters specified after `--` are forwarded verbatim to Firecracker.
1012

1113
### Changed
1214

FAQ.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ Possible mitigations are:
198198

199199
### How can I configure and start a microVM without sending API calls?
200200

201-
Passing an optional command line parameter, `vmm-config`, to the Firecracker process
202-
allows this type of configuration. This parameter must contain the entire JSON that
203-
will be used for configuring all of the microVM's resources. One example of such JSON
204-
can be found in `tests/framework/vm_config.json`.
201+
Passing an optional command line parameter, `--config-file`, to the Firecracker
202+
process allows this type of configuration. This parameter must be the path to a
203+
file that contains the JSON specification that will be used to configure and start
204+
the microVM. One example of such file can be found at `tests/framework/vm_config.json`.

docs/getting-started.md

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -245,28 +245,23 @@ curl --unix-socket /tmp/firecracker.socket -i \
245245
#### Configuring the microVM without sending API requests
246246

247247
If you'd like to boot up a guest machine without using the API socket, you can do that
248-
by passing the parameter `vmm-config` to the Firecracker process. The command for starting
249-
Firecracker with this option will look like this:
248+
by passing the parameter `--config-file` to the Firecracker process. The command for
249+
starting Firecracker with this option will look like this:
250250

251251
```bash
252-
./firecracker --api-sock /tmp/firecracker.socket --vmm-config "JSON_BODY"
252+
./firecracker --api-sock /tmp/firecracker.socket --config-file
253+
<path_to_the_configuration_file>
253254
```
254255

255-
"JSON_BODY" should contain the entire configuration for all of the microVM's resources.
256-
You **must** provide in your JSON the configuration for the guest kernel and rootfs, as
257-
these are mandatory, but all of the other resources are optional, so it's your choice if
258-
you want to configure them or not. Because using this configuration method will also start
259-
the microVM, you need to specify all desired pre-boot configurable resources in that JSON.
260-
The names of the resources are the ones from the `firecracker.yaml` file and the names of
261-
their fields are the same that are used in API requests.
262-
The easiest way to send the configuration is by storing the JSON in a file and passing
263-
the content of the file as command line parameter, for example:
264-
265-
```bash
266-
./firecracker --api-sock /tmp/firecracker.socket --vmm-config "$(cat /path_to_the_configuration_file)"
267-
```
268-
269-
You can find an example of configuration file in `tests/framework/vm_config.json`.
256+
`path_to_the_configuration_file` should represent the path to a file that contains a
257+
JSON which stores the entire configuration for all of the microVM's resources. The
258+
JSON **must** contain the configuration for the guest kernel and rootfs, as these
259+
are mandatory, but all of the other resources are optional, so it's your choice
260+
if you want to configure them or not. Because using this configuration method will
261+
also start the microVM, you need to specify all desired pre-boot configurable resources
262+
in that JSON. The names of the resources are the ones from the `firecracker.yaml` file
263+
and the names of their fields are the same that are used in API requests.
264+
You can find an example of configuration file at `tests/framework/vm_config.json`.
270265
After the machine is booted, you can still use the socket to send API requests
271266
for post-boot operations.
272267

docs/jailer.md

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jailer --id <id> \
1414
[--netns <netns>]
1515
[--daemonize]
1616
[--seccomp-level <level>]
17-
[--vmm-config <vmm_config>]
17+
[--...extra arguments for Firecracker]
1818
```
1919

2020
- `id` is the unique VM identification string, which may contain alphanumeric
@@ -39,8 +39,14 @@ jailer --id <id> \
3939
Firecracker.
4040
- 2 (default): advanced filtering. This adds further checks on some of the
4141
parameters of the allowed syscalls.
42-
- `vmm_config` represents the json that can be used for configuring and
43-
starting the microVM without using API requests.
42+
- The jailer adheres to the "end of command options" convention, meaning
43+
all parameters specified after `--` are forwarded to Firecracker. For
44+
example, this can be paired with the `--config-file` Firecracker argument to
45+
specify a configuration file when starting Firecracker via the jailer (the
46+
file path and the resources referenced within must be valid relative to a
47+
jailed Firecracker). Please note the jailer already passes the following
48+
parameters to the Firecracker process: `--api-sock`, `--seccomp-level` and
49+
`--id`.
4450

4551
## Jailer Operation
4652

@@ -82,14 +88,13 @@ After starting, the Jailer goes through the following operations:
8288
- Drop privileges via setting the provided `uid` and `gid`.
8389
- Exec into `<exec_file_name> --id=<id> --api-sock=/api.socket
8490
--seccomp-level=<level> --start-time-us=<opaque>
85-
--start-time-cpu-us=<opaque> [--vmm-config <vmm_config>]`.
86-
Where:
91+
--start-time-cpu-us=<opaque>` (and also forward any extra arguments provided
92+
to the jailer after `--`, as mentioned in the **Jailer Usage** section),
93+
where:
8794
- `id`: (`string`) - The `id` argument provided to jailer.
8895
- `level`: (`number`) - the `--seccomp-level` argument provided to jailer.
8996
- `opaque`: (`number`) time calculated by the jailer that it spent doing
9097
its work.
91-
- `vmm_config`: (`string`) - optional argument that can be used for
92-
configuring and starting a microVM without sending API requests.
9398

9499
## Example Run and Notes
95100

@@ -196,10 +201,6 @@ We can now use the socket at
196201
`/srv/jailer/firecracker/551e7604-e35c-42b3-b825-416853441234/root/api.socket`
197202
to interact with the VM.
198203

199-
In case we want to configure the VM without using the API socket, we can also
200-
pass the `vmm-config` parameter, as mentioned in the previous section
201-
(`Jailer Operation`).
202-
203204
### Observations
204205

205206
- The user must create hard links for (or copy) any resources which will be

jailer/src/env.rs

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ pub struct Env {
5252
seccomp_level: u32,
5353
start_time_us: u64,
5454
start_time_cpu_us: u64,
55-
config_json: Option<String>,
55+
extra_args: Option<Vec<String>>,
5656
}
5757

5858
impl Env {
@@ -113,7 +113,10 @@ impl Env {
113113
.parse::<u32>()
114114
.map_err(Error::SeccompLevel)?;
115115

116-
let config_json = args.value_of("vmm-config").map(String::from);
116+
let extra_args = args
117+
.values_of("extra-args")
118+
.map(|v| v.collect::<Vec<&str>>())
119+
.map(|v| v.iter().map(|s| s.to_string()).collect());
117120

118121
Ok(Env {
119122
id: id.to_string(),
@@ -127,7 +130,7 @@ impl Env {
127130
seccomp_level,
128131
start_time_us,
129132
start_time_cpu_us,
130-
config_json,
133+
extra_args,
131134
})
132135
}
133136

@@ -307,8 +310,8 @@ impl Env {
307310
.uid(self.uid())
308311
.gid(self.gid());
309312

310-
if let Some(json) = self.config_json {
311-
command.arg(format!("--vmm-config={}", json));
313+
if let Some(args) = self.extra_args {
314+
command.args(args);
312315
}
313316
Err(Error::Exec(command.exec()))
314317
}
@@ -330,7 +333,7 @@ mod tests {
330333
chroot_base: &str,
331334
netns: Option<&str>,
332335
daemonize: bool,
333-
config_json: Option<&str>,
336+
extra_args: Option<Vec<&str>>,
334337
) -> ArgMatches<'a> {
335338
let app = clap_app();
336339

@@ -359,11 +362,12 @@ mod tests {
359362
arg_vec.push("--daemonize");
360363
}
361364

362-
if let Some(json) = config_json {
363-
arg_vec.push("--vmm-config");
364-
arg_vec.push(json);
365+
if let Some(args) = extra_args {
366+
arg_vec.push("--");
367+
for exec_arg in args {
368+
arg_vec.push(exec_arg);
369+
}
365370
}
366-
367371
app.get_matches_from_safe(arg_vec).unwrap()
368372
}
369373

@@ -376,25 +380,7 @@ mod tests {
376380
let gid = "1002";
377381
let chroot_base = "/";
378382
let netns = "zzzns";
379-
let config_json = r#"{
380-
"boot-source":{
381-
"kernel_image_path": "vmlinux.bin",
382-
"boot_args": "console=ttyS0 reboot=k panic=1 pci=off"
383-
},
384-
"drives": [
385-
{
386-
"drive_id": "rootfs",
387-
"path_on_host": "xenial.rootfs.ext4",
388-
"is_root_device": true,
389-
"is_read_only": false
390-
}
391-
],
392-
"machine-config":{
393-
"vcpu_count": 2,
394-
"mem_size_mib": 1024,
395-
"ht_enabled": false
396-
}
397-
}"#;
383+
let extra_args = vec!["--config-file", "config_file_path"];
398384

399385
// This should be fine.
400386
let good_env = Env::new(
@@ -407,7 +393,7 @@ mod tests {
407393
chroot_base,
408394
Some(netns),
409395
true,
410-
Some(config_json),
396+
Some(extra_args),
411397
),
412398
0,
413399
0,
@@ -422,9 +408,16 @@ mod tests {
422408
assert_eq!(good_env.chroot_dir(), chroot_dir);
423409
assert_eq!(format!("{}", good_env.gid()), gid);
424410
assert_eq!(format!("{}", good_env.uid()), uid);
411+
425412
assert_eq!(good_env.netns, Some(netns.to_string()));
426413
assert!(good_env.daemonize);
427-
assert_eq!(good_env.config_json, Some(config_json.to_string()));
414+
assert_eq!(
415+
good_env.extra_args,
416+
Some(vec![
417+
"--config-file".to_string(),
418+
"config_file_path".to_string()
419+
])
420+
);
428421

429422
let another_good_env = Env::new(
430423
make_args(

jailer/src/lib.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use std::io;
2020
use std::path::{Path, PathBuf};
2121
use std::result;
2222

23-
use clap::{App, Arg, ArgMatches};
23+
use clap::{App, AppSettings, Arg, ArgMatches};
2424

2525
use env::Env;
2626
use fc_util::validators;
@@ -222,6 +222,7 @@ pub fn clap_app<'a, 'b>() -> App<'a, 'b> {
222222
.version(crate_version!())
223223
.author(crate_authors!())
224224
.about("Jail a microVM.")
225+
.setting(AppSettings::TrailingVarArg)
225226
.arg(
226227
Arg::with_name("id")
227228
.long("id")
@@ -299,10 +300,11 @@ pub fn clap_app<'a, 'b>() -> App<'a, 'b> {
299300
.possible_values(&["0", "1", "2"]),
300301
)
301302
.arg(
302-
Arg::with_name("vmm-config")
303-
.long("vmm-config")
303+
Arg::with_name("extra-args")
304+
.help("Arguments that will be passed verbatim to the exec file.")
305+
.required(false)
304306
.takes_value(true)
305-
.required(false),
307+
.multiple(true),
306308
)
307309
}
308310

src/main.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ extern crate vmm;
1717
use backtrace::Backtrace;
1818
use clap::{App, Arg};
1919

20+
use std::fs;
2021
use std::io::ErrorKind;
2122
use std::panic;
2223
use std::path::PathBuf;
@@ -109,8 +110,9 @@ fn main() {
109110
.hidden(true),
110111
)
111112
.arg(
112-
Arg::with_name("vmm-config")
113-
.long("vmm-config")
113+
Arg::with_name("config-file")
114+
.long("config-file")
115+
.help("Path to a file that contains the microVM configuration in JSON format.")
114116
.takes_value(true)
115117
.required(false),
116118
)
@@ -148,7 +150,11 @@ fn main() {
148150
.expect("'start-time-cpu_us' parameter expected to be of 'u64' type.")
149151
});
150152

151-
let vmm_config_json = cmd_arguments.value_of("vmm-config").map(String::from);
153+
let vmm_config_json = if let Some(path) = cmd_arguments.value_of("config-file") {
154+
Some(fs::read_to_string(path).expect("Unable to open or read from the configuration file"))
155+
} else {
156+
None
157+
};
152158

153159
let shared_info = Arc::new(RwLock::new(InstanceInfo {
154160
state: InstanceState::Uninitialized,

tests/framework/jailer.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def __del__(self):
6262
"""Cleanup this jailer context."""
6363
self.cleanup()
6464

65-
def construct_param_list(self, config_json):
65+
def construct_param_list(self, config_file):
6666
"""Create the list of parameters we want the jailer to start with.
6767
6868
We want to be able to vary any parameter even the required ones as we
@@ -94,8 +94,9 @@ def construct_param_list(self, config_json):
9494
jailer_param_list.extend(
9595
['--seccomp-level', str(self.seccomp_level)]
9696
)
97-
if config_json is not None:
98-
jailer_param_list.extend(['--vmm-config', str(config_json)])
97+
if config_file is not None:
98+
jailer_param_list.extend(['--'])
99+
jailer_param_list.extend(['--config-file', str(config_file)])
99100
return jailer_param_list
100101

101102
def chroot_base_with_id(self):

tests/framework/microvm.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def __init__(
4646
build_feature='',
4747
monitor_memory=True,
4848
bin_cloner_path=None,
49-
config_json=None
49+
config_file=None
5050
):
5151
"""Set up microVM attributes, paths, and data structures."""
5252
# Unique identifier for this machine.
@@ -96,8 +96,9 @@ def __init__(
9696
self.machine_cfg = None
9797
self.vsock = None
9898

99-
# Optional json for configuring microvm from command line parameter.
100-
self.config_json = config_json
99+
# Optional file that contains a json for configuring microvm from
100+
# command line parameter.
101+
self.config_file = config_file
101102

102103
# The ssh config dictionary is populated with information about how
103104
# to connect to a microVM that has ssh capability. The path of the
@@ -264,7 +265,7 @@ def spawn(self):
264265
self.network = Network(self._api_socket, self._api_session)
265266
self.vsock = Vsock(self._api_socket, self._api_session)
266267

267-
jailer_param_list = self._jailer.construct_param_list(self.config_json)
268+
jailer_param_list = self._jailer.construct_param_list(self.config_file)
268269

269270
# When the daemonize flag is on, we want to clone-exec into the
270271
# jailer rather than executing it via spawning a shell. Going

tests/integration_tests/functional/test_cmd_line_start.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010

1111

1212
@pytest.mark.parametrize(
13-
"vm_config_json",
13+
"vm_config_file",
1414
["framework/vm_config.json"]
1515
)
16-
def test_config_json_start(test_microvm_with_ssh, vm_config_json):
16+
def test_config_json_start(test_microvm_with_ssh, vm_config_file):
1717
"""Start a microvm using configuration sent as command line parameter.
1818
1919
Create resources needed for the configuration of the microvm, then
@@ -34,7 +34,18 @@ def test_config_json_start(test_microvm_with_ssh, vm_config_json):
3434
test_microvm.create_jailed_resource(log_fifo.path, create_jail=True)
3535
test_microvm.create_jailed_resource(metrics_fifo.path, create_jail=True)
3636

37-
test_microvm.config_json = open(vm_config_json).read()
37+
# vm_config_file is the source file that keeps the desired vmm
38+
# configuration. vm_config_path is the configuration file we
39+
# create inside the jail, such that it can be accessed by
40+
# firecracker after it starts.
41+
vm_config_path = os.path.join(test_microvm.path,
42+
os.path.basename(vm_config_file))
43+
with open(vm_config_file) as f1:
44+
with open(vm_config_path, "w") as f2:
45+
for line in f1:
46+
f2.write(line)
47+
test_microvm.create_jailed_resource(vm_config_path, create_jail=True)
48+
test_microvm.config_file = os.path.basename(vm_config_file)
3849
test_microvm.spawn()
3950

4051
response = test_microvm.machine_cfg.get()

0 commit comments

Comments
 (0)