| # Copyright (c) 2022 VEXXHOST, Inc. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may |
| # not use this file except in compliance with the License. You may obtain |
| # a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations |
| # under the License. |
| |
| # NOTE(mnaser): We're using this playbook to capture logs for the run since |
| # there is no Molecule phase that runs after the converge phase. |
| - hosts: controllers[0] |
| gather_facts: false |
| ignore_unreachable: true |
| vars: |
| logs_dir: /tmp/logs |
| tasks: |
| - name: End the play if the infrastructure is not deployed |
| ansible.builtin.meta: end_host |
| when: |
| - ansible_host is not defined |
| |
| - name: Upload logs to object storage |
| ignore_errors: True |
| when: |
| - lookup('env', 'GITHUB_RUN_ID')|length > 0 |
| - lookup('env', 'GITHUB_RUN_NUMBER')|length > 0 |
| vars: |
| build_id: "{{ lookup('env', 'GITHUB_RUN_ID') }}-{{ lookup('env', 'GITHUB_RUN_NUMBER') }}" |
| container_name: atmosphere-ci-logs |
| block: |
| - name: Describe all cluster-scoped objects |
| become: true |
| shell: |- |
| set -e |
| export OBJECT_TYPE=node,clusterrole,clusterrolebinding,storageclass,namespace |
| export PARALLELISM_FACTOR=4 |
| function list_objects () { |
| printf ${OBJECT_TYPE} | xargs -d ',' -I {} -P1 -n1 bash -c 'echo "$@"' _ {} |
| } |
| export -f list_objects |
| function name_objects () { |
| export OBJECT=$1 |
| kubectl get ${OBJECT} -o name | xargs -L1 -I {} -P1 -n1 bash -c 'echo "${OBJECT} ${1#*/}"' _ {} |
| } |
| export -f name_objects |
| function get_objects () { |
| input=($1) |
| export OBJECT=${input[0]} |
| export NAME=${input[1]#*/} |
| echo "${OBJECT}/${NAME}" |
| DIR="{{ logs_dir }}/objects/cluster/${OBJECT}" |
| mkdir -p ${DIR} |
| kubectl get ${OBJECT} ${NAME} -o yaml > "${DIR}/${NAME}.yaml" |
| kubectl describe ${OBJECT} ${NAME} > "${DIR}/${NAME}.txt" |
| } |
| export -f get_objects |
| list_objects | \ |
| xargs -r -n 1 -P ${PARALLELISM_FACTOR} -I {} bash -c 'name_objects "$@"' _ {} | \ |
| xargs -r -n 1 -P ${PARALLELISM_FACTOR} -I {} bash -c 'get_objects "$@"' _ {} |
| args: |
| executable: /bin/bash |
| |
| - name: Describe all namespace-scoped objects |
| become: true |
| shell: |- |
| set -e |
| export OBJECT_TYPE=configmaps,cronjobs,daemonsets,deployment,endpoints,ingresses,jobs,networkpolicies,pods,podsecuritypolicies,persistentvolumeclaims,rolebindings,roles,secrets,serviceaccounts,services,statefulsets |
| export PARALLELISM_FACTOR=4 |
| function get_namespaces () { |
| kubectl get namespaces -o name | awk -F '/' '{ print $NF }' |
| } |
| function list_namespaced_objects () { |
| export NAMESPACE=$1 |
| printf ${OBJECT_TYPE} | xargs -d ',' -I {} -P1 -n1 bash -c 'echo "${NAMESPACE} $@"' _ {} |
| } |
| export -f list_namespaced_objects |
| function name_objects () { |
| input=($1) |
| export NAMESPACE=${input[0]} |
| export OBJECT=${input[1]} |
| kubectl get -n ${NAMESPACE} ${OBJECT} -o name | xargs -L1 -I {} -P1 -n1 bash -c 'echo "${NAMESPACE} ${OBJECT} $@"' _ {} |
| } |
| export -f name_objects |
| function get_objects () { |
| input=($1) |
| export NAMESPACE=${input[0]} |
| export OBJECT=${input[1]} |
| export NAME=${input[2]#*/} |
| echo "${NAMESPACE}/${OBJECT}/${NAME}" |
| DIR="{{ logs_dir }}/objects/namespaced/${NAMESPACE}/${OBJECT}" |
| mkdir -p ${DIR} |
| kubectl get -n ${NAMESPACE} ${OBJECT} ${NAME} -o yaml > "${DIR}/${NAME}.yaml" |
| kubectl describe -n ${NAMESPACE} ${OBJECT} ${NAME} > "${DIR}/${NAME}.txt" |
| } |
| export -f get_objects |
| get_namespaces | \ |
| xargs -r -n 1 -P ${PARALLELISM_FACTOR} -I {} bash -c 'list_namespaced_objects "$@"' _ {} | \ |
| xargs -r -n 1 -P ${PARALLELISM_FACTOR} -I {} bash -c 'name_objects "$@"' _ {} | \ |
| xargs -r -n 1 -P ${PARALLELISM_FACTOR} -I {} bash -c 'get_objects "$@"' _ {} |
| args: |
| executable: /bin/bash |
| |
| - name: Retrieve all container logs, current and previous (if they exist) |
| become: true |
| shell: |- |
| set -e |
| PARALLELISM_FACTOR=4 |
| function get_namespaces () { |
| kubectl get namespaces -o name | awk -F '/' '{ print $NF }' |
| } |
| function get_pods () { |
| NAMESPACE=$1 |
| kubectl get pods -n ${NAMESPACE} -o name | awk -F '/' '{ print $NF }' | xargs -L1 -P 1 -I {} echo ${NAMESPACE} {} |
| } |
| export -f get_pods |
| function get_pod_logs () { |
| NAMESPACE=${1% *} |
| POD=${1#* } |
| INIT_CONTAINERS=$(kubectl get pod $POD -n ${NAMESPACE} -o jsonpath="{.spec.initContainers[*].name}") |
| CONTAINERS=$(kubectl get pod $POD -n ${NAMESPACE} -o jsonpath="{.spec.containers[*].name}") |
| for CONTAINER in ${INIT_CONTAINERS} ${CONTAINERS}; do |
| echo "${NAMESPACE}/${POD}/${CONTAINER}" |
| mkdir -p "{{ logs_dir }}/pod-logs/${NAMESPACE}/${POD}" |
| mkdir -p "{{ logs_dir }}/pod-logs/failed-pods/${NAMESPACE}/${POD}" |
| kubectl logs ${POD} -n ${NAMESPACE} -c ${CONTAINER} > "{{ logs_dir }}/pod-logs/${NAMESPACE}/${POD}/${CONTAINER}.txt" |
| kubectl logs --previous ${POD} -n ${NAMESPACE} -c ${CONTAINER} > "{{ logs_dir }}/pod-logs/failed-pods/${NAMESPACE}/${POD}/${CONTAINER}.txt" |
| done |
| find {{ logs_dir }} -type f -empty -print -delete |
| find {{ logs_dir }} -empty -type d -delete |
| } |
| export -f get_pod_logs |
| get_namespaces | \ |
| xargs -r -n 1 -P ${PARALLELISM_FACTOR} -I {} bash -c 'get_pods "$@"' _ {} | \ |
| xargs -r -n 2 -P ${PARALLELISM_FACTOR} -I {} bash -c 'get_pod_logs "$@"' _ {} |
| args: |
| executable: /bin/bash |
| |
| - name: Authenticate to cloud to get token to use in Swift client |
| delegate_to: localhost |
| openstack.cloud.auth: |
| register: _auth |
| |
| - name: Generate storage URL |
| set_fact: |
| storage_url: "{{ ((service_catalog | selectattr('name', 'equalto', 'swift') | first).endpoints | selectattr('interface', 'equalto', 'public') | first).url }}" |
| |
| - name: Install Swift client |
| become: true |
| ansible.builtin.apt: |
| name: ['python3-swiftclient', 'tree'] |
| state: present |
| |
| - name: Generate listing for all files |
| become: true |
| shell: tree -H '.' --charset utf-8 -o {{ logs_dir }}/index.html {{ logs_dir }} |
| |
| - name: Upload logs to swift |
| shell: |- |
| set -e |
| swift post -H "X-Container-Read: .r:*,.rlistings" {{ container_name }} |
| swift upload -H "X-Delete-After: 604800" -m 'web-index:index.html' --object-name {{ build_id }} {{ container_name }} {{ logs_dir }} |
| environment: |
| OS_STORAGE_URL: "{{ storage_url }}" |
| OS_AUTH_TOKEN: "{{ auth_token }}" |
| |
| - name: Print logs URL |
| debug: |
| msg: "Logs are available at {{ storage_url }}/{{ container_name }}/{{ build_id }}/index.html" |
| |
| - hosts: localhost |
| connection: local |
| gather_facts: false |
| no_log: "{{ molecule_no_log }}" |
| vars: |
| workspace_path: "{{ lookup('env', 'MOLECULE_SCENARIO_DIRECTORY') }}" |
| |
| stack_name: "{{ lookup('env', 'ATMOSPHERE_STACK_NAME') | default('atmosphere', True) }}" |
| tasks: |
| - os_stack: |
| name: "{{ stack_name }}" |
| state: absent |
| |
| - file: |
| path: "{{ molecule_instance_config }}" |
| state: absent |
| |
| - name: Capture var files to delete |
| find: |
| paths: |
| - "{{ workspace_path }}/group_vars" |
| - "{{ workspace_path }}/host_vars" |
| file_type: file |
| recurse: true |
| excludes: |
| - "molecule.yml" |
| register: _var_files |
| |
| - name: Delete var files |
| file: |
| path: "{{ item.path }}" |
| state: absent |
| with_items: "{{ _var_files['files'] }}" |