docker-config for local environment
[so/docker-config.git] / create-configs.sh
1 #!/bin/bash
2
3 # -----------------------------------------------------------------------------
4 # Copyright © 2018 AT&T USA
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 #       http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17 #
18 # -----------------------------------------------------------------------------
19
20 #
21 # This script generates docker-compose volume overlay files from the oom
22 # kubernetes configuration files.  Multiple environments can be supported. By
23 # default the environment is the 'local' environment and the docker-compose 
24 # file is named docker-compose-local.yml.  This script only generates the
25 # overlay files, not the docker-compose file.
26 #
27 # Overlay files contain the springboot configuration for each SO container.
28 #
29 # The idea here is that docker-compose is lighter weight than kubernetes with
30 # rancher, and people will find it easier to use in a development environment.
31 # Whenever the overlay files in oom are modified, this script can be used to
32 # (re)build overlay files for the docker-compose environment.
33 #
34 # Reasonably up-to-date overlay files for the docker-compose environment may
35 # be checked into the docker-config repository as a convenience to those who
36 # don't want to (or can't) run this script.  This script will refresh those.
37 #
38 # Example Workflow:
39 #
40 # 1) build SO software and docker images -or- pull SO docker images from nexus3
41 #
42 # 2) Create configuration for 'local' environment:
43 #       ./create-configs.sh -e local -i -u rd472p -b master
44 #
45 # 3) Start the environment:
46 #       docker-compose -f docker-compose-local.yml up
47 #
48
49 function usage
50 {
51         echo "usage: $(basename $0) [-e env] [-i] [-u oom-git-user] [-b oom-git-branch]"
52         echo "where  -e specifies the environment name (default: 'local')"
53         echo "       -i re-initializes the staging directory (default: no)"
54         echo "       -u specifies the git user for cloning oom (default: current user)"
55         echo "       -b specifies the oom branch (default: current docker-config branch)"
56 }
57
58 #
59 # Purpose: prompts for a yes/no (or equivalent) answer
60 # Output: none
61 # Return Code: 0 for yes, 1 for no
62 # Usage: askConfirm prompt
63 #
64 function askConfirm
65 {
66         ask_prompt="$1"
67         while :
68         do
69                 echo -n "$ask_prompt" >> /dev/tty
70                 read ask_reply
71
72                 ask_reply=${ask_reply##+([[:space:]])};
73                 ask_reply=${ask_reply%%+([[:space:]])}
74
75                 case "$ask_reply" in
76                 y | yes | Y | YES)
77                         return 0
78                         ;;
79                 n | no | N | NO)
80                         return 1
81                         ;;
82                 esac
83         done
84 }
85
86 #
87 # Purpose: performs a literal string replacement (no regexs)
88 # Output: none, but modifies the specified file
89 # Return Code: 0 for success, 1 for failure
90 # Usage: replace source replacement file
91 #
92 function replace
93 {
94         local src=$1
95         local rpl=$2
96         local file=$3
97
98         if ! type xxd > /dev/null 2>&1
99         then
100                 echo "xxd: command not found" 1>&2
101                 return 1
102         fi
103
104         local xsrc=$(echo -n "$src" | xxd -p | tr -d '\n')
105         local xrpl=$(echo -n "$rpl" | xxd -p | tr -d '\n')
106
107         xxd -p $file | tr -d '\n' | sed "s/$xsrc/$xrpl/g" | xxd -p -r > $file.replace || return 1
108         mv $file.replace $file || return 1
109         return 0
110 }
111
112 #
113 # Purpose: converts a camel-case variable name to snake (upper) case
114 #          openStackUserName -> OPEN_STACK_USER_NAME
115 # Output: the converted variable name
116 # Usage: toSnake name
117 #
118 function toSnake
119 {
120         echo "$1" | sed -r 's/([A-Z])/_\L\1/g' | sed 's/^_//' | tr '[a-z]' '[A-Z]'
121 }
122
123 #
124 # Purpose: lists all the environment variables in the specified docker
125 #          compose yaml for the specified container, e.g.:
126 #          VAR1=VALUE1
127 #          VAR2=VALUE2
128 #          ...
129 # Output: the list of variable mappings
130 # Usage: listEnv composeFile containerName
131 #
132 function listEnv
133 {
134         local composeFile=$1
135         local container=$2
136
137         local inContainer=FALSE
138         local inEnvironment=FALSE
139
140         while read line
141         do
142                 if [[ $line == "$container:" ]]
143                 then
144                         inContainer=TRUE
145                 elif [[ $inContainer == TRUE && $line == "environment:" ]]
146                 then
147                         inEnvironment=TRUE
148                 else
149                         if [[ $inEnvironment == TRUE ]]
150                         then
151                                 if [[ ${line:0:1} == "-" ]]
152                                 then
153                                         echo "$line"
154                                 else
155                                         break
156                                 fi
157                         fi
158                 fi
159         done < $composeFile | sed -e "s/^- '//" -e "s/'$//"
160 }
161
162 #
163 # Purpose: tests if the specified environment variable is defined in the
164 #          docker compose yaml for the specified container.
165 # Output: none
166 # Return Code: 0 if the variable exists, 1 if it doesn't
167 # Usage: varExists composeFile containerName variableName
168 #
169 function varExists
170 {
171         local composeFile=$1
172         local container=$2
173         local var=$3
174
175         listEnv $composeFile $container | grep "^$var=" > /dev/null
176         return $?
177 }
178
179 #
180 # Purpose: returns the value of the specified environment variable
181 #          from the compose yaml for the specified container.
182 # Output: the variable value (empty if it isn't defined)
183 # Usage: varValue composeFile containerName variableName
184 #
185 function varValue
186 {
187         local composeFile=$1
188         local container=$2
189         local var=$3
190
191         listEnv $composeFile $container | grep "^$var=" | cut -d= -f2
192 }
193
194 ### MAIN CODE STARTS HERE
195
196 if [[ ! -d volumes/so ]]
197 then
198         echo "You must run this command in the docker-config directory" 1>&2
199         exit 1
200 fi
201
202 ENV=local
203 INIT=FALSE
204 GITUSER=$(id -un)
205 GITBRANCH=$(git rev-parse --abbrev-ref HEAD)
206
207 while getopts :e:iu:b: c
208 do
209         case "$c" in
210         e)
211                 ENV=$OPTARG
212                 ;;
213         i)
214                 INIT=TRUE
215                 ;;
216         u)
217                 GITUSER=$OPTARG
218                 ;;
219         b)
220                 GITBRANCH=$OPTARG
221                 ;;
222         *)
223                 usage 1>&2
224                 exit 1
225                 ;;
226         esac
227 done
228
229 shift $(($OPTIND - 1))
230
231 if [[ $# != 0 ]]
232 then
233         usage 1>&2
234         exit 1
235 fi
236
237 if [[ ! -f docker-compose-$ENV.yml ]]
238 then
239         echo "No such file: docker-compose-$ENV.yml" 1>&2
240         exit 1
241 fi
242
243 mkdir -p staging
244
245 if [[ -d staging && $INIT == TRUE ]]
246 then
247         if ! askConfirm "Delete existing staging directory? "
248         then
249                 exit 1
250         fi
251
252         rm -fr staging || exit 1
253 fi
254
255 mkdir -p staging || exit 1
256
257 # Get the oom repository from gerrit.
258
259 if [[ -d staging/oom ]]
260 then
261         cd staging/oom || exit 1
262
263         branch=$(git rev-parse --abbrev-ref HEAD)
264
265         if [[ $branch != $GITBRANCH ]]
266         then
267                 echo "staging/oom is not on branch '$GITBRANCH'" 1>&2
268                 exit 1
269         fi
270
271         cd ../.. || exit 1
272 else
273         cd staging || exit 1
274         echo "Cloning into staging/oom"
275         git clone https://$GITUSER@gerrit.onap.org/r/a/oom || exit 1
276         cd oom || exit 1
277
278         branch=$(git rev-parse --abbrev-ref HEAD)
279
280         if [[ $branch != $GITBRANCH ]]
281         then
282                 if [[ $(git ls-remote origin $GITBRANCH | wc -l) == 0 ]]
283                 then
284                         echo "staging/oom has no '$GITBRANCH' branch" 1>&2
285                         exit 1
286                 fi
287
288                 echo "Switching staging/oom to '$GITBRANCH' branch"
289                 git checkout -b $GITBRANCH remotes/origin/$GITBRANCH || exit 1
290         fi
291
292         cd ../.. || exit 1
293 fi
294
295 kso=staging/oom/kubernetes/so
296
297 if [[ ! -d $kso ]]
298 then
299         echo "No such directory: $kso" 1>&2
300         exit 1
301 fi
302
303 if [[ ! -d staging/volumes/so/config ]]
304 then
305         mkdir -p staging/volumes/so/config
306 fi
307
308 stagedTargets=
309
310 for override in $(cd $kso && find * -name 'override.yaml')
311 do
312         subdir=${override%%/*}
313
314         if [[ $subdir == resources ]]
315         then
316                 container=so-api-handler-infra
317         elif [[ $subdir == charts ]]
318         then
319                 container=${override#charts/}
320                 container=${container%%/*}
321         fi
322
323         if [[ $container != so-monitoring ]]
324         then
325                 container=${container#so-}
326         fi
327
328         target=staging/volumes/so/config/$container/$ENV/override.yaml
329
330         if [[ ! -f $target ]]
331         then
332                 mkdir -p $(dirname $target) || exit 1
333                 cp $kso/$override $target.tmp || exit 1
334
335                 if ! varExists docker-compose-$ENV.yml $container COMMON_NAMESPACE
336                 then
337                         echo "ERROR: no COMMON_NAMESPACE variable is defined in docker-compose-$ENV.yml for container $container" 1>&2
338                         exit 1
339                 fi
340
341                 replace '{{ include "common.namespace" . }}' '${COMMON_NAMESPACE}' $target.tmp || exit 1
342
343                 if ! varExists docker-compose-$ENV.yml $container CONTAINER_PORT
344                 then
345                         echo "ERROR: no CONTAINER_PORT variable is defined in docker-compose-$ENV.yml for container $container" 1>&2
346                         exit 1
347                 fi
348
349                 replace '{{ index .Values.containerPort }}' '${CONTAINER_PORT}' $target.tmp || exit 1
350
351                 for configValue in $(grep "{{ \.Values\.config\..*}}" $target.tmp | cut -d'{' -f3 | cut -d'}' -f1 | tr -d ' ' | sed 's/.Values.config.//' | sort -u)
352                 do
353                         var=$(toSnake $configValue)
354
355                         if ! varExists docker-compose-$ENV.yml $container $var
356                         then
357                                 echo "ERROR: no $var variable is defined in docker-compose-$ENV.yml for container $container" 1>&2
358                                 exit 1
359                         fi
360
361                         if [[ ${var:0:11} == "OPEN_STACK_" ]]
362                         then
363                                 # Workaround for variables in the openstack
364                                 # adapter cloud configuration. The override
365                                 # yaml is read by a migration program that
366                                 # doesn't expand variables, so we need to
367                                 # substitute the variable values here.
368
369                                 value=$(varValue docker-compose-$ENV.yml $container $var)
370                                 replace "{{ .Values.config.$configValue }}" "$value" $target.tmp || exit 1
371                         else
372                                 replace "{{ .Values.config.$configValue }}" '${'$var'}' $target.tmp || exit 1
373                         fi
374                 done
375
376                 if grep "{{" $target.tmp > /dev/null
377                 then
378                         echo "ERROR: Unresolved placeholders in $kso/$override:" 1>&2
379                         grep "{{.*}}" $target.tmp | cut -d'{' -f3 | cut -d'}' -f1 | sed -e 's/^/    {{/' -e 's/$/}}/' | sort -u 1>&2
380                         exit 1
381                 fi
382
383                 mv $target.tmp $target || exit 1
384                 echo "Created $target"
385
386                 stagedTargets="$stagedTargets target"
387         fi
388 done
389
390 for override in $(cd staging && find volumes -name 'override.yaml')
391 do
392         dir=$(dirname $override)
393         mkdir -p $dir || exit 1
394         cp staging/$override $override || exit 1
395         echo "Installed $override" 
396 done
397
398 echo "SUCCESS"