feature: support task include vars (#2717)

feature: support task include vars



feature: support task include vars



feature: support task include vars



feature: support task include vars



feature: support task include vars



feature: support task include vars



feature: support task include vars



feature: support task include vars



feature: support task include vars



feature: support task include vars



feature: support task include vars



feature: support task include vars

Signed-off-by: xuesongzuo@yunify.com <xuesongzuo@yunify.com>
This commit is contained in:
zuoxuesong-worker 2025-08-22 09:25:53 +08:00 committed by GitHub
parent aaae2f6634
commit a8e533e608
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 190 additions and 1 deletions

View File

@ -0,0 +1,18 @@
# include_vars 模块
include_vars模块允许用户将变量设置到指定的主机中生效。
## 参数
| 参数 | 说明 | 类型 | 必填 | 默认值 |
|------|------|------|------|-------|
| include_vars | 引用的文件地址类型必须是yaml/yml | 字符串 | 是 | - |
## 使用示例
1. 设置字符串参数
```yaml
- name: set other var file
include_vars: "{{ .os.architecture }}/var.yaml"
```

View File

@ -138,3 +138,6 @@ const RuntimePlaybookVariableDir = "variable"
// KubernetesDir represents the remote host directory for each Kubernetes connection created during playbook execution.
const KubernetesDir = "kubernetes"
// VarsDir is a directory name for vars
const VarsDir = "vars"

View File

@ -182,7 +182,7 @@ func (ca copyArgs) handleAbsolutePath(ctx context.Context, conn connector.Connec
func (ca copyArgs) handleRelativePath(ctx context.Context, options ExecOptions, conn connector.Connector) (string, string, error) {
pj, err := project.New(ctx, options.Playbook, false)
if err != nil {
return StdoutFailed, "failed to get playbook", err
return StdoutFailed, StderrGetPlaybook, err
}
relPath := filepath.Join(options.Task.Annotations[kkcorev1alpha1.TaskAnnotationRelativePath], _const.ProjectRolesFilesDir, ca.src)

View File

@ -0,0 +1,96 @@
package modules
import (
"context"
"os"
"path/filepath"
"github.com/cockroachdb/errors"
kkcorev1alpha1 "github.com/kubesphere/kubekey/api/core/v1alpha1"
"gopkg.in/yaml.v3"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
_const "github.com/kubesphere/kubekey/v4/pkg/const"
"github.com/kubesphere/kubekey/v4/pkg/project"
"github.com/kubesphere/kubekey/v4/pkg/utils"
"github.com/kubesphere/kubekey/v4/pkg/variable"
)
/*
Module: include_vars
Description:
- Adds or updates host variables for one or more hosts.
Example Usage in Playbook Task:
- name: Add custom variables to hosts
include_vars: path/file.yaml
Return Values:
- On success: returns empty stdout and stderr.
- On failure: returns error message in stderr.
*/
type includeVarsArgs struct {
includeVars string
}
// ModuleIncludeVars handle the "include_vars" module ,add other var files into playbook
func ModuleIncludeVars(ctx context.Context, options ExecOptions) (string, string, error) {
// get host variable
vd, err := options.getAllVariables()
if err != nil {
return StdoutFailed, StderrGetHostVariable, err
}
// check args
includeVarsByte, err := variable.Extension2String(vd, options.Args)
if err != nil {
return StdoutFailed, StderrParseArgument, err
}
if len(includeVarsByte) == 0 {
return StdoutFailed, "input file path wrong", errors.New("input value can not be empty")
}
arg := includeVarsArgs{
includeVars: string(includeVarsByte),
}
if !filepath.IsLocal(arg.includeVars) {
return StdoutFailed, "can not read remote file", errors.New("can not read remote file")
}
if !utils.HasSuffixIn(arg.includeVars, []string{"yaml", "yml"}) {
return StdoutFailed, "input file type wrong", errors.New("input file type wrong")
}
var includeVarsFileContent []byte
if filepath.IsAbs(arg.includeVars) {
includeVarsFileContent, err = os.ReadFile(arg.includeVars)
if err != nil {
return StdoutFailed, "failed to read var file", errors.Wrap(err, "failed to read include variables file")
}
} else {
pj, err := project.New(ctx, options.Playbook, false)
if err != nil {
return StdoutFailed, StderrGetPlaybook, err
}
fileReadPath := filepath.Join(options.Task.Annotations[kkcorev1alpha1.TaskAnnotationRelativePath], _const.VarsDir, arg.includeVars)
includeVarsFileContent, err = pj.ReadFile(fileReadPath)
if err != nil {
return StdoutFailed, "failed to read var file", err
}
}
var node yaml.Node
// Unmarshal the YAML document into a root node.
if err := yaml.Unmarshal(includeVarsFileContent, &node); err != nil {
return StdoutFailed, StderrParseArgument, errors.Wrap(err, "failed to failed to unmarshal YAML")
}
if err := options.Variable.Merge(variable.MergeRuntimeVariable([]yaml.Node{node}, options.Host)); err != nil {
return StdoutFailed, StderrParseArgument, errors.Wrap(err, "failed to merge runtime variables")
}
return StdoutSuccess, "", nil
}
func init() {
utilruntime.Must(RegisterModule("include_vars", ModuleIncludeVars))
}

View File

@ -0,0 +1,70 @@
package modules
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/runtime"
)
func TestModuleIncludeVars(t *testing.T) {
testcases := []struct {
name string
opt ExecOptions
exceptStdout string
}{
{
name: "include remote path",
opt: ExecOptions{
Args: runtime.RawExtension{
Raw: []byte(`{
"include_vars": "http://127.0.0.1:8080/include_vars",
}`),
},
Variable: newTestVariable(nil, nil),
},
exceptStdout: StdoutFailed,
}, {
name: "include empty path",
opt: ExecOptions{
Args: runtime.RawExtension{
Raw: []byte(`{
"include_vars": "",
}`),
},
Variable: newTestVariable(nil, nil),
},
exceptStdout: StdoutFailed,
}, {
name: "include path not exist",
opt: ExecOptions{
Args: runtime.RawExtension{
Raw: []byte(`{
"include_vars": "/path/not/exist/not_exist.yaml",
}`),
},
Variable: newTestVariable(nil, nil),
},
exceptStdout: StdoutFailed,
}, {
name: "include path not yaml",
opt: ExecOptions{
Args: runtime.RawExtension{
Raw: []byte(`{
"include_vars": "/path/some/exist/exist.yyy",
}`),
},
Variable: newTestVariable(nil, nil),
},
exceptStdout: StdoutFailed,
},
}
for _, testcase := range testcases {
t.Run(testcase.name, func(t *testing.T) {
stdout, _, _ := ModuleIncludeVars(context.Background(), testcase.opt)
assert.Equal(t, testcase.exceptStdout, stdout)
})
}
}

View File

@ -46,6 +46,8 @@ const (
StderrParseArgument = "failed to parse argument"
// StderrUnsupportArgs is returned when the provided arguments are not supported.
StderrUnsupportArgs = "unsupport args"
// StderrGetPlaybook is returned when get playbook error
StderrGetPlaybook = "failed to get playbook"
)
// ModuleExecFunc defines the function signature for executing a module.