Ctrl K
ComunidadGuías…

api/controller/cleanup_node_executor.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package controller

import (
	"context"
	"errors"
	"net/http"

	guard "github.com/goodrain/rainbond/pkg/cleanup"
	"github.com/goodrain/rainbond/pkg/cleanup/kubeidentity"
	httputil "github.com/goodrain/rainbond/util/http"
)

func (h *CleanupCoordinationHandler) inspectNodeExecution(ctx context.Context, request guard.CoordinationRequest, locator guard.NodeExecutorLocator, terminal bool) (guard.NodeExecutorIdentity, error) {
	denied := guard.NodeExecutorIdentity{}
	if err := ctx.Err(); err != nil {
		return denied, err
	}
	if h.gcTarget == nil {
		return denied, guard.ErrCoordinationUnavailable
	}
	database := h.database()
	execution, err := guard.ReadNodeJobBinding(database, request)
	if err != nil {
		return denied, err
	}
	client, namespace, _, err := h.gcTarget()
	if err != nil || client == nil || execution.Namespace != namespace {
		return denied, guard.ErrCoordinationChanged
	}
	job, err := guard.ReconcileNodeJob(ctx, database, client.BatchV1().Jobs(namespace), request)
	if err != nil {
		return denied, err
	}
	storage, err := guard.StorageBinding(database, request.StorageID, request.Generation)
	if err != nil {
		return denied, err
	}
	var observed guard.NodeExecutorIdentity
	if terminal {
		observed, err = kubeidentity.InspectTerminatedNodeExecutor(ctx, client, job, locator.Pod, locator.PodUID, storage, execution)
	} else {
		observed, err = kubeidentity.InspectNodeExecutor(ctx, client, job, locator.Pod, locator.PodUID, storage, execution)
	}
	if contextErr := ctx.Err(); contextErr != nil {
		return denied, contextErr
	}
	return observed, err
}
func nodeAPIError(w http.ResponseWriter, r *http.Request, err error) {
	if errors.Is(err, guard.ErrNodeJobNotPrepared) {
		httputil.ReturnError(r, w, 404, "NODE_JOB_NOT_PREPARED")
		return
	}
	if errors.Is(err, kubeidentity.ErrExecutorRunning) {
		err = guard.ErrCoordinationBusy
	}
	if errors.Is(err, kubeidentity.ErrBinding) {
		err = guard.ErrCoordinationChanged
	}
	coordinationError(w, r, err)
}
func nodeRecorded(w http.ResponseWriter, r *http.Request) {
	httputil.ReturnSuccess(r, w, struct {
		Protocol int  `json:"protocol"`
		Recorded bool `json:"recorded"`
	}{1, true})
}

// EnterNodeJob admits only a Core-bound node executor using live Kubernetes facts.
// This route is always protected by CleanupIdentity; caller runtime assertions fail decoding.
func (h *CleanupCoordinationHandler) EnterNodeJob(w http.ResponseWriter, r *http.Request) {
	var body struct {
		guard.CoordinationRequest
		guard.NodeExecutorLocator
	}
	if !coordinationDecode(w, r, &body) || !coordinationScopeFromRoute(w, r, &body.CoordinationRequest) {
		return
	}
	if !body.NodeExecutorLocator.Valid() {
		httputil.ReturnError(r, w, 400, "INVALID_NODE_EXECUTOR")
		return
	}
	if err := h.validateNodeLaunchSource(r.Context(), body.CoordinationRequest); err != nil {
		nodeAPIError(w, r, err)
		return
	}
	observed, err := h.inspectNodeExecution(r.Context(), body.CoordinationRequest, body.NodeExecutorLocator, false)
	if err != nil {
		nodeAPIError(w, r, err)
		return
	}
	if err := guard.EnterNodeExecution(h.database(), body.CoordinationRequest, observed); err != nil {
		nodeAPIError(w, r, err)
		return
	}
	nodeRecorded(w, r)
}

// RecordNodeJobResult stores effects from the original live or terminated executor.
// It never releases deletion protection or accepts caller termination assertions.
func (h *CleanupCoordinationHandler) RecordNodeJobResult(w http.ResponseWriter, r *http.Request) {
	var body struct {
		guard.CoordinationRequest
		guard.NodeExecutorLocator
		Result guard.NodeExecutionResult `json:"result"`
	}
	if !coordinationDecode(w, r, &body) || !coordinationScopeFromRoute(w, r, &body.CoordinationRequest) {
		return
	}
	if !body.NodeExecutorLocator.Valid() {
		httputil.ReturnError(r, w, 400, "INVALID_NODE_EXECUTOR")
		return
	}
	recorded, err := guard.NodeResultRecorded(h.database(), body.CoordinationRequest, body.NodeExecutorLocator, body.Result)
	if err != nil {
		nodeAPIError(w, r, err)
		return
	}
	if recorded {
		nodeRecorded(w, r)
		return
	}
	observed, err := h.inspectNodeExecution(r.Context(), body.CoordinationRequest, body.NodeExecutorLocator, false)
	if errors.Is(err, kubeidentity.ErrBinding) {
		observed, err = h.inspectNodeExecution(r.Context(), body.CoordinationRequest, body.NodeExecutorLocator, true)
	}
	if err != nil {
		nodeAPIError(w, r, err)
		return
	}
	if err := guard.RecordNodeResult(h.database(), body.CoordinationRequest, observed, body.Result); err != nil {
		nodeAPIError(w, r, err)
		return
	}
	nodeRecorded(w, r)
}

// FinishNodeJob checks original container termination before releasing its scope.
func (h *CleanupCoordinationHandler) FinishNodeJob(w http.ResponseWriter, r *http.Request) {
	var body struct {
		guard.CoordinationRequest
		guard.NodeExecutorLocator
	}
	if !coordinationDecode(w, r, &body) || !coordinationScopeFromRoute(w, r, &body.CoordinationRequest) {
		return
	}
	if !body.NodeExecutorLocator.Valid() {
		httputil.ReturnError(r, w, 400, "INVALID_NODE_EXECUTOR")
		return
	}
	progress, err := guard.ReadNodeJobProgress(h.database(), body.CoordinationRequest)
	if err != nil {
		nodeAPIError(w, r, err)
		return
	}
	if progress.State == "finished" {
		if progress.Execution.PodName != body.Pod || progress.Execution.PodUID != body.PodUID {
			nodeAPIError(w, r, guard.ErrCoordinationChanged)
			return
		}
		nodeRecorded(w, r)
		return
	}
	observed, err := h.inspectNodeExecution(r.Context(), body.CoordinationRequest, body.NodeExecutorLocator, true)
	if err != nil {
		nodeAPIError(w, r, err)
		return
	}
	if err := guard.FinishNodeExecution(h.database(), body.CoordinationRequest, observed); err != nil {
		nodeAPIError(w, r, err)
		return
	}
	nodeRecorded(w, r)
}

// NodeJobProgress reads a durable task receipt without mutating its native Job.
func (h *CleanupCoordinationHandler) NodeJobProgress(w http.ResponseWriter, r *http.Request) {
	var request guard.CoordinationRequest
	if !coordinationDecode(w, r, &request) || !coordinationScopeFromRoute(w, r, &request) {
		return
	}
	result, err := guard.ReadNodeJobProgress(h.database(), request)
	if err != nil {
		nodeAPIError(w, r, err)
		return
	}
	httputil.ReturnSuccess(r, w, struct {
		Protocol int                   `json:"protocol"`
		NodeJob  guard.NodeJobProgress `json:"node_job"`
	}{1, result})
}