-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathcopy.cpp
More file actions
213 lines (188 loc) · 7.15 KB
/
copy.cpp
File metadata and controls
213 lines (188 loc) · 7.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// vim: ts=4 sts=4 sw=4 et
#include <filesystem>
#include <string>
#include <fmt/core.h>
#include <fmt/ranges.h>
#include <fmt/std.h>
#include <nlohmann/json.hpp>
#include <spdlog/spdlog.h>
#include <site/site.h>
#include <uenv/oras.h>
#include <uenv/parse.h>
#include <uenv/print.h>
#include <uenv/registry.h>
#include <uenv/repository.h>
#include <util/curl.h>
#include <util/expected.h>
#include <util/fs.h>
#include <util/signal.h>
#include "copy.h"
#include "help.h"
#include "terminal.h"
#include "util.h"
namespace uenv {
std::string image_copy_footer();
void image_copy_args::add_cli(CLI::App& cli,
[[maybe_unused]] global_settings& settings) {
auto* copy_cli =
cli.add_subcommand("copy", "copy a uenv inside a remote registry");
copy_cli
->add_option("source-uenv", src_uenv_description,
"either name/version:tag, sha256 or id")
->required();
copy_cli->add_option("dest-uenv", dst_uenv_description, "label to copy to")
->required();
copy_cli->add_option(
"--token", token,
"a path that contains a TOKEN file for accessing restricted uenv");
copy_cli->add_option("--username", username,
"user name for accessing restricted uenv.");
copy_cli->add_flag("--force", force,
"overwrite the destination if it exists");
copy_cli->callback(
[&settings]() { settings.mode = uenv::cli_mode::image_copy; });
copy_cli->footer(image_copy_footer);
}
int image_copy([[maybe_unused]] const image_copy_args& args,
[[maybe_unused]] const global_settings& settings) {
std::optional<uenv::oras::credentials> credentials;
if (auto c = site::get_credentials(args.username, args.token)) {
credentials = *c;
} else {
term::error("{}", c.error());
return 1;
}
uenv_nslabel src_label{};
if (const auto parse = parse_uenv_nslabel(args.src_uenv_description)) {
src_label = *parse;
} else {
term::error("invalid source: {}", parse.error().message());
return 1;
}
if (!src_label.nspace || !src_label.label.name) {
term::error("the source uenv {} must provide at least a namespace and "
"name, e.g. 'build::f7076704830c8de7'",
args.src_uenv_description);
return 1;
}
spdlog::debug("source label {}::{}", src_label.nspace, src_label.label);
uenv_nslabel dst_label{};
if (const auto parse = parse_uenv_nslabel(args.dst_uenv_description)) {
dst_label = *parse;
} else {
term::error("invalid destination: {}", parse.error().message());
return 1;
}
spdlog::debug("destination label {}::{}", dst_label.nspace,
dst_label.label);
if (!dst_label.nspace) {
term::error("the destination uenv {} must provide at least a "
"namespace and name, e.g. 'deploy::'",
args.dst_uenv_description);
return 1;
}
auto registry_backend = create_registry_from_config(settings.config);
if (!registry_backend) {
term::error("{}", registry_backend.error());
return 1;
}
auto src_registry = registry_backend->listing(*src_label.nspace);
if (!src_registry) {
term::error("unable to get a listing of the uenv: {}",
src_registry.error());
return 1;
}
// search db for matching records
const auto src_matches = src_registry->query(src_label.label);
if (!src_matches) {
term::error("invalid search term: {}", src_registry.error());
return 1;
}
// check that there is one record with a unique sha
if (src_matches->empty()) {
using enum help::block::admonition;
term::error("no uenv found that matches '{}'\n\n{}",
args.src_uenv_description,
help::block(info, "try searching for the uenv to copy "
"first using 'uenv image find'"));
return 1;
} else if (!src_matches->unique_sha()) {
std::string errmsg =
fmt::format("more than one uenv found that matches '{}':\n",
args.src_uenv_description);
errmsg += format_record_set(*src_matches);
term::error("{}", errmsg);
return 1;
}
// pick a record to use for pulling
const auto src_record = *(src_matches->begin());
spdlog::info("source record: {} {}", src_record.sha, src_record);
// create the destination record
auto dst_record = src_record;
{
const auto& dl = dst_label.label;
if (dl.name) {
dst_record.name = *(dl.name);
}
if (dl.tag) {
dst_record.tag = *(dl.tag);
}
if (dl.version) {
dst_record.version = *(dl.version);
}
if (dl.system) {
dst_record.system = *(dl.system);
}
if (dl.uarch) {
dst_record.uarch = *(dl.uarch);
}
}
if (dst_record == src_record) {
term::error("the source and destination are the same");
return 1;
}
spdlog::info("destination record: {} {}", dst_record.sha, dst_record);
// check whether the destination already exists
auto dst_registry = registry_backend->listing(*dst_label.nspace);
if (dst_registry && dst_registry->contains(dst_record)) {
if (!args.force) {
term::error("the destination already exists - use the --force flag "
"to copy anyway");
return 1;
}
term::error("the destination already exists and will be overwritten");
}
const auto rego_url = registry_backend->url();
spdlog::debug("registry url: {}", rego_url);
if (auto result =
oras::copy(rego_url, src_label.nspace.value(), src_record,
dst_label.nspace.value(), dst_record, credentials);
!result) {
term::error("unable to copy uenv.\n{}", result.error().message);
return 1;
}
term::msg("copied {}::{}", src_label.nspace.value(), src_record);
term::msg("to {}::{}", dst_label.nspace.value(), dst_record);
return 0;
}
std::string image_copy_footer() {
using enum help::block::admonition;
std::vector<help::item> items{
// clang-format off
help::block{none, "Copy a uenv to a new location inside a remote registry." },
help::linebreak{},
help::linebreak{},
help::block{xmpl, "deploy a uenv from build to deploy namespace"},
help::block{code, "uenv image copy prgenv-gnu/24.11:1551223269@todi%gh200 deploy:::v1"},
help::block{code, "uenv image copy 7890d67458ce7deb deploy:::v1"},
help::block{none, "when deploying a build, provide a tag."},
help::linebreak{},
help::block{xmpl, "redeploy a uenv to a new vcluster"},
help::block{code, "uenv image copy 7890d67458ce7deb deploy::@daint"},
help::block{code, "uenv image copy 7890d67458ce7deb @daint"},
help::block{none, "in this case, the uenv will deployed with the current tag."},
// clang-format on
};
return fmt::format("{}", fmt::join(items, "\n"));
}
} // namespace uenv