Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ updates:
patterns:
- "*"
- package-ecosystem: "gomod"
directory: "/"
directories:
- "/"
- "/tools/virtctl"
schedule:
interval: "weekly"
groups:
Expand Down
80 changes: 80 additions & 0 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# SPDX-FileCopyrightText: Jakob Naucke <jnaucke@redhat.com>
#
# SPDX-License-Identifier: CC0-1.0

name: "Integration tests"
on:
pull_request:
branches:
- "main"
pull_request_target:
types: [labeled]
branches:
- "main"
permissions:
contents: "read"

# Don't waste job slots on superseded code
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always
RUNTIME: docker
CONTAINER_CLI: docker
REGISTRY: quay.io/jnaucke
TAG: 20260623-f880666
OPERATOR_IMAGE: quay.io/jnaucke/trusted-cluster-operator:20260624-ea8114c
TEST_TIMEOUT_MULTIPLIER: 2

jobs:
integration-tests:
name: "KubeVirt integration tests"
if: >-
github.event.pull_request.author_association == 'MEMBER' ||
github.event.pull_request.author_association == 'OWNER' ||
github.event.pull_request.author_association == 'COLLABORATOR' ||
contains(github.event.pull_request.labels.*.name, 'ok-to-test')
runs-on: "ubuntu-24.04"
timeout-minutes: 120
steps:
- name: "Check out repository"
uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: "Enable KVM"
run: sudo chmod 666 /dev/kvm
- name: "Install build dependencies"
run: sudo apt-get install -y libssl-dev pkg-config
- name: "Install Rust toolchain"
uses: dtolnay/rust-toolchain@v1
with:
toolchain: stable
components: rustfmt
- name: "Install Go toolchain"
uses: actions/setup-go@v6
with:
go-version-file: go.mod
- name: "Cache build artifacts"
uses: Swatinem/rust-cache@v2
- name: "Install KinD"
uses: helm/kind-action@v1
with:
# TODO Dependabot cannot update action inputs, consider Renovate's regex manager
version: v0.31.0
install_only: true
- name: "Install virtctl"
run: |
version=$(cd tools/virtctl && go list -m -f '{{.Version}}' kubevirt.io/kubevirt)
curl -Lo virtctl "https://github.com/kubevirt/kubevirt/releases/download/${version}/virtctl-${version}-linux-amd64"
chmod +x virtctl
sudo mv virtctl /usr/local/bin/
- name: "Create KinD cluster"
run: make cluster-up
- name: "Install KubeVirt"
run: make install-kubevirt
- name: "Run integration tests"
run: |
eval $(ssh-agent -s)
make integration-tests
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ compute-pcrs-lib = { git = "https://github.com/trusted-execution-clusters/comput
env_logger = { version = "0.11.10", default-features = false }
http = "1.4.2"
ignition-config = "0.6.1"
# Tracking k8s version for CI: docker.io/kindest/node:v1.35.7
k8s-openapi = { version = "0.27.1", features = ["v1_35", "schemars"] }
kube = { version = "3.1.0", default-features = false, features = ["derive", "runtime", "openssl-tls"] }
log = "0.4.32"
Expand Down
6 changes: 2 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,19 @@ PLATFORM ?= kind

KUBECTL=kubectl
INTEGRATION_TEST_THREADS ?= 1
# Azure CI only: which image to use as Kind host
KIND_HOST_URN = RedHat:RHEL:10-lvm-gen2:10.1.2026022409

LOCALBIN ?= $(shell pwd)/bin
CONTROLLER_TOOLS_VERSION ?= $(shell go list -m -f '{{.Version}}' sigs.k8s.io/controller-tools)
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen-$(CONTROLLER_TOOLS_VERSION)
YQ_VERSION ?= $(shell go list -m -f '{{.Version}}' github.com/mikefarah/yq/v4)
YQ ?= $(LOCALBIN)/yq-$(YQ_VERSION)
KOPIUM_VERSION ?= $(shell grep kopium lib/Cargo.toml | sed -E 's/.*"(.*)"/\1/')
KOPIUM_VERSION ?= $(shell cargo metadata --format-version 1 | jq -r '.resolve.nodes[] | select(.deps[]?.name == "kopium") | .deps[] | select(.name == "kopium") | .pkg | split("@")[1]')
KOPIUM ?= $(LOCALBIN)/kopium-$(KOPIUM_VERSION)

REGISTRY ?= quay.io/trusted-execution-clusters
TAG ?= latest
PUSH_FLAGS ?=
OPERATOR_IMAGE=$(REGISTRY)/trusted-cluster-operator:$(TAG)
OPERATOR_IMAGE ?= $(REGISTRY)/trusted-cluster-operator:$(TAG)
COMPUTE_PCRS_IMAGE=$(REGISTRY)/compute-pcrs:$(TAG)
REG_SERVER_IMAGE=$(REGISTRY)/registration-server:$(TAG)
ATTESTATION_KEY_REGISTER_IMAGE=$(REGISTRY)/attestation-key-register:$(TAG)
Expand Down
1 change: 1 addition & 0 deletions REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ path = [
"must-gather/README.md",
"tests/README.md",
"examples/*",
"tools/virtctl/go.sum",
"scripts/install-kubevirt.sh"
]
SPDX-FileCopyrightText = [
Expand Down
105 changes: 59 additions & 46 deletions operator/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ use std::env;
use std::sync::Arc;
use std::time::Duration;

use anyhow::Result;
use anyhow::{Context, Result};
use env_logger::Env;
use futures_util::StreamExt;
use k8s_openapi::apimachinery::pkg::apis::meta::v1::Condition;
use kube::runtime::controller::{Action, Controller};
use kube::runtime::reflector::{self, Store};
use kube::runtime::watcher;
use kube::{Api, Client};
use log::{error, info, warn};
use log::{info, warn};

use operator::{generate_owner_reference, upsert_condition};
use trusted_cluster_operator_lib::{TrustedExecutionCluster, TrustedExecutionClusterStatus};
Expand Down Expand Up @@ -132,9 +132,11 @@ async fn reconcile(
update_status!(clusters, name, status)?;
}

install_trustee_configuration(kube_client.clone(), &cluster).await?;
install_register_server(kube_client.clone(), &cluster).await?;
install_attestation_key_register(kube_client.clone(), &cluster).await?;
if let Err(e) = install_components(&kube_client, &cluster).await {
// warn with `:?` to also get context
warn!("Installation of a component failed: {e:?}\nRequeueing...");
return Ok(Action::requeue(Duration::from_secs(60)));
}
reference_values::adopt_approved_images(kube_client, &cluster).await?;

let installed_condition = installed_condition(INSTALLED_REASON, generation, existing_status);
Expand All @@ -146,39 +148,42 @@ async fn reconcile(
Ok(Action::await_change())
}

async fn install_components(client: &Client, cluster: &TrustedExecutionCluster) -> Result<()> {
install_trustee_configuration(client.clone(), cluster).await?;
install_register_server(client.clone(), cluster).await?;
install_attestation_key_register(client.clone(), cluster).await?;
Ok(())
}

async fn install_trustee_configuration(
client: Client,
cluster: &TrustedExecutionCluster,
) -> Result<()> {
let owner_reference = generate_owner_reference(cluster)?;

let trustee_secret = &cluster.spec.trustee_secret;
match trustee::generate_trustee_data(client.clone(), owner_reference.clone(), trustee_secret)
trustee::generate_trustee_data(client.clone(), owner_reference.clone(), trustee_secret)
.await
{
Ok(_) => info!("Generate configmap for the KBS configuration",),
Err(e) => error!("Failed to create the KBS configuration configmap: {e}"),
}
.context("Failed to create the KBS configuration configmap")?;
info!("Generated configmap for the KBS configuration");

match trustee::generate_attestation_policy(client.clone(), owner_reference.clone()).await {
Ok(_) => info!("Generate configmap for the attestation policy",),
Err(e) => error!("Failed to create the attestation policy configmap: {e}"),
}
trustee::generate_attestation_policy(client.clone(), owner_reference.clone())
.await
.context("Failed to create the attestation policy configmap")?;
info!("Generated configmap for the attestation policy");

let kbs_port = cluster.spec.trustee_kbs_port;
match trustee::generate_kbs_service(client.clone(), owner_reference.clone(), kbs_port).await {
Ok(_) => info!("Generate the KBS service"),
Err(e) => error!("Failed to create the KBS service: {e}"),
}
trustee::generate_kbs_service(client.clone(), owner_reference.clone(), kbs_port)
.await
.context("Failed to create the KBS service")?;
info!("Generated the KBS service");

let default = format!("{TEC_REGISTRY}/key-broker-service:{TRUSTEE_VERSION}");
let trustee_image = env::var(RELATED_IMAGE_TRUSTEE).ok().unwrap_or(default);
match trustee::generate_kbs_deployment(client, owner_reference, &trustee_image, trustee_secret)
trustee::generate_kbs_deployment(client, owner_reference, &trustee_image, trustee_secret)
.await
{
Ok(_) => info!("Generate the KBS deployment"),
Err(e) => error!("Failed to create the KBS deployment: {e}"),
}
.context("Failed to create the KBS deployment")?;
info!("Generated the KBS deployment");

Ok(())
}
Expand All @@ -189,25 +194,21 @@ async fn install_register_server(client: Client, cluster: &TrustedExecutionClust
let env = RELATED_IMAGE_REGISTRATION_SERVER;
let default_image = format!("{TEC_REGISTRY}/registration-server:{COMPONENT_VERSION}");
let register_server_image = env::var(env).ok().unwrap_or(default_image);
match register_server::create_register_server_deployment(
register_server::create_register_server_deployment(
client.clone(),
owner_reference.clone(),
&register_server_image,
&cluster.spec.register_server_secret,
)
.await
{
Ok(_) => info!("Register server deployment created/updated successfully"),
Err(e) => error!("Failed to create register server deployment: {e}"),
}
.context("Failed to create register server deployment")?;
info!("Register server deployment created/updated successfully");

let port = cluster.spec.register_server_port;
match register_server::create_register_server_service(client.clone(), owner_reference, port)
register_server::create_register_server_service(client.clone(), owner_reference, port)
.await
{
Ok(_) => info!("Register server service created/updated successfully"),
Err(e) => error!("Failed to create register server service: {e}"),
}
.context("Failed to create register server service")?;
info!("Register server service created/updated successfully");

Ok(())
}
Expand All @@ -221,29 +222,24 @@ async fn install_attestation_key_register(
let env = RELATED_IMAGE_ATTESTATION_KEY_REGISTER;
let default_image = format!("{TEC_REGISTRY}/attestation-key-register:{COMPONENT_VERSION}");
let attestation_key_register_image = env::var(env).ok().unwrap_or(default_image);
match attestation_key_register::create_attestation_key_register_deployment(
attestation_key_register::create_attestation_key_register_deployment(
client.clone(),
owner_reference.clone(),
&attestation_key_register_image,
&cluster.spec.attestation_key_register_secret,
)
.await
{
Ok(_) => info!("Attestation key register deployment created/updated successfully"),
Err(e) => error!("Failed to create attestation key register deployment: {e}"),
}
.context("Failed to create attestation key register deployment")?;
info!("Attestation key register deployment created/updated successfully");

let port = cluster.spec.attestation_key_register_port;
match attestation_key_register::create_attestation_key_register_service(
attestation_key_register::create_attestation_key_register_service(
client.clone(),
owner_reference,
port,
cluster.spec.attestation_key_register_port,
)
.await
{
Ok(_) => info!("Attestation key register service created/updated successfully"),
Err(e) => error!("Failed to create attestation key register service: {e}"),
}
.context("Failed to create attestation key register service")?;
info!("Attestation key register service created/updated successfully");

Ok(())
}
Expand Down Expand Up @@ -297,6 +293,8 @@ async fn main() -> Result<()> {
#[cfg(test)]
mod tests {
use http::{Method, Request, StatusCode};
use k8s_openapi::api::apps::v1::Deployment;
use k8s_openapi::api::core::v1::{ConfigMap, Service};
use k8s_openapi::{apimachinery::pkg::apis::meta::v1::Time, jiff::Timestamp};
use kube::api::ObjectList;
use kube::client::Body;
Expand Down Expand Up @@ -439,7 +437,22 @@ mod tests {

let clos = async |req: Request<Body>, ctr| {
if ctr < 8 && req.method() == Method::POST {
Ok(serde_json::to_string(&dummy_cluster()).unwrap())
use serde_json::to_string;
let resp = match ctr {
// Trustee
0 => to_string(&ConfigMap::default()),
1 => to_string(&ConfigMap::default()),
2 => to_string(&Service::default()),
3 => to_string(&Deployment::default()),
// Registration server
4 => to_string(&Deployment::default()),
5 => to_string(&Service::default()),
// Attestation key register server
6 => to_string(&Deployment::default()),
7 => to_string(&Service::default()),
_ => unreachable!("unexpected counter {ctr}"),
};
Ok(resp.unwrap())
} else if ctr == 8 && req.method() == Method::GET {
let object_list = ObjectList::<ApprovedImage> {
items: Vec::new(),
Expand Down
Loading
Loading