Skip to content

Commit

Permalink
Basic OCI conformance tests passing (#396)
Browse files Browse the repository at this point in the history
  • Loading branch information
awoimbee authored Jan 16, 2025
1 parent 37395bd commit 5b794f5
Show file tree
Hide file tree
Showing 23 changed files with 207 additions and 204 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ data
target
certs/ca.crt
certs/domain.key
conformance.test
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ tmp/
.idea
.vs
.DS_Store
conformance.test
17 changes: 9 additions & 8 deletions DEVELOPING.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ binary will be written to `/target/debug/trow`.
To execute the binary, you can run `cargo run`, which will first recompile Trow if anything has
changed.

## Running OCI conformance tests locally

```bash
CONT=$(podman create ghcr.io/opencontainers/distribution-spec/conformance:v1.1.0)
podman cp $CONT:/conformance.test .
podman rm $CONT
OCI_ROOT_URL="http://127.0.0.1:8000" OCI_TEST_PULL=1 ./conformance.test
```
### Testing Trow

There are multiple ways to test Trow:

* `cargo test`
* `cargo test --lib --bins` to run only unit tests
* `cargo test --test '*'` to run only integration tests
* `cargo test -- --ignored` to run the smoke tests
* `./run_oci_conformance_tests.sh` to run the OCI conformance tests
22 changes: 22 additions & 0 deletions run_oci_conformance_tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash

set -euo pipefail

if [ ! -f conformance.test ]; then
CONT=$(podman create ghcr.io/opencontainers/distribution-spec/conformance:v1.1.0)
podman cp $CONT:/conformance.test .
podman rm $CONT
fi

export OCI_ROOT_URL="http://127.0.0.1:8000"
export OCI_NAMESPACE="oci-conformance/distribution-test"

export OCI_TEST_PULL=1
export OCI_TEST_PUSH=1
export OCI_TEST_CONTENT_MANAGEMENT=1
export OCI_TEST_CONTENT_DISCOVERY=1
export OCI_TEST_CONTENT_MANAGEMENT=1
export OCI_HIDE_SKIPPED_WORKFLOWS=0

echo "Checking conformance against https://github.com/opencontainers/distribution-spec/blob/main/spec.md"
./conformance.test
4 changes: 2 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use axum::Router;
use axum_server::tls_rustls::RustlsConfig;
use clap::builder::ArgPredicate;
use clap::Parser;
use tracing::{event, Level};
use trow::{TlsConfig, TrowConfig};

#[derive(Parser, Debug)]
Expand Down Expand Up @@ -189,7 +188,7 @@ async fn serve_app(app: Router, addr: SocketAddr, tls: Option<TlsConfig>) -> any
let handle = axum_server::Handle::new();
tokio::spawn(shutdown_signal(handle.clone()));

event!(Level::INFO, "Starting server on {}", addr);
tracing::info!("Starting server on {}", addr);
if let Some(ref tls) = tls {
if !(Path::new(&tls.cert_file).is_file() && Path::new(&tls.key_file).is_file()) {
return Err(anyhow!(
Expand All @@ -199,6 +198,7 @@ async fn serve_app(app: Router, addr: SocketAddr, tls: Option<TlsConfig>) -> any
));
}
let config = RustlsConfig::from_pem_file(&tls.cert_file, &tls.key_file).await?;

axum_server::bind_rustls(addr, config)
.handle(handle)
.serve(app.into_make_service())
Expand Down
15 changes: 5 additions & 10 deletions src/registry/admission.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use json_patch::{Patch, PatchOperation};
use k8s_openapi::api::core::v1::Pod;
use kube::core::admission::{AdmissionRequest, AdmissionResponse};
use serde::{Deserialize, Serialize};
use tracing::{event, Level};

use super::TrowServer;
use crate::registry::proxy::RemoteImage;
Expand All @@ -27,7 +26,7 @@ fn check_image_is_allowed(
"Allow" => true,
"Deny" => false,
_ => {
event!(Level::WARN, "Invalid default image validation config: `{}`. Should be `Allow` or `Deny`. Default to `Deny`.", config.default);
tracing::warn!( "Invalid default image validation config: `{}`. Should be `Allow` or `Deny`. Default to `Deny`.", config.default);
false
}
};
Expand Down Expand Up @@ -122,7 +121,7 @@ impl TrowServer {
let pod = match &ar.object {
Some(pod) => pod,
None => {
event!(Level::WARN, "No pod in pod admission mutation request");
tracing::warn!("No pod in pod admission mutation request");
return resp;
}
};
Expand All @@ -132,10 +131,7 @@ impl TrowServer {
let image = match RemoteImage::try_from_str(raw_image) {
Ok(image) => image,
Err(e) => {
event!(
Level::WARN,
"Could not parse image reference `{raw_image}` ({e})",
);
tracing::warn!("Could not parse image reference `{raw_image}` ({e})",);
continue;
}
};
Expand All @@ -152,8 +148,7 @@ impl TrowServer {
.iter()
.any(|repo| image_repo == repo);
if !ignored {
event!(
Level::INFO,
tracing::info!(
"mutate_admission: proxying image {} to {}",
raw_image,
proxy_config.alias
Expand All @@ -178,7 +173,7 @@ impl TrowServer {
match resp.with_patch(patch) {
Ok(resp) => resp,
Err(e) => {
event!(Level::WARN, "Produced invalid admission patch: {}", e);
tracing::warn!("Produced invalid admission patch: {}", e);
AdmissionResponse::invalid("Internal error serializing the patch")
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/registry/api_types.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! types for the trow <=> trow-server interface
use serde_derive::{Deserialize, Serialize};
use tracing::{event, Level};

#[derive(Clone, PartialEq)]
pub struct UploadRequest {
Expand Down Expand Up @@ -148,7 +147,7 @@ pub enum Status {

impl From<sqlx::Error> for Status {
fn from(err: sqlx::Error) -> Self {
event!(Level::ERROR, "Database error: {err:?}");
tracing::error!("Database error: {err:?}");
Self::Internal(String::new())
}
}
Expand Down
7 changes: 3 additions & 4 deletions src/registry/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ pub use proxy::{RegistryProxiesConfig, SingleRegistryProxyConfig};
pub use server::TrowServer;
pub use storage::StorageBackendError;
use thiserror::Error;
use tracing::{event, Level};

pub mod blob_storage;
#[allow(dead_code)]
Expand Down Expand Up @@ -45,7 +44,7 @@ pub enum RegistryError {

impl From<sqlx::Error> for RegistryError {
fn from(err: sqlx::Error) -> Self {
event!(Level::ERROR, "Database error: {err:?}");
tracing::error!("Database error: {err:?}");
Self::Internal
}
}
Expand All @@ -55,15 +54,15 @@ impl From<StorageBackendError> for RegistryError {
match err {
StorageBackendError::BlobNotFound(_) => Self::NotFound,
StorageBackendError::Internal(e) => {
event!(Level::ERROR, "Internal storage error: {e}");
tracing::error!("Internal storage error: {e}");
Self::Internal
}
StorageBackendError::InvalidContentRange => Self::InvalidContentRange,
StorageBackendError::InvalidDigest => Self::InvalidDigest,
StorageBackendError::InvalidManifest(_msg) => Self::InvalidManifest,
StorageBackendError::InvalidName(name) => Self::InvalidName(name),
StorageBackendError::Io(e) => {
event!(Level::ERROR, "Internal IO error: {e:?}");
tracing::error!("Internal IO error: {e:?}");
Self::Internal
}
StorageBackendError::Unsupported => Self::Unsupported,
Expand Down
11 changes: 5 additions & 6 deletions src/registry/proxy/proxy_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use oci_client::secrets::RegistryAuth;
use oci_client::Reference;
use serde::{Deserialize, Serialize};
use sqlx::SqlitePool;
use tracing::{event, Level};

use crate::registry::manifest::{ManifestReference, OCIManifest};
use crate::registry::proxy::remote_image::RemoteImage;
Expand Down Expand Up @@ -112,7 +111,7 @@ impl SingleRegistryProxyConfig {
) -> Result<Digest, DownloadRemoteImageError> {
// Replace eg f/docker/alpine by f/docker/library/alpine
let repo_name = format!("f/{}/{}", self.alias, image.get_repo());
event!(Level::DEBUG, "Downloading proxied image {}", repo_name);
tracing::debug!("Downloading proxied image {}", repo_name);

let image_ref: Reference = image.clone().into();
let try_cl = self.setup_client(image.scheme == "http").await.ok();
Expand Down Expand Up @@ -141,7 +140,7 @@ impl SingleRegistryProxyConfig {
digests.push(Digest::try_from_raw(&d)?);
}
}
Err(e) => event!(Level::WARN, "Failed to fetch manifest digest: {}", e),
Err(e) => tracing::warn!("Failed to fetch manifest digest: {}", e),
}
}
if let Some(local_digest) = local_digest {
Expand Down Expand Up @@ -182,7 +181,7 @@ impl SingleRegistryProxyConfig {
.await;

if let Err(e) = manifest_download {
event!(Level::WARN, "Failed to download proxied image: {}", e)
tracing::warn!("Failed to download proxied image: {}", e)
} else {
if let Some(tag) = image_ref.tag() {
sqlx::query!(
Expand Down Expand Up @@ -271,7 +270,7 @@ async fn download_manifest_and_layers(
layer_digest: &str,
local_repo_name: &str,
) -> Result<()> {
event!(Level::TRACE, "Downloading blob {}", layer_digest);
tracing::trace!("Downloading blob {}", layer_digest);
let already_has_blob = sqlx::query_scalar!(
"SELECT EXISTS(SELECT 1 FROM blob WHERE digest = $1);",
layer_digest,
Expand Down Expand Up @@ -320,7 +319,7 @@ async fn download_manifest_and_layers(
oci_client::manifest::OCI_IMAGE_INDEX_MEDIA_TYPE,
];

event!(Level::DEBUG, "Downloading manifest + layers for {}", ref_);
tracing::debug!("Downloading manifest + layers for {}", ref_);

let (raw_manifest, digest) = cl
.pull_manifest_raw(ref_, auth, MIME_TYPES_DISTRIBUTION_MANIFEST)
Expand Down
3 changes: 1 addition & 2 deletions src/registry/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use std::path::PathBuf;
use std::str;

use anyhow::Result;
use tracing::{event, Level};

// use super::manifest::Manifest;
use super::proxy::RegistryProxiesConfig;
Expand Down Expand Up @@ -68,7 +67,7 @@ impl TrowServer {
match self.storage.is_ready().await {
Ok(()) => true,
Err(e) => {
event!(Level::ERROR, "Storage backend not ready: {e}");
tracing::error!("Storage backend not ready: {e}");
false
}
}
Expand Down
Loading

0 comments on commit 5b794f5

Please sign in to comment.