cfe_internal/enterprise/federation/federation.cf

Table of Contents

agent bundles

cfengine_enterprise_federation:config

Prototype: cfengine_enterprise_federation:config

Description: Read/parse config JSON, define variables and classes for use later

Implementation:

bundle agent config
{
  vars:
    enterprise_edition.(policy_server|am_policy_hub)::
      "federation_dir" string => "/opt/cfengine/federation";
      "bin_dir" string => "$(federation_dir)/bin";
      "path" string => "$(federation_dir)/cfapache/federation-config.json";

      # TODO: Instrument augments
      "dump_interval" -> {"ENT-4806"}
        int => "20",
        comment => "Dump data on the feeders every 20 minutes";

      # TODO: don't hard-code cftransport user
      "transport_user" -> { "ENT-4610" } string => "cftransport";
      "transport_home"
        string => "$(cfengine_enterprise_federation:config.federation_dir)/cftransport";

    config_present::
      "data" data => readjson( $(path) );

  classes:
    enterprise_edition.(policy_server|am_policy_hub)::
      "config_present"
        expression => fileexists( $(path) );
    config_present::
      "enabled" expression => or(strcmp("on", "$(data[target_state])"),
                                 strcmp("paused", "$(data[target_state])")),
        scope => "namespace";
      "am_off" expression => strcmp("off", "$(data[target_state])"),
        scope => "namespace";
      "am_on" expression => strcmp("on", "$(data[target_state])"),
        scope => "namespace";

      # _stdlib_path_exists_getenforce and paths.getenforce are defined by masterfiles/lib/paths.cf
      default:_stdlib_path_exists_getenforce::
        "selinux_enabled"
          expression => strcmp("Enforcing", execresult("$(paths.getenforce)", useshell)),
          scope => "namespace";


  vars:
    enabled::
      "role" string => "$(data[role])";
      "remotes" slist => getindices( @(data[remote_hubs]) );
      "login" string => ""; # default
      "login"
        string => "$(data[remote_hubs][$(remotes)][transport][ssh_user])@$(data[remote_hubs][$(remotes)][transport][ssh_host])",
            if => and(
                       # To ensure we are using a remote hub that's actually enabled
                       strcmp( "on", "$(data[remote_hubs][$(remotes)][target_state])" ),
                       # To ensure the remote we are pushing to actually needs the data (is a superhub)
                       strcmp( "superhub", "$(data[remote_hubs][$(remotes)][role])" ));

    am_superhub::
      # Public keys of enabled pushing feeders need to be trusted (on a superhub)
      "pubkey[$(remotes)]" string => "$(data[remote_hubs][$(remotes)][transport][ssh_pubkey])",
            if => and( strcmp( "on", "$(data[remote_hubs][$(remotes)][target_state])" ),
                       strcmp( "feeder", "$(data[remote_hubs][$(remotes)][role])" ),
                       strcmp( "push_over_rsync", "$(data[remote_hubs][$(remotes)][transport][mode])"));

    am_feeder::
      # Public key(s) of enabled pulling superhub(s) need(s) to be trusted (on a feeder)
      "pubkey[$(remotes)]" string => "$(data[remote_hubs][$(remotes)][transport][ssh_pubkey])",
            if => and( strcmp( "on", "$(data[remote_hubs][$(remotes)][target_state])" ),
                       strcmp( "superhub", "$(data[remote_hubs][$(remotes)][role])" ),
                       strcmp( "pull_over_rsync", "$(data[remote_hubs][$(remotes)][transport][mode])"));

    am_superhub|am_feeder::
      "pubkeys" slist => getvalues( pubkey );

      "fingerprint[$(data[remote_hubs][$(remotes)][transport][ssh_host])]"
        slist => string_split("$(data[remote_hubs][$(remotes)][transport][ssh_fingerprint])", "$(const.n)", "inf"),
        # To ensure we are using a remote hub that's enabled
        if => strcmp( "on", "$(data[remote_hubs][$(remotes)][target_state])" );
      "fingerprints" slist => maparray("$(this.k) $(this.v)", fingerprint);
      "feeder[$(remotes)]" string => "$(data[remote_hubs][$(remotes)][hostkey])",
        if => strcmp( "feeder", "$(data[remote_hubs][$(remotes)][role])" );

  classes:
    enabled::

      # Knowing if feeder or superhub is based on explicit setting of role in
      # path (federation-config.json)
      "am_feeder"
        expression => strcmp("feeder", "$(data[role])"),
        scope => "namespace";

      "am_superhub"
        expression => strcmp("superhub", "$(data[role])"),
        scope => "namespace";

      "am_pusher"
        and => {strcmp("superhub", "$(data[remote_hubs][$(remotes)][role])"),
                strcmp("on", "$(data[remote_hubs][$(remotes)][target_state])"),
                strcmp("push_over_rsync", "$(data[remote_hubs][$(remotes)][transport][mode])")},
        comment => "Has an enabled remote superhub with push as transport method, should run push transport",
        scope => "namespace";

      "am_puller"
        and => {"am_superhub",
                strcmp("on", "$(data[remote_hubs][$(remotes)][target_state])"),
                strcmp("pull_over_rsync", "$(data[remote_hubs][$(remotes)][transport][mode])")},
        comment => "Superhub with some enabled remote hub with pull as transport method, should run pull transport",
        scope => "namespace";

      "am_transporter"
        or => {"am_pusher", "am_puller"},
        scope => "namespace";

      "am_paused"
        expression => strcmp("paused", "$(data[target_state])"),
        scope => "namespace";

  # Note: in order to see these debugs you must either define the default DEBUG class
  # or the namespace prefixed class like:
  # cf-agent -KI -DDEBUG
  # or
  # cf-agent -KI -Dcfengine_enterprise_federation:DEBUG_config

  reports:
    enabled.(default:DEBUG|DEBUG_config)::
      "Federation enabled!";
    am_superhub.(default:DEBUG|DEBUG_config)::
      "I'm a superhub!";
    am_feeder.(default:DEBUG|DEBUG_config)::
      "I'm a feeder!";
    am_pusher.(default:DEBUG|DEBUG_config)::
      "I'm pushing dumps!";
    am_puller.(default:DEBUG|DEBUG_config)::
      "I'm pulling dumps!";
    am_transporter.(default:DEBUG|DEBUG_config)::
      "I'm a transporter!";
    am_paused.(default:DEBUG|DEBUG_config)::
      "I'm paused so won't do any import/dump";
}

cfengine_enterprise_federation:semanage_installed

Prototype: cfengine_enterprise_federation:semanage_installed

Description: Install semanage utility if selinux enabled and cfengine_mp_fr_dependencies_auto_install class is defined if not defined then only warn

Implementation:

bundle agent semanage_installed
{
  vars:
    "semanage_action"
      string => ifelse( "default:_stdlib_path_exists_semanage", "fix", "default:cfengine_mp_fr_dependencies_auto_install", "fix", "warn" ),
      comment => "We only want to use semanage if it's available, or if we have
                  indicated it's ok to install it automatically. This variable
                  is subsequently used by a commands and packages promises to
                  warn or fix based.";
    debian_6|debian_7|debian_8|ubuntu_12|ubuntu_14|ubuntu_16|rhel_5::
      "semanage_package" string => "policycoreutils";
    debian_9|debian_10|ubuntu_18|redhat_8|centos_8::
      "semanage_package" string => "policycoreutils-python-utils";
    redhat_6|centos_6|redhat_7|centos_7::
      "semanage_package" string => "policycoreutils-python";

  packages:
    debian|ubuntu::
      "$(semanage_package)"
        policy => "present",
        package_module => default:apt_get,
        action => default:policy ( $(semanage_action) );
    redhat::
      "$(semanage_package)"
        policy => "present",
        package_module => default:yum,
        action => default:policy ( $(semanage_action) );

  reports:
    default:DEBUG|DEBUG_semanage_installed::
      "paths.semanage = $(paths.semanage)";
    !default:_stdlib_path_exists_semanage.!default:cfengine_mp_fr_dependencies_auto_install::
      "semanage command is not available at $(paths.semanage). Will only install needed package if cfengine_mp_fr_dependencies_auto_install class is defined in augments(def.json) or with --define cf-agent option.";
}

cfengine_enterprise_federation:transport_user

Prototype: cfengine_enterprise_federation:transport_user

Description: Manage transport user and permissions for remote SSH access

Implementation:

bundle agent transport_user
{
  vars:
      "user"
        string => "$(cfengine_enterprise_federation:config.transport_user)";
      "home"
        string => "$(cfengine_enterprise_federation:config.transport_home)";
      "ssh_key_name" string => "id_FR";
      "ssh_priv_key" string => "$(home)/.ssh/$(ssh_key_name)";
      "ssh_pub_key" string => "$(ssh_priv_key).pub";
      "ssh_auth_keys" string => "$(home)/.ssh/authorized_keys";
      "ssh_known_hosts" string => "$(home)/.ssh/known_hosts";
      "ssh_config" string => "$(home)/.ssh/config";
      "create_files"
        slist => {
          "$(home)/.",
          "$(home)/.ssh/.",
          "$(home)/source/.",      # Dumps from feeders are taken from here
          "$(home)/destination/.", # And dropped here on superhub
          "$(ssh_auth_keys)",
          "$(ssh_known_hosts)",
          "$(ssh_config)"
        };

  classes:
    enabled.selinux_enabled::
        "incorrect_ssh_context"
          expression => not( or(
                               regcmp(".*[\s:]ssh_home_t[\s:].*",
                                      execresult("ls -Z $(home) | grep .ssh",
                                                 useshell)),
                               regcmp(".*[\s:]ssh_home_t[\s:].*",
                                      execresult("ls -Z $(ssh_auth_keys)",
                                                 useshell)),
                               regcmp(".*[\s:]ssh_home_t[\s:].*",
                                      execresult("ls -Z $(ssh_priv_key)",
                                                 useshell)),
                               regcmp(".*[\s:]ssh_home_t[\s:].*",
                                      execresult("ls -Z $(ssh_pub_key)",
                                                 useshell)),
                               regcmp(".*[\s:]ssh_home_t[\s:].*",
                                      execresult("ls -Z $(ssh_config)",
                                                 useshell))));
  users:
    "$(user)"
      policy => "present",
      home_dir => "$(home)";
  files:
    "$(create_files)"
      create => "true";
    "$(home)/."
      depth_search => default:recurse_with_base("inf"),
      file_select => default:all,
      perms => default:mog( "600", $(user), "root" );
    "$(ssh_auth_keys)"
      create => "true",
      handle => "ssh_auth_keys_configured",
      edit_template_string => "}$(const.n)",
      template_data => @(cfengine_enterprise_federation:config.pubkeys),
      template_method => "inline_mustache";
    "$(ssh_known_hosts)"
      create => "true",
      handle => "ssh_known_hosts_configured",
      edit_template_string => "}$(const.n)",
      template_data => @(cfengine_enterprise_federation:config.fingerprints),
      template_method => "inline_mustache",
      if => isvariable("cfengine_enterprise_federation:config.fingerprints");
    "$(ssh_config)"
      create => "true",
      handle => "ssh_config_configured",
      edit_line => default:insert_lines("IdentityFile $(ssh_priv_key)");

  methods:
    selinux_enabled::
      "semanage_installed" usebundle => semanage_installed;

  commands:
    # _stdlib_path_exists_<command> and paths.<command> are defined is masterfiles/lib/paths.cf
    selinux_enabled.incorrect_ssh_context.default:_stdlib_path_exists_semanage.default:_stdlib_path_exists_restorecon::
      "$(paths.semanage) fcontext -a -t ssh_home_t '$(home)/.ssh(/.*)?'";
      "$(paths.restorecon) -R -F $(home)/.ssh/";

    any::
      # Generate ssh keypair
      "/usr/bin/ssh-keygen"
        handle => "ssh_keys_configured",
        args => "-N '' -f $(ssh_priv_key)",
        if => and( isdir( "$(home)/.ssh" ),
                   not( fileexists( "$(ssh_priv_key)" )));

  reports:
    selinux_enabled.incorrect_ssh_context.!default:_stdlib_path_exists_semanage::
      "need to fix incorrect ssh context for transport user but semanage path in $(sys.libdir)/paths.cf ($paths.semanage) does not resolve";
    selinux_enabled.incorrect_ssh_context.!default:_stdlib_path_exists_restorecon)::
      "need to fix incorrect ssh context for transport user but restorecon path in $(sys.libdir)/paths.cf ($paths.restorecon) does not resolve";
}

cfengine_enterprise_federation:clean_when_off

Prototype: cfengine_enterprise_federation:clean_when_off

Description: cleanup changes made for federated reporting on a feeder NOTE: a superhub turned off by removing federation-config.json or setting target_state=off will not revert schema changes. federation_manage_files will always run regardless of off or not so as to be prepared for enablement via Mission Portal UI

Implementation:

bundle agent clean_when_off
{
  vars:
    "user" string => "$(cfengine_enterprise_federation:transport_user.user)";
    "home" string => "$(cfengine_enterprise_federation:transport_user.home)";

  users:
    "$(user)"
      policy => "absent";

  methods:
    "rm_rf_cftransport_home_dir" usebundle => default:rm_rf("$(home)");

  commands:
    # _stdlib_path_exists_<command> and paths.<command> are defined is masterfiles/lib/paths.cf
    selinux_enabled.default:_stdlib_path_exists_semanage::
      "$(paths.semanage) fcontext -d '$(home)/.ssh(/.*)?'";

}

cfengine_enterprise_federation:inventory_refresh_cmd

Prototype: cfengine_enterprise_federation:inventory_refresh_cmd

Implementation:

bundle agent inventory_refresh_cmd
{
  vars:
    "cmd" data => parsejson('{"inventory_refresh_cmd":
                                "$(sys.workdir)/httpd/php/bin/php $(cfe_internal_hub_vars.docroot)/index.php cli_tasks inventory_refresh"}');
}

cfengine_enterprise_federation:federation_manage_files

Prototype: cfengine_enterprise_federation:federation_manage_files

Description: Manage files, directories and permissions in $(cfengine_enterprise_federation:config.federation_dir)

Implementation:

bundle agent federation_manage_files
{

  vars:
    "transport_user"
      string => "$(cfengine_enterprise_federation:config.transport_user)";
    "login" data => parsejson('{"login":"$(cfengine_enterprise_federation:config.login)"}');
    "feeder_username" data => parsejson('{"feeder_username":"$(cfengine_enterprise_federation:config.transport_user)"}');
    "feeder" data => parsejson('{"feeder": "$(sys.key_digest)"}');
    "cf_version" data => parsejson('{"cf_version":"$(sys.cf_version)"}');

  methods:
      "internal_vars" usebundle => "default:cfe_internal_hub_vars",
        comment => "We need the path to the inventory refresh PHP script before we use it.";
      "inventory_refresh_cmd" usebundle => "cfengine_enterprise_federation:inventory_refresh_cmd",
        comment => "Needs to be in a separate bundle so that it gets evaluated *after* cfe_internal_hub_vars.";

  files:
    enterprise_edition.(policy_server|am_policy_hub)::
      # Both cfpache and $(transport_user) need permission so adding o+x here
      "$(cfengine_enterprise_federation:config.federation_dir)/."
        create => "true",
        perms => default:mog( "661", "root", "root" );
      "$(cfengine_enterprise_federation:config.federation_dir)/cfapache/."
        create => "true",
        perms => default:mog( "600", "cfapache", "root" );
      "$(cfengine_enterprise_federation:config.federation_dir)/cfapache/."
        depth_search => default:recurse_with_base("inf"),
        file_select => default:all,
        perms => default:mog( "600", "cfapache", "root" );
    enabled::
      "$(cfengine_enterprise_federation:config.bin_dir)/."
        create => "true",
        perms => default:mog( "660", "root", "$(transport_user)" );
    am_superhub::
      "$(cfengine_enterprise_federation:config.federation_dir)/superhub/."
        create => "true",
        perms => default:mog( "770", "root", "$(transport_user)" );
      "$(cfengine_enterprise_federation:config.federation_dir)/superhub/import/."
        create => "true",
        perms => default:mog( "600", "root", "root" );
      "$(cfengine_enterprise_federation:config.federation_dir)/superhub/import/filters/."
        create => "true",
        perms => default:mog( "600", "root", "root" );
    am_feeder::
      "$(cfengine_enterprise_federation:config.federation_dir)/fedhub/."
        create => "true",
        perms => default:mog( "660", "root", "$(transport_user)" );
      "$(cfengine_enterprise_federation:config.federation_dir)/fedhub/dump/."
        create => "true",
        perms => default:mog( "660", "root", "$(transport_user)" );
      "$(cfengine_enterprise_federation:config.federation_dir)/fedhub/transport/."
        create => "true",
        perms => default:mog( "660", "root", "$(transport_user)" );
      "$(cfengine_enterprise_federation:config.federation_dir)/fedhub/dump/filters/."
        create => "true",
        perms => default:mog( "600", "root", "root" );

    am_feeder|am_transporter|am_superhub::
      # TODO: Instrument augments
      "$(cfengine_enterprise_federation:config.bin_dir)/config.sh"
        create => "true",
        template_method => "mustache",
        edit_template => "$(this.promise_dirname)/../../../templates/federated_reporting/config.sh.mustache",
        template_data => mergedata(@(login), @(feeder_username), @(feeder), @(cf_version),
                                   @(cfengine_enterprise_federation:inventory_refresh_cmd.cmd)),
        perms => default:mog( "640", "root", "$(transport_user)" );

      # TODO: Instrument augments
      "$(cfengine_enterprise_federation:config.bin_dir)/log.sh"
        create => "true",
        template_method => "mustache",
        edit_template => "$(this.promise_dirname)/../../../templates/federated_reporting/log.sh.mustache",
        perms => default:mog( "640", "root", "$(transport_user)" );

      "$(cfengine_enterprise_federation:config.bin_dir)/parallel.sh"
        copy_from => default:local_dcp( "$(this.promise_dirname)/../../../templates/federated_reporting/parallel.sh" ),
        perms => default:mog( "640", "root", "$(transport_user)" );

      "$(cfengine_enterprise_federation:config.bin_dir)/psql_wrapper.sh" -> { "ENT-4792"}
        create => "true",
        edit_template => "$(this.promise_dirname)/../../../templates/federated_reporting/psql_wrapper.sh.mustache",
        template_method => "mustache",
        perms => default:mog( "700", "root", "root" );

    am_feeder::
      "$(cfengine_enterprise_federation:config.bin_dir)/dump.sh"
        copy_from => default:local_dcp( "$(this.promise_dirname)/../../../templates/federated_reporting/dump.sh" ),
        perms => default:mog( "700", "root", "root" );

      "$(cfengine_enterprise_federation:config.federation_dir)/fedhub/dump/filters/50-merge_inserts.awk"
        copy_from => default:local_dcp( "$(this.promise_dirname)/../../../templates/federated_reporting/50-merge_inserts.awk" ),
        perms => default:mog( "600", "root", "root" );

    am_transporter::
      "$(cfengine_enterprise_federation:config.bin_dir)/transport.sh"
        copy_from => default:local_dcp( "$(this.promise_dirname)/../../../templates/federated_reporting/transport.sh" ),
        perms => default:mog( "500", "$(transport_user)", "root" );

    am_puller::
      "$(cfengine_enterprise_federation:config.bin_dir)/pull_dumps_from.sh"
        copy_from => default:local_dcp( "$(this.promise_dirname)/../../../templates/federated_reporting/pull_dumps_from.sh" ),
        perms => default:mog( "500", "$(transport_user)", "root" );

    am_superhub::
      "$(cfengine_enterprise_federation:config.bin_dir)/import.sh"
        copy_from => default:local_dcp( "$(this.promise_dirname)/../../../templates/federated_reporting/import.sh" ),
        perms => default:mog( "700", "root", "root" );

      "$(cfengine_enterprise_federation:config.bin_dir)/import_file.sh"
        copy_from => default:local_dcp( "$(this.promise_dirname)/../../../templates/federated_reporting/import_file.sh" ),
        perms => default:mog( "700", "root", "root" );

      "$(cfengine_enterprise_federation:config.federation_dir)/superhub/import/filters/10-base_filter.sed"
        copy_from => default:local_dcp( "$(this.promise_dirname)/../../../templates/federated_reporting/10-base_filter.sed" ),
        perms => default:mog( "600", "root", "root" );
}

cfengine_enterprise_federation:postgres_config

Prototype: cfengine_enterprise_federation:postgres_config

Description: Customize postgres config for superhub

Implementation:

bundle agent postgres_config
{
  vars:
    am_superhub::
      "c[shared_buffers]"
        string => "1GB",
        comment => "Changing this setting requires restarting the database.";
      "c[max_locks_per_transaction]"
        string => "4000",
        comment => "Changing this setting requires restarting the database.";
      "c[log_lock_waits]"
        string => "on",
        comment => "Changing this setting requires restarting the database.";
  files:
    am_superhub::
      "$(sys.statedir)/pg/data/postgresql.conf"
        edit_line => default:set_line_based( "$(this.namespace):$(this.bundle).c",
                                     "=",
                                     "\s*=\s*",
                                     ".*",
                                     ""),
        classes => default:results( "bundle", "postgresql_conf" );

  commands:
    am_superhub.postgresql_conf_repaired::
      "$(sys.bindir)/pg_ctl"
        arglist => {
                     "restart",
                     "-w", # Wait for startup or shutdown to complete, -W (do not wait) is the default for start and restart modes
                     "-D",
                     "$(sys.statedir)/pg/data",
                     "-l",
                     "/var/log/postgresql.log",
                     "-m",
                     "smart" # wait for online backup mode to finish and all clients to disconnect
        },
        contain => cfpostgres_user;
}

cfengine_enterprise_federation:exported_data

Prototype: cfengine_enterprise_federation:exported_data

Description: Run script to dump pg data on feeder hub

Implementation:

bundle agent exported_data
{
  methods:
    am_feeder.!am_paused::
      "Refresh Inventory"
        usebundle => "default:cfe_internal_refresh_inventory_view",
        handle => "fr_inventory_refresh",
        comment => "Use standard inventory refresh so that we don't run it twice";

  commands:
    am_feeder.!am_paused::
      "/bin/bash"
        arglist => {"$(cfengine_enterprise_federation:config.bin_dir)/dump.sh"},
        contain => default:in_shell,
        depends_on => { "fr_inventory_refresh" },
        comment => "Refresh Inventory must be completed before dumping data";
}

cfengine_enterprise_federation:data_transport

Prototype: cfengine_enterprise_federation:data_transport

Description: Run script to transport data from feeder to superhub

Implementation:

bundle agent data_transport
{
  vars:
    am_puller.!am_paused::
      # local copies of the variables to make using them below sane
      "remotes" slist => {@(cfengine_enterprise_federation:config.remotes)};
      "data"    data  => @(cfengine_enterprise_federation:config.data);

      "enabled_pull_hosts[$(remotes)]"
        string => "$(data[remote_hubs][$(remotes)][transport][ssh_host])",
        if => and(strcmp("on", "$(data[remote_hubs][$(remotes)][target_state])"),
                  strcmp("pull_over_rsync", "$(data[remote_hubs][$(remotes)][transport][mode])"));

      "pull_args" -> {"ENT-4499"}
        string => join(" ", getvalues(@(enabled_pull_hosts)));

  commands:
    am_pusher.!am_paused::
      "/bin/bash"
        arglist => {"$(cfengine_enterprise_federation:config.bin_dir)/transport.sh push"},
        contain => contain_transport_user;

    am_puller.!am_paused::
      "/bin/bash"
        arglist => {"$(cfengine_enterprise_federation:config.bin_dir)/transport.sh pull $(pull_args)"},
        contain => contain_transport_user;
}

cfengine_enterprise_federation:imported_data

Prototype: cfengine_enterprise_federation:imported_data

Description: Run script to import dumps on superhub

Implementation:

bundle agent imported_data
{
  commands:
    am_superhub.!am_paused::
      "/bin/bash"
        arglist => {"$(cfengine_enterprise_federation:config.bin_dir)/import.sh"},
        contain => default:in_shell;
}

cfengine_enterprise_federation:superhub_schema

Prototype: cfengine_enterprise_federation:superhub_schema

Description: Run SQL script to ensure schema is migrated to superhub partitioned tables architecture

Implementation:

bundle agent superhub_schema
{
  commands:
    am_superhub::
      "$(cfengine_enterprise_federation:config.bin_dir)/psql_wrapper.sh"
        arglist => {
                     "cfdb",
                     `"select superhub_schema('$(sys.key_digest)');"`,
                   },
        classes => psql_wrapper_exit_codes;
}

cfengine_enterprise_federation:ensure_feeders

Prototype: cfengine_enterprise_federation:ensure_feeders

Description: Run SQL function to ensure that all configured feeder hubs are in __hubs table

Implementation:

bundle agent ensure_feeders
{
  vars:
    am_superhub::
      "feeders" slist => getvalues( "cfengine_enterprise_federation:config.feeder");
      "feeders_arg" string => concat( "ARRAY['", join( "', '", feeders ), "']");

  commands:
    am_superhub::
      "$(cfengine_enterprise_federation:config.bin_dir)/psql_wrapper.sh"
        arglist => {
                     "cfdb",
                     `"select ensure_feeders($(feeders_arg));"`
                   },
        classes => psql_wrapper_exit_codes,
        if => isgreaterthan(length(feeders), 0);
}

cfengine_enterprise_federation:entry

Prototype: cfengine_enterprise_federation:entry

Description: Conditionally runs all federated reporting bundles

Implementation:

bundle agent entry
{
  meta:
    (policy_server|am_policy_hub).enterprise_edition::
      "tags" -> { "ENT-4383" }
        slist => { "enterprise_maintenance" };
  classes:
    enterprise_edition.(policy_server|am_policy_hub)::
      "config_exists"
        expression => fileexists("$(cfengine_enterprise_federation:config.federation_dir)/cfapache/federation-config.json");
    enterprise_edition.(policy_server|am_policy_hub)::
      "config_not_exists"
        expression => not(fileexists("$(cfengine_enterprise_federation:config.federation_dir)/cfapache/federation-config.json"));

  methods:
    config_exists::
      "CFEngine Enterprise Federation Configuration"
        handle => "config",
        usebundle => config;
    am_off|config_not_exists::
      "CFEngine Enterprise Federation Transport Off"
        handle => "clean_when_off",
        usebundle => clean_when_off;
    enabled.am_on::
      "CFEngine Enterprise Federation Transport User"
        handle => "transport_user",
        usebundle => transport_user;
    enterprise_edition.(policy_server|am_policy_hub)::
      "federation_manage_files"
        handle => "federation_manage_files",
        usebundle => federation_manage_files;
    enabled.am_on::
      "CFEngine Enterprise Federation Postgres Configuration"
        handle => "postgres_config",
        usebundle => postgres_config;
      "CFEngine Enterprise Federation Schema Migration"
        handle => "superhub_schema",
        depends_on => { "postgres_config" },
        usebundle => superhub_schema;
      "CFEngine Enterprise Federation Ensure Feeder Hubs in Database"
        handle => "ensure_feeders",
        depends_on => { "superhub_schema" },
        usebundle => ensure_feeders;
      "CFEngine Enterprise Federation Feeder Data Transport"
        handle => "data_transport",
        depends_on => { "transport_user" },
        usebundle => data_transport;
      "CFEngine Enterprise Federation Feeder Data Import"
        handle => "imported_data",
        depends_on => { "transport_user", "ensure_feeders" },
        usebundle => imported_data;
      "CFEngine Enterprise Federation Feeder Data Export"
        usebundle => exported_data,
        action => default:if_elapsed($(cfengine_enterprise_federation:config.dump_interval));
      "Configuration Status"
        usebundle => setup_status;
  reports:
    !enterprise_edition::
      "Federated reporting is only available in CFEngine Enterprise.";
    enterprise_edition.!(policy_server|am_policy_hub)::
      "Federated reporting is only available on the policy server / hub.";
}

cfengine_enterprise_federation:setup_status

Prototype: cfengine_enterprise_federation:setup_status

Implementation:

bundle agent setup_status
{
  vars:
    "role" string => "$(cfengine_enterprise_federation:config.role)";
    "ssh_pub_key"
      string => readfile( "$(cfengine_enterprise_federation:transport_user.ssh_pub_key)" ),
      if => fileexists( "$(cfengine_enterprise_federation:transport_user.ssh_pub_key)" );
    "ssh_server_fingerprint"
      # ssh-keyscan is used because it's more reliable/easy than trying to
      # parse sshd config to find the file and then readfile():
      string => execresult("ssh-keyscan localhost 2>/dev/null | sed 's/localhost //g'", useshell);
  classes:
    "superhub_setup_status_complete"
      expression => "any",
      depends_on => {
                      "config",
                      "transport_user",
                      "postgres_config", # We are depending on a deep guard within this bundle
                      "federation_manage_files",
      };

  files:
    superhub_setup_status_complete::
      "$(cfengine_enterprise_federation:config.federation_dir)/cfapache/setup-status.json"
        create => "true",
        perms => default:mog( "600", "cfapache", "root" ),
        template_method => "inline_mustache",
        edit_template_string => "$(const.n)",
        template_data => '{
          "configured": true,
          "role": "$(role)",
          "hostkey": "$(sys.key_digest)",
          "transport_ssh_public_key": "$(ssh_pub_key)",
          "transport_ssh_server_fingerprint": "$(ssh_server_fingerprint)",
        }',
        if => isvariable( ssh_pub_key );
}

main

Prototype: __main__

Description: You can run this policy file from shell without specifying bundle

Implementation:

bundle agent __main__
{
  methods:
    "entry" usebundle => cfengine_enterprise_federation:entry;
}

file bodies

control

Prototype: control

Implementation:

body file control
{
        namespace => "cfengine_enterprise_federation";
}

contain bodies

cfengine_enterprise_federation:cfpostgres_user

Prototype: cfengine_enterprise_federation:cfpostgres_user

Implementation:

body contain cfpostgres_user
{
  useshell   => "useshell";
  exec_owner => "cfpostgres";
  exec_group => "cfpostgres";
  chdir      => "/tmp";
  no_output  => "false";
}

cfengine_enterprise_federation:contain_transport_user

Prototype: cfengine_enterprise_federation:contain_transport_user

Implementation:

body contain contain_transport_user
{
    exec_owner => "$(cfengine_enterprise_federation:config.transport_user)";
    exec_group => "$(cfengine_enterprise_federation:config.transport_user)";
    chdir => "$(cfengine_enterprise_federation:config.transport_home)";
    useshell => "true";
}

classes bodies

cfengine_enterprise_federation:psql_wrapper_exit_codes

Prototype: cfengine_enterprise_federation:psql_wrapper_exit_codes

Implementation:

body classes psql_wrapper_exit_codes
{
  kept_returncodes => { "0" };
  repaired_returncodes => { "1" };
  failed_returncodes => { "2" };
}

file bodies

cfengine_enterprise_federation:control

Prototype: cfengine_enterprise_federation:control

Implementation:

body file control
{
        namespace => "default";
}