Skip to content

Commit c187114

Browse files
authored
allow adding image with sqfs file from internal store (#125)
1 parent f26e15d commit c187114

File tree

5 files changed

+148
-54
lines changed

5 files changed

+148
-54
lines changed

src/cli/add_remove.cpp

Lines changed: 91 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <fmt/std.h>
88
#include <spdlog/spdlog.h>
99

10+
#include <uenv/env.h>
1011
#include <uenv/parse.h>
1112
#include <uenv/print.h>
1213
#include <uenv/repository.h>
@@ -77,12 +78,33 @@ int image_add(const image_add_args& args, const global_settings& settings) {
7778

7879
spdlog::info("image_add: label {}", *label);
7980

80-
auto sqfs = uenv::validate_squashfs_image(args.squashfs);
81-
if (!sqfs) {
82-
term::error("invalid squashfs file {}: {}", args.squashfs,
83-
sqfs.error());
81+
const auto env = concretise_env(args.squashfs, {}, settings.config.repo,
82+
settings.calling_environment);
83+
if (!env) {
84+
term::error("{}", env.error());
8485
return 1;
8586
}
87+
if (env->uenvs.size() != 1) {
88+
term::error("Too many arguments provided for source squashfs file");
89+
return 1;
90+
}
91+
92+
auto concrete_uenv = env->uenvs.begin()->second;
93+
bool from_label = concrete_uenv.label.has_value();
94+
95+
util::expected<squashfs_image, std::string> sqfs;
96+
if (from_label) {
97+
sqfs.emplace(concrete_uenv.sqfs_path, concrete_uenv.meta_path,
98+
concrete_uenv.digest.value());
99+
} else {
100+
sqfs =
101+
uenv::validate_squashfs_image(env->uenvs.begin()->second.sqfs_path);
102+
if (!sqfs) {
103+
term::error("invalid squashfs file {}: {}", args.squashfs,
104+
sqfs.error());
105+
return 1;
106+
}
107+
}
86108
spdlog::info("image_add: squashfs {}", sqfs.value());
87109

88110
//
@@ -144,67 +166,82 @@ int image_add(const image_add_args& args, const global_settings& settings) {
144166
}
145167

146168
const auto uenv_paths = store->uenv_paths(sqfs->hash);
147-
148-
//
149-
// create the path inside the repo
150-
//
151-
std::error_code ec;
152-
// if the path exists, delete it, as it might contain a partial download
153-
if (fs::exists(uenv_paths.store)) {
154-
spdlog::debug("image_add: remove the target path {} before copying",
155-
uenv_paths.store.string());
156-
fs::remove_all(uenv_paths.store);
157-
}
158169
uenv::uenv_date date{*util::file_creation_date(sqfs->sqfs)};
159170

160-
fs::create_directories(uenv_paths.store, ec);
161-
if (ec) {
162-
spdlog::error("unable to create path {}: {}", uenv_paths.store.string(),
163-
ec.message());
164-
term::error("unable to add the uenv");
171+
bool source_in_repo =
172+
util::is_child(sqfs->sqfs, settings.config.repo.value());
173+
174+
// If an sqfs file is already in repo, and it was pulled from a repository
175+
// then there is a digest mismatch. Do not try to add this image
176+
// Such images should be retaged with the command:
177+
// uenv image add <new-label> <existing-label>
178+
if (source_in_repo && !existing_hash) {
179+
term::error("image_add: Trying to add a squashfs file which is already "
180+
"in the repository, but the hashes do not match");
165181
return 1;
166182
}
167183

168-
//
169-
// copy the meta data into the repo
170-
//
171-
if (sqfs->meta) {
172-
fs::copy_options options{};
173-
options |= fs::copy_options::recursive;
174-
fs::copy(sqfs->meta.value(), uenv_paths.meta, options, ec);
175-
if (ec) {
176-
spdlog::error("unable to copy meta data to {}: {}",
177-
uenv_paths.meta.string(), ec.message());
178-
term::error("unable to add the uenv");
179-
return 1;
184+
if (!source_in_repo) {
185+
//
186+
// create the path inside the repo
187+
//
188+
std::error_code ec;
189+
// if the path exists, delete it, as it might contain a partial download
190+
if (fs::exists(uenv_paths.store)) {
191+
spdlog::debug("image_add: remove the target path {} before copying",
192+
uenv_paths.store.string());
193+
fs::remove_all(uenv_paths.store);
180194
}
181-
}
182195

183-
// copy or move the
184-
if (!args.move) {
185-
fs::copy_file(sqfs->sqfs, uenv_paths.squashfs, ec);
196+
fs::create_directories(uenv_paths.store, ec);
186197
if (ec) {
187-
spdlog::error("unable to copy squashfs image {} to {}: {}",
188-
sqfs->sqfs.string(), uenv_paths.squashfs.string(),
189-
ec.message());
198+
spdlog::error("unable to create path {}: {}",
199+
uenv_paths.store.string(), ec.message());
190200
term::error("unable to add the uenv");
191201
return 1;
192202
}
193-
} else {
194-
fs::rename(sqfs->sqfs, uenv_paths.squashfs, ec);
195-
if (ec) {
196-
spdlog::error("unable to move squashfs image {} to {}: {}",
197-
sqfs->sqfs.string(), uenv_paths.squashfs.string(),
198-
ec.message());
199-
term::error(
200-
"unable to add the uenv\n{}",
201-
help::item{help::block{
202-
help::block::admonition::note,
203-
fmt::format(
204-
"check that the file {} is on the same filesystem as "
205-
"the repository, and that you have write access to it.",
206-
sqfs->sqfs.string())}});
207-
return 1;
203+
204+
//
205+
// copy the meta data into the repo
206+
//
207+
if (sqfs->meta) {
208+
fs::copy_options options{};
209+
options |= fs::copy_options::recursive;
210+
fs::copy(sqfs->meta.value(), uenv_paths.meta, options, ec);
211+
if (ec) {
212+
spdlog::error("unable to copy meta data to {}: {}",
213+
uenv_paths.meta.string(), ec.message());
214+
term::error("unable to add the uenv");
215+
return 1;
216+
}
217+
}
218+
219+
// copy or move the
220+
if (!args.move) {
221+
fs::copy_file(sqfs->sqfs, uenv_paths.squashfs, ec);
222+
if (ec) {
223+
spdlog::error("unable to copy squashfs image {} to {}: {}",
224+
sqfs->sqfs.string(), uenv_paths.squashfs.string(),
225+
ec.message());
226+
term::error("unable to add the uenv");
227+
return 1;
228+
}
229+
} else {
230+
fs::rename(sqfs->sqfs, uenv_paths.squashfs, ec);
231+
if (ec) {
232+
spdlog::error("unable to move squashfs image {} to {}: {}",
233+
sqfs->sqfs.string(), uenv_paths.squashfs.string(),
234+
ec.message());
235+
term::error("unable to add the uenv\n{}",
236+
help::item{help::block{
237+
help::block::admonition::note,
238+
fmt::format("check that the file {} is on the "
239+
"same filesystem as "
240+
"the repository, and that you have "
241+
"write access to it.",
242+
sqfs->sqfs.string())}});
243+
return 1;
244+
}
208245
}
209246
}
210247

src/util/fs.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,4 +246,10 @@ read_single_line_file(const std::filesystem::path& path) {
246246
return std::nullopt;
247247
}
248248

249+
bool is_child(const std::filesystem::path& child,
250+
const std::filesystem::path& parent) {
251+
auto rel = child.lexically_relative(parent);
252+
return !rel.empty() && *rel.begin() != "..";
253+
}
254+
249255
} // namespace util

src/util/fs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,8 @@ file_level file_access_level(const std::filesystem::path& path);
6363
std::optional<std::string>
6464
read_single_line_file(const std::filesystem::path& path);
6565

66+
// return if a path is inside a directory, i.e. direct or indirect child
67+
bool is_child(const std::filesystem::path& child,
68+
const std::filesystem::path& parent);
69+
6670
} // namespace util

test/integration/cli.bats

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,30 @@ EOF
372372
assert_line --partial 'quokka/24:v1'
373373
[ "${#lines[@]}" -eq "4" ]
374374

375+
# trying to add the same image with a different label pointing to an SQFS file inside the repo should generate a warning
376+
run uenv --repo=$RP image add wombat/24:replica@arapiles%zen3 $(uenv --repo=$RP image inspect --format='{sqfs}' wombat/24:v1@arapiles%zen3)
377+
assert_success
378+
assert_output --partial "warning"
379+
assert_output --partial "a uenv with the same sha"
380+
381+
run uenv --repo=$RP image ls --no-header
382+
assert_success
383+
assert_line --partial 'wombat/24:v1'
384+
assert_line --partial 'wombat/24:replica'
385+
[ "${#lines[@]}" -eq "5" ]
386+
387+
# trying to add the same image by label
388+
run uenv --repo=$RP image add numbat/24:replica@arapiles%zen3 numbat/24:v1
389+
assert_success
390+
assert_output --partial "warning"
391+
assert_output --partial "a uenv with the same sha"
392+
393+
run uenv --repo=$RP image ls --no-header
394+
assert_success
395+
assert_line --partial 'numbat/24:v1'
396+
assert_line --partial 'numbat/24:replica'
397+
[ "${#lines[@]}" -eq "6" ]
398+
375399
# TODO:
376400
# - check a read-only repo
377401
}

test/unit/fs.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,26 @@ TEST_CASE("read_single_line_file", "[fs]") {
119119
REQUIRE(r.value() == "hello world");
120120
}
121121
}
122+
123+
TEST_CASE("is_child", "[fs]") {
124+
// direct child
125+
{
126+
const std::filesystem::path child = "/path/to/child";
127+
const std::filesystem::path parent = "/path/to";
128+
REQUIRE(util::is_child(child, parent));
129+
}
130+
131+
// indirect child
132+
{
133+
const std::filesystem::path child = "/path/to/child";
134+
const std::filesystem::path parent = "/path";
135+
REQUIRE(util::is_child(child, parent));
136+
}
137+
138+
// not a child
139+
{
140+
const std::filesystem::path child = "/path/to/child";
141+
const std::filesystem::path parent = "/tmp";
142+
REQUIRE(!util::is_child(child, parent));
143+
}
144+
}

0 commit comments

Comments
 (0)