Skip to content

Commit d8eb7b1

Browse files
committed
feat: debug support msg and var
Signed-off-by: redscholar <[email protected]>
1 parent 710ae85 commit d8eb7b1

5 files changed

Lines changed: 275 additions & 31 deletions

File tree

api/project/v1/playbook.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func IsTmplSyntax(s string) bool {
5858
// ParseTmplSyntax wraps a string with template syntax delimiters "{{" and "}}"
5959
// to make it a valid Go template expression
6060
func ParseTmplSyntax(s string) string {
61-
return "{{ " + s + "}}"
61+
return "{{" + s + "}}"
6262
}
6363

6464
func TrimTmplSyntax(s string) string {

docs/en/custom/modules/debug.md

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ Print variables or strings for troubleshooting.
66

77
| Parameter | Description | Type | Required | Default |
88
|-----------|-------------|------|----------|---------|
9-
| msg | Content to output, supports [template syntax](../101-syntax.md) | string | Yes | - |
9+
| msg | Content to output, supports [template syntax](../101-syntax.md) with filters | string | No | - |
10+
| var | Variable path to output, supports template syntax `{{ .var }}` or simple path `.var` | string | No | - |
11+
12+
**Note:** One of `msg` or `var` must be specified.
1013

1114
## Examples
1215

@@ -25,7 +28,22 @@ DEBUG:
2528
I'm kubekey
2629
```
2730

28-
**2. Print map**
31+
**2. Print with template filter**
32+
33+
```yaml
34+
- name: debug with default filter
35+
debug:
36+
msg: "Name is {{ .name | default \"unknown\" }}"
37+
```
38+
39+
When `name` is not defined, output:
40+
41+
```text
42+
DEBUG:
43+
Name is unknown
44+
```
45+
46+
**3. Print map**
2947

3048
```yaml
3149
- name: debug map
@@ -43,7 +61,7 @@ DEBUG:
4361
}
4462
```
4563

46-
**3. Print array**
64+
**4. Print array**
4765

4866
```yaml
4967
- name: debug array
@@ -61,3 +79,48 @@ DEBUG:
6179
"2"
6280
]
6381
```
82+
83+
**5. Print variable using var field (template syntax)**
84+
85+
```yaml
86+
- name: debug variable with template
87+
debug:
88+
var: "{{ .config.version }}"
89+
```
90+
91+
When `config.version` is `v1.0.0`, output:
92+
93+
```text
94+
DEBUG:
95+
"v1.0.0"
96+
```
97+
98+
**6. Print variable using var field (simple path)**
99+
100+
```yaml
101+
- name: debug variable with path
102+
debug:
103+
var: ".config.version"
104+
```
105+
106+
When `config.version` is `v1.0.0`, output:
107+
108+
```text
109+
DEBUG:
110+
"v1.0.0"
111+
```
112+
113+
**7. Print nested variable**
114+
115+
```yaml
116+
- name: debug nested variable
117+
debug:
118+
var: ".data.level1.level2"
119+
```
120+
121+
When `data.level1.level2` is `value`, output:
122+
123+
```text
124+
DEBUG:
125+
"value"
126+
```

docs/zh/custom/modules/debug.md

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66

77
| 参数 | 说明 | 类型 | 必填 | 默认值 |
88
|------|------|------|------|--------|
9-
| msg | 要输出的内容,支持 [模板语法](../101-syntax.md) | 字符串 || - |
9+
| msg | 要输出的内容,支持带过滤器的 [模板语法](../101-syntax.md) | 字符串 || - |
10+
| var | 要输出的变量路径,支持模板语法 `{{ .var }}` 或简单路径 `.var` | 字符串 || - |
11+
12+
**注意:** `msg``var` 至少需要一个。
1013

1114
## 示例
1215

@@ -25,7 +28,22 @@ DEBUG:
2528
I'm kubekey
2629
```
2730

28-
**2. 打印 map**
31+
**2. 使用模板过滤器打印**
32+
33+
```yaml
34+
- name: debug with default filter
35+
debug:
36+
msg: "Name is {{ .name | default \"unknown\" }}"
37+
```
38+
39+
当 `name` 未定义时,输出:
40+
41+
```text
42+
DEBUG:
43+
Name is unknown
44+
```
45+
46+
**3. 打印 map**
2947

3048
```yaml
3149
- name: debug map
@@ -43,7 +61,7 @@ DEBUG:
4361
}
4462
```
4563

46-
**3. 打印数组**
64+
**4. 打印数组**
4765

4866
```yaml
4967
- name: debug array
@@ -61,3 +79,48 @@ DEBUG:
6179
"2"
6280
]
6381
```
82+
83+
**5. 使用 var 字段打印变量(模板语法)**
84+
85+
```yaml
86+
- name: debug variable with template
87+
debug:
88+
var: "{{ .config.version }}"
89+
```
90+
91+
当 `config.version` 为 `v1.0.0` 时,输出:
92+
93+
```text
94+
DEBUG:
95+
"v1.0.0"
96+
```
97+
98+
**6. 使用 var 字段打印变量(简单路径)**
99+
100+
```yaml
101+
- name: debug variable with path
102+
debug:
103+
var: ".config.version"
104+
```
105+
106+
当 `config.version` 为 `v1.0.0` 时,输出:
107+
108+
```text
109+
DEBUG:
110+
"v1.0.0"
111+
```
112+
113+
**7. 打印嵌套变量**
114+
115+
```yaml
116+
- name: debug nested variable
117+
debug:
118+
var: ".data.level1.level2"
119+
```
120+
121+
当 `data.level1.level2` 为 `value` 时,输出:
122+
123+
```text
124+
DEBUG:
125+
"value"
126+
```

pkg/modules/debug/debug.go

Lines changed: 90 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/cockroachdb/errors"
2828
kkprojectv1 "github.com/kubesphere/kubekey/api/project/v1"
2929

30+
"github.com/kubesphere/kubekey/v4/pkg/converter/tmpl"
3031
"github.com/kubesphere/kubekey/v4/pkg/modules/internal"
3132
"github.com/kubesphere/kubekey/v4/pkg/variable"
3233
)
@@ -39,8 +40,10 @@ Configuration:
3940
Users can specify either a message or a variable path to debug.
4041
4142
debug:
42-
msg: "message" # optional: direct message to print
43-
msg: "{{ .var }}" # optional: template syntax to print variable value
43+
msg: "message" # optional: direct message to print
44+
msg: "value is {{ .var }}" # optional: template syntax with filters
45+
var: "{{ .var }}" # optional: template syntax to get variable
46+
var: ".var" # optional: simple variable path
4447
4548
Usage Examples in Playbook Tasks:
4649
1. Print direct message:
@@ -51,11 +54,27 @@ Usage Examples in Playbook Tasks:
5154
register: debug_result
5255
```
5356
54-
2. Print variable value:
57+
2. Print message with template:
58+
```yaml
59+
- name: Debug with template
60+
debug:
61+
msg: "Version is {{ .config.version | default \"unknown\" }}"
62+
register: debug_result
63+
```
64+
65+
3. Print variable value:
5566
```yaml
5667
- name: Debug variable
5768
debug:
58-
msg: "{{ .config.version }}"
69+
var: "{{ .config.version }}"
70+
register: var_debug
71+
```
72+
73+
4. Print variable with simple path:
74+
```yaml
75+
- name: Debug variable simple
76+
debug:
77+
var: ".config.version"
5978
register: var_debug
6079
```
6180
@@ -72,40 +91,89 @@ func ModuleDebug(_ context.Context, opts internal.ExecOptions) (string, string,
7291
return internal.StdoutFailed, internal.StderrGetHostVariable, err
7392
}
7493
args := variable.Extension2Variables(opts.Args)
75-
v := reflect.ValueOf(args["msg"])
76-
switch v.Kind() {
77-
case reflect.Invalid:
78-
return internal.StdoutFailed, internal.StderrUnsupportArgs, errors.New("\"msg\" is not found")
94+
95+
// Handle "var" field - for getting variable values
96+
if v, ok := args["var"]; ok {
97+
return handleVarField(v, ha, opts.LogOutput)
98+
}
99+
100+
// Handle "msg" field - for printing messages with template support
101+
if v, ok := args["msg"]; ok {
102+
return handleMsgField(v, ha, opts.LogOutput)
103+
}
104+
105+
return internal.StdoutFailed, internal.StderrUnsupportArgs, errors.New("either \"msg\" or \"var\" must be specified")
106+
}
107+
108+
// handleVarField handles the "var" field for variable debugging
109+
// Supports both template syntax "{{ .var }}" and simple path ".var"
110+
func handleVarField(v any, ha map[string]any, output io.Writer) (string, string, error) {
111+
rv := reflect.ValueOf(v)
112+
switch rv.Kind() {
79113
case reflect.String:
80-
if !kkprojectv1.IsTmplSyntax(v.String()) {
81-
return formatOutput([]byte(v.String()), opts.LogOutput), "", nil
114+
val := rv.String()
115+
// Check if it's template syntax
116+
if kkprojectv1.IsTmplSyntax(val) {
117+
val = kkprojectv1.TrimTmplSyntax(val)
82118
}
83-
val := kkprojectv1.TrimTmplSyntax(v.String())
119+
// Ensure it starts with "."
84120
if !strings.HasPrefix(val, ".") {
85-
return internal.StdoutFailed, internal.StderrUnsupportArgs, errors.New("error tmpl value syntax")
121+
return internal.StdoutFailed, internal.StderrUnsupportArgs, errors.New("variable path must start with '.'")
86122
}
87-
data, err := variable.PrintVar(ha, strings.Split(val, ".")[1:]...)
123+
// Remove leading "." and split path
124+
path := strings.TrimPrefix(val, ".")
125+
if path == "" {
126+
return internal.StdoutFailed, internal.StderrUnsupportArgs, errors.New("variable path cannot be empty")
127+
}
128+
data, err := variable.PrintVar(ha, strings.Split(path, ".")...)
88129
if err != nil {
89130
return internal.StdoutFailed, internal.StderrParseArgument, err
90131
}
91-
return formatOutput(data, opts.LogOutput), "", nil
132+
return formatOutput(data, output), "", nil
92133
default:
93-
// do not parse by ctx
94-
data, err := json.Marshal(v.Interface())
95-
if err != nil {
96-
return internal.StdoutFailed, "failed to marshal value to json", err
134+
// For non-string types, pass directly to formatOutput for pretty printing
135+
return formatOutput(rv.Interface(), output), "", nil
136+
}
137+
}
138+
139+
// handleMsgField handles the "msg" field for message debugging
140+
// Supports template syntax with filters like "a is {{ .var | default 'b' }}"
141+
func handleMsgField(v any, ha map[string]any, output io.Writer) (string, string, error) {
142+
rv := reflect.ValueOf(v)
143+
switch rv.Kind() {
144+
case reflect.String:
145+
msg := rv.String()
146+
// If it contains template syntax, parse it
147+
if kkprojectv1.IsTmplSyntax(msg) {
148+
parsed, err := tmpl.Parse(ha, msg)
149+
if err != nil {
150+
return internal.StdoutFailed, internal.StderrParseArgument, err
151+
}
152+
return formatOutput(string(parsed), output), "", nil
97153
}
98-
return formatOutput(data, opts.LogOutput), "", nil
154+
// Direct message without template
155+
return formatOutput(msg, output), "", nil
156+
default:
157+
// For non-string types, pass directly to formatOutput for pretty printing
158+
return formatOutput(rv.Interface(), output), "", nil
99159
}
100160
}
101161

102162
// formatOutput formats data as pretty JSON and logs it with DEBUG prefix if output is provided
103163
// Returns the formatted string
104164
func formatOutput(data any, output io.Writer) string {
105165
var msg string
106-
prettyJSON, err := json.MarshalIndent(data, "", " ")
107-
if err == nil {
108-
msg = string(prettyJSON)
166+
// Handle string data directly without JSON marshaling
167+
switch v := data.(type) {
168+
case string:
169+
msg = v
170+
case []byte:
171+
msg = string(v)
172+
default:
173+
prettyJSON, err := json.MarshalIndent(v, "", " ")
174+
if err == nil {
175+
msg = string(prettyJSON)
176+
}
109177
}
110178
if output != nil {
111179
_, _ = fmt.Fprintln(output, "DEBUG: \n"+msg) // Ignore error in test context

0 commit comments

Comments
 (0)