You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

142 lines
4.8 KiB
Rust

use std::io::{self, Write};
use std::iter::once;
use buildkit_proto::pb::{self, Input};
use prost::Message;
use crate::serialization::{Context, Node, Result};
use crate::utils::OperationOutput;
/// Final operation in the graph. Responsible for printing the complete LLB definition.
#[derive(Debug)]
pub struct Terminal<'a> {
input: OperationOutput<'a>,
}
impl<'a> Terminal<'a> {
pub fn with(input: OperationOutput<'a>) -> Self {
Self { input }
}
pub fn into_definition(self) -> pb::Definition {
let mut cx = Context::default();
let final_node_iter = once(self.serialize(&mut cx).unwrap());
let (def, metadata) = {
cx.into_registered_nodes()
.chain(final_node_iter)
.map(|node| (node.bytes, (node.digest, node.metadata)))
.unzip()
};
pb::Definition { def, metadata }
}
pub fn write_definition(self, mut writer: impl Write) -> io::Result<()> {
let mut bytes = Vec::new();
self.into_definition().encode(&mut bytes).unwrap();
writer.write_all(&bytes)
}
fn serialize(&self, cx: &mut Context) -> Result<Node> {
let final_op = pb::Op {
inputs: vec![Input {
digest: cx.register(self.input.operation())?.digest.clone(),
index: self.input.output().into(),
}],
..Default::default()
};
Ok(Node::new(final_op, Default::default()))
}
}
#[test]
fn serialization() {
use crate::prelude::*;
let context = Source::local("context");
let builder_image = Source::image("rustlang/rust:nightly");
let final_image = Source::image("library/alpine:latest");
let first_command = Command::run("rustc")
.args(&["--crate-name", "crate-1"])
.mount(Mount::ReadOnlyLayer(builder_image.output(), "/"))
.mount(Mount::ReadOnlyLayer(context.output(), "/context"))
.mount(Mount::Scratch(OutputIdx(0), "/target"));
let second_command = Command::run("rustc")
.args(&["--crate-name", "crate-2"])
.mount(Mount::ReadOnlyLayer(builder_image.output(), "/"))
.mount(Mount::ReadOnlyLayer(context.output(), "/context"))
.mount(Mount::Scratch(OutputIdx(0), "/target"));
let assembly_op = FileSystem::sequence()
.append(FileSystem::mkdir(
OutputIdx(0),
LayerPath::Other(final_image.output(), "/output"),
))
.append(
FileSystem::copy()
.from(LayerPath::Other(first_command.output(0), "/target/crate-1"))
.to(
OutputIdx(1),
LayerPath::Own(OwnOutputIdx(0), "/output/crate-1"),
),
)
.append(
FileSystem::copy()
.from(LayerPath::Other(
second_command.output(0),
"/target/crate-2",
))
.to(
OutputIdx(2),
LayerPath::Own(OwnOutputIdx(1), "/output/crate-2"),
),
);
let definition = Terminal::with(assembly_op.output(0)).into_definition();
assert_eq!(
definition
.def
.iter()
.map(|bytes| Node::get_digest(&bytes))
.collect::<Vec<_>>(),
crate::utils::test::to_vec(vec![
"sha256:a60212791641cbeaa3a49de4f7dff9e40ae50ec19d1be9607232037c1db16702",
"sha256:dee2a3d7dd482dd8098ba543ff1dcb01efd29fcd16fdb0979ef556f38564543a",
"sha256:0e6b31ceed3e6dc542018f35a53a0e857e6a188453d32a2a5bbe7aa2971c1220",
"sha256:782f343f8f4ee33e4f342ed4209ad1a9eb4582485e45251595a5211ebf2b3cbf",
"sha256:3418ad515958b5e68fd45c9d6fbc8d2ce7d567a956150d22ff529a3fea401aa2",
"sha256:13bb644e4ec0cabe836392649a04551686e69613b1ea9c89a1a8f3bc86181791",
"sha256:d13a773a61236be3c7d539f3ef6d583095c32d2a2a60deda86e71705f2dbc99b",
])
);
let mut metadata_digests = {
definition
.metadata
.iter()
.map(|(digest, _)| digest.as_str())
.collect::<Vec<_>>()
};
metadata_digests.sort();
assert_eq!(
metadata_digests,
vec![
"sha256:0e6b31ceed3e6dc542018f35a53a0e857e6a188453d32a2a5bbe7aa2971c1220",
"sha256:13bb644e4ec0cabe836392649a04551686e69613b1ea9c89a1a8f3bc86181791",
"sha256:3418ad515958b5e68fd45c9d6fbc8d2ce7d567a956150d22ff529a3fea401aa2",
"sha256:782f343f8f4ee33e4f342ed4209ad1a9eb4582485e45251595a5211ebf2b3cbf",
"sha256:a60212791641cbeaa3a49de4f7dff9e40ae50ec19d1be9607232037c1db16702",
"sha256:d13a773a61236be3c7d539f3ef6d583095c32d2a2a60deda86e71705f2dbc99b",
"sha256:dee2a3d7dd482dd8098ba543ff1dcb01efd29fcd16fdb0979ef556f38564543a",
]
);
}