fix(service): Fix secret bundle and service management bugs

This commit is contained in:
Philipp Kunz 2025-01-20 02:11:12 +01:00
parent 7084d76c43
commit 04b278ee28
16 changed files with 186 additions and 11 deletions

View File

@ -1,5 +1,12 @@
# Changelog # 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) ## 2025-01-02 - 4.12.1 - fix(deps)
Updated @git.zone/tspublish to version ^1.9.1 Updated @git.zone/tspublish to version ^1.9.1

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@serve.zone/cloudly', 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.' description: 'A comprehensive tool for managing containerized applications across multiple cloud providers using Docker Swarmkit, featuring web, CLI, and API interfaces.'
} }

View File

@ -63,7 +63,7 @@ for (let i = 0; i < demoSecretGroups.length; i++) {
id: `configBundleId${i + 1}`, id: `configBundleId${i + 1}`,
data: { data: {
name: `Demo Config Bundle ${i + 1}`, name: `Demo Config Bundle ${i + 1}`,
includedImages: [], imageClaims: [],
type: 'external', type: 'external',
description: 'Demo Purpose', description: 'Demo Purpose',
includedSecretGroupIds: [secretGroup.id], includedSecretGroupIds: [secretGroup.id],

View File

@ -59,4 +59,16 @@ export class SecretBundle extends plugins.smartdata.SmartDataDbDoc<
} }
return returnObject; 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;
}
} }

View File

@ -1,3 +1,4 @@
import { SecretBundle } from 'ts/manager.secret/classes.secretbundle.js';
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import { ServiceManager } from './classes.servicemanager.js'; import { ServiceManager } from './classes.servicemanager.js';
@ -6,9 +7,51 @@ export class Service extends plugins.smartdata.SmartDataDbDoc<
plugins.servezoneInterfaces.data.IService, plugins.servezoneInterfaces.data.IService,
ServiceManager 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<plugins.servezoneInterfaces.data.IService['data']>) {
const service = new Service();
service.id = await Service.getNewId();
Object.assign(service, serviceDataArg);
await service.save();
return service;
}
// INSTANCE
@plugins.smartdata.svDb() @plugins.smartdata.svDb()
public id: string; public id: string;
@plugins.smartdata.svDb() @plugins.smartdata.svDb()
public data: plugins.servezoneInterfaces.data.IService['data']; 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;
}
} }

View File

@ -35,5 +35,32 @@ export class ServiceManager {
} }
) )
); );
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.service.IRequest_Any_Cloudly_GetServiceSecretBundlesAsFlatObject>(
'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<plugins.servezoneInterfaces.requests.service.IRequest_Any_Cloudly_CreateService>(
'createService',
async (dataArg) => {
const service = await Service.createService(dataArg.serviceData);
return {
service: await service.createSavableObject(),
};
}
)
);
} }
} }

View File

@ -59,7 +59,7 @@ export class SecretBundle implements plugins.servezoneInterfaces.data.ISecretBun
description: secretBundleDataArg.description, description: secretBundleDataArg.description,
type: secretBundleDataArg.type, type: secretBundleDataArg.type,
authorizations: secretBundleDataArg.authorizations, authorizations: secretBundleDataArg.authorizations,
includedImages: secretBundleDataArg.includedImages, imageClaims: secretBundleDataArg.imageClaims,
includedSecretGroupIds: secretBundleDataArg.includedSecretGroupIds, includedSecretGroupIds: secretBundleDataArg.includedSecretGroupIds,
includedTags: secretBundleDataArg.includedTags, includedTags: secretBundleDataArg.includedTags,
}, },

View File

@ -75,6 +75,16 @@ export class Service implements plugins.servezoneInterfaces.data.IService {
* In other words, it resolves secret groups and * In other words, it resolves secret groups and
*/ */
public async getSecretBundleAsFlatObject(environmentArg: string = 'production') { public async getSecretBundleAsFlatObject(environmentArg: string = 'production') {
const getServiceSecretBundlesAsFlatObjectTR = this.cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.service.IRequest_Any_Cloudly_GetServiceSecretBundlesAsFlatObject>(
'getServiceSecretBundlesAsFlatObject'
);
const response = await getServiceSecretBundlesAsFlatObjectTR.fire({
identity: this.cloudlyClientRef.identity,
serviceId: this.id,
environment: environmentArg,
});
const flatKeyValueObject: {[key: string]: string} = response.flatKeyValueObject;
return flatKeyValueObject;
} }
} }

View File

@ -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);
}
}

View File

@ -1,3 +1,11 @@
import * as plugins from './plugins.js';
import { CliClient } from "./classes.cliclient.js";
export const runCli = async () => { 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);
}; };

17
ts_cliclient/plugins.ts Normal file
View File

@ -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,
}

View File

@ -1,6 +1,12 @@
{ {
"name": "@serve.zone/cli", "name": "@serve.zone/cli",
"dependencies": [], "dependencies": [
"@serve.zone/api",
"@serve.zone/interfaces",
"@push.rocks/projectinfo",
"@push.rocks/qenv",
"@push.rocks/smartcli"
],
"registries": [ "registries": [
"registry.npmjs.org:public", "registry.npmjs.org:public",
"verdaccio.lossless.digital:public" "verdaccio.lossless.digital:public"

View File

@ -21,13 +21,19 @@ export interface ISecretBundle {
*/ */
type: 'service' | 'npmci' | 'gitzone' | 'external'; type: 'service' | 'npmci' | 'gitzone' | 'external';
/**
* set this if the secretBundle belongs to a service
*/
serviceId?: string;
/** /**
* You can add specific secret groups using this * You can add specific secret groups using this
*/ */
includedSecretGroupIds: string[]; includedSecretGroupIds: string[];
/** /**
* You can add specific tags using this * access to this secretBundle also grants access to resources with matching tags
*/ */
includedTags: { includedTags: {
key: string; 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; imageId: string;
permissions: ('read' | 'write')[]; permissions: ('read' | 'write')[];
}[]; }[];
/** /**
* authrozations select a specific environment of a config bundle * authrozations select a specific environment of a config bundle

View File

@ -8,7 +8,15 @@ export interface IService {
imageId: string; imageId: string;
imageVersion: string; imageVersion: string;
environment: { [key: string]: string }; environment: { [key: string]: string };
/**
* the main secret bundle id, exclusive to the service
*/
secretBundleId: string; secretBundleId: string;
/**
* those secret bundle ids do not belong to the service itself
* and thus live past the service lifecycle
*/
additionalSecretBundleIds?: string[];
scaleFactor: number; scaleFactor: number;
balancingStrategy: 'round-robin' | 'least-connections'; balancingStrategy: 'round-robin' | 'least-connections';
ports: { ports: {

View File

@ -111,3 +111,19 @@ extends plugins.typedrequestInterfaces.implementsTR<
success: boolean; 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};
};
}

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@serve.zone/cloudly', 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.' description: 'A comprehensive tool for managing containerized applications across multiple cloud providers using Docker Swarmkit, featuring web, CLI, and API interfaces.'
} }