Skip to content

Commit

Permalink
merge main
Browse files Browse the repository at this point in the history
  • Loading branch information
raosan committed Jan 24, 2025
2 parents a910652 + ffc8e9b commit 9d22631
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 28 deletions.
8 changes: 8 additions & 0 deletions docs/src/pages/guides/cli-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,14 @@ By default Monika will follow redirects 21 times. You can set the value of `--fo
monika --follow-redirects 0 # disable following redirects
```

## SKIP

For applications where a startup message or startup notification is not desired, you can skip monika startup message using `--skip-start-message`.

```bash
monika --skip-start-message
```

## STUN

By default monika will continuously check the [STUN](https://en.wikipedia.org/wiki/STUN) server every 20 second intervals. Continuously STUN checking ensures that connectivity to the outside world is guaranteed. When STUN checking fails, Monika assumes the network is down and probing will be paused.
Expand Down
37 changes: 15 additions & 22 deletions src/commands/monika.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,13 @@ import { Command, Errors } from '@oclif/core'
import pEvent from 'p-event'

import type { ValidatedConfig } from '../interfaces/config.js'
import type { Probe } from '../interfaces/probe.js'

import {
getValidatedConfig,
isSymonModeFrom,
initConfig,
} from '../components/config/index.js'
import { createConfig } from '../components/config/create.js'
import { sortProbes } from '../components/config/sort.js'
import { printAllLogs } from '../components/logger/index.js'
import { flush } from '../components/logger/flush.js'
import { closeLog } from '../components/logger/history.js'
Expand All @@ -44,10 +42,10 @@ import { sendMonikaStartMessage } from '../components/notification/start-message
import { printSummary } from '../components/summary.js'
import { getContext, setContext } from '../context/index.js'
import events from '../events/index.js'
import { type MonikaFlags, sanitizeFlags, flags } from '../flag.js'
import { sanitizeFlags, flags } from '../flag.js'
import { savePidFile } from '../jobs/summary-notification.js'
import initLoaders from '../loaders/index.js'
import { sanitizeProbe, startProbing } from '../looper/index.js'
import { startProbing } from '../looper/index.js'
import SymonClient from '../symon/index.js'
import { getEventEmitter } from '../utils/events.js'
import { log } from '../utils/pino.js'
Expand All @@ -58,6 +56,7 @@ import {
close as closeSentry,
flush as flushSentry,
} from '@sentry/node'
import { getProbes } from '../components/config/probe.js'

const em = getEventEmitter()
let symonClient: SymonClient
Expand Down Expand Up @@ -145,7 +144,9 @@ export default class Monika extends Command {
}

await initLoaders(flags, this.config)
await logRunningInfo({ isSymonMode, isVerbose: flags.verbose })
if (flags['skip-start-message'] === false) {
await logRunningInfo({ isSymonMode, isVerbose: flags.verbose })
}

if (isSymonMode) {
symonClient = new SymonClient(flags)
Expand All @@ -164,13 +165,14 @@ export default class Monika extends Command {

for (;;) {
const config = getValidatedConfig()
const probes = getProbes({ config, flags })
const probes = getProbes()

// emit the sanitized probe
em.emit(events.config.sanitized, probes)
// save some data into files for later
savePidFile(flags.config, config)
deprecationHandler(config)

logStartupMessage({
config,
flags,
Expand All @@ -190,9 +192,13 @@ export default class Monika extends Command {
break
}

sendMonikaStartMessage(notifications).catch((error) =>
log.error(error.message)
)
if (flags['skip-start-message'] === false) {
// if skipping, skip also startup notification
sendMonikaStartMessage(notifications).catch((error) =>
log.error(error.message)
)
}

// schedule status update notification
scheduleSummaryNotification({ config, flags })

Expand Down Expand Up @@ -232,19 +238,6 @@ async function logRunningInfo({ isVerbose, isSymonMode }: RunningInfoParams) {
}
}

type GetProbesParams = {
config: ValidatedConfig
flags: MonikaFlags
}

function getProbes({ config, flags }: GetProbesParams): Probe[] {
const sortedProbes = sortProbes(config.probes, flags.id)

return sortedProbes.map((probe: Probe) =>
sanitizeProbe(isSymonModeFrom(flags), probe)
)
}

function deprecationHandler(config: ValidatedConfig): ValidatedConfig {
const showDeprecateMsg: Record<'query', boolean> = {
query: false,
Expand Down
95 changes: 95 additions & 0 deletions src/components/config/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,3 +308,98 @@ describe('Setup Config', () => {
).not.undefined
})
})

describe('sanitizeConfig', () => {
beforeEach(() => {
resetContext()
})

it('should sanitize probes in normal mode', () => {
// arrange
const config = {
probes: [
{
id: '1',
name: 'Test probe',
interval: 1000,
requests: [
{
url: 'https://example.com',
body: '',
followRedirects: 21,
timeout: 1000,
},
],
alerts: [],
},
],
}

// act
const result = sanitizeConfig(config)

// assert
expect(result.probes[0].alerts).to.have.lengthOf(1)
expect(result.probes[0].alerts[0]).to.include({
assertion: '',
message: 'Probe not accessible',
})
})

it('should sanitize probes in Symon mode', () => {
// arrange
setContext({
flags: sanitizeFlags({
symonKey: 'test-key',
symonUrl: 'https://example.com',
}),
})
const config = {
probes: [
{
id: '1',
name: 'Test probe',
interval: 30_000,
requests: [
{
url: 'https://example.com',
body: '',
followRedirects: 21,
timeout: 1000,
},
],
alerts: [],
},
],
}

// act
const result = sanitizeConfig(config)

// assert
expect(result.probes[0].alerts).to.have.lengthOf(0)
expect(result.probes[0].id).to.equal('1')
expect(result.probes[0].interval).to.equal(30_000)
})

it('should preserve existing config properties', () => {
// arrange
const config: Config = {
version: '1.0.0',
notifications: [{ id: 'test-1', type: 'smtp' }],
certificate: {
domains: ['example.com'],
reminder: 15,
},
probes: [],
}

// act
const result = sanitizeConfig(config)

// assert
expect(result.version).to.equal('1.0.0')
expect(result.notifications).to.deep.equal([{ id: 'test-1', type: 'smtp' }])
expect(result.certificate?.reminder).to.equal(15)
})
})
9 changes: 7 additions & 2 deletions src/components/config/sanitize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@ import type {
Config,
ValidatedConfig,
} from '../../interfaces/config.js'
import { sanitizeProbe } from '../../looper/index.js'
import { isSymonModeFrom } from './index.js'
import { getContext } from '../../context/index.js'

const DEFAULT_TLS_EXPIRY_REMINDER_DAYS = 30
const DEFAULT_STATUS_NOTIFICATION = '0 6 * * *'

export function sanitizeConfig(config: Config): ValidatedConfig {
const { certificate, notifications = [], version } = config
const sanitizedConfigWithoutVersion = {
const isSymonMode = isSymonModeFrom(getContext().flags)
const { certificate, notifications = [], version, probes } = config
const sanitizedConfigWithoutVersion: Omit<ValidatedConfig, 'version'> = {
...config,
probes: probes.map((probe) => sanitizeProbe(isSymonMode, probe)),
certificate: sanitizeCertificate(certificate),
notifications,
'status-notification':
Expand Down
37 changes: 37 additions & 0 deletions src/components/logger/startup-message.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -531,4 +531,41 @@ describe('Startup message', () => {
expect(logMessage).include('config file')
})
})

describe('Skip startup test', () => {
it('should show startup message', () => {
// act
logStartupMessage({
config: defaultConfig,
flags: sanitizeFlags({
config: ['./test/testConfigs/simple-1p-1n.yaml'],
symonKey: '',
symonUrl: '',
verbose: false,
}),
isFirstRun: false,
})

// assert
expect(logMessage).include('Using config file:')
})

it('should not startup message', () => {
// act
logStartupMessage({
config: defaultConfig,
flags: sanitizeFlags({
'skip-start-message': true,
config: ['./test/testConfigs/simple-1p-1n.yaml'],
symonKey: '',
symonUrl: '',
verbose: false,
}),
isFirstRun: false,
})

// assert
expect(logMessage).not.include('Using config file:')
})
})
})
17 changes: 16 additions & 1 deletion src/components/logger/startup-message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,12 @@ type LogStartupMessage = {
config: ValidatedConfig
flags: Pick<
MonikaFlags,
'config' | 'symonKey' | 'symonUrl' | 'verbose' | 'native-fetch'
| 'config'
| 'symonKey'
| 'symonUrl'
| 'verbose'
| 'native-fetch'
| 'skip-start-message'
>
isFirstRun: boolean
}
Expand All @@ -49,6 +54,11 @@ export function logStartupMessage({
flags,
isFirstRun,
}: LogStartupMessage): void {
if (flags['skip-start-message'] === true) {
// if skipping, wont have any message
return
}

if (isSymonModeFrom(flags)) {
log.info('Running in Symon mode')
return
Expand Down Expand Up @@ -82,6 +92,11 @@ function generateStartupMessage({

let startupMessage = ''

if (flags['skip-start-message'] === true) {
// if skipping, wont have any message
return ''
}

// warn if config is empty
if (!hasNotification) {
startupMessage += generateEmptyNotificationMessage()
Expand Down
8 changes: 6 additions & 2 deletions src/flag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export type MonikaFlags = {
retryInitialDelayMs: number
retryMaxDelayMs: number
sitemap?: string
'skip-start-message'?: boolean
'status-notification'?: string
stun: number
summary: boolean
Expand Down Expand Up @@ -98,8 +99,8 @@ export const monikaFlagsDefaultValue: MonikaFlags = {
repeat: 0,
retryInitialDelayMs: 2000,
retryMaxDelayMs: 30_000,
// default is 20s interval lookup
stun: 20,
'skip-start-message': false,
stun: 20, // default is 20s interval lookup
summary: false,
'symon-api-version': SYMON_API_VERSION.v1,
symonGetProbesIntervalMs: 60_000,
Expand Down Expand Up @@ -244,6 +245,9 @@ export const flags = {
description: 'Run Monika using a Sitemap xml file.',
exclusive: ['har', 'insomnia', 'postman', 'text'],
}),
'skip-start-message': Flags.boolean({
description: 'Skip Monika startup message',
}),
'status-notification': Flags.string({
description: 'Cron syntax for status notification schedule',
}),
Expand Down
7 changes: 6 additions & 1 deletion src/looper/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,15 @@ export const FAILED_REQUEST_ASSERTION = {
}

function addFailedRequestAssertions(assertions: ProbeAlert[]) {
// create uuid seed from FAILED_REQUEST_ASSERTION
// to make the id deterministic, consistent, and testable
const idSeed = Buffer.from(JSON.stringify(FAILED_REQUEST_ASSERTION))
return [
...assertions,
{
id: uuid(),
id: uuid({
random: idSeed,
}),
...FAILED_REQUEST_ASSERTION,
},
]
Expand Down

0 comments on commit 9d22631

Please sign in to comment.