]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/import-dkr.c
sd-dhcp6-client: delay setting the DUID and don't fail constructor
[thirdparty/systemd.git] / src / import / import-dkr.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <curl/curl.h>
23 #include <sys/prctl.h>
24
25 #include "sd-daemon.h"
26 #include "json.h"
27 #include "strv.h"
28 #include "btrfs-util.h"
29 #include "utf8.h"
30 #include "mkdir.h"
31 #include "path-util.h"
32 #include "import-util.h"
33 #include "curl-util.h"
34 #include "aufs-util.h"
35 #include "import-job.h"
36 #include "import-common.h"
37 #include "import-dkr.h"
38
39 typedef enum DkrProgress {
40 DKR_SEARCHING,
41 DKR_RESOLVING,
42 DKR_METADATA,
43 DKR_DOWNLOADING,
44 DKR_COPYING,
45 } DkrProgress;
46
47 struct DkrImport {
48 sd_event *event;
49 CurlGlue *glue;
50
51 char *index_url;
52 char *image_root;
53
54 ImportJob *images_job;
55 ImportJob *tags_job;
56 ImportJob *ancestry_job;
57 ImportJob *json_job;
58 ImportJob *layer_job;
59
60 char *name;
61 char *tag;
62 char *id;
63
64 char *response_token;
65 char **response_registries;
66
67 char **ancestry;
68 unsigned n_ancestry;
69 unsigned current_ancestry;
70
71 DkrImportFinished on_finished;
72 void *userdata;
73
74 char *local;
75 bool force_local;
76 bool grow_machine_directory;
77
78 char *temp_path;
79 char *final_path;
80
81 pid_t tar_pid;
82 };
83
84 #define PROTOCOL_PREFIX "https://"
85
86 #define HEADER_TOKEN "X-Do" /* the HTTP header for the auth token */ "cker-Token:"
87 #define HEADER_REGISTRY "X-Do" /*the HTTP header for the registry */ "cker-Endpoints:"
88
89 #define LAYERS_MAX 2048
90
91 static void dkr_import_job_on_finished(ImportJob *j);
92
93 DkrImport* dkr_import_unref(DkrImport *i) {
94 if (!i)
95 return NULL;
96
97 if (i->tar_pid > 1) {
98 (void) kill_and_sigcont(i->tar_pid, SIGKILL);
99 (void) wait_for_terminate(i->tar_pid, NULL);
100 }
101
102 import_job_unref(i->images_job);
103 import_job_unref(i->tags_job);
104 import_job_unref(i->ancestry_job);
105 import_job_unref(i->json_job);
106 import_job_unref(i->layer_job);
107
108 curl_glue_unref(i->glue);
109 sd_event_unref(i->event);
110
111 if (i->temp_path) {
112 (void) btrfs_subvol_remove(i->temp_path);
113 (void) rm_rf_dangerous(i->temp_path, false, true, false);
114 free(i->temp_path);
115 }
116
117 free(i->name);
118 free(i->tag);
119 free(i->id);
120 free(i->response_token);
121 free(i->response_registries);
122 strv_free(i->ancestry);
123 free(i->final_path);
124 free(i->index_url);
125 free(i->image_root);
126 free(i->local);
127 free(i);
128
129 return NULL;
130 }
131
132 int dkr_import_new(
133 DkrImport **ret,
134 sd_event *event,
135 const char *index_url,
136 const char *image_root,
137 DkrImportFinished on_finished,
138 void *userdata) {
139
140 _cleanup_(dkr_import_unrefp) DkrImport *i = NULL;
141 char *e;
142 int r;
143
144 assert(ret);
145 assert(index_url);
146
147 if (!http_url_is_valid(index_url))
148 return -EINVAL;
149
150 i = new0(DkrImport, 1);
151 if (!i)
152 return -ENOMEM;
153
154 i->on_finished = on_finished;
155 i->userdata = userdata;
156
157 i->image_root = strdup(image_root ?: "/var/lib/machines");
158 if (!i->image_root)
159 return -ENOMEM;
160
161 i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
162
163 i->index_url = strdup(index_url);
164 if (!i->index_url)
165 return -ENOMEM;
166
167 e = endswith(i->index_url, "/");
168 if (e)
169 *e = 0;
170
171 if (event)
172 i->event = sd_event_ref(event);
173 else {
174 r = sd_event_default(&i->event);
175 if (r < 0)
176 return r;
177 }
178
179 r = curl_glue_new(&i->glue, i->event);
180 if (r < 0)
181 return r;
182
183 i->glue->on_finished = import_job_curl_on_finished;
184 i->glue->userdata = i;
185
186 *ret = i;
187 i = NULL;
188
189 return 0;
190 }
191
192 static void dkr_import_report_progress(DkrImport *i, DkrProgress p) {
193 unsigned percent;
194
195 assert(i);
196
197 switch (p) {
198
199 case DKR_SEARCHING:
200 percent = 0;
201 if (i->images_job)
202 percent += i->images_job->progress_percent * 5 / 100;
203 break;
204
205 case DKR_RESOLVING:
206 percent = 5;
207 if (i->tags_job)
208 percent += i->tags_job->progress_percent * 5 / 100;
209 break;
210
211 case DKR_METADATA:
212 percent = 10;
213 if (i->ancestry_job)
214 percent += i->ancestry_job->progress_percent * 5 / 100;
215 if (i->json_job)
216 percent += i->json_job->progress_percent * 5 / 100;
217 break;
218
219 case DKR_DOWNLOADING:
220 percent = 20;
221 percent += 75 * i->current_ancestry / MAX(1U, i->n_ancestry);
222 if (i->layer_job)
223 percent += i->layer_job->progress_percent * 75 / MAX(1U, i->n_ancestry) / 100;
224
225 break;
226
227 case DKR_COPYING:
228 percent = 95;
229 break;
230
231 default:
232 assert_not_reached("Unknown progress state");
233 }
234
235 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
236 log_debug("Combined progress %u%%", percent);
237 }
238
239 static int parse_id(const void *payload, size_t size, char **ret) {
240 _cleanup_free_ char *buf = NULL, *id = NULL, *other = NULL;
241 union json_value v = {};
242 void *json_state = NULL;
243 const char *p;
244 int t;
245
246 assert(payload);
247 assert(ret);
248
249 if (size <= 0)
250 return -EBADMSG;
251
252 if (memchr(payload, 0, size))
253 return -EBADMSG;
254
255 buf = strndup(payload, size);
256 if (!buf)
257 return -ENOMEM;
258
259 p = buf;
260 t = json_tokenize(&p, &id, &v, &json_state, NULL);
261 if (t < 0)
262 return t;
263 if (t != JSON_STRING)
264 return -EBADMSG;
265
266 t = json_tokenize(&p, &other, &v, &json_state, NULL);
267 if (t < 0)
268 return t;
269 if (t != JSON_END)
270 return -EBADMSG;
271
272 if (!dkr_id_is_valid(id))
273 return -EBADMSG;
274
275 *ret = id;
276 id = NULL;
277
278 return 0;
279 }
280
281 static int parse_ancestry(const void *payload, size_t size, char ***ret) {
282 _cleanup_free_ char *buf = NULL;
283 void *json_state = NULL;
284 const char *p;
285 enum {
286 STATE_BEGIN,
287 STATE_ITEM,
288 STATE_COMMA,
289 STATE_END,
290 } state = STATE_BEGIN;
291 _cleanup_strv_free_ char **l = NULL;
292 size_t n = 0, allocated = 0;
293
294 if (size <= 0)
295 return -EBADMSG;
296
297 if (memchr(payload, 0, size))
298 return -EBADMSG;
299
300 buf = strndup(payload, size);
301 if (!buf)
302 return -ENOMEM;
303
304 p = buf;
305 for (;;) {
306 _cleanup_free_ char *str;
307 union json_value v = {};
308 int t;
309
310 t = json_tokenize(&p, &str, &v, &json_state, NULL);
311 if (t < 0)
312 return t;
313
314 switch (state) {
315
316 case STATE_BEGIN:
317 if (t == JSON_ARRAY_OPEN)
318 state = STATE_ITEM;
319 else
320 return -EBADMSG;
321
322 break;
323
324 case STATE_ITEM:
325 if (t == JSON_STRING) {
326 if (!dkr_id_is_valid(str))
327 return -EBADMSG;
328
329 if (n+1 > LAYERS_MAX)
330 return -EFBIG;
331
332 if (!GREEDY_REALLOC(l, allocated, n + 2))
333 return -ENOMEM;
334
335 l[n++] = str;
336 str = NULL;
337 l[n] = NULL;
338
339 state = STATE_COMMA;
340
341 } else if (t == JSON_ARRAY_CLOSE)
342 state = STATE_END;
343 else
344 return -EBADMSG;
345
346 break;
347
348 case STATE_COMMA:
349 if (t == JSON_COMMA)
350 state = STATE_ITEM;
351 else if (t == JSON_ARRAY_CLOSE)
352 state = STATE_END;
353 else
354 return -EBADMSG;
355 break;
356
357 case STATE_END:
358 if (t == JSON_END) {
359
360 if (strv_isempty(l))
361 return -EBADMSG;
362
363 if (!strv_is_uniq(l))
364 return -EBADMSG;
365
366 l = strv_reverse(l);
367
368 *ret = l;
369 l = NULL;
370 return 0;
371 } else
372 return -EBADMSG;
373 }
374
375 }
376 }
377
378 static const char *dkr_import_current_layer(DkrImport *i) {
379 assert(i);
380
381 if (strv_isempty(i->ancestry))
382 return NULL;
383
384 return i->ancestry[i->current_ancestry];
385 }
386
387 static const char *dkr_import_current_base_layer(DkrImport *i) {
388 assert(i);
389
390 if (strv_isempty(i->ancestry))
391 return NULL;
392
393 if (i->current_ancestry <= 0)
394 return NULL;
395
396 return i->ancestry[i->current_ancestry-1];
397 }
398
399 static int dkr_import_add_token(DkrImport *i, ImportJob *j) {
400 const char *t;
401
402 assert(i);
403 assert(j);
404
405 if (i->response_token)
406 t = strjoina("Authorization: Token ", i->response_token);
407 else
408 t = HEADER_TOKEN " true";
409
410 j->request_header = curl_slist_new("Accept: application/json", t, NULL);
411 if (!j->request_header)
412 return -ENOMEM;
413
414 return 0;
415 }
416
417 static bool dkr_import_is_done(DkrImport *i) {
418 assert(i);
419 assert(i->images_job);
420
421 if (i->images_job->state != IMPORT_JOB_DONE)
422 return false;
423
424 if (!i->tags_job || i->tags_job->state != IMPORT_JOB_DONE)
425 return false;
426
427 if (!i->ancestry_job || i->ancestry_job->state != IMPORT_JOB_DONE)
428 return false;
429
430 if (!i->json_job || i->json_job->state != IMPORT_JOB_DONE)
431 return false;
432
433 if (i->layer_job && i->layer_job->state != IMPORT_JOB_DONE)
434 return false;
435
436 if (dkr_import_current_layer(i))
437 return false;
438
439 return true;
440 }
441
442 static int dkr_import_make_local_copy(DkrImport *i) {
443 int r;
444
445 assert(i);
446
447 if (!i->local)
448 return 0;
449
450 if (!i->final_path) {
451 i->final_path = strjoin(i->image_root, "/.dkr-", i->id, NULL);
452 if (!i->final_path)
453 return log_oom();
454 }
455
456 r = import_make_local_copy(i->final_path, i->image_root, i->local, i->force_local);
457 if (r < 0)
458 return r;
459
460 return 0;
461 }
462
463 static int dkr_import_job_on_open_disk(ImportJob *j) {
464 const char *base;
465 DkrImport *i;
466 int r;
467
468 assert(j);
469 assert(j->userdata);
470
471 i = j->userdata;
472 assert(i->layer_job == j);
473 assert(i->final_path);
474 assert(!i->temp_path);
475 assert(i->tar_pid <= 0);
476
477 r = tempfn_random(i->final_path, &i->temp_path);
478 if (r < 0)
479 return log_oom();
480
481 mkdir_parents_label(i->temp_path, 0700);
482
483 base = dkr_import_current_base_layer(i);
484 if (base) {
485 const char *base_path;
486
487 base_path = strjoina(i->image_root, "/.dkr-", base);
488 r = btrfs_subvol_snapshot(base_path, i->temp_path, false, true);
489 } else
490 r = btrfs_subvol_make(i->temp_path);
491 if (r < 0)
492 return log_error_errno(r, "Failed to make btrfs subvolume %s: %m", i->temp_path);
493
494 j->disk_fd = import_fork_tar(i->temp_path, &i->tar_pid);
495 if (j->disk_fd < 0)
496 return j->disk_fd;
497
498 return 0;
499 }
500
501 static void dkr_import_job_on_progress(ImportJob *j) {
502 DkrImport *i;
503
504 assert(j);
505 assert(j->userdata);
506
507 i = j->userdata;
508
509 dkr_import_report_progress(
510 i,
511 j == i->images_job ? DKR_SEARCHING :
512 j == i->tags_job ? DKR_RESOLVING :
513 j == i->ancestry_job || j == i->json_job ? DKR_METADATA :
514 DKR_DOWNLOADING);
515 }
516
517 static int dkr_import_pull_layer(DkrImport *i) {
518 _cleanup_free_ char *path = NULL;
519 const char *url, *layer = NULL;
520 int r;
521
522 assert(i);
523 assert(!i->layer_job);
524 assert(!i->temp_path);
525 assert(!i->final_path);
526
527 for (;;) {
528 layer = dkr_import_current_layer(i);
529 if (!layer)
530 return 0; /* no more layers */
531
532 path = strjoin(i->image_root, "/.dkr-", layer, NULL);
533 if (!path)
534 return log_oom();
535
536 if (laccess(path, F_OK) < 0) {
537 if (errno == ENOENT)
538 break;
539
540 return log_error_errno(errno, "Failed to check for container: %m");
541 }
542
543 log_info("Layer %s already exists, skipping.", layer);
544
545 i->current_ancestry++;
546
547 free(path);
548 path = NULL;
549 }
550
551 log_info("Pulling layer %s...", layer);
552
553 i->final_path = path;
554 path = NULL;
555
556 url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", layer, "/layer");
557 r = import_job_new(&i->layer_job, url, i->glue, i);
558 if (r < 0)
559 return log_error_errno(r, "Failed to allocate layer job: %m");
560
561 r = dkr_import_add_token(i, i->layer_job);
562 if (r < 0)
563 return log_oom();
564
565 i->layer_job->on_finished = dkr_import_job_on_finished;
566 i->layer_job->on_open_disk = dkr_import_job_on_open_disk;
567 i->layer_job->on_progress = dkr_import_job_on_progress;
568 i->layer_job->grow_machine_directory = i->grow_machine_directory;
569
570 r = import_job_begin(i->layer_job);
571 if (r < 0)
572 return log_error_errno(r, "Failed to start layer job: %m");
573
574 return 0;
575 }
576
577 static void dkr_import_job_on_finished(ImportJob *j) {
578 DkrImport *i;
579 int r;
580
581 assert(j);
582 assert(j->userdata);
583
584 i = j->userdata;
585 if (j->error != 0) {
586 if (j == i->images_job)
587 log_error_errno(j->error, "Failed to retrieve images list. (Wrong index URL?)");
588 else if (j == i->tags_job)
589 log_error_errno(j->error, "Failed to retrieve tags list.");
590 else if (j == i->ancestry_job)
591 log_error_errno(j->error, "Failed to retrieve ancestry list.");
592 else if (j == i->json_job)
593 log_error_errno(j->error, "Failed to retrieve json data.");
594 else
595 log_error_errno(j->error, "Failed to retrieve layer data.");
596
597 r = j->error;
598 goto finish;
599 }
600
601 if (i->images_job == j) {
602 const char *url;
603
604 assert(!i->tags_job);
605 assert(!i->ancestry_job);
606 assert(!i->json_job);
607 assert(!i->layer_job);
608
609 if (strv_isempty(i->response_registries)) {
610 r = -EBADMSG;
611 log_error("Didn't get registry information.");
612 goto finish;
613 }
614
615 log_info("Index lookup succeeded, directed to registry %s.", i->response_registries[0]);
616 dkr_import_report_progress(i, DKR_RESOLVING);
617
618 url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/repositories/", i->name, "/tags/", i->tag);
619 r = import_job_new(&i->tags_job, url, i->glue, i);
620 if (r < 0) {
621 log_error_errno(r, "Failed to allocate tags job: %m");
622 goto finish;
623 }
624
625 r = dkr_import_add_token(i, i->tags_job);
626 if (r < 0) {
627 log_oom();
628 goto finish;
629 }
630
631 i->tags_job->on_finished = dkr_import_job_on_finished;
632 i->tags_job->on_progress = dkr_import_job_on_progress;
633
634 r = import_job_begin(i->tags_job);
635 if (r < 0) {
636 log_error_errno(r, "Failed to start tags job: %m");
637 goto finish;
638 }
639
640 } else if (i->tags_job == j) {
641 const char *url;
642 char *id = NULL;
643
644 assert(!i->ancestry_job);
645 assert(!i->json_job);
646 assert(!i->layer_job);
647
648 r = parse_id(j->payload, j->payload_size, &id);
649 if (r < 0) {
650 log_error_errno(r, "Failed to parse JSON id.");
651 goto finish;
652 }
653
654 free(i->id);
655 i->id = id;
656
657 log_info("Tag lookup succeeded, resolved to layer %s.", i->id);
658 dkr_import_report_progress(i, DKR_METADATA);
659
660 url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", i->id, "/ancestry");
661 r = import_job_new(&i->ancestry_job, url, i->glue, i);
662 if (r < 0) {
663 log_error_errno(r, "Failed to allocate ancestry job: %m");
664 goto finish;
665 }
666
667 r = dkr_import_add_token(i, i->ancestry_job);
668 if (r < 0) {
669 log_oom();
670 goto finish;
671 }
672
673 i->ancestry_job->on_finished = dkr_import_job_on_finished;
674 i->ancestry_job->on_progress = dkr_import_job_on_progress;
675
676 url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", i->id, "/json");
677 r = import_job_new(&i->json_job, url, i->glue, i);
678 if (r < 0) {
679 log_error_errno(r, "Failed to allocate json job: %m");
680 goto finish;
681 }
682
683 r = dkr_import_add_token(i, i->json_job);
684 if (r < 0) {
685 log_oom();
686 goto finish;
687 }
688
689 i->json_job->on_finished = dkr_import_job_on_finished;
690 i->json_job->on_progress = dkr_import_job_on_progress;
691
692 r = import_job_begin(i->ancestry_job);
693 if (r < 0) {
694 log_error_errno(r, "Failed to start ancestry job: %m");
695 goto finish;
696 }
697
698 r = import_job_begin(i->json_job);
699 if (r < 0) {
700 log_error_errno(r, "Failed to start json job: %m");
701 goto finish;
702 }
703
704 } else if (i->ancestry_job == j) {
705 char **ancestry = NULL, **k;
706 unsigned n;
707
708 assert(!i->layer_job);
709
710 r = parse_ancestry(j->payload, j->payload_size, &ancestry);
711 if (r < 0) {
712 log_error_errno(r, "Failed to parse JSON id.");
713 goto finish;
714 }
715
716 n = strv_length(ancestry);
717 if (n <= 0 || !streq(ancestry[n-1], i->id)) {
718 log_error("Ancestry doesn't end in main layer.");
719 strv_free(ancestry);
720 r = -EBADMSG;
721 goto finish;
722 }
723
724 log_info("Ancestor lookup succeeded, requires layers:\n");
725 STRV_FOREACH(k, ancestry)
726 log_info("\t%s", *k);
727
728 strv_free(i->ancestry);
729 i->ancestry = ancestry;
730 i->n_ancestry = n;
731 i->current_ancestry = 0;
732
733 dkr_import_report_progress(i, DKR_DOWNLOADING);
734
735 r = dkr_import_pull_layer(i);
736 if (r < 0)
737 goto finish;
738
739 } else if (i->layer_job == j) {
740 assert(i->temp_path);
741 assert(i->final_path);
742
743 j->disk_fd = safe_close(j->disk_fd);
744
745 if (i->tar_pid > 0) {
746 r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
747 i->tar_pid = 0;
748 if (r < 0)
749 goto finish;
750 }
751
752 r = aufs_resolve(i->temp_path);
753 if (r < 0) {
754 log_error_errno(r, "Failed to resolve aufs whiteouts: %m");
755 goto finish;
756 }
757
758 r = btrfs_subvol_set_read_only(i->temp_path, true);
759 if (r < 0) {
760 log_error_errno(r, "Failed to mark snapshot read-only: %m");
761 goto finish;
762 }
763
764 if (rename(i->temp_path, i->final_path) < 0) {
765 log_error_errno(errno, "Failed to rename snaphsot: %m");
766 goto finish;
767 }
768
769 log_info("Completed writing to layer %s.", i->final_path);
770
771 i->layer_job = import_job_unref(i->layer_job);
772 free(i->temp_path);
773 i->temp_path = NULL;
774 free(i->final_path);
775 i->final_path = NULL;
776
777 i->current_ancestry ++;
778 r = dkr_import_pull_layer(i);
779 if (r < 0)
780 goto finish;
781
782 } else if (i->json_job != j)
783 assert_not_reached("Got finished event for unknown curl object");
784
785 if (!dkr_import_is_done(i))
786 return;
787
788 dkr_import_report_progress(i, DKR_COPYING);
789
790 r = dkr_import_make_local_copy(i);
791 if (r < 0)
792 goto finish;
793
794 r = 0;
795
796 finish:
797 if (i->on_finished)
798 i->on_finished(i, r, i->userdata);
799 else
800 sd_event_exit(i->event, r);
801 }
802
803 static int dkr_import_job_on_header(ImportJob *j, const char *header, size_t sz) {
804 _cleanup_free_ char *registry = NULL;
805 char *token;
806 DkrImport *i;
807 int r;
808
809 assert(j);
810 assert(j->userdata);
811
812 i = j->userdata;
813
814 r = curl_header_strdup(header, sz, HEADER_TOKEN, &token);
815 if (r < 0)
816 return log_oom();
817 if (r > 0) {
818 free(i->response_token);
819 i->response_token = token;
820 return 0;
821 }
822
823 r = curl_header_strdup(header, sz, HEADER_REGISTRY, &registry);
824 if (r < 0)
825 return log_oom();
826 if (r > 0) {
827 char **l, **k;
828
829 l = strv_split(registry, ",");
830 if (!l)
831 return log_oom();
832
833 STRV_FOREACH(k, l) {
834 if (!hostname_is_valid(*k)) {
835 log_error("Registry hostname is not valid.");
836 strv_free(l);
837 return -EBADMSG;
838 }
839 }
840
841 strv_free(i->response_registries);
842 i->response_registries = l;
843 }
844
845 return 0;
846 }
847
848 int dkr_import_pull(DkrImport *i, const char *name, const char *tag, const char *local, bool force_local) {
849 const char *url;
850 int r;
851
852 assert(i);
853
854 if (!dkr_name_is_valid(name))
855 return -EINVAL;
856
857 if (tag && !dkr_tag_is_valid(tag))
858 return -EINVAL;
859
860 if (local && !machine_name_is_valid(local))
861 return -EINVAL;
862
863 if (i->images_job)
864 return -EBUSY;
865
866 if (!tag)
867 tag = "latest";
868
869 r = free_and_strdup(&i->local, local);
870 if (r < 0)
871 return r;
872 i->force_local = force_local;
873
874 r = free_and_strdup(&i->name, name);
875 if (r < 0)
876 return r;
877 r = free_and_strdup(&i->tag, tag);
878 if (r < 0)
879 return r;
880
881 url = strjoina(i->index_url, "/v1/repositories/", name, "/images");
882
883 r = import_job_new(&i->images_job, url, i->glue, i);
884 if (r < 0)
885 return r;
886
887 r = dkr_import_add_token(i, i->images_job);
888 if (r < 0)
889 return r;
890
891 i->images_job->on_finished = dkr_import_job_on_finished;
892 i->images_job->on_header = dkr_import_job_on_header;
893 i->images_job->on_progress = dkr_import_job_on_progress;
894
895 return import_job_begin(i->images_job);
896 }