diff --git a/src/client/cli/cmd/launch.cpp b/src/client/cli/cmd/launch.cpp index d5f615d414..25f639a22b 100644 --- a/src/client/cli/cmd/launch.cpp +++ b/src/client/cli/cmd/launch.cpp @@ -378,36 +378,41 @@ mp::ParseCode cmd::Launch::parse_args(mp::ArgParser* parser) if (parser->isSet(cloudInitOption)) { + constexpr auto err_msg_template = "Could not load cloud-init configuration: {}"; try { + const QString& cloud_init_file = parser->value(cloudInitOption); YAML::Node node; - const QString& cloudInitFile = parser->value(cloudInitOption); - if (cloudInitFile == "-") + if (cloud_init_file == "-") { node = YAML::Load(term->read_all_cin()); } - else if (cloudInitFile.startsWith("http://") || cloudInitFile.startsWith("https://")) + else if (cloud_init_file.startsWith("http://") || cloud_init_file.startsWith("https://")) { URLDownloader downloader{std::chrono::minutes{1}}; - auto downloaded_yaml = downloader.download(QUrl(cloudInitFile)); + auto downloaded_yaml = downloader.download(QUrl(cloud_init_file)); node = YAML::Load(downloaded_yaml.toStdString()); } else { - auto file_type = fs::status(cloudInitFile.toStdString()).type(); + auto cloud_init_file_stdstr = cloud_init_file.toStdString(); + auto file_type = fs::status(cloud_init_file_stdstr).type(); if (file_type != fs::file_type::regular && file_type != fs::file_type::fifo) - { - cerr << "error: No such file: " << cloudInitFile.toStdString() << "\n"; - return ParseCode::CommandLineError; - } + throw YAML::BadFile{cloud_init_file_stdstr}; - node = YAML::LoadFile(cloudInitFile.toStdString()); + node = YAML::LoadFile(cloud_init_file_stdstr); } request.set_cloud_init_user_data(YAML::Dump(node)); } - catch (const std::exception& e) + catch (const YAML::BadFile& e) + { + auto err_detail = fmt::format("{}\n{}", e.what(), "Please ensure that Multipass can read it."); + fmt::println(cerr, err_msg_template, err_detail); + return ParseCode::CommandLineError; + } + catch (const YAML::Exception& e) { - cerr << "error loading cloud-init config: " << e.what() << "\n"; + fmt::println(cerr, err_msg_template, e.what()); return ParseCode::CommandLineError; } } diff --git a/tests/test_cli_client.cpp b/tests/test_cli_client.cpp index 76c7344d9f..3dbd9fe4ee 100644 --- a/tests/test_cli_client.cpp +++ b/tests/test_cli_client.cpp @@ -1097,17 +1097,25 @@ TEST_F(Client, launch_cmd_cloudinit_option_with_valid_file_is_ok) EXPECT_THAT(send_command({"launch", "--cloud-init", qPrintable(tmpfile.fileName())}), Eq(mp::ReturnCode::Ok)); } -TEST_F(Client, launch_cmd_cloudinit_option_fails_with_missing_file) +struct BadCloudInitFile : public Client, WithParamInterface +{ +}; + +TEST_P(BadCloudInitFile, launch_cmd_cloudinit_option_fails_with_missing_file) { std::stringstream cerr_stream; auto missing_file{"/definitely/missing-file"}; EXPECT_THAT(send_command({"launch", "--cloud-init", missing_file}, trash_stream, cerr_stream), Eq(mp::ReturnCode::CommandLineError)); - EXPECT_NE(std::string::npos, cerr_stream.str().find("No such file")) << "cerr has: " << cerr_stream.str(); + EXPECT_NE(std::string::npos, cerr_stream.str().find("Could not load")) << "cerr has: " << cerr_stream.str(); EXPECT_NE(std::string::npos, cerr_stream.str().find(missing_file)) << "cerr has: " << cerr_stream.str(); } +INSTANTIATE_TEST_SUITE_P(Client, + BadCloudInitFile, + Values("/definitely/missing-file", "/dev/null", "/home", "/root/.bashrc")); + TEST_F(Client, launch_cmd_cloudinit_option_fails_no_value) { EXPECT_THAT(send_command({"launch", "--cloud-init"}), Eq(mp::ReturnCode::CommandLineError));