import { TypeOf } from '@kbn/config-schema' ; import type { SnapshotGetRepositoryResponse , SnapshotRepositorySettings , PluginStats , } from '@elastic/elasticsearch/lib/api/typesWithBodyKey' ; import { DEFAULT_REPOSITORY_TYPES , REPOSITORY_PLUGINS_MAP } from '../../../common' ; import { Repository , RepositoryType } from '../../../common/types' ; import { RouteDependencies } from '../../types' ; import { addBasePath } from '../helpers' ; import { nameParameterSchema , repositorySchema } from './validate_schemas' ; import { deserializeRepositorySettings , serializeRepositorySettings , getManagedRepositoryName , } from '../../lib' ; interface ManagedRepository { name ? : string ; policy ? : string ; } export function registerRepositoriesRoutes ( { router , license , config : { isCloudEnabled } , lib : { wrapEsError , handleEsError } , } ) { router . get ( { path : addBasePath ( 'repositories' ) , validate : false } , license . guardApiRoute ( async ( ctx , req , res ) => { const { client : clusterClient } = ctx . core . elasticsearch ; const managedRepositoryName = await getManagedRepositoryName ( clusterClient . asCurrentUser ) ; let repositoryNames ; let repositories ; let managedRepository ; try { const { body : repositoriesByName } = await clusterClient . asCurrentUser . snapshot . getRepository ( { name : '_all' , } ) ; repositoryNames = Object . keys ( repositoriesByName ) ; repositories = repositoryNames . map ( ( name ) => { const { type = '' , settings = { } } = repositoriesByName [ name ] ; return { name , type , settings : deserializeRepositorySettings ( settings ) , } as Repository ; } ) ; managedRepository = { name : managedRepositoryName , } ; } catch ( e ) { return handleEsError ( { error : e , response : res } ) ; } if ( managedRepositoryName ) { try { const { body : policiesByName } = await clusterClient . asCurrentUser . slm . getLifecycle ( { human : true , } ) ; const managedRepositoryPolicy = Object . entries ( policiesByName ) . filter ( ( [ , data ] ) => { const { policy } = data ; return policy . repository === managedRepositoryName ; } ) . flat ( ) ; const [ policyName ] = managedRepositoryPolicy ; managedRepository . policy = policyName as ManagedRepository [ 'name' ] ; } catch ( e ) { } } return res . ok ( { body : { repositories , managedRepository } } ) ; } ) ) ; router . get ( { path : addBasePath ( 'repositories/{name}' ) , validate : { params : nameParameterSchema } } , license . guardApiRoute ( async ( ctx , req , res ) => { const { client : clusterClient } = ctx . core . elasticsearch ; const { name } = req . params as TypeOf < typeof nameParameterSchema > ; const managedRepository = await getManagedRepositoryName ( clusterClient . asCurrentUser ) ; let repositoryByName ; try { ( { body : repositoryByName } = await clusterClient . asCurrentUser . snapshot . getRepository ( { name , } ) ) ; } catch ( e ) { return handleEsError ( { error : e , response : res } ) ; } const { body : { snapshots : snapshotList } , } = await clusterClient . asCurrentUser . snapshot . get ( { repository : name , snapshot : '_all' , } ) . catch ( ( e ) => ( { body : { snapshots : null , } , } ) ) ; if ( repositoryByName [ name ] ) { const { type = '' , settings = { } } = repositoryByName [ name ] ; return res . ok ( { body : { repository : { name , type , settings : deserializeRepositorySettings ( settings ) , } , isManagedRepository : managedRepository === name , snapshots : { count : snapshotList ? snapshotList . length : null , } , } , } ) ; } return res . ok ( { body : { repository : { } , snapshots : { } , } , } ) ; } ) ) ; router . get ( { path : addBasePath ( 'repository_types' ) , validate : false } , license . guardApiRoute ( async ( ctx , req , res ) => { const { client : clusterClient } = ctx . core . elasticsearch ; const types = isCloudEnabled ? [ ] : [ ... DEFAULT_REPOSITORY_TYPES ] ; try { const { body : { nodes } , } = await clusterClient . asCurrentUser . nodes . info ( { node_id : '_all' , metric : 'plugins' , } ) ; const pluginNamesAllNodes = Object . keys ( nodes ) . map ( ( key ) => { return ( nodes [ key ] . plugins ?? [ ] ) . map ( ( plugin ) => plugin . name ) ; } ) ; Object . keys ( REPOSITORY_PLUGINS_MAP ) . forEach ( ( repoTypeName ) => { if ( pluginNamesAllNodes . every ( ( pluginNames ) => pluginNames . includes ( repoTypeName ) ) ) { types . push ( REPOSITORY_PLUGINS_MAP [ repoTypeName ] ) ; } } ) ; return res . ok ( { body : types } ) ; } catch ( e ) { return handleEsError ( { error : e , response : res } ) ; } } ) ) ; router . get ( { path : addBasePath ( 'repositories/{name}/verify' ) , validate : { params : nameParameterSchema } , } , license . guardApiRoute ( async ( ctx , req , res ) => { const { client : clusterClient } = ctx . core . elasticsearch ; const { name } = req . params as TypeOf < typeof nameParameterSchema > ; try { const { body : verificationResults } = await clusterClient . asCurrentUser . snapshot . verifyRepository ( { name } ) . catch ( ( e ) => ( { body : { valid : false , error : e . response ? JSON . parse ( e . response ) : e , } , } ) ) ; return res . ok ( { body : { verification : ( verificationResults as { error ? : Error ; } ) . error ? verificationResults : { valid : true , response : verificationResults , } , } , } ) ; } catch ( e ) { return handleEsError ( { error : e , response : res } ) ; } } ) ) ; router . post ( { path : addBasePath ( 'repositories/{name}/cleanup' ) , validate : { params : nameParameterSchema } , } , license . guardApiRoute ( async ( ctx , req , res ) => { const { client : clusterClient } = ctx . core . elasticsearch ; const { name } = req . params as TypeOf < typeof nameParameterSchema > ; try { const { body : cleanupResults } = await clusterClient . asCurrentUser . snapshot . cleanupRepository ( { name } ) . catch ( ( e ) => { const normalizedError = { statusCode : e . meta . body . status , response : e . meta . body , } ; return { body : { cleaned : false , error : wrapEsError ( normalizedError ) , } , } ; } ) ; return res . ok ( { body : { cleanup : ( cleanupResults as { error ? : Error ; } ) . error ? cleanupResults : { cleaned : true , response : cleanupResults , } , } , } ) ; } catch ( e ) { return handleEsError ( { error : e , response : res } ) ; } } ) ) ; router . put ( { path : addBasePath ( 'repositories' ) , validate : { body : repositorySchema } } , license . guardApiRoute ( async ( ctx , req , res ) => { const { client : clusterClient } = ctx . core . elasticsearch ; const { name = '' , type = '' , settings = { } } = req . body as TypeOf < typeof repositorySchema > ; try { const { body : repositoryByName } = await clusterClient . asCurrentUser . snapshot . getRepository ( { name } ) ; if ( repositoryByName [ name ] ) { return res . conflict ( { body : 'There is already a repository with that name.' } ) ; } } catch ( e ) { } try { const response = await clusterClient . asCurrentUser . snapshot . createRepository ( { name , body : { type , settings : serializeRepositorySettings ( settings ) as SnapshotRepositorySettings , } , verify : false , } ) ; return res . ok ( { body : response . body } ) ; } catch ( e ) { return handleEsError ( { error : e , response : res } ) ; } } ) ) ; router . put ( { path : addBasePath ( 'repositories/{name}' ) , validate : { body : repositorySchema , params : nameParameterSchema } , } , license . guardApiRoute ( async ( ctx , req , res ) => { const { client : clusterClient } = ctx . core . elasticsearch ; const { name } = req . params as TypeOf < typeof nameParameterSchema > ; const { type = '' , settings = { } } = req . body as TypeOf < typeof repositorySchema > ; try { await clusterClient . asCurrentUser . snapshot . getRepository ( { name } ) ; const response = await clusterClient . asCurrentUser . snapshot . createRepository ( { name , body : { type , settings : serializeRepositorySettings ( settings ) as SnapshotRepositorySettings , } , verify : false , } ) ; return res . ok ( { body : response . body , } ) ; } catch ( e ) { return handleEsError ( { error : e , response : res } ) ; } } ) ) ; router . delete ( { path : addBasePath ( 'repositories/{name}' ) , validate : { params : nameParameterSchema } } , license . guardApiRoute ( async ( ctx , req , res ) => { const { client : clusterClient } = ctx . core . elasticsearch ; const { name } = req . params as TypeOf < typeof nameParameterSchema > ; const repositoryNames = name . split ( ',' ) ; const response = { itemsDeleted : [ ] , errors : [ ] , } ; try { await Promise . all ( repositoryNames . map ( ( repoName ) => { return clusterClient . asCurrentUser . snapshot . deleteRepository ( { name : repoName } ) . then ( ( ) => response . itemsDeleted . push ( repoName ) ) . catch ( ( e ) => response . errors . push ( { name : repoName , error : wrapEsError ( e ) , } ) ) ; } ) ) ; return res . ok ( { body : response } ) ; } catch ( e ) { return handleEsError ( { error : e , response : res } ) ; } } ) ) ; }