1#!/usr/local/bin/bash -eu
2
3# Script to dual-home the upstream and downstream Collection in a single repo
4#
5#   This script will build or test a downstream collection, removing any
6#   upstream components that will not ship in the downstream release
7#
8#   NOTES:
9#       - All functions are prefixed with f_ so it's obvious where they come
10#         from when in use throughout the script
11
12DOWNSTREAM_VERSION="1.1.2"
13KEEP_DOWNSTREAM_TMPDIR="${KEEP_DOWNSTREAM_TMPDIR:-''}"
14_build_dir=""
15
16f_log_info()
17{
18    printf "%s:LOG:INFO: %s\n" "${0}" "${1}"
19}
20
21f_show_help()
22{
23    printf "Usage: downstream.sh [OPTION]\n"
24    printf "\t-s\t\tCreate a temporary downstream release and perform sanity tests.\n"
25    printf "\t-i\t\tCreate a temporary downstream release and perform integration tests.\n"
26    printf "\t-m\t\tCreate a temporary downstream release and perform molecule tests.\n"
27    printf "\t-b\t\tCreate a downstream release and stage for release.\n"
28    printf "\t-r\t\tCreate a downstream release and publish release.\n"
29}
30
31f_text_sub()
32{
33    # Switch FQCN and dependent components
34    OKD_sed_files="${_build_dir}/README.md ${_build_dir}/CHANGELOG.rst ${_build_dir}/changelogs/config.yaml ${_build_dir}/ci/downstream.sh ${_build_dir}/galaxy.yml"
35    # shellcheck disable=SC2068
36    for okd_file in ${OKD_sed_files[@]}; do sed -i.bak "s/OKD/OpenShift/g" "${okd_file}"; done
37
38    sed -i.bak "s/============================/==================================/" "${_build_dir}/CHANGELOG.rst"
39    sed -i.bak "s/Ansible Galaxy/Automation Hub/" "${_build_dir}/README.md"
40    sed -i.bak "s/community-okd/redhat-openshift/" "${_build_dir}/Makefile"
41    sed -i.bak "s/community\/okd/redhat\/openshift/" "${_build_dir}/Makefile"
42    sed -i.bak "s/^VERSION\:/VERSION: ${DOWNSTREAM_VERSION}/" "${_build_dir}/Makefile"
43    sed -i.bak "s/name\:.*$/name: openshift/" "${_build_dir}/galaxy.yml"
44    sed -i.bak "s/namespace\:.*$/namespace: redhat/" "${_build_dir}/galaxy.yml"
45    sed -i.bak "s/Kubernetes/OpenShift/g" "${_build_dir}/galaxy.yml"
46    sed -i.bak "s/^version\:.*$/version: ${DOWNSTREAM_VERSION}/" "${_build_dir}/galaxy.yml"
47    sed -i.bak "/STARTREMOVE/,/ENDREMOVE/d" "${_build_dir}/README.md"
48
49    find "${_build_dir}" -type f -exec sed -i.bak "s/community\.okd/redhat\.openshift/g" {} \;
50    find "${_build_dir}" -type f -name "*.bak" -delete
51}
52
53f_prep()
54{
55    f_log_info "${FUNCNAME[0]}"
56    # Array of excluded files from downstream build (relative path)
57    _file_exclude=(
58    )
59
60    # Files to copy downstream (relative repo root dir path)
61    _file_manifest=(
62        CHANGELOG.rst
63        galaxy.yml
64        LICENSE
65        README.md
66        Makefile
67        setup.cfg
68        .yamllint
69        requirements.txt
70    )
71
72    # Directories to recursively copy downstream (relative repo root dir path)
73    _dir_manifest=(
74        changelogs
75        ci
76        meta
77        molecule
78        plugins
79        tests
80    )
81
82    # Modules with inherited doc fragments from kubernetes.core that need
83    # rendering to deal with Galaxy/AH lack of functionality.
84    _doc_fragment_modules=(
85        k8s
86        openshift_process
87        openshift_route
88    )
89
90
91    # Temp build dir
92    _tmp_dir=$(mktemp -d)
93    _start_dir="${PWD}"
94    _build_dir="${_tmp_dir}/ansible_collections/redhat/openshift"
95    mkdir -p "${_build_dir}"
96}
97
98
99f_cleanup()
100{
101    f_log_info "${FUNCNAME[0]}"
102    if [[ -n "${_build_dir}" ]]; then
103        if [[ -n ${KEEP_DOWNSTREAM_TMPDIR} ]]; then
104            if [[ -d ${_build_dir} ]]; then
105                rm -fr "${_build_dir}"
106            fi
107        fi
108    else
109        exit 0
110    fi
111}
112
113# Exit and handle cleanup processes if needed
114f_exit()
115{
116    f_cleanup
117    exit "$0"
118}
119
120f_create_collection_dir_structure()
121{
122    f_log_info "${FUNCNAME[0]}"
123    # Create the Collection
124    for f_name in "${_file_manifest[@]}";
125    do
126        cp "./${f_name}" "${_build_dir}/${f_name}"
127    done
128    for d_name in "${_dir_manifest[@]}";
129    do
130        cp -r "./${d_name}" "${_build_dir}/${d_name}"
131    done
132    if [ -n "${_file_exclude:-}" ]; then
133        for exclude_file in "${_file_exclude[@]}";
134        do
135            if [[ -f "${_build_dir}/${exclude_file}" ]]; then
136                rm -f "${_build_dir}/${exclude_file}"
137            fi
138        done
139    fi
140}
141
142f_handle_doc_fragments_workaround()
143{
144    f_log_info "${FUNCNAME[0]}"
145    local install_collections_dir="${_build_dir}/collections/"
146    local temp_fragments_json="${_tmp_dir}/fragments.json"
147    local temp_start="${_tmp_dir}/startfile.txt"
148    local temp_end="${_tmp_dir}/endfile.txt"
149    local rendered_fragments="./rendereddocfragments.txt"
150
151    # Build the collection, export docs, render them, stitch it all back together
152    pushd "${_build_dir}" || return
153        ansible-galaxy collection build
154        ansible-galaxy collection install -p "${install_collections_dir}" ./*.tar.gz
155        rm ./*.tar.gz
156        for doc_fragment_mod in "${_doc_fragment_modules[@]}"
157        do
158            local module_py="plugins/modules/${doc_fragment_mod}.py"
159            f_log_info "Processing doc fragments for ${module_py}"
160            # We need following variable for ansible-doc only
161            # shellcheck disable=SC2097,SC2098
162            ANSIBLE_COLLECTIONS_PATH="${install_collections_dir}" \
163            ANSIBLE_COLLECTIONS_PATHS="${ANSIBLE_COLLECTIONS_PATH}:${install_collections_dir}" \
164                ansible-doc -j "redhat.openshift.${doc_fragment_mod}" > "${temp_fragments_json}"
165            # FIXME: Check Python interpreter from environment variable to work with prow
166            if [ -e /usr/bin/python3.6 ]; then
167                PYTHON="/usr/bin/python3.6"
168            else
169                PYTHON="python"
170            fi
171            "${PYTHON}" "${_start_dir}/ci/downstream_fragments.py" "redhat.openshift.${doc_fragment_mod}" "${temp_fragments_json}"
172            sed -n '/STARTREMOVE/q;p' "${module_py}" > "${temp_start}"
173            sed '1,/ENDREMOVE/d' "${module_py}" > "${temp_end}"
174            cat "${temp_start}" "${rendered_fragments}" "${temp_end}" > "${module_py}"
175        done
176        rm -f "${rendered_fragments}"
177        rm -fr "${install_collections_dir}"
178    popd
179
180}
181
182f_install_kubernetes_core()
183{
184    f_log_info "${FUNCNAME[0]}"
185    local install_collections_dir="${_tmp_dir}/ansible_collections"
186
187    pushd "${_tmp_dir}"
188        ansible-galaxy collection install -p "${install_collections_dir}" kubernetes.core
189    popd
190}
191
192
193f_copy_collection_to_working_dir()
194{
195    f_log_info "${FUNCNAME[0]}"
196    # Copy the Collection build result into original working dir
197    cp "${_build_dir}"/*.tar.gz ./
198}
199
200f_common_steps()
201{
202    f_log_info "${FUNCNAME[0]}"
203    f_prep
204    f_create_collection_dir_structure
205    f_text_sub
206    f_handle_doc_fragments_workaround
207}
208
209# Run the test sanity scanerio
210f_test_sanity_option()
211{
212    f_log_info "${FUNCNAME[0]}"
213    f_common_steps
214    f_install_kubernetes_core
215    pushd "${_build_dir}" || return
216        ansible-galaxy collection build
217        f_log_info "SANITY TEST PWD: ${PWD}"
218        ## Can't do this because the upstream kubernetes.core dependency logic
219        ## is bound as a Makefile dep to the test-sanity target
220        #SANITY_TEST_ARGS="--docker --color --python 3.6" make test-sanity
221        # Run tests in docker if available, venv otherwise
222        if command -v docker &> /dev/null
223        then
224            ansible-test sanity --docker -v --exclude ci/ --color --python 3.6
225        else
226            ansible-test sanity --venv -v --exclude ci/ --color --python 3.6
227        fi
228    popd || return
229    f_cleanup
230}
231
232# Run the test integration
233f_test_integration_option()
234{
235    f_log_info "${FUNCNAME[0]}"
236    f_common_steps
237    f_install_kubernetes_core
238    pushd "${_build_dir}" || return
239        f_log_info "INTEGRATION TEST WD: ${PWD}"
240        OVERRIDE_COLLECTION_PATH="${_tmp_dir}" molecule test
241    popd || return
242    f_cleanup
243}
244
245# Run the build scanerio
246f_build_option()
247{
248    f_log_info "${FUNCNAME[0]}"
249    f_common_steps
250    pushd "${_build_dir}" || return
251        f_log_info "BUILD WD: ${PWD}"
252        # FIXME
253        #   This doesn't work because we end up either recursively curl'ing
254        #   kubernetes.core and redoing the text replacement over and over
255        #
256        # make build
257        ansible-galaxy collection build
258
259    popd || return
260    f_copy_collection_to_working_dir
261    f_cleanup
262}
263
264# If no options are passed, display usage and exit
265if [[ "${#}" -eq "0" ]]; then
266    f_show_help
267    f_exit 0
268fi
269
270# Handle options
271while getopts ":sirb" option
272do
273  case $option in
274    s)
275        f_test_sanity_option
276        ;;
277    i)
278        f_test_integration_option
279        ;;
280    r)
281        f_release_option
282        ;;
283    b)
284        f_build_option
285        ;;
286    *)
287        printf "ERROR: Unimplemented option chosen.\n"
288        f_show_help
289        f_exit 1
290        ;;   # Default.
291  esac
292done
293
294# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
295