@@ -14,13 +14,16 @@ import org.opensearch.action.admin.indices.create.CreateIndexRequest
1414import org.opensearch.action.admin.indices.create.CreateIndexResponse
1515import org.opensearch.action.admin.indices.mapping.get.GetMappingsRequest
1616import org.opensearch.action.admin.indices.mapping.get.GetMappingsResponse
17+ import org.opensearch.action.admin.indices.mapping.put.PutMappingRequest
18+ import org.opensearch.action.admin.indices.settings.put.UpdateSettingsRequest
1719import org.opensearch.action.support.IndicesOptions
1820import org.opensearch.action.support.master.AcknowledgedResponse
1921import org.opensearch.client.Client
2022import org.opensearch.cluster.metadata.IndexNameExpressionResolver
2123import org.opensearch.cluster.metadata.MappingMetadata
2224import org.opensearch.cluster.service.ClusterService
2325import org.opensearch.common.settings.Settings
26+ import org.opensearch.common.xcontent.XContentType
2427import org.opensearch.indexmanagement.IndexManagementIndices
2528import org.opensearch.indexmanagement.common.model.dimension.DateHistogram
2629import org.opensearch.indexmanagement.common.model.dimension.Histogram
@@ -32,6 +35,7 @@ import org.opensearch.indexmanagement.rollup.model.Rollup
3235import org.opensearch.indexmanagement.rollup.model.RollupJobValidationResult
3336import org.opensearch.indexmanagement.rollup.settings.LegacyOpenDistroRollupSettings
3437import org.opensearch.indexmanagement.rollup.settings.RollupSettings
38+ import org.opensearch.indexmanagement.rollup.util.RollupFieldValueExpressionResolver
3539import org.opensearch.indexmanagement.rollup.util.isRollupIndex
3640import org.opensearch.indexmanagement.util.IndexUtils.Companion._META
3741import org.opensearch.indexmanagement.util.IndexUtils.Companion.getFieldFromMappings
@@ -52,14 +56,33 @@ class RollupMapperService(
5256 // If the index already exists we need to verify it's a rollup index,
5357 // confirm it does not conflict with existing jobs and is a valid job
5458 @Suppress(" ReturnCount" )
55- private suspend fun validateAndAttemptToUpdateTargetIndex (rollup : Rollup ): RollupJobValidationResult {
56- if (! isRollupIndex(rollup.targetIndex, clusterService.state())) {
57- return RollupJobValidationResult .Invalid (" Target index [${rollup.targetIndex} ] is a non rollup index" )
58- }
59+ private suspend fun validateAndAttemptToUpdateTargetIndex (rollup : Rollup , targetIndexResolvedName : String , hasLegacyPlugin : Boolean ): RollupJobValidationResult {
60+ if (! isRollupIndex(targetIndexResolvedName, clusterService.state()) &&
61+ RollupFieldValueExpressionResolver .hasAlias(targetIndexResolvedName)) {
5962
60- return when (val jobExistsResult = jobExistsInRollupIndex(rollup)) {
63+ val backingIndices = RollupFieldValueExpressionResolver .getBackingIndicesForAlias(targetIndexResolvedName)
64+ backingIndices?.forEach {
65+ if (it.index.name != targetIndexResolvedName) {
66+ when (val jobExistsResult = jobExistsInRollupIndex(rollup, it.index.name)) {
67+ is RollupJobValidationResult .Invalid , is RollupJobValidationResult .Failure -> return jobExistsResult
68+ }
69+ }
70+ }
71+ val mappings = getMappings(targetIndexResolvedName)
72+ if (mappings is GetMappingsResult .Failure ) {
73+ return RollupJobValidationResult .Failure (" Failed to get mappings for target index: $targetIndexResolvedName " )
74+ } else if (mappings is GetMappingsResult .Success && mappings.response.mappings()?.get(targetIndexResolvedName)?.sourceAsMap().isNullOrEmpty() == false ) {
75+ return RollupJobValidationResult .Failure (" If target_index is alias, backing index must be empty: $targetIndexResolvedName " )
76+ }
77+ return prepareTargetIndex(rollup, targetIndexResolvedName, hasLegacyPlugin)
78+ } else {
79+ if (! isRollupIndex(targetIndexResolvedName, clusterService.state())) {
80+ return RollupJobValidationResult .Invalid (" Target index [$targetIndexResolvedName ] is a non rollup index" )
81+ }
82+ }
83+ return when (val jobExistsResult = jobExistsInRollupIndex(rollup, targetIndexResolvedName)) {
6184 is RollupJobValidationResult .Valid -> jobExistsResult
62- is RollupJobValidationResult .Invalid -> updateRollupIndexMappings(rollup)
85+ is RollupJobValidationResult .Invalid -> updateRollupIndexMappings(rollup, targetIndexResolvedName )
6386 is RollupJobValidationResult .Failure -> jobExistsResult
6487 }
6588 }
@@ -69,14 +92,15 @@ class RollupMapperService(
6992 // TODO: error handling
7093 @Suppress(" ReturnCount" )
7194 suspend fun attemptCreateRollupTargetIndex (job : Rollup , hasLegacyPlugin : Boolean ): RollupJobValidationResult {
72- if (indexExists(job.targetIndex)) {
73- return validateAndAttemptToUpdateTargetIndex(job)
95+ val targetIndexResolvedName = RollupFieldValueExpressionResolver .resolve(job, job.targetIndex)
96+ if (indexExists(targetIndexResolvedName)) {
97+ return validateAndAttemptToUpdateTargetIndex(job, targetIndexResolvedName, hasLegacyPlugin)
7498 } else {
75- val errorMessage = " Failed to create target index [${job.targetIndex} ]"
99+ val errorMessage = " Failed to create target index [$targetIndexResolvedName ]"
76100 return try {
77- val response = createTargetIndex(job , hasLegacyPlugin)
101+ val response = createTargetIndex(targetIndexResolvedName , hasLegacyPlugin)
78102 if (response.isAcknowledged) {
79- updateRollupIndexMappings(job)
103+ updateRollupIndexMappings(job, targetIndexResolvedName )
80104 } else {
81105 RollupJobValidationResult .Failure (errorMessage)
82106 }
@@ -94,13 +118,57 @@ class RollupMapperService(
94118 }
95119 }
96120
97- private suspend fun createTargetIndex (job : Rollup , hasLegacyPlugin : Boolean ): CreateIndexResponse {
121+ suspend fun prepareTargetIndex (rollup : Rollup , targetIndexResolvedName : String , hasLegacyPlugin : Boolean ): RollupJobValidationResult {
122+ var errorMessage = " "
123+ try {
124+ // 1. First we need to add index.plugins.rollup_index setting to index
125+ val settings = if (hasLegacyPlugin) {
126+ Settings .builder().put(LegacyOpenDistroRollupSettings .ROLLUP_INDEX .key, true ).build()
127+ } else {
128+ Settings .builder().put(RollupSettings .ROLLUP_INDEX .key, true ).build()
129+ }
130+ errorMessage = " Failed to update settings for target index [$targetIndexResolvedName ]"
131+ val resp: AcknowledgedResponse = client.admin().indices().suspendUntil {
132+ updateSettings(UpdateSettingsRequest (settings, targetIndexResolvedName), it)
133+ }
134+ if (! resp.isAcknowledged) {
135+ return RollupJobValidationResult .Invalid (" Failed to update rollup settings for target index: [$targetIndexResolvedName ]" )
136+ }
137+
138+ // 2. Put rollup mappings
139+ val putMappingRequest: PutMappingRequest =
140+ PutMappingRequest (targetIndexResolvedName).source(IndexManagementIndices .rollupTargetMappings, XContentType .JSON )
141+ errorMessage = " Failed to put initial rollup mappings for target index [$targetIndexResolvedName ]"
142+ val respMappings: AcknowledgedResponse = client.admin().indices().suspendUntil {
143+ putMapping(putMappingRequest, it)
144+ }
145+ if (! respMappings.isAcknowledged) {
146+ return RollupJobValidationResult .Invalid (" Failed to update rollup settings for target index: [$targetIndexResolvedName ]" )
147+ }
148+ // 3.
149+ errorMessage = " Failed to update mappings for target index [$targetIndexResolvedName ]"
150+ updateRollupIndexMappings(rollup, targetIndexResolvedName)
151+ } catch (e: RemoteTransportException ) {
152+ val unwrappedException = ExceptionsHelper .unwrapCause(e) as Exception
153+ logger.error(errorMessage, unwrappedException)
154+ RollupJobValidationResult .Failure (errorMessage, unwrappedException)
155+ } catch (e: OpenSearchSecurityException ) {
156+ logger.error(" $errorMessage because " , e)
157+ RollupJobValidationResult .Failure (" $errorMessage - missing required cluster permissions: ${e.localizedMessage} " , e)
158+ } catch (e: Exception ) {
159+ logger.error(" $errorMessage because " , e)
160+ RollupJobValidationResult .Failure (errorMessage, e)
161+ }
162+ return RollupJobValidationResult .Valid
163+ }
164+
165+ private suspend fun createTargetIndex (targetIndexName : String , hasLegacyPlugin : Boolean ): CreateIndexResponse {
98166 val settings = if (hasLegacyPlugin) {
99167 Settings .builder().put(LegacyOpenDistroRollupSettings .ROLLUP_INDEX .key, true ).build()
100168 } else {
101169 Settings .builder().put(RollupSettings .ROLLUP_INDEX .key, true ).build()
102170 }
103- val request = CreateIndexRequest (job.targetIndex )
171+ val request = CreateIndexRequest (targetIndexName )
104172 .settings(settings)
105173 .mapping(IndexManagementIndices .rollupTargetMappings)
106174 // TODO: Perhaps we can do better than this for mappings... as it'll be dynamic for rest
@@ -204,19 +272,19 @@ class RollupMapperService(
204272 return field != null
205273 }
206274
207- private suspend fun jobExistsInRollupIndex (rollup : Rollup ): RollupJobValidationResult {
208- val res = when (val getMappingsResult = getMappings(rollup.targetIndex )) {
275+ private suspend fun jobExistsInRollupIndex (rollup : Rollup , targetIndexResolvedName : String ): RollupJobValidationResult {
276+ val res = when (val getMappingsResult = getMappings(targetIndexResolvedName )) {
209277 is GetMappingsResult .Success -> getMappingsResult.response
210278 is GetMappingsResult .Failure ->
211279 return RollupJobValidationResult .Failure (getMappingsResult.message, getMappingsResult.cause)
212280 }
213281
214- val indexMapping: MappingMetadata = res.mappings[rollup.targetIndex ]
282+ val indexMapping: MappingMetadata = res.mappings[targetIndexResolvedName ]
215283
216284 return if (((indexMapping.sourceAsMap?.get(_META ) as Map <* , * >? )?.get(ROLLUPS ) as Map <* , * >? )?.containsKey(rollup.id) == true ) {
217285 RollupJobValidationResult .Valid
218286 } else {
219- RollupJobValidationResult .Invalid (" Rollup job [${rollup.id} ] does not exist in rollup index [${rollup.targetIndex} ]" )
287+ RollupJobValidationResult .Invalid (" Rollup job [${rollup.id} ] does not exist in rollup index [$targetIndexResolvedName ]" )
220288 }
221289 }
222290
@@ -254,8 +322,8 @@ class RollupMapperService(
254322 // where they can both get the same mapping state and only add their own job, meaning one
255323 // of the jobs won't be added to the target index _meta
256324 @Suppress(" BlockingMethodInNonBlockingContext" , " ReturnCount" )
257- private suspend fun updateRollupIndexMappings (rollup : Rollup ): RollupJobValidationResult {
258- val errorMessage = " Failed to update mappings of target index [${rollup.targetIndex} ] with rollup job"
325+ private suspend fun updateRollupIndexMappings (rollup : Rollup , targetIndexResolvedName : String ): RollupJobValidationResult {
326+ val errorMessage = " Failed to update mappings of target index [$targetIndexResolvedName ] with rollup job"
259327 try {
260328 val response = withContext(Dispatchers .IO ) {
261329 val resp: AcknowledgedResponse = client.suspendUntil {
0 commit comments