/* Copyright (C) 2024 Cloudbase Solutions SRL This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ import { DeploymentItem, DeploymentItemDetails, DeploymentItemOptions, UserScriptData, } from "@src/@types/MainItem"; import { ProgressUpdate, Task } from "@src/@types/Task"; import { INSTANCE_OSMORPHING_MINION_POOL_MAPPINGS } from "@src/components/modules/WizardModule/WizardOptions"; import { OptionsSchemaPlugin } from "@src/plugins"; import DefaultOptionsSchemaPlugin from "@src/plugins/default/OptionsSchemaPlugin"; import Api from "@src/utils/ApiCaller"; import configLoader from "@src/utils/Config"; import { sortTasks } from "./TransferSource"; import type { InstanceScript } from "@src/@types/Instance"; import type { Field } from "@src/@types/Field"; import type { NetworkMap } from "@src/@types/Network"; import type { Endpoint, StorageMap } from "@src/@types/Endpoint"; class DeploymentSourceUtils { static sortTaskUpdates(updates: ProgressUpdate[]) { if (!updates) { return; } updates.sort((a, b) => { const sortNull = !a && b ? 1 : a && !b ? -1 : !a && !b ? 0 : false; if (sortNull !== false) { return sortNull; } return a.index - b.index; }); } static sortDeployments(deployments: any[]) { deployments.sort( (a: any, b: any) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime() ); deployments.forEach((deployment: { tasks: Task[] }) => { sortTasks(deployment.tasks, DeploymentSourceUtils.sortTaskUpdates); }); } } class DeploymentSource { async getDeployments(skipLog?: boolean): Promise { const response = await Api.send({ url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/deployments`, skipLog, }); const deployments = response.data.deployments; DeploymentSourceUtils.sortDeployments(deployments); return deployments; } async getDeployment( deploymentId: string, skipLog?: boolean ): Promise { const response = await Api.send({ url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/deployments/${deploymentId}`, skipLog, cancelId: deploymentId, }); const deployment = response.data.deployment; sortTasks(deployment.tasks, DeploymentSourceUtils.sortTaskUpdates); return deployment; } async recreateFullCopy( deployment: DeploymentItemOptions ): Promise { const { // eslint-disable-next-line @typescript-eslint/naming-convention origin_endpoint_id, destination_endpoint_id, destination_environment, // eslint-disable-next-line @typescript-eslint/naming-convention network_map, instances, storage_mappings, notes, destination_minion_pool_id, // eslint-disable-next-line @typescript-eslint/naming-convention origin_minion_pool_id, instance_osmorphing_minion_pool_mappings, } = deployment; const payload: any = { deployment: { origin_endpoint_id, destination_endpoint_id, destination_environment, network_map, instances, storage_mappings, notes, destination_minion_pool_id, origin_minion_pool_id, instance_osmorphing_minion_pool_mappings, }, }; if (deployment.skip_os_morphing != null) { payload.deployment.skip_os_morphing = deployment.skip_os_morphing; } if (deployment.source_environment) { payload.deployment.source_environment = deployment.source_environment; } payload.deployment.shutdown_instances = Boolean( deployment.shutdown_instances ); // payload.deployment.replication_count = deployment.replication_count || 2; const response = await Api.send({ url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/deployments`, method: "POST", data: payload, }); return response.data.deployment; } async recreate(opts: { sourceEndpoint: Endpoint; destEndpoint: Endpoint; instanceNames: string[]; destEnv: { [prop: string]: any } | null; updatedDestEnv: { [prop: string]: any } | null; sourceEnv?: { [prop: string]: any } | null; updatedSourceEnv?: { [prop: string]: any } | null; storageMappings?: { [prop: string]: any } | null; updatedStorageMappings: StorageMap[] | null; defaultStorage?: { value: string | null; busType?: string | null }; updatedDefaultStorage?: { value: string | null; busType?: string | null }; networkMappings?: any; updatedNetworkMappings: NetworkMap[] | null; defaultSkipOsMorphing: boolean | null; // replicationCount?: number | null; deployment: DeploymentItemDetails; uploadedScripts: InstanceScript[]; removedScripts: InstanceScript[]; }): Promise { const getValue = (fieldName: string): string | null => { const updatedDestEnv = opts.updatedDestEnv && opts.updatedDestEnv[fieldName]; return updatedDestEnv != null ? updatedDestEnv : opts.destEnv && opts.destEnv[fieldName]; }; const sourceParser = OptionsSchemaPlugin.for(opts.sourceEndpoint.type); const destParser = OptionsSchemaPlugin.for(opts.destEndpoint.type); const payload: any = {}; payload.deployment = { origin_endpoint_id: opts.sourceEndpoint.id, destination_endpoint_id: opts.destEndpoint.id, shutdown_instances: Boolean( opts.updatedDestEnv && opts.updatedDestEnv.shutdown_instances ), // replication_count: // (opts.updatedDestEnv && opts.updatedDestEnv.replication_count) || // opts.replicationCount || // 2, instances: opts.instanceNames, notes: opts.updatedDestEnv?.title || opts.deployment.notes || "", }; const skipOsMorphingValue = getValue("skip_os_morphing"); if (skipOsMorphingValue != null) { payload.deployment.skip_os_morphing = skipOsMorphingValue; } else if (opts.defaultSkipOsMorphing != null) { payload.deployment.skip_os_morphing = opts.defaultSkipOsMorphing; } if ( opts.networkMappings || (opts.updatedNetworkMappings && opts.updatedNetworkMappings.length) ) { payload.deployment.network_map = { ...opts.networkMappings, ...destParser.getNetworkMap(opts.updatedNetworkMappings), }; } if ( (opts.storageMappings && Object.keys(opts.storageMappings).length) || (opts.updatedStorageMappings && opts.updatedStorageMappings.length) ) { payload.deployment.storage_mappings = { ...opts.storageMappings, ...destParser.getStorageMap( opts.updatedDefaultStorage || opts.defaultStorage, opts.updatedStorageMappings ), }; } const { deployment } = opts; const sourceEnv: any = { ...opts.sourceEnv, }; const updatedSourceEnv = opts.updatedSourceEnv ? sourceParser.getDestinationEnv(opts.updatedSourceEnv) : {}; const sourceMinionPoolId = opts?.updatedSourceEnv?.minion_pool_id || deployment.origin_minion_pool_id; if (sourceMinionPoolId) { payload.deployment.origin_minion_pool_id = sourceMinionPoolId; } payload.deployment.source_environment = { ...sourceEnv, ...updatedSourceEnv, }; const destEnv: any = { ...opts.destEnv, }; const updatedDestEnv = opts.updatedDestEnv ? sourceParser.getDestinationEnv(opts.updatedDestEnv) : {}; const destMinionPoolId = opts?.updatedDestEnv?.minion_pool_id || deployment.destination_minion_pool_id; if (destMinionPoolId) { payload.deployment.destination_minion_pool_id = destMinionPoolId; } const updatedDestEnvMappings = updatedDestEnv[INSTANCE_OSMORPHING_MINION_POOL_MAPPINGS] || {}; const oldMappings = deployment[INSTANCE_OSMORPHING_MINION_POOL_MAPPINGS] || {}; const mergedMappings = { ...oldMappings, ...updatedDestEnvMappings }; if (Object.keys(mergedMappings).length) { const newMappings: any = {}; Object.keys(mergedMappings).forEach(k => { if (mergedMappings[k] !== null) { newMappings[k] = mergedMappings[k]; } }); payload.deployment[INSTANCE_OSMORPHING_MINION_POOL_MAPPINGS] = newMappings; } delete updatedDestEnv[INSTANCE_OSMORPHING_MINION_POOL_MAPPINGS]; payload.deployment.destination_environment = { ...destEnv, ...updatedDestEnv, }; if ( opts.uploadedScripts?.length || opts.removedScripts?.length || deployment.user_scripts ) { payload.deployment.user_scripts = new DefaultOptionsSchemaPlugin().getUserScripts( opts.uploadedScripts || [], opts.removedScripts || [], deployment.user_scripts ); } const response = await Api.send({ url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/deployments`, method: "POST", data: payload, }); return response.data.deployment; } async cancel(deploymentId: string, force?: boolean | null): Promise { const data: any = { cancel: null }; if (force) { data.cancel = { force: true }; } await Api.send({ url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/deployments/${deploymentId}/actions`, method: "POST", data, }); return deploymentId; } async delete(deploymentId: string): Promise { await Api.send({ url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/deployments/${deploymentId}`, method: "DELETE", }); return deploymentId; } async deployTransfer(opts: { transferId: string; options: Field[]; uploadedUserScripts: InstanceScript[]; removedUserScripts: InstanceScript[]; userScriptData: UserScriptData | null | undefined; minionPoolMappings: { [instance: string]: string }; }): Promise { const { transferId: transferId, options, uploadedUserScripts, removedUserScripts, userScriptData, minionPoolMappings, } = opts; const payload: any = { deployment: { transfer_id: transferId, }, }; options.forEach(o => { payload.deployment[o.name] = o.value || o.default || false; }); if ( uploadedUserScripts.length || removedUserScripts.length || userScriptData ) { payload.deployment.user_scripts = new DefaultOptionsSchemaPlugin().getUserScripts( uploadedUserScripts, removedUserScripts, userScriptData ); } if (Object.keys(minionPoolMappings).length) { const newMappings: any = {}; Object.keys(minionPoolMappings).forEach(k => { if (minionPoolMappings[k] !== null) { newMappings[k] = minionPoolMappings[k]; } }); payload.deployment[INSTANCE_OSMORPHING_MINION_POOL_MAPPINGS] = newMappings; } else { payload.deployment[INSTANCE_OSMORPHING_MINION_POOL_MAPPINGS] = null; } const response = await Api.send({ url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/deployments`, method: "POST", data: payload, }); return response.data.deployment; } } export default new DeploymentSource();