job.go 2.8 KB

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