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

allow to specify reason and exitCode for adapter.stop method #2654

Merged
merged 3 commits into from
Apr 23, 2024
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
16 changes: 16 additions & 0 deletions packages/adapter/src/lib/_Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,22 @@ export interface InternalDeleteStateFromEnumOptions {
callback?: ioBroker.ErrorCallback;
}

export interface StopParameters {
/** Specify an optional exit code */
exitCode?: number;
/** Specify an optional reason for stoppage */
reason?: string;
}

export interface InternalStopParameters extends StopParameters {
/** If mode is schedule or once */
isPause?: boolean;
/** If it has a restart schedule running */
isScheduled?: boolean;
/** If alive state should be updated, if undefined defaults to true */
updateAliveState?: boolean;
}

/**
* The internal adapter config type should only be used to access config properties which are set by the adapter developers.
* Only use it like `this.config as InternalAdapterConfig`
Expand Down
92 changes: 55 additions & 37 deletions packages/adapter/src/lib/adapter/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@
IoPackageInstanceObject,
AliasTargetEntry,
InstallNodeModuleOptions,
InternalInstallNodeModuleOptions
InternalInstallNodeModuleOptions,
StopParameters,
InternalStopParameters
} from '@/lib/_Types.js';
import { UserInterfaceMessagingController } from '@/lib/adapter/userInterfaceMessagingController.js';
import { SYSTEM_ADAPTER_PREFIX } from '@iobroker/js-controller-common/constants';
Expand Down Expand Up @@ -675,7 +677,7 @@
common?: ioBroker.InstanceCommon;
private mboxSubscribed?: boolean;
/** Stop the adapter */
stop?: () => Promise<void>;
stop?: (params?: StopParameters) => Promise<void>;
version?: string;
/** Stop the adapter only defined in compact, not for external usage */
protected kill?: () => Promise<void>;
Expand Down Expand Up @@ -1534,7 +1536,7 @@
* ...
* }
* ```

Check warning on line 1539 in packages/adapter/src/lib/adapter/adapter.ts

View workflow job for this annotation

GitHub Actions / Eslint

Expected JSDoc block to be aligned
* @param featureName the name of the feature to check
* @returns true/false if the feature is in the list of supported features
*/
Expand Down Expand Up @@ -2135,12 +2137,15 @@
});
}

private async _stop(
isPause?: boolean,
isScheduled?: boolean,
exitCode?: number,
updateAliveState?: boolean
): Promise<void> {
/**
* Stop an instance gracefully
*
* @param options information about the stoppage
*/
private async _stop(options: InternalStopParameters = {}): Promise<void> {
const { isPause, isScheduled, reason } = options;
let { exitCode, updateAliveState } = options;

exitCode = exitCode || (isScheduled ? EXIT_CODES.START_IMMEDIATELY_AFTER_STOP : 0);
if (updateAliveState === undefined) {
updateAliveState = true;
Expand All @@ -2153,7 +2158,7 @@
this._reportInterval = null;
const id = `system.adapter.${this.namespace}`;

const finishUnload = (): void => {
const finishUnload = async (): Promise<void> => {
if (this._timers.size) {
this._timers.forEach(timer => clearTimeout(timer));
this._timers.clear();
Expand All @@ -2176,19 +2181,18 @@

if (this.#states && updateAliveState) {
this.outputCount++;
this.#states.setState(`${id}.alive`, { val: false, ack: true, from: id }, () => {
if (!isPause) {
this._logger.info(`${this.namespaceLog} terminating`);
}
await this.#states.setState(`${id}.alive`, { val: false, ack: true, from: id });
if (!isPause) {
this._logger.info(`${this.namespaceLog} terminating`);
}

// To this moment, the class could be destroyed
this.terminate(exitCode);
});
// To this moment, the class could be destroyed
this.terminate(reason, exitCode);
} else {
if (!isPause) {
this._logger.info(`${this.namespaceLog} terminating`);
}
this.terminate(exitCode);
this.terminate(reason, exitCode);
}
};

Expand Down Expand Up @@ -2251,7 +2255,7 @@
* Reads the file certificate from a given path and adds a file watcher to restart adapter on cert changes
* if a cert is passed it is returned as it is
*
* @param cert
* @param cert cert or path to cert
*/
private _readFileCertificate(cert: string): string {
if (typeof cert === 'string') {
Expand All @@ -2265,7 +2269,7 @@
this._logger.warn(
`${this.namespaceLog} New certificate "${filename}" detected. Restart adapter`
);
setTimeout(() => this._stop(false, true), 2000);
setTimeout(() => this._stop({ isPause: false, isScheduled: true }), 2_000);
});
}
} catch (e) {
Expand Down Expand Up @@ -2859,8 +2863,13 @@
*
* @param id of the object
* @param obj The object to set
<<<<<<< HEAD
* @param [options] additional options
* @param [callback] optional callback function
=======
* @param options optional user context

Check warning on line 2870 in packages/adapter/src/lib/adapter/adapter.ts

View workflow job for this annotation

GitHub Actions / Eslint

Duplicate @param "options"
* @param callback optional callback
>>>>>>> 0e706f4213e588a7efda7be7732e25c7157e0b2c
*/
private async _setObjectWithDefaultValue(
id: string,
Expand Down Expand Up @@ -3679,7 +3688,7 @@
* @param design name of the design
* @param search name of the view
* @param params object containing startkey: first id to include in result; endkey: last id to include in result
* @param options
* @param options additional objects, e.g. for permissions
* @param callback return result
* ```js
* function (err, doc) {
Expand Down Expand Up @@ -3788,9 +3797,9 @@
* It is required, that ID consists namespace in startkey and endkey. E.g. `{startkey: 'hm-rpc.' + adapter.instance + '.', endkey: 'hm-rpc.' + adapter.instance + '.\u9999'}`
* to get all objects of the instance.
*
* @param params
* @param options
* @param callback
* @param params startkey and endkey information
* @param options additional options, e.g. for permissions
* @param callback optional callback
* ```js
* function (err, res) {
* if (res && res.rows) {
Expand Down Expand Up @@ -7108,10 +7117,10 @@
* Async version of sendTo
* As we have a special case (first arg can be error or result, we need to promisify manually)
*
* @param instanceName
* @param command
* @param message
* @param options
* @param instanceName name of the instance
* @param command command to send
* @param message message to send
* @param options additional options, e.g. for permissions
*/
sendToAsync(instanceName: unknown, command: unknown, message?: unknown, options?: unknown): any {
return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -9816,8 +9825,7 @@
* ```
*
* @param pattern string in form 'adapter.0.*'. Must be the same as subscribe.
* @param options]optional argument to describe the user context
* @param options
* @param options optional argument to describe the user context
* @param callback return result
* ```js
* function (err) {}
Expand Down Expand Up @@ -9966,7 +9974,7 @@
*
* @param pattern string in form 'adapter.0.*' or like this. Only string allowed
* @param options optional argument to describe the user context
* @param callback
* @param callback optional callback
*/
subscribeStates(pattern: unknown, options: unknown, callback?: unknown): any {
if (typeof options === 'function') {
Expand Down Expand Up @@ -10002,7 +10010,7 @@
*
* @param pattern string in form 'adapter.0.*'. Must be the same as subscribe.
* @param options optional argument to describe the user context
* @param callback
* @param callback optional callback
*/
unsubscribeStates(pattern: unknown, options: unknown, callback?: unknown): any {
if (typeof options === 'function') {
Expand Down Expand Up @@ -10664,7 +10672,12 @@
}
// by deletion of state, stop this instance
if (sigKillVal !== process.pid && !this._config.forceIfDisabled) {
this._stop(false, false, EXIT_CODES.ADAPTER_REQUESTED_TERMINATION, false);
this._stop({
isPause: false,
isScheduled: false,
exitCode: EXIT_CODES.ADAPTER_REQUESTED_TERMINATION,
updateAliveState: false
});
setTimeout(() => this.terminate(EXIT_CODES.ADAPTER_REQUESTED_TERMINATION), 4000);
}
}
Expand Down Expand Up @@ -11347,12 +11360,12 @@
// @ts-expect-error
adapterConfig.common.mode === 'once'
) {
this.stop = () => this._stop(true);
this.stop = params => this._stop({ ...params, isPause: true });
} else if (this.startedInCompactMode) {
this.stop = () => this._stop(false);
this.stop = params => this._stop({ ...params, isPause: false });
this.kill = this.stop;
} else {
this.stop = () => this._stop(false);
this.stop = params => this._stop({ ...params, isPause: false });
}

// Monitor logging state
Expand Down Expand Up @@ -11519,7 +11532,7 @@
);
this._restartScheduleJob = this._schedule.scheduleJob(adapterConfig.common.restartSchedule, () => {
this._logger.info(`${this.namespaceLog} Scheduled restart.`);
this._stop(false, true);
this._stop({ isPause: false, isScheduled: true });
});
}
}
Expand Down Expand Up @@ -11620,7 +11633,12 @@
}

try {
this._stop(false, false, EXIT_CODES.UNCAUGHT_EXCEPTION, false);
this._stop({
isPause: false,
isScheduled: false,
exitCode: EXIT_CODES.UNCAUGHT_EXCEPTION,
updateAliveState: false
});
setTimeout(() => this.terminate(EXIT_CODES.UNCAUGHT_EXCEPTION), 1_000);
} catch (err) {
this._logger.error(`${this.namespaceLog} exception by stop: ${err ? err.message : err}`);
Expand Down
Loading