module GoodData::LCM2

Constants

MODES
MODE_NAMES

Public Class Methods

check_unused_params(actions, params) click to toggle source
# File lib/gooddata/lcm/lcm2.rb, line 452
def check_unused_params(actions, params)
  default_params = [
    :client_gdc_hostname,
    :client_gdc_protocol,
    :fail_early,
    :gdc_logger,
    :gdc_password,
    :gdc_username,
    :strict
  ]

  action_params = actions.map do |action|
    action.const_get(:PARAMS).keys.map(&:downcase)
  end

  action_params.flatten!.uniq!

  param_names = params.keys.map(&:downcase)

  unused_params = param_names - (action_params + default_params)

  if unused_params.any?
    GoodData.logger.warn("Following params are not used by any action: #{JSON.pretty_generate(unused_params)}")

    rows = []
    actions.each do |action|
      action_params = action.const_get(:PARAMS)
      action_params.each do |_k, v|
        rows << [action.short_name, v[:name], v[:description], v[:type].class.short_name]
      end
    end

    table = Terminal::Table.new :headings => ['Action', 'Parameter', 'Description', 'Parameter Type'], :rows => rows
    GoodData.logger.info("\n#{table}")
  end
end
convert_params(params) click to toggle source
# File lib/gooddata/lcm/lcm2.rb, line 191
def convert_params(params)
  # Symbolize all keys
  GoodData::Helpers.symbolize_keys!(params)
  params.keys.each do |k|
    params[k.downcase] = params[k]
  end
  params.reject! do |k, _|
    k.downcase != k
  end
  convert_to_smart_hash(params)
end
convert_to_smart_hash(params) click to toggle source
# File lib/gooddata/lcm/lcm2.rb, line 203
def convert_to_smart_hash(params)
  if params.is_a?(Hash)
    res = SmartHash.new
    params.each_pair do |k, v|
      if v.is_a?(Hash) || v.is_a?(Array)
        res[k] = convert_to_smart_hash(v)
      else
        res[k] = v
      end
    end
    res
  elsif params.is_a?(Array)
    params.map do |item|
      convert_to_smart_hash(item)
    end
  else
    params
  end
end
get_mode_actions(mode) click to toggle source
# File lib/gooddata/lcm/lcm2.rb, line 223
def get_mode_actions(mode)
  mode = mode.to_sym
  actions = MODES[mode]
  if mode == :generic_lifecycle
    []
  else
    actions || fail("Invalid mode specified '#{mode}', supported modes are: '#{MODE_NAMES.join(', ')}'")
  end
end
perform(mode, params = {}) click to toggle source
# File lib/gooddata/lcm/lcm2.rb, line 297
def perform(mode, params = {})
  params = convert_params(params)

  GoodData.gd_logger.brick = mode

  final_mode = if params.set_master_project && mode == 'release'
                 'release_set_master_project'
               else
                 mode
               end

  # Get actions for mode specified
  actions = get_mode_actions(final_mode)

  if params.actions
    actions = params.actions.map do |action|
      "GoodData::LCM2::#{action}".split('::').inject(Object) do |o, c|
        begin
          o.const_get(c)
        rescue NameError
          fail NameError, "Cannot find action 'GoodData::LCM2::#{action}'"
        end
      end
    end
  end

  # TODO: Check all action params first

  fail_early = if params.key?(:fail_early)
                 params.fail_early.to_b
               else
                 true
               end

  strict_mode = if params.key?(:strict)
                  params.strict.to_b
                else
                  true
                end

  skip_actions = (params.skip_actions || [])
  actions = actions.reject do |action|
    skip_actions.include?(action.name.split('::').last)
  end

  sync_mode = params.fetch(:sync_mode, nil)
  if mode == 'users' && %w[add_to_organization remove_from_organization].include?(sync_mode)
    actions = actions.reject do |action|
      %w[CollectDataProduct CollectSegments].include?(action.name.split('::').last)
    end
  end
  check_unused_params(actions, params)
  print_action_names(mode, actions)

  # Run actions
  errors = []
  results = []
  actions.each do |action|
    GoodData.logger.info("\n")
    # Invoke action
    begin
      out = run_action action, params
    rescue Exception => e # rubocop:disable Style/RescueException
      errors << {
        action: action,
        err: e,
        backtrace: e.backtrace
      }
      break if fail_early
      results << nil
    end

    # in case fail_early = false, we need to execute another action
    next unless out

    # Handle output results and params
    res = out.is_a?(Array) ? out : out[:results]
    out_params = out.is_a?(Hash) ? out[:params] || {} : {}
    new_params = convert_to_smart_hash(out_params)

    # Merge with new params
    params.merge!(new_params)

    # Print action result
    print_action_result(action, res) if action.send(:print_result, params)

    # Store result for final summary
    results << res
  end

  brick_results = {}
  actions.each_with_index do |action, index|
    brick_results[action.short_name] = results[index]
  end

  result = {
    actions: actions.map(&:short_name),
    results: brick_results,
    params: params,
    success: errors.empty?
  }

  if errors.any?
    error_message = JSON.pretty_generate(errors)
    if strict_mode
      raise GoodData::LcmExecutionError.new(errors[0][:err], error_message)
    else
      GoodData.logger.error(error_message)
    end
  end

  process_sync_failed_projects(params) if GoodData::LCM2::Helpers.collect_synced_status(params) && strict_mode

  result
end
print_action_names(mode, actions) click to toggle source
print_action_result(action, messages) click to toggle source
print_actions_result(actions, results) click to toggle source
process_sync_failed_projects(params) click to toggle source
# File lib/gooddata/lcm/lcm2.rb, line 413
def process_sync_failed_projects(params)
  sync_failed_list = params[:sync_failed_list]
  sync_project_list = sync_failed_list[:project_client_mappings]
  sync_failed_project_list = sync_failed_list[:failed_detailed_projects]

  if sync_project_list && sync_failed_project_list && sync_project_list.size.positive? && sync_failed_project_list.size.positive?
    failed_project = sync_failed_project_list[0]
    summary_message = "Existing errors during execution. See log for details"
    error_message = failed_project[:message]
    if sync_project_list.size == sync_failed_project_list.size
      raise GoodData::LcmExecutionError.new(summary_message, error_message)
    else
      raise GoodData::LcmExecutionWarning.new(summary_message, error_message)
    end
  end
end
run_action(action, params) click to toggle source
# File lib/gooddata/lcm/lcm2.rb, line 430
def run_action(action, params)
  begin
    GoodData.gd_logger.start_action action, GoodData.gd_logger
    GoodData.gd_logger.info("Running #{action.name} action ...")
    params.clear_filters
    # Check if all required parameters were passed
    BaseAction.check_params(action.const_get('PARAMS'), params)
    params.setup_filters(action.const_get('PARAMS'))
    out = action.send(:call, params)
  rescue Exception => e # rubocop:disable Style/RescueException
    # Log to splunk
    GoodData.gd_logger.error("action=#{action} status=failed message=#{e} exception=#{e.backtrace}")
    # Log to execution log
    GoodData.logger.error("Execution #{action} failed. Error: #{e}. Detail:#{e.backtrace}")
    raise e
  ensure
    params.clear_filters
    GoodData.gd_logger.end_action GoodData.gd_logger
  end
  out
end