1#![deny(missing_docs)]
4#![deny(warnings)]
5
6use clap::ArgMatches;
7use libc::VMADDR_CID_HOST;
8#[cfg(test)]
9use libc::VMADDR_CID_LOCAL;
10use serde::{Deserialize, Serialize};
11use std::fs::File;
12use std::str::FromStr;
13
14use crate::common::{NitroCliErrorEnum, NitroCliFailure, NitroCliResult, VMADDR_CID_PARENT};
15use crate::get_id_by_name;
16use crate::new_nitro_cli_failure;
17use crate::utils::PcrType;
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct RunEnclavesArgs {
22 pub eif_path: String,
24 pub enclave_cid: Option<u64>,
26 pub memory_mib: u64,
28 pub cpu_ids: Option<Vec<u32>>,
30 #[serde(default)]
32 pub debug_mode: bool,
33 #[serde(default)]
35 pub attach_console: bool,
36 pub cpu_count: Option<u32>,
38 pub enclave_name: Option<String>,
40}
41
42impl RunEnclavesArgs {
43 pub fn new_with(args: &ArgMatches) -> NitroCliResult<Self> {
45 if let Some(config_file) = args.get_one::<String>("config") {
46 let file = File::open(config_file).map_err(|err| {
47 new_nitro_cli_failure!(
48 &format!("Failed to open config file: {err:?}"),
49 NitroCliErrorEnum::FileOperationFailure
50 )
51 .add_info(vec![config_file, "Open"])
52 })?;
53
54 let mut json: RunEnclavesArgs = serde_json::from_reader(file).map_err(|err| {
55 new_nitro_cli_failure!(
56 &format!("Invalid JSON format for config file: {err:?}"),
57 NitroCliErrorEnum::SerdeError
58 )
59 })?;
60 if json.cpu_count.is_none() && json.cpu_ids.is_none() {
61 return Err(new_nitro_cli_failure!(
62 "Missing both `cpu-count` and `cpu-ids`",
63 NitroCliErrorEnum::MissingArgument
64 ));
65 }
66 if json.cpu_count.is_some() && json.cpu_ids.is_some() {
67 return Err(new_nitro_cli_failure!(
68 "`cpu-count` and `cpu-ids` cannot be used together",
69 NitroCliErrorEnum::ConflictingArgument
70 ));
71 }
72
73 json.debug_mode = json.debug_mode || json.attach_console;
75
76 Ok(json)
77 } else {
78 Ok(RunEnclavesArgs {
79 cpu_count: parse_cpu_count(args)
80 .map_err(|err| err.add_subaction("Parse CPU count".to_string()))?,
81 eif_path: parse_eif_path(args)
82 .map_err(|err| err.add_subaction("Parse EIF path".to_string()))?,
83 enclave_cid: parse_enclave_cid(args)
84 .map_err(|err| err.add_subaction("Parse enclave CID".to_string()))?,
85 memory_mib: parse_memory(args)
86 .map_err(|err| err.add_subaction("Parse memory".to_string()))?,
87 cpu_ids: parse_cpu_ids(args)
88 .map_err(|err| err.add_subaction("Parse CPU IDs".to_string()))?,
89 debug_mode: debug_mode(args),
90 attach_console: attach_console(args),
91 enclave_name: parse_enclave_name(args)
92 .map_err(|err| err.add_subaction("Parse enclave name".to_string()))?,
93 })
94 }
95 }
96}
97
98#[derive(Debug, Clone)]
100pub struct BuildEnclavesArgs {
101 pub docker_uri: String,
103 pub docker_dir: Option<String>,
105 pub output: String,
107 pub signing_certificate: Option<String>,
109 pub private_key: Option<String>,
111 pub img_name: Option<String>,
113 pub img_version: Option<String>,
115 pub metadata: Option<String>,
117}
118
119impl BuildEnclavesArgs {
120 pub fn new_with(args: &ArgMatches) -> NitroCliResult<Self> {
122 Ok(BuildEnclavesArgs {
123 docker_uri: parse_docker_tag(args).ok_or_else(|| {
124 new_nitro_cli_failure!(
125 "`docker-uri` argument not found",
126 NitroCliErrorEnum::MissingArgument
127 )
128 .add_info(vec!["docker-uri"])
129 })?,
130 docker_dir: parse_docker_dir(args),
131 output: parse_output(args).ok_or_else(|| {
132 new_nitro_cli_failure!(
133 "`output` argument not found",
134 NitroCliErrorEnum::MissingArgument
135 )
136 .add_info(vec!["output"])
137 })?,
138 signing_certificate: parse_signing_certificate(args),
139 private_key: parse_private_key(args),
140 img_name: parse_image_name(args),
141 img_version: parse_image_version(args),
142 metadata: parse_metadata(args),
143 })
144 }
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct TerminateEnclavesArgs {
150 pub enclave_id: String,
152}
153
154impl TerminateEnclavesArgs {
155 pub fn new_with(args: &ArgMatches) -> NitroCliResult<Self> {
157 match parse_enclave_name(args)
159 .map_err(|e| e.add_subaction("Parse Enclave Name".to_string()))?
160 {
161 Some(name) => Ok(TerminateEnclavesArgs {
162 enclave_id: get_id_by_name(name)
163 .map_err(|e| e.add_subaction("Get ID by Name".to_string()))?,
164 }),
165 None => Ok(TerminateEnclavesArgs {
166 enclave_id: parse_enclave_id(args)
167 .map_err(|e| e.add_subaction("Parse enclave ID".to_string()))?,
168 }),
169 }
170 }
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct ConsoleArgs {
176 pub enclave_id: String,
178 pub disconnect_timeout_sec: Option<u64>,
180}
181
182impl ConsoleArgs {
183 pub fn new_with(args: &ArgMatches) -> NitroCliResult<Self> {
185 let enclave_id = match parse_enclave_name(args)
187 .map_err(|e| e.add_subaction("Parse Enclave Name".to_string()))?
188 {
189 Some(name) => {
190 get_id_by_name(name).map_err(|e| e.add_subaction("Get ID by Name".to_string()))?
191 }
192 None => parse_enclave_id(args)
193 .map_err(|e| e.add_subaction("Parse enclave ID".to_string()))?,
194 };
195
196 Ok(ConsoleArgs {
197 enclave_id,
198 disconnect_timeout_sec: parse_disconnect_timeout(args)
199 .map_err(|e| e.add_subaction("Parse disconnect timeout".to_string()))?,
200 })
201 }
202}
203
204#[derive(Serialize, Deserialize)]
206pub struct EmptyArgs {}
207
208#[derive(Debug, Clone, Serialize, Deserialize)]
210pub struct DescribeEnclavesArgs {
211 pub metadata: bool,
213}
214
215impl DescribeEnclavesArgs {
216 pub fn new_with(args: &ArgMatches) -> Self {
218 DescribeEnclavesArgs {
219 metadata: args.get_flag("metadata"),
220 }
221 }
222}
223
224#[derive(Debug, Clone)]
226pub struct ExplainArgs {
227 pub error_code_str: String,
229}
230
231impl ExplainArgs {
232 pub fn new_with(args: &ArgMatches) -> NitroCliResult<Self> {
234 Ok(ExplainArgs {
235 error_code_str: parse_error_code_str(args)
236 .map_err(|e| e.add_subaction("Parse error code".to_string()))?,
237 })
238 }
239}
240
241pub struct PcrArgs {
243 pub path: String,
245 pub pcr_type: PcrType,
247}
248
249impl PcrArgs {
250 pub fn new_with(args: &ArgMatches) -> NitroCliResult<Self> {
252 let (val_name, pcr_type) = match args.contains_id("signing-certificate") {
253 true => ("signing-certificate", PcrType::SigningCertificate),
254 false => ("input", PcrType::DefaultType),
255 };
256 let path = parse_file_path(args, val_name)
257 .map_err(|e| e.add_subaction("Parse PCR file".to_string()))?;
258 Ok(Self { path, pcr_type })
259 }
260}
261
262#[derive(Debug, Clone)]
264pub struct SignEifArgs {
265 pub eif_path: String,
267 pub signing_certificate: Option<String>,
269 pub private_key: Option<String>,
271}
272
273impl SignEifArgs {
274 pub fn new_with(args: &ArgMatches) -> NitroCliResult<Self> {
276 let signing_certificate = parse_signing_certificate(args);
277 let private_key = parse_private_key(args);
278
279 Ok(SignEifArgs {
280 eif_path: parse_eif_path(args)
281 .map_err(|e| e.add_subaction("Parse EIF path".to_string()))?,
282 signing_certificate,
283 private_key,
284 })
285 }
286}
287
288fn parse_file_path(args: &ArgMatches, val_name: &str) -> NitroCliResult<String> {
290 let path = args.get_one::<String>(val_name).ok_or_else(|| {
291 new_nitro_cli_failure!(
292 "`input` or `signing-certificate` argument not found",
293 NitroCliErrorEnum::MissingArgument
294 )
295 })?;
296 Ok(path.into())
297}
298
299#[derive(Debug)]
300enum MemoryUnit {
301 Mebibytes,
302 Gibibytes,
303 Tebibytes,
304}
305
306#[derive(Debug)]
307struct UnknownMemoryUnitErr;
308
309impl MemoryUnit {
310 fn to_mebibytes(&self) -> u64 {
311 match self {
312 MemoryUnit::Mebibytes => 1,
313 MemoryUnit::Gibibytes => 1024,
314 MemoryUnit::Tebibytes => 1024 * 1024,
315 }
316 }
317}
318
319impl FromStr for MemoryUnit {
320 type Err = UnknownMemoryUnitErr;
321
322 fn from_str(s: &str) -> Result<Self, Self::Err> {
323 match s {
324 "M" | "m" | "" => Ok(MemoryUnit::Mebibytes),
325 "G" | "g" => Ok(MemoryUnit::Gibibytes),
326 "T" | "t" => Ok(MemoryUnit::Tebibytes),
327 _ => Err(UnknownMemoryUnitErr),
328 }
329 }
330}
331
332pub fn parse_memory(args: &ArgMatches) -> NitroCliResult<u64> {
340 let memory = args.get_one::<String>("memory").ok_or_else(|| {
341 new_nitro_cli_failure!(
342 "`memory` argument not found",
343 NitroCliErrorEnum::MissingArgument
344 )
345 })?;
346
347 let (num_str, size_str) = match memory.find(|c: char| !c.is_numeric()) {
348 Some(index) => memory.split_at(index),
349 None => (memory.as_str(), ""),
350 };
351 let num = num_str.parse::<u64>().map_err(|_| {
352 new_nitro_cli_failure!(
353 "`memory` argument does not contain a number",
354 NitroCliErrorEnum::InvalidArgument
355 )
356 .add_info(vec!["memory", memory])
357 })?;
358
359 let unit = size_str.parse::<MemoryUnit>().map_err(|_| {
360 new_nitro_cli_failure!(
361 "`memory` argument does not contain a valid size indicator",
362 NitroCliErrorEnum::InvalidArgument
363 )
364 .add_info(vec!["memory", memory])
365 })?;
366 Ok(num * unit.to_mebibytes())
367}
368
369fn parse_docker_tag(args: &ArgMatches) -> Option<String> {
371 args.get_one::<String>("docker-uri").map(String::from)
372}
373
374fn parse_docker_dir(args: &ArgMatches) -> Option<String> {
376 args.get_one::<String>("docker-dir").map(String::from)
377}
378
379fn parse_enclave_cid(args: &ArgMatches) -> NitroCliResult<Option<u64>> {
381 let enclave_cid = if let Some(enclave_cid) = args.get_one::<String>("enclave-cid") {
382 let enclave_cid: u64 = enclave_cid.parse().map_err(|_| {
383 new_nitro_cli_failure!(
384 "`enclave-cid` is not a number",
385 NitroCliErrorEnum::InvalidArgument
386 )
387 .add_info(vec!["enclave-cid", enclave_cid])
388 })?;
389
390 if enclave_cid == 0 {
398 eprintln!("The enclave CID will be auto-generated as the provided CID is 0");
399 }
400
401 if enclave_cid > 0 && enclave_cid <= VMADDR_CID_HOST as u64 {
402 return Err(new_nitro_cli_failure!(
403 &format!("CID {enclave_cid} is a well-known CID, not to be used for enclaves"),
404 NitroCliErrorEnum::InvalidArgument
405 ));
406 }
407
408 if enclave_cid == u32::MAX as u64 {
409 return Err(new_nitro_cli_failure!(
410 &format!("CID {enclave_cid} is a well-known CID, not to be used for enclaves"),
411 NitroCliErrorEnum::InvalidArgument
412 ));
413 }
414
415 if enclave_cid == VMADDR_CID_PARENT as u64 {
417 return Err(new_nitro_cli_failure!(
418 &format!(
419 "CID {enclave_cid} is the CID of the parent VM, not to be used for enclaves"
420 ),
421 NitroCliErrorEnum::InvalidArgument
422 ));
423 }
424
425 if enclave_cid > u32::MAX as u64 {
427 return Err(new_nitro_cli_failure!(
428 &format!(
429 "CID {enclave_cid} is higher than the maximum supported (u32 max) for a vsock device"
430 ),
431 NitroCliErrorEnum::InvalidArgument
432 ));
433 }
434
435 Some(enclave_cid)
436 } else {
437 None
438 };
439
440 Ok(enclave_cid)
441}
442
443fn parse_eif_path(args: &ArgMatches) -> NitroCliResult<String> {
445 let eif_path = args.get_one::<String>("eif-path").ok_or_else(|| {
446 new_nitro_cli_failure!(
447 "`eif-path` argument not found",
448 NitroCliErrorEnum::MissingArgument
449 )
450 })?;
451 Ok(eif_path.into())
452}
453
454fn parse_enclave_id(args: &ArgMatches) -> NitroCliResult<String> {
456 let enclave_id = args.get_one::<String>("enclave-id").ok_or_else(|| {
457 new_nitro_cli_failure!(
458 "`enclave-id` argument not found",
459 NitroCliErrorEnum::MissingArgument
460 )
461 })?;
462 Ok(enclave_id.into())
463}
464
465fn parse_disconnect_timeout(args: &ArgMatches) -> NitroCliResult<Option<u64>> {
467 let disconnect_timeout = match args.get_one::<String>("disconnect-timeout") {
468 Some(arg) => Some(arg.parse::<u64>().map_err(|_| {
469 new_nitro_cli_failure!(
470 "`disconnect-timeout` argument can't be parsed as a number",
471 NitroCliErrorEnum::InvalidArgument
472 )
473 .add_info(vec!["disconnect-timeout", arg])
474 })?),
475 None => None,
476 };
477 Ok(disconnect_timeout)
478}
479
480fn parse_cpu_ids(args: &ArgMatches) -> NitroCliResult<Option<Vec<u32>>> {
482 args.get_many::<String>("cpu-ids")
483 .map(|values| {
484 values
485 .map(|id| {
486 id.parse().map_err(|_| {
487 new_nitro_cli_failure!(
488 "`cpu-id` is not a number",
489 NitroCliErrorEnum::InvalidArgument
490 )
491 .add_info(vec!["cpu-id", id])
492 })
493 })
494 .collect()
495 })
496 .transpose()
497}
498
499fn parse_cpu_count(args: &ArgMatches) -> NitroCliResult<Option<u32>> {
501 args.get_one::<String>("cpu-count")
502 .map(|count| {
503 count.parse().map_err(|_| {
504 new_nitro_cli_failure!(
505 "`cpu-count` is not a number",
506 NitroCliErrorEnum::InvalidArgument
507 )
508 .add_info(vec!["cpu-count", count])
509 })
510 })
511 .transpose()
512}
513
514fn parse_output(args: &ArgMatches) -> Option<String> {
516 args.get_one::<String>("output-file").map(String::from)
517}
518
519fn debug_mode(args: &ArgMatches) -> bool {
521 args.get_flag("debug-mode") || args.get_flag("attach-console")
522}
523
524fn attach_console(args: &ArgMatches) -> bool {
526 args.get_flag("attach-console")
527}
528
529fn parse_enclave_name(args: &ArgMatches) -> NitroCliResult<Option<String>> {
531 Ok(args.get_one::<String>("enclave-name").map(String::from))
532}
533
534fn parse_signing_certificate(args: &ArgMatches) -> Option<String> {
535 args.get_one::<String>("signing-certificate")
536 .map(String::from)
537}
538
539fn parse_private_key(args: &ArgMatches) -> Option<String> {
540 args.get_one::<String>("private-key").map(String::from)
541}
542
543fn parse_image_name(args: &ArgMatches) -> Option<String> {
544 args.get_one::<String>("image_name").map(String::from)
545}
546
547fn parse_image_version(args: &ArgMatches) -> Option<String> {
548 args.get_one::<String>("image_version").map(String::from)
549}
550
551fn parse_metadata(args: &ArgMatches) -> Option<String> {
552 args.get_one::<String>("metadata").map(String::from)
553}
554
555fn parse_error_code_str(args: &ArgMatches) -> NitroCliResult<String> {
556 let error_code_str = args.get_one::<String>("error-code").ok_or_else(|| {
557 new_nitro_cli_failure!(
558 "`error-code` argument not found",
559 NitroCliErrorEnum::MissingArgument
560 )
561 })?;
562 Ok(error_code_str.to_string())
563}
564
565#[cfg(test)]
566mod tests {
567 use super::*;
568
569 use crate::common::construct_error_message;
570 use crate::create_app;
571
572 use clap::{Arg, Command};
573
574 fn parse_config_file(args: &ArgMatches) -> NitroCliResult<String> {
576 let config_file = args
577 .get_one::<String>("config")
578 .ok_or(new_nitro_cli_failure!(
579 "`config` argument not found",
580 NitroCliErrorEnum::MissingArgument
581 ))?;
582 Ok(config_file.into())
583 }
584
585 #[test]
586 fn test_parse_memory() {
587 let app = create_app!();
588 let args = vec![
589 "nitro-cli",
590 "run-enclave",
591 "--memory",
592 "256_mb",
593 "--cpu-count",
594 "2",
595 "--eif-path",
596 "non_existing_eif.eif",
597 ];
598
599 let matches = app.try_get_matches_from(args);
600 assert!(matches.is_ok());
601
602 let result = parse_memory(
603 matches
604 .as_ref()
605 .unwrap()
606 .subcommand_matches("run-enclave")
607 .unwrap(),
608 );
609 assert!(result.is_err());
610 if let Err(err_info) = result {
611 let err_str = construct_error_message(&err_info);
612 assert!(err_str.contains("Invalid argument provided"))
613 }
614
615 let app = create_app!();
616 let args = vec![
617 "nitro-cli",
618 "run-enclave",
619 "--memory",
620 "256",
621 "--cpu-count",
622 "2",
623 "--eif-path",
624 "non_existing_eif.eif",
625 ];
626
627 let matches = app.try_get_matches_from(args);
628 assert!(matches.is_ok());
629
630 let result = parse_memory(
631 matches
632 .as_ref()
633 .unwrap()
634 .subcommand_matches("run-enclave")
635 .unwrap(),
636 );
637 assert_eq!(result, Ok(256));
638
639 let app = create_app!();
640 let args = vec![
641 "nitro-cli",
642 "run-enclave",
643 "--memory",
644 "100M",
645 "--cpu-count",
646 "2",
647 "--eif-path",
648 "non_existing_eif.eif",
649 ];
650
651 let matches = app.try_get_matches_from(args);
652 assert!(matches.is_ok());
653
654 let result = parse_memory(
655 matches
656 .as_ref()
657 .unwrap()
658 .subcommand_matches("run-enclave")
659 .unwrap(),
660 );
661 assert_eq!(result, Ok(100));
662
663 let app = create_app!();
664 let args = vec![
665 "nitro-cli",
666 "run-enclave",
667 "--memory",
668 "10G",
669 "--cpu-count",
670 "2",
671 "--eif-path",
672 "non_existing_eif.eif",
673 ];
674
675 let matches = app.try_get_matches_from(args);
676 assert!(matches.is_ok());
677
678 let result = parse_memory(
679 matches
680 .as_ref()
681 .unwrap()
682 .subcommand_matches("run-enclave")
683 .unwrap(),
684 );
685 assert_eq!(result, Ok(10_240));
686
687 let app = create_app!();
688 let args = vec![
689 "nitro-cli",
690 "run-enclave",
691 "--memory",
692 "2T",
693 "--cpu-count",
694 "2",
695 "--eif-path",
696 "non_existing_eif.eif",
697 ];
698
699 let matches = app.try_get_matches_from(args);
700 assert!(matches.is_ok());
701
702 let result = parse_memory(
703 matches
704 .as_ref()
705 .unwrap()
706 .subcommand_matches("run-enclave")
707 .unwrap(),
708 );
709 assert_eq!(result, Ok(2 * 1024 * 1024));
710 }
711
712 #[test]
713 fn test_parse_docker_tag() {
714 let app = create_app!();
715 let args = vec![
716 "nitro-cli",
717 "build-enclave",
718 "--docker-uri",
719 "mytag",
720 "--docker-dir",
721 "/home/user/non_existing_dir",
722 "--output-file",
723 "sample_eif.eif",
724 ];
725 let matches = app.try_get_matches_from(args);
726 assert!(matches.is_ok());
727
728 let result = parse_docker_tag(
729 matches
730 .as_ref()
731 .unwrap()
732 .subcommand_matches("build-enclave")
733 .unwrap(),
734 );
735 assert!(result.is_some());
736 assert_eq!(result.unwrap(), "mytag");
737 }
738
739 #[test]
740 fn test_parse_docker_dir() {
741 let app = create_app!();
742 let args = vec![
743 "nitro-cli",
744 "build-enclave",
745 "--docker-uri",
746 "mytag",
747 "--docker-dir",
748 "/home/user/non_existing_dir",
749 "--output-file",
750 "sample_eif.eif",
751 ];
752 let matches = app.try_get_matches_from(args);
753 assert!(matches.is_ok());
754
755 let result = parse_docker_dir(
756 matches
757 .as_ref()
758 .unwrap()
759 .subcommand_matches("build-enclave")
760 .unwrap(),
761 );
762 assert!(result.is_some());
763 assert_eq!(result.unwrap(), "/home/user/non_existing_dir");
764 }
765
766 #[test]
767 fn test_parse_enclave_cid_correct() {
768 let app = create_app!();
769 let args = vec![
770 "nitro-cli",
771 "run-enclave",
772 "--memory",
773 "256",
774 "--cpu-count",
775 "2",
776 "--eif-path",
777 "non_existing_eif.eif",
778 "--enclave-cid",
779 "10",
780 ];
781 let matches = app.try_get_matches_from(args);
782 assert!(matches.is_ok());
783
784 let result = parse_enclave_cid(
785 matches
786 .as_ref()
787 .unwrap()
788 .subcommand_matches("run-enclave")
789 .unwrap(),
790 );
791 assert!(result.is_ok());
792
793 if let Some(parsed_cid) = result.unwrap() {
794 assert_eq!(parsed_cid, 10);
795 }
796 }
797
798 #[test]
799 fn test_parse_enclave_cid_to_be_autogenerated() {
800 let app = create_app!();
801 let args = vec![
802 "nitro-cli",
803 "run-enclave",
804 "--memory",
805 "256",
806 "--cpu-count",
807 "2",
808 "--eif-path",
809 "non_existing_eif.eif",
810 "--enclave-cid",
811 "0",
812 ];
813 let matches = app.try_get_matches_from(args);
814 assert!(matches.is_ok());
815
816 let result = parse_enclave_cid(
817 matches
818 .as_ref()
819 .unwrap()
820 .subcommand_matches("run-enclave")
821 .unwrap(),
822 );
823 assert!(result.is_ok());
824
825 if let Some(parsed_cid) = result.unwrap() {
826 assert_eq!(parsed_cid, 0);
827 }
828 }
829
830 #[test]
831 fn test_parse_enclave_cid_str() {
832 let app = create_app!();
833 let args = vec![
834 "nitro-cli",
835 "run-enclave",
836 "--memory",
837 "256",
838 "--cpu-count",
839 "2",
840 "--eif-path",
841 "non_existing_eif.eif",
842 "--enclave-cid",
843 "0x1g",
844 ];
845 let matches = app.try_get_matches_from(args);
846 assert!(matches.is_ok());
847
848 let result = parse_enclave_cid(
849 matches
850 .as_ref()
851 .unwrap()
852 .subcommand_matches("run-enclave")
853 .unwrap(),
854 );
855 assert!(result.is_err());
856 if let Err(err_info) = result {
857 let err_str = construct_error_message(&err_info);
858 assert!(err_str.contains("Invalid argument provided"))
859 }
860 }
861
862 #[test]
863 fn test_parse_enclave_cid_well_known_cid_local() {
864 let app = create_app!();
865 let cid_local = VMADDR_CID_LOCAL.to_string();
866 let args = vec![
867 "nitro-cli",
868 "run-enclave",
869 "--memory",
870 "256",
871 "--cpu-count",
872 "2",
873 "--eif-path",
874 "non_existing_eif.eif",
875 "--enclave-cid",
876 &cid_local,
877 ];
878 let matches = app.try_get_matches_from(args);
879 assert!(matches.is_ok());
880
881 let result = parse_enclave_cid(
882 matches
883 .as_ref()
884 .unwrap()
885 .subcommand_matches("run-enclave")
886 .unwrap(),
887 );
888 assert!(result.is_err());
889 if let Err(err_info) = result {
890 let err_str = construct_error_message(&err_info);
891 assert!(err_str.contains("Invalid argument provided"));
892 }
893 }
894
895 #[test]
896 fn test_parse_enclave_cid_well_known_cid_host() {
897 let app = create_app!();
898 let cid_host = VMADDR_CID_HOST.to_string();
899 let args = vec![
900 "nitro-cli",
901 "run-enclave",
902 "--memory",
903 "256",
904 "--cpu-count",
905 "2",
906 "--eif-path",
907 "non_existing_eif.eif",
908 "--enclave-cid",
909 &cid_host,
910 ];
911 let matches = app.try_get_matches_from(args);
912 assert!(matches.is_ok());
913
914 let result = parse_enclave_cid(
915 matches
916 .as_ref()
917 .unwrap()
918 .subcommand_matches("run-enclave")
919 .unwrap(),
920 );
921 assert!(result.is_err());
922 if let Err(err_info) = result {
923 let err_str = construct_error_message(&err_info);
924 assert!(err_str.contains("Invalid argument provided"));
925 }
926 }
927
928 #[test]
929 fn test_parse_enclave_cid_parent_vm() {
930 let app = create_app!();
931 let parent_vm_cid = VMADDR_CID_PARENT.to_string();
932 let args = vec![
933 "nitro-cli",
934 "run-enclave",
935 "--memory",
936 "256",
937 "--cpu-count",
938 "2",
939 "--eif-path",
940 "non_existing_eif.eif",
941 "--enclave-cid",
942 &parent_vm_cid,
943 ];
944 let matches = app.try_get_matches_from(args);
945 assert!(matches.is_ok());
946
947 let result = parse_enclave_cid(
948 matches
949 .as_ref()
950 .unwrap()
951 .subcommand_matches("run-enclave")
952 .unwrap(),
953 );
954 assert!(result.is_err());
955 if let Err(err_info) = result {
956 let err_str = construct_error_message(&err_info);
957 assert!(err_str.contains("Invalid argument provided"));
958 }
959 }
960
961 #[test]
962 fn test_parse_enclave_cid_negative() {
963 let app = create_app!();
964 let args = vec![
965 "nitro-cli",
966 "run-enclave",
967 "--memory",
968 "256",
969 "--cpu-count",
970 "2",
971 "--eif-path",
972 "non_existing_eif.eif",
973 "--enclave-cid",
974 "-18",
975 ];
976 let matches = app.try_get_matches_from(args);
977 assert!(matches.is_err());
979 }
980
981 #[test]
982 fn test_parse_eif_path() {
983 let app = create_app!();
984 let args = vec![
985 "nitro-cli",
986 "run-enclave",
987 "--memory",
988 "256",
989 "--cpu-count",
990 "2",
991 "--eif-path",
992 "non_existing_eif.eif",
993 ];
994 let matches = app.try_get_matches_from(args);
995 assert!(matches.is_ok());
996
997 let result = parse_eif_path(
998 matches
999 .as_ref()
1000 .unwrap()
1001 .subcommand_matches("run-enclave")
1002 .unwrap(),
1003 );
1004 assert!(result.is_ok());
1005 assert_eq!(result.unwrap(), "non_existing_eif.eif");
1006 }
1007
1008 #[test]
1009 fn test_parse_enclave_id() {
1010 let app = create_app!();
1011 let args = vec![
1012 "nitro-cli",
1013 "terminate-enclave",
1014 "--enclave-id",
1015 "i-0000-enc-1234",
1016 ];
1017 let matches = app.try_get_matches_from(args);
1018 assert!(matches.is_ok());
1019
1020 let result = parse_enclave_id(
1021 matches
1022 .as_ref()
1023 .unwrap()
1024 .subcommand_matches("terminate-enclave")
1025 .unwrap(),
1026 );
1027 assert!(result.is_ok());
1028 assert_eq!(result.unwrap(), "i-0000-enc-1234");
1029 }
1030
1031 #[test]
1032 fn test_parse_cpu_ids_correct() {
1033 let app = create_app!();
1034 let args = vec![
1035 "nitro-cli",
1036 "run-enclave",
1037 "--cpu-ids",
1038 "1",
1039 "3",
1040 "--eif-path",
1041 "non_existing_eif.eif",
1042 "--memory",
1043 "64",
1044 ];
1045 let matches = app.try_get_matches_from(args);
1046 assert!(matches.is_ok());
1047
1048 let result = parse_cpu_ids(
1049 matches
1050 .as_ref()
1051 .unwrap()
1052 .subcommand_matches("run-enclave")
1053 .unwrap(),
1054 );
1055 assert!(result.is_ok());
1056
1057 if let Some(parsed_cpu_ids) = result.unwrap() {
1058 assert_eq!(parsed_cpu_ids.len(), 2);
1059 assert_eq!(parsed_cpu_ids[0], 1);
1060 assert_eq!(parsed_cpu_ids[1], 3);
1061 }
1062 }
1063
1064 #[test]
1065 fn test_parse_cpu_ids_negative() {
1066 let app = create_app!();
1067 let args = vec![
1068 "nitro-cli",
1069 "run-enclave",
1070 "--cpu-ids",
1071 "1",
1072 "-5",
1073 "--eif-path",
1074 "non_existing_eif.eif",
1075 "--memory 64",
1076 ];
1077 let matches = app.try_get_matches_from(args);
1078 assert!(matches.is_err());
1080 }
1081
1082 #[test]
1083 fn test_parse_cpu_ids_str() {
1084 let app = create_app!();
1085 let args = vec![
1086 "nitro-cli",
1087 "run-enclave",
1088 "--cpu-ids",
1089 "1",
1090 "three",
1091 "--eif-path",
1092 "non_existing_eif.eif",
1093 "--memory",
1094 "64",
1095 ];
1096 let matches = app.try_get_matches_from(args);
1097 assert!(matches.is_ok());
1098
1099 let result = parse_cpu_ids(
1100 matches
1101 .as_ref()
1102 .unwrap()
1103 .subcommand_matches("run-enclave")
1104 .unwrap(),
1105 );
1106 assert!(result.is_err());
1107 if let Err(err_info) = result {
1108 let err_str = construct_error_message(&err_info);
1109 assert!(err_str.contains("Invalid argument provided"));
1110 }
1111 }
1112
1113 #[test]
1114 fn test_parse_cpu_count_correct() {
1115 let app = create_app!();
1116 let args = vec![
1117 "nitro-cli",
1118 "run-enclave",
1119 "--cpu-count",
1120 "2",
1121 "--eif-path",
1122 "non_existing_eif.eif",
1123 "--memory",
1124 "64",
1125 ];
1126 let matches = app.try_get_matches_from(args);
1127 assert!(matches.is_ok());
1128
1129 let result = parse_cpu_count(
1130 matches
1131 .as_ref()
1132 .unwrap()
1133 .subcommand_matches("run-enclave")
1134 .unwrap(),
1135 );
1136 assert!(result.is_ok());
1137
1138 if let Some(parsed_cpu_count) = result.unwrap() {
1139 assert_eq!(parsed_cpu_count, 2);
1140 }
1141 }
1142
1143 #[test]
1144 fn test_parse_cpu_count_str() {
1145 let app = create_app!();
1146 let args = vec![
1147 "nitro-cli",
1148 "run-enclave",
1149 "--cpu-count",
1150 "2n",
1151 "--eif-path",
1152 "non_existing_eif.eif",
1153 "--memory",
1154 "64",
1155 ];
1156 let matches = app.try_get_matches_from(args);
1157 assert!(matches.is_ok());
1158
1159 let result = parse_cpu_count(
1160 matches
1161 .as_ref()
1162 .unwrap()
1163 .subcommand_matches("run-enclave")
1164 .unwrap(),
1165 );
1166 assert!(result.is_err());
1167 if let Err(err_info) = result {
1168 let err_str = construct_error_message(&err_info);
1169 assert!(err_str.contains("Invalid argument provided"));
1170 }
1171 }
1172
1173 #[test]
1174 fn test_parse_output() {
1175 let app = create_app!();
1176 let args = vec![
1177 "nitro-cli",
1178 "build-enclave",
1179 "--docker-uri",
1180 "mytag",
1181 "--docker-dir",
1182 "/home/user/non_existing_dir",
1183 "--output-file",
1184 "sample_eif.eif",
1185 ];
1186 let matches = app.try_get_matches_from(args);
1187 assert!(matches.is_ok());
1188
1189 let result = parse_output(
1190 matches
1191 .as_ref()
1192 .unwrap()
1193 .subcommand_matches("build-enclave")
1194 .unwrap(),
1195 );
1196 assert!(result.is_some());
1197 assert_eq!(result.unwrap(), "sample_eif.eif");
1198 }
1199
1200 #[test]
1201 fn test_parse_output_not_supplied() {
1202 let app = create_app!();
1203 let args = vec![
1204 "nitro-cli",
1205 "build-enclave",
1206 "--docker-uri",
1207 "mytag",
1208 "--docker-dir",
1209 "/home/user/non_existing_dir",
1210 ];
1211 let matches = app.try_get_matches_from(args);
1212 assert!(matches.is_err());
1214 }
1215
1216 #[test]
1217 fn test_debug_mode_supplied() {
1218 let app = create_app!();
1219 let args = vec![
1220 "nitro-cli",
1221 "run-enclave",
1222 "--memory",
1223 "64",
1224 "--cpu-count",
1225 "2",
1226 "--eif-path",
1227 "non_existing_eif.eif",
1228 "--debug-mode",
1229 ];
1230 let matches = app.try_get_matches_from(args);
1231 assert!(matches.is_ok());
1232
1233 let result = debug_mode(
1234 matches
1235 .as_ref()
1236 .unwrap()
1237 .subcommand_matches("run-enclave")
1238 .unwrap(),
1239 );
1240 assert!(result);
1241 }
1242
1243 #[test]
1244 fn test_debug_mode_not_supplied() {
1245 let app = create_app!();
1246 let args = vec![
1247 "nitro-cli",
1248 "run-enclave",
1249 "--memory",
1250 "64",
1251 "--cpu-count",
1252 "2",
1253 "--eif-path",
1254 "non_existing_eif.eif",
1255 ];
1256 let matches = app.try_get_matches_from(args);
1257 assert!(matches.is_ok());
1258
1259 let result = debug_mode(
1260 matches
1261 .as_ref()
1262 .unwrap()
1263 .subcommand_matches("run-enclave")
1264 .unwrap(),
1265 );
1266 assert!(!result);
1267 }
1268
1269 #[test]
1270 fn test_attach_console_supplied() {
1271 let app = create_app!();
1272 let args = vec![
1273 "nitro-cli",
1274 "run-enclave",
1275 "--attach-console",
1276 "--memory",
1277 "64",
1278 "--cpu-count",
1279 "2",
1280 "--eif-path",
1281 "non_existing_eif.eif",
1282 ];
1283 let matches = app.try_get_matches_from(args);
1284 assert!(matches.is_ok());
1285
1286 let matches = matches
1287 .as_ref()
1288 .unwrap()
1289 .subcommand_matches("run-enclave")
1290 .unwrap();
1291 let attach_console = attach_console(matches);
1292 let debug_mode = debug_mode(matches);
1293 assert!(attach_console);
1294 assert!(debug_mode);
1295 }
1296
1297 #[test]
1298 fn test_parse_json_config() {
1299 let app = create_app!();
1300 let args = vec![
1301 "nitro-cli",
1302 "run-enclave",
1303 "--config",
1304 "non_existing_config.json",
1305 ];
1306 let matches = app.try_get_matches_from(args);
1307 assert!(matches.is_ok());
1308
1309 let result = parse_config_file(
1310 matches
1311 .as_ref()
1312 .unwrap()
1313 .subcommand_matches("run-enclave")
1314 .unwrap(),
1315 );
1316 assert!(result.is_ok());
1317 assert_eq!(result.unwrap(), "non_existing_config.json");
1318 }
1319}