Skip to content

Commit

Permalink
feat(station): store monitoring data in ExternalCanister (#430)
Browse files Browse the repository at this point in the history
```
Benchmark: list_external_canisters_with_all_statuses
  total:
    instructions: 231.11 M (regressed by 9.11%)
    heap_increase: 0 pages (no change)
    stable_memory_increase: 0 pages (no change)
```
  • Loading branch information
jedna authored Nov 22, 2024
1 parent a026ed5 commit 5dc4772
Show file tree
Hide file tree
Showing 18 changed files with 173 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ const submit = async (input: CanisterMonitorModel) => {
canister_id: assertAndReturn(input.canisterId, 'canisterId'),
kind: {
Start: {
strategy: assertAndReturn(input.strategy, 'strategy'),
funding_strategy: assertAndReturn(input.strategy, 'strategy'),
cycle_obtain_strategy: [],
},
},
});
Expand Down
8 changes: 6 additions & 2 deletions apps/wallet/src/generated/station/station.did
Original file line number Diff line number Diff line change
Expand Up @@ -796,8 +796,10 @@ type MonitorExternalCanisterStrategyInput = variant {

// The input type for specifying the strategy for monitoring an external canister.
type MonitorExternalCanisterStartInput = record {
// The amount of cycles to send to the canister.
strategy : MonitorExternalCanisterStrategyInput;
// The strategy for funding the canister.
funding_strategy : MonitorExternalCanisterStrategyInput;
// The strategy for obtaining cycles for the funding operation.
cycle_obtain_strategy: opt CycleObtainStrategyInput;
};

// The operation kind for monitoring an external canister in the station.
Expand Down Expand Up @@ -2595,6 +2597,8 @@ type ExternalCanister = record {
created_at : TimestampRFC3339;
// The time at which the canister was last modified, if available.
modified_at : opt TimestampRFC3339;
// Monitoring configuration for the canister.
monitoring : opt MonitorExternalCanisterStartInput;
};

// The state of the external canister.
Expand Down
4 changes: 3 additions & 1 deletion apps/wallet/src/generated/station/station.did.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ export interface ExternalCanister {
'created_at' : TimestampRFC3339,
'request_policies' : ExternalCanisterRequestPolicies,
'state' : ExternalCanisterState,
'monitoring' : [] | [MonitorExternalCanisterStartInput],
}
export interface ExternalCanisterCallPermission {
'execution_method' : string,
Expand Down Expand Up @@ -840,7 +841,8 @@ export type MonitorExternalCanisterOperationKind = {
} |
{ 'Stop' : null };
export interface MonitorExternalCanisterStartInput {
'strategy' : MonitorExternalCanisterStrategyInput,
'cycle_obtain_strategy' : [] | [CycleObtainStrategyInput],
'funding_strategy' : MonitorExternalCanisterStrategyInput,
}
export type MonitorExternalCanisterStrategyInput = { 'Always' : bigint } |
{ 'BelowThreshold' : MonitoringExternalCanisterCyclesThresholdInput } |
Expand Down
12 changes: 7 additions & 5 deletions apps/wallet/src/generated/station/station.did.js
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,10 @@ export const idlFactory = ({ IDL }) => {
'module_checksum' : Sha256Hash,
'arg_checksum' : IDL.Opt(Sha256Hash),
});
const CycleObtainStrategyInput = IDL.Variant({
'Disabled' : IDL.Null,
'MintFromNativeToken' : IDL.Record({ 'account_id' : UUID }),
});
const MonitoringExternalCanisterCyclesThresholdInput = IDL.Record({
'fund_cycles' : IDL.Nat,
'min_cycles' : IDL.Nat,
Expand All @@ -328,7 +332,8 @@ export const idlFactory = ({ IDL }) => {
'BelowEstimatedRuntime' : MonitoringExternalCanisterEstimatedRuntimeInput,
});
const MonitorExternalCanisterStartInput = IDL.Record({
'strategy' : MonitorExternalCanisterStrategyInput,
'cycle_obtain_strategy' : IDL.Opt(CycleObtainStrategyInput),
'funding_strategy' : MonitorExternalCanisterStrategyInput,
});
const MonitorExternalCanisterOperationKind = IDL.Variant({
'Start' : MonitorExternalCanisterStartInput,
Expand Down Expand Up @@ -504,10 +509,6 @@ export const idlFactory = ({ IDL }) => {
'identities' : IDL.Opt(IDL.Vec(IDL.Principal)),
});
const EditUserOperation = IDL.Record({ 'input' : EditUserOperationInput });
const CycleObtainStrategyInput = IDL.Variant({
'Disabled' : IDL.Null,
'MintFromNativeToken' : IDL.Record({ 'account_id' : UUID }),
});
const ManageSystemInfoOperationInput = IDL.Record({
'name' : IDL.Opt(IDL.Text),
'cycle_obtain_strategy' : IDL.Opt(CycleObtainStrategyInput),
Expand Down Expand Up @@ -933,6 +934,7 @@ export const idlFactory = ({ IDL }) => {
'created_at' : TimestampRFC3339,
'request_policies' : ExternalCanisterRequestPolicies,
'state' : ExternalCanisterState,
'monitoring' : IDL.Opt(MonitorExternalCanisterStartInput),
});
const GetExternalCanisterResult = IDL.Variant({
'Ok' : IDL.Record({
Expand Down
1 change: 1 addition & 0 deletions apps/wallet/src/locales/en.locale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ export default {
start_monitoring: 'Start Monitoring',
monitor: {
title: 'Monitor cycles',
stop_title: 'Stop Monitoring',
strategy: {
label: 'Monitoring strategy',
BelowEstimatedRuntime: 'Below estimated runtime',
Expand Down
1 change: 1 addition & 0 deletions apps/wallet/src/locales/fr.locale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,7 @@ export default {
start_monitoring: 'Démarrer la surveillance',
monitor: {
title: 'Contrôler les cycles',
stop_title: 'Arrêter la surveillance',
strategy: {
label: 'Stratégie de suivi',
BelowEstimatedRuntime: "Durée d'exécution inférieure à l'estimation",
Expand Down
1 change: 1 addition & 0 deletions apps/wallet/src/locales/pt.locale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,7 @@ export default {
start_monitoring: 'Iniciar monitoramento',
monitor: {
title: 'Monitorizar ciclos',
stop_title: 'Parar monitorização',
strategy: {
label: 'Estratégia de controlo',
BelowEstimatedRuntime: 'Tempo de execução inferior ao estimado',
Expand Down
69 changes: 61 additions & 8 deletions apps/wallet/src/pages/ExternalCanisterDetailPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@
<VListItem @click="dialogs.settings = true">
<VListItemTitle class="d-flex flex-nowrap ga-2">
<div class="flex-grow-1">{{ $t('external_canisters.configuration') }}</div>
<div><VIcon :icon="mdiDatabase" size="x-small" /></div>
<div>
<VIcon :icon="mdiDatabase" size="x-small" />
</div>
</VListItemTitle>
</VListItem>
<VListItem
Expand All @@ -87,14 +89,18 @@
>
<VListItemTitle class="d-flex flex-nowrap ga-2">
<div class="flex-grow-1">{{ $t('external_canisters.ic_settings') }}</div>
<div><VIcon :icon="mdiInfinity" size="x-small" /></div>
<div>
<VIcon :icon="mdiInfinity" size="x-small" />
</div>
</VListItemTitle>
</VListItem>
<VDivider />
<VListItem @click="dialogs.unlink = true">
<VListItemTitle color="warning" class="d-flex flex-nowrap ga-2 text-error">
<div class="flex-grow-1">{{ $t('external_canisters.unlink') }}</div>
<div><VIcon :icon="mdiDatabaseOff" size="x-small" /></div>
<div>
<VIcon :icon="mdiDatabaseOff" size="x-small" />
</div>
</VListItemTitle>
</VListItem>
</VList>
Expand Down Expand Up @@ -289,6 +295,11 @@
</VListItem>
<VListItem class="pt-0 px-0">
<VListItemTitle class="font-weight-bold">
<VIcon
v-if="canister.monitoring.length"
:icon="mdiBatteryChargingMedium"
:tooltip="$t(`external_canisters.cycles`)"
/>
{{ $t(`external_canisters.cycles`) }}
<template v-if="privileges.can_fund">
<CanisterTopUpDialog
Expand All @@ -306,24 +317,38 @@
color="default"
variant="tonal"
class="ml-1 px-2"
:append-icon="mdiDatabaseArrowUp"
:append-icon="mdiBatteryArrowUpOutline"
@click="dialogs.topUp = true"
>
{{ $t('external_canisters.top_up') }}
</VBtn>
<VBtn
:disabled="!canisterDetails.status.value"
v-if="!canister.monitoring.length"
:disabled="canisterDetails.status.loading"
size="small"
density="compact"
color="default"
variant="tonal"
class="ml-1 px-2"
:append-icon="mdiDatabaseArrowUp"
:append-icon="mdiBatterySyncOutline"
@click="dialogs.monitor = true"
>
{{ $t('external_canisters.monitor.title') }}
</VBtn>
<VBtn
v-if="canister.monitoring.length"
:disabled="canisterDetails.status.loading"
size="small"
density="compact"
color="default"
variant="tonal"
class="ml-1 px-2"
:append-icon="mdiBatteryOffOutline"
@click="removeMonitoring"
>
{{ $t('external_canisters.monitor.stop_title') }}
</VBtn>
</template>
</VListItemTitle>
<VListItemSubtitle>
Expand Down Expand Up @@ -397,9 +422,12 @@
<script lang="ts" setup>
import { Principal } from '@dfinity/principal';
import {
mdiBatteryArrowUpOutline,
mdiBatteryChargingMedium,
mdiBatteryOffOutline,
mdiBatterySyncOutline,
mdiContentCopy,
mdiDatabase,
mdiDatabaseArrowUp,
mdiDatabaseCog,
mdiDatabaseOff,
mdiInfinity,
Expand Down Expand Up @@ -464,7 +492,11 @@ import { RequestDomains } from '~/types/station.types';
import { copyToClipboard } from '~/utils/app.utils';
import { hasRequiredPrivilege } from '~/utils/auth.utils';
import { fetchCanisterIdlFromMetadata } from '~/utils/didc.utils';
import { debounce } from '~/utils/helper.utils';
import { assertAndReturn, debounce } from '~/utils/helper.utils';
import {
useOnFailedOperation,
useOnSuccessfulOperation,
} from '~/composables/notifications.composable.ts';
const props = withDefaults(defineProps<PageProps>(), {
title: undefined,
Expand Down Expand Up @@ -654,4 +686,25 @@ const loadExternalCanister = async (): Promise<void> => {
logger.error('Failed to load external canister', error);
}
};
const removeMonitoring = async (): Promise<void> => {
try {
canisterDetails.value.status.loading = true;
const request = await station.service.monitorExternalCanister({
canister_id: assertAndReturn(Principal.fromText(currentRouteCanisterId.value), 'canisterId'),
kind: {
Stop: null,
},
});
useOnSuccessfulOperation(request);
} catch (error) {
logger.error('Failed to submit monitoring request', error);
useOnFailedOperation();
} finally {
canisterDetails.value.status.loading = false;
}
};
</script>
8 changes: 6 additions & 2 deletions core/station/api/spec.did
Original file line number Diff line number Diff line change
Expand Up @@ -796,8 +796,10 @@ type MonitorExternalCanisterStrategyInput = variant {

// The input type for specifying the strategy for monitoring an external canister.
type MonitorExternalCanisterStartInput = record {
// The amount of cycles to send to the canister.
strategy : MonitorExternalCanisterStrategyInput;
// The strategy for funding the canister.
funding_strategy : MonitorExternalCanisterStrategyInput;
// The strategy for obtaining cycles for the funding operation.
cycle_obtain_strategy: opt CycleObtainStrategyInput;
};

// The operation kind for monitoring an external canister in the station.
Expand Down Expand Up @@ -2595,6 +2597,8 @@ type ExternalCanister = record {
created_at : TimestampRFC3339;
// The time at which the canister was last modified, if available.
modified_at : opt TimestampRFC3339;
// Monitoring configuration for the canister.
monitoring : opt MonitorExternalCanisterStartInput;
};

// The state of the external canister.
Expand Down
10 changes: 6 additions & 4 deletions core/station/api/src/external_canister.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
AllowDTO, CanisterInstallMode, ChangeMetadataDTO, MetadataDTO, PaginationInput,
RequestPolicyRuleDTO, Sha256HashDTO, SortDirection, TimestampRfc3339, UuidDTO,
AllowDTO, CanisterInstallMode, ChangeMetadataDTO, CycleObtainStrategyInput, MetadataDTO,
PaginationInput, RequestPolicyRuleDTO, Sha256HashDTO, SortDirection, TimestampRfc3339, UuidDTO,
ValidationMethodResourceTargetDTO,
};
use candid::{CandidType, Deserialize, Nat, Principal};
Expand Down Expand Up @@ -285,6 +285,7 @@ pub struct ExternalCanisterDTO {
pub request_policies: ExternalCanisterRequestPoliciesDTO,
pub created_at: TimestampRfc3339,
pub modified_at: Option<TimestampRfc3339>,
pub monitoring: Option<MonitorExternalCanisterStartInput>,
}

#[derive(CandidType, serde::Serialize, Deserialize, Debug, Clone)]
Expand Down Expand Up @@ -398,15 +399,16 @@ pub struct MonitoringExternalCanisterCyclesThresholdInput {
}

#[derive(CandidType, serde::Serialize, Deserialize, Debug, Clone)]
pub enum MonitorExternalCanisterStartStrategyDTO {
pub enum MonitorExternalCanisterStrategyDTO {
Always(u128),
BelowThreshold(MonitoringExternalCanisterCyclesThresholdInput),
BelowEstimatedRuntime(MonitoringExternalCanisterEstimatedRuntimeInput),
}

#[derive(CandidType, serde::Serialize, Deserialize, Debug, Clone)]
pub struct MonitorExternalCanisterStartInput {
pub strategy: MonitorExternalCanisterStartStrategyDTO,
pub funding_strategy: MonitorExternalCanisterStrategyDTO,
pub cycle_obtain_strategy: Option<CycleObtainStrategyInput>,
}

#[derive(CandidType, serde::Serialize, Deserialize, Debug, Clone)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ impl Execute for MonitorExternalCanisterRequestExecute<'_, '_> {
match &self.operation.kind {
MonitorExternalCanisterOperationKind::Start(input) => {
self.external_canister_service
.canister_monitor_start(self.operation.canister_id, input.strategy.clone())
.canister_monitor_start(
self.operation.canister_id,
input.funding_strategy.clone(),
)
.map_err(|e| RequestExecuteError::Failed {
reason: format!("Failed to monitor canister: {}", e),
})?;
Expand Down
Loading

0 comments on commit 5dc4772

Please sign in to comment.