/* Copyright (C) 2020 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 moment from 'moment' import Api from '../utils/ApiCaller' import configLoader from '../utils/Config' import { MinionPool, MinionPoolDetails } from '../@types/MinionPool' import { ProviderTypes } from '../@types/Providers' import { Field } from '../@types/Field' import { providerTypes } from '../constants' import { SchemaParser } from './Schemas' import { OptionValues } from '../@types/Endpoint' import { MinionPoolAction } from '../stores/MinionPoolStore' import { Execution, ExecutionTasks } from '../@types/Execution' import { sortTasks } from './ReplicaSource' import { ProgressUpdate } from '../@types/Task' const transformFieldsToPayload = (schema: Field[], data: any) => { const payload: any = {} schema.forEach(field => { if (data[field.name] === null || data[field.name] === undefined || data[field.name] === '') { if (field.default !== null) { payload[field.name] = field.default } } else { payload[field.name] = data[field.name] } }) return payload } class MinionPoolSource { getMinionPoolDefaultSchema(): Field[] { return [ { name: 'endpoint_id', label: 'Endpoint', type: 'string', }, { name: 'pool_platform', type: 'string', }, { name: 'pool_name', type: 'string', required: true, }, { name: 'pool_os_type', type: 'string', required: true, enum: [ { value: 'linux', label: 'Linux', }, { value: 'windows', label: 'Windows', }, ], }, { name: 'minimum_minions', type: 'integer', minimum: 1, default: 1, }, { name: 'notes', type: 'string', }, ] } async loadMinionPools(options?: { skipLog?: boolean }): Promise { const response = await Api.send({ url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/minion_pools`, skipLog: options?.skipLog, }) return response.data.minion_pools } async loadEnvOptions(endpointId: string, platform: 'source' | 'destination', useCache?: boolean): Promise { const response = await Api.send({ url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/endpoints/${endpointId}/${platform}-minion-pool-options`, cache: useCache, }) return response.data[`${platform}_minion_pool_options`] } async loadMinionPoolSchema(providerName: ProviderTypes, platform: 'source' | 'destination'): Promise { const providerType = platform === 'source' ? providerTypes.SOURCE_MINION_POOL : providerTypes.DESTINATION_MINION_POOL try { const response = await Api.send({ url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/providers/${providerName}/schemas/${providerType}`, }) const schema = response.data?.schemas?.[`${platform}_minion_pool_environment_schema`] let fields = [] if (schema) { fields = SchemaParser.optionsSchemaToFields(providerName, schema, `${providerName}-minion-pool`) } return fields } catch (err) { console.error(err) return [] } } async add(endpointId: string, data: any, defaultSchema: Field[], envSchema: Field[]) { const payload = { minion_pool: { ...transformFieldsToPayload(defaultSchema, data), endpoint_id: endpointId, environment_options: { ...transformFieldsToPayload(envSchema, data), }, }, } const response = await Api.send({ url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/minion_pools`, method: 'POST', data: payload, }) return response.data.minion_pool } async update(data: any, defaultSchema: Field[], envSchema: Field[]) { const payload = { minion_pool: { ...transformFieldsToPayload(defaultSchema, data), environment_options: { ...transformFieldsToPayload(envSchema, data), }, }, } const response = await Api.send({ url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/minion_pools/${data.id}`, method: 'PUT', data: payload, }) return response.data.minion_pool } async runAction(minionPoolId: string, minionPoolAction: MinionPoolAction): Promise { const payload: any = {} payload[minionPoolAction] = null const response = await Api.send({ url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/minion_pools/${minionPoolId}/actions`, method: 'POST', data: payload, }) return response.data.execution } async getMinionPoolDetails( minionPoolId: string, options?: { skipLog?: boolean }, ): Promise { const response = await Api.send({ url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/minion_pools/${minionPoolId}`, skipLog: options?.skipLog, }) const minionPool: MinionPoolDetails = response.data.minion_pool minionPool.executions.sort((a, b) => a.number - b.number) return minionPool } async cancelExecution(minionPoolId: string, force?: boolean, executionId?: string) { let usableExecutionId = executionId if (!usableExecutionId) { const details = await this.getMinionPoolDetails(minionPoolId) const lastExecution = details.executions[details.executions.length - 1] if (!lastExecution) { return null } usableExecutionId = lastExecution.id } const payload: any = { cancel: { force: force || false } } await Api.send({ url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/minion_pools/${minionPoolId}/executions/${usableExecutionId}/actions`, method: 'POST', data: payload, }) return null } async deleteMinionPool(minionPoolId: string) { const response = await Api.send({ url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/minion_pools/${minionPoolId}`, method: 'DELETE', }) return response.data.execution } async getExecutionTasks(options: { minionPoolId: string, executionId?: string, skipLog?: boolean, }): Promise { const { minionPoolId, executionId, skipLog, } = options const response = await Api.send({ url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/minion_pools/${minionPoolId}/executions/${executionId}`, skipLog, quietError: true, }) const execution: ExecutionTasks = response.data.execution const sortTaskUpdates = (updates: ProgressUpdate[]) => { if (!updates) { return } updates.sort((a, b) => moment(a.created_at) .toDate().getTime() - moment(b.created_at).toDate().getTime()) } sortTasks(execution.tasks, sortTaskUpdates) return execution } } export default new MinionPoolSource()