Skip to content

Commit

Permalink
Enable legacy support workaround (#9)
Browse files Browse the repository at this point in the history
* feat. adding a workaround for specifying tenant in image path

* Feat. adding support for ctrd version under 1.7
- allows for image references to specify tenant in path
  • Loading branch information
juliusl authored Feb 7, 2023
1 parent 080cf25 commit 0d5b90b
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 38 deletions.
53 changes: 37 additions & 16 deletions src/bin/shared/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ impl Commands {
None::<String>,
)
};

if let Some(guest) = acr.guest.as_ref() {
std::env::set_var("ACCOUNT_NAME", guest);
}

tokio::task::block_in_place(|| {
host.open_runtime_editor::<RegistryProxy>(debug);
})
Expand All @@ -73,8 +73,18 @@ impl Commands {
}
}


async fn default_handle(self, ACR {registry, registry_host, guest, ..}: ACR, _: bool, mirror_runmd: PathBuf, world_dir: PathBuf) {
async fn default_handle(
self,
ACR {
registry,
registry_host,
guest,
..
}: ACR,
_: bool,
mirror_runmd: PathBuf,
world_dir: PathBuf,
) {
match self {
Commands::Mirror(mut host_settings) => {
if host_settings.workspace.is_none() {
Expand Down Expand Up @@ -108,25 +118,36 @@ impl Commands {
event!(Level::WARN, "Overwriting existing file {:?}", mirror_runmd);
}

let hosts_config = if let Some(registry) = registry.as_ref() {
MirrorHost::get_hosts_config(
let hosts_configs = if let Some(registry) = registry.as_ref() {
vec![MirrorHost::get_hosts_config(
format!("{registry}.{registry_host}"),
mirror_address.to_string(),
true,
Some(teleport_format.to_string()),
)
)]
} else {
DefaultHost::get_hosts_config(
mirror_address.to_string(),
true,
Some(registry_host.to_string()),
Some(teleport_format.to_string()),
)
vec![
DefaultHost::get_hosts_config(
mirror_address.to_string(),
true,
Some(registry_host.to_string()),
Some(teleport_format.to_string()),
),
DefaultHost::get_legacy_supported_hosts_config(
&registry_host,
mirror_address.to_string(),
true,
Some(registry_host.to_string()),
Some(teleport_format.to_string()),
),
]
};

match hosts_config.install(fs_root.to_owned()) {
Ok(path) => event!(Level::INFO, "Wrote hosts.toml for host, {:?}", path),
Err(err) => panic!("Could not write hosts.toml {err}"),
for host_config in hosts_configs {
match host_config.install(fs_root.to_owned()) {
Ok(path) => event!(Level::INFO, "Wrote hosts.toml for host, {:?}", path),
Err(err) => panic!("Could not write hosts.toml {err}"),
}
}

if hosts_config_only {
Expand Down
13 changes: 12 additions & 1 deletion src/config/hosts_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub struct HostsConfig {
server: Option<String>,
/// List of hosts in priority order that will be used to serve registry requests
hosts: Vec<Host>,
/// If true, adds logic to handle configuring a hosts.toml for containerd versions under 1.7
legacy_support: bool,
}

impl HostsConfig {
Expand All @@ -27,9 +29,17 @@ impl HostsConfig {
Self {
server: server.map(|s| s.into()),
hosts: vec![],
legacy_support: false,
}
}

/// Enables legacy support for containerd version under 1.7
///
pub fn enable_legacy_support(mut self) -> Self {
self.legacy_support = true;
self
}

/// Adds host to list of hosts, chainable
///
pub fn add_host(mut self, host: Host) -> Self {
Expand Down Expand Up @@ -65,7 +75,8 @@ impl HostsConfig {

impl Display for HostsConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(server) = self.server.as_ref() {
// TODO: Workaround for _default host being only available in ctrd 1.7 +, a server should never start with azurecr
if let Some(server) = self.server.as_ref().filter(|_| !self.legacy_support) {
writeln!(f, r#"server = "https://{}""#, server)?;
writeln!(f, "")?;
}
Expand Down
15 changes: 12 additions & 3 deletions src/content/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use lifec::prelude::{SpecialAttribute, ThunkContext};
use lifec::state::AttributeIndex;
use lifec_poem::RoutePlugin;
use poem::{Request, Response};
use tracing::{debug, event, Level, error};
use tracing::{debug, event, Level, error, info};

use crate::hosts_config::MirrorHost;

Expand Down Expand Up @@ -47,8 +47,17 @@ impl Registry {
where
P: RoutePlugin + SpecialAttribute,
{
let repo = repo.into();
let namespace = namespace.into();
let mut repo = repo.into();
let mut namespace = namespace.into();

if repo.starts_with("_tenant_") {
if let Some((tenant, _repo)) = repo.split_once("/") {
let tenant = tenant.trim_start_matches("_tenant_");
namespace = format!("{tenant}.{namespace}");
repo = _repo.to_string();
info!("Applied tenant workaround, namespace -> {namespace}, repo -> {repo}");
}
}

// Check if the request uri ends with the suffix value of the header, if not then return 503 Service Unavailable
let accept_if_header = request.header(consts::ACCEPT_IF_SUFFIX_HEADER);
Expand Down
29 changes: 28 additions & 1 deletion src/plugins/mirror/default_host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,34 @@ impl DefaultHost {
let config = HostsConfig::new(None::<String>);

let mut host = RegistryHost::new(address.into())
.enable_resolve();
.enable_resolve()
.enable_pull();

if insecure {
host = host.skip_verify();
}

if let Some(suffix_match) = suffix_match {
let suffix_match = suffix_match.into();
host = host.add_header(crate::consts::ACCEPT_IF_SUFFIX_HEADER, suffix_match.clone());
host = host.add_header(crate::consts::ENABLE_MIRROR_IF_SUFFIX_HEADER, suffix_match);
}

if let Some(streamable_format) = streamable_format {
host = host.add_header(crate::consts::UPGRADE_IF_STREAMABLE_HEADER, streamable_format.into());
}

config.add_host(host)
}

/// Returns a host config that is capable of handling azurecr.io/_tenant_ requests
///
pub fn get_legacy_supported_hosts_config(host: impl Into<String>, address: impl Into<String>, insecure: bool, suffix_match: Option<impl Into<String>>, streamable_format: Option<impl Into<String>>) -> HostsConfig {
let config = HostsConfig::new(Some(host.into())).enable_legacy_support();

let mut host = RegistryHost::new(address.into())
.enable_resolve()
.enable_pull();

if insecure {
host = host.skip_verify();
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/mirror/mirror_host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ impl MirrorHost {
pub fn get_hosts_config(server: impl Into<String>, host: impl Into<String>, insecure: bool, upgrade_streamable_format: Option<impl Into<String>>) -> HostsConfig {
let config = HostsConfig::new(Some(server));

let mut host = RegistryHost::new(host).enable_resolve();
let mut host = RegistryHost::new(host).enable_resolve().enable_pull();

if insecure {
host = host.skip_verify();
Expand Down
17 changes: 11 additions & 6 deletions src/plugins/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use lifec::prelude::{
AttributeIndex, BlockObject, BlockProperties, Component, CustomAttribute, DenseVecStorage,
Plugin, ThunkContext,
};
use tracing::{event, Level};
use tracing::{event, Level, debug};

/// Plugin that mirrors image resolution api's, based on OCI spec endpoints,
///
Expand All @@ -28,16 +28,21 @@ impl Plugin for Resolve {
}

fn call(context: &mut ThunkContext) -> Option<lifec::plugins::AsyncContext> {
let digest = context.cached_response().and_then(|c| c.headers().get("docker-content-digest")).cloned();

let digest = context
.cached_response()
.and_then(|c| c.headers().get("docker-content-digest"))
.cloned();

debug!("Resolved digest: {:?}", digest);

context.task(|_| {
let mut tc = context.clone();
async move {
if let Some(digest) = digest {
event!(Level::DEBUG, "Found digest {:?}", digest);
tc.state_mut().with_symbol("digest", digest.to_str().expect("should be a string"));
tc.state_mut()
.with_symbol("digest", digest.to_str().expect("should be a string"));
} else {
event!(Level::ERROR, "Did not find digest from cached response");
event!(Level::ERROR, "Did not find digest from cached response");
}

tc.copy_previous();
Expand Down
34 changes: 24 additions & 10 deletions src/plugins/teleport.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use hyper::Body;
use hyper::Response;
use hyper::StatusCode;
use hyper::Method;
use hyper::Request;
use lifec::prelude::{
AsyncContext, AttributeIndex, BlockObject, BlockProperties, CustomAttribute,
Plugin, ThunkContext,
Expand All @@ -11,6 +11,7 @@ use tracing::info;
use tracing::warn;

use crate::ImageIndex;
use crate::ProxyTarget;
use crate::ReferrersList;

/// Plugin to handle swapping out the manifest resolution to a teleportable image
Expand Down Expand Up @@ -47,19 +48,32 @@ impl Plugin for Teleport {
let streamable = list.find_streamable_descriptors();
let digest = if let Some(streamable_desc) = streamable.first() {
info!("Streamable descriptor was found");
streamable_desc.digest.clone()
streamable_desc.digest.to_string()
} else {
warn!("No streamable descriptor was found, {:?} {:?}", list, streamable);
tc.search().find_symbol("digest").expect("should have a digest property")
let digest = tc.search().find_symbol("digest").expect("should have a digest property");
digest
};

let mut ptc = tc.clone();
ptc.replace_symbol("digest", digest);

let manifest_uri = ProxyTarget::try_from(&ptc).expect("should have a proxy target");
let manifest = tc.client()
.expect("should have client")
.request(
Request::builder()
.method(Method::HEAD)
.uri(manifest_uri.manifest_url())
.header("Accept", tc.search().find_symbol("accept").expect("should have accept header"))
.header("Authorization", tc.search().find_symbol("Authorization").expect("should have authorization"))
.body(Body::empty())
.expect("should be able to create request")
.into()
).await.expect("should have response");

tc.cache_response(
Response::builder()
.header("docker-content-digest", digest)
.status(StatusCode::OK)
.body(Body::empty())
.expect("should build a response")
.into(),
manifest
)
}
Err(err) => {
Expand Down

0 comments on commit 0d5b90b

Please sign in to comment.