Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
b2a2ba1
[sagas] Make a macro to simplify declaring saga actions
smklein Dec 16, 2022
f399ff7
ignore rustdoc
smklein Dec 16, 2022
66cf0c0
[nexus] Instance Deletion is now a saga
smklein Dec 16, 2022
828ed21
[nexus] Project and VPC creation are now sagas
smklein Dec 19, 2022
2e0a199
Merge branch 'main' into saga-macro
smklein Dec 19, 2022
533ee76
Merge branch 'main' into saga-macro
smklein Dec 19, 2022
6abce08
Move some lazy_static to once_cell
smklein Dec 20, 2022
169ed10
Merge branch 'main' into saga-macro
smklein Dec 20, 2022
29a3ab5
Extend docs
smklein Dec 20, 2022
3b62a6a
Merge branch 'main' into saga-macro
smklein Dec 20, 2022
05cbd9d
Merge branch 'saga-macro' into more-sagas
smklein Dec 20, 2022
f7f5fbe
Merge branch 'more-sagas' into project-creation-saga
smklein Dec 20, 2022
2f89973
[vpc_create] Add undo actions
smklein Dec 21, 2022
b9fef96
[vpc_create] Add tests for idempotency, fix things
smklein Dec 21, 2022
d68c763
[nexus] Make project creation unwind safe, add tests
smklein Dec 21, 2022
9a8504b
fix project lookup
smklein Dec 21, 2022
79271d2
Lookup project when deleting to avoid rcgen issue during unwind
smklein Dec 21, 2022
23801dd
[nexus] Make snapshot deletion a saga
smklein Dec 22, 2022
80932d8
[nexus] Add basic test for snapshot create
smklein Dec 23, 2022
bfc6048
Merge branch 'main' into project-creation-saga
smklein Dec 23, 2022
f9c635a
Merge branch 'project-creation-saga' into vpc-creation-saga-idempotent
smklein Dec 23, 2022
8c01fcd
Merge branch 'vpc-creation-saga-idempotent' into project-creation-sag…
smklein Dec 23, 2022
3f37a48
Merge branch 'project-creation-saga-idempotent' into snapshot-delete-…
smklein Dec 23, 2022
e1a5e5c
Merge branch 'snapshot-delete-saga' into snapshot-create-saga-idempotent
smklein Dec 23, 2022
163d9c5
fix node names
smklein Dec 23, 2022
fedfbb8
Merge branch 'snapshot-delete-saga' into snapshot-create-saga-idempotent
smklein Dec 23, 2022
ffdc483
Verify clean slate in snapshot test, add volume check to disk test
smklein Dec 23, 2022
839fac6
Passing snapshot unwind test
smklein Dec 26, 2022
e4190e8
More state validation
smklein Dec 26, 2022
c4b16ce
fmt
smklein Dec 26, 2022
9008dce
Don't record sled_id; not needed yet
smklein Dec 26, 2022
666bf64
Merge branch 'main' into project-creation-saga-idempotent
smklein Dec 27, 2022
af15ed0
Merge branch 'project-creation-saga-idempotent' into snapshot-delete-…
smklein Dec 27, 2022
ee2519e
Merge branch 'snapshot-delete-saga' into snapshot-create-saga-idempotent
smklein Dec 27, 2022
bd2786c
[nexus] Make instance creation actions/undo actions idempotent
smklein Dec 28, 2022
c6e929d
Fix steno rev
smklein Dec 28, 2022
a5293df
[nexus] Make instance deletion actions idempotent
smklein Dec 28, 2022
b6dc6c3
[nexus] Make disk creation saga idempotent
smklein Dec 28, 2022
bca0c97
[nexus] Make disk deletion saga idempotent
smklein Dec 28, 2022
1d68f03
Merge branch 'main' into project-creation-saga-idempotent
smklein Dec 28, 2022
344df7a
Merge branch 'project-creation-saga-idempotent' into snapshot-delete-…
smklein Dec 28, 2022
de1213e
Merge branch 'snapshot-delete-saga' into snapshot-create-saga-idempotent
smklein Dec 28, 2022
ae4d65f
Merge branch 'snapshot-create-saga-idempotent' into instance-create-i…
smklein Dec 28, 2022
57a34aa
Merge branch 'instance-create-idempotency' into instance-delete-idemp…
smklein Dec 28, 2022
5024072
Merge branch 'instance-delete-idempotency' into disk-create-idempotency
smklein Dec 28, 2022
7e8dfd4
Merge branch 'disk-create-idempotency' into disk-delete-idempotency
smklein Dec 28, 2022
d59ef33
Merge branch 'main' into project-creation-saga-idempotent
smklein Dec 29, 2022
805ee99
Merge branch 'project-creation-saga-idempotent' into snapshot-delete-…
smklein Dec 29, 2022
61d9d5c
Merge branch 'snapshot-delete-saga' into snapshot-create-saga-idempotent
smklein Dec 29, 2022
4e0ab41
Merge branch 'snapshot-create-saga-idempotent' into instance-create-i…
smklein Dec 29, 2022
e0833a2
Merge branch 'instance-create-idempotency' into instance-delete-idemp…
smklein Dec 29, 2022
b039a17
Merge branch 'instance-delete-idempotency' into disk-create-idempotency
smklein Dec 29, 2022
ef5d14f
Merge branch 'disk-create-idempotency' into disk-delete-idempotency
smklein Dec 29, 2022
0163d16
Merge branch 'main' into project-creation-saga-idempotent
smklein Dec 29, 2022
c8fcbf5
Merge branch 'project-creation-saga-idempotent' into snapshot-delete-…
smklein Dec 29, 2022
6f8ab62
Merge branch 'snapshot-delete-saga' into snapshot-create-saga-idempotent
smklein Dec 29, 2022
92ec4a2
Merge branch 'snapshot-create-saga-idempotent' into instance-create-i…
smklein Dec 29, 2022
9a4d9d7
Merge branch 'main' into instance-create-idempotency
smklein Dec 29, 2022
69d38c3
Merge branch 'instance-create-idempotency' into instance-delete-idemp…
smklein Dec 29, 2022
cb0c74b
Merge branch 'main' into instance-create-idempotency
smklein Dec 29, 2022
4ab6de7
Merge branch 'instance-create-idempotency' into instance-delete-idemp…
smklein Dec 29, 2022
cf9ecc0
Merge branch 'instance-delete-idempotency' into disk-create-idempotency
smklein Dec 29, 2022
fe71e10
Merge branch 'disk-create-idempotency' into disk-delete-idempotency
smklein Dec 29, 2022
282cb1f
Merge branch 'main' into disk-create-idempotency
smklein Dec 30, 2022
0cc2755
Merge branch 'disk-create-idempotency' into disk-delete-idempotency
smklein Dec 30, 2022
c9378fc
Merge branch 'main' into disk-create-idempotency
smklein Jan 6, 2023
06305b2
Merge branch 'disk-create-idempotency' into disk-delete-idempotency
smklein Jan 6, 2023
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
24 changes: 14 additions & 10 deletions nexus/src/app/sagas/disk_create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,21 +617,25 @@ pub(crate) mod test {
project.identity.id
}

pub fn new_disk_create_params() -> params::DiskCreate {
params::DiskCreate {
identity: IdentityMetadataCreateParams {
name: DISK_NAME.parse().expect("Invalid disk name"),
description: "My disk".to_string(),
},
disk_source: params::DiskSource::Blank {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be expanded to create more DiskSources. There are other code paths that
we don't get coverage on for the saga when we have a Snapshot or a Global Image.
I created an issue for this: #2115

This PR can go ahead without it, as it's improving on what we have now.

block_size: params::BlockSize(512),
},
size: ByteCount::from_gibibytes_u32(1),
}
}

// Helper for creating disk create parameters
fn new_test_params(opctx: &OpContext, project_id: Uuid) -> Params {
Params {
serialized_authn: Serialized::for_opctx(opctx),
project_id,
create_params: params::DiskCreate {
identity: IdentityMetadataCreateParams {
name: DISK_NAME.parse().expect("Invalid disk name"),
description: "My disk".to_string(),
},
disk_source: params::DiskSource::Blank {
block_size: params::BlockSize(512),
},
size: ByteCount::from_gibibytes_u32(1),
},
create_params: new_disk_create_params(),
}
}

Expand Down
124 changes: 124 additions & 0 deletions nexus/src/app/sagas/disk_delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,127 @@ async fn sdd_delete_volume(
.map_err(ActionError::action_failed)?;
Ok(())
}

#[cfg(test)]
pub(crate) mod test {
use crate::{
app::saga::create_saga_dag, app::sagas::disk_delete::Params,
app::sagas::disk_delete::SagaDiskDelete, context::OpContext, db,
};
use dropshot::test_util::ClientTestContext;
use nexus_test_utils::resource_helpers::create_ip_pool;
use nexus_test_utils::resource_helpers::create_organization;
use nexus_test_utils::resource_helpers::create_project;
use nexus_test_utils::resource_helpers::DiskTest;
use nexus_test_utils_macros::nexus_test;
use omicron_common::api::external::Name;
use ref_cast::RefCast;
use std::num::NonZeroU32;
use uuid::Uuid;

type ControlPlaneTestContext =
nexus_test_utils::ControlPlaneTestContext<crate::Server>;

const ORG_NAME: &str = "test-org";
const PROJECT_NAME: &str = "springfield-squidport";

async fn create_org_and_project(client: &ClientTestContext) -> Uuid {
create_ip_pool(&client, "p0", None).await;
create_organization(&client, ORG_NAME).await;
let project = create_project(client, ORG_NAME, PROJECT_NAME).await;
project.identity.id
}

pub fn test_opctx(cptestctx: &ControlPlaneTestContext) -> OpContext {
OpContext::for_tests(
cptestctx.logctx.log.new(o!()),
cptestctx.server.apictx.nexus.datastore().clone(),
)
}

async fn create_disk(cptestctx: &ControlPlaneTestContext) -> Uuid {
let nexus = &cptestctx.server.apictx.nexus;
let opctx = test_opctx(&cptestctx);

nexus
.project_create_disk(
&opctx,
db::model::Name::ref_cast(
&Name::try_from(ORG_NAME.to_string()).unwrap(),
),
db::model::Name::ref_cast(
&Name::try_from(PROJECT_NAME.to_string()).unwrap(),
),
&crate::app::sagas::disk_create::test::new_disk_create_params(),
)
.await
.expect("Failed to create disk")
.id()
}

#[nexus_test(server = crate::Server)]
async fn test_saga_basic_usage_succeeds(
cptestctx: &ControlPlaneTestContext,
) {
DiskTest::new(cptestctx).await;

let client = &cptestctx.external_client;
let nexus = &cptestctx.server.apictx.nexus;
create_org_and_project(&client).await;
let disk_id = create_disk(&cptestctx).await;

// Build the saga DAG with the provided test parameters
let params = Params { disk_id };
let dag = create_saga_dag::<SagaDiskDelete>(params).unwrap();
let runnable_saga = nexus.create_runnable_saga(dag).await.unwrap();

// Actually run the saga
nexus.run_saga(runnable_saga).await.unwrap();
}

#[nexus_test(server = crate::Server)]
async fn test_actions_succeed_idempotently(
cptestctx: &ControlPlaneTestContext,
) {
let test = DiskTest::new(cptestctx).await;

let client = &cptestctx.external_client;
let nexus = &cptestctx.server.apictx.nexus;
create_org_and_project(&client).await;
let disk_id = create_disk(&cptestctx).await;

// Build the saga DAG with the provided test parameters
let params = Params { disk_id };
let dag = create_saga_dag::<SagaDiskDelete>(params).unwrap();

let runnable_saga =
nexus.create_runnable_saga(dag.clone()).await.unwrap();

// Cause all actions to run twice. The saga should succeed regardless!
for node in dag.get_nodes() {
nexus
.sec()
.saga_inject_repeat(
runnable_saga.id(),
node.index(),
steno::RepeatInjected {
action: NonZeroU32::new(2).unwrap(),
undo: NonZeroU32::new(1).unwrap(),
},
)
.await
.unwrap();
}

// Verify that the saga's execution succeeded.
nexus
.run_saga(runnable_saga)
.await
.expect("Saga should have succeeded");

crate::app::sagas::disk_create::test::verify_clean_slate(
&cptestctx, &test,
)
.await;
}
}