<commit_before>/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0; you may not use this file except in compliance with the Elastic License
 * 2.0.
 */
package org.elasticsearch.xpack.ml;

import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.support.ActionFilter;
import org.elasticsearch.cluster.ClusterChangedEvent;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.xpack.core.ml.MlMetadata;
import org.elasticsearch.xpack.core.ml.action.CloseJobAction;
import org.elasticsearch.xpack.core.ml.action.DeleteCalendarAction;
import org.elasticsearch.xpack.core.ml.action.DeleteCalendarEventAction;
import org.elasticsearch.xpack.core.ml.action.DeleteDataFrameAnalyticsAction;
import org.elasticsearch.xpack.core.ml.action.DeleteDatafeedAction;
import org.elasticsearch.xpack.core.ml.action.DeleteExpiredDataAction;
import org.elasticsearch.xpack.core.ml.action.DeleteFilterAction;
import org.elasticsearch.xpack.core.ml.action.DeleteForecastAction;
import org.elasticsearch.xpack.core.ml.action.DeleteJobAction;
import org.elasticsearch.xpack.core.ml.action.DeleteModelSnapshotAction;
import org.elasticsearch.xpack.core.ml.action.DeleteTrainedModelAction;
import org.elasticsearch.xpack.core.ml.action.FinalizeJobExecutionAction;
import org.elasticsearch.xpack.core.ml.action.FlushJobAction;
import org.elasticsearch.xpack.core.ml.action.ForecastJobAction;
import org.elasticsearch.xpack.core.ml.action.KillProcessAction;
import org.elasticsearch.xpack.core.ml.action.OpenJobAction;
import org.elasticsearch.xpack.core.ml.action.PersistJobAction;
import org.elasticsearch.xpack.core.ml.action.PostCalendarEventsAction;
import org.elasticsearch.xpack.core.ml.action.PostDataAction;
import org.elasticsearch.xpack.core.ml.action.PutCalendarAction;
import org.elasticsearch.xpack.core.ml.action.PutDataFrameAnalyticsAction;
import org.elasticsearch.xpack.core.ml.action.PutDatafeedAction;
import org.elasticsearch.xpack.core.ml.action.PutFilterAction;
import org.elasticsearch.xpack.core.ml.action.PutJobAction;
import org.elasticsearch.xpack.core.ml.action.PutTrainedModelAction;
import org.elasticsearch.xpack.core.ml.action.RevertModelSnapshotAction;
import org.elasticsearch.xpack.core.ml.action.StartDataFrameAnalyticsAction;
import org.elasticsearch.xpack.core.ml.action.StartDatafeedAction;
import org.elasticsearch.xpack.core.ml.action.StopDataFrameAnalyticsAction;
import org.elasticsearch.xpack.core.ml.action.StopDatafeedAction;
import org.elasticsearch.xpack.core.ml.action.UpdateCalendarJobAction;
import org.elasticsearch.xpack.core.ml.action.UpdateDatafeedAction;
import org.elasticsearch.xpack.core.ml.action.UpdateFilterAction;
import org.elasticsearch.xpack.core.ml.action.UpdateJobAction;
import org.elasticsearch.xpack.core.ml.action.UpdateModelSnapshotAction;
import org.elasticsearch.xpack.core.ml.action.UpdateProcessAction;

import java.util.Collections;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

/**
 * {@link MlUpgradeModeActionFilter} disallows certain actions if the cluster is currently in upgrade mode.
 *
 * Disallowed actions are the ones which can access/alter the state of ML internal indices.
 *
 * There is a complication that the feature reset API knows nothing about ML upgrade mode.  If the feature
 * reset API is called while ML upgrade mode is enabled then it takes precedence and resets the ML state.
 * This means that all ML entities will be deleted and upgrade mode will be disabled if the reset completes
 * successfully.
 */
class MlUpgradeModeActionFilter extends ActionFilter.Simple {

    private static final Set<String> ACTIONS_DISALLOWED_IN_UPGRADE_MODE =
        Collections.unmodifiableSet(Sets.newHashSet(
            PutJobAction.NAME,
            UpdateJobAction.NAME,
            DeleteJobAction.NAME,
            OpenJobAction.NAME,
            FlushJobAction.NAME,
            CloseJobAction.NAME,
            PersistJobAction.NAME,

            FinalizeJobExecutionAction.NAME,
            PostDataAction.NAME,

            RevertModelSnapshotAction.NAME,
            UpdateModelSnapshotAction.NAME,
            DeleteModelSnapshotAction.NAME,

            PutDatafeedAction.NAME,
            UpdateDatafeedAction.NAME,
            DeleteDatafeedAction.NAME,
            StartDatafeedAction.NAME,
            StopDatafeedAction.NAME,

            PutFilterAction.NAME,
            UpdateFilterAction.NAME,
            DeleteFilterAction.NAME,

            PutCalendarAction.NAME,
            UpdateCalendarJobAction.NAME,
            PostCalendarEventsAction.NAME,
            DeleteCalendarAction.NAME,
            DeleteCalendarEventAction.NAME,

            UpdateProcessAction.NAME,
            KillProcessAction.NAME,

            DeleteExpiredDataAction.NAME,

            ForecastJobAction.NAME,
            DeleteForecastAction.NAME,

            PutDataFrameAnalyticsAction.NAME,
            DeleteDataFrameAnalyticsAction.NAME,
            StartDataFrameAnalyticsAction.NAME,
            StopDataFrameAnalyticsAction.NAME,

            PutTrainedModelAction.NAME,
            DeleteTrainedModelAction.NAME
        ));

    private static final Set<String> RESET_MODE_EXEMPTIONS =
        Collections.unmodifiableSet(Sets.newHashSet(
            DeleteJobAction.NAME,
            CloseJobAction.NAME,
<commit_msg>[ML] prevent APIs from being called when upgrade mode is set (#71654)<commit_after>/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0; you may not use this file except in compliance with the Elastic License
 * 2.0.
 */
package org.elasticsearch.xpack.ml;

import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.support.ActionFilter;
import org.elasticsearch.cluster.ClusterChangedEvent;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.xpack.core.ml.MlMetadata;
import org.elasticsearch.xpack.core.ml.action.CloseJobAction;
import org.elasticsearch.xpack.core.ml.action.DeleteCalendarAction;
import org.elasticsearch.xpack.core.ml.action.DeleteCalendarEventAction;
import org.elasticsearch.xpack.core.ml.action.DeleteDataFrameAnalyticsAction;
import org.elasticsearch.xpack.core.ml.action.DeleteDatafeedAction;
import org.elasticsearch.xpack.core.ml.action.DeleteExpiredDataAction;
import org.elasticsearch.xpack.core.ml.action.DeleteFilterAction;
import org.elasticsearch.xpack.core.ml.action.DeleteForecastAction;
import org.elasticsearch.xpack.core.ml.action.DeleteJobAction;
import org.elasticsearch.xpack.core.ml.action.DeleteModelSnapshotAction;
import org.elasticsearch.xpack.core.ml.action.DeleteTrainedModelAction;
import org.elasticsearch.xpack.core.ml.action.DeleteTrainedModelAliasAction;
import org.elasticsearch.xpack.core.ml.action.ExplainDataFrameAnalyticsAction;
import org.elasticsearch.xpack.core.ml.action.FinalizeJobExecutionAction;
import org.elasticsearch.xpack.core.ml.action.FlushJobAction;
import org.elasticsearch.xpack.core.ml.action.ForecastJobAction;
import org.elasticsearch.xpack.core.ml.action.KillProcessAction;
import org.elasticsearch.xpack.core.ml.action.OpenJobAction;
import org.elasticsearch.xpack.core.ml.action.PersistJobAction;
import org.elasticsearch.xpack.core.ml.action.PostCalendarEventsAction;
import org.elasticsearch.xpack.core.ml.action.PostDataAction;
import org.elasticsearch.xpack.core.ml.action.PutCalendarAction;
import org.elasticsearch.xpack.core.ml.action.PutDataFrameAnalyticsAction;
import org.elasticsearch.xpack.core.ml.action.PutDatafeedAction;
import org.elasticsearch.xpack.core.ml.action.PutFilterAction;
import org.elasticsearch.xpack.core.ml.action.PutJobAction;
import org.elasticsearch.xpack.core.ml.action.PutTrainedModelAction;
import org.elasticsearch.xpack.core.ml.action.PutTrainedModelAliasAction;
import org.elasticsearch.xpack.core.ml.action.RevertModelSnapshotAction;
import org.elasticsearch.xpack.core.ml.action.StartDataFrameAnalyticsAction;
import org.elasticsearch.xpack.core.ml.action.StartDatafeedAction;
import org.elasticsearch.xpack.core.ml.action.StopDataFrameAnalyticsAction;
import org.elasticsearch.xpack.core.ml.action.StopDatafeedAction;
import org.elasticsearch.xpack.core.ml.action.UpdateCalendarJobAction;
import org.elasticsearch.xpack.core.ml.action.UpdateDatafeedAction;
import org.elasticsearch.xpack.core.ml.action.UpdateFilterAction;
import org.elasticsearch.xpack.core.ml.action.UpdateJobAction;
import org.elasticsearch.xpack.core.ml.action.UpdateModelSnapshotAction;
import org.elasticsearch.xpack.core.ml.action.UpdateProcessAction;
import org.elasticsearch.xpack.core.ml.action.UpgradeJobModelSnapshotAction;

import java.util.Collections;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

/**
 * {@link MlUpgradeModeActionFilter} disallows certain actions if the cluster is currently in upgrade mode.
 *
 * Disallowed actions are the ones which can access/alter the state of ML internal indices.
 *
 * There is a complication that the feature reset API knows nothing about ML upgrade mode.  If the feature
 * reset API is called while ML upgrade mode is enabled then it takes precedence and resets the ML state.
 * This means that all ML entities will be deleted and upgrade mode will be disabled if the reset completes
 * successfully.
 */
class MlUpgradeModeActionFilter extends ActionFilter.Simple {

    private static final Set<String> ACTIONS_DISALLOWED_IN_UPGRADE_MODE =
        Collections.unmodifiableSet(Sets.newHashSet(
            PutJobAction.NAME,
            UpdateJobAction.NAME,
            DeleteJobAction.NAME,
            OpenJobAction.NAME,
            FlushJobAction.NAME,
            CloseJobAction.NAME,
            PersistJobAction.NAME,

            FinalizeJobExecutionAction.NAME,
            PostDataAction.NAME,

            RevertModelSnapshotAction.NAME,
            UpdateModelSnapshotAction.NAME,
            DeleteModelSnapshotAction.NAME,
            UpgradeJobModelSnapshotAction.NAME,

            PutDatafeedAction.NAME,
            UpdateDatafeedAction.NAME,
            DeleteDatafeedAction.NAME,
            StartDatafeedAction.NAME,
            StopDatafeedAction.NAME,

            PutFilterAction.NAME,
            UpdateFilterAction.NAME,
            DeleteFilterAction.NAME,

            PutCalendarAction.NAME,
            UpdateCalendarJobAction.NAME,
            PostCalendarEventsAction.NAME,
            DeleteCalendarAction.NAME,
            DeleteCalendarEventAction.NAME,

            UpdateProcessAction.NAME,
            KillProcessAction.NAME,

            DeleteExpiredDataAction.NAME,

            ForecastJobAction.NAME,
            DeleteForecastAction.NAME,

            PutDataFrameAnalyticsAction.NAME,
            DeleteDataFrameAnalyticsAction.NAME,
            StartDataFrameAnalyticsAction.NAME,
            StopDataFrameAnalyticsAction.NAME,
            ExplainDataFrameAnalyticsAction.NAME,

            PutTrainedModelAliasAction.NAME,
            PutTrainedModelAction.NAME,
            DeleteTrainedModelAction.NAME,
            DeleteTrainedModelAliasAction.NAME
        ));

    private static final Set<String> RESET_MODE_EXEMPTIONS =
        Collections.unmodifiableSet(Sets.newHashSet(
            DeleteJobAction.NAME,
            CloseJobAction.NAME,

{'fraction_non_alphanumeric': 0.07829211229946524, 'fraction_numerical': 0.0010862299465240642, 'mean_word_length': 4.054476351351352, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2502090', 'n_tokens_mistral': 3224, 'n_tokens_neox': 3278, 'n_words': 571}