fix(ansible): support import_playbook expansion

This commit is contained in:
Virgil 2026-04-01 20:54:36 +00:00
parent f71e8642e9
commit f80825783c
3 changed files with 75 additions and 1 deletions

View file

@ -38,6 +38,17 @@ func NewParser(basePath string) *Parser {
//
// plays, err := parser.ParsePlaybook("/workspace/playbooks/site.yml")
func (p *Parser) ParsePlaybook(path string) ([]Play, error) {
return p.parsePlaybook(path, make(map[string]bool))
}
func (p *Parser) parsePlaybook(path string, seen map[string]bool) ([]Play, error) {
cleanedPath := cleanPath(path)
if seen[cleanedPath] {
return nil, coreerr.E("Parser.parsePlaybook", "circular import_playbook detected: "+cleanedPath, nil)
}
seen[cleanedPath] = true
defer delete(seen, cleanedPath)
data, err := coreio.Local.Read(path)
if err != nil {
return nil, coreerr.E("Parser.ParsePlaybook", "read playbook", err)
@ -48,14 +59,27 @@ func (p *Parser) ParsePlaybook(path string) ([]Play, error) {
return nil, coreerr.E("Parser.ParsePlaybook", "parse playbook", err)
}
var expanded []Play
// Process each play
for i := range plays {
if plays[i].ImportPlaybook != "" {
importPath := joinPath(pathDir(path), plays[i].ImportPlaybook)
imported, err := p.parsePlaybook(importPath, seen)
if err != nil {
return nil, coreerr.E("Parser.ParsePlaybook", sprintf("expand import_playbook %d", i), err)
}
expanded = append(expanded, imported...)
continue
}
if err := p.processPlay(&plays[i]); err != nil {
return nil, coreerr.E("Parser.ParsePlaybook", sprintf("process play %d", i), err)
}
expanded = append(expanded, plays[i])
}
return plays, nil
return expanded, nil
}
// ParsePlaybookIter returns an iterator for plays in an Ansible playbook file.

View file

@ -1,6 +1,7 @@
package ansible
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
@ -74,6 +75,54 @@ func TestParser_ParsePlaybook_Good_MultiplePlays(t *testing.T) {
assert.Equal(t, "local", plays[1].Connection)
}
func TestParser_ParsePlaybook_Good_ImportPlaybook(t *testing.T) {
dir := t.TempDir()
mainPath := joinPath(dir, "site.yml")
importDir := joinPath(dir, "plays")
importPath := joinPath(importDir, "web.yml")
yamlMain := `---
- name: Before import
hosts: all
tasks:
- name: Say before
debug:
msg: "before"
- import_playbook: plays/web.yml
- name: After import
hosts: all
tasks:
- name: Say after
debug:
msg: "after"
`
yamlImported := `---
- name: Imported play
hosts: webservers
tasks:
- name: Say imported
debug:
msg: "imported"
`
require.NoError(t, os.MkdirAll(importDir, 0755))
require.NoError(t, writeTestFile(mainPath, []byte(yamlMain), 0644))
require.NoError(t, writeTestFile(importPath, []byte(yamlImported), 0644))
p := NewParser(dir)
plays, err := p.ParsePlaybook(mainPath)
require.NoError(t, err)
require.Len(t, plays, 3)
assert.Equal(t, "Before import", plays[0].Name)
assert.Equal(t, "Imported play", plays[1].Name)
assert.Equal(t, "After import", plays[2].Name)
assert.Equal(t, "webservers", plays[1].Hosts)
assert.Len(t, plays[1].Tasks, 1)
assert.Equal(t, "Say imported", plays[1].Tasks[0].Name)
}
func TestParser_ParsePlaybook_Good_WithVars(t *testing.T) {
dir := t.TempDir()
path := joinPath(dir, "playbook.yml")

View file

@ -21,6 +21,7 @@ type Playbook struct {
type Play struct {
Name string `yaml:"name"`
Hosts string `yaml:"hosts"`
ImportPlaybook string `yaml:"import_playbook,omitempty"`
Connection string `yaml:"connection,omitempty"`
Become bool `yaml:"become,omitempty"`
BecomeUser string `yaml:"become_user,omitempty"`