<commit_before>require 'chef'
require 'sequel'
require 'highline/import'


# General comments:
#
# How can these actions be made idempotent as best as possible?
#
# A way should be provided to either suppress all output, or else
# have it suppressed by default and then made available if a verbose
# flag is set (what do the other commands do by default? Can their
# output be tuned to this level?)

def run_osc_upgrade

  # Main function
  def run_upgrade
    # This is to help make this process idempotent, but currently if EC has
    # been started at all OSC won't come up properly. ctl will report EC as
    # stopped and OSC up, but any attempt to access OSC will result in a 502
    # This issue will have to be solved for this process to be idempotent
    stop_ec

    start_osc

    log "Preparing knife to download data from the Open Source Chef server"

    # As a precaution, we want the location to vary. TODO: We need to save
    # the value to a read protected file so it can be read from if a resume
    # is needed
    # Do we want to delete the directory on failure or leave it for debugging?
    # Are the permissions good enough? (0700)
    osc_data_dir = Dir.mktmpdir('osc-chef-server-data')
    log "Making #{osc_data_dir} as the location to save the server data"

    write_knife_config(osc_data_dir)

    log "Downloading data from the Open Source Chef server"

    run_knife_download

    key_file = "#{osc_data_dir}/key_dump.json"
    create_osc_key_file(key_file)

    log "Finished downloading data from the Open Source Chef server"

    stop_osc

    log "Configuring the Enterprise Chef server for use"

    reconfigure(false)

    start_ec

    log "Transforming Open Source server data for upload to Enterprise Chef server"

    # To prepare the downloaded OSC data for upload to the EC server
    # it is put into a file structure that knife-ec-backup expects
    # and then knife-ec-backup restore functionality is used to upload it to the
    # new Chef server.

    org_name, org_full_name, org_type = determine_org_name

    # See note above on osc_data_dir
    ec_data_dir = Dir.mktmpdir('ec-chef-server-data')
    org_dir = "#{ec_data_dir}/organizations/#{org_name}"
    make_dir("#{ec_data_dir}/organizations", 0644)
    org_dir = "#{ec_data_dir}/organizations/#{org_name}"
    make_dir(org_dir, 0644)

    create_org_json(org_dir, org_name, org_full_name, org_type)

    # Copy over the key_dump.json file
    FileUtils.cp(key_file, "#{ec_data_dir}/key_dump.json")

    # Copy over users
    FileUtils.cp_r("#{osc_data_dir}/users", "#{ec_data_dir}/users")

    # Copy over clients, cookbooks, data_bags, environments, nodes, roles
    %w{clients cookbooks data_bags environments nodes roles}.each do |name|
      FileUtils.cp_r("#{osc_data_dir}/#{name}", "#{org_dir}/#{name}")
    end

    user_names, admin_users = transform_osc_user_data(osc_data_dir, ec_data_dir)

    create_invitations_json(org_dir)

    create_members_json(user_names, org_dir)

    groups_dir = "#{org_dir}/groups"
    make_dir(groups_dir, 0644)

    # Under organizations/#{org_name}/groups, an admins.json and billing-admins.json is needed.
    # Any admins from OSC need to go into the admins group, as that is how it is determined that
    # a user is an admin in EC
    # All admins will be billing admins due to lack of a better selection criteria

    create_admins_json(admin_users, groups_dir)
    create_billing_admins(admin_users, groups_dir)

    write_knife_ec_backup_config

    log "Uploading transformed Open Source server data to Enterprise Chef server"

    run_knife_ec_restore(ec_data_dir)

    log "Open Source chef server upgraded to an Enterprise Chef server"

    # Need to ensure all of OSC is stopped
    # By default, pkill sends TERM, which will cause runsv,runsvdir, and svlogd to shutdown
    # Doing this will completely hose OSC so that a start command won't restart it, if needed
    # If we're going to make this process as idempotent as possible, we'll need to work around this
    # Maybe it's best to do this at the end ... or do we let the user manually remove OSC
    # with a package uninstall?
    # Once we do this we lose most hope at being idempotent
    # Maybe prompt the user before doing this and off to give them steps to do this
    # manually if they don't want this done automatically?
  #  log "Ensuring all the runit processes associated with the Open Source Server are stopped"
  #  run_command("pkill runsv")
  #  run_command("pkill runsvdir")
  #  run_command("pkill svlogd")
    # epmd lives outside of runit, but a pkill will do just fine here too
  #  run_command("pkill epmd")

    # This could all be run at the end, so that OSC could be brought back up if
    # needed, but then this would also kill EC. This code will need to be re-worked
    # so it only removes OSC and leaves EC in place, or else the user needs to
    # be directed on how to do this.

    # The OSC bits still live on the system - do we delete them here?
    # For example, /opt/chef-server is still in the path, but /opt/opscode is not
    # on dev-vm testing
    # This has the effect of making the default knife, gem, etc the chef-server versions

    # The migration data still lives on the system - it is probably worth while
    # to include an optional step to delete it, if the user specifies this, other
    # wise leave it on the system
  end


  def start_osc
    # Assumption is EC isn't running, since we detected OSC on the system
    log 'Ensuring the Open Source Chef server is started'
    msg = "Unable to start Open Source Chef server, which is needed to complete the upgrade"
    check_status(run_command("chef-server-ctl start"), msg)
    # start command can return faster than the services are ready; resulting in 502 gateway
    sleep(10)
  end

  def check_status(status, msg)
    unless status.success?
      log msg
      exit 1
    end
  end

  def write_knife_config(osc_data_dir)
    # Hard coded path to key (stole idea to use from pedant), but the path is in attributes
    # Need to ensure we have a valid path to the key here

    # Do the rest of these need to be arguments?
    config = <<-EOH
      chef_server_url "#{@options.chef_server_url}"
      node_name 'admin'
      client_key '/etc/chef-server/admin.pem'
      repo_mode 'everything'
      versioned_cookbooks true
      chef_repo_path "#{osc_data_dir}"
    EOH
<commit_msg>Fix for upgrades the bad rabbit wait script.<commit_after>require 'chef'
require 'sequel'
require 'highline/import'


# General comments:
#
# How can these actions be made idempotent as best as possible?
#
# A way should be provided to either suppress all output, or else
# have it suppressed by default and then made available if a verbose
# flag is set (what do the other commands do by default? Can their
# output be tuned to this level?)

def run_osc_upgrade

  # Main function
  def run_upgrade
    # This is to help make this process idempotent, but currently if EC has
    # been started at all OSC won't come up properly. ctl will report EC as
    # stopped and OSC up, but any attempt to access OSC will result in a 502
    # This issue will have to be solved for this process to be idempotent
    stop_ec

    fix_rabbit_wait_script

    start_osc

    log "Preparing knife to download data from the Open Source Chef server"

    # As a precaution, we want the location to vary. TODO: We need to save
    # the value to a read protected file so it can be read from if a resume
    # is needed
    # Do we want to delete the directory on failure or leave it for debugging?
    # Are the permissions good enough? (0700)
    osc_data_dir = Dir.mktmpdir('osc-chef-server-data')
    log "Making #{osc_data_dir} as the location to save the server data"

    write_knife_config(osc_data_dir)

    log "Downloading data from the Open Source Chef server"

    run_knife_download

    key_file = "#{osc_data_dir}/key_dump.json"
    create_osc_key_file(key_file)

    log "Finished downloading data from the Open Source Chef server"

    stop_osc

    log "Configuring the Enterprise Chef server for use"

    reconfigure(false)

    start_ec

    log "Transforming Open Source server data for upload to Enterprise Chef server"

    # To prepare the downloaded OSC data for upload to the EC server
    # it is put into a file structure that knife-ec-backup expects
    # and then knife-ec-backup restore functionality is used to upload it to the
    # new Chef server.

    org_name, org_full_name, org_type = determine_org_name

    # See note above on osc_data_dir
    ec_data_dir = Dir.mktmpdir('ec-chef-server-data')
    org_dir = "#{ec_data_dir}/organizations/#{org_name}"
    make_dir("#{ec_data_dir}/organizations", 0644)
    org_dir = "#{ec_data_dir}/organizations/#{org_name}"
    make_dir(org_dir, 0644)

    create_org_json(org_dir, org_name, org_full_name, org_type)

    # Copy over the key_dump.json file
    FileUtils.cp(key_file, "#{ec_data_dir}/key_dump.json")

    # Copy over users
    FileUtils.cp_r("#{osc_data_dir}/users", "#{ec_data_dir}/users")

    # Copy over clients, cookbooks, data_bags, environments, nodes, roles
    %w{clients cookbooks data_bags environments nodes roles}.each do |name|
      FileUtils.cp_r("#{osc_data_dir}/#{name}", "#{org_dir}/#{name}")
    end

    user_names, admin_users = transform_osc_user_data(osc_data_dir, ec_data_dir)

    create_invitations_json(org_dir)

    create_members_json(user_names, org_dir)

    groups_dir = "#{org_dir}/groups"
    make_dir(groups_dir, 0644)

    # Under organizations/#{org_name}/groups, an admins.json and billing-admins.json is needed.
    # Any admins from OSC need to go into the admins group, as that is how it is determined that
    # a user is an admin in EC
    # All admins will be billing admins due to lack of a better selection criteria

    create_admins_json(admin_users, groups_dir)
    create_billing_admins(admin_users, groups_dir)

    write_knife_ec_backup_config

    log "Uploading transformed Open Source server data to Enterprise Chef server"

    run_knife_ec_restore(ec_data_dir)

    log "Open Source chef server upgraded to an Enterprise Chef server"

    # Need to ensure all of OSC is stopped
    # By default, pkill sends TERM, which will cause runsv,runsvdir, and svlogd to shutdown
    # Doing this will completely hose OSC so that a start command won't restart it, if needed
    # If we're going to make this process as idempotent as possible, we'll need to work around this
    # Maybe it's best to do this at the end ... or do we let the user manually remove OSC
    # with a package uninstall?
    # Once we do this we lose most hope at being idempotent
    # Maybe prompt the user before doing this and off to give them steps to do this
    # manually if they don't want this done automatically?
  #  log "Ensuring all the runit processes associated with the Open Source Server are stopped"
  #  run_command("pkill runsv")
  #  run_command("pkill runsvdir")
  #  run_command("pkill svlogd")
    # epmd lives outside of runit, but a pkill will do just fine here too
  #  run_command("pkill epmd")

    # This could all be run at the end, so that OSC could be brought back up if
    # needed, but then this would also kill EC. This code will need to be re-worked
    # so it only removes OSC and leaves EC in place, or else the user needs to
    # be directed on how to do this.

    # The OSC bits still live on the system - do we delete them here?
    # For example, /opt/chef-server is still in the path, but /opt/opscode is not
    # on dev-vm testing
    # This has the effect of making the default knife, gem, etc the chef-server versions

    # The migration data still lives on the system - it is probably worth while
    # to include an optional step to delete it, if the user specifies this, other
    # wise leave it on the system
  end

  def fix_rabbit_wait_script
    # This is an existing file, but the desire is just to overwrite it
    # so it becomes a no-op. Due to a mistake, it was supposed to look for
    # rabbit on an open source system, but it looks for rabbit on an enterprise
    # system instead. It has a bail option if it doesn't find enterprise chef
    # server installed, so this hasn't affected open source systems, until now,
    # since the upgrade process puts both on the same system.
    # The assumption has to be that this file will be wrong on any system
    # undergoing an upgrade, because even if fixed, it can't be assumed that the
    # system will have the fix applied.
    # (The best fix would be to fix the erronious file in omnibus-chef-server)
    #
    # If the open source server is reconfigured it will restore the bad script
    # and this fix will need to re-applied.
    # If the bad script is on the system, the symptom that shows up is that
    # erchef will log that it is waiting for rabbit to start. This will only
    # happen if enterprise chef has been installed and configured on the system,
    # because the script will look for the enterprise rabbit install.
    # The script is kicked off when the open source server tries to start erchef,
    # as the script is hooked into the runit start process for erchef.
    File.open("/opt/chef-server/bin/wait-for-rabbit", "w"){}
  end

  def start_osc
    # Assumption is EC isn't running, since we detected OSC on the system
    log 'Ensuring the Open Source Chef server is started'
    msg = "Unable to start Open Source Chef server, which is needed to complete the upgrade"
    check_status(run_command("chef-server-ctl start"), msg)
    # start command can return faster than the services are ready; resulting in 502 gateway
    sleep(10)
  end

  def check_status(status, msg)
    unless status.success?
      log msg
      exit 1
    end
  end

  def write_knife_config(osc_data_dir)
    # Hard coded path to key (stole idea to use from pedant), but the path is in attributes
    # Need to ensure we have a valid path to the key here

    # Do the rest of these need to be arguments?
    config = <<-EOH
      chef_server_url "#{@options.chef_server_url}"
      node_name 'admin'
      client_key '/etc/chef-server/admin.pem'
      repo_mode 'everything'
      versioned_cookbooks true
      chef_repo_path "#{osc_data_dir}"
    EOH

{'fraction_non_alphanumeric': 0.060764981469827284, 'fraction_numerical': 0.003496259002866932, 'mean_word_length': 3.337882923870185, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4255459', 'n_tokens_mistral': 4369, 'n_tokens_neox': 4132, 'n_words': 1887}