diff --git a/garnet/go/src/amber/BUILD.gn b/garnet/go/src/amber/BUILD.gn index bcce302f8a687c27c8fdeb644873bb2b81c650e8..916a5f20aa7b785bd532b4bb42d0297d65b47689 100644 --- a/garnet/go/src/amber/BUILD.gn +++ b/garnet/go/src/amber/BUILD.gn @@ -36,7 +36,6 @@ go_library("lib") { "//garnet/public/lib/syslog/go/src/syslog", "//sdk/fidl/fuchsia.amber($go_toolchain)", "//sdk/fidl/fuchsia.pkg($go_toolchain)", - "//sdk/fidl/fuchsia.pkg.rewrite($go_toolchain)", "//sdk/fidl/fuchsia.sys($go_toolchain)", "//zircon/public/fidl/fuchsia-cobalt($go_toolchain)", "//zircon/public/fidl/fuchsia-mem($go_toolchain)", diff --git a/garnet/go/src/amber/amberctl/amberctl.go b/garnet/go/src/amber/amberctl/amberctl.go index 13981fd742caca3e3597e0c5bb3471559e66e094..7b301e927f309bb825518713af426d1e1300cc7b 100644 --- a/garnet/go/src/amber/amberctl/amberctl.go +++ b/garnet/go/src/amber/amberctl/amberctl.go @@ -28,7 +28,6 @@ import ( "fidl/fuchsia/amber" fuchsiaio "fidl/fuchsia/io" "fidl/fuchsia/pkg" - "fidl/fuchsia/pkg/rewrite" ) const usage = `usage: %s <command> [opts] @@ -102,185 +101,27 @@ func doTest(pxy *amber.ControlInterface) error { return nil } -type Services struct { - amber *amber.ControlInterface - resolver *pkg.PackageResolverInterface - repoMgr *pkg.RepositoryManagerInterface - rewriteEngine *rewrite.EngineInterface -} - -func connectToAmber(ctx *context.Context) *amber.ControlInterface { +func connectToAmber(ctx *context.Context) (*amber.ControlInterface, amber.ControlInterfaceRequest) { req, pxy, err := amber.NewControlInterfaceRequest() if err != nil { panic(err) } ctx.ConnectToEnvService(req) - return pxy + return pxy, req } -func connectToPackageResolver(ctx *context.Context) *pkg.PackageResolverInterface { +func connectToPackageResolver(ctx *context.Context) (*pkg.PackageResolverInterface, pkg.PackageResolverInterfaceRequest) { req, pxy, err := pkg.NewPackageResolverInterfaceRequest() if err != nil { panic(err) } ctx.ConnectToEnvService(req) - return pxy -} - -func connectToRepositoryManager(ctx *context.Context) *pkg.RepositoryManagerInterface { - req, pxy, err := pkg.NewRepositoryManagerInterfaceRequest() - if err != nil { - panic(err) - } - ctx.ConnectToEnvService(req) - return pxy -} - -func connectToRewriteEngine(ctx *context.Context) *rewrite.EngineInterface { - req, pxy, err := rewrite.NewEngineInterfaceRequest() - if err != nil { - panic(err) - } - ctx.ConnectToEnvService(req) - return pxy -} - -// upgradeSourceConfig attempts to upgrade an amber.SourceConfig into a pkg.RepositoryConfig -// -// The two config formats are incompatible in various ways: -// -// * repo configs cannot be disabled. amberctl will attempt to preserve a config's disabled bit by -// not configuring a rewrite rule for the source. -// -// * repo configs do not support oauth, network client config options, or polling frequency -// overrides. If present, these options are discarded. -// -// * repo config mirrors do not accept different URLs for the TUF repo and the blobs. Any custom -// blob URL is discarded. -func upgradeSourceConfig(cfg amber.SourceConfig) pkg.RepositoryConfig { - repoCfg := pkg.RepositoryConfig{ - RepoUrl: repoUrlForId(cfg.Id), - RepoUrlPresent: true, - } - - mirror := pkg.MirrorConfig{ - MirrorUrl: cfg.RepoUrl, - MirrorUrlPresent: true, - Subscribe: cfg.Auto, - SubscribePresent: true, - } - if cfg.BlobKey != nil { - var blobKey pkg.RepositoryBlobKey - blobKey.SetAesKey(cfg.BlobKey.Data[:]) - mirror.SetBlobKey(blobKey) - } - repoCfg.SetMirrors([]pkg.MirrorConfig{mirror}) - - for _, key := range cfg.RootKeys { - if key.Type != "ed25519" { - continue - } - - var rootKey pkg.RepositoryKeyConfig - bytes, err := hex.DecodeString(key.Value) - if err != nil { - continue - } - rootKey.SetEd25519Key(bytes) - - repoCfg.RootKeys = append(repoCfg.RootKeys, rootKey) - repoCfg.RootKeysPresent = true - } - - return repoCfg -} - -func repoUrlForId(id string) string { - return fmt.Sprintf("fuchsia-pkg://%s", id) -} - -func rewriteRuleForId(id string) rewrite.Rule { - var rule rewrite.Rule - rule.SetLiteral(rewrite.LiteralRule{ - HostMatch: "fuchsia.com", - HostReplacement: id, - PathPrefixMatch: "/", - PathPrefixReplacement: "/", - }) - return rule -} - -func replaceDynamicRewriteRules(rewriteEngine *rewrite.EngineInterface, rule rewrite.Rule) error { - return doRewriteRuleEditTransaction(rewriteEngine, func(transaction *rewrite.EditTransactionInterface) error { - if err := transaction.ResetAll(); err != nil { - return fmt.Errorf("fuchsia.pkg.rewrite.EditTransaction.ResetAll IPC encountered an error: %s", err) - } - - s, err := transaction.Add(rule) - if err != nil { - return fmt.Errorf("fuchsia.pkg.rewrite.EditTransaction.Add IPC encountered an error: %s", err) - } - status := zx.Status(s) - if status != zx.ErrOk { - return fmt.Errorf("unable to add rewrite rule: %s", status) - } - - return nil - }) -} - -func removeAllDynamicRewriteRules(rewriteEngine *rewrite.EngineInterface) error { - return doRewriteRuleEditTransaction(rewriteEngine, func(transaction *rewrite.EditTransactionInterface) error { - if err := transaction.ResetAll(); err != nil { - return fmt.Errorf("fuchsia.pkg.rewrite.EditTransaction.ResetAll IPC encountered an error: %s", err) - } - - return nil - }) + return pxy, req } -// doRewriteRuleEditTransaction executes a rewrite rule edit transaction using -// the provided callback, retrying on data races a few times before giving up. -func doRewriteRuleEditTransaction(rewriteEngine *rewrite.EngineInterface, cb func(*rewrite.EditTransactionInterface) error) error { - for i := 0; i < 10; i++ { - err, status := func() (error, zx.Status) { - var status zx.Status - req, transaction, err := rewrite.NewEditTransactionInterfaceRequest() - if err != nil { - return fmt.Errorf("creating edit transaction: %s", err), status - } - defer transaction.Close() - if err := rewriteEngine.StartEditTransaction(req); err != nil { - return fmt.Errorf("fuchsia.pkg.rewrite.Engine IPC encountered an error: %s", err), status - } - - if err := cb(transaction); err != nil { - return err, status - } - - s, err := transaction.Commit() - if err != nil { - return fmt.Errorf("fuchsia.pkg.rewrite.EditTransaction.Commit IPC encountered an error: %s", err), status - } - return nil, zx.Status(s) - }() - if err != nil { - return err - } - switch status { - case zx.ErrOk: - return nil - case zx.ErrUnavailable: - continue - default: - return fmt.Errorf("unexpected error while committing rewrite rule transaction: %s", status) - } - } - - return fmt.Errorf("unable to commit rewrite rule changes") -} +func addSource(a *amber.ControlInterface) error { + var cfg amber.SourceConfig -func addSource(services Services) error { if len(*pkgFile) == 0 { return fmt.Errorf("a url or file path (via -f) are required") } @@ -338,7 +179,6 @@ func addSource(services Services) error { source = f } - var cfg amber.SourceConfig if err := json.NewDecoder(source).Decode(&cfg); err != nil { return fmt.Errorf("failed to parse source config: %v", err) } @@ -366,38 +206,16 @@ func addSource(services Services) error { cfg.BlobRepoUrl = filepath.Join(cfg.RepoUrl, "blobs") } - added, err := services.amber.AddSrc(cfg) + added, err := a.AddSrc(cfg) if err != nil { - return fmt.Errorf("fuchsia.amber.Control IPC encountered an error: %s", err) + return fmt.Errorf("IPC encountered an error: %s", err) } if !added { return fmt.Errorf("request arguments properly formatted, but possibly otherwise invalid") } if isSourceConfigEnabled(&cfg) && !*nonExclusive { - if err := disableAllSources(services.amber, cfg.Id); err != nil { - return err - } - } - - repoCfg := upgradeSourceConfig(cfg) - s, err := services.repoMgr.Add(repoCfg) - if err != nil { - return fmt.Errorf("fuchsia.pkg.RepositoryManager IPC encountered an error: %s", err) - } - status := zx.Status(s) - if !(status == zx.ErrOk || status == zx.ErrAlreadyExists) { - return fmt.Errorf("unable to register source with RepositoryManager: %s", status) - } - - // Nothing currently registers sources in a disabled state, but make a best effort attempt - // to try to prevent the source from being used anyway by only configuring a mapping of - // fuchsia.com to this source if it is enabled. Note that this doesn't prevent resolving a - // package using this config's id explicitly or calling an amber source config - // "fuchsia.com". - if isSourceConfigEnabled(&cfg) { - rule := rewriteRuleForId(cfg.Id) - if err := replaceDynamicRewriteRules(services.rewriteEngine, rule); err != nil { + if err := disableAllSources(a, cfg.Id); err != nil { return err } } @@ -405,19 +223,19 @@ func addSource(services Services) error { return nil } -func rmSource(services Services) error { +func rmSource(a *amber.ControlInterface) error { name := strings.TrimSpace(*name) if name == "" { return fmt.Errorf("no source id provided") } - status, err := services.amber.RemoveSrc(name) + status, err := a.RemoveSrc(name) if err != nil { - return fmt.Errorf("fuchsia.amber.Control IPC encountered an error: %s", err) + return fmt.Errorf("IPC encountered an error: %s", err) } switch status { case amber.StatusOk: - break + return nil case amber.StatusErrNotFound: return fmt.Errorf("Source not found") case amber.StatusErr: @@ -425,25 +243,6 @@ func rmSource(services Services) error { default: return fmt.Errorf("Unexpected status: %v", status) } - - // Since modifications to amber.Control, RepositoryManager, and rewrite.Engine aren't - // atomic and amberctl could be interrupted or encounter an error during any step, - // unregister the rewrite rule before removing the repo config to prevent a dangling - // rewrite rule to a repo that no longer exists. - if err := removeAllDynamicRewriteRules(services.rewriteEngine); err != nil { - return err - } - - s, err := services.repoMgr.Remove(repoUrlForId(name)) - if err != nil { - return fmt.Errorf("fuchsia.pkg.RepositoryManager IPC encountered an error: %s", err) - } - zxStatus := zx.Status(s) - if !(zxStatus == zx.ErrOk || zxStatus == zx.ErrNotFound) { - return fmt.Errorf("unable to remove source from RepositoryManager: %s", zxStatus) - } - - return nil } func getUp(r *pkg.PackageResolverInterface) error { @@ -523,10 +322,10 @@ func disableAllSources(a *amber.ControlInterface, except string) error { return nil } -func do(services Services) int { +func do(amberProxy *amber.ControlInterface, resolverProxy *pkg.PackageResolverInterface) int { switch os.Args[1] { case "get_up": - if err := getUp(services.resolver); err != nil { + if err := getUp(resolverProxy); err != nil { log.Printf("error getting an update: %s", err) return 1 } @@ -535,12 +334,12 @@ func do(services Services) int { log.Printf("no blob id provided") return 1 } - if err := services.amber.GetBlob(*blobID); err != nil { + if err := amberProxy.GetBlob(*blobID); err != nil { log.Printf("error requesting blob fetch: %s", err) return 1 } case "add_src": - if err := addSource(services); err != nil { + if err := addSource(amberProxy); err != nil { log.Printf("error adding source: %s", err) if _, ok := err.(ErrGetFile); ok { return 2 @@ -549,12 +348,12 @@ func do(services Services) int { } } case "rm_src": - if err := rmSource(services); err != nil { + if err := rmSource(amberProxy); err != nil { log.Printf("error removing source: %s", err) return 1 } case "list_srcs": - if err := listSources(services.amber); err != nil { + if err := listSources(amberProxy); err != nil { log.Printf("error listing sources: %s", err) return 1 } @@ -562,12 +361,12 @@ func do(services Services) int { log.Printf("%q not yet supported\n", os.Args[1]) return 1 case "test": - if err := doTest(services.amber); err != nil { + if err := doTest(amberProxy); err != nil { log.Printf("error testing connection to amber: %s", err) return 1 } case "system_update": - configured, err := services.amber.CheckForSystemUpdate() + configured, err := amberProxy.CheckForSystemUpdate() if err != nil { log.Printf("error checking for system update: %s", err) return 1 @@ -583,19 +382,14 @@ func do(services Services) int { log.Printf("Error enabling source: no source id provided") return 1 } - err := setSourceEnablement(services.amber, *name, true) + err := setSourceEnablement(amberProxy, *name, true) if err != nil { log.Printf("Error enabling source: %s", err) return 1 } - err = replaceDynamicRewriteRules(services.rewriteEngine, rewriteRuleForId(*name)) - if err != nil { - log.Printf("Error configuring rewrite rules: %s", err) - return 1 - } fmt.Printf("Source %q enabled\n", *name) if !*nonExclusive { - if err := disableAllSources(services.amber, *name); err != nil { + if err := disableAllSources(amberProxy, *name); err != nil { log.Printf("Error disabling sources: %s", err) return 1 } @@ -605,19 +399,14 @@ func do(services Services) int { log.Printf("Error disabling source: no source id provided") return 1 } - err := setSourceEnablement(services.amber, *name, false) + err := setSourceEnablement(amberProxy, *name, false) if err != nil { log.Printf("Error disabling source: %s", err) return 1 } - err = removeAllDynamicRewriteRules(services.rewriteEngine) - if err != nil { - log.Printf("Error configuring rewrite rules: %s", err) - return 1 - } fmt.Printf("Source %q disabled\n", *name) case "gc": - err := services.amber.Gc() + err := amberProxy.Gc() if err != nil { log.Printf("Error collecting garbage: %s", err) return 1 @@ -673,21 +462,13 @@ func Main() { ctx := context.CreateFromStartupInfo() - var services Services - - services.amber = connectToAmber(ctx) - defer services.amber.Close() - - services.resolver = connectToPackageResolver(ctx) - defer services.resolver.Close() - - services.repoMgr = connectToRepositoryManager(ctx) - defer services.repoMgr.Close() + amberProxy, _ := connectToAmber(ctx) + defer amberProxy.Close() - services.rewriteEngine = connectToRewriteEngine(ctx) - defer services.rewriteEngine.Close() + resolverProxy, _ := connectToPackageResolver(ctx) + defer resolverProxy.Close() - os.Exit(do(services)) + os.Exit(do(amberProxy, resolverProxy)) } type ErrDaemon string diff --git a/garnet/go/src/amber/meta/amberctl.cmx b/garnet/go/src/amber/meta/amberctl.cmx index 4bc953cfb13b0cc15bc30e6b571cc0c7e8b2905e..ba9393cf941b54c078e1476667fdb1c328077b15 100644 --- a/garnet/go/src/amber/meta/amberctl.cmx +++ b/garnet/go/src/amber/meta/amberctl.cmx @@ -7,9 +7,7 @@ "fuchsia.amber.Control", "fuchsia.logger.LogSink", "fuchsia.net.SocketProvider", - "fuchsia.pkg.PackageResolver", - "fuchsia.pkg.RepositoryManager", - "fuchsia.pkg.rewrite.Engine" + "fuchsia.pkg.PackageResolver" ] } } diff --git a/garnet/lib/rust/fidl_fuchsia_pkg_ext/src/repo.rs b/garnet/lib/rust/fidl_fuchsia_pkg_ext/src/repo.rs index 59ce8884268776969173f4379ee39eb73b023694..83b783284f13806dd820f13c56c114c5ad04178a 100644 --- a/garnet/lib/rust/fidl_fuchsia_pkg_ext/src/repo.rs +++ b/garnet/lib/rust/fidl_fuchsia_pkg_ext/src/repo.rs @@ -8,18 +8,18 @@ use { fuchsia_uri::pkg_uri::{PkgUri, RepoUri}, serde_derive::{Deserialize, Serialize}, std::convert::TryFrom, - std::{fmt, mem}, + std::mem, }; /// Convenience wrapper for the FIDL RepositoryKeyConfig type -#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase", tag = "type", content = "value", deny_unknown_fields)] pub enum RepositoryKey { Ed25519(#[serde(with = "hex_serde")] Vec<u8>), } /// Convenience wrapper for the FIDL RepositoryBlobConfig type -#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase", tag = "type", content = "value", deny_unknown_fields)] pub enum RepositoryBlobKey { Aes(#[serde(with = "hex_serde")] Vec<u8>), @@ -40,27 +40,17 @@ impl MirrorConfig { } /// Convenience wrapper for generating [MirrorConfig] values. -#[derive(Clone, Debug)] pub struct MirrorConfigBuilder { config: MirrorConfig, } impl MirrorConfigBuilder { - pub fn new(mirror_url: impl Into<String>) -> Self { + pub fn new(mirror_url: String) -> Self { MirrorConfigBuilder { - config: MirrorConfig { - mirror_url: mirror_url.into(), - subscribe: false, - blob_key: None, - }, + config: MirrorConfig { mirror_url: mirror_url, subscribe: false, blob_key: None }, } } - pub fn mirror_url(mut self, mirror_url: impl Into<String>) -> Self { - self.config.mirror_url = mirror_url.into(); - self - } - pub fn subscribe(mut self, subscribe: bool) -> Self { self.config.subscribe = subscribe; self @@ -76,12 +66,6 @@ impl MirrorConfigBuilder { } } -impl Into<MirrorConfig> for MirrorConfigBuilder { - fn into(self) -> MirrorConfig { - self.build() - } -} - impl TryFrom<fidl::MirrorConfig> for MirrorConfig { type Error = RepositoryParseError; fn try_from(other: fidl::MirrorConfig) -> Result<Self, RepositoryParseError> { @@ -185,7 +169,6 @@ impl Into<fidl::RepositoryConfig> for RepositoryConfig { } /// Convenience wrapper for generating [RepositoryConfig] values. -#[derive(Clone, Debug)] pub struct RepositoryConfigBuilder { config: RepositoryConfig, } @@ -194,7 +177,7 @@ impl RepositoryConfigBuilder { pub fn new(repo_url: RepoUri) -> Self { RepositoryConfigBuilder { config: RepositoryConfig { - repo_url, + repo_url: repo_url, root_keys: vec![], mirrors: vec![], update_package_uri: None, @@ -202,18 +185,13 @@ impl RepositoryConfigBuilder { } } - pub fn repo_url(mut self, repo_url: RepoUri) -> Self { - self.config.repo_url = repo_url; - self - } - pub fn add_root_key(mut self, key: RepositoryKey) -> Self { self.config.root_keys.push(key); self } - pub fn add_mirror(mut self, mirror: impl Into<MirrorConfig>) -> Self { - self.config.mirrors.push(mirror.into()); + pub fn add_mirror(mut self, mirror: MirrorConfig) -> Self { + self.config.mirrors.push(mirror); self } @@ -227,12 +205,6 @@ impl RepositoryConfigBuilder { } } -impl Into<RepositoryConfig> for RepositoryConfigBuilder { - fn into(self) -> RepositoryConfig { - self.build() - } -} - /// Wraper for serializing repository configs to the on-disk JSON format. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] #[serde(tag = "version", content = "content", deny_unknown_fields)] @@ -259,13 +231,6 @@ impl Into<fidl::RepositoryKeyConfig> for RepositoryKey { } } -impl fmt::Debug for RepositoryKey { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let RepositoryKey::Ed25519(ref value) = self; - f.debug_tuple("Ed25519").field(&hex::encode(value)).finish() - } -} - impl TryFrom<fidl::RepositoryBlobKey> for RepositoryBlobKey { type Error = RepositoryParseError; fn try_from(id: fidl::RepositoryBlobKey) -> Result<Self, RepositoryParseError> { @@ -284,13 +249,6 @@ impl Into<fidl::RepositoryBlobKey> for RepositoryBlobKey { } } -impl fmt::Debug for RepositoryBlobKey { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let RepositoryBlobKey::Aes(ref value) = self; - f.debug_tuple("Aes").field(&hex::encode(value)).finish() - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/garnet/lib/rust/fuchsia_uri_rewrite/src/rule.rs b/garnet/lib/rust/fuchsia_uri_rewrite/src/rule.rs index e61d14c5d3fe34914f95c5d061724361b91e91f2..3a17a326196d9383c273db22a6aa42d917b2694c 100644 --- a/garnet/lib/rust/fuchsia_uri_rewrite/src/rule.rs +++ b/garnet/lib/rust/fuchsia_uri_rewrite/src/rule.rs @@ -30,16 +30,11 @@ pub enum RuleConfig { impl Rule { /// Creates a new `Rule`. pub fn new( - host_match: impl Into<String>, - host_replacement: impl Into<String>, - path_prefix_match: impl Into<String>, - path_prefix_replacement: impl Into<String>, + host_match: String, + host_replacement: String, + path_prefix_match: String, + path_prefix_replacement: String, ) -> Result<Self, RuleParseError> { - let host_match = host_match.into(); - let host_replacement = host_replacement.into(); - let path_prefix_match = path_prefix_match.into(); - let path_prefix_replacement = path_prefix_replacement.into(); - fn validate_host(s: &str) -> Result<(), RuleParseError> { PkgUri::new_repository(s.to_owned()).map_err(|_err| RuleParseError::InvalidHost)?; Ok(()) @@ -170,8 +165,13 @@ mod serde_tests { macro_rules! rule { ($host_match:expr => $host_replacement:expr, $path_prefix_match:expr => $path_prefix_replacement:expr) => { - Rule::new($host_match, $host_replacement, $path_prefix_match, $path_prefix_replacement) - .unwrap() + Rule::new( + $host_match.to_owned(), + $host_replacement.to_owned(), + $path_prefix_match.to_owned(), + $path_prefix_replacement.to_owned(), + ) + .unwrap() }; } @@ -322,19 +322,19 @@ mod rule_tests { #[test] fn $test_name() { let error = Rule::new( - $host_match, - $host_replacement, - $path_prefix_match, - $path_prefix_replacement, + $host_match.to_owned(), + $host_replacement.to_owned(), + $path_prefix_match.to_owned(), + $path_prefix_replacement.to_owned() ) .expect_err("should have failed to parse"); assert_eq!(error, $error); let error = Rule::new( - $host_replacement, - $host_match, - $path_prefix_replacement, - $path_prefix_match, + $host_replacement.to_owned(), + $host_match.to_owned(), + $path_prefix_replacement.to_owned(), + $path_prefix_match.to_owned() ) .expect_err("should have failed to parse"); assert_eq!(error, $error); diff --git a/garnet/tests/amberctl/BUILD.gn b/garnet/tests/amberctl/BUILD.gn index 59df86f799b7f61717419e7ec33d9f509dc3bf89..0faf5ec4ed6a5d0dfe0052d89cc5f00a8d41332b 100644 --- a/garnet/tests/amberctl/BUILD.gn +++ b/garnet/tests/amberctl/BUILD.gn @@ -13,15 +13,10 @@ rustc_library("driver") { with_unit_tests = true deps = [ - "//garnet/lib/rust/fidl_fuchsia_pkg_ext", - "//garnet/lib/rust/fuchsia_uri", - "//garnet/lib/rust/fuchsia_uri_rewrite", "//garnet/public/lib/fidl/rust/fidl", "//garnet/public/rust/fuchsia-async", "//garnet/public/rust/fuchsia-component", "//sdk/fidl/fuchsia.amber:fuchsia.amber-rustc", - "//sdk/fidl/fuchsia.pkg:fuchsia.pkg-rustc", - "//sdk/fidl/fuchsia.pkg.rewrite:fuchsia.pkg.rewrite-rustc", "//sdk/fidl/fuchsia.sys:fuchsia.sys-rustc", "//third_party/rust_crates:failure", "//third_party/rust_crates:hex", diff --git a/garnet/tests/amberctl/src/lib.rs b/garnet/tests/amberctl/src/lib.rs index e8938d8b7f61a72972db79dbad2d32179b42ab8f..c23983c89d539d01df422729708ade90d359ae73 100644 --- a/garnet/tests/amberctl/src/lib.rs +++ b/garnet/tests/amberctl/src/lib.rs @@ -6,23 +6,13 @@ #![cfg(test)] use { - failure::Error, fidl_fuchsia_amber::{ControlMarker as AmberMarker, ControlProxy as AmberProxy}, - fidl_fuchsia_pkg::{RepositoryManagerMarker, RepositoryManagerProxy}, - fidl_fuchsia_pkg_ext::{ - MirrorConfigBuilder, RepositoryConfig, RepositoryConfigBuilder, RepositoryKey, - }, - fidl_fuchsia_pkg_rewrite::{ - EngineMarker as RewriteEngineMarker, EngineProxy as RewriteEngineProxy, - }, fidl_fuchsia_sys::TerminationReason, fuchsia_async as fasync, fuchsia_component::{ client::{App, AppBuilder, Stdio}, server::{NestedEnvironment, ServiceFs}, }, - fuchsia_uri::pkg_uri::RepoUri, - fuchsia_uri_rewrite::Rule, futures::prelude::*, std::{convert::TryInto, fs::File}, }; @@ -33,10 +23,6 @@ use types::SourceConfigBuilder; const ROOT_KEY_1: &str = "be0b983f7396da675c40c6b93e47fced7c1e9ea8a32a1fe952ba8f519760b307"; const ROOT_KEY_2: &str = "00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100"; -fn amberctl() -> AppBuilder { - AppBuilder::new("fuchsia-pkg://fuchsia.com/amberctl-tests#meta/amberctl.cmx".to_owned()) -} - struct Mounts { misc: tempfile::TempDir, data_amber: tempfile::TempDir, @@ -53,14 +39,11 @@ impl Mounts { struct Proxies { amber: AmberProxy, - repo_manager: RepositoryManagerProxy, - rewrite_engine: RewriteEngineProxy, } struct TestEnv { _amber: App, - _pkg_resolver: App, - _mounts: Mounts, + mounts: Mounts, env: NestedEnvironment, proxies: Proxies, } @@ -86,67 +69,43 @@ impl TestEnv { ) .expect("/data/amber to mount"); - let mut pkg_resolver = AppBuilder::new( - "fuchsia-pkg://fuchsia.com/pkg_resolver#meta/pkg_resolver.cmx".to_owned(), - ); - let mut fs = ServiceFs::new(); - fs.add_proxy_service_to::<AmberMarker, _>(amber.directory_request().unwrap().clone()) - .add_proxy_service_to::<RepositoryManagerMarker, _>( - pkg_resolver.directory_request().unwrap().clone(), - ) - .add_proxy_service_to::<RewriteEngineMarker, _>( - pkg_resolver.directory_request().unwrap().clone(), - ); - + fs.add_proxy_service_to::<AmberMarker, _>(amber.directory_request().unwrap().clone()); let env = fs .create_salted_nested_environment("amberctl_env") .expect("nested environment to create successfully"); fasync::spawn(fs.collect()); let amber = amber.spawn(env.launcher()).expect("amber to launch"); - let pkg_resolver = pkg_resolver.spawn(env.launcher()).expect("amber to launch"); let amber_proxy = env.connect_to_service::<AmberMarker>().expect("connect to amber"); - let repo_manager_proxy = env - .connect_to_service::<RepositoryManagerMarker>() - .expect("connect to repository manager"); - let rewrite_engine_proxy = - env.connect_to_service::<RewriteEngineMarker>().expect("connect to rewrite engine"); - Self { - _amber: amber, - _pkg_resolver: pkg_resolver, - _mounts: mounts, - env, - proxies: Proxies { - amber: amber_proxy, - repo_manager: repo_manager_proxy, - rewrite_engine: rewrite_engine_proxy, - }, - } + Self { _amber: amber, mounts, env, proxies: Proxies { amber: amber_proxy } } } - async fn _run_amberctl(&self, builder: AppBuilder) { - let fut = - builder.stderr(Stdio::Inherit).output(self.env.launcher()).expect("amberctl to launch"); - let output = await!(fut).expect("amberctl to run"); + /// Tear down the test environment, retaining the state directories. + fn into_mounts(self) -> Mounts { + self.mounts + } - assert_eq!(output.exit_status.reason(), TerminationReason::Exited); - assert!( - output.exit_status.success(), - "amberctl exited with {}\nSTDOUT\n{}\nSTDOUT", - output.exit_status.code(), - String::from_utf8_lossy(&output.stdout), - ); + /// Re-create the test environment, re-using the existing temporary state directories. + fn restart(self) -> Self { + Self::new_with_mounts(self.into_mounts()) } - async fn run_amberctl<'a>(&'a self, args: &'a [impl std::fmt::Debug + AsRef<str>]) { - let fut = amberctl() - .args(args.into_iter().map(|s| s.as_ref())) - .stderr(Stdio::Inherit) - .output(self.env.launcher()) - .expect("amberctl to launch"); + async fn run_amberctl<'a>(&'a self, args: &'a [&'a str]) { + let fut = AppBuilder::new( + "fuchsia-pkg://fuchsia.com/amberctl-tests#meta/amberctl.cmx".to_owned(), + ) + .args(args.into_iter().map(|s| *s)) + .add_dir_to_namespace( + "/sources".to_string(), + File::open("/pkg/data/sources").expect("/pkg/data/sources to exist"), + ) + .expect("/sources to mount") + .stderr(Stdio::Inherit) + .output(self.env.launcher()) + .expect("amberctl to launch"); let output = await!(fut).expect("amberctl to run"); assert_eq!(output.exit_status.reason(), TerminationReason::Exited); @@ -159,32 +118,6 @@ impl TestEnv { ); } - async fn run_amberctl_add_static_src(&self, name: &'static str) { - await!(self._run_amberctl( - amberctl() - .add_dir_to_namespace( - "/configs".to_string(), - File::open("/pkg/data/sources").expect("/pkg/data/sources to exist"), - ) - .expect("static /configs to mount") - .args(["add_src", "-f"].into_iter().cloned()) - .arg(format!("/configs/{}", name)) - )); - } - - async fn run_amberctl_add_src(&self, source: types::SourceConfig) { - let mut config_file = tempfile::tempfile().expect("temp config file to create"); - serde_json::to_writer(&mut config_file, &source).expect("source config to serialize"); - - await!(self._run_amberctl( - amberctl() - .add_dir_to_namespace("/configs/test.json".to_string(), config_file) - .expect("static /configs to mount") - // Run amberctl in non-exclusive mode so it doesn't disable existing source configs - .args(["add_src", "-x", "-f", "/configs/test.json"].iter().map(|s| *s)) - )); - } - async fn amber_list_sources(&self) -> Vec<types::SourceConfig> { let sources = await!(self.proxies.amber.list_srcs()).unwrap(); @@ -197,90 +130,51 @@ impl TestEnv { sources.sort_unstable(); sources } - - async fn resolver_list_repos(&self) -> Vec<RepositoryConfig> { - let (iterator, iterator_server_end) = fidl::endpoints::create_proxy().unwrap(); - self.proxies.repo_manager.list(iterator_server_end).unwrap(); - await!(collect_iterator(|| iterator.next())).unwrap() - } - - async fn rewrite_engine_list_rules(&self) -> Vec<Rule> { - let (iterator, iterator_server_end) = fidl::endpoints::create_proxy().unwrap(); - self.proxies.rewrite_engine.list(iterator_server_end).unwrap(); - await!(collect_iterator(|| iterator.next())).unwrap() - } -} - -async fn collect_iterator<F, E, I, O>(mut next: impl FnMut() -> F) -> Result<Vec<O>, Error> -where - F: Future<Output = Result<Vec<I>, fidl::Error>>, - I: TryInto<O, Error = E>, - Error: From<E>, -{ - let mut res = Vec::new(); - loop { - let more = await!(next())?; - if more.is_empty() { - break; - } - res.extend(more.into_iter().map(|cfg| cfg.try_into()).collect::<Result<Vec<_>, _>>()?); - } - Ok(res) } struct SourceConfigGenerator { - id_prefix: String, + builder: SourceConfigBuilder, + root_id: String, + root_url: String, n: usize, } impl SourceConfigGenerator { - fn new(id_prefix: impl Into<String>) -> Self { - Self { id_prefix: id_prefix.into(), n: 0 } + fn new(builder: SourceConfigBuilder) -> Self { + let config = builder.clone().build(); + Self { + root_id: config.id().to_owned(), + root_url: config.repo_url().to_owned(), + builder, + n: 0, + } } } impl Iterator for SourceConfigGenerator { - type Item = (types::SourceConfigBuilder, RepositoryConfigBuilder); + type Item = types::SourceConfigBuilder; fn next(&mut self) -> Option<Self::Item> { - let id = format!("{}{:02}", &self.id_prefix, self.n); - let repo_url = format!("fuchsia-pkg://{}", &id); - let mirror_url = format!("http://example.com/{}", &id); + let id = format!("{}{:02}", &self.root_id, self.n); + let url = format!("{}/{:02}", &self.root_url, self.n); self.n += 1; - Some(( - SourceConfigBuilder::new(id) - .repo_url(mirror_url.clone()) - .add_root_key(ROOT_KEY_1) - .auto(true), - RepositoryConfigBuilder::new(RepoUri::parse(&repo_url).unwrap()) - .add_root_key(RepositoryKey::Ed25519(hex::decode(ROOT_KEY_1).unwrap())) - .add_mirror(MirrorConfigBuilder::new(mirror_url).subscribe(true)), - )) + Some(self.builder.clone().id(id).repo_url(url)) } } -fn make_test_repo_config() -> RepositoryConfig { - RepositoryConfigBuilder::new("fuchsia-pkg://test".parse().unwrap()) - .add_root_key(RepositoryKey::Ed25519(hex::decode(ROOT_KEY_1).unwrap())) - .add_mirror(MirrorConfigBuilder::new("http://example.com").subscribe(true)) - .build() -} - #[fasync::run_singlethreaded(test)] -async fn test_services_start_with_no_config() { +async fn test_amber_starts_with_no_sources() { let env = TestEnv::new(); assert_eq!(await!(env.amber_list_sources()), vec![]); - assert_eq!(await!(env.resolver_list_repos()), vec![]); - assert_eq!(await!(env.rewrite_engine_list_rules()), vec![]); } #[fasync::run_singlethreaded(test)] async fn test_add_src() { let env = TestEnv::new(); - await!(env.run_amberctl_add_static_src("test.json")); + await!(env.run_amberctl(&["add_src", "-f", "/sources/test.json"])); let cfg_test = SourceConfigBuilder::new("test") .repo_url("http://example.com") @@ -289,43 +183,44 @@ async fn test_add_src() { .add_root_key(ROOT_KEY_1) .build(); + assert_eq!(await!(env.amber_list_sources()), vec![cfg_test.clone()]); + + // Ensure source configs persist across service restarts + let env = env.restart(); assert_eq!(await!(env.amber_list_sources()), vec![cfg_test]); - assert_eq!(await!(env.resolver_list_repos()), vec![make_test_repo_config()]); - assert_eq!( - await!(env.rewrite_engine_list_rules()), - vec![Rule::new("fuchsia.com", "test", "/", "/").unwrap()] - ); } #[fasync::run_singlethreaded(test)] async fn test_add_src_disables_other_sources() { let env = TestEnv::new(); - let configs = SourceConfigGenerator::new("testgen").take(3).collect::<Vec<_>>(); - - for (config, _) in &configs { - await!(env.run_amberctl_add_src(config.clone().build().into())); + let configs = SourceConfigGenerator::new( + SourceConfigBuilder::new("test") + .repo_url("http://example.com") + .rate_period(60) + .auto(true) + .add_root_key(ROOT_KEY_1), + ) + .take(3) + .collect::<Vec<_>>(); + + for config in &configs { + assert_eq!( + await!(env.proxies.amber.add_src(&mut config.clone().build().into())).unwrap(), + true + ); } - await!(env.run_amberctl_add_static_src("test.json")); + await!(env.run_amberctl(&["add_src", "-f", "/sources/test.json"])); - let mut source_configs = vec![]; - let mut repo_configs = vec![make_test_repo_config()]; - for (source_config, repo_config) in configs { - source_configs.push(source_config.enabled(false).build()); - repo_configs.push(repo_config.build()); - } + let mut configs = + configs.into_iter().map(|builder| builder.enabled(false).build()).collect::<Vec<_>>(); let test_config = serde_json::from_reader(File::open("/pkg/data/sources/test.json").unwrap()).unwrap(); - source_configs.push(test_config); - source_configs.sort_unstable(); + configs.push(test_config); + configs.sort_unstable(); - assert_eq!(await!(env.amber_list_sources()), source_configs); - assert_eq!(await!(env.resolver_list_repos()), repo_configs); - assert_eq!( - await!(env.rewrite_engine_list_rules()), - vec![Rule::new("fuchsia.com", "test", "/", "/").unwrap()] - ); + assert_eq!(await!(env.amber_list_sources()), configs); } #[fasync::run_singlethreaded(test)] @@ -344,55 +239,30 @@ async fn test_rm_src() { .add_root_key(ROOT_KEY_2) .build(); - await!(env.run_amberctl_add_src(cfg_a.clone().into())); - await!(env.run_amberctl_add_src(cfg_b.clone().into())); - - await!(env.run_amberctl(&["rm_src", "-n", "a"])); - assert_eq!(await!(env.amber_list_sources()), vec![cfg_b]); - assert_eq!( - await!(env.resolver_list_repos()), - vec![RepositoryConfigBuilder::new("fuchsia-pkg://b".parse().unwrap()) - .add_root_key(RepositoryKey::Ed25519(hex::decode(ROOT_KEY_2).unwrap())) - .add_mirror(MirrorConfigBuilder::new("http://example.com/b")) - .build()] - ); - // rm_src removes all rules, so no source remains enabled. - assert_eq!(await!(env.rewrite_engine_list_rules()), vec![]); + assert_eq!(await!(env.proxies.amber.add_src(&mut cfg_a.clone().into())).unwrap(), true); + assert_eq!(await!(env.proxies.amber.add_src(&mut cfg_b.clone().into())).unwrap(), true); await!(env.run_amberctl(&["rm_src", "-n", "b"])); + assert_eq!(await!(env.amber_list_sources()), vec![cfg_a]); + + await!(env.run_amberctl(&["rm_src", "-n", "a"])); assert_eq!(await!(env.amber_list_sources()), vec![]); - assert_eq!(await!(env.resolver_list_repos()), vec![]); - assert_eq!(await!(env.rewrite_engine_list_rules()), vec![]); } #[fasync::run_singlethreaded(test)] async fn test_enable_src() { let env = TestEnv::new(); - let source = SourceConfigBuilder::new("test") + let cfg = SourceConfigBuilder::new("test") .repo_url("http://example.com") .enabled(false) .add_root_key(ROOT_KEY_1); - let repo = RepositoryConfigBuilder::new("fuchsia-pkg://test".parse().unwrap()) - .add_root_key(RepositoryKey::Ed25519(hex::decode(ROOT_KEY_1).unwrap())) - .add_mirror(MirrorConfigBuilder::new("http://example.com")) - .build(); - - await!(env.run_amberctl_add_src(source.clone().build().into())); - - assert_eq!(await!(env.resolver_list_repos()), vec![repo.clone()]); - // Adding a disabled source does not add a rewrite rule for it. - assert_eq!(await!(env.rewrite_engine_list_rules()), vec![]); + assert_eq!(await!(env.proxies.amber.add_src(&mut cfg.clone().build().into())).unwrap(), true); await!(env.run_amberctl(&["enable_src", "-n", "test"])); - assert_eq!(await!(env.amber_list_sources()), vec![source.enabled(true).build()]); - assert_eq!(await!(env.resolver_list_repos()), vec![repo]); - assert_eq!( - await!(env.rewrite_engine_list_rules()), - vec![Rule::new("fuchsia.com", "test", "/", "/").unwrap()] - ); + assert_eq!(await!(env.amber_list_sources()), vec![cfg.enabled(true).build()]); } #[fasync::run_singlethreaded(test)] @@ -400,39 +270,33 @@ async fn test_enable_src_disables_other_sources() { let env = TestEnv::new(); // add some enabled sources - let mut gen = SourceConfigGenerator::new("test"); + let mut gen = SourceConfigGenerator::new( + SourceConfigBuilder::new("test").repo_url("http://example.com").add_root_key(ROOT_KEY_1), + ); let configs = gen.by_ref().take(3).collect::<Vec<_>>(); - for (config, _) in &configs { - await!(env.run_amberctl_add_src(config.clone().build().into())); + for config in &configs { + assert_eq!( + await!(env.proxies.amber.add_src(&mut config.clone().build().into())).unwrap(), + true + ); } - // add an initially disabled source - let (config, repo) = gen.next().unwrap(); - let config = config.enabled(false); + // add an initially disabled source. + let config = gen.next().unwrap().enabled(false); let c = config.clone().build(); let id = c.id().to_owned(); - await!(env.run_amberctl_add_src(c.into())); + assert_eq!(await!(env.proxies.amber.add_src(&mut c.into())).unwrap(), true); // enable that source let args = ["enable_src", "-n", &id]; await!(env.run_amberctl(&args)); // verify the enabled sources are now disabled and the disabled source is now enabled - let mut source_configs = vec![]; - let mut repo_configs = vec![]; - for (source_config, repo_config) in configs { - source_configs.push(source_config.enabled(false).build()); - repo_configs.push(repo_config.build()); - } - source_configs.push(config.enabled(true).build()); - repo_configs.push(repo.build()); - source_configs.sort_unstable(); - assert_eq!(await!(env.amber_list_sources()), source_configs); - assert_eq!(await!(env.resolver_list_repos()), repo_configs); - assert_eq!( - await!(env.rewrite_engine_list_rules()), - vec![Rule::new("fuchsia.com", id, "/", "/").unwrap()] - ); + let mut configs = + configs.into_iter().map(|builder| builder.enabled(false).build()).collect::<Vec<_>>(); + configs.push(config.enabled(true).build()); + configs.sort_unstable(); + assert_eq!(await!(env.amber_list_sources()), configs); } #[fasync::run_singlethreaded(test)] @@ -449,8 +313,8 @@ async fn test_disable_src() { .rate_period(60) .add_root_key(ROOT_KEY_2); - await!(env.run_amberctl_add_src(cfg_a.clone().build().into())); - await!(env.run_amberctl_add_src(cfg_b.clone().build().into())); + assert_eq!(await!(env.proxies.amber.add_src(&mut cfg_a.clone().build().into())).unwrap(), true); + assert_eq!(await!(env.proxies.amber.add_src(&mut cfg_b.clone().build().into())).unwrap(), true); await!(env.run_amberctl(&["disable_src", "-n", "a"])); @@ -458,19 +322,4 @@ async fn test_disable_src() { await!(env.amber_list_sources()), vec![cfg_a.enabled(false).build(), cfg_b.enabled(true).build().into(),] ); - assert_eq!( - await!(env.resolver_list_repos()), - vec![ - RepositoryConfigBuilder::new("fuchsia-pkg://a".parse().unwrap()) - .add_root_key(RepositoryKey::Ed25519(hex::decode(ROOT_KEY_1).unwrap())) - .add_mirror(MirrorConfigBuilder::new("http://example.com/a")) - .build(), - RepositoryConfigBuilder::new("fuchsia-pkg://b".parse().unwrap()) - .add_root_key(RepositoryKey::Ed25519(hex::decode(ROOT_KEY_2).unwrap())) - .add_mirror(MirrorConfigBuilder::new("http://example.com/b")) - .build(), - ] - ); - // disabling any source clears all rewrite rules. - assert_eq!(await!(env.rewrite_engine_list_rules()), vec![]); } diff --git a/garnet/tests/amberctl/src/types.rs b/garnet/tests/amberctl/src/types.rs index eedbbf7b8fc031b5da14dd20b9169fdca97f15e3..cbf00faec8ca244399e166dfc67bdd143417df22 100644 --- a/garnet/tests/amberctl/src/types.rs +++ b/garnet/tests/amberctl/src/types.rs @@ -35,6 +35,11 @@ impl SourceConfigBuilder { } } + pub fn id(mut self, value: impl Into<String>) -> Self { + self.config.id = value.into(); + self + } + pub fn repo_url(mut self, value: impl Into<String>) -> Self { self.config.repo_url = value.into(); self.config.blob_repo_url = format!("{}/blobs", self.config.repo_url); @@ -93,6 +98,9 @@ impl SourceConfig { pub fn id(&self) -> &str { self.id.as_str() } + pub fn repo_url(&self) -> &str { + self.repo_url.as_str() + } } impl Into<fidl::SourceConfig> for SourceConfig {