diff --git a/internal/view/scale_extender.go b/internal/view/scale_extender.go index f9e4d00b06..45058d8bee 100644 --- a/internal/view/scale_extender.go +++ b/internal/view/scale_extender.go @@ -109,20 +109,13 @@ func (s *ScaleExtender) replicasFromReady(_ string) (string, error) { } func (s *ScaleExtender) replicasFromScaleSubresource(sel string) (string, error) { - res, err := dao.AccessorFor(s.App().factory, s.GVR()) - if err != nil { - return "", err - } - - replicasGetter, ok := res.(dao.ReplicasGetter) - if !ok { - return "", fmt.Errorf("expecting a replicasGetter resource for %q", s.GVR()) - } - + var scaler dao.Scaler + scaler.Init(s.App().factory, s.GVR()) + ctx, cancel := context.WithTimeout(context.Background(), s.App().Conn().Config().CallTimeout()) defer cancel() - replicas, err := replicasGetter.Replicas(ctx, sel) + replicas, err := scaler.Replicas(ctx, sel) if err != nil { return "", err } @@ -131,7 +124,9 @@ func (s *ScaleExtender) replicasFromScaleSubresource(sel string) (string, error) } func (s *ScaleExtender) makeScaleForm(fqns []string) (*tview.Form, error) { - factor := "0" + // Use empty string as the "unknown" sentinel so a legitimate current + // replica count of 0 (e.g., paused workload) is not treated as failure. + factor := "" if len(fqns) == 1 { // If the CRD resource supports scaling, then first try to // read the replicas directly from the CRD. @@ -144,14 +139,30 @@ func (s *ScaleExtender) makeScaleForm(fqns []string) (*tview.Form, error) { // For built-in resources or cases where we can't get the replicas from the CRD, we can // only try to get the number of copies from the READY field. - if factor == "0" { + var readyErr error + if factor == "" { replicas, err := s.replicasFromReady(fqns[0]) - if err != nil { - return nil, err + if err == nil { + factor = replicas + } else { + readyErr = err + slog.Warn("Unable to read replicas from ready column", slogs.Error, err) } + } - factor = replicas + // Refuse to open the dialog when we cannot determine the current + // replica count — otherwise a user hitting OK without editing would + // silently scale the workload to zero. + if factor == "" { + if readyErr != nil { + return nil, fmt.Errorf("unable to determine current replica count: %w", readyErr) + } + return nil, fmt.Errorf("unable to determine current replica count for %s", fqns[0]) } + } else { + // Bulk-scale: there is no single "current" value to pre-fill, so the + // user must explicitly enter a target. Keep the documented default. + factor = "0" } styles := s.App().Styles.Dialog() @@ -222,7 +233,9 @@ func (s *ScaleExtender) scale(ctx context.Context, path string, replicas int32) } scaler, ok := res.(dao.Scalable) if !ok { - return fmt.Errorf("expecting a scalable resource for %q", s.GVR()) + var genericScaler dao.Scaler + genericScaler.Init(s.App().factory, s.GVR()) + return genericScaler.Scale(ctx, path, replicas) } return scaler.Scale(ctx, path, replicas)