2
0

run_app_job.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  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. // DeploymentTargetName is the name of deployment target to run the job on
  23. DeploymentTargetName string
  24. AppName string
  25. JobName string
  26. // WaitForExit
  27. WaitForExit bool
  28. }
  29. // RunAppJob triggers a job run for an app and returns without waiting for the job to complete
  30. func RunAppJob(ctx context.Context, inp RunAppJobInput) error {
  31. currentAppRevisionResp, err := inp.Client.CurrentAppRevision(ctx, api.CurrentAppRevisionInput{
  32. ProjectID: inp.CLIConfig.Project,
  33. ClusterID: inp.CLIConfig.Cluster,
  34. AppName: inp.AppName,
  35. DeploymentTargetName: inp.DeploymentTargetName,
  36. DeploymentTargetID: "",
  37. })
  38. if err != nil {
  39. return fmt.Errorf("error getting current app revision: %w", err)
  40. }
  41. resp, err := inp.Client.RunAppJob(ctx, inp.CLIConfig.Project, inp.CLIConfig.Cluster, inp.AppName, inp.JobName, inp.DeploymentTargetName) // nolint:staticcheck
  42. if err != nil {
  43. return fmt.Errorf("unable to run job: %w", err)
  44. }
  45. triggeredBackgroundColor := color.FgGreen
  46. if inp.WaitForExit {
  47. triggeredBackgroundColor = color.FgBlue
  48. }
  49. color.New(triggeredBackgroundColor).Println("Triggered job with id:", resp.JobRunID) // nolint:errcheck,gosec
  50. if !inp.WaitForExit {
  51. return nil
  52. }
  53. decoded, err := base64.StdEncoding.DecodeString(currentAppRevisionResp.AppRevision.B64AppProto)
  54. if err != nil {
  55. return fmt.Errorf("unable to decode base64 app for revision: %w", err)
  56. }
  57. app := &porterv1.PorterApp{}
  58. err = helpers.UnmarshalContractObject(decoded, app)
  59. if err != nil {
  60. return fmt.Errorf("unable to unmarshal app for revision: %w", err)
  61. }
  62. timeoutSeconds := 1800 * time.Second
  63. for _, service := range app.ServiceList {
  64. if inp.JobName != service.Name {
  65. continue
  66. }
  67. if service.GetJobConfig() == nil {
  68. return fmt.Errorf("error getting job timeout")
  69. }
  70. timeoutSeconds = time.Duration(service.GetJobConfig().TimeoutSeconds) * time.Second
  71. }
  72. deadline := time.Now().Add(timeoutSeconds)
  73. color.New(color.FgBlue).Printf("Waiting %.f seconds for job to complete\n", timeoutSeconds.Seconds()) // nolint:errcheck,gosec
  74. time.Sleep(2 * time.Second)
  75. input := api.RunAppJobStatusInput{
  76. AppName: inp.AppName,
  77. ClusterID: inp.CLIConfig.Cluster,
  78. DeploymentTargetName: inp.DeploymentTargetName,
  79. ServiceName: inp.JobName,
  80. JobRunID: resp.JobRunID,
  81. ProjectID: inp.CLIConfig.Project,
  82. }
  83. for time.Now().Before(deadline) {
  84. statusResp, err := inp.Client.RunAppJobStatus(ctx, input)
  85. if err != nil {
  86. return fmt.Errorf("unable to get job status: %w", err)
  87. }
  88. switch statusResp.Status {
  89. case porter_app_internal.InstanceStatusDescriptor_Pending:
  90. print(".")
  91. time.Sleep(WaitIntervalInSeconds)
  92. case porter_app_internal.InstanceStatusDescriptor_Running:
  93. print(".")
  94. time.Sleep(WaitIntervalInSeconds)
  95. case porter_app_internal.InstanceStatusDescriptor_Succeeded:
  96. print("\n")
  97. color.New(color.FgGreen).Println("Job completed successfully") // nolint:errcheck,gosec
  98. return nil
  99. case porter_app_internal.InstanceStatusDescriptor_Failed:
  100. return fmt.Errorf("job exited with non-zero exit code: %w", err)
  101. case porter_app_internal.InstanceStatusDescriptor_Unknown:
  102. return fmt.Errorf("unknown job status: %w", err)
  103. }
  104. }
  105. return fmt.Errorf("timeout exceeded")
  106. }