feat: wire service registry targets
This commit is contained in:
+2
-2
@@ -74,8 +74,8 @@
|
|||||||
"@push.rocks/smartunique": "^3.0.9",
|
"@push.rocks/smartunique": "^3.0.9",
|
||||||
"@push.rocks/taskbuffer": "^3.4.0",
|
"@push.rocks/taskbuffer": "^3.4.0",
|
||||||
"@push.rocks/webjwt": "^1.0.9",
|
"@push.rocks/webjwt": "^1.0.9",
|
||||||
"@serve.zone/api": "^5.3.2",
|
"@serve.zone/api": "^5.3.4",
|
||||||
"@serve.zone/interfaces": "^5.4.5",
|
"@serve.zone/interfaces": "^5.4.6",
|
||||||
"@tsclass/tsclass": "^9.2.0"
|
"@tsclass/tsclass": "^9.2.0"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
|
|||||||
Generated
+22
-11
@@ -141,11 +141,11 @@ importers:
|
|||||||
specifier: ^1.0.9
|
specifier: ^1.0.9
|
||||||
version: 1.0.9
|
version: 1.0.9
|
||||||
'@serve.zone/api':
|
'@serve.zone/api':
|
||||||
specifier: ^5.3.2
|
specifier: ^5.3.4
|
||||||
version: 5.3.2
|
version: 5.3.4
|
||||||
'@serve.zone/interfaces':
|
'@serve.zone/interfaces':
|
||||||
specifier: ^5.4.5
|
specifier: ^5.4.6
|
||||||
version: 5.4.5
|
version: 5.4.6
|
||||||
'@tsclass/tsclass':
|
'@tsclass/tsclass':
|
||||||
specifier: ^9.2.0
|
specifier: ^9.2.0
|
||||||
version: 9.5.0
|
version: 9.5.0
|
||||||
@@ -1922,11 +1922,11 @@ packages:
|
|||||||
'@sec-ant/readable-stream@0.4.1':
|
'@sec-ant/readable-stream@0.4.1':
|
||||||
resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
|
resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
|
||||||
|
|
||||||
'@serve.zone/api@5.3.2':
|
'@serve.zone/api@5.3.4':
|
||||||
resolution: {integrity: sha512-ETQ4KSNfhDP7O1WxXXLcMn/A+jZtDfd7FjuQ0k3n8tnXG9hExh8ZmqvMwVj8eT2CnXO+xQVlbAgT0HLMLnxCfA==}
|
resolution: {integrity: sha512-3CqyeZkZPCJ4775UoNPKfknhTlAk6zmU/MVVSu6DoIAWgUaOuAlLUHlV45xIGtHmKAppsiYUoyoEhBLTZf9iMw==}
|
||||||
|
|
||||||
'@serve.zone/interfaces@5.4.5':
|
'@serve.zone/interfaces@5.4.6':
|
||||||
resolution: {integrity: sha512-asqUUjem3MGfIbseovHR8SxE+6FvjeQEYtV+PxcyY8YRXJ/vE3hNCDs7ePXgBbh4JXa+vNMaXHsFfz5Vrk6Ggg==}
|
resolution: {integrity: sha512-o4k7Wr6t3NLiP6gfAZZz8Jx8RlQ4sZYHTbhr4WkXzGf78vczFRIuFLyY1Y+TTNzDLEIzLVIyMsuECMV1KTwB2Q==}
|
||||||
|
|
||||||
'@shikijs/engine-oniguruma@3.12.2':
|
'@shikijs/engine-oniguruma@3.12.2':
|
||||||
resolution: {integrity: sha512-hozwnFHsLvujK4/CPVHNo3Bcg2EsnG8krI/ZQ2FlBlCRpPZW4XAEQmEwqegJsypsTAN9ehu2tEYe30lYKSZW/w==}
|
resolution: {integrity: sha512-hozwnFHsLvujK4/CPVHNo3Bcg2EsnG8krI/ZQ2FlBlCRpPZW4XAEQmEwqegJsypsTAN9ehu2tEYe30lYKSZW/w==}
|
||||||
@@ -6808,8 +6808,10 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@nuxt/kit'
|
- '@nuxt/kit'
|
||||||
- '@swc/helpers'
|
- '@swc/helpers'
|
||||||
|
- bufferutil
|
||||||
- react
|
- react
|
||||||
- supports-color
|
- supports-color
|
||||||
|
- utf-8-validate
|
||||||
- vue
|
- vue
|
||||||
|
|
||||||
'@happy-dom/global-registrator@15.11.7':
|
'@happy-dom/global-registrator@15.11.7':
|
||||||
@@ -7561,6 +7563,7 @@ snapshots:
|
|||||||
- '@mongodb-js/zstd'
|
- '@mongodb-js/zstd'
|
||||||
- '@nuxt/kit'
|
- '@nuxt/kit'
|
||||||
- aws-crt
|
- aws-crt
|
||||||
|
- bufferutil
|
||||||
- encoding
|
- encoding
|
||||||
- gcp-metadata
|
- gcp-metadata
|
||||||
- kerberos
|
- kerberos
|
||||||
@@ -7569,6 +7572,7 @@ snapshots:
|
|||||||
- snappy
|
- snappy
|
||||||
- socks
|
- socks
|
||||||
- supports-color
|
- supports-color
|
||||||
|
- utf-8-validate
|
||||||
- vue
|
- vue
|
||||||
|
|
||||||
'@push.rocks/smartai@0.5.11(typescript@5.9.2)(ws@8.18.3)(zod@3.25.76)':
|
'@push.rocks/smartai@0.5.11(typescript@5.9.2)(ws@8.18.3)(zod@3.25.76)':
|
||||||
@@ -8625,7 +8629,7 @@ snapshots:
|
|||||||
|
|
||||||
'@sec-ant/readable-stream@0.4.1': {}
|
'@sec-ant/readable-stream@0.4.1': {}
|
||||||
|
|
||||||
'@serve.zone/api@5.3.2':
|
'@serve.zone/api@5.3.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@api.global/typedrequest': 3.1.10
|
'@api.global/typedrequest': 3.1.10
|
||||||
'@api.global/typedrequest-interfaces': 3.0.19
|
'@api.global/typedrequest-interfaces': 3.0.19
|
||||||
@@ -8634,10 +8638,17 @@ snapshots:
|
|||||||
'@push.rocks/smartpromise': 4.2.3
|
'@push.rocks/smartpromise': 4.2.3
|
||||||
'@push.rocks/smartrx': 3.0.10
|
'@push.rocks/smartrx': 3.0.10
|
||||||
'@push.rocks/smartstream': 3.4.0
|
'@push.rocks/smartstream': 3.4.0
|
||||||
'@serve.zone/interfaces': 5.4.5
|
'@serve.zone/interfaces': 5.4.6
|
||||||
'@tsclass/tsclass': 9.5.0
|
'@tsclass/tsclass': 9.5.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@nuxt/kit'
|
||||||
|
- bufferutil
|
||||||
|
- react
|
||||||
|
- supports-color
|
||||||
|
- utf-8-validate
|
||||||
|
- vue
|
||||||
|
|
||||||
'@serve.zone/interfaces@5.4.5':
|
'@serve.zone/interfaces@5.4.6':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@api.global/typedrequest-interfaces': 3.0.19
|
'@api.global/typedrequest-interfaces': 3.0.19
|
||||||
'@push.rocks/smartlog-interfaces': 3.0.2
|
'@push.rocks/smartlog-interfaces': 3.0.2
|
||||||
|
|||||||
@@ -157,6 +157,39 @@ tap.test('should deny OCI registry push tokens for non-admin users', async () =>
|
|||||||
expect(pushResponse.status).toEqual(403);
|
expect(pushResponse.status).toEqual(403);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
tap.test('should expose generated service registry targets', async () => {
|
||||||
|
const image = await testClient.image.createImage({
|
||||||
|
name: 'Registry Target Test Image',
|
||||||
|
description: 'Image used by the registry target test',
|
||||||
|
});
|
||||||
|
const service = await testClient.services.createService({
|
||||||
|
name: 'Registry Target Test Service',
|
||||||
|
description: 'Service used by the registry target test',
|
||||||
|
imageId: image.id,
|
||||||
|
imageVersion: 'latest',
|
||||||
|
environment: {},
|
||||||
|
secretBundleId: '',
|
||||||
|
serviceCategory: 'workload',
|
||||||
|
deploymentStrategy: 'custom',
|
||||||
|
scaleFactor: 1,
|
||||||
|
balancingStrategy: 'round-robin',
|
||||||
|
ports: {
|
||||||
|
web: 3000,
|
||||||
|
},
|
||||||
|
domains: [],
|
||||||
|
deploymentIds: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const registryTarget = await testClient.services.getRegistryTarget(service.id, 'latest');
|
||||||
|
expect(registryTarget.protocol).toEqual('oci');
|
||||||
|
expect(registryTarget.registryHost).toEqual(`${helpers.testCloudlyConfig.publicUrl}:${helpers.testCloudlyConfig.publicPort}`);
|
||||||
|
expect(registryTarget.repository.startsWith('workloads/registry-target-test-service-')).toBeTrue();
|
||||||
|
expect(registryTarget.imageUrl).toEqual(`${registryTarget.registryHost}/${registryTarget.repository}:latest`);
|
||||||
|
|
||||||
|
const refreshedService = await testClient.services.getServiceById(service.id);
|
||||||
|
expect(refreshedService.data.registryTarget?.imageUrl).toEqual(registryTarget.imageUrl);
|
||||||
|
});
|
||||||
|
|
||||||
tap.test('should expose platform desired state', async () => {
|
tap.test('should expose platform desired state', async () => {
|
||||||
const capabilitiesResponse = await testClient.platform.getPlatformCapabilities();
|
const capabilitiesResponse = await testClient.platform.getPlatformCapabilities();
|
||||||
expect(capabilitiesResponse.capabilities.find((capability) => capability.id === 'database')).toBeTruthy();
|
expect(capabilitiesResponse.capabilities.find((capability) => capability.id === 'database')).toBeTruthy();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { Cloudly } from '../classes.cloudly.js';
|
import type { Cloudly } from '../classes.cloudly.js';
|
||||||
import { logger } from '../logger.js';
|
import { logger } from '../logger.js';
|
||||||
import * as plugins from '../plugins.js';
|
import * as plugins from '../plugins.js';
|
||||||
|
import type { Service } from '../manager.service/classes.service.js';
|
||||||
|
|
||||||
type TAuthenticatedRegistryUser = {
|
type TAuthenticatedRegistryUser = {
|
||||||
userId: string;
|
userId: string;
|
||||||
@@ -11,6 +12,7 @@ type TAuthenticatedRegistryUser = {
|
|||||||
export class CloudlyRegistryManager {
|
export class CloudlyRegistryManager {
|
||||||
private cloudlyRef: Cloudly;
|
private cloudlyRef: Cloudly;
|
||||||
private smartRegistry!: plugins.smartregistry.SmartRegistry;
|
private smartRegistry!: plugins.smartregistry.SmartRegistry;
|
||||||
|
private recordedTagDigests = new Map<string, string>();
|
||||||
private started = false;
|
private started = false;
|
||||||
|
|
||||||
constructor(cloudlyRefArg: Cloudly) {
|
constructor(cloudlyRefArg: Cloudly) {
|
||||||
@@ -27,6 +29,11 @@ export class CloudlyRegistryManager {
|
|||||||
|
|
||||||
this.smartRegistry = new plugins.smartregistry.SmartRegistry({
|
this.smartRegistry = new plugins.smartregistry.SmartRegistry({
|
||||||
storage: s3Descriptor as plugins.smartregistry.IStorageConfig,
|
storage: s3Descriptor as plugins.smartregistry.IStorageConfig,
|
||||||
|
storageHooks: {
|
||||||
|
afterPut: async (contextArg) => {
|
||||||
|
await this.handleRegistryStorageAfterPut(contextArg);
|
||||||
|
},
|
||||||
|
},
|
||||||
auth: {
|
auth: {
|
||||||
jwtSecret: registryJwtSecret,
|
jwtSecret: registryJwtSecret,
|
||||||
tokenStore: 'memory',
|
tokenStore: 'memory',
|
||||||
@@ -93,6 +100,160 @@ export class CloudlyRegistryManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getRegistryHost() {
|
||||||
|
if (!this.cloudlyRef.config.data.publicUrl) {
|
||||||
|
throw new Error('Cloudly registry requires publicUrl');
|
||||||
|
}
|
||||||
|
|
||||||
|
const publicPort = this.cloudlyRef.config.data.publicPort;
|
||||||
|
const includePort =
|
||||||
|
this.cloudlyRef.config.data.sslMode === 'none' && publicPort && !['80', '443'].includes(publicPort);
|
||||||
|
return `${this.cloudlyRef.config.data.publicUrl}${includePort ? `:${publicPort}` : ''}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getServiceRegistryTarget(
|
||||||
|
serviceArg: Service,
|
||||||
|
tagArg = 'latest',
|
||||||
|
): plugins.servezoneInterfaces.data.IRegistryTarget {
|
||||||
|
const registryHost = this.getRegistryHost();
|
||||||
|
const repository = this.getServiceRepository(serviceArg);
|
||||||
|
return {
|
||||||
|
protocol: 'oci',
|
||||||
|
registryHost,
|
||||||
|
repository,
|
||||||
|
tag: tagArg,
|
||||||
|
imageUrl: `${registryHost}/${repository}:${tagArg}`,
|
||||||
|
serviceId: serviceArg.id,
|
||||||
|
imageId: serviceArg.data?.imageId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleRegistryStorageAfterPut(
|
||||||
|
contextArg: plugins.smartregistry.IStorageHookContext,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
if (contextArg.protocol !== 'oci') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!contextArg.key.startsWith('oci/tags/') || !contextArg.key.endsWith('/tags.json')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const repository = contextArg.key.slice('oci/tags/'.length, -'/tags.json'.length);
|
||||||
|
const tagsBuffer = await this.smartRegistry.getStorage().getObject(contextArg.key);
|
||||||
|
if (!tagsBuffer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tags = JSON.parse(tagsBuffer.toString('utf8')) as Record<string, string>;
|
||||||
|
for (const [tag, digest] of Object.entries(tags)) {
|
||||||
|
const tagKey = `${repository}:${tag}`;
|
||||||
|
if (this.recordedTagDigests.get(tagKey) === digest) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this.recordedTagDigests.set(tagKey, digest);
|
||||||
|
await this.recordRegistryPushEvent(repository, tag, digest, contextArg.actor?.userId);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.log('error', `registry push event handling failed: ${(error as Error).message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async recordRegistryPushEvent(
|
||||||
|
repositoryArg: string,
|
||||||
|
tagArg: string,
|
||||||
|
digestArg: string,
|
||||||
|
actorUserIdArg?: string,
|
||||||
|
) {
|
||||||
|
const service = await this.getServiceByRegistryRepository(repositoryArg);
|
||||||
|
if (!service) {
|
||||||
|
logger.log('info', `registry push for unmapped repository ${repositoryArg}:${tagArg}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const registryTarget = this.getServiceRegistryTarget(service, tagArg);
|
||||||
|
const pushEvent: plugins.servezoneInterfaces.data.IRegistryPushEvent = {
|
||||||
|
protocol: 'oci',
|
||||||
|
registryHost: registryTarget.registryHost,
|
||||||
|
repository: repositoryArg,
|
||||||
|
tag: tagArg,
|
||||||
|
digest: digestArg,
|
||||||
|
imageUrl: registryTarget.imageUrl,
|
||||||
|
pushedAt: Date.now(),
|
||||||
|
serviceId: service.id,
|
||||||
|
imageId: service.data.imageId,
|
||||||
|
actorUserId: actorUserIdArg,
|
||||||
|
};
|
||||||
|
|
||||||
|
service.data = {
|
||||||
|
...service.data,
|
||||||
|
...(service.data.deployOnPush === false ? {} : { imageVersion: tagArg }),
|
||||||
|
registryTarget,
|
||||||
|
};
|
||||||
|
await service.save();
|
||||||
|
|
||||||
|
await this.recordImagePushEvent(service, pushEvent);
|
||||||
|
logger.log('info', `recorded registry push ${repositoryArg}:${tagArg} -> ${digestArg}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async recordImagePushEvent(
|
||||||
|
serviceArg: Service,
|
||||||
|
pushEventArg: plugins.servezoneInterfaces.data.IRegistryPushEvent,
|
||||||
|
) {
|
||||||
|
if (!serviceArg.data.imageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const image = await this.cloudlyRef.imageManager.CImage.getInstance({
|
||||||
|
id: serviceArg.data.imageId,
|
||||||
|
}).catch(() => null);
|
||||||
|
if (!image) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
image.data.versions = image.data.versions || [];
|
||||||
|
const existingVersion = image.data.versions.find((versionArg) => {
|
||||||
|
return versionArg.versionString === pushEventArg.tag;
|
||||||
|
});
|
||||||
|
const versionData = {
|
||||||
|
versionString: pushEventArg.tag,
|
||||||
|
digest: pushEventArg.digest,
|
||||||
|
registryRepository: pushEventArg.repository,
|
||||||
|
registryTag: pushEventArg.tag,
|
||||||
|
source: 'registry' as const,
|
||||||
|
size: existingVersion?.size || 0,
|
||||||
|
createdAt: existingVersion?.createdAt || pushEventArg.pushedAt,
|
||||||
|
};
|
||||||
|
if (existingVersion) {
|
||||||
|
Object.assign(existingVersion, versionData);
|
||||||
|
} else {
|
||||||
|
image.data.versions.push(versionData);
|
||||||
|
}
|
||||||
|
image.data.lastPushEvent = pushEventArg;
|
||||||
|
await image.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getServiceByRegistryRepository(repositoryArg: string) {
|
||||||
|
const services = await this.cloudlyRef.serviceManager.CService.getInstances({});
|
||||||
|
return services.find((serviceArg) => {
|
||||||
|
return this.getServiceRepository(serviceArg) === repositoryArg;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private getServiceRepository(serviceArg: Service) {
|
||||||
|
const serviceName = this.slugify(serviceArg.data?.name || serviceArg.id);
|
||||||
|
const serviceId = this.slugify(serviceArg.id).slice(0, 12) || serviceArg.id;
|
||||||
|
return `workloads/${serviceName}-${serviceId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private slugify(valueArg: string) {
|
||||||
|
return valueArg
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[^a-z0-9._-]+/g, '-')
|
||||||
|
.replace(/^-+|-+$/g, '')
|
||||||
|
|| 'service';
|
||||||
|
}
|
||||||
|
|
||||||
private async handleTokenRequest(
|
private async handleTokenRequest(
|
||||||
req: plugins.typedserver.Request,
|
req: plugins.typedserver.Request,
|
||||||
res: plugins.typedserver.Response,
|
res: plugins.typedserver.Response,
|
||||||
@@ -202,13 +363,7 @@ export class CloudlyRegistryManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getPublicRegistryUrl() {
|
private getPublicRegistryUrl() {
|
||||||
if (!this.cloudlyRef.config.data.publicUrl) {
|
return `${this.cloudlyRef.config.data.sslMode === 'none' ? 'http' : 'https'}://${this.getRegistryHost()}`;
|
||||||
throw new Error('Cloudly registry requires publicUrl');
|
|
||||||
}
|
|
||||||
const publicPort = this.cloudlyRef.config.data.publicPort;
|
|
||||||
const includePort =
|
|
||||||
this.cloudlyRef.config.data.sslMode === 'none' && publicPort && !['80', '443'].includes(publicPort);
|
|
||||||
return `${this.cloudlyRef.config.data.sslMode === 'none' ? 'http' : 'https'}://${this.cloudlyRef.config.data.publicUrl}${includePort ? `:${publicPort}` : ''}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private headersToRecord(headersArg: plugins.typedserver.Request['headers']) {
|
private headersToRecord(headersArg: plugins.typedserver.Request['headers']) {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export class Service extends plugins.smartdata.SmartDataDbDoc<
|
|||||||
public static async createService(serviceDataArg: Partial<plugins.servezoneInterfaces.data.IService['data']>) {
|
public static async createService(serviceDataArg: Partial<plugins.servezoneInterfaces.data.IService['data']>) {
|
||||||
const service = new Service();
|
const service = new Service();
|
||||||
service.id = await Service.getNewId();
|
service.id = await Service.getNewId();
|
||||||
Object.assign(service, serviceDataArg);
|
service.data = serviceDataArg as plugins.servezoneInterfaces.data.IService['data'];
|
||||||
await service.save();
|
await service.save();
|
||||||
|
|
||||||
// Create DNS entries if service has web port and domains configured
|
// Create DNS entries if service has web port and domains configured
|
||||||
@@ -36,7 +36,7 @@ export class Service extends plugins.smartdata.SmartDataDbDoc<
|
|||||||
}
|
}
|
||||||
|
|
||||||
// INSTANCE
|
// INSTANCE
|
||||||
@plugins.smartdata.svDb()
|
@plugins.smartdata.unI()
|
||||||
public id: string;
|
public id: string;
|
||||||
|
|
||||||
@plugins.smartdata.svDb()
|
@plugins.smartdata.svDb()
|
||||||
|
|||||||
@@ -38,6 +38,23 @@ export class ServiceManager {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.typedrouter.addTypedHandler(
|
||||||
|
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.service.IRequest_Any_Cloudly_GetServiceById>(
|
||||||
|
'getServiceById',
|
||||||
|
async (dataArg) => {
|
||||||
|
await plugins.smartguard.passGuardsOrReject(dataArg, [
|
||||||
|
this.cloudlyRef.authManager.validIdentityGuard,
|
||||||
|
]);
|
||||||
|
const service = await Service.getInstance({
|
||||||
|
id: dataArg.serviceId,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
service: await service.createSavableObject(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
this.typedrouter.addTypedHandler(
|
this.typedrouter.addTypedHandler(
|
||||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.service.IRequest_Any_Cloudly_GetServiceSecretBundlesAsFlatObject>(
|
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.service.IRequest_Any_Cloudly_GetServiceSecretBundlesAsFlatObject>(
|
||||||
'getServiceSecretBundlesAsFlatObject',
|
'getServiceSecretBundlesAsFlatObject',
|
||||||
@@ -53,11 +70,36 @@ export class ServiceManager {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.typedrouter.addTypedHandler(
|
||||||
|
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.service.IRequest_Any_Cloudly_GetServiceRegistryTarget>(
|
||||||
|
'getServiceRegistryTarget',
|
||||||
|
async (dataArg) => {
|
||||||
|
await plugins.smartguard.passGuardsOrReject(dataArg, [
|
||||||
|
this.cloudlyRef.authManager.validIdentityGuard,
|
||||||
|
]);
|
||||||
|
const service = await Service.getInstance({
|
||||||
|
id: dataArg.serviceId,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
registryTarget: this.cloudlyRef.registryManager.getServiceRegistryTarget(
|
||||||
|
service,
|
||||||
|
dataArg.tag || service.data.imageVersion || 'latest',
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
this.typedrouter.addTypedHandler(
|
this.typedrouter.addTypedHandler(
|
||||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.service.IRequest_Any_Cloudly_CreateService>(
|
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.service.IRequest_Any_Cloudly_CreateService>(
|
||||||
'createService',
|
'createService',
|
||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
const service = await Service.createService(dataArg.serviceData);
|
const service = await Service.createService(dataArg.serviceData);
|
||||||
|
service.data.registryTarget = this.cloudlyRef.registryManager.getServiceRegistryTarget(
|
||||||
|
service,
|
||||||
|
service.data.imageVersion || 'latest',
|
||||||
|
);
|
||||||
|
await service.save();
|
||||||
return {
|
return {
|
||||||
service: await service.createSavableObject(),
|
service: await service.createSavableObject(),
|
||||||
};
|
};
|
||||||
@@ -76,6 +118,10 @@ export class ServiceManager {
|
|||||||
...service.data,
|
...service.data,
|
||||||
...dataArg.serviceData,
|
...dataArg.serviceData,
|
||||||
};
|
};
|
||||||
|
service.data.registryTarget = this.cloudlyRef.registryManager.getServiceRegistryTarget(
|
||||||
|
service,
|
||||||
|
service.data.imageVersion || 'latest',
|
||||||
|
);
|
||||||
await service.save();
|
await service.save();
|
||||||
return {
|
return {
|
||||||
service: await service.createSavableObject(),
|
service: await service.createSavableObject(),
|
||||||
|
|||||||
Reference in New Issue
Block a user