StopDeploymentInner stops a deployment by checkpointing it and hibernating its provisioner resources. Persistent state (e.g. the runtime's PVC) is preserved across stop/start so that restarts are fast. The implementation is idempotent, enabling it to be called from a retryable background job.
(ctx context.Context, depl *database.Deployment)
| 338 | // Persistent state (e.g. the runtime's PVC) is preserved across stop/start so that restarts are fast. |
| 339 | // The implementation is idempotent, enabling it to be called from a retryable background job. |
| 340 | func (s *Service) StopDeploymentInner(ctx context.Context, depl *database.Deployment) error { |
| 341 | // Connect to the deployment's runtime and checkpoint repo changes if editable. |
| 342 | // Best effort: if the runtime is unreachable (e.g. already gone or partially torn down), we proceed to hibernate the provisioner resources anyway. |
| 343 | if depl.Editable { |
| 344 | rt, err := s.OpenRuntimeClient(depl) |
| 345 | if err != nil { |
| 346 | s.Logger.Info("failed to open runtime client for deployment stop", zap.String("deployment_id", depl.ID), zap.String("runtime_instance_id", depl.RuntimeInstanceID), zap.Error(err), observability.ZapCtx(ctx)) |
| 347 | } else { |
| 348 | defer rt.Close() |
| 349 | |
| 350 | _, err = rt.GitPush(ctx, &runtimev1.GitPushRequest{ |
| 351 | InstanceId: depl.RuntimeInstanceID, |
| 352 | CommitMessage: "Auto checkpoint", |
| 353 | }) |
| 354 | if err != nil { |
| 355 | s.Logger.Error("failed to checkpoint repo changes", zap.String("deployment_id", depl.ID), zap.String("runtime_instance_id", depl.RuntimeInstanceID), zap.Error(err), observability.ZapCtx(ctx)) |
| 356 | } |
| 357 | } |
| 358 | } |
| 359 | |
| 360 | // Hibernate all provisioned resources for the deployment. |
| 361 | // Hibernation is a lighter-weight alternative to deprovisioning that attempts to preserve persistent state. |
| 362 | prs, err := s.DB.FindProvisionerResourcesForDeployment(ctx, depl.ID) |
| 363 | if err != nil { |
| 364 | return err |
| 365 | } |
| 366 | for _, pr := range prs { |
| 367 | p, ok := s.ProvisionerSet[pr.Provisioner] |
| 368 | if !ok { |
| 369 | s.Logger.Warn("provisioner: hibernation skipped, provisioner not found", zap.String("deployment_id", depl.ID), zap.String("provisioner", pr.Provisioner), zap.String("provision_id", pr.ID), observability.ZapCtx(ctx)) |
| 370 | continue |
| 371 | } |
| 372 | |
| 373 | r, err := p.Hibernate(ctx, &provisioner.Resource{ |
| 374 | ID: pr.ID, |
| 375 | Type: provisioner.ResourceType(pr.Type), |
| 376 | State: pr.State, |
| 377 | Config: pr.Config, |
| 378 | }) |
| 379 | if err != nil { |
| 380 | return err |
| 381 | } |
| 382 | |
| 383 | _, err = s.DB.UpdateProvisionerResource(ctx, pr.ID, &database.UpdateProvisionerResourceOptions{ |
| 384 | Status: database.ProvisionerResourceStatusOK, |
| 385 | StatusMessage: "", |
| 386 | Args: pr.Args, |
| 387 | State: r.State, |
| 388 | Config: r.Config, |
| 389 | }) |
| 390 | if err != nil { |
| 391 | return err |
| 392 | } |
| 393 | } |
| 394 | |
| 395 | return nil |
| 396 | } |
| 397 |
no test coverage detected