job.go 6.8 KB

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