From 58a377895da5482dda8db241ba927da0b00100d7 Mon Sep 17 00:00:00 2001 From: Michael Halka Date: Mon, 16 Jun 2025 15:29:43 -0700 Subject: [PATCH] Adding Manage Instance examples --- api/manage-instances/README.md | 25 +++ api/manage-instances/index.js | 58 ++++++ api/manage-instances/package-lock.json | 249 +++++++++++++++++++++++++ api/manage-instances/package.json | 15 ++ api/manage-instances/queries.js | 57 ++++++ 5 files changed, 404 insertions(+) create mode 100644 api/manage-instances/README.md create mode 100644 api/manage-instances/index.js create mode 100644 api/manage-instances/package-lock.json create mode 100644 api/manage-instances/package.json create mode 100644 api/manage-instances/queries.js diff --git a/api/manage-instances/README.md b/api/manage-instances/README.md new file mode 100644 index 0000000..793e1a2 --- /dev/null +++ b/api/manage-instances/README.md @@ -0,0 +1,25 @@ +# Programmatically Manage Instances + +This script demonstrates how to programmatically query for all active instances through Prismatic's GraphQL API, and delete or disable them. +Documentation for the API can be found at [prismatic.io/docs](https://prismatic.io/docs/api/api-overview/). + +`index.ts` creates an authenticated GraphQL client and uses that client to make requests to the Prismatic API. +Files in the `queries/` directory wrap various GraphQL queries and mutations to do things like list instances or disable or delete instances. + +## Running this script + +To run this script, you will need to supply an API key and an action you would like to perform on an instance, and the ID or name of the instance. +Those values can be shared as environment variables `PRISMATIC_API_KEY`,`ACTION`,and `IDENTIFIER` respectively. +If you have the [prism CLI tool](https://www.npmjs.com/package/@prismatic-io/prism) installed, you can use it to fetch a valid token: + +```bash +PRISMATIC_API_KEY=$(prism me:token) ACTION=disable IDENTIFIER=SW5zdGFuY2U6OT... npm run start +``` + +## Extending this script + +This script is meant to be used as an example to illustrate how to programmatically list instances, and disable or delete instances. +You can extend this script's logic however you like. +For example, there may only be a set of instances you want to disable, each of those instances have a specific label you can query for. +Or, you may want to loop through all instances and disable specific ones by a particular Id. +Use this script as a jumping-off point to build your own logic to manage your instances. diff --git a/api/manage-instances/index.js b/api/manage-instances/index.js new file mode 100644 index 0000000..61e64fa --- /dev/null +++ b/api/manage-instances/index.js @@ -0,0 +1,58 @@ +const { GraphQLClient } = require("graphql-request"); +const { + GET_INSTANCES, + DISABLE_INSTANCE, + DELETE_INSTANCE, +} = require("./queries"); + +const PRISMATIC_API_KEY = process.env.PRISMATIC_API_KEY; + +if (!PRISMATIC_API_KEY) { + throw new Error("You must set a PRISMATIC_API_KEY environment variable."); +} + +const identifier = process.env.IDENTIFIER; +const action = process.env.ACTION; + +const API_ENDPOINT = process.env.PRISMATIC_URL + ? `${process.env.PRISMATIC_URL}/api` + : "https://app.prismatic.io/api"; + +const client = new GraphQLClient(API_ENDPOINT, { + headers: { + Authorization: `Bearer ${PRISMATIC_API_KEY}`, + }, +}); + +async function manageInstances(identifier, action) { + try { + /** Get a list of all enabled instances */ + const instancesList = await client.request(GET_INSTANCES); + + const instances = instancesList.instances.nodes; + + /** Loop through all nodes and find the instance Id or name that matches the identifier */ + const instance = instances.find( + (i) => i.id === identifier || i.name === identifier + ); + if (!instance) { + console.log(`No instance found with ID or name "${identifier}".`); + return; + } + + /** Disable or Delete the found instance depending on the action */ + if (action === "disable") { + const res = await client.request(DISABLE_INSTANCE, { id: instance.id }); + console.log(`Disabled: ${res.updateInstance.instance.name}`); + } else if (action === "delete") { + const res = await client.request(DELETE_INSTANCE, { id: instance.id }); + console.log(`🗑️ Deleted: ${res.deleteInstance.instance.name}`); + } else { + console.log('Unknown action. Use "disable" or "delete".'); + } + } catch (err) { + console.error("GraphQL error:", err.message); + } +} + +manageInstances(identifier, action.toLowerCase()); diff --git a/api/manage-instances/package-lock.json b/api/manage-instances/package-lock.json new file mode 100644 index 0000000..18ef767 --- /dev/null +++ b/api/manage-instances/package-lock.json @@ -0,0 +1,249 @@ +{ + "name": "Manage Instances", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "Manage Instances", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "graphql-request": "^7.2.0", + "ts-node": "^10.9.2" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "license": "MIT", + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.0.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.3.tgz", + "integrity": "sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg==", + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "license": "MIT" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "license": "MIT" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/graphql": { + "version": "16.11.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.11.0.tgz", + "integrity": "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/graphql-request": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-7.2.0.tgz", + "integrity": "sha512-0GR7eQHBFYz372u9lxS16cOtEekFlZYB2qOyq8wDvzRmdRSJ0mgUVX1tzNcIzk3G+4NY+mGtSz411wZdeDF/+A==", + "license": "MIT", + "dependencies": { + "@graphql-typed-document-node/core": "^3.2.0" + }, + "peerDependencies": { + "graphql": "14 - 16" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "license": "ISC" + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "license": "MIT", + "peer": true + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "license": "MIT" + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + } + } +} diff --git a/api/manage-instances/package.json b/api/manage-instances/package.json new file mode 100644 index 0000000..c38fd1e --- /dev/null +++ b/api/manage-instances/package.json @@ -0,0 +1,15 @@ +{ + "name": "Manage Instances", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "ts-node index.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "graphql-request": "^7.2.0", + "ts-node": "^10.9.2" + } +} diff --git a/api/manage-instances/queries.js b/api/manage-instances/queries.js new file mode 100644 index 0000000..8117eed --- /dev/null +++ b/api/manage-instances/queries.js @@ -0,0 +1,57 @@ +const { gql } = require("graphql-request"); + +// Query Enabled Instances +const GET_INSTANCES = gql` + query { + instances(enabled: true, isSystem: false) { + nodes { + enabled + id + name + customer { + name + } + } + } + } +`; + +// Disable specific instance by Instance Id +const DISABLE_INSTANCE = gql` + mutation ($id: ID!) { + updateInstance(input: { id: $id, enabled: false }) { + instance { + enabled + id + name + } + errors { + field + messages + } + } + } +`; + +// Delete specific Instance by Instance Id +const DELETE_INSTANCE = gql` + mutation ($id: ID!) { + deleteInstance(input: { id: $id }) { + errors { + field + messages + } + instance { + enabled + id + name + } + } + } +`; + +module.exports = { + GET_INSTANCES, + DISABLE_INSTANCE, + DELETE_INSTANCE, +};