Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[V2V] Update Transformation Task in InfraConversionJob #19154

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions app/models/infra_conversion_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ def load_transitions
}
end

# Example state:
# :state_name => {
# :description => 'State description',
# :weight => 30,
# :max_retries => 960
# }
def state_settings
@state_settings ||= {
:running_in_automate => {
Expand All @@ -55,6 +61,49 @@ def migration_task
# valid states: %w(migrate pending finished active queued)
end

def on_entry(state_hash, _)
state_hash || {
:state => 'active',
:status => 'Ok',
:description => state_settings[state.to_sym][:description],
:started_on => Time.now.utc,
:percent => 0.0
}.compact
end

def on_retry(state_hash, state_progress = nil)
if state_progress.nil?
state_hash[:percent] = context["retries_#{state}".to_sym].to_f / state_settings[state.to_sym][:max_retries].to_f * 100.0
else
state_hash.merge!(state_progress)
end
state_hash[:updated_on] = Time.now.utc
state_hash
end

def on_exit(state_hash, _)
state_hash[:state] = 'finished'
state_hash[:percent] = 100.0
state_hash[:updated_on] = Time.now.utc
state_hash
end

def on_error(state_hash, _)
state_hash[:state] = 'finished'
state_hash[:status] = 'Error'
state_hash[:updated_on] = Time.now.utc
state_hash
end

def update_migration_task_progress(state_phase, state_progress = nil)
progress = migration_task.options[:progress] || { :current_state => state, :percent => 0.0, :states => {} }
state_hash = send(state_phase, progress[:states][state.to_sym], state_progress)
progress[:states][state.to_sym] = state_hash
progress[:current_description] = state_settings[state.to_sym][:description] if state_phase == :on_entry && state_settings[state.to_sym][:description].present?
progress[:percent] += state_hash[:percent] * state_settings[state.to_sym][:weight] / 100.0 if state_settings[state.to_sym][:weight].present?
migration_task.update_transformation_progress(progress)
end

# Temporary method to allow switching from InfraConversionJob to Automate.
# In Automate, another method waits for workflow_runner to be 'automate'.
def handover_to_automate
Expand Down Expand Up @@ -94,6 +143,8 @@ def prep_message(contents)

# --- Methods that implement the state machine transitions --- #

# This transition simply allows to officially mark the task as migrating.
# Temporarily, it also hands over to Automate.
def start
migration_task.update!(:state => 'migrate')
handover_to_automate
Expand Down
250 changes: 249 additions & 1 deletion spec/models/infra_conversion_job_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,254 @@
end
end

context 'state hash methods' do
before do
job.state = 'running_in_automate'
job.context[:retries_running_in_automate] = 1728
end

context '.on_entry' do
it 'initializes the state hash if it did not exist' do
Timecop.freeze(2019, 2, 6) do
expect(job.on_entry(nil, nil)).to eq(
:state => 'active',
:status => 'Ok',
:started_on => Time.now.utc,
:percent => 0.0
)
end
end
end

context '.on_retry' do
it 'uses ad-hoc percentage if no progress is provided' do
Timecop.freeze(2019, 2, 6) do
state_hash = {
:state => 'active',
:status => 'Ok',
:started_on => Time.now.utc - 1.minute,
:percent => 10.0
}
state_hash_diff = {
:percent => 20.0,
:updated_on => Time.now.utc
}
expect(job.on_retry(state_hash, nil)).to eq(state_hash.merge(state_hash_diff))
end
end

it 'uses percentage from progress hash' do
Timecop.freeze(2019, 2, 6) do
state_hash = {
:state => 'active',
:status => 'Ok',
:started_on => Time.now.utc - 1.minute,
:percent => 10.0
}
state_hash_diff = {
:percent => 25.0,
:updated_on => Time.now.utc
}
expect(job.on_retry(state_hash, :percent => 25.0)).to eq(state_hash.merge(state_hash_diff))
end
end
end

context '.on_exit' do
it 'uses percentage from progress hash' do
Timecop.freeze(2019, 2, 6) do
state_hash = {
:state => 'active',
:status => 'Ok',
:started_on => Time.now.utc - 1.minute,
:percent => 80.0
}
state_hash_diff = {
:state => 'finished',
:percent => 100.0,
:updated_on => Time.now.utc
}
expect(job.on_exit(state_hash, nil)).to eq(state_hash.merge(state_hash_diff))
end
end
end

context '.on_error' do
it 'uses percentage from progress hash' do
Timecop.freeze(2019, 2, 6) do
state_hash = {
:state => 'active',
:status => 'Ok',
:started_on => Time.now.utc - 1.minute,
:percent => 80.0
}
state_hash_diff = {
:state => 'finished',
:status => 'Error',
:updated_on => Time.now.utc
}
expect(job.on_error(state_hash, nil)).to eq(state_hash.merge(state_hash_diff))
end
end
end

context '.update_migration_task_progress' do
it 'initializes the progress hash on entry if it does not exist' do
Timecop.freeze(2019, 2, 6) do
job.update_migration_task_progress(:on_entry, nil)
expect(task.reload.options[:progress]).to eq(
:current_state => 'running_in_automate',
:percent => 0.0,
:states => {
:running_in_automate => {
:state => 'active',
:status => 'Ok',
:started_on => Time.now.utc,
:percent => 0.0
}
}
)
end
end

it 'updates the task progress hash on retry without a state progress hash' do
job.context[:retries_running_in_automate] = 1728
Timecop.freeze(2019, 2, 6) do
progress = {
:current_state => 'running_in_automate',
:percent => 10.0,
:states => {
:running_in_automate => {
:state => 'active',
:status => 'Ok',
:started_on => Time.now.utc - 1.minute,
:percent => 10.0,
:updated_on => Time.now.utc - 30.seconds
}
}
}
task.update_options(:progress => progress)
job.update_migration_task_progress(:on_retry, nil)
expect(task.reload.options[:progress]).to eq(
:current_state => 'running_in_automate',
:percent => 10.0,
:states => {
:running_in_automate => {
:state => 'active',
:status => 'Ok',
:started_on => Time.now.utc - 1.minute,
:percent => 20.0,
:updated_on => Time.now.utc
}
}
)
end
end

it 'updates the task progress hash on retry with a state progress hash' do
job.context[:retries_running_in_automate] = 1728
Timecop.freeze(2019, 2, 6) do
progress = {
:current_state => 'running_in_automate',
:percent => 10.0,
:states => {
:running_in_automate => {
:state => 'active',
:status => 'Ok',
:started_on => Time.now.utc - 1.minute,
:percent => 10.0,
:updated_on => Time.now.utc - 30.seconds
}
}
}
task.update_options(:progress => progress)
job.update_migration_task_progress(:on_retry, :percent => 30)
expect(task.reload.options[:progress]).to eq(
:current_state => 'running_in_automate',
:percent => 10.0,
:states => {
:running_in_automate => {
:state => 'active',
:status => 'Ok',
:started_on => Time.now.utc - 1.minute,
:percent => 30.0,
:updated_on => Time.now.utc
}
}
)
end
end

it 'updates the task progress hash on exit' do
job.context[:retries_running_in_automate] = 1728
Timecop.freeze(2019, 2, 6) do
progress = {
:current_state => 'running_in_automate',
:percent => 10.0,
:states => {
:running_in_automate => {
:state => 'active',
:status => 'Ok',
:started_on => Time.now.utc - 1.minute,
:percent => 10.0,
:updated_on => Time.now.utc - 30.seconds
}
}
}
task.update_options(:progress => progress)
job.update_migration_task_progress(:on_exit, nil)
expect(task.reload.options[:progress]).to eq(
:current_state => 'running_in_automate',
:percent => 10.0,
:states => {
:running_in_automate => {
:state => 'finished',
:status => 'Ok',
:started_on => Time.now.utc - 1.minute,
:percent => 100.0,
:updated_on => Time.now.utc
}
}
)
end
end

it 'updates the task progress hash on error' do
job.context[:retries_running_in_automate] = 1728
Timecop.freeze(2019, 2, 6) do
progress = {
:current_state => 'running_in_automate',
:percent => 10.0,
:states => {
:running_in_automate => {
:state => 'active',
:status => 'Ok',
:started_on => Time.now.utc - 1.minute,
:percent => 10.0,
:updated_on => Time.now.utc - 30.seconds
}
}
}
task.update_options(:progress => progress)
job.update_migration_task_progress(:on_error, nil)
expect(task.reload.options[:progress]).to eq(
:current_state => 'running_in_automate',
:percent => 10.0,
:states => {
:running_in_automate => {
:state => 'finished',
:status => 'Error',
:started_on => Time.now.utc - 1.minute,
:percent => 10.0,
:updated_on => Time.now.utc
}
}
)
end
end
end
end

context 'state transitions' do
%w[start poll_automate_state_machine finish abort_job cancel error].each do |signal|
shared_examples_for "allows #{signal} signal" do
Expand Down Expand Up @@ -73,7 +321,7 @@
end
end

context 'operations' do
context 'transition methods' do
let(:poll_interval) { Settings.transformation.job.retry_interval }

context '#start' do
Expand Down