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

Set up code coverage properly #740

Merged
merged 18 commits into from
Dec 6, 2021
Merged
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
34 changes: 34 additions & 0 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Coverage

on:
push:
#branches:
# - master

jobs:
rust:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- uses: Swatinem/rust-cache@v1
- uses: AbsaOSS/k3d-action@v2
name: "Create Single Cluster"
with:
cluster-name: "test-cluster-1"
args: >-
--agents 1
--image docker.io/rancher/k3s:v1.22.4-k3s1
--k3s-arg "--no-deploy=traefik,servicelb,metrics-server@server:*"
- name: Run cargo-tarpaulin
uses: actions-rs/tarpaulin@v0.1
with:
version: '0.18.5'
out-type: Xml
args: ' -- --test-threads 1'
- uses: codecov/codecov-action@v2
22 changes: 0 additions & 22 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,3 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
body_path: release.txt
code-coverage:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- name: Run cargo-tarpaulin
uses: actions-rs/tarpaulin@v0.1
with:
version: '0.18.0'
out-type: Lcov
timeout: 600
args: '--all -e tests -- --test-threads 1'
- name: Coveralls Finished
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
path-to-lcov: ./lcov.info
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ test:

test-kubernetes:
cargo test --lib --all -- --ignored # also run tests that fail on github actions
cargo test -p kube --features=derive -- --ignored
cargo test -p kube --lib --features=derive,runtime -- --ignored
cargo run -p kube-examples --example crd_derive
cargo run -p kube-examples --example crd_api

Expand Down
1 change: 0 additions & 1 deletion kube-core/src/admission.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ pub struct SerializePatchError(#[source] serde_json::Error);
/// Failed to convert `AdmissionReview` into `AdmissionRequest`.
pub struct ConvertAdmissionReviewError;


/// The `kind` field in [`TypeMeta`].
pub const META_KIND: &str = "AdmissionReview";
/// The `api_version` field in [`TypeMeta`] on the v1 version.
Expand Down
2 changes: 2 additions & 0 deletions kube-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,10 @@ mod custom_resource;
/// If you have to override a lot, [you can opt-out of schema-generation entirely](#kubeschema--mode)
///
/// ## Advanced Features
///
/// - **embedding k8s-openapi types** can be done by enabling the `schemars` feature of `k8s-openapi` from [`0.13.0`](https://github.com/Arnavion/k8s-openapi/blob/master/CHANGELOG.md#v0130-2021-08-09)
/// - **adding validation** via [validator crate](https://github.com/Keats/validator) is supported from `schemars` >= [`0.8.5`](https://github.com/GREsau/schemars/blob/master/CHANGELOG.md#085---2021-09-20)
/// - **generating rust code from schemas** can be done via [kopium](https://github.com/kube-rs/kopium) and is supported on stable crds (> 1.16 kubernetes)
///
/// ### Validation Caveats
/// The supported **`#[validate]` attrs also exist as `#[schemars]` attrs** so you can use those directly if you do not require the validation to run client-side (in your code).
Expand Down
121 changes: 105 additions & 16 deletions kube/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,26 +179,37 @@ pub use crate::core::{CustomResourceExt, Resource, ResourceExt};
#[doc(inline)]
pub use kube_core as core;


// Tests that require a cluster and the complete feature set
// Can be run with `cargo test -p kube --lib --features=runtime,derive -- --ignored`
#[cfg(test)]
#[cfg(all(feature = "derive", feature = "client"))]
mod test {
#[cfg(all(feature = "derive", feature = "client"))]
use crate::{Api, Client, CustomResourceExt, ResourceExt};
use kube_derive::CustomResource;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(CustomResource, Deserialize, Serialize, Clone, Debug, JsonSchema)]
#[kube(group = "clux.dev", version = "v1", kind = "Foo", namespaced)]
#[kube(status = "FooStatus")]
#[kube(scale = r#"{"specReplicasPath":".spec.replicas", "statusReplicasPath":".status.replicas"}"#)]
#[kube(crates(kube_core = "crate::core"))] // for dev-dep test structure
pub struct FooSpec {
name: String,
info: Option<String>,
replicas: isize,
}

#[derive(Deserialize, Serialize, Clone, Debug, Default, JsonSchema)]
pub struct FooStatus {
is_bad: bool,
replicas: isize,
}

#[tokio::test]
#[ignore] // needs kubeconfig
async fn convenient_custom_resource() {
use crate::{
core::{ApiResource, DynamicObject, GroupVersionKind},
Api, Client, CustomResource,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, CustomResource, Deserialize, Serialize, JsonSchema)]
#[kube(group = "clux.dev", version = "v1", kind = "Foo", namespaced)]
#[kube(crates(kube_core = "crate::core"))]
struct FooSpec {
foo: String,
}
async fn custom_resource_generates_correct_core_structs() {
use crate::core::{ApiResource, DynamicObject, GroupVersionKind};
let client = Client::try_default().await.unwrap();

let gvk = GroupVersionKind::gvk("clux.dev", "v1", "Foo");
Expand All @@ -209,4 +220,82 @@ mod test {
// make sure they return the same url_path through their impls
assert_eq!(a1.resource_url(), a2.resource_url());
}

use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1::CustomResourceDefinition;
#[tokio::test]
#[ignore] // needs cluster (creates + patches foo crd)
#[cfg(all(feature = "derive", feature = "runtime"))]
async fn derived_resource_queriable() -> Result<(), Box<dyn std::error::Error>> {
use crate::{
core::params::{DeleteParams, Patch, PatchParams},
runtime::wait::{await_condition, conditions},
};
let client = Client::try_default().await?;
let ssapply = PatchParams::apply("kube").force();
let crds: Api<CustomResourceDefinition> = Api::all(client.clone());
// Server-side apply CRD
crds.patch("foos.clux.dev", &ssapply, &Patch::Apply(Foo::crd()))
.await?;
// Wait for it to be ready:
let establish = await_condition(crds, "foos.clux.dev", conditions::is_crd_established());
let _ = tokio::time::timeout(std::time::Duration::from_secs(10), establish).await?;
// Use it
let foos: Api<Foo> = Api::default_namespaced(client.clone());
// Apply from generated struct
let foo = Foo::new("baz", FooSpec {
name: "baz".into(),
info: Some("old baz".into()),
replicas: 3,
});
let o = foos.patch("baz", &ssapply, &Patch::Apply(&foo)).await?;
assert_eq!(o.spec.name, "baz");
// Apply from partial json!
let patch = serde_json::json!({
"apiVersion": "clux.dev/v1",
"kind": "Foo",
"spec": {
"name": "foo",
"replicas": 2
}
});
let o2 = foos.patch("baz", &ssapply, &Patch::Apply(patch)).await?;
assert_eq!(o2.spec.replicas, 2);
assert_eq!(foos.get_scale("baz").await?.spec.unwrap().replicas, Some(2));
assert!(foos.get_status("baz").await?.status.is_none()); // nothing has set this
foos.delete("baz", &DeleteParams::default()).await?;
Ok(())
}

#[tokio::test]
#[ignore] // needs cluster (fetches api resources, and lists all)
#[cfg(all(feature = "derive", feature = "runtime"))]
async fn dynamic_resources_discoverable() -> Result<(), Box<dyn std::error::Error>> {
use crate::{
core::DynamicObject,
discovery::{verbs, Discovery, Scope},
};
let client = Client::try_default().await?;

let discovery = Discovery::new(client.clone()).run().await?;
for group in discovery.groups() {
for (ar, caps) in group.recommended_resources() {
if !caps.supports_operation(verbs::LIST) {
continue;
}
let api: Api<DynamicObject> = if caps.scope == Scope::Namespaced {
Api::default_namespaced_with(client.clone(), &ar)
} else {
Api::all_with(client.clone(), &ar)
};
println!("{}/{} : {}", group.name(), ar.version, ar.kind);
let list = api.list(&Default::default()).await?;
for item in list.items {
let name = item.name();
let ns = item.metadata.namespace.map(|s| s + "/").unwrap_or_default();
println!("\t\t{}{}", ns, name);
}
}
}
Ok(())
}
}
12 changes: 12 additions & 0 deletions tarpaulin.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Usage cargo tarpaulin -- --test-threads 1

[one_pass_coverage]
workspace = true
features = "kube/derive kube/runtime"
color = "Always"
ignored = true
timeout = "600s"
exclude = ["integration"]
# NB: skipping Doctests because they are slow to build and generally marked no_run
run-types = ["Tests"]
generate = ["Json"]