bundle agent ha_update
{
 methods:

  enable_cfengine_enterprise_hub_ha::

      "Hubs have am_policy_hub marker"
        usebundle => ha_hubs_have_am_policy_hub_marker,
        comment => "All hubs should have the am_policy_hub marker. This bundle
                    ensures that standby hubs get this marker even though they are not
                    bootstrapped to themselves";

  enable_cfengine_enterprise_hub_ha.am_policy_hub::
      "share_keys" usebundle => ha_share_hub_keys;
      "sync_hub_data" usebundle => ha_hub_sync;

  enable_cfengine_enterprise_hub_ha.!am_policy_hub::
      "sync_agent_data" usebundle => ha_agent_sync;

  enable_cfengine_enterprise_hub_ha.hub_data_synced::
      "manage_keys" usebundle => manage_hub_synced_data;

  enable_cfengine_enterprise_hub_ha.ha_replication_only_node::
      "syncronize_master_hub_dat" usebundle => sync_master_hub_dat;
}

bundle agent ha_hubs_have_am_policy_hub_marker
# @brief Ensure that all ha hub members have the am_policy_hub state marker
{
  classes:
      # We know we need the am_policy_hub marker if any of our ips are found in the ha definition
      "ha_hub_member" expression => iprange( $(ha_def.ips) );

  files:

    ha_hub_member::

      "$(sys.statedir)/am_policy_hub" -> { "ENT-3328" }
        create => "true",
        comment => "This file is automatically created when bootstrapping to
                    self, but in a clustered environment standby hubs bootstrap to the
                    primary and this marker will not be automatically created.";

}

bundle agent ha_agent_sync
{
 vars:
  # TODO: Remove this once sys.keydir is always available (3.26+ only)
  "keydir" -> { "CFE-2822" }
      string => ifelse(isvariable("sys.keydir"), "$(sys.keydir)", "$(sys.workdir)/ppkeys");
 files:
  "$(sys.workdir)/policy_server.dat"
      copy_from => ha_update_ha_no_backup_scp("$(ha_def.master_hub_location)", @(update_def.policy_servers)),
      handle => "ha_cfengine_node_update_master_ip",
      comment => "Update master hub IP on CFEngine node. This is causing that clients will try
                  to contact active/master hub first.";

  "$(keydir)"
      copy_from => ha_update_ha_no_backup_scp("$(ha_def.hubs_keys_location)", @(update_def.policy_servers)),
      file_select => hub_all_keys,
      depth_search => ha_update_ha_recurse("inf"),
      handle => "ha_copy_hub_keys_to_nodes",
      comment => "Download keys of all hubs working in HA cluster and store in client's ppkeys directory.
                  This is important for establishing trusted connection  with standby hub(s) in
                  case of failover.";
}

bundle agent ha_share_hub_keys
{
 vars:
  # TODO: Remove this once sys.keydir is always available (3.26+ only)
  "keydir" -> { "CFE-2822" }
      string => ifelse(isvariable("sys.keydir"), "$(sys.keydir)", "$(sys.workdir)/ppkeys");
 files:
   "$(ha_def.hubs_keys_location)"
      copy_from => ha_update_no_backup_cp("$(keydir)"),
      file_select => hubs_keys_select,
      handle => "ha_copy_hubs_keys",
      depth_search => ha_update_recurse("1"),
      comment => "Clients need to be able to download keys of all hubs working in
                  HA cluster. This is needed to establish trusted connection
                  with standby hubs in case of failover. In order to limit possibility
                  of copying wrong keys hub keys are copied to separate directory first
                  and only ppkeys_hubs is accessible by clients.";
}

bundle agent ha_hub_sync
{
 vars:
  # TODO: Remove this once sys.keydir is always available (3.26+ only)
  "keydir" -> { "CFE-2822" }
      string => ifelse(isvariable("sys.keydir"), "$(sys.keydir)", "$(sys.workdir)/ppkeys");
  "exclude_files" slist => {"localhost.priv", "localhost.pub", @(ha_def.hub_shas)};
 files:
  "$(ha_def.ppkeys_staging)"
      copy_from => ha_update_ha_no_backup_scp("$(keydir)", @(update_def.standby_servers)),
      file_select => ha_update_ex_list(@(exclude_files)),
      handle => "ha_copy_client_keys_between_replica_set_servers",
      depth_search => ha_update_recurse("1"),
      classes => ha_update_if_repaired("hub_data_synced"),
      comment => "Distribute all client keys between replica set servers. This is
                  important in case of failover. Once clients keys are synchronized
                  between all hubs working in HA cluster, clients will be able
                  to authenticate and establish connection with all hubs working in HA cluster.";
}

bundle agent manage_hub_synced_data
# @brief Manage trust of clients bootstrapped other hubs in cluster
#
# Ensures keys collected from other hubs are present in ppkeys so that the
# agents bootstrapped to other hubs are trusted.
{
 vars:
  # TODO: Remove this once sys.keydir is always available (3.26+ only)
  "keydir" -> { "CFE-2822" }
      string => ifelse(isvariable("sys.keydir"), "$(sys.keydir)", "$(sys.workdir)/ppkeys");
 files:

   # Ensure that localhost.pub and localhost.priv are not in the directory of
   # keys collected from standby hubs

   "$(ha_def.ppkeys_staging)/localhost.*" -> { "ENT-3303" }
      delete => ha_tidy,
      handle => "manage_hub_synced_data_ppkeys_staging_localhost_absent",
      comment => "We don't want localhost related key files from a standby
                  server to end up over-writing the active hubs key. That will
                  cause an identity crisis and trust issues.";

   # Ensure that keys collected from standby hubs are present in this hubs
   # ppkeys directory so that agents bootstrapped to standby hubs will be
   # trusted.

   "$(keydir)"
      copy_from => ha_update_no_backup_cp("$(ha_def.ppkeys_staging)"),
      file_select => ha_update_plain,
      depth_search => ha_update_recurse("1"),
      handle => "ha_copy_staged_client_keys",
      comment => "Copy staged client keys to ppkeys. First client keys are copied
                  to ppkeys_staging directory and then to ppkeys. Only clients which
                  keys are copied to ppkeys will be able to authenticate and connect
                  to hub.";
}

bundle agent sync_master_hub_dat
{
 files:
    "$(ha_def.master_hub_location)"
      copy_from => ha_update_ha_no_backup_scp("$(ha_def.master_hub_location)", @(update_def.standby_servers)),
      comment => "Update master hub IP on CFEngine node",
      handle => "ha_cfengine_hub_update_master_ip";

}

body file_select hub_all_keys
{
  leaf_name => {".*.pub"};
  file_result => "leaf_name";
}

body file_select hubs_keys_select
{
 search_size => irange("426", "426");
 leaf_name => {escape("root-SHA=$(ha_def.config[$(update_def.standby_servers)][sha]).pub")};
 file_result => "leaf_name";
}

body copy_from ha_update_ha_no_backup_scp(from,server)
{
 servers     => { "$(server)" };
 source      => "$(from)";
 compare     => "digest";
 copy_backup => "false";
 encrypt     => "true";
}

body depth_search ha_update_ha_recurse(d)
{
 depth => "$(d)";
 exclude_dirs => { "\.svn", "\.git", "git-core" };
}

body depth_search ha_update_recurse(d)
{
      depth => "$(d)";
      xdev  => "true";
}

body classes ha_update_if_repaired(x)
{
      promise_repaired => { "$(x)" };
}

body file_select ha_update_ex_list(names)
{
      leaf_name  => { @(names)};
      file_result => "!leaf_name";
}

body file_select ha_update_plain
{
      file_types  => { "plain" };
      file_result => "file_types";
}

body copy_from ha_update_no_backup_cp(from)
{
      source      => "$(from)";
      copy_backup => "false";
}

body delete ha_tidy
# @brief Copy of body delete tidy from the standard library
{
        dirlinks => "delete";
        rmdirs   => "true";
}
