mirror of
https://github.com/kubesphere/kubekey.git
synced 2025-12-25 17:12:50 +00:00
fix: playbook delete is error (#2654)
Signed-off-by: joyceliu <joyceliu@yunify.com>
This commit is contained in:
parent
b68c73de2d
commit
39657b3dd9
|
|
@ -1,23 +0,0 @@
|
|||
package v1
|
||||
|
||||
import (
|
||||
"github.com/cockroachdb/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
const PlaybookFieldPlaybook = "spec.playbook"
|
||||
|
||||
// AddConversionFuncs adds the conversion functions to the given scheme.
|
||||
// NOTE: ownerReferences:playbook is valid in proxy client.
|
||||
func AddConversionFuncs(scheme *runtime.Scheme) error {
|
||||
return scheme.AddFieldLabelConversionFunc(
|
||||
SchemeGroupVersion.WithKind("Playbook"),
|
||||
func(label, value string) (string, string, error) {
|
||||
if label == PlaybookFieldPlaybook {
|
||||
return label, value, nil
|
||||
}
|
||||
|
||||
return "", "", errors.Errorf("field label %q not supported for Playbook", label)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package v1alpha1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
// supportedFields defines the set of field labels that are allowed for field selector queries
|
||||
// on the Task resource. Only these fields can be used in field selectors for filtering.
|
||||
var supportedFields = sets.NewString(
|
||||
"metadata.name",
|
||||
"metadata.namespace",
|
||||
"playbook.name",
|
||||
"playbook.uid",
|
||||
)
|
||||
|
||||
// RegisterFieldLabelConversion registers a field label conversion function for the Task resource.
|
||||
// This function ensures that only supported field labels can be used in field selectors.
|
||||
// If an unsupported field label is used, an error is returned.
|
||||
func RegisterFieldLabelConversion(scheme *runtime.Scheme) error {
|
||||
return scheme.AddFieldLabelConversionFunc(
|
||||
SchemeGroupVersion.WithKind("Task"),
|
||||
func(label, value string) (string, string, error) {
|
||||
if !supportedFields.Has(label) {
|
||||
return "", "", fmt.Errorf("field label %q is not supported", label)
|
||||
}
|
||||
return label, value, nil
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -55,5 +55,7 @@ func newScheme() *runtime.Scheme {
|
|||
utilruntime.Must(clusterv1beta1.AddToScheme(s))
|
||||
utilruntime.Must(kubeadmcpv1beta1.AddToScheme(s))
|
||||
|
||||
utilruntime.Must(kkcorev1alpha1.RegisterFieldLabelConversion(s))
|
||||
|
||||
return s
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,9 @@ type playbookExecutor struct {
|
|||
|
||||
// Exec playbook. covert playbook to block and executor it.
|
||||
func (e playbookExecutor) Exec(ctx context.Context) (retErr error) {
|
||||
defer e.syncStatus(ctx, retErr)
|
||||
defer func() {
|
||||
e.syncStatus(ctx, retErr)
|
||||
}()
|
||||
fmt.Fprint(e.logOutput, `
|
||||
|
||||
_ __ _ _ __
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ func (w *fileWatcher) watch() {
|
|||
|
||||
// watchFile for resource.
|
||||
func (w *fileWatcher) watchFile(event fsnotify.Event) error {
|
||||
if !strings.HasSuffix(event.Name, yamlSuffix) {
|
||||
if !strings.HasSuffix(event.Name, yamlSuffix) && !strings.HasSuffix(event.Name, yamlSuffix+deleteTagSuffix) {
|
||||
return nil
|
||||
}
|
||||
data, err := os.ReadFile(event.Name)
|
||||
|
|
@ -164,11 +164,6 @@ func (w *fileWatcher) watchFile(event fsnotify.Event) error {
|
|||
|
||||
switch event.Op {
|
||||
case fsnotify.Create:
|
||||
w.watchEvents <- watch.Event{
|
||||
Type: watch.Added,
|
||||
Object: obj,
|
||||
}
|
||||
case fsnotify.Write:
|
||||
if strings.HasSuffix(filepath.Base(event.Name), deleteTagSuffix) {
|
||||
// delete event
|
||||
w.watchEvents <- watch.Event{
|
||||
|
|
@ -179,12 +174,17 @@ func (w *fileWatcher) watchFile(event fsnotify.Event) error {
|
|||
klog.ErrorS(err, "failed to remove file", "event", event)
|
||||
}
|
||||
} else {
|
||||
// update event
|
||||
w.watchEvents <- watch.Event{
|
||||
Type: watch.Modified,
|
||||
Type: watch.Added,
|
||||
Object: obj,
|
||||
}
|
||||
}
|
||||
case fsnotify.Write:
|
||||
// update event
|
||||
w.watchEvents <- watch.Event{
|
||||
Type: watch.Modified,
|
||||
Object: obj,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -16,6 +16,11 @@ limitations under the License.
|
|||
|
||||
package api
|
||||
|
||||
const (
|
||||
// SchemaLabelSubfix is the label key used to indicate which schema a playbook belongs to.
|
||||
SchemaLabelSubfix = "kubekey.kubesphere.io/schema"
|
||||
)
|
||||
|
||||
const (
|
||||
// ResultSucceed indicates a successful operation result.
|
||||
ResultSucceed = "success"
|
||||
|
|
@ -69,15 +74,15 @@ type InventoryHostGroups struct {
|
|||
// It includes fields such as name, type, title, description, version, namespace, logo, priority, and associated playbooks.
|
||||
// The Playbook field is a slice of SchemaTablePlaybook, each representing a playbook reference.
|
||||
type SchemaTable struct {
|
||||
Name string `json:"name"` // Name of schema, defined by filename
|
||||
SchemaType string `json:"schemaType"` // Type of the schema (e.g., CRD, built-in)
|
||||
Title string `json:"title"` // Title of the schema
|
||||
Description string `json:"description"` // Description of the schema
|
||||
Version string `json:"version"` // Version of the schema
|
||||
Namespace string `json:"namespace"` // Namespace of the schema
|
||||
Logo string `json:"logo"` // Logo URL or identifier
|
||||
Priority int `json:"priority"` // Priority for display or ordering
|
||||
Playbook []SchemaTablePlaybook `json:"playbook"` // List of reference playbooks
|
||||
Name string `json:"name"` // Name of schema, defined by filename
|
||||
SchemaType string `json:"schemaType"` // Type of the schema (e.g., CRD, built-in)
|
||||
Title string `json:"title"` // Title of the schema
|
||||
Description string `json:"description"` // Description of the schema
|
||||
Version string `json:"version"` // Version of the schema
|
||||
Namespace string `json:"namespace"` // Namespace of the schema
|
||||
Logo string `json:"logo"` // Logo URL or identifier
|
||||
Priority int `json:"priority"` // Priority for display or ordering
|
||||
Playbook SchemaTablePlaybook `json:"playbook"` // List of reference playbooks
|
||||
}
|
||||
|
||||
// SchemaTablePlaybook represents a reference to a playbook associated with a schema.
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
restfulspec "github.com/emicklei/go-restful-openapi/v2"
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
kkcorev1 "github.com/kubesphere/kubekey/api/core/v1"
|
||||
|
|
@ -38,15 +39,9 @@ import (
|
|||
// NewCoreService creates and configures a new RESTful web service for managing inventories and playbooks.
|
||||
// It sets up routes for CRUD operations on inventories and playbooks, including pagination, sorting, and filtering.
|
||||
func NewCoreService(workdir string, client ctrlclient.Client, config *rest.Config) *restful.WebService {
|
||||
ws := new(restful.WebService)
|
||||
// the GroupVersion might be empty, we need to remove the final /
|
||||
ws.Path(strings.TrimRight(_const.APIPath+kkcorev1.SchemeGroupVersion.String(), "/")).
|
||||
Produces(restful.MIME_JSON).Consumes(
|
||||
string(types.JSONPatchType),
|
||||
string(types.MergePatchType),
|
||||
string(types.StrategicMergePatchType),
|
||||
string(types.ApplyPatchType),
|
||||
restful.MIME_JSON)
|
||||
ws := new(restful.WebService).
|
||||
// the GroupVersion might be empty, we need to remove the final /
|
||||
Path(strings.TrimRight(_const.APIPath+kkcorev1.SchemeGroupVersion.String(), "/"))
|
||||
|
||||
h := newCoreHandler(workdir, client, config)
|
||||
|
||||
|
|
@ -54,20 +49,23 @@ func NewCoreService(workdir string, client ctrlclient.Client, config *rest.Confi
|
|||
ws.Route(ws.POST("/inventories").To(h.createInventory).
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{_const.KubeKeyTag}).
|
||||
Doc("create a inventory.").
|
||||
Consumes(restful.MIME_JSON).Produces(restful.MIME_JSON).
|
||||
Reads(kkcorev1.Inventory{}).
|
||||
Returns(http.StatusOK, _const.StatusOK, kkcorev1.Inventory{}))
|
||||
|
||||
ws.Route(ws.PATCH("/namespaces/{namespace}/inventories/{inventory}").To(h.patchInventory).
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{_const.KubeKeyTag}).
|
||||
Doc("patch a inventory.").
|
||||
Consumes(string(types.JSONPatchType), string(types.MergePatchType), string(types.ApplyPatchType)).Produces(restful.MIME_JSON).
|
||||
Reads(kkcorev1.Inventory{}).
|
||||
Param(ws.PathParameter("namespace", "the namespace of the inventory")).
|
||||
Param(ws.PathParameter("inventory", "the name of the inventory")).
|
||||
Reads(kkcorev1.Inventory{}).
|
||||
Returns(http.StatusOK, _const.StatusOK, kkcorev1.Inventory{}))
|
||||
|
||||
ws.Route(ws.GET("/inventories").To(h.listInventories).
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{_const.KubeKeyTag}).
|
||||
Doc("list all inventories.").
|
||||
Produces(restful.MIME_JSON).
|
||||
Param(ws.QueryParameter(query.ParameterPage, "page").Required(false).DataFormat("page=%d")).
|
||||
Param(ws.QueryParameter(query.ParameterLimit, "limit").Required(false)).
|
||||
Param(ws.QueryParameter(query.ParameterAscending, "sort parameters, e.g. reverse=true").Required(false).DefaultValue("false")).
|
||||
|
|
@ -77,6 +75,7 @@ func NewCoreService(workdir string, client ctrlclient.Client, config *rest.Confi
|
|||
ws.Route(ws.GET("/namespaces/{namespace}/inventories").To(h.listInventories).
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{_const.KubeKeyTag}).
|
||||
Doc("list all inventories in a namespace.").
|
||||
Produces(restful.MIME_JSON).
|
||||
Param(ws.PathParameter("namespace", "the namespace of the inventory")).
|
||||
Param(ws.QueryParameter(query.ParameterPage, "page").Required(false).DataFormat("page=%d")).
|
||||
Param(ws.QueryParameter(query.ParameterLimit, "limit").Required(false)).
|
||||
|
|
@ -87,6 +86,7 @@ func NewCoreService(workdir string, client ctrlclient.Client, config *rest.Confi
|
|||
ws.Route(ws.GET("/namespaces/{namespace}/inventories/{inventory}").To(h.inventoryInfo).
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{_const.KubeKeyTag}).
|
||||
Doc("get a inventory in a namespace.").
|
||||
Produces(restful.MIME_JSON).
|
||||
Param(ws.PathParameter("namespace", "the namespace of the inventory")).
|
||||
Param(ws.PathParameter("inventory", "the name of the inventory")).
|
||||
Returns(http.StatusOK, _const.StatusOK, kkcorev1.Inventory{}))
|
||||
|
|
@ -94,6 +94,7 @@ func NewCoreService(workdir string, client ctrlclient.Client, config *rest.Confi
|
|||
ws.Route(ws.GET("/namespaces/{namespace}/inventories/{inventory}/hosts").To(h.listInventoryHosts).
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{_const.KubeKeyTag}).
|
||||
Doc("list all hosts in a inventory.").
|
||||
Produces(restful.MIME_JSON).
|
||||
Param(ws.PathParameter("namespace", "the namespace of the inventory")).
|
||||
Param(ws.PathParameter("inventory", "the name of the inventory")).
|
||||
Param(ws.QueryParameter(query.ParameterPage, "page").Required(false).DataFormat("page=%d")).
|
||||
|
|
@ -106,13 +107,14 @@ func NewCoreService(workdir string, client ctrlclient.Client, config *rest.Confi
|
|||
ws.Route(ws.POST("/playbooks").To(h.createPlaybook).
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{_const.KubeKeyTag}).
|
||||
Doc("create a playbook.").
|
||||
Consumes(restful.MIME_JSON).Produces(restful.MIME_JSON).
|
||||
Reads(kkcorev1.Playbook{}).
|
||||
Returns(http.StatusOK, _const.StatusOK, kkcorev1.Playbook{}))
|
||||
|
||||
ws.Route(ws.GET("/playbooks").To(h.listPlaybooks).
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{_const.KubeKeyTag}).
|
||||
Doc("list all playbooks.").
|
||||
Reads(kkcorev1.Playbook{}).
|
||||
Produces(restful.MIME_JSON).
|
||||
Param(ws.QueryParameter(query.ParameterPage, "page").Required(false).DataFormat("page=%d")).
|
||||
Param(ws.QueryParameter(query.ParameterLimit, "limit").Required(false)).
|
||||
Param(ws.QueryParameter(query.ParameterAscending, "sort parameters, e.g. reverse=true").Required(false).DefaultValue("false")).
|
||||
|
|
@ -122,6 +124,7 @@ func NewCoreService(workdir string, client ctrlclient.Client, config *rest.Confi
|
|||
ws.Route(ws.GET("/namespaces/{namespace}/playbooks").To(h.listPlaybooks).
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{_const.KubeKeyTag}).
|
||||
Doc("list all playbooks in a namespace.").
|
||||
Produces(restful.MIME_JSON).
|
||||
Param(ws.PathParameter("namespace", "the namespace of the playbook")).
|
||||
Param(ws.QueryParameter(query.ParameterPage, "page").Required(false).DataFormat("page=%d")).
|
||||
Param(ws.QueryParameter(query.ParameterLimit, "limit").Required(false)).
|
||||
|
|
@ -132,6 +135,7 @@ func NewCoreService(workdir string, client ctrlclient.Client, config *rest.Confi
|
|||
ws.Route(ws.GET("/namespaces/{namespace}/playbooks/{playbook}").To(h.playbookInfo).
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{_const.KubeKeyTag}).
|
||||
Doc("get or watch a playbook in a namespace.").
|
||||
Produces(restful.MIME_JSON).
|
||||
Param(ws.PathParameter("namespace", "the namespace of the playbook")).
|
||||
Param(ws.PathParameter("playbook", "the name of the playbook")).
|
||||
Param(ws.QueryParameter("watch", "set to true to watch this playbook")).
|
||||
|
|
@ -140,13 +144,15 @@ func NewCoreService(workdir string, client ctrlclient.Client, config *rest.Confi
|
|||
ws.Route(ws.GET("/namespaces/{namespace}/playbooks/{playbook}/log").To(h.logPlaybook).
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{_const.KubeKeyTag}).
|
||||
Doc("get a playbook execute log.").
|
||||
Produces("text/plain").
|
||||
Param(ws.PathParameter("namespace", "the namespace of the playbook")).
|
||||
Param(ws.PathParameter("playbook", "the name of the playbook")).
|
||||
Returns(http.StatusOK, _const.StatusOK, "text/plain"))
|
||||
Returns(http.StatusOK, _const.StatusOK, ""))
|
||||
|
||||
ws.Route(ws.DELETE("/namespaces/{namespace}/playbooks/{playbook}").To(h.deletePlaybook).
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{_const.KubeKeyTag}).
|
||||
Doc("delete a playbook.").
|
||||
Produces(restful.MIME_JSON).
|
||||
Param(ws.PathParameter("namespace", "the namespace of the playbook")).
|
||||
Param(ws.PathParameter("playbook", "the name of the playbook")).
|
||||
Returns(http.StatusOK, _const.StatusOK, api.Result{}))
|
||||
|
|
@ -469,37 +475,83 @@ func (h *coreHandler) listInventoryHosts(request *restful.Request, response *res
|
|||
// It reads the playbook from the request, sets the workdir, creates the resource, and starts execution in a goroutine.
|
||||
func (h *coreHandler) createPlaybook(request *restful.Request, response *restful.Response) {
|
||||
playbook := &kkcorev1.Playbook{}
|
||||
// Read the playbook entity from the request body
|
||||
if err := request.ReadEntity(playbook); err != nil {
|
||||
api.HandleBadRequest(response, request, err)
|
||||
return
|
||||
}
|
||||
// Set workdir to playbook spec config.
|
||||
|
||||
// Check for schema label: only one allowed, must not be empty, and must be unique among playbooks
|
||||
hasSchemaLabel := false
|
||||
for labelKey, labelValue := range playbook.Labels {
|
||||
// Only consider labels with the schema label suffix
|
||||
if !strings.HasSuffix(labelKey, api.SchemaLabelSubfix) {
|
||||
continue
|
||||
}
|
||||
// If a schema label was already found, this is a conflict
|
||||
if hasSchemaLabel {
|
||||
api.HandleConflict(response, request, errors.New("a playbook can only have one schema label. Please ensure only one schema label is set"))
|
||||
return
|
||||
}
|
||||
// The schema label value must not be empty
|
||||
if labelValue == "" {
|
||||
api.HandleConflict(response, request, errors.New("the schema label value must not be empty. Please provide a valid schema label value"))
|
||||
return
|
||||
}
|
||||
hasSchemaLabel = true
|
||||
// Check if there is already a playbook with the same schema label
|
||||
playbookList := &kkcorev1.PlaybookList{}
|
||||
if err := h.client.List(request.Request.Context(), playbookList, ctrlclient.MatchingLabels{
|
||||
labelKey: labelValue,
|
||||
}); err != nil {
|
||||
api.HandleBadRequest(response, request, err)
|
||||
return
|
||||
}
|
||||
// If any playbook with the same schema label exists, this is a conflict
|
||||
if len(playbookList.Items) > 0 {
|
||||
api.HandleConflict(response, request, errors.New("a playbook with the same schema label already exists. Please use a different schema label or remove the existing playbook"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Set the workdir in the playbook's spec config
|
||||
if err := unstructured.SetNestedField(playbook.Spec.Config.Value(), h.workdir, _const.Workdir); err != nil {
|
||||
api.HandleBadRequest(response, request, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Create the playbook resource in the cluster
|
||||
if err := h.client.Create(request.Request.Context(), playbook); err != nil {
|
||||
api.HandleBadRequest(response, request, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Start playbook execution in a separate goroutine
|
||||
go func() {
|
||||
// Create playbook log file and execute the playbook, writing output to the log.
|
||||
filename := filepath.Join(_const.GetWorkdirFromConfig(playbook.Spec.Config), _const.RuntimeDir, kkcorev1.SchemeGroupVersion.Group, kkcorev1.SchemeGroupVersion.Version, "playbooks", playbook.Namespace, playbook.Name, playbook.Name+".log")
|
||||
// Check if the directory for the log file exists, and create it if it does not.
|
||||
// Build the log file path for the playbook execution
|
||||
filename := filepath.Join(
|
||||
_const.GetWorkdirFromConfig(playbook.Spec.Config),
|
||||
_const.RuntimeDir,
|
||||
kkcorev1.SchemeGroupVersion.Group,
|
||||
kkcorev1.SchemeGroupVersion.Version,
|
||||
"playbooks",
|
||||
playbook.Namespace,
|
||||
playbook.Name,
|
||||
playbook.Name+".log",
|
||||
)
|
||||
// Ensure the directory for the log file exists
|
||||
if _, err := os.Stat(filepath.Dir(filename)); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
api.HandleBadRequest(response, request, err)
|
||||
return
|
||||
}
|
||||
// If directory does not exist, create it.
|
||||
// If directory does not exist, create it
|
||||
if err := os.MkdirAll(filepath.Dir(filename), os.ModePerm); err != nil {
|
||||
api.HandleBadRequest(response, request, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
// Open the log file for writing.
|
||||
// Open the log file for writing
|
||||
file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "failed to open file", "file", filename)
|
||||
|
|
@ -507,15 +559,15 @@ func (h *coreHandler) createPlaybook(request *restful.Request, response *restful
|
|||
}
|
||||
defer file.Close()
|
||||
|
||||
// Create a cancellable context for the playbook execution.
|
||||
// Create a cancellable context for playbook execution
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
// Add the playbook and its cancel function to the playbookManager.
|
||||
// Register the playbook and its cancel function in the playbookManager
|
||||
h.playbookManager.addPlaybook(playbook, cancel)
|
||||
// Execute the playbook and write output to the log file.
|
||||
// Execute the playbook and write output to the log file
|
||||
if err := executor.NewPlaybookExecutor(ctx, h.client, playbook, file).Exec(ctx); err != nil {
|
||||
klog.ErrorS(err, "failed to exec playbook", "playbook", playbook.Name)
|
||||
}
|
||||
// Remove the playbook from the playbookManager after execution.
|
||||
// Remove the playbook from the playbookManager after execution
|
||||
h.playbookManager.deletePlaybook(playbook)
|
||||
}()
|
||||
|
||||
|
|
@ -697,10 +749,11 @@ func (h *coreHandler) deletePlaybook(request *restful.Request, response *restful
|
|||
}
|
||||
// delete relative filepath: variable and log
|
||||
_ = os.Remove(filepath.Join(_const.GetWorkdirFromConfig(playbook.Spec.Config), _const.RuntimeDir, kkcorev1.SchemeGroupVersion.Group, kkcorev1.SchemeGroupVersion.Version, "playbooks", playbook.Namespace, playbook.Name, playbook.Name+".log"))
|
||||
_ = os.RemoveAll(filepath.Join(_const.GetWorkdirFromConfig(playbook.Spec.Config), _const.RuntimeDir, kkcorev1.SchemeGroupVersion.Group, kkcorev1.SchemeGroupVersion.Version, "playbooks", playbook.Namespace, playbook.Name, playbook.Name))
|
||||
_ = os.RemoveAll(filepath.Join(_const.GetWorkdirFromConfig(playbook.Spec.Config), _const.RuntimeDir, kkcorev1.SchemeGroupVersion.Group, kkcorev1.SchemeGroupVersion.Version, "playbooks", playbook.Namespace, playbook.Name))
|
||||
// Delete all tasks owned by this playbook.
|
||||
if err := h.client.DeleteAllOf(request.Request.Context(), &kkcorev1alpha1.Task{}, ctrlclient.MatchingFields{
|
||||
"playbook.name": playbook.Name, "playbook.namespace": playbook.Namespace,
|
||||
if err := h.client.DeleteAllOf(request.Request.Context(), &kkcorev1alpha1.Task{}, ctrlclient.InNamespace(playbook.Namespace), ctrlclient.MatchingFields{
|
||||
"playbook.name": playbook.Name,
|
||||
"playbook.uid": string(playbook.UID),
|
||||
}); err != nil {
|
||||
api.HandleError(response, request, err)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package web
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
|
|
@ -15,6 +15,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
restfulspec "github.com/emicklei/go-restful-openapi/v2"
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
kkcorev1 "github.com/kubesphere/kubekey/api/core/v1"
|
||||
|
|
@ -44,6 +45,7 @@ func NewSchemaService(rootPath string, workdir string, client ctrlclient.Client)
|
|||
Doc("list available ip from ip cidr").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{_const.ResourceTag}).
|
||||
Param(ws.QueryParameter("cidr", "the cidr for ip").Required(true)).
|
||||
Param(ws.QueryParameter("sshPort", "the ssh port for ip").Required(false)).
|
||||
Param(ws.QueryParameter(query.ParameterPage, "page").Required(false).DataFormat("page=%d")).
|
||||
Param(ws.QueryParameter(query.ParameterLimit, "limit").Required(false)).
|
||||
Param(ws.QueryParameter(query.ParameterAscending, "sort parameters, e.g. reverse=true").Required(false).DefaultValue("false")).
|
||||
|
|
@ -57,7 +59,7 @@ func NewSchemaService(rootPath string, workdir string, client ctrlclient.Client)
|
|||
Doc("list all schema as table").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{_const.ResourceTag}).
|
||||
Param(ws.QueryParameter("schemaType", "the type of schema json").Required(false)).
|
||||
Param(ws.QueryParameter("playbookLabel", "the reference playbook of schema. eg: install.kubekey.kubesphere.io/schema,check.kubekey.kubesphere.io/schema"+
|
||||
Param(ws.QueryParameter("playbookLabel", "the reference playbook of schema. eg: \"install.kubekey.kubesphere.io/schema\", \"check.kubekey.kubesphere.io/schema\" \\n"+
|
||||
"if empty will not return any reference playbook").Required(false)).
|
||||
Param(ws.QueryParameter(query.ParameterPage, "page").Required(false).DataFormat("page=%d")).
|
||||
Param(ws.QueryParameter(query.ParameterLimit, "limit").Required(false)).
|
||||
|
|
@ -83,10 +85,14 @@ type schemaHandler struct {
|
|||
func (h schemaHandler) listIP(request *restful.Request, response *restful.Response) {
|
||||
queryParam := query.ParseQueryParameter(request)
|
||||
cidr, ok := queryParam.Filters["cidr"]
|
||||
if !ok || len(cidr) == 0 {
|
||||
if !ok || string(cidr) == "" {
|
||||
api.HandleBadRequest(response, request, errors.New("cidr parameter is required"))
|
||||
return
|
||||
}
|
||||
sshPort, ok := queryParam.Filters["sshPort"]
|
||||
if !ok || string(sshPort) == "" {
|
||||
sshPort = "22"
|
||||
}
|
||||
ips := _const.ParseIP(string(cidr))
|
||||
ipTable := make([]api.IPTable, 0, len(ips))
|
||||
maxConcurrency := 20
|
||||
|
|
@ -115,7 +121,7 @@ func (h schemaHandler) listIP(request *restful.Request, response *restful.Respon
|
|||
if !ifIPOnline(ip) {
|
||||
continue
|
||||
}
|
||||
reachable, authorized := ifIPSSHAuthorized(ip)
|
||||
reachable, authorized := ifIPSSHAuthorized(ip, string(sshPort))
|
||||
|
||||
mu.Lock()
|
||||
ipTable = append(ipTable, api.IPTable{
|
||||
|
|
@ -220,13 +226,22 @@ func (h schemaHandler) allSchema(request *restful.Request, response *restful.Res
|
|||
api.HandleBadRequest(response, request, err)
|
||||
return
|
||||
}
|
||||
schema.Playbook = make([]api.SchemaTablePlaybook, len(playbookList.Items))
|
||||
for i, playbook := range playbookList.Items {
|
||||
schema.Playbook[i] = api.SchemaTablePlaybook{
|
||||
Name: playbook.Name,
|
||||
Namespace: playbook.Namespace,
|
||||
Phase: string(playbook.Status.Phase),
|
||||
switch len(playbookList.Items) {
|
||||
case 0: // skip
|
||||
case 1:
|
||||
item := &playbookList.Items[0]
|
||||
schema.Playbook = api.SchemaTablePlaybook{
|
||||
Name: item.Name,
|
||||
Namespace: item.Namespace,
|
||||
Phase: string(item.Status.Phase),
|
||||
}
|
||||
default:
|
||||
playbookNames := make([]string, 0, len(playbookList.Items))
|
||||
for _, playbook := range playbookList.Items {
|
||||
playbookNames = append(playbookNames, playbook.Name)
|
||||
}
|
||||
api.HandleBadRequest(response, request, errors.Errorf("schema %q has multiple playbooks: %q", entry.Name(), playbookNames))
|
||||
return
|
||||
}
|
||||
}
|
||||
schemaTable = append(schemaTable, schema)
|
||||
|
|
@ -439,9 +454,9 @@ func isValidICMPReply(n int, reply []byte, src net.Addr, expectedIP net.IP, prot
|
|||
// ifIPSSHAuthorized checks if SSH authorization to the given IP is possible using the local private key.
|
||||
// It returns two booleans: the first indicates if the SSH port (22) is reachable, and the second indicates if SSH authorization using the local private key is successful.
|
||||
// The function attempts to find the user's private key, read and parse it, and then connect via SSH.
|
||||
func ifIPSSHAuthorized(ipStr string) (bool, bool) {
|
||||
func ifIPSSHAuthorized(ipStr, sshPort string) (bool, bool) {
|
||||
// First check if port 22 is reachable on the target IP address.
|
||||
conn, err := net.DialTimeout("tcp", ipStr+":22", time.Second)
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%s", ipStr, sshPort), time.Second)
|
||||
if err != nil {
|
||||
klog.V(6).Infof("port 22 not reachable on ip %q, error %v", ipStr, err)
|
||||
return false, false
|
||||
|
|
|
|||
Loading…
Reference in New Issue