feat: support pre_install & post_install scripts by global config (#2872)

This commit is contained in:
William Wang 2025-11-30 22:02:57 +08:00 committed by GitHub
parent bd780ee397
commit 86dbf89026
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 65 additions and 20 deletions

View File

@ -20,8 +20,19 @@
mode: 0755
register: post_install_copy_result
- name: Post | Copy post-installation config scripts to remote hosts
copy:
src: >-
{{ .scripts_dir }}/{{ .item.script }}
dest: >-
/etc/kubekey/scripts/post_install_{{ .item.script }}
mode: 0755
loop: "{{ .post_install | toJson }}"
when: "{{ getStringSlice .groups .item.group | default list | has .inventory_hostname }}"
register: post_install_config_copy_result
- name: Post | Execute post-installation scripts on remote hosts
when: .post_install_copy_result.error | empty
when: or (.post_install_copy_result.error | empty) (and .post_install_config_copy_result (.post_install_config_copy_result.error | empty))
command: |
for file in /etc/kubekey/scripts/post_install_*.sh; do
if [ -f "$file" ]; then
@ -29,4 +40,4 @@
chmod +x "$file"
"$file"
fi
done
done

View File

@ -13,8 +13,19 @@
mode: 0755
register: pre_install_copy_result
- name: Pre | Copy pre-installation config scripts to remote hosts
copy:
src: >-
{{ .work_dir }}/scripts/{{ .item.script }}
dest: >-
/etc/kubekey/scripts/pre_install_{{ .item.script }}
mode: 0755
loop: "{{ .pre_install | toJson }}"
when: "{{ getStringSlice .groups .item.group | default list | has .inventory_hostname }}"
register: pre_install_config_copy_result
- name: Pre | Execute pre-installation scripts on remote hosts
when: .pre_install_copy_result.error | empty
when: or (.pre_install_copy_result.error | empty) (and .pre_install_config_copy_result (.pre_install_config_copy_result.error | empty))
command: |
for file in /etc/kubekey/scripts/pre_install_*.sh; do
if [ -f "$file" ]; then

View File

@ -2,7 +2,7 @@ work_dir: /root/kubekey
binary_dir: >-
{{ .work_dir }}/kubekey
scripts_dir: >-
{{ .binary_dir }}/scripts
{{ .work_dir }}/scripts
tmp_dir: /tmp/kubekey
# Mapping of common machine architecture names to their standard forms

View File

@ -30,6 +30,7 @@ func funcMap() template.FuncMap {
f["subtractList"] = subtractList
f["fileExist"] = fileExist
f["unquote"] = unquote
f["getStringSlice"] = getStringSlice
return f
}
@ -131,3 +132,10 @@ func unquote(input any) string {
}
return output
}
func getStringSlice(d map[string][]string, key string) []string {
if val, ok := d[key]; ok {
return val
}
return nil
}

View File

@ -252,6 +252,16 @@ func TestParseValue(t *testing.T) {
},
excepted: []byte("bar"),
},
{
name: "get string slice from dictionary",
input: "{{ getStringSlice .foo \"foo\" }}",
variable: map[string]any{
"foo": map[string][]string{
"foo": {"bar1", "bar2"},
},
},
excepted: []byte("[bar1 bar2]"),
},
{
name: "multi level 2",
input: "{{ index .foo \"foo\" }}",

View File

@ -166,21 +166,19 @@ func (e *taskExecutor) execTaskHost(i int, h string) func(ctx context.Context) {
resErr = errors.Errorf("host: %s variable is not a map", h)
return
}
// check when condition
if skip, err := e.dealWhen(had); err != nil {
resErr = err
return
} else if skip {
stdout = modules.StdoutSkip
return
}
// execute module in loop with loop item.
// if loop is empty. execute once, and the item is null
for _, item := range e.dealLoop(had) {
resErr = e.executeModule(ctx, e.task, item, h, &stdout, &stderr)
if resErr != nil {
resSkip, exeErr := e.executeModule(ctx, e.task, item, h, &stdout, &stderr)
if exeErr != nil {
resErr = exeErr
break
}
// loop execute once, skip task
if item == nil && resSkip {
stdout = modules.StdoutSkip
return
}
}
}
}
@ -260,18 +258,18 @@ func (e *taskExecutor) execTaskHostLogs(ctx context.Context, h string, stdout, _
}
// executeModule executes a single module task on a specific host.
func (e *taskExecutor) executeModule(ctx context.Context, task *kkcorev1alpha1.Task, item any, host string, stdout, stderr *string) (resErr error) {
func (e *taskExecutor) executeModule(ctx context.Context, task *kkcorev1alpha1.Task, item any, host string, stdout, stderr *string) (resSkip bool, resErr error) {
// Set loop item variable if one was provided
if item != nil {
// Convert item to runtime variable
node, err := converter.ConvertMap2Node(map[string]any{_const.VariableItem: item})
if err != nil {
return errors.Wrap(err, "failed to convert loop item")
return false, errors.Wrap(err, "failed to convert loop item")
}
// Merge item into host's runtime variables
if err := e.variable.Merge(variable.MergeRuntimeVariable([]yaml.Node{node}, host)); err != nil {
return errors.Wrap(err, "failed to set loop item to variable")
return false, errors.Wrap(err, "failed to set loop item to variable")
}
// Clean up loop item variable after execution
@ -295,13 +293,20 @@ func (e *taskExecutor) executeModule(ctx context.Context, task *kkcorev1alpha1.T
// Get all variables for this host, including any loop item
ha, err := e.variable.Get(variable.GetAllVariable(host))
if err != nil {
return errors.Wrapf(err, "failed to get host %s variable", host)
return false, errors.Wrapf(err, "failed to get host %s variable", host)
}
// Convert host variables to map type
had, ok := ha.(map[string]any)
if !ok {
return errors.Wrapf(err, "host %s variable is not a map", host)
return false, errors.Wrapf(err, "host %s variable is not a map", host)
}
// check when condition
if skip, err := e.dealWhen(had); err != nil {
return false, err
} else if skip {
return true, nil
}
// Execute the actual module with the prepared context
@ -313,7 +318,7 @@ func (e *taskExecutor) executeModule(ctx context.Context, task *kkcorev1alpha1.T
Playbook: *e.playbook,
LogOutput: e.logOutput,
})
return e.dealFailedWhen(had, resErr)
return false, e.dealFailedWhen(had, resErr)
}
// dealLoop parses the loop specification into a slice of items to iterate over.