Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Real template #1

Merged
merged 5 commits into from
Apr 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ RUN tar xf "${GCLOUD_SDK_FILENAME}" && \
/google-cloud-sdk/bin/gcloud components install -q kubectl;

ADD cf-deploy-kubernetes.sh /cf-deploy-kubernetes
ADD template.sh /template.sh

# Set the default path to include all the commands
RUN \
ln -s /google-cloud-sdk/bin/kubectl /usr/local/bin/kubectl && \
chmod +x /cf-deploy-kubernetes
chmod +x /cf-deploy-kubernetes && \
chmod +x /template.sh

CMD ["bash"]

66 changes: 57 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
This is the source code for the `codefresh/cf-deploy-kubernetes` container.
This container is used to demonstrate a Kubernetes deployment using Codefresh.io

For a complete example, check:
https://github.com/codefresh-io/cf-deploy-kubernetes-demo

# Assumptions

The deployment script makes the following assumptions about your application and
Expand All @@ -14,9 +11,10 @@ Kubernetes configuration:
1. The application is deployed using the Kubernetes deployment API (versus the
the replication controller directly). For more information read
http://kubernetes.io/docs/user-guide/deployments/
2. The tested codebase has a yaml file that describes the Kubernetes deployment
2. The tested codebase has a yaml file (i.e. deployment.yml) that describes the Kubernetes deployment
parameters and configuration of your application.
3. At the moment, only the basic username/pass authentication is supported.
3. The script processes deployment.yml as a simple template where all `{{ ENV_VARIABLE }}` are replaced with a value of $ENV_VARIABLE deployment.yml
4. At the moment, only the basic username/pass authentication is supported.

# Configuration

Expand All @@ -28,9 +26,59 @@ before failing the build. Defaults to 120 (secs).
3. KUBERNETES_PASSWORD - The password for the Kubernetes cluster. Mandatory.
4. KUBERNETES_SERVER - The server (HTTPS endpoint) of the Kubernetes cluster's
API. Mandatory.
5. DOCKER_IMAGE_TAG - The docker tag to use for the deployment. Requires the
`deployment.yml` file to specify a `$DOCKER_IMAGE_TAG` variable so it can be
substitutes at deployment time.
6. FORCE_RE_CREATE_RESOURCE - Will force re-creation of the deployment

# Usage in codefresh.io

### deployment.yml

```yaml
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: api-svc
spec:
replicas: 1
template:
metadata:
annotations:
revision: "{{CF_REVISION}}"
labels:
app: api-svc
spec:
containers:
- name: apisvc
image: myrepo/apisvc:{{CF_BRANCH}}
ports:
- containerPort: 80
name: http

```

### codefresh.yml
```yaml
---
version: '1.0'

steps:
build:
type: build
working-directory: ${{initial-clone}}
image-name: $docker-image
tag: ${{CF_BRANCH}}
push:
type: push
candidate: ${{build}}
tag: ${{CF_BRANCH}}

deploy-to-kubernetes:
image: codefresh/cf-deploy-kubernetes
tag: latest
working-directory: ${{initial-clone}}
commands:
- /cf-deploy-kubernetes deployment.yml
environment:
- KUBERNETES_USER=${{KUBERNETES_USER}}
- KUBERNETES_PASSWORD=${{KUBERNETES_PASSWORD}}
- KUBERNETES_SERVER=${{KUBERNETES_SERVER}}
```
23 changes: 12 additions & 11 deletions cf-deploy-kubernetes.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
#!/bin/bash

fatal() {
echo "ERROR: $1"
exit 1
}

readonly DEFAULT_NAMESPACE=default

deployment_file=${1:-deployment.yml}
Expand All @@ -9,11 +14,13 @@ deployment_file=${1:-deployment.yml}
[ -z "$KUBERNETES_USER" ] && echo "Please set KUBERNETES_USER" && exit 1;
[ -z "$KUBERNETES_PASSWORD" ] && echo "Please set KUBERNETES_PASSWORD" && exit 1;
[ -z "$KUBERNETES_SERVER" ] && echo "Please set KUBERNETES_SERVER" && exit 1;
[ -z "$DOCKER_IMAGE_TAG" ] && echo "Please set DOCKER_IMAGE_TAG" && exit 1;
# [ -z "$DOCKER_IMAGE_TAG" ] && echo "Please set DOCKER_IMAGE_TAG" && exit 1;

[ ! -f "${deployment_file}" ] && echo "Couldn't find $deployment_file file at $(pwd)" && exit 1;
sed -i "s/\$DOCKER_IMAGE_TAG/$DOCKER_IMAGE_TAG/g" $deployment_file
sed -i "s/\$UNIQ_ID/$(date '+%y-%m-%d_%H:%M:%S')/g" $deployment_file


DEPLOYMENT_FILE=${deployment_file}-$(date '+%y-%m-%d_%H-%M-%S').yml
$(dirname $0)/template.sh "$deployment_file" > "$DEPLOYMENT_FILE" || fatal "Failed to apply deployment template on $deployment_file"


echo "---> Setting up Kubernetes credentials..."
Expand All @@ -22,15 +29,9 @@ kubectl config set-cluster foo.kubernetes.com --insecure-skip-tls-verify=true --
kubectl config set-context foo.kubernetes.com/deployer --user=deployer --namespace=$DEFAULT_NAMESPACE --cluster=foo.kubernetes.com
kubectl config use-context foo.kubernetes.com/deployer

echo "---> Submittinig a deployment to Kubernetes..."
kubectl apply -f "$DEPLOYMENT_FILE" || fatal "Deployment Failed"

# Check if the cloned dir already exists from previous builds
if [ "$FORCE_RE_CREATE_RESOURCE" == "true" ]; then
echo "---> Submittinig a deployment to Kubernetes with --force flag..."
kubectl apply -f $deployment_file --force
else
echo "---> Submittinig a deployment to Kubernetes..."
kubectl apply -f $deployment_file
fi

echo "---> Waiting for a succesful deployment status..."

Expand Down
184 changes: 184 additions & 0 deletions template.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
#!/bin/bash
#
# Very simple templating system that replaces {{VAR}} by the value of $VAR.
# Supports default values by writting {{VAR=value}} in the template.
#
# Copyright (c) 2017 Sébastien Lavoie
# Copyright (c) 2017 Johan Haleby
#
# By: https://github.com/johanhaleby/bash-templater
# Version: https://github.com/johanhaleby/bash-templater/commit/5ac655d554238ac70b08ee4361d699ea9954c941

# Replaces all {{VAR}} by the $VAR value in a template file and outputs it

readonly PROGNAME=$(basename $0)

config_file="<none>"
print_only="false"
silent="false"

usage="${PROGNAME} [-h] [-d] [-f] [-s] --
where:
-h, --help
Show this help text
-p, --print-envs
Don't do anything, just print the result of the variable expansion(s)
-e, --env-file
Specify a file to read variables from
-s, --silent
Don't print warning messages (for example if no variables are found)
examples:
VAR1=Something VAR2=1.2.3 ${PROGNAME} test.txt
${PROGNAME} test.txt -e my-variables.txt
${PROGNAME} test.txt -e my-variables.txt > new-test.txt"

if [ $# -eq 0 ]; then
echo "$usage"
exit 1
fi

while [[ $1 =~ ^(-(h|p|e|s)|--(help|print-envs|env-file|silent)) ]]
do
key=$1
value=$2

case $key in
-h|--help)
echo "$usage"
exit 0
;;
-p|--print)
print_only="true"
;;
-e|--env-file)
config_file="$2"
shift
;;
-s|--silent)
silent="true"
;;
*)
echo "Usage ERROR: Invalid Option $key"
echo "$usage"
exit 1
;;
esac
shift # past argument or value
done


if [[ ! -f "${1}" ]]; then
echo "You need to specify a template file" >&2
echo "$usage"
exit 1
fi

template="${1}"

if [ "$#" -ne 0 ]; then
while [ "$#" -gt 0 ]
do
case "$1" in
-h|--help)
echo "$usage"
exit 0
;;
-p|--print-envs)
print_only="true"
;;
-f|--file)
config_file="$2"
;;
-s|--silent)
silent="true"
;;
--)
break
;;
-*)
echo "Invalid option '$1'. Use --help to see the valid options" >&2
exit 1
;;
# an option argument, continue
*) ;;
esac
shift
done
fi

vars=$(grep -oE '\{\{[A-Za-z0-9_]+\}\}' "${template}" | sort | uniq | sed -e 's/^{{//' -e 's/}}$//')

if [[ -z "$vars" ]]; then
if [ "$silent" == "false" ]; then
echo "Warning: No variable was found in ${template}, syntax is {{VAR}}" >&2
fi
cat ${template}
exit 0
fi

# Load variables from file if needed
if [ "${config_file}" != "<none>" ]; then
if [[ ! -f "${config_file}" ]]; then
echo "The file ${config_file} does not exists" >&2
echo "$usage"
exit 1
fi

# Create temp file where & and "space" is escaped
tmpfile=`mktemp`
sed -e "s;\&;\\\&;g" -e "s;\ ;\\\ ;g" "${config_file}" > $tmpfile
source $tmpfile
fi

var_value() {
eval echo \$$1
}

replaces=""

# Reads default values defined as {{VAR=value}} and delete those lines
# There are evaluated, so you can do {{PATH=$HOME}} or {{PATH=`pwd`}}
# You can even reference variables defined in the template before
defaults=$(grep -oE '^\{\{[A-Za-z0-9_]+=.+\}\}' "${template}" | sed -e 's/^{{//' -e 's/}}$//')

for default in $defaults; do
var=$(echo "$default" | grep -oE "^[A-Za-z0-9_]+")
current=`var_value $var`

# Replace only if var is not set
if [[ -z "$current" ]]; then
eval $default
fi

# remove define line
replaces="-e '/^{{$var=/d' $replaces"
vars="$vars
$current"
done

vars=$(echo $vars | sort | uniq)

if [[ "$print_only" == "true" ]]; then
for var in $vars; do
value=`var_value $var`
echo "$var = $value"
done
exit 0
fi

# Replace all {{VAR}} by $VAR value
for var in $vars; do
value=`var_value $var`
if [[ -z "$value" ]]; then
if [ $silent == "false" ]; then
echo "Warning: $var is not defined and no default is set, replacing by empty" >&2
fi
fi

# Escape slashes
value=$(echo "$value" | sed 's/\//\\\//g');
replaces="-e 's/{{$var}}/${value}/g' $replaces"
done

escaped_template_path=$(echo $template | sed 's/ /\\ /g')
eval sed $replaces "$escaped_template_path"