Skip to content

Commit

Permalink
Add plugin interface
Browse files Browse the repository at this point in the history
  • Loading branch information
Hexagon committed Apr 25, 2024
1 parent 5f95842 commit 43e45e2
Show file tree
Hide file tree
Showing 19 changed files with 231 additions and 96 deletions.
2 changes: 1 addition & 1 deletion application.meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

const Application = {
name: "pup",
version: "1.0.0-rc.32",
version: "1.0.0-rc.33",
url: "jsr:@pup/pup@$VERSION",
canary_url: "https://raw.githubusercontent.com/Hexagon/pup/main/pup.ts",
deno: null, /* Minimum stable version of Deno required to run Pup (without --unstable-* flags) */
Expand Down
7 changes: 4 additions & 3 deletions deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pup/pup",
"version": "1.0.0-rc.32",
"version": "1.0.0-rc.33",

"exports": {
".": "./pup.ts",
Expand Down Expand Up @@ -41,17 +41,18 @@
"imports": {
"@cross/deepmerge": "jsr:@cross/deepmerge@^1.0.0",
"@cross/env": "jsr:@cross/env@^1.0.2",
"@cross/fs": "jsr:@cross/fs@^0.0.9",
"@cross/fs": "jsr:@cross/fs@^0.0.10",
"@cross/jwt": "jsr:@cross/jwt@^0.4.1",
"@cross/runtime": "jsr:@cross/runtime@^1.0.0",
"@cross/service": "jsr:@cross/service@^1.0.3",
"@cross/test": "jsr:@cross/test@^0.0.9",
"@cross/utils": "jsr:@cross/utils@^0.11.0",
"@cross/utils": "jsr:@cross/utils@^0.12.0",
"@hexagon/croner": "jsr:@hexagon/croner@^8.0.1",
"@oak/oak": "jsr:@oak/oak@^15.0.0",
"@pup/api-client": "jsr:@pup/api-client@^1.0.0",
"@pup/api-definitions": "jsr:@pup/api-definitions@^1.0.0",
"@pup/common": "jsr:@pup/common@^1.0.0",
"@pup/plugin": "jsr:@pup/plugin@^1.0.0",
"@std/assert": "jsr:@std/assert@^0.223.0",
"@std/async": "jsr:@std/async@^0.223.0",
"@std/encoding": "jsr:@std/encoding@^0.223.0",
Expand Down
2 changes: 1 addition & 1 deletion docs/src/_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"description": "Universal Process Manager"
},
"substitute": {
"$PUP_VERSION": "1.0.0-rc.32"
"$PUP_VERSION": "1.0.0-rc.33"
},
"top_links": [
{
Expand Down
6 changes: 6 additions & 0 deletions docs/src/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ nav_order: 13

All notable changes to this project will be documented in this section.

## [1.0.0-rc.33] - 2024-04-25

## Added

- Added support for Plugins based on <https://github.com/hexagon/pup-plugin>

## [1.0.0-rc.32] - 2024-04-23

**Breaking:** Any plugins or packages importing telemetry from `@pup/pup/telemetry.ts` needs to be updated to import from `@pup/telemetry` instead.
Expand Down
4 changes: 2 additions & 2 deletions docs/src/examples/basic-webinterface/pup.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@
],
"plugins": [
{
// Use full uri to plugin, e.g. jsr:@pup/pup@$VERSION/plugins/web-interface
"url": "../../plugins/web-interface/mod.ts",
// Use full uri to plugin, e.g. jsr:@pup/plugin-web-interface
"url": "../../../pup-plugin-web-interface/mod.ts",
"options": {
"port": 8002
}
Expand Down
8 changes: 0 additions & 8 deletions docs/src/examples/cluster/pup.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,5 @@
"cron": "*/20 * * * * *",
"restartDelayMs": 3000
}
],
"plugins": [
{
"url": "../../plugins/web-interface/mod.ts",
"options": {
"port": 5000
}
}
]
}
2 changes: 1 addition & 1 deletion docs/src/examples/docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM denoland/deno:debian-1.31.1
FROM denoland/deno:debian-1.42.4

# Copy application
RUN mkdir /app
Expand Down
2 changes: 2 additions & 0 deletions docs/src/examples/plugins/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ nav_order: 2

# Plugin development

**Note! This is outdated, need to be updated asap.** Refer to <https://github.com/hexagon/pup-plugin> for up-to-date info.

---

### Getting started
Expand Down
2 changes: 2 additions & 0 deletions docs/src/examples/splunk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ nav_order: 6

# Server with Splunk Logging

**Note! This is outdated, need to be updated asap.**

---

The example at [/docs/src/examples/splunk](https://github.com/Hexagon/pup/tree/main/docs/src/examples/splunk) runs a Deno server script as a continuously monitored and restarted process, logging all
Expand Down
44 changes: 17 additions & 27 deletions docs/src/examples/telemetry/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,69 +13,59 @@ nav_order: 6
This example demonstrates the telemetry feature of pup, which ...

- Automatically report process metrics, such as memory usage, back to Pup.
- Opens a channel for managed processes to communicate using a slow polling IPC
mechanism.
- Opens a channel for managed processes to communicate using a slow polling IPC mechanism.

The simplest use case, where you only want to monitor your client metrics is
used like this:
The simplest use case, where you only want to monitor your client metrics is used like this:

```ts
import { PupTelemetry } from "jsr:@pup/telemetry";
import { PupTelemetry } from "jsr:@pup/telemetry"

new PupTelemetry();
new PupTelemetry()

// The rest of your application
```

This will make your process report memory usage and current working directory
back to the Pup main process, no further configuration needed. Now you can see
memory usage for all processes running telemetry (including main process) using
`status` on the cli.
This will make your process report memory usage and current working directory back to the Pup main process, no further configuration needed. Now you can see memory usage for all processes running
telemetry (including main process) using `status` on the cli.

**To use the IPC mechanism:**

```ts
// PupTelemetry is a singleton, so it can be imported one or many times in your application
// - Installation instructions for different runtimes available at https://jsr.io/@pup/telemetry
// This example imports directly from jsr.io using Deno jsr:-specifier
import { PupTelemetry } from "jsr:@pup/telemetry";
const telemetry = new PupTelemetry();
import { PupTelemetry } from "jsr:@pup/telemetry"
const telemetry = new PupTelemetry()

// One part of your application ...

// Listen for ipc events
telemetry.on("my-event", (data) => {
console.log(
`Another process triggered 'my-event' with data ${JSON.stringify(data)}`,
);
});
)
})

// Send ipc events
telemetry.emit("another-process-id", "my-event", { data: { to: "send" } });
telemetry.emit("another-process-id", "my-event", { data: { to: "send" } })

// ... another part of your application
```

## Files

- [pup.json](https://github.com/Hexagon/pup/tree/main/docs/src/examples/telemetry/pup.json) -
Pup configuration, sets up `task-with-telemetry.ts` to run forever.
`server.ts` to be kept alive forever.
- [task-with-telemetry-1.ts](https://github.com/Hexagon/pup/tree/main/docs/src/examples/telemetry/task-with-telemetry-1.ts) -
"task-1", script sending telemetry data to main process, and sending messages
over IPC to task-2
- [task-with-telemetry-2.ts](https://github.com/Hexagon/pup/tree/main/docs/src/examples/telemetry/task-with-telemetry-2.ts) -
"task-2", script sending telemetry data to main process, and receiving
- [pup.json](https://github.com/Hexagon/pup/tree/main/docs/src/examples/telemetry/pup.json) - Pup configuration, sets up `task-with-telemetry.ts` to run forever. `server.ts` to be kept alive forever.
- [task-with-telemetry-1.ts](https://github.com/Hexagon/pup/tree/main/docs/src/examples/telemetry/task-with-telemetry-1.ts) - "task-1", script sending telemetry data to main process, and sending
messages over IPC to task-2
- [task-with-telemetry-2.ts](https://github.com/Hexagon/pup/tree/main/docs/src/examples/telemetry/task-with-telemetry-2.ts) - "task-2", script sending telemetry data to main process, and receiving
messages over IPC from task-1

## Running

`cd` to `docs/examples/telemetry` directory.

Start example by running `pup run` if pup is installed, or something like
`deno run -A ../../../pup.ts run` if not.
Start example by running `pup run` if pup is installed, or something like `deno run -A ../../../pup.ts run` if not.

Now open another terminal and issue `pup status`, a brief overview of current
status is shown, including memory usage.
Now open another terminal and issue `pup status`, a brief overview of current status is shown, including memory usage.

Success!
18 changes: 8 additions & 10 deletions docs/src/examples/telemetry/task-with-telemetry-1.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
// See docs/examples/telemetry/README.md for full documentation on telemetry, including using the IPC
// - Pin this to the latest version of pup, or include in import map
import { PupTelemetry } from "jsr:@pup/telemetry";
const telemetry = new PupTelemetry(1);
import { PupTelemetry } from "jsr:@pup/telemetry"
const telemetry = new PupTelemetry(1)

// The task
let i = 0;
console.log("Task running");
let i = 0
console.log("Task running")

// Send data every 5th second
setInterval(() => {
i += 1;
i += 1
telemetry.emit(
"task-2",
"message",
`${
Deno.env.get("PUP_PROCESS_ID")
} sending "Hello" to 'task-2', iteration ${i}`,
);
}, 2000);
`${Deno.env.get("PUP_PROCESS_ID")} sending "Hello" to 'task-2', iteration ${i}`,
)
}, 2000)
14 changes: 7 additions & 7 deletions docs/src/examples/telemetry/task-with-telemetry-2.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
// See docs/examples/telemetry/README.md for full documentation on telemetry, including using the IPC
// - Pin this to the latest version of pup, or include in import map
import { PupTelemetry } from "jsr:@pup/telemetry";
const telemetry = new PupTelemetry(1);
import { PupTelemetry } from "jsr:@pup/telemetry"
const telemetry = new PupTelemetry(1)

// The task
console.log("Process running");
console.log("Process running")

// Receive data
// deno-lint-ignore no-explicit-any
telemetry.on("message", (data: any) => {
console.log(`task-2 received: ${data}`);
});
console.log(`task-2 received: ${data}`)
})

// Wait 5 minutes
setTimeout(() => {
console.log("Done!");
}, 300_000);
console.log("Done!")
}, 300_000)
2 changes: 1 addition & 1 deletion lib/common/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface PupTokenPayload {
exp: number | undefined
}

export async function GenerateToken(secret: string, data: unknown, expMs: number | undefined) {
export async function GenerateToken(secret: string, data: unknown, expMs: number | undefined): Promise<string> {
const key = await generateKey(secret, DEFAULT_SECRET_KEY_ALGORITHM)

// Require a consumer
Expand Down
9 changes: 7 additions & 2 deletions lib/core/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ export class PupApi {
return statuses
}
public applicationState(): ApiApplicationState {
return this._pup.status.applicationState(this._pup.allProcesses(), this._pup.port) as ApiApplicationState
return this._pup.status.applicationState(
this._pup.allProcesses(),
this._pup.port,
) as ApiApplicationState
}

// Global actions
Expand All @@ -65,7 +68,9 @@ export class PupApi {
}
public async stop(id: string, reason: string): Promise<boolean> {
const processesToStart = (id === "all") ? this.allProcessStates() : [this.allProcessStates().find((p) => p.status.id === id)]
const results = await Promise.all([processesToStart.map((process) => this._pup.stop(process!.status.id, reason))])
const results = await Promise.all([
processesToStart.map((process) => this._pup.stop(process!.status.id, reason)),
])
return results.filter((r) => r).length > 0
}
public block(id: string, reason: string): boolean {
Expand Down
10 changes: 10 additions & 0 deletions lib/core/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import { z } from "zod"
import { PluginConfiguration } from "@pup/plugin"

// Logger constants
export const DEFAULT_INTERNAL_LOG_HOURS = 48
Expand All @@ -32,6 +33,7 @@ interface Configuration {
logger?: GlobalLoggerConfiguration
watcher?: GlobalWatcherConfiguration
processes: ProcessConfiguration[]
plugins?: PluginConfiguration[]
terminateTimeout?: number
terminateGracePeriod?: number
}
Expand Down Expand Up @@ -165,6 +167,14 @@ const ConfigurationSchema = z.object({
),
}).strict(),
),
plugins: z.optional(
z.array(
z.object({
url: z.string(),
options: z.optional(z.any()),
}),
),
),
}).strict()

function validateConfiguration(unsafeConfiguration: unknown): Configuration {
Expand Down
41 changes: 41 additions & 0 deletions lib/core/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { PluginConfiguration, PluginImplementation } from "@pup/plugin"

/**
* Internal representation of a plugin
* - Used by @pup/pup internally!
*/
export class Plugin {
private config: PluginConfiguration
private apiUrl: string
private apiToken: string
public impl?: PluginImplementation
constructor(config: PluginConfiguration, apiUrl: string, apiToken: string) {
this.config = config
this.apiUrl = apiUrl
this.apiToken = apiToken
}
/**
* Will throw on any error
*/
public async load() {
const { PupPlugin } = await import(this.config.url)
this.impl = new PupPlugin(this.config, this.apiUrl, this.apiToken) as PluginImplementation
}
public verify() {
if (!this.impl || this.impl.meta.name === "unset") {
throw new Error("Plugin missing meta.name")
}
if (!this.impl || this.impl.meta.repository === "unset") {
throw new Error("Plugin missing meta.repository")
}
if (!this.impl || this.impl.meta.version === "unset") {
throw new Error("Plugin missing meta.version")
}
if (!this.impl || this.impl.meta.api === "unset") {
throw new Error("Plugin missing meta.api")
}
}
async terminate() {
if (this.impl?.cleanup) await this.impl?.cleanup()
}
}
Loading

0 comments on commit 43e45e2

Please sign in to comment.