This is an automated email from the ASF dual-hosted git repository.
ocket8888 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
The following commit(s) were added to refs/heads/master by this push:
new 108034f Use TO client library API v3 for the CDN-in-a-Box enroller
service (#4909)
108034f is described below
commit 108034f6edb8ccc04183eed17fa7888d6760e595
Author: Zach Hoffman <[email protected]>
AuthorDate: Mon Jul 27 16:00:34 2020 +0000
Use TO client library API v3 for the CDN-in-a-Box enroller service (#4909)
* Use TO client library API v3 for the enroller
* Add server IP addresses to new interfaces field when enrolling servers
* Remove unused imports, functions, and structs
* Fix some warnings
---
CHANGELOG.md | 1 +
infrastructure/cdn-in-a-box/dns/Dockerfile | 5 ++-
infrastructure/cdn-in-a-box/dns/entrypoint.sh | 14 ++++--
infrastructure/cdn-in-a-box/enroller/Dockerfile | 8 +++-
infrastructure/cdn-in-a-box/enroller/enroller.go | 52 ++++++++--------------
infrastructure/cdn-in-a-box/enroller/run.sh | 12 ++++-
.../cdn-in-a-box/enroller/server_template.json | 16 +++----
.../cdn-in-a-box/traffic_ops/Dockerfile-db | 6 ++-
infrastructure/cdn-in-a-box/traffic_ops/run-db.sh | 8 +++-
.../cdn-in-a-box/traffic_ops/to-access.sh | 29 +++++++++++-
.../traffic_ops_data/servers/010-dns_server.json | 16 -------
.../traffic_ops_data/servers/020-db_server.json | 17 -------
.../servers/030-enroller_server.json | 16 -------
.../cdn-in-a-box/traffic_router/Dockerfile | 2 +-
.../cdn-in-a-box/traffic_stats/Dockerfile-influxdb | 8 +++-
15 files changed, 107 insertions(+), 103 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 236d0ae..8eae060 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -56,6 +56,7 @@ The format is based on [Keep a
Changelog](http://keepachangelog.com/en/1.0.0/).
- Updated `/cdns/{{name}}/configs/monitoring` TO API endpoint to return
multi-interface data.
- CDN Snapshots now use a server's "service addresses" to provide its IP
addresses.
- Changed the `/publish/CacheStats` in Traffic Monitor to support multiple
interfaces.
+ - Changed the CDN-in-a-Box server enrollment template to support multiple
interfaces.
### Deprecated
- Deprecated the non-nullable `DeliveryService` Go struct and other structs
that use it. `DeliveryServiceNullable` structs should be used instead.
diff --git a/infrastructure/cdn-in-a-box/dns/Dockerfile
b/infrastructure/cdn-in-a-box/dns/Dockerfile
index 333a35f..804a89b 100644
--- a/infrastructure/cdn-in-a-box/dns/Dockerfile
+++ b/infrastructure/cdn-in-a-box/dns/Dockerfile
@@ -26,7 +26,10 @@ RUN echo 'APT::Install-Recommends 0;' >>
/etc/apt/apt.conf.d/01norecommends \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y vim.tiny wget net-tools
sudo net-tools ca-certificates unzip apt-transport-https \
&& rm -rf /var/lib/apt/lists/* && rm -rf
/etc/apt/apt.conf.d/docker-gzip-indexes \
&& apt-get update \
- && DEBIAN_FRONTEND=noninteractive apt-get install -y bind9=${BIND_VERSION}*
bind9-host=${BIND_VERSION}* dnsutils \
+ && DEBIAN_FRONTEND=noninteractive apt-get install -y \
+ #to-access dependencies
+ jq gettext \
+ bind9=${BIND_VERSION}* bind9-host=${BIND_VERSION}* dnsutils \
&& rm -rf /var/lib/apt/lists/*
COPY dns/entrypoint.sh /sbin/entrypoint.sh
diff --git a/infrastructure/cdn-in-a-box/dns/entrypoint.sh
b/infrastructure/cdn-in-a-box/dns/entrypoint.sh
index cb924cc..6cf3428 100644
--- a/infrastructure/cdn-in-a-box/dns/entrypoint.sh
+++ b/infrastructure/cdn-in-a-box/dns/entrypoint.sh
@@ -16,7 +16,9 @@
# specific language governing permissions and limitations
# under the License.
#
-set -e
+set -o errexit
+# enable job control
+set -o monitor
BIND_DATA_DIR=${DATA_DIR}/bind
@@ -70,7 +72,13 @@ fi
# default behaviour is to launch named
if [[ -z ${1} ]]; then
echo "Starting named..."
- exec $(which named) -u ${BIND_USER} -g ${EXTRA_ARGS}
+ $(which named) -u ${BIND_USER} -g ${EXTRA_ARGS} &
else
- exec "$@"
+ "$@" &
fi
+
+source /to-access.sh
+# Enroll with traffic ops
+TO_URL="https://$TO_FQDN:$TO_PORT"
+to-enroll dns ALL '' 53 || (while true; do echo "enroll failed."; sleep 3 ;
done)
+fg;
diff --git a/infrastructure/cdn-in-a-box/enroller/Dockerfile
b/infrastructure/cdn-in-a-box/enroller/Dockerfile
index f951f7b..0fc2755 100644
--- a/infrastructure/cdn-in-a-box/enroller/Dockerfile
+++ b/infrastructure/cdn-in-a-box/enroller/Dockerfile
@@ -20,7 +20,7 @@ FROM golang:1.14.2 AS enroller-builder
# enroller source and dependencies
COPY ./lib/ /go/src/github.com/apache/trafficcontrol/lib/
COPY ./vendor/ /go/src/github.com/apache/trafficcontrol/vendor/
-COPY ./traffic_ops/v2-client/
/go/src/github.com/apache/trafficcontrol/traffic_ops/v2-client/
+COPY ./traffic_ops/client/
/go/src/github.com/apache/trafficcontrol/traffic_ops/client/
COPY ./traffic_ops/vendor/
/go/src/github.com/apache/trafficcontrol/traffic_ops/vendor/
COPY ./infrastructure/cdn-in-a-box/
/go/src/github.com/apache/trafficcontrol/infrastructure/cdn-in-a-box/
@@ -31,7 +31,11 @@ RUN go clean && go get -v && go build
FROM debian:stretch
-RUN apt-get update && apt-get install -y netcat curl dnsutils net-tools &&
apt-get clean
+RUN apt-get update && apt-get install -y \
+ netcat curl dnsutils net-tools \
+ #to-access dependencies
+ jq gettext && \
+ apt-get clean
COPY --from=enroller-builder \
/go/src/github.com/apache/trafficcontrol/infrastructure/cdn-in-a-box/enroller/enroller
\
/go/src/github.com/apache/trafficcontrol/infrastructure/cdn-in-a-box/enroller/run.sh
\
diff --git a/infrastructure/cdn-in-a-box/enroller/enroller.go
b/infrastructure/cdn-in-a-box/enroller/enroller.go
index e9c1bd8..f3a9cbd 100644
--- a/infrastructure/cdn-in-a-box/enroller/enroller.go
+++ b/infrastructure/cdn-in-a-box/enroller/enroller.go
@@ -18,7 +18,6 @@ package main
// under the License.
import (
- "bytes"
"encoding/json"
"errors"
"flag"
@@ -33,7 +32,7 @@ import (
log "github.com/apache/trafficcontrol/lib/go-log"
tc "github.com/apache/trafficcontrol/lib/go-tc"
- client "github.com/apache/trafficcontrol/traffic_ops/v2-client"
+ "github.com/apache/trafficcontrol/traffic_ops/client"
"github.com/kelseyhightower/envconfig"
"gopkg.in/fsnotify.v1"
)
@@ -49,18 +48,10 @@ func newSession(reqTimeout time.Duration, toURL string,
toUser string, toPass st
return session{s}, err
}
-func printJSON(label string, b interface{}) {
- var buf bytes.Buffer
- enc := json.NewEncoder(&buf)
- enc.SetIndent(``, ` `)
- enc.Encode(b)
- log.Infoln(label, buf.String())
-}
-
func (s session) getParameter(m tc.Parameter) (tc.Parameter, error) {
// TODO: s.GetParameterByxxx() does not seem to work with values with
spaces --
// doing this the hard way for now
- parameters, _, err := s.GetParameters()
+ parameters, _, err := s.GetParameters(nil)
if err != nil {
return m, err
}
@@ -73,7 +64,7 @@ func (s session) getParameter(m tc.Parameter) (tc.Parameter,
error) {
}
func (s session) getDeliveryServiceIDByXMLID(n string) (int, error) {
- dses, _, err := s.GetDeliveryServiceByXMLIDNullable(url.QueryEscape(n))
+ dses, _, err := s.GetDeliveryServiceByXMLIDNullable(url.QueryEscape(n),
nil)
if err != nil {
return -1, err
}
@@ -230,7 +221,7 @@ func enrollDeliveryServiceServer(toSession *session, r
io.Reader) error {
return err
}
- dses, _, err := toSession.GetDeliveryServiceByXMLIDNullable(dss.XmlId)
+ dses, _, err := toSession.GetDeliveryServiceByXMLIDNullable(dss.XmlId,
nil)
if err != nil {
return err
}
@@ -242,18 +233,20 @@ func enrollDeliveryServiceServer(toSession *session, r
io.Reader) error {
}
dsID := *dses[0].ID
+ params := &url.Values{}
var serverIDs []int
for _, sn := range dss.ServerNames {
- servers, _, err := toSession.GetServerByHostName(sn)
+ params.Set("hostName", sn)
+ servers, _, err := toSession.GetServers(params, nil)
if err != nil {
return err
}
- if len(servers) == 0 {
+ if len(servers.Response) == 0 {
return errors.New("no server with hostName " + sn)
}
- serverIDs = append(serverIDs, servers[0].ID)
+ serverIDs = append(serverIDs, *servers.Response[0].ID)
}
- _, err = toSession.CreateDeliveryServiceServers(dsID, serverIDs, true)
+ _, _, err = toSession.CreateDeliveryServiceServers(dsID, serverIDs,
true)
if err != nil {
log.Infof("error creating DeliveryServiceServer: %s\n", err)
}
@@ -354,7 +347,7 @@ func enrollParameter(toSession *session, r io.Reader) error
{
}
for _, n := range profiles {
- profiles, _, err :=
toSession.GetProfileByName(n)
+ profiles, _, err :=
toSession.GetProfileByName(n, nil)
if err != nil {
return err
}
@@ -509,12 +502,6 @@ func enrollUser(toSession *session, r io.Reader) error {
return err
}
-// using documented import form for profiles
-type profileImport struct {
- Profile tc.Profile `json:"profile"`
- Parameters []tc.ParameterNullable `json:"parameters"`
-}
-
// enrollProfile takes a json file and creates a Profile object using the TO
API
func enrollProfile(toSession *session, r io.Reader) error {
dec := json.NewDecoder(r)
@@ -537,7 +524,7 @@ func enrollProfile(toSession *session, r io.Reader) error {
return errors.New("missing name on profile")
}
- profiles, _, err := toSession.GetProfileByName(profile.Name)
+ profiles, _, err := toSession.GetProfileByName(profile.Name, nil)
createProfile := false
if err != nil || len(profiles) == 0 {
@@ -559,7 +546,7 @@ func enrollProfile(toSession *session, r io.Reader) error {
log.Infof("error creating profile from %+v:
%s\n", profile, err.Error())
}
}
- profiles, _, err = toSession.GetProfileByName(profile.Name)
+ profiles, _, err = toSession.GetProfileByName(profile.Name, nil)
if err != nil {
log.Infof("error getting profile ID from %+v: %s\n",
profile, err.Error())
}
@@ -602,8 +589,7 @@ func enrollProfile(toSession *session, r io.Reader) error {
}
eparam, err = toSession.getParameter(param)
if err != nil {
- log.Infof("error getting new parameter %+v\n",
param)
- eparam, err = toSession.getParameter(param)
+ log.Infof("error getting new parameter %+v:
\n", param)
log.Infof(err.Error())
continue
}
@@ -635,7 +621,7 @@ func enrollProfile(toSession *session, r io.Reader) error {
// enrollServer takes a json file and creates a Server object using the TO API
func enrollServer(toSession *session, r io.Reader) error {
dec := json.NewDecoder(r)
- var s tc.Server
+ var s tc.ServerNullable
err := dec.Decode(&s)
if err != nil && err != io.EOF {
log.Infof("error decoding Server: %s\n", err)
@@ -750,7 +736,7 @@ func (dw *dirWatcher) watch(watchdir, t string, f
func(*session, io.Reader) erro
if err != nil {
return err
}
- defer fh.Close()
+ defer log.Close(fh, "could not close file")
return f(toSession, fh)
}
}
@@ -770,7 +756,7 @@ func startServer(httpPort string, toSession *session,
dispatcher map[string]func
baseEP := "/api/2.0/"
for d, f := range dispatcher {
http.HandleFunc(baseEP+d, func(w http.ResponseWriter, r
*http.Request) {
- defer r.Body.Close()
+ defer log.Close(r.Body, "could not close reader")
f(toSession, r.Body)
})
}
@@ -876,7 +862,7 @@ func main() {
if len(watchDir) != 0 {
log.Infoln("Watching directory " + watchDir)
dw, err := startWatching(watchDir, &toSession, dispatcher)
- defer dw.Close()
+ defer log.Close(dw, "could not close dirwatcher")
if err != nil {
log.Errorf("dirwatcher on %s failed: %s", watchDir,
err.Error())
}
@@ -888,7 +874,7 @@ func main() {
panic(err)
}
log.Infoln("Created " + startedFile)
- f.Close()
+ log.Close(f, "could not close file")
var waitforever chan struct{}
<-waitforever
diff --git a/infrastructure/cdn-in-a-box/enroller/run.sh
b/infrastructure/cdn-in-a-box/enroller/run.sh
index 50bdb7c..6e8a57b 100755
--- a/infrastructure/cdn-in-a-box/enroller/run.sh
+++ b/infrastructure/cdn-in-a-box/enroller/run.sh
@@ -18,7 +18,9 @@
# under the License.
############################################################
-set -x
+set -o xtrace
+# enable job control
+set -o monitor
. /to-access.sh
set-dns.sh
@@ -62,4 +64,10 @@ fi
# clear out the enroller dir first so no files left from previous run
rm -rf ${ENROLLER_DIR}/*
-/enroller -dir "$ENROLLER_DIR" || tail -f /dev/null
+/enroller -dir "$ENROLLER_DIR" &
+
+source /to-access.sh
+# Enroll with traffic ops
+TO_URL="https://$TO_FQDN:$TO_PORT"
+to-enroll enroller ALL '' 53 || (while true; do echo "enroll failed."; sleep 3
; done)
+fg || tail -f /dev/null
diff --git a/infrastructure/cdn-in-a-box/enroller/server_template.json
b/infrastructure/cdn-in-a-box/enroller/server_template.json
index f4ac961..b51ed04 100644
--- a/infrastructure/cdn-in-a-box/enroller/server_template.json
+++ b/infrastructure/cdn-in-a-box/enroller/server_template.json
@@ -2,14 +2,14 @@
"hostName": "$MY_HOSTNAME",
"domainName": "$MY_DOMAINNAME",
"cachegroup": "$MY_CACHE_GROUP",
- "interfaceName": "eth0",
- "ipAddress": "$MY_IP",
- "ipNetmask": "$MY_NETMASK",
- "ipGateway": "$MY_GATEWAY",
- "ip6Address": "$MY_IP6_ADDRESS",
- "ipIsService": true,
- "ip6Gateway": "$MY_IP6_GATEWAY",
- "interfaceMtu": 1500,
+ "interfaces": [
+ {
+ "ipAddresses": [],
+ "monitor": true,
+ "mtu": 1500,
+ "name": "eth0"
+ }
+ ],
"type": "$MY_TYPE",
"physLocation": "Apachecon North America 2018",
"profile": "$MY_PROFILE",
diff --git a/infrastructure/cdn-in-a-box/traffic_ops/Dockerfile-db
b/infrastructure/cdn-in-a-box/traffic_ops/Dockerfile-db
index 11ec0f5..040a9ed 100644
--- a/infrastructure/cdn-in-a-box/traffic_ops/Dockerfile-db
+++ b/infrastructure/cdn-in-a-box/traffic_ops/Dockerfile-db
@@ -22,8 +22,12 @@
FROM postgres:9.6-alpine
ENV POSTGRES_PASSWORD=$POSTGRES_PASSWORD
-RUN apk add bind-tools
+RUN apk add bind-tools \
+ # to-access dependencies
+ jq gettext
COPY traffic_ops/initdb.d /docker-entrypoint-initdb.d
COPY traffic_ops/run-db.sh /
+COPY traffic_ops/to-access.sh /
+COPY enroller/server_template.json /
RUN chmod -R a+rx /run-db.sh /docker-entrypoint-initdb.d
ENTRYPOINT /run-db.sh
diff --git a/infrastructure/cdn-in-a-box/traffic_ops/run-db.sh
b/infrastructure/cdn-in-a-box/traffic_ops/run-db.sh
index c1693ee..c26b7af 100755
--- a/infrastructure/cdn-in-a-box/traffic_ops/run-db.sh
+++ b/infrastructure/cdn-in-a-box/traffic_ops/run-db.sh
@@ -18,5 +18,11 @@
set-dns.sh
insert-self-into-dns.sh
+set -o monitor #enable job control
+/docker-entrypoint.sh postgres &
-exec /docker-entrypoint.sh postgres
+source /to-access.sh
+# Enroll with traffic ops
+TO_URL="https://$TO_FQDN:$TO_PORT"
+to-enroll db ALL '' 5432 5432 || (while true; do echo "enroll failed."; sleep
3 ; done)
+fg;
diff --git a/infrastructure/cdn-in-a-box/traffic_ops/to-access.sh
b/infrastructure/cdn-in-a-box/traffic_ops/to-access.sh
index 9d0a22c..7a1788f 100755
--- a/infrastructure/cdn-in-a-box/traffic_ops/to-access.sh
+++ b/infrastructure/cdn-in-a-box/traffic_ops/to-access.sh
@@ -203,6 +203,21 @@ to-enroll() {
export MY_IP6_GATEWAY="$(route -n6 | grep UG | awk '{print $2}')"
case "$serverType" in
+ "db" )
+ export MY_TYPE="TRAFFIC_OPS_DB"
+ export MY_PROFILE="TRAFFIC_OPS_DB"
+ export MY_STATUS="ONLINE"
+ ;;
+ "dns" )
+ export MY_TYPE="BIND"
+ export MY_PROFILE="BIND_ALL"
+ export MY_STATUS="ONLINE"
+ ;;
+ "enroller" )
+ export MY_TYPE="ENROLLER"
+ export MY_PROFILE="ENROLLER_ALL"
+ export MY_STATUS="ONLINE"
+ ;;
"edge" )
export MY_TYPE="EDGE"
export MY_PROFILE="ATS_EDGE_TIER_CACHE"
@@ -261,7 +276,19 @@ to-enroll() {
esac
# replace env references in the file
- envsubst < "/server_template.json" >
"${ENROLLER_DIR}/servers/$HOSTNAME.json"
+ <"/server_template.json" envsubst | #first envsubst expands
$MY_TCP_PORT and $MY_HTTPS_PORT so they are valid JSON
+ jq '.cdnName = "$MY_CDN"' |
+ if [[ -n "$MY_IP" && -n "$MY_GATEWAY" ]]; then
+ jq '.interfaces[0].ipAddresses += [({} | .address =
"$MY_IP" | .gateway = "$MY_GATEWAY" | .serviceAddress = true)]'
+ else
+ cat
+ fi |
+ if [[ -n "$MY_IP6_ADDRESS" && -n "$MY_IP6_GATEWAY" ]]; then
+ jq '.interfaces[0].ipAddresses += [({} | .address =
"$MY_IP6_ADDRESS" | .gateway = "$MY_IP6_GATEWAY" | .serviceAddress = true)]'
+ else
+ cat
+ fi |
+ envsubst >"${ENROLLER_DIR}/servers/$HOSTNAME.json"
sleep 3
}
diff --git
a/infrastructure/cdn-in-a-box/traffic_ops_data/servers/010-dns_server.json
b/infrastructure/cdn-in-a-box/traffic_ops_data/servers/010-dns_server.json
deleted file mode 100644
index 4cfb2cb..0000000
--- a/infrastructure/cdn-in-a-box/traffic_ops_data/servers/010-dns_server.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "hostName": "dns",
- "domainName": "infra.ciab.test",
- "cachegroup": "CDN_in_a_Box_Edge",
- "interfaceName": "eth0",
- "interfaceMtu": 1500,
- "ipIsService": true,
- "type": "BIND",
- "physLocation": "Apachecon North America 2018",
- "profile": "BIND_ALL",
- "cdnName": "ALL",
- "updPending": false,
- "status": "ONLINE",
- "tcpPort": 53,
- "httpsPort": 443
-}
diff --git
a/infrastructure/cdn-in-a-box/traffic_ops_data/servers/020-db_server.json
b/infrastructure/cdn-in-a-box/traffic_ops_data/servers/020-db_server.json
deleted file mode 100644
index 28f2a86..0000000
--- a/infrastructure/cdn-in-a-box/traffic_ops_data/servers/020-db_server.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "hostName": "db",
- "domainName": "infra.ciab.test",
- "cachegroup": "CDN_in_a_Box_Edge",
- "interfaceName": "eth0",
- "interfaceMtu": 1500,
- "ipIsService": true,
- "type": "TRAFFIC_OPS_DB",
- "physLocation": "Apachecon North America 2018",
- "profile": "TRAFFIC_OPS_DB",
- "cdnName": "ALL",
- "updPending": false,
- "status": "ONLINE",
- "tcpPort": 5432,
- "httpsPort": 5432
-}
-
diff --git
a/infrastructure/cdn-in-a-box/traffic_ops_data/servers/030-enroller_server.json
b/infrastructure/cdn-in-a-box/traffic_ops_data/servers/030-enroller_server.json
deleted file mode 100644
index b41f6c9..0000000
---
a/infrastructure/cdn-in-a-box/traffic_ops_data/servers/030-enroller_server.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "hostName": "enroller",
- "domainName": "infra.ciab.test",
- "cachegroup": "CDN_in_a_Box_Edge",
- "interfaceName": "eth0",
- "interfaceMtu": 1500,
- "ipIsService": true,
- "type": "ENROLLER",
- "physLocation": "Apachecon North America 2018",
- "profile": "ENROLLER_ALL",
- "cdnName": "ALL",
- "updPending": false,
- "status": "ONLINE",
- "tcpPort": 53,
- "httpsPort": 443
-}
diff --git a/infrastructure/cdn-in-a-box/traffic_router/Dockerfile
b/infrastructure/cdn-in-a-box/traffic_router/Dockerfile
index 6741764..6350b92 100644
--- a/infrastructure/cdn-in-a-box/traffic_router/Dockerfile
+++ b/infrastructure/cdn-in-a-box/traffic_router/Dockerfile
@@ -27,7 +27,7 @@ ARG TRAFFIC_ROUTER_RPM=traffic_router/traffic_router.rpm
ARG TOMCAT_RPM=traffic_router/tomcat.rpm
RUN yum -y install epel-release && \
- yum -y install git rpm-build net-tools iproute nc wget tar unzip \
+ yum -y install jq git rpm-build net-tools iproute nc wget tar unzip \
perl-JSON perl-WWWCurl which make autoconf automake gcc gcc-c++ apr
apr-devel \
openssl openssl-devel bind-utils net-tools perl-JSON-PP gettext \
java-1.8.0-openjdk-headless java-1.8.0-openjdk-devel tomcat-native
&& \
diff --git a/infrastructure/cdn-in-a-box/traffic_stats/Dockerfile-influxdb
b/infrastructure/cdn-in-a-box/traffic_stats/Dockerfile-influxdb
index 17cc208..b3fef58 100644
--- a/infrastructure/cdn-in-a-box/traffic_stats/Dockerfile-influxdb
+++ b/infrastructure/cdn-in-a-box/traffic_stats/Dockerfile-influxdb
@@ -18,7 +18,13 @@
FROM influxdb:1.7.3
RUN apt-get update && \
- apt-get install -y dnsutils net-tools gettext-base netcat && \
+ apt-get install -y \
+ dnsutils \
+ gettext-base \
+ # server enrollment uses jq
+ jq \
+ netcat \
+ net-tools && \
rm -rf /var/lib/apt/lists/*
ADD enroller/server_template.json \