Skip to content

Commit eafbb6d

Browse files
feat(config): Adds additional configuration option for specifying metadata
Adds support for an additional configuration for namespaces and packages so a well-known is not necessarily required Signed-off-by: Taylor Thomas <taylor@cosmonic.com>
1 parent cc1993d commit eafbb6d

File tree

9 files changed

+298
-23
lines changed

9 files changed

+298
-23
lines changed

README.md

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,20 @@ default_registry = "acme.registry.com"
6969
[namespace_registries]
7070
wasi = "wasi.dev"
7171
example = "example.com"
72+
# An example of providing your own registry mapping. For large and/or public registries, we
73+
# recommend creating a well-known metadata file that can be used to determine the registry to use
74+
# (see the section on "metadata" below). But many times you might want to override mappings or
75+
# provide something that is used by a single team. The registry name does not matter, but must be
76+
# parsable to URL authority. This name is purely used for mapping to registry config and isn't
77+
# actually used as a URL when metadata is provided
78+
another = { registry = "another", metadata = { preferredProtocol = "oci", "oci" = {registry = "ghcr.io", namespacePrefix = "webassembly/" } } }
7279

7380
# This overrides the default registry for a specific package. This is useful for cases where a
7481
# package is published to multiple registries.
7582
[package_registry_overrides]
7683
"example:foo" = "example.com"
84+
# Same as namespace_registries above, but for a specific package.
85+
"example:bar" = { registry = "another", metadata = { preferredProtocol = "oci", "oci" = {registry = "ghcr.io", namespacePrefix = "webassembly/" } } }
7786

7887
# This section contains a mapping of registries to their configuration. There are currently 3
7988
# supported types of registries: "oci", "warg", and "local". The "oci" type is the default. The
@@ -117,14 +126,20 @@ root = "/a/path"
117126
# config_file = "/a/path"
118127
[registry."example.com".warg]
119128
config_file = "/a/path"
129+
130+
# Configuration for the "another" registry defined above.
131+
[registry."another".oci]
132+
auth = { username = "open", password = "sesame" }
120133
```
121134

122135
### Well-known metadata
123136

124-
The `wkg` tool and libraries expect a `registry.json` file to be present at a specific location to
125-
indicate to the tooling where the components are stored. If a registry was `example.com`, then the
126-
tooling will attempt to find a `registry.json` file at
127-
`https://example.com/.well-known/wasm-pkg/registry.json`.
137+
For well-used or public registries, we recommend creating a well-known metadata file that is used by
138+
the tool chain to simplify configuration and indicate to a client which protocols and mappings to
139+
use (although this can be set directly in config as well). The `wkg` tool and libraries expect a
140+
`registry.json` file to be present at a specific location to indicate to the tooling where the
141+
components are stored. For example, if a registry was `example.com`, then the tooling will attempt
142+
to find a `registry.json` file at `https://example.com/.well-known/wasm-pkg/registry.json`.
128143

129144
A full example of what this `registry.json` file should look like is below:
130145

crates/wasm-pkg-client/src/lib.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ use tokio::io::AsyncSeekExt;
4646
use tokio::sync::RwLock;
4747
use tokio_util::io::SyncIoBridge;
4848
pub use wasm_pkg_common::{
49-
config::Config,
49+
config::{Config, CustomConfig, RegistryMapping},
5050
digest::ContentDigest,
5151
metadata::RegistryMetadata,
5252
package::{PackageRef, Version},
@@ -191,6 +191,7 @@ impl Client {
191191
package: &PackageRef,
192192
registry_override: Option<Registry>,
193193
) -> Result<Arc<InnerClient>, Error> {
194+
let is_override = registry_override.is_some();
194195
let registry = if let Some(registry) = registry_override {
195196
registry
196197
} else {
@@ -212,7 +213,25 @@ impl Client {
212213

213214
// Skip fetching metadata for "local" source
214215
let should_fetch_meta = registry_config.default_backend() != Some("local");
215-
let registry_meta = if should_fetch_meta {
216+
let maybe_metadata = self
217+
.config
218+
.namespace_registry(package.namespace())
219+
.and_then(|meta| {
220+
// If the overriden registry matches the registry we are trying to resolve, we
221+
// should use the metadata, otherwise we'll need to fetch the metadata from the
222+
// registry
223+
match (meta, is_override) {
224+
(RegistryMapping::Custom(custom), true) if custom.registry == registry => {
225+
Some(custom.metadata.clone())
226+
}
227+
(RegistryMapping::Custom(custom), false) => Some(custom.metadata.clone()),
228+
_ => None,
229+
}
230+
});
231+
232+
let registry_meta = if let Some(meta) = maybe_metadata {
233+
meta
234+
} else if should_fetch_meta {
216235
RegistryMetadata::fetch_or_default(&registry).await
217236
} else {
218237
RegistryMetadata::default()

crates/wasm-pkg-client/src/oci/config.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ fn serialize_secret<S: Serializer>(
159159

160160
#[cfg(test)]
161161
mod tests {
162+
use wasm_pkg_common::config::RegistryMapping;
163+
164+
use crate::oci::OciRegistryMetadata;
165+
162166
use super::*;
163167

164168
#[test]
@@ -241,4 +245,30 @@ mod tests {
241245
"Password should be set to the right value"
242246
);
243247
}
248+
249+
#[test]
250+
fn test_custom_namespace_config() {
251+
let toml_config = toml::toml! {
252+
[namespace_registries]
253+
test = { registry = "localhost:1234", metadata = { preferredProtocol = "oci", "oci" = { registry = "ghcr.io", namespacePrefix = "webassembly/" } } }
254+
};
255+
256+
let cfg = wasm_pkg_common::config::Config::from_toml(&toml_config.to_string())
257+
.expect("Should be able to load config");
258+
259+
let ns_config = cfg
260+
.namespace_registry(&"test".parse().unwrap())
261+
.expect("Should have a namespace config");
262+
let custom = match ns_config {
263+
RegistryMapping::Custom(c) => c,
264+
_ => panic!("Should have a custom namespace config"),
265+
};
266+
let map: OciRegistryMetadata = custom
267+
.metadata
268+
.protocol_config("oci")
269+
.expect("Should be able to deserialize config")
270+
.expect("protocol config should be present");
271+
assert_eq!(map.namespace_prefix, Some("webassembly/".to_string()));
272+
assert_eq!(map.registry, Some("ghcr.io".to_string()));
273+
}
244274
}

crates/wasm-pkg-client/src/warg/config.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ fn serialize_secret<S: Serializer>(
116116

117117
#[cfg(test)]
118118
mod tests {
119+
use wasm_pkg_common::config::RegistryMapping;
120+
121+
use crate::warg::WargRegistryMetadata;
122+
119123
use super::*;
120124

121125
#[tokio::test]
@@ -178,4 +182,33 @@ mod tests {
178182
"Signing key should be set to the right value"
179183
);
180184
}
185+
186+
#[test]
187+
fn test_custom_namespace_config() {
188+
let toml_config = toml::toml! {
189+
[namespace_registries]
190+
test = { registry = "localhost:1234", metadata = { preferredProtocol = "warg", "warg" = { url = "http://localhost:1234" } } }
191+
};
192+
193+
let cfg = wasm_pkg_common::config::Config::from_toml(&toml_config.to_string())
194+
.expect("Should be able to load config");
195+
196+
let ns_config = cfg
197+
.namespace_registry(&"test".parse().unwrap())
198+
.expect("Should have a namespace config");
199+
let custom = match ns_config {
200+
RegistryMapping::Custom(c) => c,
201+
_ => panic!("Should have a custom namespace config"),
202+
};
203+
let map: WargRegistryMetadata = custom
204+
.metadata
205+
.protocol_config("warg")
206+
.expect("Should be able to deserialize config")
207+
.expect("protocol config should be present");
208+
assert_eq!(
209+
map.url,
210+
Some("http://localhost:1234".into()),
211+
"URL should be set to the right value"
212+
);
213+
}
181214
}

crates/wasm-pkg-client/tests/e2e.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,28 @@ async fn publish_and_fetch_smoke_test() {
5959
.expect("Failed to read fixture");
6060
assert_eq!(content, expected_content);
6161
}
62+
63+
// Simple smoke test to make sure the custom metadata section is parsed and used correctly. Down the
64+
// line we might want to just push a thing to a local registry and then fetch it, but for now we'll
65+
// just use the bytecodealliance registry.
66+
#[tokio::test]
67+
async fn fetch_with_custom_config() {
68+
let toml_config = toml::toml! {
69+
[namespace_registries]
70+
wasi = { registry = "fake.com:1234", metadata = { preferredProtocol = "oci", "oci" = {registry = "ghcr.io", namespacePrefix = "bytecodealliance/wasm-pkg/" } } }
71+
};
72+
73+
let conf = Config::from_toml(&toml_config.to_string()).expect("Failed to parse config");
74+
let client = Client::new(conf);
75+
76+
// Try listing all versions of the wasi package and make sure it doesn't fail
77+
let package = "wasi:http".parse().unwrap();
78+
let versions = client
79+
.list_all_versions(&package)
80+
.await
81+
.expect("Should be able to list versions with custom config");
82+
assert!(
83+
!versions.is_empty(),
84+
"Should be able to list versions with custom config"
85+
);
86+
}

crates/wasm-pkg-common/src/config.rs

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ use std::{
66

77
use serde::{Deserialize, Serialize};
88

9-
use crate::{label::Label, package::PackageRef, registry::Registry, Error};
9+
use crate::{
10+
label::Label, metadata::RegistryMetadata, package::PackageRef, registry::Registry, Error,
11+
};
1012

1113
mod toml;
1214

@@ -23,13 +25,35 @@ const DEFAULT_FALLBACK_NAMESPACE_REGISTRIES: &[(&str, &str)] =
2325
#[serde(into = "toml::TomlConfig")]
2426
pub struct Config {
2527
default_registry: Option<Registry>,
26-
namespace_registries: HashMap<Label, Registry>,
27-
package_registry_overrides: HashMap<PackageRef, Registry>,
28+
namespace_registries: HashMap<Label, RegistryMapping>,
29+
package_registry_overrides: HashMap<PackageRef, RegistryMapping>,
2830
// Note: these are only used for hard-coded defaults currently
2931
fallback_namespace_registries: HashMap<Label, Registry>,
3032
registry_configs: HashMap<Registry, RegistryConfig>,
3133
}
3234

35+
/// Possible options for namespace configuration.
36+
#[derive(Debug, Clone, Serialize, Deserialize)]
37+
#[serde(untagged)]
38+
pub enum RegistryMapping {
39+
/// Use the given registry address (this will fetch the well-known registry metadata from the given hostname).
40+
Registry(Registry),
41+
/// Use custom configuration for reaching a registry
42+
Custom(CustomConfig),
43+
}
44+
45+
/// Custom registry configuration
46+
#[derive(Debug, Clone, Serialize, Deserialize)]
47+
pub struct CustomConfig {
48+
/// A valid name for the registry. This still must be a valid [`Registry`] in that it should
49+
/// look like a valid hostname. When doing custom configuration however, this is just used as a
50+
/// key to identify the configuration for this namespace
51+
pub registry: Registry,
52+
/// The metadata for the registry. This is used to determine the protocol to use for the
53+
/// registry as well as mapping information for the registry.
54+
pub metadata: RegistryMetadata,
55+
}
56+
3357
impl Default for Config {
3458
fn default() -> Self {
3559
let fallback_namespace_registries = DEFAULT_FALLBACK_NAMESPACE_REGISTRIES
@@ -150,10 +174,20 @@ impl Config {
150174
/// - The default registry
151175
/// - Hard-coded fallbacks for certain well-known namespaces
152176
pub fn resolve_registry(&self, package: &PackageRef) -> Option<&Registry> {
153-
if let Some(reg) = self.package_registry_overrides.get(package) {
177+
if let Some(RegistryMapping::Registry(reg)) = self.package_registry_overrides.get(package) {
154178
Some(reg)
155-
} else if let Some(reg) = self.namespace_registries.get(package.namespace()) {
179+
} else if let Some(RegistryMapping::Custom(custom)) =
180+
self.package_registry_overrides.get(package)
181+
{
182+
Some(&custom.registry)
183+
} else if let Some(RegistryMapping::Registry(reg)) =
184+
self.namespace_registries.get(package.namespace())
185+
{
156186
Some(reg)
187+
} else if let Some(RegistryMapping::Custom(custom)) =
188+
self.namespace_registries.get(package.namespace())
189+
{
190+
Some(&custom.registry)
157191
} else if let Some(reg) = self.default_registry.as_ref() {
158192
Some(reg)
159193
} else if let Some(reg) = self.fallback_namespace_registries.get(package.namespace()) {
@@ -179,25 +213,29 @@ impl Config {
179213
///
180214
/// Does not fall back to the default registry; see
181215
/// [`Self::resolve_registry`].
182-
pub fn namespace_registry(&self, namespace: &Label) -> Option<&Registry> {
216+
pub fn namespace_registry(&self, namespace: &Label) -> Option<&RegistryMapping> {
183217
self.namespace_registries.get(namespace)
184218
}
185219

186220
/// Sets a registry for the given namespace.
187-
pub fn set_namespace_registry(&mut self, namespace: Label, registry: Registry) {
221+
pub fn set_namespace_registry(&mut self, namespace: Label, registry: RegistryMapping) {
188222
self.namespace_registries.insert(namespace, registry);
189223
}
190224

191225
/// Returns a registry override configured for the given package.
192226
///
193227
/// Does not fall back to namespace or default registries; see
194228
/// [`Self::resolve_registry`].
195-
pub fn package_registry_override(&self, package: &PackageRef) -> Option<&Registry> {
229+
pub fn package_registry_override(&self, package: &PackageRef) -> Option<&RegistryMapping> {
196230
self.package_registry_overrides.get(package)
197231
}
198232

199233
/// Sets a registry override for the given package.
200-
pub fn set_package_registry_override(&mut self, package: PackageRef, registry: Registry) {
234+
pub fn set_package_registry_override(
235+
&mut self,
236+
package: PackageRef,
237+
registry: RegistryMapping,
238+
) {
201239
self.package_registry_overrides.insert(package, registry);
202240
}
203241

0 commit comments

Comments
 (0)