#!/bin/bash
#Tiny contextualization script, supporting a minimal set of Cloud-init syntax.
#It requires linux core utils or busybox and bash

#Retreive contextualization information
CS=/tmp/$RANDOM$RANDOM.cinit
rm -f $CS

##OpenNebula and OpenStack ConfigDrive
echo -n "Trying OpenNebula and ConfigDrive datasource..."
mkdir -p $CS.rom
blkid -t TYPE="iso9660" -o device | while read name; do
  mount -o ro -t iso9660 $name $CS.rom
  if [ -f $CS.rom/context.sh ]; then
    sed -n "s|^ *USER_DATA *= *[\"']*\(.*\)[\"']$|\1|p" $CS.rom/context.sh > $CS
  fi
  if [ -f $CS.rom/ec2/latest/user-data ]; then
    cp $CS.rom/ec2/latest/user-data $CS
  elif [ -f $CS.rom/openstack/latest/user-data ]; then
    cp $CS.rom/openstack/latest/user-data $CS
  fi
  umount $CS.rom
done
rmdir $CS.rom
[ -f $CS ] && echo "got it!" || echo "failed!"

##Amazon EC2
if [ ! -f $CS ]; then
  echo -n "Trying Amazon EC2 datasource..."
  wget -T 5 http://169.254.169.254/latest/user-data -q -O $CS &>/dev/null
  [ -f $CS ] && echo "got it!" || echo "failed!"
fi

#If user data is not provided, nothing to be done
if [[ ! -s $CS ]]; then
  echo "Cannot find any userdata or userdata is empty. Nothing to do..."
  exit 0
fi

#Decode user data (if not already decoded)
if ! [[ "`head -c 12 $CS`" == "Content-Type" || "`head -c 1 $CS`" == "#" ]]; then
  echo "Data source seems to be encoded in base64. I will decode it."
  base64 -d $CS > $CS.new
  mv $CS.new $CS
fi

#Parse MIME encoding if present
BOUNDARY=`head -1 $CS | sed -n "s|^Content-Type: *multipart/mixed; *boundary *= *[\"']*\([^\"']*\)[\"']*$|\1|p"`
if [ -n "$BOUNDARY" ]; then
  echo "Data source seems to be encoded in MIME format. I will decode it."
  awk -v B="$BOUNDARY" -v F="$CS." '{if($0=="--"B"--"){exit}if($0=="--"B){k=k+1;f=F k;getline;s=1;printf "" > f};if(s==1)print $0 >> f}' $CS
  rm -f $CS
fi

#Execute contextualization commands
for f in $CS*; do

  #Skip content-type (if present)
  if [ "`head -c 13 $f`" == "Content-Type:" ]; then
    awk '{if($0==""){s=1;getline;}if(s==1)print $0}' $f > $f.new
    mv $f.new $f
  fi

  #Skip files not containing #
  [ "`head -c 1 $f`" == "#" ] || continue

  #Execute contextualization command
  FIRST_LINE="`head -1 $f`"
  if [ "$FIRST_LINE" == "#!/bin/bash" ]; then
    #This is a bash script, let's just execute it
    echo "Executing Bash contextualization script..."
    which bash &>/dev/null
    if [ $? -ne 0 ]; then
      which ash &>/dev/null
      if [ $? -eq 0 ]; then
        ash $f
      else
        echo "error. No bash or ash present"
      fi
    else
      bash $f
    fi
  elif [ "$FIRST_LINE" == "#!/bin/sh" ]; then
    #This is a shell script, let's just execute it
    echo "Executing Shell contextualization script..."
    sh $f
  elif [ "$FIRST_LINE" == "#cloud-config" ]; then
    #This is a YAML Cloud config script, we need to parse it. This is only partially implemented here
    newuser(){
      echo -n "Creating user $1..."
      un="$1"
      ud=
      i=0
      while [ $# -gt 0 ]; do
        case $1 in
          -k) uk="$uk$2\n"; shift 2;;
          -S) uS="$2"; shift 2;;
          -g) ud="$ud $1 $2"; addgroup $2 &>/dev/null; shift 2;;
          -G) ud="$ud $1 $2"; for g in ${2//,/ }; do groupadd -f $g; done; shift 2;;
          *) ud="$ud $1"; shift 1;;
        esac
      done
      id $un &>/dev/null
      if [ $? -eq 0 ]; then
        echo -n "already exists..."
      else
        adduser $ud &>/dev/null
      fi
      ung="`id -g $un &>/dev/null`"
      if [ $? -ne 0 ]; then
        echo "error!"
        return
      fi
      unh=`eval echo ~${un}`
      if [[ -n "$uk" ]]; then
        mkdir -p $unh/.ssh/
        echo -e "$uk" >> $unh/.ssh/authorized_keys
        chown $un:$ung -R $unh/.ssh
        chmod og-rwx $unh/.ssh
      fi
      if [[ -n "$uS" ]]; then
        [ "$uS" == "ALL=(ALL)NOPASSWD" ] && uS="ALL=(ALL) NOPASSWD:ALL"
        [ "$uS" == "ALL=(ALL)ALL" ] && uS="ALL=(ALL) ALL"
        grep -q -F "$un $uS" /etc/sudoers ||  echo "$un $uS" >> /etc/sudoers
      fi
      echo "ok!"
    }

    awk 'BEGIN{FS=":|-"}{gsub(/^[ \t]+/,"",$1);gsub(/^[ \t]+/,"",$2);gsub(/^[ \t]+/,"",$3);if($1=="users"){u=1}if(u==1){if(ua!=""){if($1=="primary-group")ua=ua" -g \""$2"\"";if($1=="groups")ua=ua" -G \""$2"\"";if($1=="sudo")ua=ua" -S \""$2"\"";if($1=="passwd")ua=ua" -p \""$2"\"";if($1=="ssh"&&$2=="authorized"){while(1){getline;k=$0;gsub(/^[ \t]+-[ ]+/,"",k);if(k!~/^ssh-rsa/)break;ua=ua" -k \""k"\""}}}if($1==""&&$2=="name"){if(ua!=""){print "newuser "ua;}ua=$3;}}}END{if(ua!=""){print "newuser "ua;}}' $f | while read line; do
      eval $line
    done

  else
    #Other formats are unsupported
    echo "Ignoring script starting with `head -1 $f | head -c 100`. Not supported."
  fi

done

exit 0