Ctrl K
ComunidadGuías…

api/controller/cleanup_node_launch.go

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

Archivo original ↗Preguntar a KiBot
package controller

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

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

// NodeJobSelection contains the saved scan identity; paths and executor settings
// are intentionally absent. All supplied locators are checked against Kubernetes.
type NodeJobSelection struct {
	guard.CoordinationRequest
	SourcePod        string `json:"source_pod"`
	SourceUID        string `json:"source_uid"`
	NodeName         string `json:"node_name"`
	NodeUID          string `json:"node_uid"`
	Entry            string `json:"entry"`
	EntryFingerprint string `json:"entry_fingerprint"`
}

func systemNodeJobSettings() kubeidentity.NodeJobSettings {
	return kubeidentity.NodeJobSettings{Region: os.Getenv("REGION_NAME"), Image: os.Getenv("CLEANUP_NODE_EXECUTOR_IMAGE"), Endpoint: os.Getenv("CLEANUP_NODE_CORE_ENDPOINT"), CredentialSecret: os.Getenv("CLEANUP_NODE_CORE_SECRET"), StateClaim: os.Getenv("CLEANUP_NODE_STATE_CLAIM"), ConsoleEnterprise: os.Getenv("CLEANUP_NODE_CONSOLE_ENTERPRISE"), AllowConsoleHTTP: os.Getenv("CLEANUP_NODE_CONSOLE_ALLOW_HTTP") == "true"}
}

// SubmitNodeJob builds an immutable suspended executor for an admitted selection.
func (h *CleanupCoordinationHandler) SubmitNodeJob(w http.ResponseWriter, r *http.Request) {
	var body NodeJobSelection
	if !coordinationDecode(w, r, &body) || !coordinationScopeFromRoute(w, r, &body.CoordinationRequest) {
		return
	}
	if h.gcTarget == nil || h.nodeSettings == nil {
		coordinationError(w, r, guard.ErrCoordinationUnavailable)
		return
	}
	client, namespace, _, err := h.gcTarget()
	if err != nil || client == nil {
		coordinationError(w, r, guard.ErrCoordinationUnavailable)
		return
	}
	database := h.database()
	binding, err := guard.StorageBinding(database, body.StorageID, body.Generation)
	if err != nil {
		coordinationError(w, r, err)
		return
	}
	intent := guard.NodeJobIntent{Namespace: namespace, NodeName: body.NodeName, NodeUID: body.NodeUID, Entry: body.Entry, Fingerprint: body.EntryFingerprint}
	expected, err := guard.ManagedNodeRequest(binding, body.Owner, body.OperationID, body.Fingerprint, intent)
	if err != nil || expected != body.CoordinationRequest {
		coordinationError(w, r, guard.ErrCoordinationChanged)
		return
	}
	existing, err := guard.ReadNodeJobBinding(database, body.CoordinationRequest)
	if err == nil {
		if existing.Namespace != namespace || existing.NodeName != intent.NodeName || existing.NodeUID != intent.NodeUID || existing.Entry != intent.Entry || existing.Fingerprint != intent.Fingerprint {
			coordinationError(w, r, guard.ErrCoordinationChanged)
			return
		}
		if _, err := guard.ReconcileNodeJob(r.Context(), database, client.BatchV1().Jobs(namespace), body.CoordinationRequest); err != nil {
			coordinationError(w, r, err)
			return
		}
	} else {
		if !errors.Is(err, guard.ErrNodeJobNotPrepared) {
			coordinationError(w, r, err)
			return
		}
		template, err := kubeidentity.BuildManagedNodeJob(r.Context(), client, body.SourcePod, body.SourceUID, binding, body.CoordinationRequest, intent, h.nodeSettings())
		if err != nil {
			coordinationError(w, r, guard.ErrCoordinationChanged)
			return
		}
		if _, err := guard.SubmitSuspendedNodeJob(r.Context(), database, client.BatchV1().Jobs(namespace), body.CoordinationRequest, intent, template); err != nil {
			coordinationError(w, r, err)
			return
		}
	}
	httputil.ReturnSuccess(r, w, struct {
		Protocol int  `json:"protocol"`
		Recorded bool `json:"recorded"`
	}{1, true})
}

// StartNodeJob reconstructs the trusted source before starting the original Job.
func (h *CleanupCoordinationHandler) StartNodeJob(w http.ResponseWriter, r *http.Request) {
	var request guard.CoordinationRequest
	if !coordinationDecode(w, r, &request) || !coordinationScopeFromRoute(w, r, &request) {
		return
	}
	if h.gcTarget == nil || h.nodeSettings == nil {
		coordinationError(w, r, guard.ErrCoordinationUnavailable)
		return
	}
	client, namespace, _, err := h.gcTarget()
	if err != nil || client == nil {
		coordinationError(w, r, guard.ErrCoordinationUnavailable)
		return
	}
	database := h.database()
	binding, err := guard.ReadNodeJobBinding(database, request)
	if err != nil {
		coordinationError(w, r, err)
		return
	}
	if binding.Namespace != namespace {
		coordinationError(w, r, guard.ErrCoordinationChanged)
		return
	}
	job, err := guard.ReconcileNodeJob(r.Context(), database, client.BatchV1().Jobs(namespace), request)
	if err != nil {
		coordinationError(w, r, err)
		return
	}
	storage, err := guard.StorageBinding(database, request.StorageID, request.Generation)
	if err != nil {
		coordinationError(w, r, err)
		return
	}
	source := job.Spec.Template.Annotations
	current, err := kubeidentity.BuildManagedNodeJob(r.Context(), client, source["rainbond.io/node-source-pod"], source["rainbond.io/node-source-uid"], storage, request, binding.NodeJobIntent, h.nodeSettings())
	if err != nil || !kubeidentity.SameManagedNodeSource(job, current) {
		coordinationError(w, r, guard.ErrCoordinationChanged)
		return
	}
	if _, ready, err := h.certifyManagedNodeWriters(r.Context(), storage, &request); err != nil || !ready {
		if err == nil {
			err = guard.ErrCoordinationBusy
		}
		nodeAPIError(w, r, err)
		return
	}
	if _, err := guard.StartNodeJob(r.Context(), database, client.BatchV1().Jobs(namespace), request); err != nil {
		coordinationError(w, r, err)
		return
	}
	httputil.ReturnSuccess(r, w, struct {
		Protocol int  `json:"protocol"`
		Recorded bool `json:"recorded"`
	}{1, true})
}

// Recheck immediately before the one-use grant; result recovery must not depend
// on the continued availability of the builder that originally supplied a mount.
func (h *CleanupCoordinationHandler) validateNodeLaunchSource(ctx context.Context, request guard.CoordinationRequest) error {
	if h.gcTarget == nil || h.nodeSettings == nil {
		return guard.ErrCoordinationUnavailable
	}
	client, namespace, _, err := h.gcTarget()
	if err != nil || client == nil {
		return guard.ErrCoordinationUnavailable
	}
	binding, err := guard.ReadNodeJobBinding(h.database(), request)
	if err != nil {
		return err
	}
	if binding.Namespace != namespace {
		return guard.ErrCoordinationChanged
	}
	job, err := guard.ReconcileNodeJob(ctx, h.database(), client.BatchV1().Jobs(namespace), request)
	if err != nil {
		return err
	}
	storage, err := guard.StorageBinding(h.database(), request.StorageID, request.Generation)
	if err != nil {
		return err
	}
	source := job.Spec.Template.Annotations
	current, err := kubeidentity.BuildManagedNodeJob(ctx, client, source["rainbond.io/node-source-pod"], source["rainbond.io/node-source-uid"], storage, request, binding.NodeJobIntent, h.nodeSettings())
	if err != nil || !kubeidentity.SameManagedNodeSource(job, current) {
		return guard.ErrCoordinationChanged
	}
	if _, ready, err := h.certifyManagedNodeWriters(ctx, storage, &request); err != nil || !ready {
		if err != nil {
			return err
		}
		return guard.ErrCoordinationBusy
	}

	return nil
}

// CancelNodeBeforeGrant reconciles an original task that never obtained native
// authority. Core's storage transaction, not caller claims, proves that fact.
func (h *CleanupCoordinationHandler) CancelNodeBeforeGrant(w http.ResponseWriter, r *http.Request) {
	var body NodeJobSelection
	if !coordinationDecode(w, r, &body) || !coordinationScopeFromRoute(w, r, &body.CoordinationRequest) {
		return
	}
	if h.gcTarget == nil {
		coordinationError(w, r, guard.ErrCoordinationUnavailable)
		return
	}
	_, namespace, _, err := h.gcTarget()
	if err != nil || namespace == "" {
		coordinationError(w, r, guard.ErrCoordinationUnavailable)
		return
	}
	intent := guard.NodeJobIntent{Namespace: namespace, NodeName: body.NodeName, NodeUID: body.NodeUID, Entry: body.Entry, Fingerprint: body.EntryFingerprint}
	if err := guard.CancelNodeBeforeGrant(h.database(), body.CoordinationRequest, intent); err != nil {
		nodeAPIError(w, r, err)
		return
	}
	nodeRecorded(w, r)
}