Skip to content
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
3,924 changes: 2,543 additions & 1,381 deletions .circleci/config.yml

Large diffs are not rendered by default.

92 changes: 54 additions & 38 deletions bin/docker_build.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#!/bin/sh

set -e

usage() {
Expand All @@ -8,38 +7,64 @@ docker_build.sh - Create a build image in Docker

usage:

docker_build.sh [--yes] [--push] [--rm] [<BASE_IMAGE>]
docker_build.sh --platforms <PLATFORM> [--yes] [--push] [<BASE_IMAGE>]
`docker build` an image suitable for building a Datadog nginx
module compatible with the optionally specified BASE_IMAGE.
If BASE_IMAGE is not specified, use the contents of the
nginx-version-info file.

Prompt the user for confirmation unless --yes is specified.

--platforms is a comma separated list of platforms to target. --platforms is required.
Example: --platforms linux/amd64,linux/arm64

If --push is specified, push the resulting image to DockerHub
with a suitable tag.

If --rm is specified, then the image will be deleted locally after
it's been build and/or pushed.

docker_build.sh --help
docker_build.sh -h
Print this message.
END_USAGE
}

ask() {
if [ "$yes" -eq 1 ]; then
return
fi

while true; do
printf '%s [Yn]: ' "$1"
read -r response
case "$response" in
n|N|no|NO|No) return 1 ;;
y|Y|yes|YES|Yes|'') return ;;
*) >&2 printf "\nI don't understand.\n"
esac
done
}

repo=$(dirname "$0")/..
yes=0
push=0
delete=0
platforms=''
base_image=''

while [ $# -ne 0 ]; do
case "$1" in
-h|--help) usage; exit ;;
-y|--yes) yes=1 ;;
-p|--push) push=1 ;;
-d|--rm) delete=1 ;;
-h|--help)
usage
exit
;;
-y|--yes)
yes=1
;;
-p|--push)
push=1
;;
--platforms)
platforms="$2"
shift
;;
*)
if [ -n "$base_image" ]; then
>&2 printf 'base image was specified twice: first as %s and now as %s.' "$base_image" "$1"
Expand All @@ -57,40 +82,31 @@ if [ -z "$base_image" ]; then
base_image="$BASE_IMAGE"
fi

ask() {
if [ "$yes" -eq 1 ]; then
return
fi

while true; do
printf '%s [Yn]: ' "$1"
read -r response
case "$response" in
n|N|no|NO|No) return 1 ;;
y|Y|yes|YES|Yes|'') return ;;
*) >&2 printf "\nI don't understand.\n"
esac
done
}
if [ -z "$platforms" ]; then
>&2 printf 'missing required option: --platforms\n'
usage
exit
fi

base_image_without_colons=$(echo "$base_image" | tr ':' '_')
built_tag="nginx-datadog-build-$base_image_without_colons"
if ! ask "Build image compatible with $base_image and tag as $built_tag?"; then

local_destination="$(pwd)/${built_tag}.tar"
remote_destination="datadog/docker-library:$built_tag"
buildx_output_args="--output=type=image,name=${remote_destination},push=true"

if ! ask "Build image compatible with ${base_image} for ${platforms} and tag as ${built_tag}?"; then
exit 1
fi
docker build --build-arg "BASE_IMAGE=$base_image" --tag "$built_tag" "$repo"

if [ "$push" -eq 0 ]; then
exit
fi
destination="datadog/docker-library:$built_tag"
if ! ask "Push built image to \"$destination\"?"; then
exit
if ! ask "Push built image to \"${remote_destination}\"?"; then
buildx_output_args="--output=type=tar,dest=${local_destination}"
fi
fi

docker tag "$built_tag" "$destination"
docker push "$destination"

if [ "$delete" -eq 1 ]; then
docker images --no-trunc | awk "{ if (\$2 == \"$built_tag\") { print \$3 } }" | xargs docker rmi --force
fi
docker buildx build \
--platform "${platforms}" \
--build-arg "BASE_IMAGE=${base_image}" \
"${buildx_output_args}" \
"${repo}"
352 changes: 223 additions & 129 deletions bin/generate_jobs_yaml.sh

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion bin/rebuild_all_images.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ bin=$(dirname "$0")

base_images | while read -r base_image; do
echo "Building build image for base image $base_image"
"$bin"/docker_build.sh --yes --push --rm "$base_image"
"$bin"/docker_build.sh --platform linux/amd64,linux/arm64 --yes --push "$base_image"
done
12 changes: 11 additions & 1 deletion example/bin/run
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
#!/bin/sh

set -e

cd "$(dirname "$0")"/..

BASE_IMAGE="${BASE_IMAGE:-nginx:1.23.1-alpine}"
export BASE_IMAGE

# Fail fast in case the base image is not supported.
# For now, we only support nginx, nginx-alpine, and amazonlinux images.
case "$BASE_IMAGE" in
amazonlinux:*) ;;
nginx:*) ;;
*)
>&2 echo 'BASE_IMAGE value "%s" is invalid. See nginx-version-info.example for more information.' "$BASE_IMAGE"
exit 1
;;
esac

if [ "$DD_API_KEY" = '' ]; then
>&2 echo 'The environment variable DD_API_KEY must be set to a Datadog API key.'
exit 1
Expand Down
18 changes: 14 additions & 4 deletions example/services/client/install-tools.sh
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
#!/bin/sh

set -e

case "$(uname -m)" in
aarch64)
ARCH="arm64"
;;
*)
ARCH="$(uname -m)"
;;
esac

apk update
apk add wget tar curl jq

# grpcurl is a self-contained binary (Go program)
GRPCURL_TAR="grpcurl_1.8.6_linux_${ARCH}.tar.gz"

cd /tmp
wget https://github.com/fullstorydev/grpcurl/releases/download/v1.8.6/grpcurl_1.8.6_linux_x86_64.tar.gz
tar -xzf grpcurl_1.8.6_linux_x86_64.tar.gz
wget https://github.com/fullstorydev/grpcurl/releases/download/v1.8.6/"${GRPCURL_TAR}"
tar -xzf "${GRPCURL_TAR}"
mv grpcurl /usr/local/bin
rm grpcurl_1.8.6_linux_x86_64.tar.gz
rm "${GRPCURL_TAR}"
83 changes: 57 additions & 26 deletions example/services/nginx/install_datadog.sh
Original file line number Diff line number Diff line change
@@ -1,61 +1,92 @@
#!/bin/sh
# Set up the nginx container image. Install nginx, if necessary, and install the nginx-datadog module.

set -x
set -e

is_installed() {
command -v "$1" > /dev/null 2>&1
}

get_latest_release() {
curl --silent "https://api.github.com/repos/$1/releases/latest" | jq --raw-output .tag_name
}

if [ "$BASE_IMAGE" = '' ]; then
>&2 echo 'This script expects BASE_IMAGE to be in the environment, e.g. "nginx:1.23.1-alpine" or "amazonlinux:2.0.20220121.0".'
exit 1
fi

# Fail fast in case the base image is not supported.
# For now, we only support nginx, nginx-alpine, and amazonlinux images.
case "$BASE_IMAGE" in
amazonlinux:*) nginx_modules_path=/usr/share/nginx/modules ;;
*) nginx_modules_path=/usr/lib/nginx/modules ;;
amazonlinux:*)
nginx_modules_path=/usr/share/nginx/modules
;;
nginx:*)
nginx_modules_path=/usr/lib/nginx/modules
;;
*)
>&2 printf 'BASE_IMAGE value "%s" is invalid. See nginx-version-info.example for more information.\n' "$BASE_IMAGE"
exit 1
;;
esac

# If nginx itself is not installed already, then install it.
if ! command -v nginx >/dev/null 2>&1; then
if command -v apt-get >/dev/null 2>&1; then
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y nginx
elif command -v apk >/dev/null 2>&1; then
apk update
apk add nginx
elif command -v yum >/dev/null 2>&1; then
yum update -y
amazon-linux-extras enable -y nginx1
yum install -y nginx
else
>&2 printf 'Did not find a supported package manager.\n'
exit 2
fi
fi
ARCH="$(uname -m)"
case "$ARCH" in
aarch64)
ARCH="arm64"
;;
x86_64)
ARCH="amd64"
;;
*)
>&2 echo "Platform ${BASE_IMAGE}-${ARCH} is not supported."
exit 1
;;
esac

# Install the command line tools needed to fetch and extract the module.
# `apt-get` (Debian, Ubuntu), `apk` (Alpine), and `yum` (Amazon Linux) are
# supported.
if command -v apt-get >/dev/null 2>&1; then
if is_installed apt-get; then
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y tar wget curl jq
elif command -v apk >/dev/null 2>&1; then
elif is_installed apk; then
apk update
apk add tar wget curl jq
elif command -v yum >/dev/null 2>&1; then
elif is_installed yum; then
yum update -y
yum install -y tar wget curl jq
else
>&2 printf 'Did not find a supported package manager.\n'
exit 3
fi

get_latest_release() {
curl --silent "https://api.github.com/repos/$1/releases/latest" | jq --raw-output .tag_name
}
# If nginx itself is not installed already, then install it.
if ! is_installed nginx; then
if is_installed apt-get; then
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y nginx
elif is_installed apk; then
apk update
apk add nginx
elif is_installed yum; then
yum update -y
amazon-linux-extras enable -y nginx1
yum install -y nginx
else
>&2 printf 'Did not find a supported package manager.\n'
exit 2
fi
fi

# TODO(@dmehala): nginx version can be different from the base image
RELEASE_TAG=$(get_latest_release DataDog/nginx-datadog)

base_image_without_colons=$(echo "$BASE_IMAGE" | tr ':' '_')
tarball="$base_image_without_colons-ngx_http_datadog_module.so.tgz"
tarball="${base_image_without_colons}-${ARCH}-ngx_http_datadog_module.so.tgz"

wget "https://github.com/DataDog/nginx-datadog/releases/download/$RELEASE_TAG/$tarball"
tar -xzf "$tarball" -C "$nginx_modules_path"
rm "$tarball"
1 change: 1 addition & 0 deletions nginx-version-info.example
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
# NGINX_MODULES_PATH is not required for the "build" or "build-in-docker"
# make targets, but is required for the "test" and "lab" targets.

ARCH=linux/amd64
NGINX_VERSION=1.24.0
BASE_IMAGE=nginx:1.24.0-alpine
NGINX_MODULES_PATH=/usr/lib/nginx/modules
Expand Down
8 changes: 8 additions & 0 deletions test/bin/run
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,13 @@ export BASE_IMAGE
export NGINX_MODULES_PATH="${NGINX_MODULES_PATH:-/usr/lib/nginx/modules}"
export NGINX_CONF_PATH="${NGINX_CONF_PATH:-/etc/nginx/nginx.conf}"

printf "===============================\n"
printf "Running test with:\n"
printf " - ARCH=%s\n" "${ARCH}"
printf " - BASE_IMAGE=%s\n" "${BASE_IMAGE}"
printf " - NGINX_CONF_PATH=%s\n" "${NGINX_CONF_PATH}"
printf " - NGINX_MODULES_PATH=%s\n" "${NGINX_MODULES_PATH}"
printf "===============================\n"

docker compose build --parallel
python3 -m unittest "$@"
29 changes: 29 additions & 0 deletions test/cases/orchestration.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ def child_env(parent_env=None):

result['PATH'] = docker_bin

# `docker compose` uses $HOME to examine `~/.cache`, though I have no idea
# why.
result['HOME'] = parent_env['HOME']

return result


Expand Down Expand Up @@ -325,6 +329,30 @@ def header_args():
return fields, body


def add_services_in_nginx_etc_hosts(services):
"""When we added build/test support on ARM64 by using the ARM64 execution
environment in CircleCI, we started seeing intermittent delays in DNS
lookups, always about five seconds.

This function uses `getent` to look up the IP address of each `docker
compose` service, and then associates the address with the service name by
adding a line to /etc/hosts in the nginx service container. This way, the
default `gethostbyname()` resolver used by nginx will not use DNS."""
script = f"""
for service in {" ".join(f"'{service}'" for service in services)}; do
getent hosts "$service" >>/etc/hosts
done
"""
# "-T" means "don't allocate a TTY". This is necessary to avoid the
# error "the input device is not a TTY".
command = docker_compose_command('exec', '-T', '--', 'nginx',
'/bin/sh')
subprocess.run(command,
input=script,
env=child_env(),
encoding='utf8')


class Orchestration:
"""A handle for a `docker compose` session.

Expand Down Expand Up @@ -386,6 +414,7 @@ def up(self):
runtime_info = ready_queue.get()
self.containers = runtime_info['containers']
print(runtime_info, file=self.verbose, flush=True)
add_services_in_nginx_etc_hosts(self.services)

def down(self):
"""Stop service orchestration.
Expand Down
Loading