job.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. package cmd
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "github.com/fatih/color"
  7. api "github.com/porter-dev/porter/api/client"
  8. "github.com/porter-dev/porter/api/types"
  9. "github.com/porter-dev/porter/cli/cmd/deploy"
  10. "github.com/porter-dev/porter/cli/cmd/deploy/wait"
  11. "github.com/spf13/cobra"
  12. )
  13. var jobCmd = &cobra.Command{
  14. Use: "job",
  15. }
  16. var batchImageUpdateCmd = &cobra.Command{
  17. Use: "update-images",
  18. Short: "Updates the image tag of all jobs in a namespace which use a specific image.",
  19. Long: fmt.Sprintf(`
  20. %s
  21. Updates the image tag of all jobs in a namespace which use a specific image. Note that for all
  22. jobs with version <= v0.4.0, this will trigger a new run of a manual job. However, for versions
  23. >= v0.5.0, this will not create a new run of the job.
  24. Example commands:
  25. %s
  26. This command is namespace-scoped and uses the default namespace. To specify a different namespace,
  27. use the --namespace flag:
  28. %s
  29. `,
  30. color.New(color.FgBlue, color.Bold).Sprintf("Help for \"porter job update-images\":"),
  31. color.New(color.FgGreen, color.Bold).Sprintf("porter job update-images --image-repo-uri my-image.registry.io --tag newtag"),
  32. color.New(color.FgGreen, color.Bold).Sprintf("porter job update-images --namespace custom-namespace --image-repo-uri my-image.registry.io --tag newtag"),
  33. ),
  34. Run: func(cmd *cobra.Command, args []string) {
  35. err := checkLoginAndRun(args, batchImageUpdate)
  36. if err != nil {
  37. os.Exit(1)
  38. }
  39. },
  40. }
  41. var waitCmd = &cobra.Command{
  42. Use: "wait",
  43. Short: "Waits for a job to complete.",
  44. Long: fmt.Sprintf(`
  45. %s
  46. Waits for a job with a given name and namespace to complete a run. If the job completes successfully,
  47. this command exits with exit code 0. Otherwise, this command exits with exit code 1.
  48. Example commands:
  49. %s
  50. This command is namespace-scoped and uses the default namespace. To specify a different namespace,
  51. use the --namespace flag:
  52. %s
  53. `,
  54. color.New(color.FgBlue, color.Bold).Sprintf("Help for \"porter job wait\":"),
  55. color.New(color.FgGreen, color.Bold).Sprintf("porter job wait --name job-example"),
  56. color.New(color.FgGreen, color.Bold).Sprintf("porter job wait --name job-example --namespace custom-namespace"),
  57. ),
  58. Run: func(cmd *cobra.Command, args []string) {
  59. err := checkLoginAndRun(args, waitForJob)
  60. if err != nil {
  61. os.Exit(1)
  62. }
  63. },
  64. }
  65. var runJobCmd = &cobra.Command{
  66. Use: "run",
  67. Short: "Manually runs a job and waits for it to complete.",
  68. Long: fmt.Sprintf(`
  69. %s
  70. Manually runs a job and waits for it to complete a run. If the job completes successfully,
  71. this command exits with exit code 0. Otherwise, this command exits with exit code 1.
  72. Example commands:
  73. %s
  74. This command is namespace-scoped and uses the default namespace. To specify a different namespace,
  75. use the --namespace flag:
  76. %s
  77. `,
  78. color.New(color.FgBlue, color.Bold).Sprintf("Help for \"porter job run\":"),
  79. color.New(color.FgGreen, color.Bold).Sprintf("porter job run --name job-example"),
  80. color.New(color.FgGreen, color.Bold).Sprintf("porter job run --name job-example --namespace custom-namespace"),
  81. ),
  82. Run: func(cmd *cobra.Command, args []string) {
  83. err := checkLoginAndRun(args, runJob)
  84. if err != nil {
  85. os.Exit(1)
  86. }
  87. },
  88. }
  89. var imageRepoURI string
  90. func init() {
  91. rootCmd.AddCommand(jobCmd)
  92. jobCmd.AddCommand(batchImageUpdateCmd)
  93. jobCmd.AddCommand(waitCmd)
  94. jobCmd.AddCommand(runJobCmd)
  95. batchImageUpdateCmd.PersistentFlags().StringVar(
  96. &tag,
  97. "tag",
  98. "",
  99. "The new image tag to use.",
  100. )
  101. batchImageUpdateCmd.PersistentFlags().StringVar(
  102. &namespace,
  103. "namespace",
  104. "",
  105. "The namespace of the jobs.",
  106. )
  107. batchImageUpdateCmd.PersistentFlags().StringVarP(
  108. &imageRepoURI,
  109. "image-repo-uri",
  110. "i",
  111. "",
  112. "Image repo uri",
  113. )
  114. batchImageUpdateCmd.MarkPersistentFlagRequired("image-repo-uri")
  115. batchImageUpdateCmd.MarkPersistentFlagRequired("tag")
  116. waitCmd.PersistentFlags().StringVar(
  117. &namespace,
  118. "namespace",
  119. "",
  120. "The namespace of the jobs.",
  121. )
  122. waitCmd.PersistentFlags().StringVar(
  123. &name,
  124. "name",
  125. "",
  126. "The name of the jobs.",
  127. )
  128. waitCmd.MarkPersistentFlagRequired("name")
  129. runJobCmd.PersistentFlags().StringVar(
  130. &namespace,
  131. "namespace",
  132. "",
  133. "The namespace of the job.",
  134. )
  135. runJobCmd.PersistentFlags().StringVar(
  136. &name,
  137. "name",
  138. "",
  139. "The name of the job.",
  140. )
  141. runJobCmd.MarkPersistentFlagRequired("name")
  142. }
  143. func batchImageUpdate(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
  144. color.New(color.FgGreen).Println("Updating all jobs which use the image:", imageRepoURI)
  145. return client.UpdateBatchImage(
  146. context.TODO(),
  147. cliConf.Project,
  148. cliConf.Cluster,
  149. namespace,
  150. &types.UpdateImageBatchRequest{
  151. ImageRepoURI: imageRepoURI,
  152. Tag: tag,
  153. },
  154. )
  155. }
  156. // waits for a job with a given name/namespace
  157. func waitForJob(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
  158. return wait.WaitForJob(client, &wait.WaitOpts{
  159. ProjectID: cliConf.Project,
  160. ClusterID: cliConf.Cluster,
  161. Namespace: namespace,
  162. Name: name,
  163. })
  164. }
  165. func runJob(authRes *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
  166. color.New(color.FgGreen).Printf("Running job %s in namespace %s\n", name, namespace)
  167. waitForSuccessfulDeploy = true
  168. updateAgent := &deploy.DeployAgent{
  169. App: name,
  170. Client: client,
  171. Opts: &deploy.DeployOpts{
  172. SharedOpts: &deploy.SharedOpts{
  173. ProjectID: cliConf.Project,
  174. ClusterID: cliConf.Cluster,
  175. Namespace: namespace,
  176. },
  177. },
  178. }
  179. err := updateAgent.UpdateImageAndValues(map[string]interface{}{
  180. "paused": false,
  181. })
  182. if err != nil {
  183. return fmt.Errorf("error running job: %w", err)
  184. }
  185. err = waitForJob(authRes, client, args)
  186. if err != nil {
  187. return fmt.Errorf("error waiting for job to complete: %w", err)
  188. }
  189. return nil
  190. }