job.go 6.6 KB

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