mirror of
https://github.com/kubesphere/kubekey.git
synced 2025-12-25 17:12:50 +00:00
feature: support defaults main dir (#2709)
feature: support defaults main dir feature: support defaults main dir feature: support defaults main dir feature: support defaults main dir feature: support defaults main dir feature: support defaults main dir Signed-off-by: xuesongzuo@yunify.com <xuesongzuo@yunify.com>
This commit is contained in:
parent
b49cfc7fc4
commit
68f3ee1fca
|
|
@ -171,6 +171,14 @@ func GetRoleDefaultsRelPath(baseRole string) []string {
|
|||
}
|
||||
}
|
||||
|
||||
// GetRoleDefaultsRelDirPath returns possible relative dir paths for a role's defaults files
|
||||
// The format follows similar structure to role tasks
|
||||
func GetRoleDefaultsRelDirPath(baseRole string) []string {
|
||||
return []string{
|
||||
filepath.Join(baseRole, _const.ProjectRolesDefaultsDir, "main"),
|
||||
}
|
||||
}
|
||||
|
||||
// GetIncludeTaskRelPath returns possible relative paths for included task files
|
||||
// The format follows PathFormatIncludeTask structure
|
||||
func GetIncludeTaskRelPath(top string, source string, includeTask string) []string {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import (
|
|||
|
||||
_const "github.com/kubesphere/kubekey/v4/pkg/const"
|
||||
"github.com/kubesphere/kubekey/v4/pkg/converter/tmpl"
|
||||
"github.com/kubesphere/kubekey/v4/pkg/utils"
|
||||
"github.com/kubesphere/kubekey/v4/pkg/variable"
|
||||
)
|
||||
|
||||
|
|
@ -169,7 +170,7 @@ func (f *project) loadPlaybook(basePlaybook string) error {
|
|||
// dealImportPlaybook handles the "import_playbook" argument in a play
|
||||
func (f *project) dealImportPlaybook(p kkprojectv1.Play, basePlaybook string) error {
|
||||
if p.ImportPlaybook != "" {
|
||||
importPlaybook := f.getPath(GetImportPlaybookRelPath(basePlaybook, p.ImportPlaybook))
|
||||
importPlaybook, _ := f.getPath(GetImportPlaybookRelPath(basePlaybook, p.ImportPlaybook))
|
||||
if importPlaybook == "" {
|
||||
return errors.Errorf("failed to find import_playbook %q base on %q. it's should be:\n %s", p.ImportPlaybook, basePlaybook, PathFormatImportPlaybook)
|
||||
}
|
||||
|
|
@ -189,7 +190,7 @@ func (f *project) dealVarsFiles(p *kkprojectv1.Play, basePlaybook string) error
|
|||
if err != nil {
|
||||
return errors.Errorf("failed to parse varFile %q", varsFileStr)
|
||||
}
|
||||
file := f.getPath(GetVarsFilesRelPath(basePlaybook, varsFile))
|
||||
file, _ := f.getPath(GetVarsFilesRelPath(basePlaybook, varsFile))
|
||||
if file == "" {
|
||||
return errors.Errorf("failed to find vars_files %q base on %q. it's should be:\n %s", varsFile, basePlaybook, PathFormatVarsFile)
|
||||
}
|
||||
|
|
@ -216,12 +217,12 @@ func (f *project) dealVarsFiles(p *kkprojectv1.Play, basePlaybook string) error
|
|||
}
|
||||
|
||||
func (f *project) dealRole(role *kkprojectv1.Role, basePlaybook string) error {
|
||||
baseRole := f.getPath(GetRoleRelPath(basePlaybook, role.Role))
|
||||
baseRole, _ := f.getPath(GetRoleRelPath(basePlaybook, role.Role))
|
||||
if baseRole == "" {
|
||||
return errors.Errorf("failed to find role %q base on %q. it's should be:\n %s", role.Role, basePlaybook, PathFormatRole)
|
||||
}
|
||||
// deal dependency
|
||||
if meta := f.getPath(GetRoleMetaRelPath(baseRole)); meta != "" {
|
||||
if meta, _ := f.getPath(GetRoleMetaRelPath(baseRole)); meta != "" {
|
||||
mdata, err := fs.ReadFile(f.FS, meta)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to read role meta file %q", meta)
|
||||
|
|
@ -238,7 +239,7 @@ func (f *project) dealRole(role *kkprojectv1.Role, basePlaybook string) error {
|
|||
}
|
||||
}
|
||||
// deal tasks
|
||||
if task := f.getPath(GetRoleTaskRelPath(baseRole)); task != "" {
|
||||
if task, _ := f.getPath(GetRoleTaskRelPath(baseRole)); task != "" {
|
||||
rdata, err := fs.ReadFile(f.FS, task)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to read file %q", task)
|
||||
|
|
@ -250,29 +251,48 @@ func (f *project) dealRole(role *kkprojectv1.Role, basePlaybook string) error {
|
|||
role.Block = blocks
|
||||
}
|
||||
// deal defaults (optional)
|
||||
if defaults := f.getPath(GetRoleDefaultsRelPath(baseRole)); defaults != "" {
|
||||
if defaults, _ := f.getPath(GetRoleDefaultsRelPath(baseRole)); defaults != "" {
|
||||
data, err := fs.ReadFile(f.FS, defaults)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to read defaults variable file %q", defaults)
|
||||
}
|
||||
|
||||
var node yaml.Node
|
||||
// Unmarshal the YAML document into a root node.
|
||||
if err := yaml.Unmarshal(data, &node); err != nil {
|
||||
return errors.Wrap(err, "failed to unmarshal YAML")
|
||||
err = f.combineRoleVars(role, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if node.Kind != yaml.DocumentNode || len(node.Content) != 1 {
|
||||
return errors.Errorf("unsupport vars_files format. it should be single map file")
|
||||
}
|
||||
// combine map node
|
||||
if node.Content[0].Kind == yaml.MappingNode {
|
||||
// skip empty file
|
||||
role.Vars = *variable.CombineMappingNode(&role.Vars, node.Content[0])
|
||||
}
|
||||
if dirDefaults, info := f.getPath(GetRoleDefaultsRelDirPath(baseRole)); dirDefaults != "" {
|
||||
// only handle [roles]/defaults/main directory,if file found but not a directory,skip file
|
||||
if info.IsDir() {
|
||||
err := utils.ReadDirFiles(f.FS, dirDefaults, func(data []byte) error {
|
||||
return f.combineRoleVars(role, data)
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to read defaults variable file %q", dirDefaults)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *project) combineRoleVars(role *kkprojectv1.Role, content []byte) error {
|
||||
var node yaml.Node
|
||||
// Unmarshal the YAML document into a root node.
|
||||
if err := yaml.Unmarshal(content, &node); err != nil {
|
||||
return errors.Wrap(err, "failed to unmarshal YAML")
|
||||
}
|
||||
if node.Kind != yaml.DocumentNode || len(node.Content) != 1 {
|
||||
return errors.Errorf("unsupport vars_files format. it should be single map file")
|
||||
}
|
||||
// combine map node
|
||||
if node.Content[0].Kind == yaml.MappingNode {
|
||||
// skip empty file
|
||||
role.Vars = *variable.CombineMappingNode(&role.Vars, node.Content[0])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// dealRoleTask recursively processes the tasks for a given role and its dependencies.
|
||||
// It ensures that all dependent roles are processed before handling the current role's tasks.
|
||||
func (f *project) dealRoleTask(role *kkprojectv1.Role, basePlaybook string) error {
|
||||
|
|
@ -282,7 +302,7 @@ func (f *project) dealRoleTask(role *kkprojectv1.Role, basePlaybook string) erro
|
|||
}
|
||||
}
|
||||
// Get the base path for the current role
|
||||
baseRole := f.getPath(GetRoleRelPath(basePlaybook, role.Role))
|
||||
baseRole, _ := f.getPath(GetRoleRelPath(basePlaybook, role.Role))
|
||||
// Process the tasks for the current role
|
||||
return f.dealBlock(baseRole, filepath.Join(baseRole, _const.ProjectRolesTasksDir), role.Block)
|
||||
}
|
||||
|
|
@ -304,7 +324,7 @@ func (f *project) dealBlock(top string, source string, blocks []kkprojectv1.Bloc
|
|||
}
|
||||
case block.IncludeTasks != "": // it's an include_tasks directive
|
||||
// Resolve the path to the include_tasks file
|
||||
includeTask := f.getPath(GetIncludeTaskRelPath(top, source, block.IncludeTasks))
|
||||
includeTask, _ := f.getPath(GetIncludeTaskRelPath(top, source, block.IncludeTasks))
|
||||
if includeTask == "" {
|
||||
return errors.Errorf("failed to find include_task %q base on %q. it's should be:\n %s", block.IncludeTasks, source, PathFormatIncludeTask)
|
||||
}
|
||||
|
|
@ -336,12 +356,12 @@ func (f *project) dealBlock(top string, source string, blocks []kkprojectv1.Bloc
|
|||
}
|
||||
|
||||
// getPath returns the first valid path from a list of possible paths
|
||||
func (f *project) getPath(paths []string) string {
|
||||
func (f *project) getPath(paths []string) (string, fs.FileInfo) {
|
||||
for _, path := range paths {
|
||||
if _, err := fs.Stat(f.FS, path); err == nil {
|
||||
return path
|
||||
if info, err := fs.Stat(f.FS, path); err == nil {
|
||||
return path, info
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
return "", nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ReadDirFiles read all file in input fs and dir
|
||||
func ReadDirFiles(fsys fs.FS, dir string, handler func(data []byte) error) error {
|
||||
d, err := fsys.Open(dir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open path %s with error: %w", dir, err)
|
||||
}
|
||||
defer d.Close()
|
||||
entries, err := d.(fs.ReadDirFile).ReadDir(-1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read dir %s failed with error: %w", dir, err)
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
// skip dir
|
||||
continue
|
||||
}
|
||||
if !HasSuffixIn(entry.Name(), []string{"yaml", "yml"}) {
|
||||
continue
|
||||
}
|
||||
filePath := dir + "/" + entry.Name()
|
||||
if dir == "." {
|
||||
filePath = entry.Name()
|
||||
}
|
||||
// open file
|
||||
file, err := fsys.Open(filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open file %q with error: %w", filePath, err)
|
||||
}
|
||||
// read file content
|
||||
content, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read file %q failed with error: %w", filePath, err)
|
||||
}
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("close file %q failed with error: %w", filePath, err)
|
||||
}
|
||||
err = handler(content)
|
||||
if err != nil {
|
||||
return fmt.Errorf("handle file %q failed with error: %w", filePath, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasSuffixIn check input string a end with one of slice b
|
||||
func HasSuffixIn(a string, b []string) bool {
|
||||
for _, suffix := range b {
|
||||
if strings.HasSuffix(a, suffix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Loading…
Reference in New Issue