run_app_job.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. package v2
  2. import (
  3. "context"
  4. "encoding/base64"
  5. "fmt"
  6. "time"
  7. "github.com/fatih/color"
  8. "github.com/porter-dev/api-contracts/generated/go/helpers"
  9. porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
  10. api "github.com/porter-dev/porter/api/client"
  11. "github.com/porter-dev/porter/cli/cmd/config"
  12. porter_app_internal "github.com/porter-dev/porter/internal/porter_app"
  13. )
  14. // WaitIntervalInSeconds is the amount of time to wait when polling for job status
  15. const WaitIntervalInSeconds = 5 * time.Second
  16. // RunAppJobInput is the input for the RunAppJob function
  17. type RunAppJobInput struct {
  18. // CLIConfig is the CLI configuration
  19. CLIConfig config.CLIConfig
  20. // Client is the Porter API client
  21. Client api.Client
  22. AppName string
  23. JobName string
  24. // WaitForExit
  25. WaitForExit bool
  26. }
  27. // RunAppJob triggers a job run for an app and returns without waiting for the job to complete
  28. func RunAppJob(ctx context.Context, inp RunAppJobInput) error {
  29. targetResp, err := inp.Client.DefaultDeploymentTarget(ctx, inp.CLIConfig.Project, inp.CLIConfig.Cluster)
  30. if err != nil {
  31. return fmt.Errorf("error calling default deployment target endpoint: %w", err)
  32. }
  33. currentAppRevisionResp, err := inp.Client.CurrentAppRevision(ctx, inp.CLIConfig.Project, inp.CLIConfig.Cluster, inp.AppName, targetResp.DeploymentTargetID) // nolint:staticcheck
  34. if err != nil {
  35. return fmt.Errorf("error getting current app revision: %w", err)
  36. }
  37. resp, err := inp.Client.RunAppJob(ctx, inp.CLIConfig.Project, inp.CLIConfig.Cluster, inp.AppName, inp.JobName, targetResp.DeploymentTargetID) // nolint:staticcheck
  38. if err != nil {
  39. return fmt.Errorf("unable to run job: %w", err)
  40. }
  41. triggeredBackgroundColor := color.FgGreen
  42. if inp.WaitForExit {
  43. triggeredBackgroundColor = color.FgBlue
  44. }
  45. color.New(triggeredBackgroundColor).Println("Triggered job with id:", resp.JobRunID) // nolint:errcheck,gosec
  46. if !inp.WaitForExit {
  47. return nil
  48. }
  49. decoded, err := base64.StdEncoding.DecodeString(currentAppRevisionResp.AppRevision.B64AppProto)
  50. if err != nil {
  51. return fmt.Errorf("unable to decode base64 app for revision: %w", err)
  52. }
  53. app := &porterv1.PorterApp{}
  54. err = helpers.UnmarshalContractObject(decoded, app)
  55. if err != nil {
  56. return fmt.Errorf("unable to unmarshal app for revision: %w", err)
  57. }
  58. timeoutSeconds := 1800 * time.Second
  59. for _, service := range app.ServiceList {
  60. if inp.JobName != service.Name {
  61. continue
  62. }
  63. if service.GetJobConfig() == nil {
  64. return fmt.Errorf("error getting job timeout")
  65. }
  66. timeoutSeconds = time.Duration(service.GetJobConfig().TimeoutSeconds) * time.Second
  67. }
  68. deadline := time.Now().Add(timeoutSeconds)
  69. color.New(color.FgBlue).Printf("Waiting %.f seconds for job to complete\n", timeoutSeconds.Seconds()) // nolint:errcheck,gosec
  70. time.Sleep(2 * time.Second)
  71. input := api.RunAppJobStatusInput{
  72. AppName: inp.AppName,
  73. ClusterID: inp.CLIConfig.Cluster,
  74. DeploymentTargetID: targetResp.DeploymentTargetID, // nolint:staticcheck
  75. DeploymentTargetNamespace: targetResp.Namespace,
  76. ServiceName: inp.JobName,
  77. JobRunID: resp.JobRunID,
  78. ProjectID: inp.CLIConfig.Project,
  79. }
  80. for time.Now().Before(deadline) {
  81. statusResp, err := inp.Client.RunAppJobStatus(ctx, input)
  82. if err != nil {
  83. return fmt.Errorf("unable to get job status: %w", err)
  84. }
  85. switch statusResp.Status {
  86. case porter_app_internal.InstanceStatusDescriptor_Pending:
  87. print(".")
  88. time.Sleep(WaitIntervalInSeconds)
  89. case porter_app_internal.InstanceStatusDescriptor_Running:
  90. print(".")
  91. time.Sleep(WaitIntervalInSeconds)
  92. case porter_app_internal.InstanceStatusDescriptor_Succeeded:
  93. print("\n")
  94. color.New(color.FgGreen).Println("Job completed successfully") // nolint:errcheck,gosec
  95. return nil
  96. case porter_app_internal.InstanceStatusDescriptor_Failed:
  97. return fmt.Errorf("job exited with non-zero exit code: %w", err)
  98. case porter_app_internal.InstanceStatusDescriptor_Unknown:
  99. return fmt.Errorf("unknown job status: %w", err)
  100. }
  101. }
  102. return fmt.Errorf("timeout exceeded")
  103. }