221 lines
7.6 KiB
Rust
221 lines
7.6 KiB
Rust
//! 只在测试自己的目录中,用 dev Pod 已安装的绝对路径 Go 工具制作签名 fixture。
|
|
|
|
use super::*;
|
|
use ring::signature::{Ed25519KeyPair, KeyPair};
|
|
use std::{
|
|
fs,
|
|
os::unix::fs::{DirBuilderExt, PermissionsExt},
|
|
path::{Path, PathBuf},
|
|
process::Command,
|
|
};
|
|
|
|
pub struct Fixture {
|
|
pub root: PathBuf,
|
|
pub signed: SignedResources,
|
|
pub files: BTreeMap<String, Vec<u8>>,
|
|
pub descriptor: serde_json::Value,
|
|
}
|
|
|
|
pub fn write(root: &Path, path: &str, bytes: impl AsRef<[u8]>) {
|
|
let path = root.join(path);
|
|
fs::create_dir_all(path.parent().unwrap()).unwrap();
|
|
for directory in path
|
|
.parent()
|
|
.unwrap()
|
|
.ancestors()
|
|
.take_while(|path| path.starts_with(root))
|
|
{
|
|
fs::set_permissions(directory, fs::Permissions::from_mode(0o700)).unwrap();
|
|
}
|
|
fs::write(&path, bytes).unwrap();
|
|
fs::set_permissions(path, fs::Permissions::from_mode(0o600)).unwrap();
|
|
}
|
|
|
|
fn go(root: &Path, arguments: &[&str]) -> String {
|
|
let result = Command::new("/usr/local/go/bin/go")
|
|
.args(arguments)
|
|
.current_dir(root)
|
|
.env_clear()
|
|
.env("GOTOOLCHAIN", "local")
|
|
.env("GOPROXY", "off")
|
|
.env("GOSUMDB", "off")
|
|
.env("CGO_ENABLED", "0")
|
|
.env("GO111MODULE", "off")
|
|
.env("GOCACHE", root.join("go-cache"))
|
|
.output()
|
|
.unwrap();
|
|
assert!(
|
|
result.status.success(),
|
|
"{}",
|
|
String::from_utf8_lossy(&result.stderr)
|
|
);
|
|
String::from_utf8(result.stdout).unwrap()
|
|
}
|
|
|
|
impl Fixture {
|
|
pub fn new() -> Self {
|
|
let root = std::env::temp_dir().join(format!(
|
|
"dever-go-acceptance-{}-{}",
|
|
std::process::id(),
|
|
std::time::SystemTime::now()
|
|
.duration_since(std::time::UNIX_EPOCH)
|
|
.unwrap()
|
|
.as_nanos()
|
|
));
|
|
fs::DirBuilder::new().mode(0o700).create(&root).unwrap();
|
|
let version = go(&root, &["version"]);
|
|
let target = BuildTarget::host().unwrap();
|
|
let arch = if target == BuildTarget::LinuxX86_64 {
|
|
"amd64"
|
|
} else {
|
|
"arm64"
|
|
};
|
|
let version_number = version
|
|
.split_whitespace()
|
|
.nth(2)
|
|
.unwrap()
|
|
.strip_prefix("go")
|
|
.unwrap();
|
|
write(&root, "selector.go", include_bytes!("selector.go"));
|
|
go(
|
|
&root,
|
|
&["build", "-trimpath", "-o", "selector", "selector.go"],
|
|
);
|
|
let mut files = BTreeMap::new();
|
|
files.insert(
|
|
"bin/analyze".into(),
|
|
fs::read(root.join("selector")).unwrap(),
|
|
);
|
|
for tool in ["compile", "link"] {
|
|
files.insert(
|
|
format!("bin/{tool}"),
|
|
fs::read(format!("/usr/local/go/pkg/tool/linux_{arch}/{tool}")).unwrap(),
|
|
);
|
|
}
|
|
let exports = go(
|
|
&root,
|
|
&[
|
|
"list",
|
|
"-deps",
|
|
"-export",
|
|
"-f",
|
|
"{{if .Export}}{{.ImportPath}}={{.Export}}{{end}}",
|
|
"bytes",
|
|
"context",
|
|
"encoding/binary",
|
|
"encoding/json",
|
|
"errors",
|
|
"fmt",
|
|
"io",
|
|
"math",
|
|
"os",
|
|
"regexp",
|
|
"sort",
|
|
"strconv",
|
|
"strings",
|
|
"time",
|
|
"unicode/utf8",
|
|
"embed",
|
|
],
|
|
);
|
|
let mut cfg = String::new();
|
|
for line in exports.lines().filter(|line| !line.is_empty()) {
|
|
let (name, source) = line.split_once('=').unwrap();
|
|
let path = format!("stdlib/{name}.a");
|
|
cfg.push_str(&format!("packagefile {name}={path}\n"));
|
|
files.insert(path, fs::read(source).unwrap());
|
|
}
|
|
files.insert("stdlib/importcfg".into(), cfg.into_bytes());
|
|
let descriptor = serde_json::json!({"host":target.name(), "compiler":"bin/compile", "linker":"bin/link", "analyzer":"bin/analyze",
|
|
"stdlib_importcfg":"stdlib/importcfg", "goos":"linux", "goarch":arch, "go_version":version.trim()});
|
|
files.insert("dever-runtime.json".into(), serde_json::to_vec(&serde_json::json!({"format":"dever-worker-runtime-v1", "ecosystem":"go", "target":target.name(),
|
|
"executable":null, "arguments":[], "build":descriptor, "python_markers":null, "python_wheel_tags":[]})).unwrap());
|
|
let archive = archive(&files);
|
|
let metadata = serde_json::to_vec(&serde_json::json!({"format":"dever-registry-runtime-v1", "runtime":{"pack":{"name":"go", "version":version_number,
|
|
"sha256":crate::compiler::project::dependency::digest(&archive)}, "target":target.name(), "python_markers":null, "python_wheel_tags":[]}})).unwrap();
|
|
let loader = if arch == "amd64" {
|
|
"ld-linux-x86-64.so.2"
|
|
} else {
|
|
"ld-linux-aarch64.so.1"
|
|
};
|
|
let loader_source = if arch == "amd64" {
|
|
"/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2"
|
|
} else {
|
|
"/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1"
|
|
};
|
|
let artifacts = BTreeMap::from([
|
|
(
|
|
format!("runtime/go/{}/runtime.pack", target.name()),
|
|
archive,
|
|
),
|
|
(
|
|
format!("runtime/go/{}/manifest.json", target.name()),
|
|
metadata,
|
|
),
|
|
(
|
|
format!("process-runtime/{}/lib/{loader}", target.name()),
|
|
fs::read(loader_source).unwrap(),
|
|
),
|
|
(
|
|
"sdk/go/component.go".into(),
|
|
fs::read(Path::new(env!("CARGO_MANIFEST_DIR")).join("../sdk/go/component.go"))
|
|
.unwrap(),
|
|
),
|
|
]);
|
|
let signed = sign(&root.join("installation"), &artifacts);
|
|
Self {
|
|
root,
|
|
signed,
|
|
files,
|
|
descriptor,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn archive(files: &BTreeMap<String, Vec<u8>>) -> Vec<u8> {
|
|
let gzip = flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::fast());
|
|
let mut tar = tar::Builder::new(gzip);
|
|
for (name, bytes) in files {
|
|
let mut header = tar::Header::new_gnu();
|
|
header.set_size(bytes.len() as u64);
|
|
header.set_mode(0o644);
|
|
header.set_cksum();
|
|
tar.append_data(&mut header, name, bytes.as_slice())
|
|
.unwrap();
|
|
}
|
|
tar.into_inner().unwrap().finish().unwrap()
|
|
}
|
|
|
|
pub fn sign(root: &Path, artifacts: &BTreeMap<String, Vec<u8>>) -> SignedResources {
|
|
let key = Ed25519KeyPair::from_seed_unchecked(&[93; 32]).unwrap();
|
|
let hex = |bytes: &[u8]| {
|
|
bytes
|
|
.iter()
|
|
.map(|byte| format!("{byte:02x}"))
|
|
.collect::<String>()
|
|
};
|
|
write(
|
|
root,
|
|
"state/trusted-release-key",
|
|
hex(key.public_key().as_ref()),
|
|
);
|
|
let entries = artifacts.iter().map(|(path, bytes)| {
|
|
write(root, &format!("versions/0.0.1/{path}"), bytes);
|
|
serde_json::json!({"path":path, "bytes":bytes.len(), "sha256":crate::compiler::project::dependency::digest(bytes)})
|
|
}).collect::<Vec<_>>();
|
|
let manifest = serde_json::to_vec(&serde_json::json!({"format":"dever-release-v2", "version":"0.0.1", "platform":BuildTarget::host().unwrap().name(), "artifacts":entries, "extensions":[]})).unwrap();
|
|
write(root, "versions/0.0.1/manifest.json", &manifest);
|
|
write(
|
|
root,
|
|
"versions/0.0.1/manifest.sig",
|
|
hex(key.sign(&manifest).as_ref()),
|
|
);
|
|
SignedResources::load(root, "0.0.1").unwrap()
|
|
}
|
|
|
|
impl Drop for Fixture {
|
|
fn drop(&mut self) {
|
|
fs::remove_dir_all(&self.root).unwrap();
|
|
}
|
|
}
|