From 04b278ee2804e5ac17155b7d747109a320e1f0d3 Mon Sep 17 00:00:00 2001 From: Philipp Kunz Date: Mon, 20 Jan 2025 02:11:12 +0100 Subject: [PATCH] fix(service): Fix secret bundle and service management bugs --- changelog.md | 7 ++++ ts/00_commitinfo_data.ts | 2 +- ts/00demo/demo.data.secrets.ts | 2 +- ts/manager.secret/classes.secretbundle.ts | 12 ++++++ ts/manager.service/classes.service.ts | 43 ++++++++++++++++++++ ts/manager.service/classes.servicemanager.ts | 27 ++++++++++++ ts_apiclient/classes.secretbundle.ts | 2 +- ts_apiclient/classes.service.ts | 12 +++++- ts_cliclient/classes.cliclient.ts | 15 +++++++ ts_cliclient/index.ts | 10 ++++- ts_cliclient/plugins.ts | 17 ++++++++ ts_cliclient/tspublish.json | 8 +++- ts_interfaces/data/secretbundle.ts | 14 +++++-- ts_interfaces/data/service.ts | 8 ++++ ts_interfaces/requests/service.ts | 16 ++++++++ ts_web/00_commitinfo_data.ts | 2 +- 16 files changed, 186 insertions(+), 11 deletions(-) create mode 100644 ts_cliclient/classes.cliclient.ts create mode 100644 ts_cliclient/plugins.ts diff --git a/changelog.md b/changelog.md index d0c8936..60971d6 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,12 @@ # Changelog +## 2025-01-20 - 4.12.2 - fix(service) +Fix secret bundle and service management bugs + +- Corrected the field name from 'includedImages' to 'imageClaims' in secret bundles. +- Implemented 'getFlatKeyValueObject' for secret bundles and modified related API interactions. +- Enhanced the Service class with methods for handling secret bundle data by resolving related groups and environments. + ## 2025-01-02 - 4.12.1 - fix(deps) Updated @git.zone/tspublish to version ^1.9.1 diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 84a4afa..132dd7a 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@serve.zone/cloudly', - version: '4.12.1', + version: '4.12.2', description: 'A comprehensive tool for managing containerized applications across multiple cloud providers using Docker Swarmkit, featuring web, CLI, and API interfaces.' } diff --git a/ts/00demo/demo.data.secrets.ts b/ts/00demo/demo.data.secrets.ts index 49527a8..3711293 100644 --- a/ts/00demo/demo.data.secrets.ts +++ b/ts/00demo/demo.data.secrets.ts @@ -63,7 +63,7 @@ for (let i = 0; i < demoSecretGroups.length; i++) { id: `configBundleId${i + 1}`, data: { name: `Demo Config Bundle ${i + 1}`, - includedImages: [], + imageClaims: [], type: 'external', description: 'Demo Purpose', includedSecretGroupIds: [secretGroup.id], diff --git a/ts/manager.secret/classes.secretbundle.ts b/ts/manager.secret/classes.secretbundle.ts index 567faad..d0d2deb 100644 --- a/ts/manager.secret/classes.secretbundle.ts +++ b/ts/manager.secret/classes.secretbundle.ts @@ -59,4 +59,16 @@ export class SecretBundle extends plugins.smartdata.SmartDataDbDoc< } return returnObject; } + + public async getFlatKeyValueObject(environmentArg: string) { + if (!environmentArg) { + throw new Error('environment is required'); + } + const secretGroups = await this.getSecretGroups(); + const returnObject = {}; + for (const secretGroup of secretGroups) { + returnObject[secretGroup.data.key] = secretGroup.data.environments[environmentArg].value; + } + return returnObject; + } } diff --git a/ts/manager.service/classes.service.ts b/ts/manager.service/classes.service.ts index 91b340f..6f45fe0 100644 --- a/ts/manager.service/classes.service.ts +++ b/ts/manager.service/classes.service.ts @@ -1,3 +1,4 @@ +import { SecretBundle } from 'ts/manager.secret/classes.secretbundle.js'; import * as plugins from '../plugins.js'; import { ServiceManager } from './classes.servicemanager.js'; @@ -6,9 +7,51 @@ export class Service extends plugins.smartdata.SmartDataDbDoc< plugins.servezoneInterfaces.data.IService, ServiceManager > { + // STATIC + public static async getServiceById(serviceIdArg: string) { + const service = await this.getInstance({ + id: serviceIdArg, + }); + return service; + } + + public static async getServices() { + const services = await this.getInstances({}); + return services; + } + + public static async createService(serviceDataArg: Partial) { + const service = new Service(); + service.id = await Service.getNewId(); + Object.assign(service, serviceDataArg); + await service.save(); + return service; + } + + // INSTANCE @plugins.smartdata.svDb() public id: string; @plugins.smartdata.svDb() public data: plugins.servezoneInterfaces.data.IService['data']; + + /** + * a service runs in a specific environment + * so -> this method returns the secret bundles as a flat object accordingly. + * in other words, it resolves secret groups for the relevant environment + * @param environmentArg + */ + public async getSecretBundlesAsFlatObject(environmentArg: string = 'production') { + const secreBundleIds = this.data.additionalSecretBundleIds || []; + secreBundleIds.push(this.data.secretBundleId); // put this last, so it overwrites any other secret bundles. + let finalFlatObject = {}; + for (const secretBundleId of secreBundleIds) { + const secretBundle = await SecretBundle.getInstance({ + id: secretBundleId, + }); + const flatObject = await secretBundle.getFlatKeyValueObject(environmentArg); + Object.assign(finalFlatObject, flatObject); + } + return finalFlatObject; + } } diff --git a/ts/manager.service/classes.servicemanager.ts b/ts/manager.service/classes.servicemanager.ts index dd568f4..06f8d48 100644 --- a/ts/manager.service/classes.servicemanager.ts +++ b/ts/manager.service/classes.servicemanager.ts @@ -35,5 +35,32 @@ export class ServiceManager { } ) ); + + this.typedrouter.addTypedHandler( + new plugins.typedrequest.TypedHandler( + 'getServiceSecretBundlesAsFlatObject', + async (dataArg) => { + const service = await Service.getInstance({ + id: dataArg.serviceId, + }); + const flatKeyValueObject = await service.getSecretBundlesAsFlatObject(dataArg.environment); + return { + flatKeyValueObject: flatKeyValueObject, + }; + } + ) + ); + + this.typedrouter.addTypedHandler( + new plugins.typedrequest.TypedHandler( + 'createService', + async (dataArg) => { + const service = await Service.createService(dataArg.serviceData); + return { + service: await service.createSavableObject(), + }; + } + ) + ); } } diff --git a/ts_apiclient/classes.secretbundle.ts b/ts_apiclient/classes.secretbundle.ts index 1e0771c..8b4d127 100644 --- a/ts_apiclient/classes.secretbundle.ts +++ b/ts_apiclient/classes.secretbundle.ts @@ -59,7 +59,7 @@ export class SecretBundle implements plugins.servezoneInterfaces.data.ISecretBun description: secretBundleDataArg.description, type: secretBundleDataArg.type, authorizations: secretBundleDataArg.authorizations, - includedImages: secretBundleDataArg.includedImages, + imageClaims: secretBundleDataArg.imageClaims, includedSecretGroupIds: secretBundleDataArg.includedSecretGroupIds, includedTags: secretBundleDataArg.includedTags, }, diff --git a/ts_apiclient/classes.service.ts b/ts_apiclient/classes.service.ts index f7db3bf..b4b7af4 100644 --- a/ts_apiclient/classes.service.ts +++ b/ts_apiclient/classes.service.ts @@ -75,6 +75,16 @@ export class Service implements plugins.servezoneInterfaces.data.IService { * In other words, it resolves secret groups and */ public async getSecretBundleAsFlatObject(environmentArg: string = 'production') { - + const getServiceSecretBundlesAsFlatObjectTR = this.cloudlyClientRef.typedsocketClient.createTypedRequest( + 'getServiceSecretBundlesAsFlatObject' + ); + const response = await getServiceSecretBundlesAsFlatObjectTR.fire({ + identity: this.cloudlyClientRef.identity, + serviceId: this.id, + environment: environmentArg, + }); + const flatKeyValueObject: {[key: string]: string} = response.flatKeyValueObject; + + return flatKeyValueObject; } } diff --git a/ts_cliclient/classes.cliclient.ts b/ts_cliclient/classes.cliclient.ts new file mode 100644 index 0000000..8fe04ad --- /dev/null +++ b/ts_cliclient/classes.cliclient.ts @@ -0,0 +1,15 @@ +import * as plugins from './plugins.js'; +import { CloudlyApiClient } from '@serve.zone/api'; + +export class CliClient { + public cloudlyApiClient: CloudlyApiClient; + + constructor(cloudlyApiClientArg: CloudlyApiClient) { + this.cloudlyApiClient = cloudlyApiClientArg; + } + + public async getClusters() { + const clusters = await this.cloudlyApiClient.cluster.getClusters(); + console.log(clusters); + } +} \ No newline at end of file diff --git a/ts_cliclient/index.ts b/ts_cliclient/index.ts index 0344a69..656b566 100644 --- a/ts_cliclient/index.ts +++ b/ts_cliclient/index.ts @@ -1,3 +1,11 @@ +import * as plugins from './plugins.js'; +import { CliClient } from "./classes.cliclient.js"; + export const runCli = async () => { - console.log('serve.zone cli client'); + const cliQenv = new plugins.qenv.Qenv(); + const apiClient = new plugins.servezoneApi.CloudlyApiClient({ + registerAs: 'cli', + cloudlyUrl: await cliQenv.getEnvVarOnDemand('CLOUDLY_URL'), + }); + const cliClient = new CliClient(apiClient); }; \ No newline at end of file diff --git a/ts_cliclient/plugins.ts b/ts_cliclient/plugins.ts new file mode 100644 index 0000000..b499290 --- /dev/null +++ b/ts_cliclient/plugins.ts @@ -0,0 +1,17 @@ +// @serve.zone scope +import * as servezoneApi from '@serve.zone/api'; +import * as servezoneInterfaces from '@serve.zone/interfaces'; + +export { + servezoneApi, + servezoneInterfaces +} + +// @push.rocks scope +import * as projectinfo from '@push.rocks/projectinfo'; +import * as qenv from '@push.rocks/qenv'; + +export { + projectinfo, + qenv, +} \ No newline at end of file diff --git a/ts_cliclient/tspublish.json b/ts_cliclient/tspublish.json index 39a7565..1e6081e 100644 --- a/ts_cliclient/tspublish.json +++ b/ts_cliclient/tspublish.json @@ -1,6 +1,12 @@ { "name": "@serve.zone/cli", - "dependencies": [], + "dependencies": [ + "@serve.zone/api", + "@serve.zone/interfaces", + "@push.rocks/projectinfo", + "@push.rocks/qenv", + "@push.rocks/smartcli" + ], "registries": [ "registry.npmjs.org:public", "verdaccio.lossless.digital:public" diff --git a/ts_interfaces/data/secretbundle.ts b/ts_interfaces/data/secretbundle.ts index 95b58bd..a7fa107 100644 --- a/ts_interfaces/data/secretbundle.ts +++ b/ts_interfaces/data/secretbundle.ts @@ -21,13 +21,19 @@ export interface ISecretBundle { */ type: 'service' | 'npmci' | 'gitzone' | 'external'; + + /** + * set this if the secretBundle belongs to a service + */ + serviceId?: string; + /** * You can add specific secret groups using this */ includedSecretGroupIds: string[]; /** - * You can add specific tags using this + * access to this secretBundle also grants access to resources with matching tags */ includedTags: { key: string; @@ -35,12 +41,12 @@ export interface ISecretBundle { }[]; /** - * add images + * access to this secretBundle also grants access to the images */ - includedImages: { + imageClaims: { imageId: string; permissions: ('read' | 'write')[]; - }[]; + }[]; /** * authrozations select a specific environment of a config bundle diff --git a/ts_interfaces/data/service.ts b/ts_interfaces/data/service.ts index de533bd..da3cdb6 100644 --- a/ts_interfaces/data/service.ts +++ b/ts_interfaces/data/service.ts @@ -8,7 +8,15 @@ export interface IService { imageId: string; imageVersion: string; environment: { [key: string]: string }; + /** + * the main secret bundle id, exclusive to the service + */ secretBundleId: string; + /** + * those secret bundle ids do not belong to the service itself + * and thus live past the service lifecycle + */ + additionalSecretBundleIds?: string[]; scaleFactor: number; balancingStrategy: 'round-robin' | 'least-connections'; ports: { diff --git a/ts_interfaces/requests/service.ts b/ts_interfaces/requests/service.ts index 11eaeb6..5a9565f 100644 --- a/ts_interfaces/requests/service.ts +++ b/ts_interfaces/requests/service.ts @@ -111,3 +111,19 @@ extends plugins.typedrequestInterfaces.implementsTR< success: boolean; }; } + +export interface IRequest_Any_Cloudly_GetServiceSecretBundlesAsFlatObject +extends plugins.typedrequestInterfaces.implementsTR< + plugins.typedrequestInterfaces.ITypedRequest, + IRequest_Any_Cloudly_GetServiceSecretBundlesAsFlatObject +> { + method: 'getServiceSecretBundlesAsFlatObject'; + request: { + identity: IIdentity; + serviceId: string; + environment: string; + }; + response: { + flatKeyValueObject: {[key: string]: string}; + }; +} diff --git a/ts_web/00_commitinfo_data.ts b/ts_web/00_commitinfo_data.ts index 84a4afa..132dd7a 100644 --- a/ts_web/00_commitinfo_data.ts +++ b/ts_web/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@serve.zone/cloudly', - version: '4.12.1', + version: '4.12.2', description: 'A comprehensive tool for managing containerized applications across multiple cloud providers using Docker Swarmkit, featuring web, CLI, and API interfaces.' }