feat: run gather_facts by module (#2588)

Signed-off-by: joyceliu <joyceliu@yunify.com>
This commit is contained in:
liujian 2025-05-23 13:56:10 +08:00 committed by GitHub
parent de5cc690e2
commit 202700fb43
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 131 additions and 32 deletions

View File

@ -4,7 +4,6 @@ import (
"context"
"encoding/json"
"slices"
"time"
"github.com/cockroachdb/errors"
kkcorev1 "github.com/kubesphere/kubekey/api/core/v1"
@ -182,5 +181,5 @@ func (e blockExecutor) dealTask(ctx context.Context, hosts []string, when []stri
return errors.Wrapf(err, "failed to set playbook %q ownerReferences to %q", ctrlclient.ObjectKeyFromObject(e.playbook), block.Name)
}
return (&taskExecutor{option: e.option, task: task, taskRunTimeout: 60 * time.Minute}).Exec(ctx)
return (&taskExecutor{option: e.option, task: task}).Exec(ctx)
}

View File

@ -22,11 +22,12 @@ import (
"github.com/cockroachdb/errors"
kkcorev1 "github.com/kubesphere/kubekey/api/core/v1"
kkcorev1alpha1 "github.com/kubesphere/kubekey/api/core/v1alpha1"
kkprojectv1 "github.com/kubesphere/kubekey/api/project/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog/v2"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
"github.com/kubesphere/kubekey/v4/pkg/connector"
"github.com/kubesphere/kubekey/v4/pkg/converter"
"github.com/kubesphere/kubekey/v4/pkg/project"
"github.com/kubesphere/kubekey/v4/pkg/variable"
@ -199,36 +200,21 @@ func (e playbookExecutor) dealGatherFacts(ctx context.Context, gatherFacts bool,
// skip
return nil
}
dealGatherFactsInHost := func(hostname string) error {
// get host connector
conn, err := connector.NewConnector(hostname, e.variable)
if err != nil {
return err
}
if err := conn.Init(ctx); err != nil {
return err
}
defer conn.Close(ctx)
// run as option
if gf, ok := conn.(connector.GatherFacts); ok {
remoteInfo, err := gf.HostInfo(ctx)
if err != nil {
return err
}
if err := e.variable.Merge(variable.MergeRemoteVariable(remoteInfo, hostname)); err != nil {
return err
}
}
return nil
}
for _, hostname := range hosts {
if err := dealGatherFactsInHost(hostname); err != nil {
return err
}
}
return nil
return (&taskExecutor{option: e.option, task: &kkcorev1alpha1.Task{
ObjectMeta: metav1.ObjectMeta{
GenerateName: e.playbook.Name + "-",
Namespace: e.playbook.Namespace,
},
Spec: kkcorev1alpha1.TaskSpec{
Name: "gather_facts",
Hosts: hosts,
Module: kkcorev1alpha1.Module{
Name: "setup",
},
},
}}).Exec(ctx)
}
// dealSerial "serial" argument in playbook.

View File

@ -39,6 +39,9 @@ type taskExecutor struct {
// Exec creates and executes a task, updating its status and the parent playbook's status.
// It returns an error if the task creation or execution fails.
func (e *taskExecutor) Exec(ctx context.Context) error {
if e.taskRunTimeout == time.Duration(0) {
e.taskRunTimeout = 60 * time.Minute
}
// create task
if err := e.client.Create(ctx, e.task); err != nil {
return errors.Wrapf(err, "failed to create task %q", e.task.Spec.Name)

54
pkg/modules/setup.go Normal file
View File

@ -0,0 +1,54 @@
package modules
import (
"context"
"fmt"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"github.com/kubesphere/kubekey/v4/pkg/connector"
"github.com/kubesphere/kubekey/v4/pkg/variable"
)
/*
Module Setup
This module is used to set up the connection to a remote host and gather facts about it.
It performs the following operations:
1. Establishes a connection to the specified host using the appropriate connector
2. If the connector supports fact gathering (implements GatherFacts interface):
- Retrieves host information
- Merges the remote host information into the variables map
3. Returns success if all operations complete successfully
Usage:
- host: The target host to connect to
- variable: Map of variables to be used for connection and fact gathering
*/
// ModuleSetup establishes a connection to a remote host and gathers facts about it.
// It returns StdoutSuccess if successful, or an error message if any step fails.
func ModuleSetup(ctx context.Context, options ExecOptions) (string, string) {
// get connector
conn, err := getConnector(ctx, options.Host, options.Variable)
if err != nil {
return "", fmt.Sprintf("failed to connector of %q error: %v", options.Host, err)
}
defer conn.Close(ctx)
if gf, ok := conn.(connector.GatherFacts); ok {
remoteInfo, err := gf.HostInfo(ctx)
if err != nil {
return "", err.Error()
}
if err := options.Variable.Merge(variable.MergeRemoteVariable(remoteInfo, options.Host)); err != nil {
return "", err.Error()
}
}
return StdoutSuccess, ""
}
func init() {
utilruntime.Must(RegisterModule("setup", ModuleSetup))
}

57
pkg/modules/setup_test.go Normal file
View File

@ -0,0 +1,57 @@
package modules
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestModuleSetup(t *testing.T) {
testcases := []struct {
name string
opt ExecOptions
ctxFunc func() context.Context
exceptStdout string
exceptStderr string
}{
{
name: "successful setup with fact gathering",
opt: ExecOptions{
Host: "test-host",
Variable: &testVariable{},
},
ctxFunc: func() context.Context {
return context.WithValue(context.Background(), ConnKey, successConnector)
},
exceptStdout: StdoutSuccess,
exceptStderr: "",
},
{
name: "failed connector setup",
opt: ExecOptions{
Host: "invalid-host",
Variable: &testVariable{},
},
ctxFunc: context.Background,
exceptStdout: "",
exceptStderr: "failed to connector of \"invalid-host\" error:",
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
ctx, cancel := context.WithTimeout(tc.ctxFunc(), time.Second*5)
defer cancel()
stdout, stderr := ModuleSetup(ctx, tc.opt)
assert.Contains(t, stdout, tc.exceptStdout)
if tc.exceptStderr != "" {
assert.Contains(t, stderr, tc.exceptStderr)
} else {
assert.Empty(t, stderr)
}
})
}
}