linera_service/cli_wrappers/
docker.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::path::PathBuf;
5
6use anyhow::{Context, Result};
7use linera_base::command::{current_binary_parent, CommandExt};
8use pathdiff::diff_paths;
9use tokio::process::Command;
10
11use crate::cli_wrappers::local_kubernetes_net::BuildMode;
12
13pub enum Dockerfile {
14    Main,
15    Indexer,
16    Explorer,
17}
18
19impl Dockerfile {
20    pub fn path(&self) -> &'static str {
21        match self {
22            Dockerfile::Main => "docker/Dockerfile",
23            Dockerfile::Indexer => "docker/Dockerfile.indexer",
24            Dockerfile::Explorer => "docker/Dockerfile.explorer",
25        }
26    }
27}
28
29pub struct DockerImage {
30    name: String,
31}
32
33impl DockerImage {
34    pub fn name(&self) -> &String {
35        &self.name
36    }
37
38    pub async fn build(
39        name: String,
40        binaries: BuildArg,
41        github_root: PathBuf,
42        build_mode: BuildMode,
43        dual_store: bool,
44        dockerfile: Dockerfile,
45    ) -> Result<Self> {
46        let docker_image = Self {
47            name: name.to_owned(),
48        };
49        let mut command = Command::new("docker");
50        command
51            .current_dir(github_root.clone())
52            .arg("build")
53            .args(["-f", dockerfile.path()]);
54
55        if let Dockerfile::Main = dockerfile {
56            let build_arg = match binaries {
57                BuildArg::Directory(bin_path) => {
58                    // Get the binaries from the specified path
59                    let bin_path = diff_paths(bin_path, github_root)
60                        .context("Getting relative path failed")?;
61                    let bin_path_str = bin_path.to_str().context("Getting str failed")?;
62                    format!("binaries={bin_path_str}")
63                }
64                BuildArg::ParentDirectory => {
65                    // Get the binaries from current_binary_parent
66                    let parent_path = current_binary_parent()
67                        .expect("Fetching current binaries path should not fail");
68                    let bin_path = diff_paths(parent_path, github_root)
69                        .context("Getting relative path failed")?;
70                    let bin_path_str = bin_path.to_str().context("Getting str failed")?;
71                    format!("binaries={bin_path_str}")
72                }
73                BuildArg::Build => {
74                    // Build inside the Docker container
75                    let arch = std::env::consts::ARCH;
76                    // Translate architecture for Docker build arg
77                    let docker_arch = match arch {
78                        "arm" => "aarch",
79                        _ => arch,
80                    };
81                    format!("target={}-unknown-linux-gnu", docker_arch)
82                }
83            };
84
85            command.args(["--build-arg", &build_arg]);
86
87            match build_mode {
88                // Release is the default, so no need to add any arguments
89                BuildMode::Release => {}
90                BuildMode::Debug => {
91                    command.args(["--build-arg", "build_folder=debug"]);
92                    command.args(["--build-arg", "build_flag="]);
93                }
94            }
95
96            if dual_store {
97                command.args(["--build-arg", "build_features=rocksdb,scylladb,metrics"]);
98            }
99
100            #[cfg(not(with_testing))]
101            command
102                .args([
103                    "--build-arg",
104                    &format!(
105                        "git_commit={}",
106                        linera_version::VersionInfo::get()?.git_commit
107                    ),
108                ])
109                .args([
110                    "--build-arg",
111                    &format!(
112                        "build_date={}",
113                        // Same format as $(TZ=UTC date)
114                        chrono::Utc::now().format("%a %b %d %T UTC %Y")
115                    ),
116                ]);
117        }
118
119        command
120            .arg(".")
121            .args(["-t", &name])
122            .spawn_and_wait()
123            .await?;
124        Ok(docker_image)
125    }
126}
127
128/// Which binaries to use in the Docker container.
129#[derive(Clone)]
130pub enum BuildArg {
131    /// Build the binaries within the container.
132    Build,
133    /// Look for the binaries in the parent directory of the current binary.
134    ParentDirectory,
135    /// Look for the binaries in the specified path.
136    Directory(PathBuf),
137}
138
139impl From<Option<Option<PathBuf>>> for BuildArg {
140    fn from(arg: Option<Option<PathBuf>>) -> Self {
141        match arg {
142            None => BuildArg::Build,
143            Some(None) => BuildArg::ParentDirectory,
144            Some(Some(path)) => BuildArg::Directory(path),
145        }
146    }
147}