Rudder agent configuration at node first boot

After migrating all my rules from Cfengine to Rudder, i was still missing something. All my freshly kickstarted nodes weren't by default integrated to Rudder.
While it was fairly easy on my Cfengine installation to fetch rules for a new node during the kickstart phase, the fact that Rudder requires the node to be accepted first, was kind of breaking my server provisioning workflow.

Hopefully Rudder API provides everything's needed to programatically accept a new node on your Rudder server.
The following init script is to be used at your node's first boot. It will send its inventory to the server, accept it from the pending nodes list and then fetch and apply the rules.

I consider that you've been able to install rudder-agent and activate this script from you kickstart install prior to the node's first boot.

rudder-installer init script:

#!/bin/bash
## BEGIN INIT INFO
# Provides: rudder-installer
# Default-Start: 3 4 5
# Default-Stop: 0 1 2 3 4 6
# Required-Start:
#
## END INIT INFO
# rudder-installer: Configure rudder agent
# chkconfig: 345 70 99
# description: Configure rudder agent

# Source function library.
. /etc/init.d/functions

# Variables
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin
API_TOKEN="<YOUR API TOKEN>"
RUDDER_SRV="<YOUR RUDDER SERVER URL>"
RUDDER_SRV_HOSTNAME="<YOUR RUDDER SERVER HOSTNAME>"

# This will remove the script if configuration ended up correctly
clean() {
    echo -n "Removing starting script"
    chkconfig --del rudder-installer
    rm -f /etc/init.d/rudder-installer &> /dev/null
    RETVAL=$?
    return $RETVAL
}

# Configuration of agent
start() {
    echo "Configuring rudder agent"
    # Configure server
    echo $RUDDER_SRV_HOSTNAME > /var/rudder/cfengine-community/policy_server.dat

    # Now declare host on server
    echo "    - Sending node inventory to the server - Can take up to 10mn please be patient..."
    /opt/rudder/bin/cf-agent -KI -D force_inventory &>> /root/rudder.log

    # My inotify script will process the new inventory in far less than 10s, but better safe than sorry
    echo "    - Actually sleeps 10s as it's the needed time to process inventory"
    sleep 10

    echo "    - Verify that node is now pending on the server"
    # Ask server using API
    I=0
    RESULT=$(curl -k -X GET -H 'Content-Type: application/json' -H "X-API-Token: ${API_TOKEN}" -H "X-API-Version: 2" ${RUDDER_SRV}/rudder/api/nodes/pending 2>> /root/rudder.log | sed 's/,/\n/g' | grep $HOSTNAME -B 2)
    RET=$?

    # Looping to check if node is in pending list
    while [ $RET != 0 ]; do
        sleep 1
        I=$((${I}+1))
        if [ $I -lt 30 ]; then
            RESULT=$(curl -k -X GET -H 'Content-Type: application/json' -H "X-API-Token: ${API_TOKEN}" -H "X-API-Version: 2" ${RUDDER_SRV}/rudder/api/nodes/pending 2>> /root/rudder.log | sed 's/,/\n/g' | grep $HOSTNAME -B 2)
            RET=$?
        else
            # We exit if the node is still not in pending list
            echo "!! Inventory discovery took too long, something must have fail - Exiting"
            sleep 5
            exit
        fi
    done

    echo "    - Get the node ID from the server"
    NODEID=$(echo $RESULT | sed 's/.*id\":\"//;s/\".*//')
    RET=$?

    echo "    - Auto accept node"
    echo "Accepting node" >> /root/rudder.log
    curl -k -X POST -H "X-API-Version: latest" -H "X-API-Token: ${API_TOKEN}" ${RUDDER_SRV}/rudder/api/nodes/pending/${NODEID} -d "status=accepted" &>> /root/rudder.log

    # At least should have :)
    echo "    - Node has been accepted!"

    # Yes 6mn is a safe waiting time on my server which takes ~4mn30 to process new server rules
    echo "    - Now that the node is accepted wait for 6 mn while Rudder server rebuilds rules"
    i=0
    # Here's the ugly "Make the user wait" timer
    while [ $i -lt 360 ]; do
        i=$(($i+1))
        sleep 1
        if [ $i == 30 ]; then
            echo -ne "$i"
        elif [ $i == 60 ]; then
            echo -ne "$i"
        elif [ $i == 90 ]; then
            echo -ne "$i"
        elif [ $i == 120 ]; then
            echo -ne "$i"
        elif [ $i == 150 ]; then
            echo -ne "$i"
        elif [ $i == 180 ]; then
            echo -ne "$i"
        elif [ $i == 210 ]; then
            echo -ne "$i"
        elif [ $i == 240 ]; then
            echo -ne "$i"
        elif [ $i == 270 ]; then
            echo -ne "$i"
        elif [ $i == 300 ]; then
            echo -ne "$i"
        elif [ $i == 330 ]; then
            echo -ne "$i"
        else
            echo -ne "."
        fi
    done

    # Ok rules should be generated now let's apply them on the node
    # I run two first command twice to ensure everything's good - once should be sufficient
    echo -e "\n    - Updating rules"
    echo "### Ensure UUID is correctly detected by the node ###" >> /root/rudder.log
    /opt/rudder/bin/cf-agent -KI &>> /root/rudder.log
    /opt/rudder/bin/cf-agent -KI &>> /root/rudder.log
    echo "### Update rules ###" >> /root/rudder.log
    /opt/rudder/bin/cf-agent -KI -b update &>> /root/rudder.log
    /opt/rudder/bin/cf-agent -KI -b update &>> /root/rudder.log
    echo "### Now apply them ###" >> /root/rudder.log
    /opt/rudder/bin/cf-agent -KI &>> /root/rudder.log

    RETVAL=$?
    return $RETVAL
}

# Init script parameters stuff
case "$1" in
    start)
        start && success || failure
        [[ $? == 0 ]] && clean && success || failure
        echo
        ;;
    *)
        echo $"Usage: $0 start"
        exit 3
        ;;
esac

So upon completion, the script will remove itself from the init sequence and your freshly installed node should have all required rules applied. If not, consult /root/rudder.log for details.
You have to use my inotify script on the server, as if you don't you will encounter the infamous "5mn delay" before your node appears in the Rudder new nodes pending list which would render this init script completely useless as it would exit without applying any rules.

As usual, don't hesitate to contact me if you have any question.