internal/actions/actions.go
Rama main · réplica de lectura del repositorio original.
package actions
import (
"context"
"errors"
"fmt"
"slices"
"time"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"github.com/sophotechlabs/spinoza/internal/api"
)
const (
fieldManager = "spinoza"
appsGroup = "apps"
batchGroup = "batch"
specField = "spec"
)
type Action string
const (
Scale Action = "scale"
Restart Action = "restart"
Cordon Action = "cordon"
Uncordon Action = "uncordon"
Drain Action = "drain"
Suspend Action = "suspend"
Resume Action = "resume"
Trigger Action = "trigger"
Undo Action = "undo"
)
var ErrUnsupported = errors.New("action is not supported for this resource")
type groupResource struct {
group string
resource string
}
var scalable = map[groupResource]bool{
{group: appsGroup, resource: "deployments"}: true,
{group: appsGroup, resource: "statefulsets"}: true,
{group: appsGroup, resource: "replicasets"}: true,
{group: "", resource: "replicationcontrollers"}: true,
}
var restartable = map[groupResource]bool{
{group: appsGroup, resource: "deployments"}: true,
{group: appsGroup, resource: "statefulsets"}: true,
{group: appsGroup, resource: "daemonsets"}: true,
}
var revertible = map[groupResource]bool{
{group: appsGroup, resource: "deployments"}: true,
{group: appsGroup, resource: "statefulsets"}: true,
{group: appsGroup, resource: "daemonsets"}: true,
}
var cronJobs = groupResource{group: batchGroup, resource: "cronjobs"}
type Request struct {
Ref api.ObjectRef
Action Action
Replicas int64
Revision int64
Force bool
DryRun bool
}
type Service struct {
dyn dynamic.Interface
cs kubernetes.Interface
retryDelay time.Duration
}
const evictRetryDelay = 2 * time.Second
func New(dyn dynamic.Interface, cs kubernetes.Interface) *Service {
return newWithDelay(dyn, cs, evictRetryDelay)
}
func newWithDelay(dyn dynamic.Interface, cs kubernetes.Interface, retryDelay time.Duration) *Service {
return &Service{dyn: dyn, cs: cs, retryDelay: retryDelay}
}
func Supported(ref api.ObjectRef, action Action) bool {
key := groupResource{group: ref.Group, resource: ref.Resource}
switch action {
case Scale:
return scalable[key]
case Restart:
return restartable[key]
case Cordon, Uncordon, Drain:
return key == groupResource{group: "", resource: "nodes"}
case Suspend, Resume, Trigger:
return key == cronJobs
case Undo:
return revertible[key]
default:
return false
}
}
func Every() []Action {
return []Action{Scale, Restart, Cordon, Uncordon, Drain, Suspend, Resume, Trigger, Undo}
}
func known(action Action) bool {
return slices.Contains(Every(), action)
}
func (s *Service) Do(ctx context.Context, req Request, now time.Time) (api.ActionResult, error) {
if req.Ref.Name == "" {
return api.ActionResult{}, errors.New("name is required")
}
if !known(req.Action) {
return api.ActionResult{}, fmt.Errorf("unknown action %q", req.Action)
}
if !Supported(req.Ref, req.Action) {
return api.ActionResult{}, fmt.Errorf("%w: %s on %s", ErrUnsupported, req.Action, describe(req.Ref))
}
switch req.Action {
case Scale:
return s.scale(ctx, req)
case Restart:
return s.restart(ctx, req, now)
case Cordon:
return s.setSchedulable(ctx, req.Ref, false)
case Uncordon:
return s.setSchedulable(ctx, req.Ref, true)
case Drain:
return s.drain(ctx, req)
case Suspend:
return s.setSuspended(ctx, req.Ref, true)
case Resume:
return s.setSuspended(ctx, req.Ref, false)
case Trigger:
return s.trigger(ctx, req.Ref)
case Undo:
return s.undo(ctx, req)
default:
return api.ActionResult{}, fmt.Errorf("unknown action %q", req.Action)
}
}
func describe(ref api.ObjectRef) string {
if ref.Group == "" {
return ref.Resource
}
return ref.Group + "/" + ref.Resource
}