19
19
import org .elasticsearch .action .support .HandledTransportAction ;
20
20
import org .elasticsearch .action .support .master .AcknowledgedResponse ;
21
21
import org .elasticsearch .client .Client ;
22
- import org .elasticsearch .cluster .metadata .Metadata ;
22
+ import org .elasticsearch .cluster .ClusterState ;
23
+ import org .elasticsearch .cluster .service .ClusterService ;
23
24
import org .elasticsearch .common .Strings ;
24
25
import org .elasticsearch .common .collect .Tuple ;
25
26
import org .elasticsearch .common .inject .Inject ;
36
37
import org .elasticsearch .index .reindex .DeleteByQueryAction ;
37
38
import org .elasticsearch .index .reindex .DeleteByQueryRequest ;
38
39
import org .elasticsearch .index .reindex .ScrollableHitSource ;
40
+ import org .elasticsearch .persistent .PersistentTasksCustomMetadata ;
39
41
import org .elasticsearch .rest .RestStatus ;
40
42
import org .elasticsearch .search .SearchHit ;
41
43
import org .elasticsearch .search .SearchHits ;
42
44
import org .elasticsearch .search .builder .SearchSourceBuilder ;
43
45
import org .elasticsearch .tasks .Task ;
44
46
import org .elasticsearch .transport .TransportService ;
47
+ import org .elasticsearch .xpack .core .ml .MlTasks ;
45
48
import org .elasticsearch .xpack .core .ml .action .DeleteForecastAction ;
49
+ import org .elasticsearch .xpack .core .ml .job .config .JobState ;
46
50
import org .elasticsearch .xpack .core .ml .job .messages .Messages ;
47
51
import org .elasticsearch .xpack .core .ml .job .persistence .AnomalyDetectorsIndex ;
48
52
import org .elasticsearch .xpack .core .ml .job .results .Forecast ;
55
59
import java .io .InputStream ;
56
60
import java .util .ArrayList ;
57
61
import java .util .Arrays ;
62
+ import java .util .Collection ;
58
63
import java .util .EnumSet ;
59
64
import java .util .HashSet ;
60
65
import java .util .List ;
@@ -71,21 +76,28 @@ public class TransportDeleteForecastAction extends HandledTransportAction<Delete
71
76
private static final Logger logger = LogManager .getLogger (TransportDeleteForecastAction .class );
72
77
73
78
private final Client client ;
79
+ private final ClusterService clusterService ;
74
80
private static final int MAX_FORECAST_TO_SEARCH = 10_000 ;
75
81
76
82
private static final Set <ForecastRequestStatus > DELETABLE_STATUSES =
77
83
EnumSet .of (ForecastRequestStatus .FINISHED , ForecastRequestStatus .FAILED );
78
84
79
85
@ Inject
80
- public TransportDeleteForecastAction (TransportService transportService , ActionFilters actionFilters , Client client ) {
86
+ public TransportDeleteForecastAction (TransportService transportService ,
87
+ ActionFilters actionFilters ,
88
+ Client client ,
89
+ ClusterService clusterService ) {
81
90
super (DeleteForecastAction .NAME , transportService , actionFilters , DeleteForecastAction .Request ::new );
82
91
this .client = client ;
92
+ this .clusterService = clusterService ;
83
93
}
84
94
85
95
@ Override
86
96
protected void doExecute (Task task , DeleteForecastAction .Request request , ActionListener <AcknowledgedResponse > listener ) {
87
97
final String jobId = request .getJobId ();
88
- final String forecastsExpression = request .getForecastId ();
98
+
99
+ String forecastsExpression = request .getForecastId ();
100
+ final String [] forecastIds = Strings .tokenizeToStringArray (forecastsExpression , "," );
89
101
ActionListener <SearchResponse > forecastStatsHandler = ActionListener .wrap (
90
102
searchResponse -> deleteForecasts (searchResponse , request , listener ),
91
103
e -> listener .onFailure (new ElasticsearchException ("An error occurred while searching forecasts to delete" , e )));
@@ -95,10 +107,8 @@ protected void doExecute(Task task, DeleteForecastAction.Request request, Action
95
107
BoolQueryBuilder builder = QueryBuilders .boolQuery ();
96
108
BoolQueryBuilder innerBool = QueryBuilders .boolQuery ().must (
97
109
QueryBuilders .termQuery (Result .RESULT_TYPE .getPreferredName (), ForecastRequestStats .RESULT_TYPE_VALUE ));
98
-
99
- if (Metadata .ALL .equals (request .getForecastId ()) == false ) {
100
- Set <String > forcastIds = new HashSet <>(Arrays .asList (Strings .tokenizeToStringArray (forecastsExpression , "," )));
101
- innerBool .must (QueryBuilders .termsQuery (Forecast .FORECAST_ID .getPreferredName (), forcastIds ));
110
+ if (Strings .isAllOrWildcard (forecastIds ) == false ) {
111
+ innerBool .must (QueryBuilders .termsQuery (Forecast .FORECAST_ID .getPreferredName (), new HashSet <>(Arrays .asList (forecastIds ))));
102
112
}
103
113
104
114
source .query (builder .filter (innerBool ));
@@ -109,6 +119,17 @@ protected void doExecute(Task task, DeleteForecastAction.Request request, Action
109
119
executeAsyncWithOrigin (client , ML_ORIGIN , SearchAction .INSTANCE , searchRequest , forecastStatsHandler );
110
120
}
111
121
122
+ static void validateForecastState (Collection <ForecastRequestStats > forecastsToDelete , JobState jobState , String jobId ) {
123
+ List <String > badStatusForecasts = forecastsToDelete .stream ()
124
+ .filter ((f ) -> DELETABLE_STATUSES .contains (f .getStatus ()) == false )
125
+ .map (ForecastRequestStats ::getForecastId )
126
+ .collect (Collectors .toList ());
127
+ if (badStatusForecasts .size () > 0 && JobState .OPENED .equals (jobState )) {
128
+ throw ExceptionsHelper .conflictStatusException (
129
+ Messages .getMessage (Messages .REST_CANNOT_DELETE_FORECAST_IN_CURRENT_STATE , badStatusForecasts , jobId ));
130
+ }
131
+ }
132
+
112
133
private void deleteForecasts (SearchResponse searchResponse ,
113
134
DeleteForecastAction .Request request ,
114
135
ActionListener <AcknowledgedResponse > listener ) {
@@ -122,7 +143,7 @@ private void deleteForecasts(SearchResponse searchResponse,
122
143
}
123
144
124
145
if (forecastsToDelete .isEmpty ()) {
125
- if (Metadata . ALL . equals ( request .getForecastId ()) &&
146
+ if (Strings . isAllOrWildcard ( new String []{ request .getForecastId ()} ) &&
126
147
request .isAllowNoForecasts ()) {
127
148
listener .onResponse (new AcknowledgedResponse (true ));
128
149
} else {
@@ -131,13 +152,13 @@ private void deleteForecasts(SearchResponse searchResponse,
131
152
}
132
153
return ;
133
154
}
134
- List < String > badStatusForecasts = forecastsToDelete . stream ()
135
- . filter (( f ) -> ! DELETABLE_STATUSES . contains ( f . getStatus ()))
136
- . map ( ForecastRequestStats :: getForecastId ). collect ( Collectors . toList () );
137
- if ( badStatusForecasts . size () > 0 ) {
138
- listener . onFailure (
139
- ExceptionsHelper . conflictStatusException (
140
- Messages . getMessage ( Messages . REST_CANNOT_DELETE_FORECAST_IN_CURRENT_STATE , badStatusForecasts , jobId )) );
155
+ final ClusterState state = clusterService . state ();
156
+ PersistentTasksCustomMetadata persistentTasks = state . metadata (). custom ( PersistentTasksCustomMetadata . TYPE );
157
+ JobState jobState = MlTasks . getJobState ( jobId , persistentTasks );
158
+ try {
159
+ validateForecastState ( forecastsToDelete , jobState , jobId );
160
+ } catch ( ElasticsearchException ex ) {
161
+ listener . onFailure ( ex );
141
162
return ;
142
163
}
143
164
0 commit comments