Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(CI): qualifying as github job #661

Merged
merged 29 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
13b8a99
qualifying as github job
NikolaMilosa Jul 24, 2024
ed763fb
updating workflow
NikolaMilosa Jul 24, 2024
2812150
fixing possible error on running ict which keeps the code hanging ind…
NikolaMilosa Jul 24, 2024
be6a66e
removing unnecessary step for qualification
NikolaMilosa Jul 24, 2024
f7658cb
enhansing error printing
NikolaMilosa Jul 24, 2024
3d0dbef
dry
NikolaMilosa Jul 24, 2024
f4d7900
Branch was auto-updated.
sa-github-api Jul 24, 2024
5a22c81
updating version of runner
NikolaMilosa Jul 24, 2024
8472e02
setting alias
NikolaMilosa Jul 24, 2024
0b8d917
trying to add alias
NikolaMilosa Jul 24, 2024
7fa3e99
Branch was auto-updated.
sa-github-api Jul 25, 2024
5da73f0
removing docker alias
NikolaMilosa Jul 25, 2024
d6a2536
Branch was auto-updated.
sa-github-api Jul 25, 2024
9d43d9f
upgrading runner image
NikolaMilosa Jul 25, 2024
01b3ff5
removing utsns and netns
NikolaMilosa Jul 25, 2024
0aaaca9
adding missing sudo
NikolaMilosa Jul 25, 2024
c73bac6
removing cgroupmanager
NikolaMilosa Jul 25, 2024
94b9b60
fixing sed
NikolaMilosa Jul 25, 2024
3290643
fixing sed
NikolaMilosa Jul 25, 2024
759d180
removing containers.conf
NikolaMilosa Jul 25, 2024
e19b865
adding missing sudo
NikolaMilosa Jul 25, 2024
6e6a67f
removing containers.conf
NikolaMilosa Jul 25, 2024
812ae6d
adding manual ttl for farm
NikolaMilosa Jul 25, 2024
1b3b5ff
adding manual ttl for farm
NikolaMilosa Jul 25, 2024
72f02a7
adding xnet key check
NikolaMilosa Jul 25, 2024
e2e7fc9
adding to pipeline
NikolaMilosa Jul 25, 2024
bb19af7
fixing quotes
NikolaMilosa Jul 25, 2024
64dadf9
Branch was auto-updated.
sa-github-api Jul 26, 2024
2d7ef35
Branch was auto-updated.
sa-github-api Jul 26, 2024
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
47 changes: 47 additions & 0 deletions .github/workflows/qualify.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Qualification

on:
workflow_dispatch:
inputs:
version:
description: "The version that should be qualified"
type: string
default: ""

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
qualify:
runs-on:
labels: dre-runner-custom
container: ghcr.io/dfinity/dre/actions-runner:7efd87b0eac3ebd255be7efe00a3b39b0f9e9fc1
steps:
- uses: actions/checkout@v4

- name: "🔍 Check if the version is set"
shell: bash
run: |
if [ -z "${{ github.event.inputs.version }}" ]; then
echo "Error: 'version' is required and cannot be empty."
exit 1
fi

- name: "☁️ Setup runner"
uses: ./.github/workflows/manage-runner-pre

# This should be done in runner.Dockerfile
- name: "Remove utsns and netns from containers.conf"
shell: bash
run: |
sudo rm /etc/containers/containers.conf
NikolaMilosa marked this conversation as resolved.
Show resolved Hide resolved

- name: "✨ Running qualification"
shell: bash
env:
MANUALY_TTL_FARM: 1
run: |
mkdir -p ~/.config/dfx/identity/xnet-testing/
echo "${{ secrets.XNET_PRINCIPAL_KEY }}" > ~/.config/dfx/identity/xnet-testing/identity.pem
bazel run //rs/qualifier -- "${{ github.event.inputs.version }}"
13 changes: 12 additions & 1 deletion rs/qualifier/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use clap::Parser;
use ic_nervous_system_common_test_keys::TEST_NEURON_1_OWNER_KEYPAIR;
use tokio::process::Command;
const TEST_NEURON_1_IDENTITY_PATH: &str = ".config/dfx/identity/test_neuron_1/identity.pem";
const XNET_TESTING_IDENTITY_PATH: &str = ".config/dfx/identity/xnet-testing/identity.pem";

#[derive(Parser)]
#[clap(about, version)]
Expand Down Expand Up @@ -38,7 +39,7 @@ pub struct Args {
}

impl Args {
pub fn ensure_key(&self) -> anyhow::Result<(u64, PathBuf)> {
pub fn ensure_test_key(&self) -> anyhow::Result<(u64, PathBuf)> {
let key_pair = &TEST_NEURON_1_OWNER_KEYPAIR;
let path = dirs::home_dir()
.ok_or(anyhow::anyhow!("No home dir present"))?
Expand All @@ -52,6 +53,16 @@ impl Args {
Ok((449479075714955186, path))
}

pub fn ensure_xnet_test_key(&self) -> anyhow::Result<()> {
let path = dirs::home_dir()
.ok_or(anyhow::anyhow!("No home dir present"))?
.join(PathBuf::from_str(XNET_TESTING_IDENTITY_PATH)?);
match path.exists() {
true => Ok(()),
false => anyhow::bail!("Missing xnet-testing identity on path: {}", path.display()),
}
}

pub async fn ensure_git(&self) -> anyhow::Result<()> {
std::fs::create_dir_all(&self.ic_repo_path)?;

Expand Down
97 changes: 81 additions & 16 deletions rs/qualifier/src/ict_util.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
use std::{path::PathBuf, process::Stdio, str::FromStr};
use std::{path::PathBuf, process::Stdio, str::FromStr, time::Duration};

use itertools::Itertools;
use log::info;
use reqwest::ClientBuilder;
use serde_json::Value;
use tokio::{
io::{AsyncBufReadExt, BufReader},
process::{ChildStdout, Command},
process::Command,
sync::mpsc::Sender,
};
use tokio_util::sync::CancellationToken;

use crate::Message;

// Each 30s set the ttl for a testnet to 90 seconds
const FARM_GROUP_KEEPALIVE_TTL_SEC: u64 = 90;
const KEEPALIVE_PERIOD: Duration = Duration::from_secs(30);
const KEEPALIVE_PERIOD_ERROR: Duration = Duration::from_secs(5);
pub const FARM_BASE_URL: &str = "https://farm.dfinity.systems";

pub async fn ict(ic_git: PathBuf, config: String, token: CancellationToken, sender: Sender<Message>) -> anyhow::Result<()> {
let ic_config = PathBuf::from_str("/tmp/ic_config.json")?;
std::fs::write(&ic_config, &config)?;
Expand All @@ -35,19 +42,8 @@ pub async fn ict(ic_git: PathBuf, config: String, token: CancellationToken, send
.current_dir(&ic_git)
.spawn()?;

if let Some(taken) = child.stdout.as_mut() {
wait_data(taken, token.clone(), sender).await?;
}

token.cancelled().await;
info!("Received shutdown, killing testnet");
child.kill().await?;

Ok(())
}

async fn wait_data(stdout: &mut ChildStdout, token: CancellationToken, sender: Sender<Message>) -> anyhow::Result<()> {
let mut stdout_reader = BufReader::new(stdout).lines();
let mut stdout = child.stdout.take().ok_or(anyhow::anyhow!("Couldn't take stdout"))?;
let mut stdout_reader = BufReader::new(&mut stdout).lines();

let target = "Testnet is being deployed, please wait ...";
let logs;
Expand All @@ -70,6 +66,21 @@ async fn wait_data(stdout: &mut ChildStdout, token: CancellationToken, sender: S
if token.is_cancelled() {
return Ok(());
}

match child.try_wait() {
Ok(Some(s)) => {
let stderr = child.stderr.take().ok_or(anyhow::anyhow!("Unable to get stderr"))?;
let mut lines = BufReader::new(stderr).lines();

let mut all = vec![];
while let Some(line) = lines.next_line().await? {
all.push(line)
}

anyhow::bail!("Finished early with status code {:?} and error: \n{}", s, all.iter().join("\n"))
}
_ => continue,
}
}

sender
Expand All @@ -79,20 +90,40 @@ async fn wait_data(stdout: &mut ChildStdout, token: CancellationToken, sender: S

info!("Building testnet...");
let mut whole_config = vec![];
let deployment_name;
loop {
let line = stdout_reader.next_line().await?;
if let Some(line) = line {
whole_config.push(line.trim().to_string());
let config = whole_config.iter().join("");

if serde_json::from_str::<Value>(&config).is_ok() {
if let Ok(v) = serde_json::from_str::<Value>(&config) {
deployment_name = v["farm"]["group"]
.as_str()
.ok_or(anyhow::anyhow!("Failed to find 'farm.group'"))?
.to_string();
break;
}
}

if token.is_cancelled() {
return Ok(());
}

match child.try_wait() {
Ok(Some(s)) => {
let stderr = child.stderr.take().ok_or(anyhow::anyhow!("Unable to get stderr"))?;
let mut lines = BufReader::new(stderr).lines();

let mut all = vec![];
while let Some(line) = lines.next_line().await? {
all.push(line)
}

anyhow::bail!("Finished early with status code {:?} and error: \n{}", s, all.iter().join("\n"))
}
_ => continue,
}
}

let config = whole_config.iter().join("");
Expand All @@ -101,5 +132,39 @@ async fn wait_data(stdout: &mut ChildStdout, token: CancellationToken, sender: S
.await
.map_err(|_| anyhow::anyhow!("Failed to send data across channels"))?;

child.stdout = Some(stdout);

if std::env::var("MANUALY_TTL_FARM").is_ok() {
tokio::select! {
_ = keep_testnet_alive(deployment_name, token.clone()) => {},
_ = token.cancelled() => {}
}
} else {
token.cancelled().await;
}

info!("Received shutdown, killing testnet");
child.kill().await?;

Ok(())
}

async fn keep_testnet_alive(group_name: String, token: CancellationToken) -> anyhow::Result<()> {
let client = ClientBuilder::new().timeout(Duration::from_secs(15)).build()?;
let ttl_url = format!("{}/group/{}/ttl/{}", FARM_BASE_URL, &group_name, FARM_GROUP_KEEPALIVE_TTL_SEC);
info!("Will be using following ttl link: {}", ttl_url);

while !token.is_cancelled() {
let resp_future = client.put(&ttl_url).send().await;

match resp_future {
Ok(r) => match r.error_for_status() {
Ok(_) => tokio::time::sleep(KEEPALIVE_PERIOD).await,
_ => tokio::time::sleep(KEEPALIVE_PERIOD_ERROR).await,
},
_ => tokio::time::sleep(KEEPALIVE_PERIOD_ERROR).await,
}
}

Ok(())
}
29 changes: 15 additions & 14 deletions rs/qualifier/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ async fn main() -> anyhow::Result<()> {
// Check if farm is reachable. If not, error
let client = ClientBuilder::new().timeout(Duration::from_secs(30)).build()?;
client
.get("https://kibana.testnet.dfinity.network")
.get("https://kibana.testnet.dfinity.network/")
.send()
.await
.map_err(|e| anyhow::anyhow!("Checking connectivity failed: {}", e.to_string()))?
Expand All @@ -39,9 +39,10 @@ async fn main() -> anyhow::Result<()> {
let args = Args::parse();
info!("Running qualification for {}", args.version_to_qualify);
info!("Generating keys for farm testnets...");
let (neuron_id, private_key_pem) = args.ensure_key()?;
let (neuron_id, private_key_pem) = args.ensure_test_key()?;
info!("Principal key created");

args.ensure_xnet_test_key()?;
// Take in one version and figure out what is the base version
//
// To find the initial version we could take NNS version?
Expand Down Expand Up @@ -133,22 +134,22 @@ async fn main() -> anyhow::Result<()> {
// we have to extract the neuron pem file to use with dre
let token = CancellationToken::new();
let (sender, mut receiver) = mpsc::channel(2);
let handle = tokio::spawn(ict(args.ic_repo_path.clone(), config, token.clone(), sender));

qualify(
&mut receiver,
private_key_pem,
neuron_id,
NETWORK_NAME,
initial_version,
args.version_to_qualify.to_string(),
)
.await?;

tokio::select! {
res = ict(args.ic_repo_path.clone(), config, token.clone(), sender) => res?,
res = qualify(
&mut receiver,
private_key_pem,
neuron_id,
NETWORK_NAME,
initial_version,
args.version_to_qualify.to_string(),
) => res?
};

info!("Finished qualifier run for: {}", args.version_to_qualify);

token.cancel();
handle.await??;
Ok(())
}

Expand Down
Loading