use crate::errors::DaggerError; use crate::querybuilder::Selection; use dagger_core::graphql_client::DynGraphQLClient; use derive_builder::Builder; use serde::{Deserialize, Serialize}; use std::sync::Arc; use tokio::process::Child; #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] pub struct CacheId(pub String); impl Into for &str { fn into(self) -> CacheId { CacheId(self.to_string()) } } impl Into for String { fn into(self) -> CacheId { CacheId(self.clone()) } } #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] pub struct ContainerId(pub String); impl Into for &str { fn into(self) -> ContainerId { ContainerId(self.to_string()) } } impl Into for String { fn into(self) -> ContainerId { ContainerId(self.clone()) } } #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] pub struct DirectoryId(pub String); impl Into for &str { fn into(self) -> DirectoryId { DirectoryId(self.to_string()) } } impl Into for String { fn into(self) -> DirectoryId { DirectoryId(self.clone()) } } #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] pub struct FileId(pub String); impl Into for &str { fn into(self) -> FileId { FileId(self.to_string()) } } impl Into for String { fn into(self) -> FileId { FileId(self.clone()) } } #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] pub struct Platform(pub String); impl Into for &str { fn into(self) -> Platform { Platform(self.to_string()) } } impl Into for String { fn into(self) -> Platform { Platform(self.clone()) } } #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] pub struct SecretId(pub String); impl Into for &str { fn into(self) -> SecretId { SecretId(self.to_string()) } } impl Into for String { fn into(self) -> SecretId { SecretId(self.clone()) } } #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] pub struct SocketId(pub String); impl Into for &str { fn into(self) -> SocketId { SocketId(self.to_string()) } } impl Into for String { fn into(self) -> SocketId { SocketId(self.clone()) } } #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] pub struct BuildArg { pub name: String, pub value: String, } #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] pub struct PipelineLabel { pub name: String, pub value: String, } #[derive(Clone)] pub struct CacheVolume { pub proc: Option>, pub selection: Selection, pub graphql_client: DynGraphQLClient, } impl CacheVolume { pub async fn id(&self) -> Result { let query = self.selection.select("id"); query.execute(self.graphql_client.clone()).await } } #[derive(Clone)] pub struct Container { pub proc: Option>, pub selection: Selection, pub graphql_client: DynGraphQLClient, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerBuildOpts<'a> { /// Additional build arguments. #[builder(setter(into, strip_option), default)] pub build_args: Option>, /// Path to the Dockerfile to use. /// Default: './Dockerfile'. #[builder(setter(into, strip_option), default)] pub dockerfile: Option<&'a str>, /// Secrets to pass to the build. /// They will be mounted at /run/secrets/[secret-name]. #[builder(setter(into, strip_option), default)] pub secrets: Option>, /// Target build stage to build. #[builder(setter(into, strip_option), default)] pub target: Option<&'a str>, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerEndpointOpts<'a> { /// The exposed port number for the endpoint #[builder(setter(into, strip_option), default)] pub port: Option, /// Return a URL with the given scheme, eg. http for http:// #[builder(setter(into, strip_option), default)] pub scheme: Option<&'a str>, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerExecOpts<'a> { /// Command to run instead of the container's default command (e.g., ["run", "main.go"]). #[builder(setter(into, strip_option), default)] pub args: Option>, /// Provide dagger access to the executed command. /// Do not use this option unless you trust the command being executed. /// The command being executed WILL BE GRANTED FULL ACCESS TO YOUR HOST FILESYSTEM. #[builder(setter(into, strip_option), default)] pub experimental_privileged_nesting: Option, /// Redirect the command's standard error to a file in the container (e.g., "/tmp/stderr"). #[builder(setter(into, strip_option), default)] pub redirect_stderr: Option<&'a str>, /// Redirect the command's standard output to a file in the container (e.g., "/tmp/stdout"). #[builder(setter(into, strip_option), default)] pub redirect_stdout: Option<&'a str>, /// Content to write to the command's standard input before closing (e.g., "Hello world"). #[builder(setter(into, strip_option), default)] pub stdin: Option<&'a str>, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerExportOpts { /// Identifiers for other platform specific containers. /// Used for multi-platform image. #[builder(setter(into, strip_option), default)] pub platform_variants: Option>, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerImportOpts<'a> { /// Identifies the tag to import from the archive, if the archive bundles /// multiple tags. #[builder(setter(into, strip_option), default)] pub tag: Option<&'a str>, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerPipelineOpts<'a> { /// Pipeline description. #[builder(setter(into, strip_option), default)] pub description: Option<&'a str>, /// Pipeline labels. #[builder(setter(into, strip_option), default)] pub labels: Option>, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerPublishOpts { /// Identifiers for other platform specific containers. /// Used for multi-platform image. #[builder(setter(into, strip_option), default)] pub platform_variants: Option>, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerWithDefaultArgsOpts<'a> { /// Arguments to prepend to future executions (e.g., ["-v", "--no-cache"]). #[builder(setter(into, strip_option), default)] pub args: Option>, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerWithDirectoryOpts<'a> { /// Patterns to exclude in the written directory (e.g., ["node_modules/**", ".gitignore", ".git/"]). #[builder(setter(into, strip_option), default)] pub exclude: Option>, /// Patterns to include in the written directory (e.g., ["*.go", "go.mod", "go.sum"]). #[builder(setter(into, strip_option), default)] pub include: Option>, /// A user:group to set for the directory and its contents. /// The user and group can either be an ID (1000:1000) or a name (foo:bar). /// If the group is omitted, it defaults to the same as the user. #[builder(setter(into, strip_option), default)] pub owner: Option<&'a str>, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerWithExecOpts<'a> { /// Provides dagger access to the executed command. /// Do not use this option unless you trust the command being executed. /// The command being executed WILL BE GRANTED FULL ACCESS TO YOUR HOST FILESYSTEM. #[builder(setter(into, strip_option), default)] pub experimental_privileged_nesting: Option, /// Execute the command with all root capabilities. This is similar to running a command /// with "sudo" or executing `docker run` with the `--privileged` flag. Containerization /// does not provide any security guarantees when using this option. It should only be used /// when absolutely necessary and only with trusted commands. #[builder(setter(into, strip_option), default)] pub insecure_root_capabilities: Option, /// Redirect the command's standard error to a file in the container (e.g., "/tmp/stderr"). #[builder(setter(into, strip_option), default)] pub redirect_stderr: Option<&'a str>, /// Redirect the command's standard output to a file in the container (e.g., "/tmp/stdout"). #[builder(setter(into, strip_option), default)] pub redirect_stdout: Option<&'a str>, /// If the container has an entrypoint, ignore it for args rather than using it to wrap them. #[builder(setter(into, strip_option), default)] pub skip_entrypoint: Option, /// Content to write to the command's standard input before closing (e.g., "Hello world"). #[builder(setter(into, strip_option), default)] pub stdin: Option<&'a str>, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerWithExposedPortOpts<'a> { /// Optional port description #[builder(setter(into, strip_option), default)] pub description: Option<&'a str>, /// Transport layer network protocol #[builder(setter(into, strip_option), default)] pub protocol: Option, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerWithFileOpts<'a> { /// A user:group to set for the file. /// The user and group can either be an ID (1000:1000) or a name (foo:bar). /// If the group is omitted, it defaults to the same as the user. #[builder(setter(into, strip_option), default)] pub owner: Option<&'a str>, /// Permission given to the copied file (e.g., 0600). /// Default: 0644. #[builder(setter(into, strip_option), default)] pub permissions: Option, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerWithMountedCacheOpts<'a> { /// A user:group to set for the mounted cache directory. /// Note that this changes the ownership of the specified mount along with the /// initial filesystem provided by source (if any). It does not have any effect /// if/when the cache has already been created. /// The user and group can either be an ID (1000:1000) or a name (foo:bar). /// If the group is omitted, it defaults to the same as the user. #[builder(setter(into, strip_option), default)] pub owner: Option<&'a str>, /// Sharing mode of the cache volume. #[builder(setter(into, strip_option), default)] pub sharing: Option, /// Identifier of the directory to use as the cache volume's root. #[builder(setter(into, strip_option), default)] pub source: Option, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerWithMountedDirectoryOpts<'a> { /// A user:group to set for the mounted directory and its contents. /// The user and group can either be an ID (1000:1000) or a name (foo:bar). /// If the group is omitted, it defaults to the same as the user. #[builder(setter(into, strip_option), default)] pub owner: Option<&'a str>, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerWithMountedFileOpts<'a> { /// A user or user:group to set for the mounted file. /// The user and group can either be an ID (1000:1000) or a name (foo:bar). /// If the group is omitted, it defaults to the same as the user. #[builder(setter(into, strip_option), default)] pub owner: Option<&'a str>, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerWithMountedSecretOpts<'a> { /// A user:group to set for the mounted secret. /// The user and group can either be an ID (1000:1000) or a name (foo:bar). /// If the group is omitted, it defaults to the same as the user. #[builder(setter(into, strip_option), default)] pub owner: Option<&'a str>, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerWithNewFileOpts<'a> { /// Content of the file to write (e.g., "Hello world!"). #[builder(setter(into, strip_option), default)] pub contents: Option<&'a str>, /// A user:group to set for the file. /// The user and group can either be an ID (1000:1000) or a name (foo:bar). /// If the group is omitted, it defaults to the same as the user. #[builder(setter(into, strip_option), default)] pub owner: Option<&'a str>, /// Permission given to the written file (e.g., 0600). /// Default: 0644. #[builder(setter(into, strip_option), default)] pub permissions: Option, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerWithUnixSocketOpts<'a> { /// A user:group to set for the mounted socket. /// The user and group can either be an ID (1000:1000) or a name (foo:bar). /// If the group is omitted, it defaults to the same as the user. #[builder(setter(into, strip_option), default)] pub owner: Option<&'a str>, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerWithoutExposedPortOpts { /// Port protocol to unexpose #[builder(setter(into, strip_option), default)] pub protocol: Option, } impl Container { /// Initializes this container from a Dockerfile build. /// /// # Arguments /// /// * `context` - Directory context used by the Dockerfile. /// * `opt` - optional argument, see inner type for documentation, use _opts to use pub fn build(&self, context: DirectoryId) -> Container { let mut query = self.selection.select("build"); query = query.arg("context", context); return Container { proc: self.proc.clone(), selection: query, graphql_client: self.graphql_client.clone(), }; } /// Initializes this container from a Dockerfile build. /// /// # Arguments /// /// * `context` - Directory context used by the Dockerfile. /// * `opt` - optional argument, see inner type for documentation, use _opts to use pub fn build_opts<'a>(&self, context: DirectoryId, opts: ContainerBuildOpts<'a>) -> Container { let mut query = self.selection.select("build"); query = query.arg("context", context); if let Some(dockerfile) = opts.dockerfile { query = query.arg("dockerfile", dockerfile); } if let Some(build_args) = opts.build_args { query = query.arg("buildArgs", build_args); } if let Some(target) = opts.target { query = query.arg("target", target); } if let Some(secrets) = opts.secrets { query = query.arg("secrets", secrets); } return Container { proc: self.proc.clone(), selection: query, graphql_client: self.graphql_client.clone(), }; } /// Retrieves default arguments for future commands. pub async fn default_args(&self) -> Result, DaggerError> { let query = self.selection.select("defaultArgs"); query.execute(self.graphql_client.clone()).await } /// Retrieves a directory at the given path. /// Mounts are included. /// /// # Arguments /// /// * `path` - The path of the directory to retrieve (e.g., "./src"). pub fn directory(&self, path: impl Into) -> Directory { let mut query = self.selection.select("directory"); query = query.arg("path", path.into()); return Directory { proc: self.proc.clone(), selection: query, graphql_client: self.graphql_client.clone(), }; } /// Retrieves an endpoint that clients can use to reach this container. /// If no port is specified, the first exposed port is used. If none exist an error is returned. /// If a scheme is specified, a URL is returned. Otherwise, a host:port pair is returned. /// Currently experimental; set _EXPERIMENTAL_DAGGER_SERVICES_DNS=0 to disable. /// /// # Arguments /// /// * `opt` - optional argument, see inner type for documentation, use _opts to use pub async fn endpoint(&self) -> Result { let query = self.selection.select("endpoint"); query.execute(self.graphql_client.clone()).await } /// Retrieves an endpoint that clients can use to reach this container. /// If no port is specified, the first exposed port is used. If none exist an error is returned. /// If a scheme is specified, a URL is returned. Otherwise, a host:port pair is returned. /// Currently experimental; set _EXPERIMENTAL_DAGGER_SERVICES_DNS=0 to disable. /// /// # Arguments /// /// * `opt` - optional argument, see inner type for documentation, use _opts to use pub async fn endpoint_opts<'a>( &self, opts: ContainerEndpointOpts<'a>, ) -> Result { let mut query = self.selection.select("endpoint"); if let Some(port) = opts.port { query = query.arg("port", port); } if let Some(scheme) = opts.scheme { query = query.arg("scheme", scheme); } query.execute(self.graphql_client.clone()).await } /// Retrieves entrypoint to be prepended to the arguments of all commands. pub async fn entrypoint(&self) -> Result, DaggerError> { let query = self.selection.select("entrypoint"); query.execute(self.graphql_client.clone()).await } /// Retrieves the value of the specified environment variable. /// /// # Arguments /// /// * `name` - The name of the environment variable to retrieve (e.g., "PATH"). pub async fn env_variable(&self, name: impl Into) -> Result { let mut query = self.selection.select("envVariable"); query = query.arg("name", name.into()); query.execute(self.graphql_client.clone()).await } /// Retrieves the list of environment variables passed to commands. pub fn env_variables(&self) -> Vec { let query = self.selection.select("envVariables"); return vec![EnvVariable { proc: self.proc.clone(), selection: query, graphql_client: self.graphql_client.clone(), }]; } /// Retrieves this container after executing the specified command inside it. /// /// # Arguments /// /// * `opt` - optional argument, see inner type for documentation, use _opts to use pub fn exec(&self) -> Container { let query = self.selection.select("exec"); return Container { proc: self.proc.clone(), selection: query, graphql_client: self.graphql_client.clone(), }; } /// Retrieves this container after executing the specified command inside it. /// /// # Arguments /// /// * `opt` - optional argument, see inner type for documentation, use _opts to use pub fn exec_opts<'a>(&self, opts: ContainerExecOpts<'a>) -> Container { let mut query = self.selection.select("exec"); if let Some(args) = opts.args { query = query.arg("args", args); } if let Some(stdin) = opts.stdin { query = query.arg("stdin", stdin); } if let Some(redirect_stdout) = opts.redirect_stdout { query = query.arg("redirectStdout", redirect_stdout); } if let Some(redirect_stderr) = opts.redirect_stderr { query = query.arg("redirectStderr", redirect_stderr); } if let Some(experimental_privileged_nesting) = opts.experimental_privileged_nesting { query = query.arg( "experimentalPrivilegedNesting", experimental_privileged_nesting, ); } return Container { proc: self.proc.clone(), selection: query, graphql_client: self.graphql_client.clone(), }; } /// Exit code of the last executed command. Zero means success. /// Will execute default command if none is set, or error if there's no default. pub async fn exit_code(&self) -> Result { let query = self.selection.select("exitCode"); query.execute(self.graphql_client.clone()).await } /// Writes the container as an OCI tarball to the destination file path on the host for the specified platform variants. /// Return true on success. /// It can also publishes platform variants. /// /// # Arguments /// /// * `path` - Host's destination path (e.g., "./tarball"). /// Path can be relative to the engine's workdir or absolute. /// * `opt` - optional argument, see inner type for documentation, use _opts to use pub async fn export(&self, path: impl Into) -> Result { let mut query = self.selection.select("export"); query = query.arg("path", path.into()); query.execute(self.graphql_client.clone()).await } /// Writes the container as an OCI tarball to the destination file path on the host for the specified platform variants. /// Return true on success. /// It can also publishes platform variants. /// /// # Arguments /// /// * `path` - Host's destination path (e.g., "./tarball"). /// Path can be relative to the engine's workdir or absolute. /// * `opt` - optional argument, see inner type for documentation, use _opts to use pub async fn export_opts( &self, path: impl Into, opts: ContainerExportOpts, ) -> Result { let mut query = self.selection.select("export"); query = query.arg("path", path.into()); if let Some(platform_variants) = opts.platform_variants { query = query.arg("platformVariants", platform_variants); } query.execute(self.graphql_client.clone()).await } /// Retrieves the list of exposed ports. /// Currently experimental; set _EXPERIMENTAL_DAGGER_SERVICES_DNS=0 to disable. pub fn exposed_ports(&self) -> Vec { let query = self.selection.select("exposedPorts"); return vec![Port { proc: self.proc.clone(), selection: query, graphql_client: self.graphql_client.clone(), }]; } /// Retrieves a file at the given path. /// Mounts are included. /// /// # Arguments /// /// * `path` - The path of the file to retrieve (e.g., "./README.md"). pub fn file(&self, path: impl Into) -> File { let mut query = self.selection.select("file"); query = query.arg("path", path.into()); return File { proc: self.proc.clone(), selection: query, graphql_client: self.graphql_client.clone(), }; } /// Initializes this container from a pulled base image. /// /// # Arguments /// /// * `address` - Image's address from its registry. /// /// Formatted as [host]/[user]/[repo]:[tag] (e.g., "docker.io/dagger/dagger:main"). pub fn from(&self, address: impl Into) -> Container { let mut query = self.selection.select("from"); query = query.arg("address", address.into()); return Container { proc: self.proc.clone(), selection: query, graphql_client: self.graphql_client.clone(), }; } /// Retrieves this container's root filesystem. Mounts are not included. pub fn fs(&self) -> Directory { let query = self.selection.select("fs"); return Directory { proc: self.proc.clone(), selection: query, graphql_client: self.graphql_client.clone(), }; } /// Retrieves a hostname which can be used by clients to reach this container. /// Currently experimental; set _EXPERIMENTAL_DAGGER_SERVICES_DNS=0 to disable. pub async fn hostname(&self) -> Result { let query = self.selection.select("hostname"); query.execute(self.graphql_client.clone()).await } /// A unique identifier for this container. pub async fn id(&self) -> Result { let query = self.selection.select("id"); query.execute(self.graphql_client.clone()).await } /// The unique image reference which can only be retrieved immediately after the 'Container.From' call. pub async fn image_ref(&self) -> Result { let query = self.selection.select("imageRef"); query.execute(self.graphql_client.clone()).await } /// Reads the container from an OCI tarball. /// NOTE: this involves unpacking the tarball to an OCI store on the host at /// $XDG_CACHE_DIR/dagger/oci. This directory can be removed whenever you like. /// /// # Arguments /// /// * `source` - File to read the container from. /// * `opt` - optional argument, see inner type for documentation, use _opts to use pub fn import(&self, source: FileId) -> Container { let mut query = self.selection.select("import"); query = query.arg("source", source); return Container { proc: self.proc.clone(), selection: query, graphql_client: self.graphql_client.clone(), }; } /// Reads the container from an OCI tarball. /// NOTE: this involves unpacking the tarball to an OCI store on the host at /// $XDG_CACHE_DIR/dagger/oci. This directory can be removed whenever you like. /// /// # Arguments /// /// * `source` - File to read the container from. /// * `opt` - optional argument, see inner type for documentation, use _opts to use pub fn import_opts<'a>(&self, source: FileId, opts: ContainerImportOpts<'a>) -> Container { let mut query = self.selection.select("import"); query = query.arg("source", source); if let Some(tag) = opts.tag { query = query.arg("tag", tag); } return Container { proc: self.proc.clone(), selection: query, graphql_client: self.graphql_client.clone(), }; } /// Retrieves the value of the specified label. pub async fn label(&self, name: impl Into) -> Result { let mut query = self.selection.select("label"); query = query.arg("name", name.into()); query.execute(self.graphql_client.clone()).await } /// Retrieves the list of labels passed to container. pub fn labels(&self) -> Vec