1function supported_osarch {
2   # Arguments:
3   #   $1 - osarch - example, linux/amd64
4   #
5   # Returns:
6   #   0 - supported
7   #   * - not supported
8   local osarch="$1"
9   for valid in $(go tool dist list)
10   do
11      if test "${osarch}" = "${valid}"
12      then
13         return 0
14      fi
15   done
16   return 1
17}
18
19function refresh_docker_images {
20   # Arguments:
21   #   $1 - Path to top level Consul source
22   #   $2 - Which make target to invoke (optional)
23   #
24   # Return:
25   #   0 - success
26   #   * - failure
27
28   if ! test -d "$1"
29   then
30      err "ERROR: '$1' is not a directory. refresh_docker_images must be called with the path to the top level source as the first argument'"
31      return 1
32   fi
33
34   local sdir="$1"
35   local targets="$2"
36
37   test -n "${targets}" || targets="docker-images"
38
39   make -C "${sdir}" ${targets}
40   return $?
41}
42
43function build_ui {
44   # Arguments:
45   #   $1 - Path to the top level Consul source
46   #   $2 - The docker image to run the build within (optional)
47   #   $3 - Version override
48   #
49   # Returns:
50   #   0 - success
51   #   * - error
52   #
53   # Notes:
54   #   Use the GIT_COMMIT environment variable to pass off to the build
55   #   Use the GIT_COMMIT_YEAR environment variable to pass off to the build
56
57   if ! test -d "$1"
58   then
59      err "ERROR: '$1' is not a directory. build_ui must be called with the path to the top level source as the first argument'"
60      return 1
61   fi
62
63   local image_name=${UI_BUILD_CONTAINER_DEFAULT}
64   if test -n "$2"
65   then
66      image_name="$2"
67   fi
68
69   local sdir="$1"
70   local ui_dir="${1}/ui"
71
72   # parse the version
73   version=$(parse_version "${sdir}")
74
75   if test -n "$3"
76   then
77      version="$3"
78   fi
79
80   local commit_hash="${GIT_COMMIT}"
81   if test -z "${commit_hash}"
82   then
83      commit_hash=$(git rev-parse --short HEAD)
84   fi
85
86   local commit_year="${GIT_COMMIT_YEAR}"
87   if test -z "${commit_year}"
88   then
89      commit_year=$(git show -s --format=%cd --date=format:%Y HEAD)
90   fi
91
92   local logo_type="${CONSUL_BINARY_TYPE}"
93   if test "$logo_type" != "oss"
94   then
95     logo_type="enterprise"
96   fi
97
98   # make sure we run within the ui dir
99   pushd ${ui_dir} > /dev/null
100
101   status "Creating the UI Build Container with image: ${image_name} and version '${version}'"
102   local container_id=$(docker create -it -e "CONSUL_GIT_SHA=${commit_hash}" -e "CONSUL_COPYRIGHT_YEAR=${commit_year}" -e "CONSUL_VERSION=${version}" -e "CONSUL_BINARY_TYPE=${CONSUL_BINARY_TYPE}" ${image_name})
103   local ret=$?
104   if test $ret -eq 0
105   then
106      status "Copying the source from '${ui_dir}' to /consul-src within the container"
107      (
108         tar -c $(ls -A | grep -v "^(node_modules\|dist\|tmp)") | docker cp - ${container_id}:/consul-src &&
109         status "Running build in container" && docker start -i ${container_id} &&
110         rm -rf ${1}/ui/dist &&
111         status "Copying back artifacts" && docker cp ${container_id}:/consul-src/packages/consul-ui/dist ${1}/ui/dist
112      )
113      ret=$?
114      docker rm ${container_id} > /dev/null
115   fi
116
117   # Check the version is baked in correctly
118   if test ${ret} -eq 0
119   then
120      local ui_vers=$(ui_version "${1}/ui/dist/index.html")
121      if test "${version}" != "${ui_vers}"
122      then
123         err "ERROR: UI version mismatch. Expecting: '${version}' found '${ui_vers}'"
124         ret=1
125      fi
126   fi
127
128   # Check the logo is baked in correctly
129   if test ${ret} -eq 0
130   then
131     local ui_logo_type=$(ui_logo_type "${1}/ui/dist/index.html")
132     if test "${logo_type}" != "${ui_logo_type}"
133     then
134       err "ERROR: UI logo type mismatch. Expecting: '${logo_type}' found '${ui_logo_type}'"
135       ret=1
136     fi
137   fi
138
139   # Copy UI over ready to be packaged into the binary
140   if test ${ret} -eq 0
141   then
142      rm -rf ${1}/pkg/web_ui
143      mkdir -p ${1}/pkg
144      cp -r ${1}/ui/dist ${1}/pkg/web_ui
145   fi
146
147   popd > /dev/null
148   return $ret
149}
150
151function build_assetfs {
152   # Arguments:
153   #   $1 - Path to the top level Consul source
154   #   $2 - The docker image to run the build within (optional)
155   #
156   # Returns:
157   #   0 - success
158   #   * - error
159   #
160   # Note:
161   #   The GIT_COMMIT and GIT_DIRTY environment variables will be used if present
162
163   if ! test -d "$1"
164   then
165      err "ERROR: '$1' is not a directory. build_assetfs must be called with the path to the top level source as the first argument'"
166      return 1
167   fi
168
169   local sdir="$1"
170   local image_name=${GO_BUILD_CONTAINER_DEFAULT}
171   if test -n "$2"
172   then
173      image_name="$2"
174   fi
175
176   pushd ${sdir} > /dev/null
177   status "Creating the Go Build Container with image: ${image_name}"
178   local container_id=$(docker create -it -e GIT_COMMIT=${GIT_COMMIT} -e GIT_DIRTY=${GIT_DIRTY} ${image_name} make static-assets ASSETFS_PATH=bindata_assetfs.go)
179   local ret=$?
180   if test $ret -eq 0
181   then
182      status "Copying the sources from '${sdir}/(pkg/web_ui|GNUmakefile)' to /consul/pkg"
183      (
184         tar -c pkg/web_ui GNUmakefile | docker cp - ${container_id}:/consul &&
185         status "Running build in container" && docker start -i ${container_id} &&
186         status "Copying back artifacts" && docker cp ${container_id}:/consul/bindata_assetfs.go ${sdir}/agent/uiserver/bindata_assetfs.go
187      )
188      ret=$?
189      docker rm ${container_id} > /dev/null
190   fi
191   popd >/dev/null
192   return $ret
193}
194
195function build_consul_post {
196   # Arguments
197   #   $1 - Path to the top level Consul source
198   #   $2 - Subdirectory under pkg/bin (Optional)
199   #
200   # Returns:
201   #   0 - success
202   #   * - error
203   #
204   # Notes:
205   #   pkg/bin is where to place binary packages
206   #   pkg.bin.new is where the just built binaries are located
207   #   bin is where to place the local systems versions
208
209   if ! test -d "$1"
210   then
211      err "ERROR: '$1' is not a directory. build_consul_post must be called with the path to the top level source as the first argument'"
212      return 1
213   fi
214
215   local sdir="$1"
216
217   local extra_dir_name="$2"
218   local extra_dir=""
219
220   if test -n "${extra_dir_name}"
221   then
222      extra_dir="${extra_dir_name}/"
223   fi
224
225   pushd "${sdir}" > /dev/null
226
227   # recreate the pkg dir
228   rm -r pkg/bin/${extra_dir}* 2> /dev/null
229   mkdir -p pkg/bin/${extra_dir} 2> /dev/null
230
231   # move all files in pkg.new into pkg
232   cp -r pkg.bin.new/${extra_dir}* pkg/bin/${extra_dir}
233   rm -r pkg.bin.new
234
235   DEV_PLATFORM="./pkg/bin/${extra_dir}$(go env GOOS)_$(go env GOARCH)"
236   for F in $(find ${DEV_PLATFORM} -mindepth 1 -maxdepth 1 -type f 2>/dev/null)
237   do
238      # recreate the bin dir
239      rm -r bin/* 2> /dev/null
240      mkdir -p bin 2> /dev/null
241
242      cp ${F} bin/
243      cp ${F} ${MAIN_GOPATH}/bin
244   done
245
246   popd > /dev/null
247
248   return 0
249}
250
251function build_consul {
252   # Arguments:
253   #   $1 - Path to the top level Consul source
254   #   $2 - Subdirectory to put binaries in under pkg/bin (optional - must specify if needing to specify the docker image)
255   #   $3 - The docker image to run the build within (optional)
256   #
257   # Returns:
258   #   0 - success
259   #   * - error
260   #
261   # Note:
262   #   The GOLDFLAGS and GOTAGS environment variables will be used if set
263   #   If the CONSUL_DEV environment var is truthy only the local platform/architecture is built.
264   #   If the XC_OS or the XC_ARCH environment vars are present then only those platforms/architectures
265   #   will be built. Otherwise all supported platform/architectures are built
266
267   if ! test -d "$1"
268   then
269      err "ERROR: '$1' is not a directory. build_consul must be called with the path to the top level source as the first argument'"
270      return 1
271   fi
272
273   local sdir="$1"
274   local extra_dir_name="$2"
275   local extra_dir=""
276   local image_name=${GO_BUILD_CONTAINER_DEFAULT}
277   if test -n "$3"
278   then
279      image_name="$3"
280   fi
281
282   pushd ${sdir} > /dev/null
283   if is_set "${CONSUL_DEV}"
284   then
285      if test -z "${XC_OS}"
286      then
287         XC_OS=$(go env GOOS)
288      fi
289
290      if test -z "${XC_ARCH}"
291      then
292         XC_ARCH=$(go env GOARCH)
293      fi
294   fi
295   XC_OS=${XC_OS:-"solaris darwin freebsd linux windows"}
296   XC_ARCH=${XC_ARCH:-"386 amd64 arm arm64"}
297
298   if test -n "${extra_dir_name}"
299   then
300      extra_dir="${extra_dir_name}/"
301   fi
302
303   # figure out if the compiler supports modules
304   local use_modules=0
305   if go help modules >/dev/null 2>&1
306   then
307      use_modules=1
308   elif test -n "${GO111MODULE}"
309   then
310      use_modules=1
311   fi
312
313   local volume_mount=
314   if is_set "${use_modules}"
315   then
316      status "Ensuring Go modules are up to date"
317      # ensure our go module cache is correct
318      go_mod_assert || return 1
319      # setup to bind mount our hosts module cache into the container
320      volume_mount="--mount=type=bind,source=${MAIN_GOPATH}/pkg/mod,target=/go/pkg/mod"
321   fi
322
323   status "Creating the Go Build Container with image: ${image_name}"
324   local container_id=$(docker create -it \
325      ${volume_mount} \
326      -e CGO_ENABLED=0 \
327      -e GOLDFLAGS="${GOLDFLAGS}" \
328      -e GOTAGS="${GOTAGS}" \
329      ${image_name} \
330      ./build-support/scripts/build-local.sh -o "${XC_OS}" -a "${XC_ARCH}")
331   ret=$?
332
333   if test $ret -eq 0
334   then
335      status "Copying the source from '${sdir}' to /consul"
336      (
337         tar -c $(ls | grep -v "^(ui\|website\|bin\|pkg\|.git)") | docker cp - ${container_id}:/consul &&
338         status "Running build in container" &&
339         docker start -i ${container_id} &&
340         status "Copying back artifacts" &&
341         docker cp ${container_id}:/consul/pkg/bin pkg.bin.new
342      )
343      ret=$?
344      docker rm ${container_id} > /dev/null
345
346      if test $ret -eq 0
347      then
348         build_consul_post "${sdir}" "${extra_dir_name}"
349         ret=$?
350      else
351         rm -r pkg.bin.new 2> /dev/null
352      fi
353   fi
354   popd > /dev/null
355   return $ret
356}
357
358function build_consul_local {
359   # Arguments:
360   #   $1 - Path to the top level Consul source
361   #   $2 - Space separated string of OSes to build. If empty will use env vars for determination.
362   #   $3 - Space separated string of architectures to build. If empty will use env vars for determination.
363   #   $4 - Subdirectory to put binaries in under pkg/bin (optional)
364   #
365   # Returns:
366   #   0 - success
367   #   * - error
368   #
369   # Note:
370   #   The GOLDFLAGS and GOTAGS environment variables will be used if set
371   #   If the CONSUL_DEV environment var is truthy only the local platform/architecture is built.
372   #   If the XC_OS or the XC_ARCH environment vars are present then only those platforms/architectures
373   #   will be built. Otherwise all supported platform/architectures are built
374   #   The GOXPARALLEL environment variable is used if set
375
376   if ! test -d "$1"
377   then
378      err "ERROR: '$1' is not a directory. build_consul must be called with the path to the top level source as the first argument'"
379      return 1
380   fi
381
382   local sdir="$1"
383   local build_os="$2"
384   local build_arch="$3"
385   local extra_dir_name="$4"
386   local extra_dir=""
387
388   if test -n "${extra_dir_name}"
389   then
390      extra_dir="${extra_dir_name}/"
391   fi
392
393   pushd ${sdir} > /dev/null
394   if is_set "${CONSUL_DEV}"
395   then
396      if test -z "${XC_OS}"
397      then
398         XC_OS=$(go env GOOS)
399      fi
400
401      if test -z "${XC_ARCH}"
402      then
403         XC_ARCH=$(go env GOARCH)
404      fi
405   fi
406   XC_OS=${XC_OS:-"solaris darwin freebsd linux windows"}
407   XC_ARCH=${XC_ARCH:-"386 amd64 arm arm64"}
408
409   if test -z "${build_os}"
410   then
411      build_os="${XC_OS}"
412   fi
413
414   if test -z "${build_arch}"
415   then
416      build_arch="${XC_ARCH}"
417   fi
418
419   status_stage "==> Building Consul - OSes: ${build_os}, Architectures: ${build_arch}"
420   mkdir pkg.bin.new 2> /dev/null
421
422   status "Building sequentially with go install"
423   for os in ${build_os}
424   do
425      for arch in ${build_arch}
426      do
427         outdir="pkg.bin.new/${extra_dir}${os}_${arch}"
428         osarch="${os}/${arch}"
429
430         if ! supported_osarch "${osarch}"
431         then
432            continue
433         fi
434         echo "--->   ${osarch}"
435
436         mkdir -p "${outdir}"
437         GOBIN_EXTRA=""
438         if test "${os}" != "$(go env GOHOSTOS)" -o "${arch}" != "$(go env GOHOSTARCH)"
439         then
440            GOBIN_EXTRA="${os}_${arch}/"
441         fi
442         binname="consul"
443         if [ $os == "windows" ];then
444               binname="consul.exe"
445         fi
446         debug_run env CGO_ENABLED=0 GOOS=${os} GOARCH=${arch} go install -ldflags "${GOLDFLAGS}" -tags "${GOTAGS}" && cp "${MAIN_GOPATH}/bin/${GOBIN_EXTRA}${binname}" "${outdir}/${binname}"
447         if test $? -ne 0
448         then
449            err "ERROR: Failed to build Consul for ${osarch}"
450            rm -r pkg.bin.new
451            return 1
452         fi
453      done
454   done
455
456   build_consul_post "${sdir}" "${extra_dir_name}"
457   if test $? -ne 0
458   then
459      err "ERROR: Failed postprocessing Consul binaries"
460      return 1
461   fi
462   return 0
463}
464