/** * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.aurora.scheduler.updater; import java.util.Objects; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import org.apache.aurora.gen.JobUpdatePulseStatus; import org.apache.aurora.scheduler.storage.entities.IInstanceKey; import org.apache.aurora.scheduler.storage.entities.IJobUpdate; import org.apache.aurora.scheduler.storage.entities.IJobUpdateKey; import org.apache.aurora.scheduler.storage.entities.IScheduledTask; import static java.util.Objects.requireNonNull; /** * A controller that exposes commands to initiate and modify active job updates. */ public interface JobUpdateController { /** * Metadata associated with a change to a job update. */ class AuditData { @VisibleForTesting public static final int MAX_MESSAGE_LENGTH = 1024; private final String user; private final Optional<String> message; public AuditData(String user, Optional<String> message) { this.user = requireNonNull(user); if (message.isPresent()) { Preconditions.checkArgument(message.get().length() <= MAX_MESSAGE_LENGTH); } this.message = requireNonNull(message); } public String getUser() { return user; } public Optional<String> getMessage() { return message; } @Override public int hashCode() { return Objects.hash(user, message); } @Override public boolean equals(Object obj) { if (!(obj instanceof AuditData)) { return false; } AuditData other = (AuditData) obj; return Objects.equals(user, other.user) && Objects.equals(message, other.message); } } /** * Initiates an update. * * @param update Instructions for what job to update, and how to update it. * @param auditData Details about the origin of this state change. * @throws UpdateStateException If the update cannot be started, for example if the instructions * are invalid, or if there is already an in-progress update for the * job. */ void start(IJobUpdate update, AuditData auditData) throws UpdateStateException; /** * Pauses an in-progress update. * <p> * A paused update may be resumed by invoking {@link #resume(IJobUpdateKey, AuditData)}. * * @param key Update to pause. * @param auditData Details about the origin of this state change. * @throws UpdateStateException If the job update is not in a state that may be paused. */ void pause(IJobUpdateKey key, AuditData auditData) throws UpdateStateException; /** * Resumes a paused in-progress update. * <p> * The outcome of this call depends on the state the updater was in prior to the pause. If the * updater was rolling forward, it will resume rolling forward. If it was rolling back, it will * resume rolling back. * * @param key Update to resume. * @param auditData Details about the origin of this state change. * @throws UpdateStateException If the job update is not in a state that may be resumed. */ void resume(IJobUpdateKey key, AuditData auditData) throws UpdateStateException; /** * Aborts an in-progress update. * <p> * This will abandon the update, and make no further modifications to the job on behalf of the * update. An aborted update may not be resumed. * * @param key Update to abort. * @param auditData Details about the origin of this state change. * @throws UpdateStateException If there is no active update for the job. */ void abort(IJobUpdateKey key, AuditData auditData) throws UpdateStateException; /** * Rollbacks an active job update. * <p> * This will rollback the update to its initial state effectively 'undoing' it. * The rollback is possible if update is in following states: * <ul> * <li>ROLLING_FORWARD</li> * <li>ROLL_BACK_PAUSED</li> * <li>ROLL_BACK_AWAITING_PULSE</li> * <li>ROLL_FORWARD_PAUSED</li> * <li>ROLL_FORWARD_AWAITING_PULSE</li> * </ul> * has not reached its terminal state yet. * * @param key Update to rollback. * @param auditData Details about the origin of this state change. * @throws UpdateStateException If pre-condition is not met. */ void rollback(IJobUpdateKey key, AuditData auditData) throws UpdateStateException; /** * Notifies the updater that the state of an instance has changed. A state change could also mean * deletion. * * @param updatedTask The latest state for the task that changed. */ void instanceChangedState(IScheduledTask updatedTask); /** * Notifies the updater that an instance was deleted. * * @param instance Identifier of the deleted instance. */ void instanceDeleted(IInstanceKey instance); /** * Restores active updates that have been halted due to the scheduler restarting. * This is distinct from {@link #resume(IJobUpdateKey, AuditData)} in that it does not change the * state of updates, but resumes after a restart of the scheduler process. */ void systemResume(); /** * Resets the update pulse timeout specified by the * {@link org.apache.aurora.gen.JobUpdateSettings#getBlockIfNoPulsesAfterMs}. Unblocks progress * if the update was previously blocked. * * @param key Update identifier. * @return Job update pulse status. * @throws UpdateStateException If there is no update found or update is not coordinated. */ JobUpdatePulseStatus pulse(IJobUpdateKey key) throws UpdateStateException; }