Skip to content

Commit 4eea2ac

Browse files
committed
Work in progress
1 parent 795c013 commit 4eea2ac

File tree

11 files changed

+231
-437
lines changed

11 files changed

+231
-437
lines changed

Dockerfile

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ FROM debian:jessie
33
MAINTAINER Datadog <package@datadoghq.com>
44

55
ENV DOCKER_DD_AGENT=yes \
6-
AGENT_VERSION=1:5.16.0-1
6+
AGENT_VERSION=1:5.16.0-1 \
7+
PATH="/opt/datadog-agent/embedded/bin:/opt/datadog-agent/bin:${PATH}" \
8+
PYTHONPATH=/opt/datadog-agent/agent
79

810
# Install the Agent
911
RUN echo "deb http://apt.datadoghq.com/ stable main" > /etc/apt/sources.list.d/datadog.list \
@@ -15,15 +17,13 @@ RUN echo "deb http://apt.datadoghq.com/ stable main" > /etc/apt/sources.list.d/d
1517
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
1618

1719
# Configure the Agent
18-
# 1. Listen to statsd (8125) and traces (8126) from other containers
19-
# 2. Turn syslog off
20+
# 1. Turn syslog off by using the DD_CONF_LOG_TO_SYSLOG env var
2021
# 3. Remove dd-agent user from supervisor configuration
2122
# 4. Remove dd-agent user from init.d configuration
2223
# 5. Fix permission on /etc/init.d/datadog-agent
24+
ENV DD_CONF_LOG_TO_SYSLOG=no \
25+
NON_LOCAL_TRAFFIC=yes
2326
RUN mv /etc/dd-agent/datadog.conf.example /etc/dd-agent/datadog.conf \
24-
&& sed -i -e"s/^.*non_local_traffic:.*$/non_local_traffic: yes/" /etc/dd-agent/datadog.conf \
25-
&& sed -i -e"s/^.*log_to_syslog:.*$/log_to_syslog: no/" /etc/dd-agent/datadog.conf \
26-
&& sed -i "/user=dd-agent/d" /etc/dd-agent/supervisor.conf \
2727
&& sed -i 's/AGENTUSER="dd-agent"/AGENTUSER="root"/g' /etc/init.d/datadog-agent \
2828
&& rm /etc/dd-agent/conf.d/network.yaml.default \
2929
|| chmod +x /etc/init.d/datadog-agent
@@ -45,5 +45,6 @@ HEALTHCHECK --interval=5m --timeout=3s --retries=1 \
4545
-c /etc/dd-agent/supervisor.conf status | awk '{print $2}' | egrep -v 'RUNNING|EXITED' | wc -l) \
4646
-eq 0 || exit 1
4747

48+
ADD config_builder.py /config_builder.py
4849
ENTRYPOINT ["/entrypoint.sh"]
4950
CMD ["supervisord", "-n", "-c", "/etc/dd-agent/supervisor.conf"]

alpine/Dockerfile

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@ ENV DD_HOME=/opt/datadog-agent \
77
DD_START_AGENT=0 \
88
DOCKER_DD_AGENT=yes \
99
PYCURL_SSL_LIBRARY=openssl \
10-
AGENT_VERSION=5.16.0
10+
AGENT_VERSION=5.16.0 \
11+
PATH="/opt/datadog-agent/embedded/bin:/opt/datadog-agent/bin:$PATH" \
12+
PYTHONPATH=/opt/datadog-agent/agent
1113

1214
# Add Docker check
1315
COPY conf.d/docker_daemon.yaml "$DD_HOME/agent/conf.d/docker_daemon.yaml"
1416

1517
# Add install and config files
1618
ADD https://raw.githubusercontent.com/DataDog/dd-agent/master/packaging/datadog-agent/source/setup_agent.sh /tmp/setup_agent.sh
19+
ADD https://raw.githubusercontent.com/DataDog/docker-dd-agent/master/config_builder.py /config_builder.py
1720
COPY entrypoint.sh /entrypoint.sh
1821

1922
# Expose supervisor and DogStatsD port
@@ -34,9 +37,9 @@ RUN apk add -qU --no-cache -t .build-deps gcc musl-dev pgcluster-dev linux-heade
3437
# 2. Turn syslog off
3538
# 3. Remove dd-agent user from supervisor configuration
3639
# 4. Remove setup script
40+
ENV DD_CONF_LOG_TO_SYSLOG=no \
41+
NON_LOCAL_TRAFFIC=yes
3742
RUN cp "$DD_HOME/agent/datadog.conf.example" "$DD_HOME/agent/datadog.conf" \
38-
&& sed -i -e"s/^.*non_local_traffic:.*$/non_local_traffic: yes/" "$DD_HOME/agent/datadog.conf" \
39-
&& sed -i -e"s/^.*log_to_syslog:.*$/log_to_syslog: no/" "$DD_HOME/agent/datadog.conf" \
4043
&& sed -i "/user=dd-agent/d" "$DD_HOME/agent/supervisor.conf" \
4144
&& rm "$DD_HOME/agent/conf.d/network.yaml.default" \
4245
|| rm /tmp/setup_agent.sh

alpine/entrypoint.sh

Lines changed: 1 addition & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,7 @@
33
set -x
44

55
##### Core config #####
6-
7-
if [[ $DD_API_KEY ]]; then
8-
export API_KEY=${DD_API_KEY}
9-
fi
10-
11-
if [[ $DD_API_KEY_FILE ]]; then
12-
export API_KEY=$(cat $DD_API_KEY_FILE)
13-
fi
14-
15-
if [[ $API_KEY ]]; then
16-
sed -i -e "s/^.*api_key:.*$/api_key: ${API_KEY}/" /opt/datadog-agent/agent/datadog.conf
17-
else
18-
echo "You must set API_KEY environment variable to run the Datadog Agent container"
19-
exit 1
20-
fi
21-
22-
if [[ $DD_HOSTNAME ]]; then
23-
sed -i -r -e "s/^# ?hostname.*$/hostname: ${DD_HOSTNAME}/" /opt/datadog-agent/agent/datadog.conf
24-
fi
25-
26-
if [[ $DD_TAGS ]]; then
27-
export TAGS=${DD_TAGS}
28-
fi
29-
30-
if [[ $EC2_TAGS ]]; then
31-
export EC2_TAGS=${EC2_TAGS//\//\\/} # escape forward slashes from tags before invoking sed
32-
sed -i -e "s/^# collect_ec2_tags.*$/collect_ec2_tags: ${EC2_TAGS}/" /opt/datadog-agent/agent/datadog.conf
33-
fi
34-
35-
if [[ $TAGS ]]; then
36-
export TAGS=${TAGS//\//\\/} # escape forward slashes from tags before invoking sed
37-
sed -i -r -e "s/^# ?tags:.*$/tags: ${TAGS}/" /opt/datadog-agent/agent/datadog.conf
38-
fi
39-
40-
if [[ $DD_LOG_LEVEL ]]; then
41-
export LOG_LEVEL=$DD_LOG_LEVEL
42-
fi
43-
44-
if [[ $LOG_LEVEL ]]; then
45-
sed -i -e"s/^.*log_level:.*$/log_level: ${LOG_LEVEL}/" /opt/datadog-agent/agent/datadog.conf
46-
fi
6+
/opt/datadog-agent/venv/bin/activate && python /config_builder.py
477

488
if [[ $DD_LOGS_STDOUT ]]; then
499
export LOGS_STDOUT=$DD_LOGS_STDOUT
@@ -57,75 +17,6 @@ if [[ $LOGS_STDOUT == "yes" ]]; then
5717
sed -i -e "/^.*\[program:.*\].*$/a stderr_logfile_maxbytes=0" /opt/datadog-agent/agent/supervisor.conf
5818
fi
5919

60-
if [[ $DD_URL ]]; then
61-
sed -i -e 's@^.*dd_url:.*$@dd_url: '${DD_URL}'@' /opt/datadog-agent/agent/datadog.conf
62-
fi
63-
64-
if [[ $STATSD_METRIC_NAMESPACE ]]; then
65-
sed -i -e "s/^# statsd_metric_namespace:.*$/statsd_metric_namespace: ${STATSD_METRIC_NAMESPACE}/" /opt/datadog-agent/agent/datadog.conf
66-
fi
67-
68-
if [[ $USE_DOGSTATSD ]]; then
69-
sed -i -e "s/^.*use_dogstatsd:.*$/use_dogstatsd: ${USE_DOGSTATSD}/" /opt/datadog-agent/agent/datadog.conf
70-
fi
71-
72-
73-
##### Proxy config #####
74-
75-
if [[ $PROXY_HOST ]]; then
76-
sed -i -e "s/^# proxy_host:.*$/proxy_host: ${PROXY_HOST}/" /opt/datadog-agent/agent/datadog.conf
77-
fi
78-
79-
if [[ $PROXY_PORT ]]; then
80-
sed -i -e "s/^# proxy_port:.*$/proxy_port: ${PROXY_PORT}/" /opt/datadog-agent/agent/datadog.conf
81-
fi
82-
83-
if [[ $PROXY_USER ]]; then
84-
sed -i -e "s/^# proxy_user:.*$/proxy_user: ${PROXY_USER}/" /opt/datadog-agent/agent/datadog.conf
85-
fi
86-
87-
if [[ $PROXY_PASSWORD ]]; then
88-
sed -i -e "s/^# proxy_password:.*$/proxy_password: ${PROXY_PASSWORD}/" /opt/datadog-agent/agent/datadog.conf
89-
fi
90-
91-
##### Service discovery #####
92-
EC2_HOST_IP=`curl --silent http://169.254.169.254/latest/meta-data/local-ipv4 --max-time 1`
93-
94-
if [[ $SD_BACKEND ]]; then
95-
sed -i -e "s/^# service_discovery_backend:.*$/service_discovery_backend: ${SD_BACKEND}/" /opt/datadog-agent/agent/datadog.conf
96-
fi
97-
98-
if [[ $SD_CONFIG_BACKEND ]]; then
99-
sed -i -e "s/^# sd_config_backend:.*$/sd_config_backend: ${SD_CONFIG_BACKEND}/" /opt/datadog-agent/agent/datadog.conf
100-
# If no SD_BACKEND_HOST value is defined AND running in EC2 and host ip is available
101-
if [[ -z $SD_BACKEND_HOST && -n $EC2_HOST_IP ]]; then
102-
export SD_BACKEND_HOST="$EC2_HOST_IP"
103-
fi
104-
fi
105-
106-
if [[ $SD_BACKEND_HOST ]]; then
107-
sed -i -e "s/^# sd_backend_host:.*$/sd_backend_host: ${SD_BACKEND_HOST}/" /opt/datadog-agent/agent/datadog.conf
108-
fi
109-
110-
if [[ $SD_BACKEND_PORT ]]; then
111-
sed -i -e "s/^# sd_backend_port:.*$/sd_backend_port: ${SD_BACKEND_PORT}/" /opt/datadog-agent/agent/datadog.conf
112-
fi
113-
114-
if [[ $SD_TEMPLATE_DIR ]]; then
115-
sed -i -e 's@^# sd_template_dir:.*$@sd_template_dir: '${SD_TEMPLATE_DIR}'@' /opt/datadog-agent/agent/datadog.conf
116-
fi
117-
118-
if [[ $SD_CONSUL_TOKEN ]]; then
119-
sed -i -e 's@^# consul_token:.*$@consul_token: '${SD_CONSUL_TOKEN}'@' /opt/datadog-agent/agent/datadog.conf
120-
fi
121-
122-
if [[ $SD_BACKEND_USER ]]; then
123-
sed -i -e 's@^# sd_backend_username:.*$@sd_backend_username: '${SD_BACKEND_USER}'@' /opt/datadog-agent/agent/datadog.conf
124-
fi
125-
126-
if [[ $SD_BACKEND_PASSWORD ]]; then
127-
sed -i -e 's@^# sd_backend_password:.*$@sd_backend_password: '${SD_BACKEND_PASSWORD}'@' /opt/datadog-agent/agent/datadog.conf
128-
fi
12920

13021
##### Integrations config #####
13122

@@ -184,8 +75,6 @@ find /checks.d -name '*.py' -exec cp {} /opt/datadog-agent/agent/checks.d \;
18475

18576
##### Starting up #####
18677

187-
export PATH="/opt/datadog-agent/embedded/bin:/opt/datadog-agent/bin:$PATH"
188-
18978
if [[ $DOGSTATSD_ONLY ]]; then
19079
source /opt/datadog-agent/venv/bin/activate && python /opt/datadog-agent/agent/dogstatsd.py
19180
else

config_builder.py

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
#!/opt/datadog-agent/embedded/bin/python
2+
'''
3+
This script is used to generate the configuration of the datadog agent, its
4+
integrations and other moving parts.
5+
'''
6+
7+
from os import getenv, environ
8+
import logging
9+
from urllib2 import urlopen, URLError, HTTPError
10+
from socket import getdefaulttimeout, setdefaulttimeout
11+
from ConfigParser import ConfigParser
12+
13+
class ConfBuilder(object):
14+
'''
15+
This class manages the configuration files
16+
'''
17+
def __init__(self):
18+
# excludes from the generic variables parsing the ones that have a
19+
# certain logic warpped around them
20+
self.exclude_from_generic = [
21+
'DD_API_KEY', 'DD_API_KEY_FILE', 'DD_HOME',
22+
'DD_START_AGENT', 'DD_LOGS_STDOUT'
23+
]
24+
self.datadog_conf_file = '/etc/dd-agent/datadog.conf'
25+
self.supervisor_conf_file = '/etc/dd-agent/supervisor.conf'
26+
dd_home = getenv('DD_HOME')
27+
if dd_home is not None:
28+
self.datadog_conf_file = '{}/agent/datadog.conf'.format(dd_home)
29+
self.supervisor_conf_file = '{}/agent/supervisor.conf'.format(dd_home)
30+
# This will store the config parser object that is used in the different functions
31+
self.config = None
32+
33+
def load_config(self, config_file):
34+
'''
35+
Loads a config file using ConfigParser
36+
'''
37+
self.config = ConfigParser()
38+
# import existing config from file
39+
with open(config_file, 'rb') as cfd:
40+
self.config.readfp(cfd)
41+
42+
def save_config(self, config_file):
43+
'''
44+
Saves a ConfigParser object (self.config) to the given file
45+
'''
46+
if self.config is None:
47+
logging.error('config object needs to be created before saving anything')
48+
exit(1)
49+
with open(config_file, 'wb') as cfd:
50+
self.config.write(cfd)
51+
52+
def build_supervisor_conf(self):
53+
'''
54+
Builds the supervisor.conf based on the environment variables
55+
'''
56+
self.load_config(self.supervisor_conf_file)
57+
58+
_logs_stdout = getenv('DD_LOGS_STDOUT', getenv('LOGS_STDOUT', 'no'))
59+
for _section in self.config.sections():
60+
if self.config.has_option(_section, 'user'):
61+
self.config.remove_option(_section, 'user')
62+
if _logs_stdout == 'yes':
63+
for _opt in self.config.options(_section):
64+
if _opt.endswith('_logfile'):
65+
self.config.remove_option(_section, _opt)
66+
if _section.startswith('program:'):
67+
self.set_property('stdout_logfile', '/dev/stdout', _section)
68+
self.set_property('stdout_logfile_maxbytes', '0', _section)
69+
self.set_property('stderr_logfile', '/dev/stderr', _section)
70+
self.set_property('stderr_logfile_maxbytes', '0', _section)
71+
72+
if getenv('KUBERNETES') is not None or getenv('MESOS_MASTER') is not None or getenv('MESOS_SLAVE') is not None:
73+
# expose supervisord as a health check
74+
if not self.config.has_section('inet_http_server'):
75+
self.config.add_section('inet_http_server')
76+
self.set_property('port', '0.0.0.0:9001', 'inet_http_server')
77+
78+
self.save_config(self.supervisor_conf_file)
79+
80+
def build_datadog_conf(self):
81+
'''
82+
Builds the datadog.conf based on the environment variables
83+
'''
84+
self.load_config(self.datadog_conf_file)
85+
86+
##### Core config #####
87+
self.set_api_key()
88+
self.set_from_env_mapping('DD_HOSTNAME', 'hostname')
89+
self.set_from_env_mapping('EC2_TAGS', 'collect_ec2_tags')
90+
# The TAGS env variable superseeds DD_TAGS
91+
self.set_from_env_mapping('DD_TAGS', 'tags')
92+
self.set_from_env_mapping('TAGS', 'tags')
93+
# The LOG_LEVEL env variable superseeds DD_LOG_LEVEL
94+
self.set_from_env_mapping('DD_LOG_LEVEL', 'log_level')
95+
self.set_from_env_mapping('LOG_LEVEL', 'log_level')
96+
self.set_from_env_mapping('NON_LOCAL_TRAFFIC', 'non_local_traffic', action='store_true')
97+
self.set_from_env_mapping('DD_URL', 'dd_url')
98+
self.set_from_env_mapping('STATSD_METRIC_NAMESPACE', 'statsd_metric_namespace')
99+
self.set_from_env_mapping('USE_DOGSTATSD', 'use_dogstatsd')
100+
##### Proxy config #####
101+
self.set_from_env_mapping('PROXY_HOST', 'proxy_host')
102+
self.set_from_env_mapping('PROXY_PORT', 'proxy_port')
103+
self.set_from_env_mapping('PROXY_USER', 'proxy_user')
104+
self.set_from_env_mapping('PROXY_PASSWORD', 'proxy_password')
105+
##### Service discovery #####
106+
self.set_from_env_mapping('SD_BACKEND', 'service_discovery_backend')
107+
self.set_sd_backend_host()
108+
self.set_from_env_mapping('SD_BACKEND_PORT', 'sd_backend_port')
109+
self.set_from_env_mapping('SD_TEMPLATE_DIR', 'sd_template_dir')
110+
self.set_from_env_mapping('SD_CONSUL_TOKEN', 'consul_token')
111+
self.set_from_env_mapping('SD_BACKEND_USER', 'sd_backend_username')
112+
self.set_from_env_mapping('SD_BACKEND_PASSWORD', 'sd_backend_password')
113+
# Magic trick to automatically add properties not yet define in the doc
114+
self.set_generics('DD_CONF_')
115+
116+
self.save_config(self.datadog_conf_file)
117+
118+
def set_api_key(self):
119+
'''
120+
Used for building datadog.conf
121+
Gets the API key from the environment or the key file
122+
and sets it in the configuration
123+
'''
124+
api_key = getenv('DD_API_KEY', getenv('API_KEY', ''))
125+
keyfile = getenv('DD_API_KEY_FILE')
126+
if keyfile is not None:
127+
try:
128+
with open(keyfile, 'r') as kfile:
129+
api_key = kfile.read()
130+
except IOError:
131+
logging.warning('Unable to read the content of they key file specified in DD_API_KEY_FILE')
132+
if len(api_key) <= 0:
133+
logging.error('You must set API_KEY environment variable or include a DD_API_KEY_FILE to run the Datadog Agent container')
134+
exit(1)
135+
self.set_property('api_key', api_key)
136+
137+
def set_from_env_mapping(self, env_var_name, property_name, section='Main', action=None):
138+
'''
139+
Sets a property using the corresponding environment variable if it exists
140+
It also returns the value in case you want to play with it
141+
If action is specified to 'store_true', whatever the content of the
142+
env variable is (if exists), the value of the property will be true
143+
'''
144+
_val = getenv(env_var_name)
145+
if _val is not None:
146+
if action == 'store_true':
147+
_val = 'true'
148+
self.set_property(property_name, _val, section)
149+
return _val
150+
return None
151+
152+
def set_sd_backend_host(self):
153+
'''
154+
Used for building datadog.conf
155+
Sets sd_config_backend and sd_backend_host depending on the environment
156+
'''
157+
_config_backend = getenv('SD_CONFIG_BACKEND', 'sd_config_backend')
158+
if _config_backend is not None:
159+
_backend_host = getenv('SD_BACKEND_HOST', 'sd_backend_host')
160+
if _backend_host is None:
161+
_timeout = getdefaulttimeout()
162+
try:
163+
setdefaulttimeout(1)
164+
_ec2_ip = urlopen('http://169.254.169.254/latest/meta-data/local-ipv4')
165+
self.set_property('sd_backend_host', _ec2_ip.read())
166+
except (URLError, HTTPError):
167+
pass # silent fail on purpose
168+
setdefaulttimeout(_timeout)
169+
170+
def set_generics(self, prefix='DD_CONF_'):
171+
'''
172+
Looks for environment variables starting by the given prefix and consider that the
173+
rest of the variable name is the name of the property to set
174+
'''
175+
for dd_var in environ:
176+
if dd_var.startswith(prefix) and dd_var.upper() not in self.exclude_from_generic:
177+
if len(dd_var) > 0:
178+
self.set_property(dd_var[len(prefix):].lower(), environ[dd_var])
179+
180+
def set_property(self, property_name, property_value, section='Main'):
181+
'''
182+
Sets the given property to the given value in the configuration
183+
'''
184+
if self.config is None:
185+
logging.error('config object needs to be created before setting properties')
186+
exit(1)
187+
self.config.set(section, property_name, property_value)
188+
189+
if __name__ == '__main__':
190+
cfg = ConfBuilder()
191+
cfg.build_datadog_conf()

0 commit comments

Comments
 (0)