-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdocker.R
732 lines (659 loc) · 24.4 KB
/
docker.R
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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
#' ORS Docker instance
#' @description
#' Creates a new ORS instance object that operates on a Docker instance of
#' OpenRouteService. This R6 class is typically constructed by
#' \code{\link{ors_instance}}. \code{ORSDocker} requires Docker to be
#' installed on the system and accessible by the current user. If these
#' requirements cannot be met, consider using \code{\link{ORSJar}} or
#' \code{\link{ORSWar}}.
#'
#' For technical details on the setup of local ORS instances, refer to the
#' \href{https://giscience.github.io/openrouteservice/run-instance/running-with-docker}{Running with Docker documentation}.
#' For details on how to use \code{ORSDocker} objects, refer to the installation
#' vignette:
#'
#' \preformatted{
#' vignette("ors-installation", package = "rors")
#' }
#'
#'
#' @details
#' \code{ORSDocker} defines methods for all four steps in the ORS setup:
#' \itemize{
#' \item{\bold{Extract:} Download an extract and set it up for graph building.}
#' \item{\bold{Compose:} Change docker settings that control how the service is set up.}
#' \item{\bold{Configuration:} Change the way ORS computes routes.}
#' \item{\bold{Docker:} Send commands to docker to control the ORS container.}
#' }
#'
#' @section Docker commands:
#'
#' Local ORS instances are built using Docker containers. Initializing
#' \code{ORSLocal} downloads a \code{docker-compose.yml} file that tells
#' Docker how to build an ORS container. The Docker backend is pulled as
#' a Docker image (see
#' \href{https://hub.docker.com/r/openrouteservice/openrouteservice}{Dockerhub}).
#' \code{rors} communicates with Docker from within R to setup and manage ORS
#' containers.
#'
#' On Unix systems, Docker requires superuser permissions. In Unix shells,
#' this is not problematic. However, R cannot communicate with Docker without
#' explicitly being granted superuser permissions or at least permissions to
#' access Docker. Thus, \code{ORSLocal} checks if the current user is the
#' superuser or if the current user has access to the Docker group. If not,
#' it aborts. For more details on how to manage Docker as a non-root user on
#' Linux, refer to the
#' \href{https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user}{Docker documentation}.
#' Note that this procedure grants root-level privileges to a user and can
#' negatively impact your system's security.
#'
#' @section Manual changes:
#'
#' \code{ORSLocal} provides a range of convenience methods to change the
#' setup of the ORS instance. All of these methods automatically read or write
#' their changes to disk. It is also possible to make direct changes to the
#' (parsed) configuration and compose files. In this case, reading and writing
#' also need to be done manually. To write changes to disk, run
#' \code{$update()}, e.g.:
#'
#' \preformatted{
#' ors$compose$parsed$services$`ors-app`$container_name <- "new-name-123"
#' ors$update()
#' }
#'
#' To read changes done manually to the files on disk, run
#' \code{$update("self")}.
#'
#' @section Configuration files:
#'
#' Contrary to what is written in the
#' \href{https://giscience.github.io/openrouteservice/run-instance/configuration/}{ORS documentation},
#' \code{ORSLocal} does not search for a pre-defined configuration file.
#' Instead, it explicitly creates a configuration file in the runtime directory.
#' Configurations are passed using an .env file but changes to the
#' configuration file are done using a .yml file due to easier reading and
#' writing within R. Accordingly, changes made to the .env file are
#' overwritten when `$update("fs")` is called and cannot be read using
#' `$update("self")`. All manual changes should be made to the .yml file.
#' Alternative configuration files can be specified by modifying the
#' \code{ORS_CONFIG_LOCATION} option in the compose file:
#'
#' \preformatted{
#' ors$compose$parsed$services$`ors-app`$environment$ORS_CONFIG_LOCATION <- "ors-config.yml"
#' ors$update()
#' }
#'
#' @export
#'
#' @examples
#' \dontrun{
#' # Download ORS, start docker and jumpstart a default session
#' ors <- ors_instance(tempdir())
#'
#' # Set a new extract file
#' ors$set_extract("Rutland")
#'
#' # Allocate 100 MB of RAM
#' ors$set_ram(0.1)
#'
#' # Add a routing profile
#' walking <- ors_profile("walking")
#' ors$add_profiles(walking)
#'
#' # Set random port
#' ors$set_port()
#'
#' # Change project name
#' ors$set_name("example-ors")
#'
#' # Set up ORS
#' ors$up()
#'
#' # Check if ORS container exists and is running
#' ors$is_built()
#' ors$is_running()
#'
#' # Check if ORS is ready to use
#' ors$is_ready()
#'
#' # Stop container, e.g. to make configuration changes
#' ors$stop()
#'
#' # Make changes to the configuration
#' ors$set_endpoints(matrix = list(maximum_routes = 1e+05)) # default is 2500
#'
#' # Change default profile settings
#' default <- ors_profile(maximum_snapping_radius = -1)
#' ors$add_profiles(default)
#'
#' # If we make manual changes to the configuration, we need to apply the
#' # changes explicitly
#' ors$config$parsed$ors$engine$profiles$car$elevation <- FALSE
#' ors$update() # writes the current object state to the disk
#'
#' # If the compose or config files are changed on disk, the object can be
#' # refreshed
#' ors$update("self") # reads the disk state to the R object
#'
#' # Adding profiles does not work when the container is still built!
#' # Why? Because graphs need to be built for new profiles, so the container
#' # must be down.
#' if (FALSE) {
#' bike <- ors_profile("bike-road")
#' ors$add_profiles(bike) # only do this when the container is down!!
#' }
#'
#' # Additionally, graphs are only re-built if we enable graph building.
#' # When changing the extract, this happens automatically, but we can also
#' # control graph building manually, e.g. for adding new profiles.
#' ors$set_graphbuilding(TRUE)
#'
#' # Finally, start the container again to run the newly configured service
#' ors$start()
#' }
ORSDocker <- R6Class(
classname = "ORSDocker",
inherit = ORSLocal,
# Public ----
public = list(
#' @field paths List of relevant file paths for the ORS setup. Includes
#' the top directory, compose file, config file, and extract file.
paths = list(),
#' @field version Version of the local ORS backend
version = NULL,
#' @field compose Information of the compose file (\code{docker-compose.yml}).
#' The compose file holds various settings for the Docker setup. The most
#' important settings are included in this field:
#'
#' \itemize{
#' \item{\code{ports}: A 2×2 matrix with Docker ports}
#' \item{\code{name}: Name of the Docker container}
#' \item{\code{memory}: List with memory information on total and free
#' system memory as well as initial and max memory allocated to the Docker
#' instance.}
#' \item{\code{image}: Version of the ORS image. \code{"latest"} refers
#' to the latest stable version. \code{"nightly"} refers to the devel
#' version.}
#' \item{\code{parsed}: Parsed compose file. When making changes to this
#' object, make sure to run \code{$update()} to apply the changes. For details,
#' refer to the \href{https://giscience.github.io/openrouteservice/run-instance/installation/running-with-docker#docker-configuration}{official reference}.}
#' }
compose = NULL,
#' @field config Information of the configuration file (\code{ors-config.yml}).
#' The config file holds various options about the ORS instance. This
#' field gives details about:
#'
#' \itemize{
#' \item{profiles: A named vector of active routing profiles}
#' \item{parsed: Parsed configuration file. When making changes to
#' this obhect, make sure to run \code{$update()} to apply the changes.
#' For details, refer to the
#' \href{https://giscience.github.io/openrouteservice/run-instance/configuration/}{ORS reference}.}
#' }
config = NULL,
#' @field extract Information on the extract file. Contains the name and
#' size of the selected extract file.
extract = NULL,
## Meta ----
#' @description
#' Initialize the ORSDocker object.
#'
#' @param dir \code{[character]}
#'
#' Custom OpenRouteService directory. If not specified, the compose file
#' will be downloaded to the current working directory. If a directory called
#' \code{"openrouteservice-{version}"} is present, the download will be skipped.
#' Ignored if \code{server} is not \code{NULL}.
#' @param version \code{[character]}
#'
#' The OpenRouteService version to use. Can either be a version number (e.g.
#' 8.1.1) or \code{"master"}. Defaults to the most recent supported version.
#' @param overwrite \code{[logical]}
#'
#' Whether to overwrite the current OpenRouteService directory if it exists.
#' @param dry \code{[logical]}
#'
#' Whether to start a dry instance, i.e. initialize a docker instance without
#' requiring docker. This allows you to manipulate config, compose file,
#' and extract but does not allow you to interact with docker.
#' @param verbose \code{[logical]}
#'
#' Level of verbosity. If \code{TRUE}, shows informative warnings and messages,
#' spinners, progress bars and system notifications.
#' @param prompts \code{[logical]}
#'
#' Whether to ask for permission throughout the setup. Defaults to
#' \code{TRUE} in interactive sessions.
#' @param ... Not used.
initialize = function(dir = ".",
version = NULL,
overwrite = FALSE,
dry = FALSE,
verbose = TRUE,
prompts = interactive(),
...) {
version <- version %||% ORS_VERSION
assert_that(assertthat::is.dir(dir), add = paste(
"The {.var dir} argument is expected to be a valid path to store",
"the OpenRouteService source code in."
))
assert_that(
assertthat::is.string(version),
is_true_or_false(overwrite),
is_true_or_false(dry),
is_string(version),
is_true_or_false(verbose),
is_true_or_false(prompts)
)
if (!dry) {
check_docker_installation()
check_docker_access()
start_docker(verbose = verbose)
docker_info(verbose = verbose)
}
dir <- get_ors_release(dir, version, file = "docker", overwrite, verbose)
check_ors_dir(dir, type = "docker")
private$.verbose <- verbose
private$.prompts <- prompts
private$.overwrite <- overwrite
self$version <- version
self$paths$top <- dir
private$.parse()
# create directories manually
create_ors_docker_dirs(dir)
# if possible, re-use version as image version
if (is_numver(version) || is_version_desc(version)) {
if (is_version_desc(version, "gh")) {
version <- "nightly"
}
version <- check_version(version)
self$compose$parsed <- set_compose_image(self$compose$parsed, version)
private$.write()
}
private$.mount()
invisible(self)
},
#' @description
#' Purge ORS instance, i.e. take down container, (optionally) delete
#' image, delete ORS directory, and clean up R6 class.
#'
#' This method can be useful for testing and writing reproducible
#' examples and can easily be used together with \code{\link{on.exit}}.
#'
#' @param image \code{[logical]}
#'
#' Whether to remove the docker image or keep it for other projects. The
#' default is \code{FALSE} to prevent accidentally breaking other projects.
purge = function(image = FALSE) {
if (!private$.alive) {
return(invisible(NULL))
}
if (private$.prompts) {
ors_cli(info = list(c("i" = paste(
"Purging the current instance removes the docker container,",
"ORS directory and cleans up the R6 object."
))))
yes_no("Do you want to proceed?", no = cancel())
}
ors_cli(h2 = "Purging ORS")
if (isTRUE(self$is_built())) self$down()
if (image) rm_image(self, private)
ors_cli(progress = list(
"step",
msg = "Removing ORS directory",
msg_done = "Removed ORS directory",
msg_failed = "Could not remove ORS directory"
))
unlink(self$paths$top, recursive = TRUE, force = TRUE)
ors_cli(progress = list(
"step",
msg = "Cleaning up R6 object",
msg_done = "Cleaned up R6 object",
msg_failed = "Could not clean up R6 object"
))
self$extract <- NULL
self$compose <- NULL
self$config <- NULL
self$paths <- NULL
private$.alive <- FALSE
invisible(NULL)
},
## Compose ----
#' @description
#' Set a name for the ORS container.
#'
#' @param name Name for the ORS container. If \code{NULL}, generates
#' a random name (\code{"ors-appXXXX"}).
set_name = function(name = NULL) {
assert_that(is_string(name, null = TRUE))
old <- self$compose$parsed$services$`ors-app`$container_name
new <- name %||% random_ors_name(private, name)
if (!identical(old, new)) {
ors_cli(info = list(c("*" = "Setting name to {.val {new}}")))
self$compose$parsed$services$`ors-app`$container_name <- new
self$compose$name <- new
self$update()
}
invisible(self)
},
#' @description
#' Set a port for the localhost of the ORS container.
#'
#' @param port \code{[numeric]}/\code{NULL}
#'
#' Port to use for the container. Can be a vector of length 1 or 2.
#' The first port is for the API, the second port is optionally for
#' additional monitoring. If \code{NULL}, assigns a random port using
#' \code{\link[httpuv:randomPort]{randomPort()}}.
set_port = function(port = NULL) {
assert_that(is_integerish(port, null = TRUE), length(port) <= 2)
new <- as.character(port %||% random_port(2))
old <- self$compose$ports[1, seq_along(new)]
if (!identical(old, new)) {
ors_cli(info = list(c(
"*" = "Setting {cli::qty(length(new))} port{?s} to {.val {new}}"
)))
compose <- self$compose$parsed
self$compose$parsed$services$`ors-app`$ports <- format_ports(self, new)
self$compose$ports[1, seq_along(new)] <- new
self$update()
}
invisible(self)
},
#' @description
#' Set initial and max memory that the ORS container is allowed to use.
#'
#' @param init \code{[numeric/NULL]}
#'
#' Initial memory. This can change if more memory is needed. If not
#' specified, uses \code{max}. If both are \code{NULL}, estimates
#' memory.
#' @param max \code{[numeric/NULL]}
#'
#' Maximum memory. The container is not allowed to use more
#' memory than this value. If not specified, uses \code{init}. If both are
#' \code{NULL}, estimates memory.
set_memory = function(init = NULL, max = NULL) {
assert_that(is_number(init, null = TRUE), is_number(max, null = TRUE))
old <- unlist(self$compose$memory[3:4], use.names = FALSE) * 1000
new <- adjust_memory(self, private, init, max)
if (!identical(old, new) && !is.null(new)) {
ors_cli(info = list(c("*" = "Setting memory to:")))
ors_cli(bullets = list(stats::setNames(
paste0(cli::style_bold(sprintf(
c("- init: {.field {%s}} GB", "- max: {.field {%s}} GB"),
new / 1000
))),
rep(" ", 2)
)))
self$compose$parsed$services$`ors-app`$environment <- format_memory(self, new)
self$compose$memory$init <- new[1] / 1000
self$compose$memory$max <- new[2] / 1000
self$update()
}
invisible(self)
},
#' @description
#' Graph building specifies whether routing graphs should be (re-)built.
#' Turning graph building on enables new profiles to be built or the
#' extract to be changed but significantly increases setup time. Turn
#' this off if you are changing configuration options that do not alter
#' routing graphs.
#'
#' @param mode \code{[logical]}
#'
#' Whether to turn graph building on or off.
set_graphbuilding = function(mode) {
assert_that(is_true_or_false(mode))
old <- self$compose$rebuild_graphs
new <- mode
if (!identical(old, new)) {
verb <- ifelse(mode, "Enabling", "Disabling")
ors_cli(info = list(c("*" = "{verb} graph building")))
self$compose$parsed$services$`ors-app`$environment[1] <- set_gp(self, mode)
self$compose$rebuild_graphs <- mode
self$update()
}
invisible(self)
},
#' @description
#' Set version of the ORS Docker image. This should preferably be compatible
#' with the compose version.
#'
#' @param version \code{[character]}
#'
#' Version specification of the ORS image.
set_image = function(version) {
assert_that(is_string(version))
old <- self$compose$image
new <- check_version(version) %||% old
if (!identical(old, new)) {
ors_cli(info = list(c("*" = "Setting image version to {.field {new}}")))
self$compose$parsed$services$`ors-app`$image <- paste0(
"openrouteservice/openrouteservice:v", new
)
self$compose$image <- new
self$update()
}
invisible(self)
},
## Docker ----
#' @description
#' Create the ORS docker container and setup the ORS backend on a local
#' host.
#'
#' @param wait \code{logical}
#'
#' Whether to run a spinner and show a system notification when the setup
#' is completed. If \code{FALSE} releases the console after the Docker
#' container is created. You can then check the service status using
#' \code{$is_ready()}.
#'
#' @param ... Additional flags passed to the \code{docker up} command.
#'
#' @details
#' The exact command run by \code{$up()} is:
#'
#' \code{docker compose -p [name] -f [compose file] up -d --no-build [...]}
#'
#' The \code{-p} flag allows docker to run multiple ORS containers and keep
#' them separate. It uses the service name defined in the compose file.
#'
#' If not found, \code{$up()} builds the underlying OpenRouteService docker
#' image specified by \code{version} during the initialization of
#' \code{ORSLocal}.
#'
#' Usually in detach mode (\code{-d}), docker returns terminal control back
#' to the user. By default, \code{$up()} blocks the console, checks for
#' errors and notifies the user when the service setup has finished. This
#' behavior can be suppresed by setting \code{wait = FALSE}. The service
#' status can then be checked using \code{$is_ready()} or
#' \code{\link[=ors_ready]{ors_ready()}}. Container logs can be accessed by typing
#' \code{docker logs [name]} in the terminal.
up = function(wait = TRUE, ...) {
ors_up(self, private, wait, ...)
self$set_graphbuilding(FALSE)
private$.mount()
invisible(self)
},
#' @description
#' Stop and remove the ORS docker container. Use this if you want to make
#' changes to a running ORS setup such as changing the extract or selected
#' profiles.
down = function() {
ors_down(self, private)
private$.mount()
invisible(self)
},
#' @description
#' Start the ORS docker container.
#'
#' @param wait \code{logical}
#'
#' Whether to run a spinner and show a system notification when the setup
#' is completed. If \code{FALSE} releases the console after the Docker
#' container is created. You can then check the service status using
#' \code{$is_ready()}.
start = function(wait = TRUE) {
ors_start(self, private, wait)
private$.mount()
invisible(self)
},
#' @description
#' Stop the ORS docker container.
stop = function() {
ors_stop(self, private)
private$.mount()
invisible(self)
},
#' @description
#' Retrieve technical information on the docker image used.
get_image = function() {
image <- self$get_container()$image
container_image(image)
},
#' @description
#' Retrieve technical information on the docker container used.
get_container = function() {
container_info(self$compose$name)
},
#' @description
#' Show container logs as returned by \code{docker logs}. Useful for
#' debugging docker setups.
#'
#' @param format \code{[logical]}
#'
#' If \code{TRUE}, includes ANSI colors and adds exdents. Otherwise,
#' trims ANSI colors. Disabling formatting increases performance, which
#' can be useful for larger logs.
show_logs = function(format = TRUE) {
logs <- docker_logs(self$compose$name)
if (format) {
logs <- cli::ansi_strwrap(logs, width = cli::console_width(), exdent = 2)
} else {
logs <- cli::ansi_strip(logs)
}
class(logs) <- "ors_logs"
logs
},
#' @description
#' Checks if the ORS container is built. You can control this state
#' using \code{$up()} and \code{$down()}.
is_built = function() {
container_built(self$compose$name)
},
#' @description
#' Checks if the ORS container is running. You can control this state
#' using \code{$start()} and \code{$stop()}. Check \code{$is_ready()} to see
#' if the ORS setup succeeded.
is_running = function() {
container_running(self$compose$name)
},
#' @description
#' Checks if ORS is initialized. ORS is initialized if it was built
#' for the first time. An initialized ORS instance has a subdirectory
#' called \code{"graphs"} that contains built graphs for at least one
#' routing profile. \code{$is_init()} therefore checks for the
#' existence of at least one sub-directory of \code{"graphs"}.
is_init = function() {
graphs <- file.path(self$paths$top, "graphs")
length(list.dirs(graphs, recursive = FALSE)) > 0
}
),
# Private ----
private = list(
.overwrite = FALSE,
.verbose = TRUE,
.prompts = TRUE,
.alive = TRUE,
.parse = function() {
self$paths <- private$.construct("paths")
self$compose <- private$.construct("compose")
self$extract <- private$.construct("extract")
self$config <- private$.construct("config")
}
)
)
start_docker <- function(verbose = TRUE) {
if (docker_running()) {
return(invisible(NULL))
}
if (is_windows()) {
ors_cli(info = list(c(
"i" = paste(
"Docker is required to be running in order to",
"start an OpenRouteService instance."
)
)))
docker_path <- Sys.which("docker")
docker_desktop <- file.path(
file_path_up(docker_path, 3L),
"Docker Desktop.exe"
)
status <- file.open(docker_desktop)
# If Docker is installed, it will try to open
if (status() == 0L || is.null(status())) {
ors_cli(progress = list(
"step",
msg = "Starting Docker...",
spinner = verbose,
msg_done = "Docker Desktop is now running.",
msg_failed = "The Docker startup has timed out."
))
# Check if Docker is usable by running a Docker command
proc <- callr::r_bg(
function(docker_running) {
while (!docker_running()) {
Sys.sleep(1L)
}
},
package = TRUE,
args = list(docker_running)
)
while (proc$is_alive()) {
ors_cli(progress = "update")
Sys.sleep(0.01)
difft <- difftime(Sys.time(), proc$get_start_time(), units = "secs")
if (difft > 180L) cli::cli_abort(
"Docker startup timed out.",
class = "ors_docker_timeout"
)
}
} else {
cli::cli_abort(
"Something went wrong while starting Docker. Is it installed?",
class = "ors_docker_corrupt_installation_error"
)
}
} else if (is_linux()) {
callr::run(
command = "systemctl",
args = c("start", "docker"),
stdout = NULL,
stderr = NULL
)
}
}
check_ors_dir <- function(dir, type) {
switch(
type,
docker = {
check <- file.exists(file.path(dir, "docker-compose.yml"))
if (!check) {
cli::cli_abort(c(
"!" = "OpenRouteService directory is corrupted",
"i" = "Compose file is missing."
), class = "ors_corrupted_dir_error")
}
},
jar = {
check <- file.exists(file.path(dir, "ors.jar"))
if (!check) {
cli::cli_abort(c(
"!" = "OpenRouteService directory is corrupted",
"i" = "JAR file is missing."
), class = "ors_corrupted_dir_error")
}
}
)
}