deploy.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967
  1. package cmd
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "time"
  9. "github.com/briandowns/spinner"
  10. "github.com/fatih/color"
  11. api "github.com/porter-dev/porter/api/client"
  12. "github.com/porter-dev/porter/api/types"
  13. "github.com/porter-dev/porter/cli/cmd/config"
  14. "github.com/porter-dev/porter/cli/cmd/deploy"
  15. "github.com/porter-dev/porter/cli/cmd/docker"
  16. "github.com/porter-dev/porter/cli/cmd/utils"
  17. templaterUtils "github.com/porter-dev/porter/internal/templater/utils"
  18. "github.com/spf13/cobra"
  19. "k8s.io/client-go/util/homedir"
  20. )
  21. // updateCmd represents the "porter update" base command when called
  22. // without any subcommands
  23. var updateCmd = &cobra.Command{
  24. Use: "update",
  25. Short: "Builds and updates a specified application given by the --app flag.",
  26. Long: fmt.Sprintf(`
  27. %s
  28. Builds and updates a specified application given by the --app flag. For example:
  29. %s
  30. This command will automatically build from a local path. The path can be configured via the
  31. --path flag. You can also overwrite the tag using the --tag flag. For example, to build from the
  32. local directory ~/path-to-dir with the tag "testing":
  33. %s
  34. If the application has a remote Git repository source configured, you can specify that the remote
  35. Git repository should be used to build the new image by specifying "--source github". Porter will use
  36. the latest commit from the remote repo and branch to update an application, and will use the latest
  37. commit as the image tag.
  38. %s
  39. To add new configuration or update existing configuration, you can pass a values.yaml file in via the
  40. --values flag. For example;
  41. %s
  42. If your application is set up to use a Dockerfile by default, you can use a buildpack via the flag
  43. "--method pack". Conversely, if your application is set up to use a buildpack by default, you can
  44. use a Dockerfile by passing the flag "--method docker". You can specify the relative path to a Dockerfile
  45. in your remote Git repository. For example, if a Dockerfile is found at ./docker/prod.Dockerfile, you can
  46. specify it as follows:
  47. %s
  48. `,
  49. color.New(color.FgBlue, color.Bold).Sprintf("Help for \"porter update\":"),
  50. color.New(color.FgGreen, color.Bold).Sprintf("porter update --app example-app"),
  51. color.New(color.FgGreen, color.Bold).Sprintf("porter update --app example-app --path ~/path-to-dir --tag testing"),
  52. color.New(color.FgGreen, color.Bold).Sprintf("porter update --app remote-git-app --source github"),
  53. color.New(color.FgGreen, color.Bold).Sprintf("porter update --app example-app --values my-values.yaml"),
  54. color.New(color.FgGreen, color.Bold).Sprintf("porter update --app example-app --method docker --dockerfile ./docker/prod.Dockerfile"),
  55. ),
  56. Run: func(cmd *cobra.Command, args []string) {
  57. err := checkLoginAndRun(args, updateFull)
  58. if err != nil {
  59. os.Exit(1)
  60. }
  61. },
  62. }
  63. var updateGetEnvCmd = &cobra.Command{
  64. Use: "get-env",
  65. Short: "Gets environment variables for a deployment for a specified application given by the --app flag.",
  66. Long: fmt.Sprintf(`
  67. %s
  68. Gets environment variables for a deployment for a specified application given by the --app
  69. flag. By default, env variables are printed via stdout for use in downstream commands:
  70. %s
  71. Output can also be written to a file via the --file flag, which should specify the
  72. destination path for a .env file. For example:
  73. %s
  74. `,
  75. color.New(color.FgBlue, color.Bold).Sprintf("Help for \"porter update get-env\":"),
  76. color.New(color.FgGreen, color.Bold).Sprintf("porter update get-env --app example-app | xargs"),
  77. color.New(color.FgGreen, color.Bold).Sprintf("porter update get-env --app example-app --file .env"),
  78. ),
  79. Run: func(cmd *cobra.Command, args []string) {
  80. err := checkLoginAndRun(args, updateGetEnv)
  81. if err != nil {
  82. os.Exit(1)
  83. }
  84. },
  85. }
  86. var updateBuildCmd = &cobra.Command{
  87. Use: "build",
  88. Short: "Builds a new version of the application specified by the --app flag.",
  89. Long: fmt.Sprintf(`
  90. %s
  91. Builds a new version of the application specified by the --app flag. Depending on the
  92. configured settings, this command may work automatically or will require a specified
  93. --method flag.
  94. If you have configured the Dockerfile path and/or a build context for this application,
  95. this command will by default use those settings, so you just need to specify the --app
  96. flag:
  97. %s
  98. If you have not linked the build-time requirements for this application, the command will
  99. use a local build. By default, the cloud-native buildpacks builder will automatically be run
  100. from the current directory. If you would like to change the build method, you can do so by
  101. using the --method flag, for example:
  102. %s
  103. When using "--method docker", you can specify the path to the Dockerfile using the
  104. --dockerfile flag. This will also override the Dockerfile path that you may have linked
  105. for the application:
  106. %s
  107. `,
  108. color.New(color.FgBlue, color.Bold).Sprintf("Help for \"porter update build\":"),
  109. color.New(color.FgGreen, color.Bold).Sprintf("porter update build --app example-app"),
  110. color.New(color.FgGreen, color.Bold).Sprintf("porter update build --app example-app --method docker"),
  111. color.New(color.FgGreen, color.Bold).Sprintf("porter update build --app example-app --method docker --dockerfile ./prod.Dockerfile"),
  112. ),
  113. Run: func(cmd *cobra.Command, args []string) {
  114. err := checkLoginAndRun(args, updateBuild)
  115. if err != nil {
  116. os.Exit(1)
  117. }
  118. },
  119. }
  120. var updatePushCmd = &cobra.Command{
  121. Use: "push",
  122. Short: "Pushes an image to a Docker registry linked to your Porter project.",
  123. Args: cobra.MaximumNArgs(1),
  124. Long: fmt.Sprintf(`
  125. %s
  126. Pushes a local Docker image to a registry linked to your Porter project. This command
  127. requires the project ID to be set either by using the %s command
  128. or the --project flag. For example, to push a local nginx image:
  129. %s
  130. %s
  131. Pushes a new image for an application specified by the --app flag. This command uses
  132. the image repository saved in the application config by default. For example, if an
  133. application "nginx" was created from the image repo "gcr.io/snowflake-123456/nginx",
  134. the following command would push the image "gcr.io/snowflake-123456/nginx:new-tag":
  135. %s
  136. This command will not use your pre-saved authentication set up via "docker login," so if you
  137. are using an image registry that was created outside of Porter, make sure that you have
  138. linked it via "porter connect".
  139. `,
  140. color.New(color.FgBlue, color.Bold).Sprintf("Help for \"porter update push\":"),
  141. color.New(color.FgBlue).Sprintf("porter config set-project"),
  142. color.New(color.FgGreen, color.Bold).Sprintf("porter update push gcr.io/snowflake-123456/nginx:1234567"),
  143. color.New(color.Bold).Sprintf("LEGACY USAGE:"),
  144. color.New(color.FgGreen, color.Bold).Sprintf("porter update push --app nginx --tag new-tag"),
  145. ),
  146. Run: func(cmd *cobra.Command, args []string) {
  147. err := checkLoginAndRun(args, updatePush)
  148. if err != nil {
  149. os.Exit(1)
  150. }
  151. },
  152. }
  153. var updateConfigCmd = &cobra.Command{
  154. Use: "config",
  155. Short: "Updates the configuration for an application specified by the --app flag.",
  156. Long: fmt.Sprintf(`
  157. %s
  158. Updates the configuration for an application specified by the --app flag, using the configuration
  159. given by the --values flag. This will trigger a new deployment for the application with
  160. new configuration set. Note that this will merge your existing configuration with configuration
  161. specified in the --values file. For example:
  162. %s
  163. You can update the configuration with only a new tag with the --tag flag, which will only update
  164. the image that the application uses if no --values file is specified:
  165. %s
  166. `,
  167. color.New(color.FgBlue, color.Bold).Sprintf("Help for \"porter update config\":"),
  168. color.New(color.FgGreen, color.Bold).Sprintf("porter update config --app example-app --values my-values.yaml"),
  169. color.New(color.FgGreen, color.Bold).Sprintf("porter update config --app example-app --tag custom-tag"),
  170. ),
  171. Run: func(cmd *cobra.Command, args []string) {
  172. err := checkLoginAndRun(args, updateUpgrade)
  173. if err != nil {
  174. os.Exit(1)
  175. }
  176. },
  177. }
  178. var updateEnvGroupCmd = &cobra.Command{
  179. Use: "env-group",
  180. Aliases: []string{"eg", "envgroup", "env-groups", "envgroups"},
  181. Short: "Updates an environment group's variables, specified by the --name flag.",
  182. Run: func(cmd *cobra.Command, args []string) {
  183. color.New(color.FgRed).Println("need to specify an operation to continue")
  184. },
  185. }
  186. var updateSetEnvGroupCmd = &cobra.Command{
  187. Use: "set",
  188. Short: "Sets the desired value of an environment variable in an env group in the form VAR=VALUE.",
  189. Run: func(cmd *cobra.Command, args []string) {
  190. err := checkLoginAndRun(args, updateSetEnvGroup)
  191. if err != nil {
  192. os.Exit(1)
  193. }
  194. },
  195. }
  196. var updateUnsetEnvGroupCmd = &cobra.Command{
  197. Use: "unset",
  198. Short: "Removes an environment variable from an env group.",
  199. Run: func(cmd *cobra.Command, args []string) {
  200. err := checkLoginAndRun(args, updateUnsetEnvGroup)
  201. if err != nil {
  202. os.Exit(1)
  203. }
  204. },
  205. }
  206. var app string
  207. var getEnvFileDest string
  208. var localPath string
  209. var tag string
  210. var dockerfile string
  211. var method string
  212. var stream bool
  213. var buildFlagsEnv []string
  214. var forcePush bool
  215. var useCache bool
  216. var value string
  217. var version uint
  218. var varType string
  219. func init() {
  220. buildFlagsEnv = []string{}
  221. rootCmd.AddCommand(updateCmd)
  222. updateCmd.PersistentFlags().StringVar(
  223. &app,
  224. "app",
  225. "",
  226. "Application in the Porter dashboard",
  227. )
  228. updateCmd.PersistentFlags().BoolVar(
  229. &useCache,
  230. "use-cache",
  231. false,
  232. "Whether to use cache (currently in beta)",
  233. )
  234. updateCmd.PersistentFlags().StringVar(
  235. &namespace,
  236. "namespace",
  237. "default",
  238. "Namespace of the application",
  239. )
  240. updateCmd.PersistentFlags().StringVar(
  241. &source,
  242. "source",
  243. "local",
  244. "the type of source (\"local\" or \"github\")",
  245. )
  246. updateCmd.PersistentFlags().StringVarP(
  247. &localPath,
  248. "path",
  249. "p",
  250. "",
  251. "If local build, the path to the build directory. If remote build, the relative path from the repository root to the build directory.",
  252. )
  253. updateCmd.PersistentFlags().StringVarP(
  254. &tag,
  255. "tag",
  256. "t",
  257. "",
  258. "the specified tag to use, if not \"latest\"",
  259. )
  260. updateCmd.PersistentFlags().StringVarP(
  261. &values,
  262. "values",
  263. "v",
  264. "",
  265. "Filepath to a values.yaml file",
  266. )
  267. updateCmd.PersistentFlags().StringVar(
  268. &dockerfile,
  269. "dockerfile",
  270. "",
  271. "the path to the dockerfile",
  272. )
  273. updateCmd.PersistentFlags().StringArrayVarP(
  274. &buildFlagsEnv,
  275. "env",
  276. "e",
  277. []string{},
  278. "Build-time environment variable, in the form 'VAR=VALUE'. These are not available at image runtime.",
  279. )
  280. updateCmd.PersistentFlags().StringVar(
  281. &method,
  282. "method",
  283. "",
  284. "the build method to use (\"docker\" or \"pack\")",
  285. )
  286. updateCmd.PersistentFlags().BoolVar(
  287. &stream,
  288. "stream",
  289. false,
  290. "stream update logs to porter dashboard",
  291. )
  292. updateCmd.PersistentFlags().BoolVar(
  293. &forceBuild,
  294. "force-build",
  295. false,
  296. "set this to force build an image (images tagged with \"latest\" have this set by default)",
  297. )
  298. updateCmd.PersistentFlags().BoolVar(
  299. &forcePush,
  300. "force-push",
  301. false,
  302. "set this to force push an image (images tagged with \"latest\" have this set by default)",
  303. )
  304. updateCmd.PersistentFlags().MarkDeprecated("force-build", "--force-build is now deprecated")
  305. updateCmd.PersistentFlags().MarkDeprecated("force-push", "--force-push is now deprecated")
  306. updateCmd.AddCommand(updateGetEnvCmd)
  307. updateGetEnvCmd.PersistentFlags().StringVar(
  308. &getEnvFileDest,
  309. "file",
  310. "",
  311. "file destination for .env files",
  312. )
  313. updateGetEnvCmd.MarkPersistentFlagRequired("app")
  314. updateBuildCmd.MarkPersistentFlagRequired("app")
  315. updateConfigCmd.MarkPersistentFlagRequired("app")
  316. updateEnvGroupCmd.PersistentFlags().StringVar(
  317. &name,
  318. "name",
  319. "",
  320. "the name of the environment group",
  321. )
  322. updateEnvGroupCmd.PersistentFlags().UintVar(
  323. &version,
  324. "version",
  325. 0,
  326. "the version of the environment group",
  327. )
  328. updateEnvGroupCmd.MarkPersistentFlagRequired("name")
  329. updateSetEnvGroupCmd.PersistentFlags().StringVar(
  330. &varType,
  331. "type",
  332. "normal",
  333. "the type of environment variable (either \"normal\" or \"secret\")",
  334. )
  335. updateEnvGroupCmd.AddCommand(updateSetEnvGroupCmd)
  336. updateEnvGroupCmd.AddCommand(updateUnsetEnvGroupCmd)
  337. updateCmd.AddCommand(updateBuildCmd)
  338. updateCmd.AddCommand(updatePushCmd)
  339. updateCmd.AddCommand(updateConfigCmd)
  340. updateCmd.AddCommand(updateEnvGroupCmd)
  341. }
  342. func updateFull(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
  343. fullPath, err := filepath.Abs(localPath)
  344. if err != nil {
  345. return err
  346. }
  347. if os.Getenv("GITHUB_ACTIONS") == "" && source == "local" && fullPath == homedir.HomeDir() {
  348. proceed, err := utils.PromptConfirm("You are deploying your home directory. Do you want to continue?", false)
  349. if err != nil {
  350. return err
  351. }
  352. if !proceed {
  353. return nil
  354. }
  355. }
  356. color.New(color.FgGreen).Println("Deploying app:", app)
  357. updateAgent, err := updateGetAgent(client)
  358. if err != nil {
  359. return err
  360. }
  361. err = updateBuildWithAgent(updateAgent)
  362. if err != nil {
  363. return err
  364. }
  365. err = updatePushWithAgent(updateAgent)
  366. if err != nil {
  367. return err
  368. }
  369. err = updateUpgradeWithAgent(updateAgent)
  370. if err != nil {
  371. return err
  372. }
  373. return nil
  374. }
  375. func updateGetEnv(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
  376. updateAgent, err := updateGetAgent(client)
  377. if err != nil {
  378. return err
  379. }
  380. buildEnv, err := updateAgent.GetBuildEnv(&deploy.GetBuildEnvOpts{
  381. UseNewConfig: false,
  382. })
  383. if err != nil {
  384. return err
  385. }
  386. // set the environment variables in the process
  387. err = updateAgent.SetBuildEnv(buildEnv)
  388. if err != nil {
  389. return err
  390. }
  391. // write the environment variables to either a file or stdout (stdout by default)
  392. return updateAgent.WriteBuildEnv(getEnvFileDest)
  393. }
  394. func updateBuild(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
  395. updateAgent, err := updateGetAgent(client)
  396. if err != nil {
  397. return err
  398. }
  399. return updateBuildWithAgent(updateAgent)
  400. }
  401. func updatePush(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
  402. if app == "" {
  403. if len(args) == 0 {
  404. return fmt.Errorf("please provide the docker image name")
  405. }
  406. image := args[0]
  407. registries, err := client.ListRegistries(context.Background(), cliConf.Project)
  408. if err != nil {
  409. return err
  410. }
  411. regs := *registries
  412. regID := uint(0)
  413. for _, reg := range regs {
  414. if strings.Contains(image, reg.URL) {
  415. regID = reg.ID
  416. break
  417. }
  418. }
  419. if regID == 0 {
  420. return fmt.Errorf("could not find registry for image: %s", image)
  421. }
  422. err = client.CreateRepository(context.Background(), cliConf.Project, regID,
  423. &types.CreateRegistryRepositoryRequest{
  424. ImageRepoURI: strings.Split(image, ":")[0],
  425. },
  426. )
  427. if err != nil {
  428. return err
  429. }
  430. agent, err := docker.NewAgentWithAuthGetter(client, cliConf.Project)
  431. if err != nil {
  432. return err
  433. }
  434. err = agent.PushImage(image)
  435. if err != nil {
  436. return err
  437. }
  438. return nil
  439. }
  440. updateAgent, err := updateGetAgent(client)
  441. if err != nil {
  442. return err
  443. }
  444. return updatePushWithAgent(updateAgent)
  445. }
  446. func updateUpgrade(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
  447. updateAgent, err := updateGetAgent(client)
  448. if err != nil {
  449. return err
  450. }
  451. return updateUpgradeWithAgent(updateAgent)
  452. }
  453. func updateSetEnvGroup(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
  454. if len(args) == 0 {
  455. return fmt.Errorf("required variable in the form of VAR=VALUE")
  456. }
  457. key, value, found := strings.Cut(args[0], "=")
  458. if !found {
  459. return fmt.Errorf("variable should be in the form of VAR=VALUE")
  460. }
  461. s := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
  462. s.Color("cyan")
  463. s.Suffix = fmt.Sprintf(" Fetching env group '%s' in namespace '%s'", name, namespace)
  464. s.Start()
  465. envGroupResp, err := client.GetEnvGroup(context.Background(), cliConf.Project, cliConf.Cluster, namespace,
  466. &types.GetEnvGroupRequest{
  467. Name: name, Version: version,
  468. },
  469. )
  470. s.Stop()
  471. if err != nil {
  472. return err
  473. }
  474. newEnvGroup := &types.CreateEnvGroupRequest{
  475. Name: envGroupResp.Name,
  476. Variables: envGroupResp.Variables,
  477. }
  478. delete(newEnvGroup.Variables, key)
  479. if varType == "secret" {
  480. newEnvGroup.SecretVariables = make(map[string]string)
  481. newEnvGroup.SecretVariables[key] = value
  482. s.Suffix = fmt.Sprintf(" Adding new secret variable '%s' to env group '%s' in namespace '%s'", key, name, namespace)
  483. } else {
  484. newEnvGroup.Variables[key] = value
  485. s.Suffix = fmt.Sprintf(" Adding new variable '%s' to env group '%s' in namespace '%s'", key, name, namespace)
  486. }
  487. s.Start()
  488. _, err = client.CreateEnvGroup(
  489. context.Background(), cliConf.Project, cliConf.Cluster, namespace, newEnvGroup,
  490. )
  491. s.Stop()
  492. if err != nil {
  493. return err
  494. }
  495. color.New(color.FgGreen).Println("env group successfully updated")
  496. return nil
  497. }
  498. func updateUnsetEnvGroup(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
  499. if len(args) == 0 {
  500. return fmt.Errorf("required variable name")
  501. }
  502. s := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
  503. s.Color("cyan")
  504. s.Suffix = fmt.Sprintf(" Fetching env group '%s' in namespace '%s'", name, namespace)
  505. s.Start()
  506. envGroupResp, err := client.GetEnvGroup(context.Background(), cliConf.Project, cliConf.Cluster, namespace,
  507. &types.GetEnvGroupRequest{
  508. Name: name, Version: version,
  509. },
  510. )
  511. s.Stop()
  512. if err != nil {
  513. return err
  514. }
  515. newEnvGroup := &types.CreateEnvGroupRequest{
  516. Name: envGroupResp.Name,
  517. Variables: envGroupResp.Variables,
  518. }
  519. delete(newEnvGroup.Variables, args[0])
  520. s.Suffix = fmt.Sprintf(" Removing variable '%s' from env group '%s' in namespace '%s'", args[0], name, namespace)
  521. s.Start()
  522. _, err = client.CreateEnvGroup(
  523. context.Background(), cliConf.Project, cliConf.Cluster, namespace, newEnvGroup,
  524. )
  525. s.Stop()
  526. if err != nil {
  527. return err
  528. }
  529. color.New(color.FgGreen).Println("env group successfully updated")
  530. return nil
  531. }
  532. // HELPER METHODS
  533. func updateGetAgent(client *api.Client) (*deploy.DeployAgent, error) {
  534. var buildMethod deploy.DeployBuildType
  535. if method != "" {
  536. buildMethod = deploy.DeployBuildType(method)
  537. }
  538. // add additional env, if they exist
  539. additionalEnv := make(map[string]string)
  540. for _, buildEnv := range buildFlagsEnv {
  541. if strSplArr := strings.SplitN(buildEnv, "=", 2); len(strSplArr) >= 2 {
  542. additionalEnv[strSplArr[0]] = strSplArr[1]
  543. }
  544. }
  545. // initialize the update agent
  546. return deploy.NewDeployAgent(client, app, &deploy.DeployOpts{
  547. SharedOpts: &deploy.SharedOpts{
  548. ProjectID: cliConf.Project,
  549. ClusterID: cliConf.Cluster,
  550. Namespace: namespace,
  551. LocalPath: localPath,
  552. LocalDockerfile: dockerfile,
  553. OverrideTag: tag,
  554. Method: buildMethod,
  555. AdditionalEnv: additionalEnv,
  556. UseCache: useCache,
  557. },
  558. Local: source != "github",
  559. })
  560. }
  561. func updateBuildWithAgent(updateAgent *deploy.DeployAgent) error {
  562. // build the deployment
  563. color.New(color.FgGreen).Println("Building docker image for", app)
  564. if stream {
  565. updateAgent.StreamEvent(types.SubEvent{
  566. EventID: "build",
  567. Name: "Build",
  568. Index: 100,
  569. Status: types.EventStatusInProgress,
  570. Info: "",
  571. })
  572. }
  573. if useCache {
  574. err := config.SetDockerConfig(updateAgent.Client)
  575. if err != nil {
  576. return err
  577. }
  578. }
  579. // read the values if necessary
  580. valuesObj, err := readValuesFile()
  581. if err != nil {
  582. return err
  583. }
  584. buildEnv, err := updateAgent.GetBuildEnv(&deploy.GetBuildEnvOpts{
  585. UseNewConfig: true,
  586. NewConfig: valuesObj,
  587. })
  588. if err != nil {
  589. if stream {
  590. // another concern: is it safe to ignore the error here?
  591. updateAgent.StreamEvent(types.SubEvent{
  592. EventID: "build",
  593. Name: "Build",
  594. Index: 110,
  595. Status: types.EventStatusFailed,
  596. Info: err.Error(),
  597. })
  598. }
  599. return err
  600. }
  601. // set the environment variables in the process
  602. err = updateAgent.SetBuildEnv(buildEnv)
  603. if err != nil {
  604. if stream {
  605. updateAgent.StreamEvent(types.SubEvent{
  606. EventID: "build",
  607. Name: "Build",
  608. Index: 120,
  609. Status: types.EventStatusFailed,
  610. Info: err.Error(),
  611. })
  612. }
  613. return err
  614. }
  615. if err := updateAgent.Build(nil); err != nil {
  616. if stream {
  617. updateAgent.StreamEvent(types.SubEvent{
  618. EventID: "build",
  619. Name: "Build",
  620. Index: 130,
  621. Status: types.EventStatusFailed,
  622. Info: err.Error(),
  623. })
  624. }
  625. return err
  626. }
  627. if stream {
  628. updateAgent.StreamEvent(types.SubEvent{
  629. EventID: "build",
  630. Name: "Build",
  631. Index: 140,
  632. Status: types.EventStatusSuccess,
  633. Info: "",
  634. })
  635. }
  636. return nil
  637. }
  638. func updatePushWithAgent(updateAgent *deploy.DeployAgent) error {
  639. if useCache {
  640. color.New(color.FgGreen).Println("Skipping image push for", app, "as use-cache is set")
  641. return nil
  642. }
  643. // push the deployment
  644. color.New(color.FgGreen).Println("Pushing new image for", app)
  645. if stream {
  646. updateAgent.StreamEvent(types.SubEvent{
  647. EventID: "push",
  648. Name: "Push",
  649. Index: 200,
  650. Status: types.EventStatusInProgress,
  651. Info: "",
  652. })
  653. }
  654. if err := updateAgent.Push(); err != nil {
  655. if stream {
  656. updateAgent.StreamEvent(types.SubEvent{
  657. EventID: "push",
  658. Name: "Push",
  659. Index: 210,
  660. Status: types.EventStatusFailed,
  661. Info: err.Error(),
  662. })
  663. }
  664. return err
  665. }
  666. if stream {
  667. updateAgent.StreamEvent(types.SubEvent{
  668. EventID: "push",
  669. Name: "Push",
  670. Index: 220,
  671. Status: types.EventStatusSuccess,
  672. Info: "",
  673. })
  674. }
  675. return nil
  676. }
  677. func updateUpgradeWithAgent(updateAgent *deploy.DeployAgent) error {
  678. // push the deployment
  679. color.New(color.FgGreen).Println("Upgrading configuration for", app)
  680. if stream {
  681. updateAgent.StreamEvent(types.SubEvent{
  682. EventID: "upgrade",
  683. Name: "Upgrade",
  684. Index: 300,
  685. Status: types.EventStatusInProgress,
  686. Info: "",
  687. })
  688. }
  689. var err error
  690. // read the values if necessary
  691. valuesObj, err := readValuesFile()
  692. if err != nil {
  693. return err
  694. }
  695. if err != nil {
  696. if stream {
  697. updateAgent.StreamEvent(types.SubEvent{
  698. EventID: "upgrade",
  699. Name: "Upgrade",
  700. Index: 310,
  701. Status: types.EventStatusFailed,
  702. Info: err.Error(),
  703. })
  704. }
  705. return err
  706. }
  707. if len(updateAgent.Opts.AdditionalEnv) > 0 {
  708. syncedEnv, err := deploy.GetSyncedEnv(
  709. updateAgent.Client,
  710. updateAgent.Release.Config,
  711. updateAgent.Opts.ProjectID,
  712. updateAgent.Opts.ClusterID,
  713. updateAgent.Opts.Namespace,
  714. false,
  715. )
  716. if err != nil {
  717. return err
  718. }
  719. for k := range updateAgent.Opts.AdditionalEnv {
  720. if _, ok := syncedEnv[k]; ok {
  721. return fmt.Errorf("environment variable %s already exists as part of a synced environment group", k)
  722. }
  723. }
  724. normalEnv, err := deploy.GetNormalEnv(
  725. updateAgent.Client,
  726. updateAgent.Release.Config,
  727. updateAgent.Opts.ProjectID,
  728. updateAgent.Opts.ClusterID,
  729. updateAgent.Opts.Namespace,
  730. false,
  731. )
  732. if err != nil {
  733. return err
  734. }
  735. // add the additional environment variables to container.env.normal
  736. for k, v := range updateAgent.Opts.AdditionalEnv {
  737. normalEnv[k] = v
  738. }
  739. valuesObj = templaterUtils.CoalesceValues(valuesObj, map[string]interface{}{
  740. "container": map[string]interface{}{
  741. "env": map[string]interface{}{
  742. "normal": normalEnv,
  743. },
  744. },
  745. })
  746. }
  747. err = updateAgent.UpdateImageAndValues(valuesObj)
  748. if err != nil {
  749. if stream {
  750. updateAgent.StreamEvent(types.SubEvent{
  751. EventID: "upgrade",
  752. Name: "Upgrade",
  753. Index: 320,
  754. Status: types.EventStatusFailed,
  755. Info: err.Error(),
  756. })
  757. }
  758. return err
  759. }
  760. if stream {
  761. updateAgent.StreamEvent(types.SubEvent{
  762. EventID: "upgrade",
  763. Name: "Upgrade",
  764. Index: 330,
  765. Status: types.EventStatusSuccess,
  766. Info: "",
  767. })
  768. }
  769. color.New(color.FgGreen).Println("Successfully updated", app)
  770. return nil
  771. }