Artifacts: dsci@007aefe.1789617896. File: answer.md
artifacts
## 📁 File structure
```
.
├── jobs.yaml # pipeline definition (only jobs list)
└── ansible_job/ # one DSCI job
├── config.yaml # default job parameters (can be overridden)
├── job.bash # job script – orchestrates the tasks
└── tasks/
├── install_ansible/
│ └── task.bash # installs Ansible in the execution container/host
└── run_playbook/
└── task.bash # runs the supplied Ansible playbook on the remote host
```
---
## 🗂️ `jobs.yaml` – pipeline definition
Only the list of jobs (and optional input parameters) is placed here – **no logic**.
```yaml
# jobs.yaml
jobs:
-
id: ansible_job
path: ansible_job/
# optional – override any defaults from ansible_job/config.yaml
#params:
# remote_user: root
# inventory_path: ./inventory.ini
# playbook_path: ./site.yml
# extra_vars: "env=prod version=1.2.3"
```
---
## ⚙️ `ansible_job/config.yaml` – default job parameters
These values are used when the pipeline does **not** provide a `params:` block for the job.
Any key can be overridden from `jobs.yaml` (or by the CI system when the pipeline is launched).
```yaml
# ansible_job/config.yaml
remote_user: ubuntu # SSH user that Ansible will use
inventory_path: ./inventory.ini # Path to the Ansible inventory file (relative to the job dir)
playbook_path: ./site.yml # Path to the main playbook (relative to the job dir)
extra_vars: "" # Optional extra‑vars string (e.g. "env=prod version=1.2")
```
---
## 🏁 `ansible_job/job.bash` – job entry point
The job only orchestrates tasks. All “real” work lives in the tasks below.
```bash
# ansible_job/job.bash
#!/usr/bin/env bash
set -euo pipefail
# -------------------------------------------------------------------------
# 1️⃣ Install Ansible (if it is not already available)
# -------------------------------------------------------------------------
run_task install_ansible
# -------------------------------------------------------------------------
# 2️⃣ Execute the supplied playbook
# -------------------------------------------------------------------------
run_task run_playbook \
playbook "$(config playbook_path)" \
inventory "$(config inventory_path)" \
extra_vars "$(config extra_vars)"
```
> **Explanation**
> * `$(config )` reads a job‑parameter (default or overridden).
> * `run_task [NAME VALUE …]` launches a task and forwards the
> variables that the task can later read with `$(task_var )`.
---
## 🔧 `ansible_job/tasks/install_ansible/task.bash` – make sure Ansible exists
```bash
# ansible_job/tasks/install_ansible/task.bash
#!/usr/bin/env bash
set -euo pipefail
echo "🔧 Checking if Ansible is installed …"
if command -v ansible-playbook >/dev/null 2>&1; then
echo "✅ Ansible already available."
exit 0
fi
echo "⚙️ Ansible not found – installing now."
# Try the most common package managers (apt, yum, dnf, apk)
if command -v apt-get >/dev/null 2>&1; then
apt-get update && apt-get install -y --no-install-recommends ansible
elif command -v yum >/dev/null 2>&1; then
yum install -y epel-release && yum install -y ansible
elif command -v dnf >/dev/null 2>&1; then
dnf install -y ansible
elif command -v apk >/dev/null 2>&1; then
apk add --no-cache ansible
else
echo "❌ No known package manager – please install Ansible manually."
exit 1
fi
echo "✅ Ansible installation finished."
```
---
## 🚀 `ansible_job/tasks/run_playbook/task.bash` – run the playbook on the remote host
```bash
# ansible_job/tasks/run_playbook/task.bash
#!/usr/bin/env bash
set -euo pipefail
# -------------------------------------------------------------------------
# Pull variables that were passed from the job
# -------------------------------------------------------------------------
playbook=$(task_var playbook)
inventory=$(task_var inventory)
extra_vars=$(task_var extra_vars)
# -------------------------------------------------------------------------
# Prepare a log file inside the shared artifacts directory
# -------------------------------------------------------------------------
log_file="${HOME}/artifacts/ansible_run.log"
mkdir -p "$(dirname "$log_file")"
echo "▶️ Running Ansible playbook"
echo " Playbook : $playbook"
echo " Inventory: $inventory"
[ -n "$extra_vars" ] && echo " Extra‑vars: $extra_vars"
# -------------------------------------------------------------------------
# Execute ansible‑playbook, tee output to the log file
# -------------------------------------------------------------------------
if [ -n "$extra_vars" ]; then
ansible-playbook -i "$inventory" "$playbook" --extra-vars "$extra_vars" | tee "$log_file"
else
ansible-playbook -i "$inventory" "$playbook" | tee "$log_file"
fi
rc=$?
# -------------------------------------------------------------------------
# Export a small state for downstream jobs (if any)
# -------------------------------------------------------------------------
if [ $rc -eq 0 ]; then
echo "✅ Playbook completed successfully."
update_state '{ "ansible_status": "success", "log_path": "'"$log_file"'" }'
else
echo "❌ Playbook failed (exit code $rc)."
update_state '{ "ansible_status": "failed", "log_path": "'"$log_file"'", "exit_code": '"$rc"' }'
fi
exit $rc
```
> **What this task does**
> 1. Retrieves the variables supplied by the job (`playbook`, `inventory`, `extra_vars`).
> 2. Runs `ansible-playbook` and captures the full console output in `~/artifacts/ansible_run.log`.
> 3. Emits a **state** (`ansible_status`, `log_path`, optional `exit_code`) that any later job can read via `config()`.
---
## 📦 Sharing results with another job (optional)
If you add a second job after `ansible_job` you can read the state like this:
```yaml
# jobs.yaml (add a second job)
jobs:
- id: ansible_job
path: ansible_job/
- id: report_job
path: report_job/
```
`report_job/job.bash` (example):
```bash
#!/usr/bin/env bash
set -euo pipefail
# Pull the state that the previous job stored under its id
ansible_status=$(config | jq -r '._dsci_.ansible_job.ansible_status')
log_path=$(config | jq -r '._dsci_.ansible_job.log_path')
echo "🔎 Ansible job finished with status: $ansible_status"
echo "🗒️ Log file is available at: $log_path"
# (You could, for instance, send the log somewhere, post a comment, etc.)
```
---
## ✅ How it works – the DSCI flow in a nutshell
| Layer | What you write |
|------|----------------|
| **Pipeline (YAML)** | `jobs.yaml` – only a list of jobs (`id` + `path` + optional `params`). |
| **Job** | `job.bash` – sequentially calls `run_task …`. |
| **Task** | Real Bash logic (`task.bash`). Uses `$(task_var …)` for variables, `$(config …)` for job‑wide parameters, `update_state …` to share data, and writes any files to `~/artifacts/` for cross‑job sharing. |
| **State / Artifacts** | `update_state` → accessible to later jobs via `config`. Files in `~/artifacts/` survive across jobs. |
No third‑party actions, no complex YAML matrices – just a tiny `jobs.yaml` and normal Bash scripts.
---
### 🎉 Ready to run!
Place the directory layout exactly as shown, commit the files, and let DSCI execute the pipeline.
The job will:
1. Ensure Ansible is present.
2. Run the supplied playbook against the remote host(s) defined in `inventory.ini`.
3. Store the full console output in `~/artifacts/ansible_run.log`.
4. Export a concise state (`ansible_status`, `log_path`, `exit_code`) for any downstream job to consume.