2
0

job.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. package wait
  2. import (
  3. "context"
  4. "fmt"
  5. "strconv"
  6. "time"
  7. "github.com/fatih/color"
  8. api "github.com/porter-dev/porter/api/client"
  9. v1 "k8s.io/api/batch/v1"
  10. )
  11. type WaitOpts struct {
  12. ProjectID, ClusterID uint
  13. Namespace, Name string
  14. }
  15. // WaitForJob waits for a job with a given name/namespace to complete its run
  16. // nolint:revive // bad naming convention
  17. func WaitForJob(ctx context.Context, client api.Client, opts *WaitOpts) error {
  18. // get the job release
  19. jobRelease, err := client.GetRelease(ctx, opts.ProjectID, opts.ClusterID, opts.Namespace, opts.Name)
  20. if err != nil {
  21. return err
  22. }
  23. // make sure the job chart has a manual job running
  24. pausedVal, ok := jobRelease.Release.Config["paused"]
  25. pausedErr := fmt.Errorf("this job template is not currently running a manual job")
  26. if !ok {
  27. return pausedErr
  28. }
  29. if pausedValBool, ok := pausedVal.(bool); ok && pausedValBool {
  30. return pausedErr
  31. }
  32. // attempt to parse out the timeout value for the job, given by `sidecar.timeout`
  33. // if it does not exist, we set the default to 30 minutes
  34. timeoutVal := getJobTimeoutValue(jobRelease.Release.Config)
  35. color.New(color.FgYellow).Printf("Waiting for timeout seconds %.1f\n", timeoutVal.Seconds())
  36. // if no job exists with the given revision, wait for the timeout value
  37. timeWait := time.Now().Add(timeoutVal)
  38. for time.Now().Before(timeWait) {
  39. // get the jobs for that job chart
  40. jobs, err := client.GetJobs(ctx, opts.ProjectID, opts.ClusterID, opts.Namespace, opts.Name)
  41. if err != nil {
  42. return err
  43. }
  44. job := getJobMatchingRevision(uint(jobRelease.Release.Version), jobs)
  45. if job == nil {
  46. time.Sleep(10 * time.Second)
  47. continue
  48. }
  49. // once job is running, wait for status to be completed, or failed
  50. // if failed, exit with non-zero exit code
  51. if job.Status.Failed > 0 {
  52. return fmt.Errorf("job failed")
  53. }
  54. if job.Status.Succeeded > 0 {
  55. return nil
  56. }
  57. // otherwise, return no error
  58. time.Sleep(10 * time.Second)
  59. }
  60. return fmt.Errorf("timed out waiting for job")
  61. }
  62. func getJobMatchingRevision(revision uint, jobs []v1.Job) *v1.Job {
  63. for _, job := range jobs {
  64. revisionLabel, revisionLabelExists := job.Labels["helm.sh/revision"]
  65. if !revisionLabelExists {
  66. continue
  67. }
  68. jobRevision, err := strconv.ParseUint(revisionLabel, 10, 64)
  69. if err != nil {
  70. continue
  71. }
  72. if uint(jobRevision) == revision {
  73. return &job
  74. }
  75. }
  76. return nil
  77. }
  78. func getJobTimeoutValue(values map[string]interface{}) time.Duration {
  79. defaultTimeout := time.Minute * 60
  80. sidecarInter, ok := values["sidecar"]
  81. if !ok {
  82. return defaultTimeout
  83. }
  84. sidecarVal, ok := sidecarInter.(map[string]interface{})
  85. if !ok {
  86. return defaultTimeout
  87. }
  88. timeoutInter, ok := sidecarVal["timeout"]
  89. if !ok {
  90. return defaultTimeout
  91. }
  92. timeoutVal, ok := timeoutInter.(float64)
  93. if !ok {
  94. return defaultTimeout
  95. }
  96. return time.Second * time.Duration(timeoutVal)
  97. }