Overview
A playbook is Ansible's configuration, deployment, and orchestration language. It contains one or more plays, each targeting a set of hosts and containing tasks to execute. This plugin parses playbook YAML files and returns structured domain types for the host to interpret.
Reference: docs/ansible-schema/playbook.json
Decisions to Make
| Question | Options |
|---|---|
| Plugin name | shep-plugin-playbook |
| Capability | playbook (single capability) |
| Scope | Parse only, no execution (execution is host/task concern) |
| Task representation | Reference tasks.fbs domain types |
Architecture
Relationship to Other Components
playbook.yml ──► playbook plugin ──► Playbook (plays, tasks, handlers)
│
▼
Host orchestration loop
│
▼
┌──────────────────┴──────────────────┐
▼ ▼
inventory plugin task executor
(resolve hosts) (run tasks)
The playbook plugin is parse-only. It transforms YAML into structured domain types. The host owns:
- Resolving which hosts match
hosts:patterns - Variable precedence and resolution
- Conditional evaluation (
when:) - Error handling policy
- Handler notification
Domain Types
Playbook Structure (from schema analysis)
A playbook is an array of items, each being either:
- Play - targets hosts with tasks
- Import playbook - includes another playbook file
A Play contains:
name- optional descriptionhosts- required host pattern (string or list)tasks- list of tasks to executehandlers- tasks triggered bynotifypre_tasks/post_tasks- tasks before/after rolesroles- list of roles to includevars/vars_files/vars_prompt- variable definitionsbecome/become_user/become_method- privilege escalationgather_facts- whether to collect host factsenvironment- environment variablesstrategy- execution strategy (linear, free, etc.)serial- batch size for rolling updatesmax_fail_percentage- failure thresholdany_errors_fatal- stop on any errortags- for selective execution
FlatBuffers Schema Design
Domain types (flatbuffers/domain/playbook.fbs):
namespace shep.domain;
// A complete playbook
table Playbook {
plays:[Play];
imports:[PlaybookImport];
}
// Import another playbook
table PlaybookImport {
path:string; // file path to import
name:string; // optional name
vars:[Var]; // vars to pass
tags:[string];
when:string; // conditional
}
// A play targets hosts with tasks
table Play {
name:string;
hosts:string; // host pattern (may contain commas, groups)
// Task lists
tasks:[Task];
handlers:[Task];
pre_tasks:[Task];
post_tasks:[Task];
// Roles
roles:[RoleInclude];
// Variables
vars:[Var];
vars_files:[string];
// Privilege escalation
become:bool;
become_user:string;
become_method:string;
// Execution control
gather_facts:bool = true;
strategy:string;
serial:string; // can be int, percent, or list - store as string
max_fail_percentage:int;
any_errors_fatal:bool;
// Connection
connection:string;
remote_user:string;
port:int;
// Filtering
tags:[string];
when:string;
// Environment
environment:[Var];
}
// Role inclusion in a play
table RoleInclude {
role:string; // role name or path
name:string; // optional display name
vars:[Var];
tags:[string];
when:string;
become:bool;
become_user:string;
}
Capability types (flatbuffers/capabilities/playbook.fbs):
namespace shep.cap.playbook;
include "domain/playbook.fbs";
include "domain/task.fbs";
table PlaybookRequest {
file_path:string;
content:[ubyte];
}
table PlaybookResponse {
playbook:shep.domain.Playbook;
warnings:[string];
error:string;
}
Dependencies
This plugin depends on the Task domain types from tasks.fbs. The playbook
contains task lists, so we must define Task types first (or concurrently).
Dependency order:
flatbuffers/domain/task.fbs(Task, Block types)flatbuffers/domain/playbook.fbs(imports task.fbs)flatbuffers/capabilities/playbook.fbs
Implementation Plan
Phase 1: Domain Schema Definition
Prerequisites: Task domain types must exist (see shep-plugin-tasks-plan.md)
Files to create:
flatbuffers/domain/playbook.fbs
flatbuffers/capabilities/playbook.fbs
Phase 2: Plugin Implementation
Files to create:
plugins/playbook/
├── main.go # Plugin entry point
├── parser.go # YAML parsing logic
└── parser_test.go # Unit tests
Parser responsibilities:
- Parse playbook YAML array
- Distinguish plays from import_playbook directives
- Parse each play's tasks, handlers, pre_tasks, post_tasks
- Parse role inclusions
- Handle vars, vars_files
- Normalize become settings
- Build Playbook FlatBuffer
Phase 3: CLI Integration
Add playbook commands:
shep playbook show -f site.yml # Show playbook structure
shep playbook validate -f site.yml # Validate playbook syntax
shep p show -f site.yml # Short alias
Output (show):
PLAYBOOK: site.yml
PLAY: Configure web servers
hosts: webservers
gather_facts: true
become: true
TASKS:
- Install nginx (apt)
- Configure nginx (template)
- Start nginx (service)
HANDLERS:
- Restart nginx (service)
PLAY: Configure database
hosts: dbservers
...
Phase 4: Testing
Test data needed:
testdata/playbook/
├── simple-play.yml # Single play, few tasks
├── multi-play.yml # Multiple plays
├── with-handlers.yml # Handlers and notify
├── with-roles.yml # Role inclusions
├── with-blocks.yml # Block/rescue/always
├── import-playbook.yml # Import directive
└── complex-vars.yml # vars, vars_files, vars_prompt
Milestones
- [ ] M1: Task schemas exist — Prerequisite from tasks plugin
- [ ] M2: Playbook schemas — FlatBuffers compile, Go code generates
- [ ] M3: Plugin skeleton — Handshake and capability routing
- [ ] M4: Play parsing — Parse plays with hosts, tasks
- [ ] M5: Full parsing — Roles, handlers, vars, blocks
- [ ] M6: CLI integration —
shep playbook showworks - [ ] M7: Tests passing — Unit and integration tests
Out of Scope
- [ ] Playbook execution (host responsibility)
- [ ] Variable resolution/templating (host responsibility)
- [ ] Role discovery and loading (separate capability)
- [ ]
include_tasks/import_tasksresolution - [ ] Vault decryption
- [ ] Jinja2 template validation
Notes
Import vs Include
Ansible distinguishes:
import_playbook- static, processed at parse timeinclude_*- dynamic, processed at runtime
For the parser plugin, we only handle import_playbook since it's static. The
host handles dynamic includes during execution.
Blocks
Blocks (block, rescue, always) are parsed as part of task lists. They
contain nested task lists. The Task domain type must support blocks.
Serial and Strategy
These affect execution parallelism but are host concerns. The parser just captures the values; the host interprets them.