nginx-conf-flatten 8.49 KB
Newer Older
Michał Woźniak's avatar
Michał Woźniak committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash

#
# flatten an nginx config
# 
# replaces all occurences of the `include` directive
# with the relevant included files
#
# assumptions:
# - include file paths do not contain spaces
#   (this seems compatible with how nginx handles includes)
# - no files are included from above the directory where the nginx config file
#   being processed resides in

Michał Woźniak's avatar
bugfix    
Michał Woźniak committed
15
# set debug verbosity to none
Michał Woźniak's avatar
Michał Woźniak committed
16
[ -z "${NGINX_CONF_FLATTEN_DEBUG+x}" ] && NGINX_CONF_FLATTEN_DEBUG=""
Michał Woźniak's avatar
bugfix    
Michał Woźniak committed
17
18


Michał Woźniak's avatar
Michał Woźniak committed
19
function debug {
Michał Woźniak's avatar
Michał Woźniak committed
20
    if [ "$NGINX_CONF_FLATTEN_DEBUG" != "" ]; then
Michał Woźniak's avatar
bugfix    
Michał Woźniak committed
21
22
        echo "$@" >&2
    fi
Michał Woźniak's avatar
Michał Woźniak committed
23
24
}

Michał Woźniak's avatar
Michał Woźniak committed
25
26

#
27
28
29
30
31
32
33
34
35
36
37
38
39
# get the path relative to a different base path
# if the former is relative
# 
# $1 - path
# $2 - base path
function get-path-relative-to {
    # if the path is absolute, that's all we need
    if [[ "$1" = /* ]]; then
        echo "$1"
    else
        echo "$2/$1"
    fi
}
Michał Woźniak's avatar
Michał Woźniak committed
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64


# 
# extract the include paths from an nginx config file
# 
# $1 - the file to work with
function nginx-extract-includes {
    # assumption: include path does not contain spaces
    egrep '^\s*include ' "$1" | sed -r -e "s/^\s*include\s*([^;]+).*$/\1/"
}

# 
# handle all includes of a file to the temporary workdir
# creating the directory structure in the process
# and adding them to the NGINX_CONFIG_FILES variable
# for further processing
# 
# $1 - the file to work with
function nginx-handle-includes {
    # get the included files
    # this will loop through actual globbed filenames
    # 
    # assumption: no files are included from directories above the directory
    # the main config file resides in
    for i in $( nginx-extract-includes "$1" ); do
Michał Woźniak's avatar
Michał Woźniak committed
65
        debug -n "        +-- found include: $i"
Michał Woźniak's avatar
Michał Woźniak committed
66
67
        # if the temporary copy of the file does not exist
        if [ ! -e "$TMP_WORKDIR/$i" ]; then
Michał Woźniak's avatar
Michał Woźniak committed
68
            debug " - copying!"
Michał Woźniak's avatar
Michał Woźniak committed
69
70
71
72
73
74
75
            # create the directory structure
            mkdir -p "$TMP_WORKDIR/$( dirname "$i" )"
            # copy the file
            cp "$NGINX_CONFIG_DIR/$i" "$TMP_WORKDIR/$i"
            # add to the list of files to proces
            NGINX_CONFIG_ELEMENTS+=("$i")
        else
Michał Woźniak's avatar
Michał Woźniak committed
76
            debug " - already copied."
Michał Woźniak's avatar
Michał Woźniak committed
77
78
79
80
81
        fi
    done
}


82
83
84
85
86
87
88
#
# generate a cleaned nginx config directory,
# containing only the files that are included from the config file
# passed as the argument, or files included from those, etc
# 
# $NGINX_CONFIG - nginx config file to work off of
# $OUTPUT_DEST  - target directory (must *not* exist)
Michał Woźniak's avatar
Michał Woźniak committed
89
90
91
92
93
94
95
96
97
98
function nginx-clean-directory {
    # let's get the file in a temporary location
    # (we don't want to screw up the original!)
    cp "$NGINX_CONFIG" "$TMP_WORKDIR"/

    # go one by one over the NGINX_CONFIG_ELEMENTS array
    # but from the end
    while [[ ${#NGINX_CONFIG_ELEMENTS[@]} > 0 ]]; do
        CUR_INDEX=$(( ${#NGINX_CONFIG_ELEMENTS[@]} - 1 ))
        CUR_ELEMENT=${NGINX_CONFIG_ELEMENTS[$CUR_INDEX]}
Michał Woźniak's avatar
Michał Woźniak committed
99
100
        debug "    +-- working with index: $CUR_INDEX"
        debug "        $CUR_ELEMENT"
Michał Woźniak's avatar
Michał Woźniak committed
101
102
103
        unset "NGINX_CONFIG_ELEMENTS[$CUR_INDEX]"
        nginx-handle-includes "$CUR_ELEMENT";
    done
104
105
106
107
108
109
    
    # move the temporary work dir to the output location
    debug "+-- moving the temporary output directory to the final output location"
    debug "    src: $TMP_WORKDIR"
    debug "    dst: $OUTPUT_DEST"
    mv "$TMP_WORKDIR" "$OUTPUT_DEST"
Michał Woźniak's avatar
Michał Woźniak committed
110
111
112
}


113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#
# flatten an nginx config file,
# including all includes inline, recursively
# 
# $NGINX_CONFIG - nginx config file to work off of
# $OUTPUT_DEST  - where to output the assembled, flattened file (must not exist)
function nginx-flatten {
    
    # let's get the file in a temporary location
    # (we don't want to screw up the original!)
    cp "$NGINX_CONFIG" "$TMP_WORKDIR"/
    NGINX_CONFIG_COPY="$TMP_WORKDIR/$( basename "$NGINX_CONFIG")"

    OLDIFS="$IFS"
    IFS=$'\n'
    while CURRENT_INCLUDES="$( egrep -n '^\s*include\s*.+;.*$' "$NGINX_CONFIG_COPY" )"; do
        PREVIOUS_CI_LINE=0;
        for ci in $CURRENT_INCLUDES; do
            # get the actual file glob
            CI_FILE_GLOB="$( echo "$ci" | sed -r -e "s/^[0-9]+\:\s*include\s*([^;]+).*$/\1/" )"
            CI_LINE="$( echo "$ci" | cut -d ':' -f 1 )"
134
            CI_INDENT="$( echo "$ci" | cut -d ':' -f 2 | sed -r -e 's/^(\s*).*$/\1/' )"
135
            DATE_EXT="$( date +%s%N )"
136
137
138
            debug "    +-- previous line : $PREVIOUS_CI_LINE"
            debug "        ci line       : $CI_LINE"
            debug "        indent        : '$CI_INDENT'"
Michał Woźniak's avatar
bugfix    
Michał Woźniak committed
139
            debug "        glob          : '$CI_FILE_GLOB'"
140
            tail -n "+$(( $PREVIOUS_CI_LINE + 1 ))" "$NGINX_CONFIG_COPY" | head -n $(( $CI_LINE - 1 - $PREVIOUS_CI_LINE )) > "$NGINX_CONFIG_COPY.$DATE_EXT"
141
142
143
            echo -e "\n$CI_INDENT############################################################\n$CI_INDENT### $CI_FILE_GLOB \n$CI_INDENT############################################################" >> "$NGINX_CONFIG_COPY.$DATE_EXT"
            sed -r -e "s/^/$CI_INDENT/" $CI_FILE_GLOB >> "$NGINX_CONFIG_COPY.$DATE_EXT"
            echo -e "\n$CI_INDENT############################################################\n$CI_INDENT### end $CI_FILE_GLOB \n$CI_INDENT############################################################" >> "$NGINX_CONFIG_COPY.$DATE_EXT"
144
145
            PREVIOUS_CI_LINE="$CI_LINE"
        done
Michał Woźniak's avatar
bugfix    
Michał Woźniak committed
146
147
148
149
        debug "    +-- previous line : $PREVIOUS_CI_LINE"
        debug "        finishing this run off."
        DATE_EXT="$( date +%s%N )"
        tail -n "+$(( $PREVIOUS_CI_LINE + 1 ))" "$NGINX_CONFIG_COPY" > "$NGINX_CONFIG_COPY.$DATE_EXT"
150
151
152
153
        cat $NGINX_CONFIG_COPY.* > "$NGINX_CONFIG_COPY"
        rm $NGINX_CONFIG_COPY.*
    done
    IFS="$OLDIFS"
Michał Woźniak's avatar
Michał Woźniak committed
154

Michał Woźniak's avatar
Michał Woźniak committed
155
156
    debug "+-- moving the temporary output file to the output destination"
    debug "    src: $NGINX_CONFIG_COPY"
157
    debug "    dst: $OUTPUT_DEST"
158
159
160
161
162
    if [ "$OUTPUT_DEST" == '-' ]; then
        cat "$NGINX_CONFIG_COPY"
    else
        mv "$NGINX_CONFIG_COPY" "$OUTPUT_DEST"
    fi
Michał Woźniak's avatar
Michał Woźniak committed
163
    debug "+-- cleaning up the temporary output directory"
Michał Woźniak's avatar
Michał Woźniak committed
164
    rm -rf "$TMP_WORKDIR"
165
166
}

167
168
169
170
171
172
173
174
175
176

#
# print the include tree, or dump it to a file
function nginx-tree {
    echo "NOT IMPLEMENTED YET"
}


#
# print the usage
177
178
179
180
function print-usage {
    cat - <<HEREDOC
    
usage:
181
    $0 <mode> <input_file> <output_file|output_directory>
182
183
184
    
modes:
    
185
186
187
188
    tree:
        generate a tree of includes and save it to <output_file>
        (or to standard output if <output_file> not given)
    
189
190
    flatten:
        flatten an nginx config file by inlining all includes recursively,
191
        save to <output_file> (or to standard output if <output_file> not given)
192
193
194
        
    clean-directory:
        generate a cleaned nginx config directory, containing only the files
195
        that are included from the input file, or files included from those,
196
197
198
199
200
201
202
203
204
205
206
207
208
        and so on; output to <output_directory>
    
HEREDOC
}


ORIG_CWD="$PWD"

# we really need 3 arguments:
# $1 - mode of operation
# $2 - source file
# $3 - destination file/directory

209
if [ "$1" == "" ] || [ "$2" == "" ]; then
210
211
212
213
214
215
216
217
218
219
    print-usage
    exit 1
fi

# source file exists?
if [ ! -f "$2" ] || [ ! -r "$2" ]; then
    echo
    echo "ERROR: source config file $2 doesn't exist or read access not granted"
    echo
    exit 2
Michał Woźniak's avatar
Michał Woźniak committed
220
fi
221

222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
if [ "$1" == "flatten" ] || [ "$1" == "tree" ]; then
    if [ "$3" == "" ]; then
        OUTPUT_DEST="-"
        debug "+-- output to stdout."
    else
        # destination file/directory must not exist
        OUTPUT_DEST="$( get-path-relative-to "$3" "$ORIG_CWD" )"
        if [ -e "$OUTPUT_DEST" ]; then
            echo
            echo "WARNING: output file/directory exists; quitting!"
            echo
            exit 3
        fi
    fi
# for clean-director we always have to have $3
else
    if [ "$3" == "" ]; then
        print-usage
        exit 1
    fi
    # destination file/directory must not exist
    OUTPUT_DEST="$( get-path-relative-to "$3" "$ORIG_CWD" )"
    if [ -e "$OUTPUT_DEST" ]; then
        echo
        echo "WARNING: output file/directory exists; quitting!"
        echo
        exit 3
    fi
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
fi

NGINX_CONFIG="$( realpath -eL "$2" )"
debug "+-- config file: '$NGINX_CONFIG'"

NGINX_CONFIG_DIR="$( dirname "$NGINX_CONFIG" )"
debug "    config directory: 'NGINX_CONFIG_DIR'"

cd "$NGINX_CONFIG_DIR"

TMP_WORKDIR="$( mktemp -d /tmp/nginx-conf-flatten.XXXX )"
debug "+-- TMP_WORKDIR: $TMP_WORKDIR"

# the list of files whose includes we have not copied yet
NGINX_CONFIG_ELEMENTS=("$NGINX_CONFIG")


# what are we doing, eh?
if [ "$1" == "flatten" ]; then
269
    nginx-flatten
270
elif [ "$1" == "clean-directory" ]; then
271
272
273
    nginx-clean-directory
elif [ "$1" == "tree" ]; then
    nginx-tree
274
275
276
277
278
279
280
else
    print-usage
    exit 1
fi

# get back to the original directory we've been running from
cd "$ORIG_CWD"