// Package vars реализует единственную ответственность: для заданного // хоста построить финальный набор переменных, накатывая слои строго // в порядке приоритета Ansible (упрощённом до того, что реально нужно). package vars import "github.com/vladimir/goherence/internal/parser" // Resolve строит финальные переменные для (хост, роль, play), от самого // низкого приоритета к самому высокому. Порядок совпадает с комментарием // в архитектурном документе — намеренно не абстрагирован дальше, чтобы // последовательность накатывания оставалась видна с первого взгляда. // facts — собранные internal/facts данные о хосте (os_family и // т.п.); в реальном Ansible факты имеют высокий приоритет, но ниже // явных play/role vars и extra-vars — этот порядок здесь и соблюдён. func Resolve( host string, inv *parser.Inventory, play *parser.Play, role *parser.Role, facts map[string]interface{}, extraVars map[string]interface{}, ) map[string]interface{} { result := map[string]interface{}{} if role != nil { merge(result, role.Defaults) // 1. role defaults — самый низкий приоритет } merge(result, inv.GroupVars("all")) // 2. group_vars/all for _, g := range inv.GroupChain(host) { if g == "all" { continue } merge(result, inv.GroupVars(g)) // 3. group_vars/, родители раньше потомков } merge(result, inv.HostVars(host)) // 4. host_vars/ merge(result, facts) // 4.5. факты хоста (os_family и т.п.) if play != nil { merge(result, play.Vars) // 5. play vars } if role != nil { merge(result, role.Vars) // 6. role vars/main.yml } // 7. set_fact / registered vars — накатываются executor'ом во время // выполнения, а не здесь: Resolve вызывается один раз до старта таска. merge(result, extraVars) // 8. extra-vars — самый высокий приоритет, всегда последний return result } // merge — намеренно плоская перезапись по ключу (dst[k] = src[k]), БЕЗ // рекурсивного deep-merge вложенных map. Ровно так себя ведёт и сам // Ansible по умолчанию (hash_behaviour=replace) — это осознанное решение // для совместимости ожиданий, а не недосмотр. func merge(dst, src map[string]interface{}) { for k, v := range src { dst[k] = v } }