From 6f8badf056d9f79211f984e857403fca4234f143 Mon Sep 17 00:00:00 2001 From: Guido Tripaldi Date: Fri, 19 Jul 2019 11:11:37 +0200 Subject: [PATCH 1/8] Adds (preliminary) hot upgrade support --- lib/bootleg/tasks/build/remote.exs | 71 ++++++++++++++++++++++++++++ lib/bootleg/tasks/build_upgrade.exs | 12 +++++ lib/bootleg/tasks/deploy_upgrade.exs | 52 ++++++++++++++++++++ lib/bootleg/tasks/hot_downgrade.exs | 11 +++++ lib/bootleg/tasks/hot_upgrade.exs | 11 +++++ lib/bootleg/tasks/upgrade.exs | 8 ++++ lib/mix/tasks/build_upgrade.ex | 14 ++++++ lib/mix/tasks/deploy_upgrade.ex | 14 ++++++ lib/mix/tasks/hot_downgrade.ex | 14 ++++++ lib/mix/tasks/hot_upgrade.ex | 14 ++++++ lib/mix/tasks/upgrade.ex | 15 ++++++ 11 files changed, 236 insertions(+) create mode 100644 lib/bootleg/tasks/build_upgrade.exs create mode 100644 lib/bootleg/tasks/deploy_upgrade.exs create mode 100644 lib/bootleg/tasks/hot_downgrade.exs create mode 100644 lib/bootleg/tasks/hot_upgrade.exs create mode 100644 lib/bootleg/tasks/upgrade.exs create mode 100644 lib/mix/tasks/build_upgrade.ex create mode 100644 lib/mix/tasks/deploy_upgrade.ex create mode 100644 lib/mix/tasks/hot_downgrade.ex create mode 100644 lib/mix/tasks/hot_upgrade.ex create mode 100644 lib/mix/tasks/upgrade.ex diff --git a/lib/bootleg/tasks/build/remote.exs b/lib/bootleg/tasks/build/remote.exs index 4723da7..7fe0c67 100644 --- a/lib/bootleg/tasks/build/remote.exs +++ b/lib/bootleg/tasks/build/remote.exs @@ -87,6 +87,77 @@ task :clean do end end +## Hot Upgrade support + task :remote_build_upgrade do + build_role = Config.get_role(:build) + invoke(:init) + invoke(:clean_for_upgrade) + invoke(:remote_scm_update) + invoke(:compile) + invoke(:remote_generate_release_upgrade) + + if build_role.options[:release_workspace] do + invoke(:copy_build_release) + else + invoke(:download_release) + end + end + + task :clean_for_upgrade do + remote :build do + "ls" + end + |> Enum.map( fn result -> + with {:ok, stdout_list, _code, _host} when stdout_list != [] <- result do + + locations = + stdout_list + |> Keyword.get(:stdout) + |> String.split("\n") + |> Enum.drop(-1) + |> Enum.filter(fn el -> el != "_build" end) + |> Enum.join(" ") + + if locations != "" do + remote :build do + "rm -rvf #{locations}" + end + end + end + end) + end + + task :generate_upgrade_release do + invoke(:remote_generate_release_upgrade) + end + + task :remote_generate_release_upgrade do + mix_env = config({:mix_env, "prod"}) + source_path = config({:ex_path, ""}) + + release_args = + {:release_args, ["--quiet"]} + |> config() + |> Enum.join(" ") + + UI.info("Generating upgrade release...") + + remote :build, cd: source_path do + "MIX_ENV=#{mix_env} mix distillery.release --upgrade #{release_args}" + end + end + + task :remote_hot_upgrade do + app_name = "#{Config.app()}" + + UI.info("Upgrading #{app_name} to version: #{Config.version()}") + + remote :app do + "bin/#{app_name} upgrade #{Config.version()}" + end + end +## / Hot Upgrade support + task :copy_build_release do build_role = Config.get_role(:build) mix_env = config({:mix_env, "prod"}) diff --git a/lib/bootleg/tasks/build_upgrade.exs b/lib/bootleg/tasks/build_upgrade.exs new file mode 100644 index 0000000..2edd4da --- /dev/null +++ b/lib/bootleg/tasks/build_upgrade.exs @@ -0,0 +1,12 @@ +alias Bootleg.{UI, Config, DSL} +use Bootleg.DSL + +task :build_upgrade do + build_type = config({:build_type, "remote"}) + bootleg_env = config(:env) + UI.info("Starting #{build_type} build for #{bootleg_env} environment") + invoke(:"#{build_type}_verify_config") + invoke(:"#{build_type}_build_upgrade") +end + +before_task(:build, :verify_config) diff --git a/lib/bootleg/tasks/deploy_upgrade.exs b/lib/bootleg/tasks/deploy_upgrade.exs new file mode 100644 index 0000000..0392da6 --- /dev/null +++ b/lib/bootleg/tasks/deploy_upgrade.exs @@ -0,0 +1,52 @@ +alias Bootleg.{UI, Config, DSL} +use Bootleg.DSL + +task :deploy_upgrade do + app_role = Config.get_role(:app) + + if app_role.options[:release_workspace] do + invoke(:copy_deploy_release_upgrade) + else + invoke(:upload_release_upgrade) + end + + invoke(:unpack_release_upgrade) +end + +task :copy_deploy_release_upgrade do + app_role = Config.get_role(:app) + release_workspace = app_role.options[:release_workspace] + release = "#{Config.version()}.tar.gz" + source_path = Path.join(release_workspace, release) + dest_path = "#{Config.app()}.tar.gz" + + UI.info("Copying release archive from release upgrade workspace") + + remote :app do + "cp #{source_path} #{dest_path}" + end +end + +task :upload_release_upgrade do + remote_path = "#{Config.app()}.tar.gz" + local_archive_folder = "#{File.cwd!()}/releases" + local_path = Path.join(local_archive_folder, "#{Config.version()}.tar.gz") + UI.info("Uploading release upgrade archive") + upload(:app, local_path, remote_path) +end + +task :unpack_release_upgrade do + remote_path = "#{Config.app()}.tar.gz" + remote_dest = "#{Config.version()}/#{Config.app()}.tar.gz" + + UI.info("Unpacking release upgrade archive") + + remote :app do + "tar -zxvf #{remote_path}" + "cp #{remote_path} releases/#{remote_dest}" + end + + UI.info("Unpacked release upgrade archive") +end + + diff --git a/lib/bootleg/tasks/hot_downgrade.exs b/lib/bootleg/tasks/hot_downgrade.exs new file mode 100644 index 0000000..d1096cb --- /dev/null +++ b/lib/bootleg/tasks/hot_downgrade.exs @@ -0,0 +1,11 @@ +alias Bootleg.{UI, Config, DSL} +use Bootleg.DSL + +task :hot_downgrade do + build_type = config({:build_type, "remote"}) + bootleg_env = config(:env) + UI.info("Starting #{build_type} hot downgrade for #{bootleg_env} environment") + invoke(:"#{build_type}_hot_downgrade") +end + +before_task(:build, :verify_config) diff --git a/lib/bootleg/tasks/hot_upgrade.exs b/lib/bootleg/tasks/hot_upgrade.exs new file mode 100644 index 0000000..bb46e67 --- /dev/null +++ b/lib/bootleg/tasks/hot_upgrade.exs @@ -0,0 +1,11 @@ +alias Bootleg.{UI, Config, DSL} +use Bootleg.DSL + +task :hot_upgrade do + build_type = config({:build_type, "remote"}) + bootleg_env = config(:env) + UI.info("Starting #{build_type} hot upgrade for #{bootleg_env} environment") + invoke(:"#{build_type}_hot_upgrade") +end + +before_task(:build, :verify_config) diff --git a/lib/bootleg/tasks/upgrade.exs b/lib/bootleg/tasks/upgrade.exs new file mode 100644 index 0000000..44c5440 --- /dev/null +++ b/lib/bootleg/tasks/upgrade.exs @@ -0,0 +1,8 @@ +alias Bootleg.{Config, DSL} +use DSL + +task :upgrade do + invoke(:build_upgrade) + invoke(:deploy_upgrade) + invoke(:hot_upgrade) +end diff --git a/lib/mix/tasks/build_upgrade.ex b/lib/mix/tasks/build_upgrade.ex new file mode 100644 index 0000000..e8c0ae2 --- /dev/null +++ b/lib/mix/tasks/build_upgrade.ex @@ -0,0 +1,14 @@ +defmodule Mix.Tasks.Bootleg.BuildUpgrade do + use Bootleg.MixTask, :build_upgrade + + @shortdoc "Build a release for upgrade" + + @moduledoc """ + Build a release for upgrade + + # Usage: + + * mix bootleg.build + + """ +end diff --git a/lib/mix/tasks/deploy_upgrade.ex b/lib/mix/tasks/deploy_upgrade.ex new file mode 100644 index 0000000..e41014d --- /dev/null +++ b/lib/mix/tasks/deploy_upgrade.ex @@ -0,0 +1,14 @@ +defmodule Mix.Tasks.Bootleg.DeployUpgrade do + use Bootleg.MixTask, :deploy_upgrade + + @shortdoc "Deploy an upgrade release" + + @moduledoc """ + Deploy an upgrade release + + # Usage: + + * mix bootleg.deploy_upgrade + + """ +end diff --git a/lib/mix/tasks/hot_downgrade.ex b/lib/mix/tasks/hot_downgrade.ex new file mode 100644 index 0000000..9deb45f --- /dev/null +++ b/lib/mix/tasks/hot_downgrade.ex @@ -0,0 +1,14 @@ +defmodule Mix.Tasks.Bootleg.HotDowngrade do + use Bootleg.MixTask, :hot_downgrade + + @shortdoc "Downgrade a running release with the last release" + + @moduledoc """ + Downgrade a running release with the last release + + # Usage: + + * mix bootleg.hot_downgrade + + """ +end diff --git a/lib/mix/tasks/hot_upgrade.ex b/lib/mix/tasks/hot_upgrade.ex new file mode 100644 index 0000000..ffbd857 --- /dev/null +++ b/lib/mix/tasks/hot_upgrade.ex @@ -0,0 +1,14 @@ +defmodule Mix.Tasks.Bootleg.HotUpgrade do + use Bootleg.MixTask, :hot_upgrade + + @shortdoc "Upgrade a running release with the last release" + + @moduledoc """ + Upgrade a running release with the last release + + # Usage: + + * mix bootleg.hot_upgrade + + """ +end diff --git a/lib/mix/tasks/upgrade.ex b/lib/mix/tasks/upgrade.ex new file mode 100644 index 0000000..f6bac3e --- /dev/null +++ b/lib/mix/tasks/upgrade.ex @@ -0,0 +1,15 @@ +defmodule Mix.Tasks.Bootleg.Upgrade do + use Bootleg.MixTask, :upgrade + + @shortdoc "Build, deploy, and hot upgrade a release all in one command." + + @moduledoc """ + Build, deploy, and hot upgrade a release all in one command. + + Note that this comand will not do an Ecto migration. + + # Usage: + + * mix bootleg.upgrade + """ +end From 0f998a2079481518f56d8f99291e9666f60d7b6f Mon Sep 17 00:00:00 2001 From: Guido Tripaldi Date: Tue, 23 Jul 2019 12:11:20 +0200 Subject: [PATCH 2/8] Removes added code --- lib/bootleg/tasks/build/remote.exs | 71 ------------------------------ 1 file changed, 71 deletions(-) diff --git a/lib/bootleg/tasks/build/remote.exs b/lib/bootleg/tasks/build/remote.exs index 7fe0c67..4723da7 100644 --- a/lib/bootleg/tasks/build/remote.exs +++ b/lib/bootleg/tasks/build/remote.exs @@ -87,77 +87,6 @@ task :clean do end end -## Hot Upgrade support - task :remote_build_upgrade do - build_role = Config.get_role(:build) - invoke(:init) - invoke(:clean_for_upgrade) - invoke(:remote_scm_update) - invoke(:compile) - invoke(:remote_generate_release_upgrade) - - if build_role.options[:release_workspace] do - invoke(:copy_build_release) - else - invoke(:download_release) - end - end - - task :clean_for_upgrade do - remote :build do - "ls" - end - |> Enum.map( fn result -> - with {:ok, stdout_list, _code, _host} when stdout_list != [] <- result do - - locations = - stdout_list - |> Keyword.get(:stdout) - |> String.split("\n") - |> Enum.drop(-1) - |> Enum.filter(fn el -> el != "_build" end) - |> Enum.join(" ") - - if locations != "" do - remote :build do - "rm -rvf #{locations}" - end - end - end - end) - end - - task :generate_upgrade_release do - invoke(:remote_generate_release_upgrade) - end - - task :remote_generate_release_upgrade do - mix_env = config({:mix_env, "prod"}) - source_path = config({:ex_path, ""}) - - release_args = - {:release_args, ["--quiet"]} - |> config() - |> Enum.join(" ") - - UI.info("Generating upgrade release...") - - remote :build, cd: source_path do - "MIX_ENV=#{mix_env} mix distillery.release --upgrade #{release_args}" - end - end - - task :remote_hot_upgrade do - app_name = "#{Config.app()}" - - UI.info("Upgrading #{app_name} to version: #{Config.version()}") - - remote :app do - "bin/#{app_name} upgrade #{Config.version()}" - end - end -## / Hot Upgrade support - task :copy_build_release do build_role = Config.get_role(:build) mix_env = config({:mix_env, "prod"}) From fadb167809d16c8ebfdd5552a888fa7f7277cc40 Mon Sep 17 00:00:00 2001 From: Guido Tripaldi Date: Tue, 23 Jul 2019 12:32:15 +0200 Subject: [PATCH 3/8] Adds hot upgrade support to updated task/build/remote.exs --- lib/bootleg/tasks/build/remote.exs | 70 ++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/lib/bootleg/tasks/build/remote.exs b/lib/bootleg/tasks/build/remote.exs index 4723da7..8761b80 100644 --- a/lib/bootleg/tasks/build/remote.exs +++ b/lib/bootleg/tasks/build/remote.exs @@ -294,3 +294,73 @@ task :pull_remote do end before_task(:pull_remote, :verify_repo_config) + +# Hot Upgrade support +task :remote_build_upgrade do + build_role = Config.get_role(:build) + invoke(:init) + invoke(:clean_for_upgrade) + invoke(:remote_scm_update) + invoke(:compile) + invoke(:remote_generate_release_upgrade) + + if build_role.options[:release_workspace] do + invoke(:copy_build_release) + else + invoke(:download_release) + end +end + +task :clean_for_upgrade do + remote :build do + "ls" + end + |> Enum.map( fn result -> + with {:ok, stdout_list, _code, _host} when stdout_list != [] <- result do + + locations = + stdout_list + |> Keyword.get(:stdout) + |> String.split("\n") + |> Enum.drop(-1) + |> Enum.filter(fn el -> el != "_build" end) + |> Enum.join(" ") + + if locations != "" do + remote :build do + "rm -rvf #{locations}" + end + end + end + end) +end + +task :generate_upgrade_release do + invoke(:remote_generate_release_upgrade) +end + +task :remote_generate_release_upgrade do + mix_env = config({:mix_env, "prod"}) + source_path = config({:ex_path, ""}) + + release_args = + {:release_args, ["--quiet"]} + |> config() + |> Enum.join(" ") + + UI.info("Generating upgrade release...") + + remote :build, cd: source_path do + "MIX_ENV=#{mix_env} mix distillery.release --upgrade #{release_args}" + end +end + +task :remote_hot_upgrade do + app_name = "#{Config.app()}" + + UI.info("Upgrading #{app_name} to version: #{Config.version()}") + + remote :app do + "bin/#{app_name} upgrade #{Config.version()}" + end +end From 7ed1202c74f15a065e22becc29481546913e0ceb Mon Sep 17 00:00:00 2001 From: Guido Tripaldi Date: Tue, 23 Jul 2019 16:56:07 +0200 Subject: [PATCH 4/8] Adds hot upgrade documentation --- lib/mix/tasks/build_upgrade.ex | 17 +++- lib/mix/tasks/deploy_upgrade.ex | 17 +++- lib/mix/tasks/hot_downgrade.ex | 17 +++- lib/mix/tasks/hot_upgrade.ex | 13 ++- lib/mix/tasks/upgrade.ex | 166 +++++++++++++++++++++++++++++++- 5 files changed, 219 insertions(+), 11 deletions(-) diff --git a/lib/mix/tasks/build_upgrade.ex b/lib/mix/tasks/build_upgrade.ex index e8c0ae2..9e4283a 100644 --- a/lib/mix/tasks/build_upgrade.ex +++ b/lib/mix/tasks/build_upgrade.ex @@ -6,9 +6,22 @@ defmodule Mix.Tasks.Bootleg.BuildUpgrade do @moduledoc """ Build a release for upgrade - # Usage: + ## Usage - * mix bootleg.build + mix bootleg.build + ## Caution + + Please never try to hot upgrade a running application without + having first a good understand of how a hot upgrade is performed, + its limitations and steps required. + + ## Documentation + + Please see the "Hot upgrading a running application" section + of `bootleg.upgrade` documentation for an overview of + the hot upgrade process: + + mix help bootleg.upgrade """ end diff --git a/lib/mix/tasks/deploy_upgrade.ex b/lib/mix/tasks/deploy_upgrade.ex index e41014d..9a510a1 100644 --- a/lib/mix/tasks/deploy_upgrade.ex +++ b/lib/mix/tasks/deploy_upgrade.ex @@ -6,9 +6,22 @@ defmodule Mix.Tasks.Bootleg.DeployUpgrade do @moduledoc """ Deploy an upgrade release - # Usage: + ## Usage - * mix bootleg.deploy_upgrade + mix bootleg.deploy_upgrade + ## Caution + + Please never try to hot upgrade a running application without + having first a good understand of how a hot upgrade is performed, + its limitations and steps required. + + ## Documentation + + Please see the "Hot upgrading a running application" section + of `bootleg.upgrade` documentation for an overview of + the hot upgrade process: + + mix help bootleg.upgrade """ end diff --git a/lib/mix/tasks/hot_downgrade.ex b/lib/mix/tasks/hot_downgrade.ex index 9deb45f..a5e0091 100644 --- a/lib/mix/tasks/hot_downgrade.ex +++ b/lib/mix/tasks/hot_downgrade.ex @@ -6,9 +6,22 @@ defmodule Mix.Tasks.Bootleg.HotDowngrade do @moduledoc """ Downgrade a running release with the last release - # Usage: + ## Usage - * mix bootleg.hot_downgrade + mix bootleg.hot_downgrade + ## Caution + + Please never try to hot upgrade a running application without + having first a good understand of how a hot upgrade is performed, + its limitations and steps required. + + ## Documentation + + Please see the "Hot upgrading a running application" section + of `bootleg.upgrade` documentation for an overview of + the hot upgrade process: + + mix help bootleg.upgrade """ end diff --git a/lib/mix/tasks/hot_upgrade.ex b/lib/mix/tasks/hot_upgrade.ex index ffbd857..cb6c033 100644 --- a/lib/mix/tasks/hot_upgrade.ex +++ b/lib/mix/tasks/hot_upgrade.ex @@ -6,9 +6,18 @@ defmodule Mix.Tasks.Bootleg.HotUpgrade do @moduledoc """ Upgrade a running release with the last release - # Usage: + ## Caution - * mix bootleg.hot_upgrade + Please never try to hot upgrade a running application without + having first a good understand of how a hot upgrade is performed, + its limitations and steps required. + ## Documentation + + Please see the "Hot upgrading a running application" section + of `bootleg.upgrade` documentation for an overview of + the hot upgrade process: + + mix help bootleg.upgrade """ end diff --git a/lib/mix/tasks/upgrade.ex b/lib/mix/tasks/upgrade.ex index f6bac3e..8476289 100644 --- a/lib/mix/tasks/upgrade.ex +++ b/lib/mix/tasks/upgrade.ex @@ -4,12 +4,172 @@ defmodule Mix.Tasks.Bootleg.Upgrade do @shortdoc "Build, deploy, and hot upgrade a release all in one command." @moduledoc """ - Build, deploy, and hot upgrade a release all in one command. + Build, deploy, and hot upgrade a new release all in one command. - Note that this comand will not do an Ecto migration. + Note that this comand will _not_ do an Ecto migration. - # Usage: + ## Usage: * mix bootleg.upgrade + + ## Caution + + Please never try to hot upgrade a running application without + having first a good understand of how a hot upgrade is performed, + its limitations and steps required. + + See nex section for an overview of the hot upgrade process. + + ## Hot upgrading a running application + + Hot upgrade is the process that allows to seamlessly change + the code of a running application without the need to + stopping and restarting it, i.e. mantaining active the + service in production. + + This is one of the most interesting capabilities of Erlang/OTP, + but it is a very complex process that *cannot* be fully + automated, i.e. require a good knowledge of the tecnologies + involved and the configurations files needed and their locations + at every stage of the process. You have also to know how to + recognize when a hot upgrade isn't an advisable action, + because it could have some severe limitations + and unwanted consequences in some circumstances. + + Therefore it is strongly advised you read the official + documentation about the hot upgrade stuff on the Erlang/OTP + website, and how Distillery, the technolgy underlying + the Bootleg task, accomplished that. + + Here it is a selected - but not exaustive - list of important + pieces of documentation to read: + + # OTP Design Principles - Releases + http://erlang.org/doc/design_principles/release_structure.html + + # OTP Design Principles - Release Handling + http://erlang.org/doc/design_principles/release_handling.html + + # System Architecture Support Libraries - appup + http://erlang.org/doc/man/appup.html + + # Distillery - Hot upgrades and downgrades + https://hexdocs.pm/distillery/guides/upgrades_and_downgrades.html + + # Distillery - Appups + https://hexdocs.pm/distillery/guides/appups.html + +### Bootleg hot upgrade task + +In the following description we assume that the development +enviroinment is organized in this way (the build and +the production places can be the same machine): + + * the development machine - where you edit and + test locally your app source files; + + * the build machine - the computer where you will transfer to + and compile the committed source code; + + * the production server - the server where you will deploy + (transfer to and run) the code previously compiled on + the build machine. + +Bootleg helps you in the hot upgrade process providing +some specific tasks: + + * mix bootleg.build_upgrade + will tranfer the last committed source code of your application + from the development machine to the build directory of + your build machine, for example `~/build/myapp/`, then + it will clean the directory from the previous code deleting + every file but the `_buil` directory, it will generate the + `appup` file and compile the newest app release. + Please note th before you can use this task for the first time, + you have to deploy your _first version_ of your app using + `bootleg.build`, `bootleg.deploy` and `bootleg.start` + (or `bootleg.update`); + + * mix bootleg.deploy_upgrade + will transfer the tarball of the compiled app from the + build machine to the production directory of the production + machine, e.g. `~/production/myapp/` + + then will extract and setting up the needed files; + + * mix bootleg.hot_upgrade + will call `mix distillery upgrade ` that + will upgrade the running app to the last version. Notice that + you *cannot* use this task if the app is not running, or + if it there is a mismatch in the version numbers of the + deployed versions. + + * mix bootleg.upgrade + Call in sequences the above tasks in just one command. + +### A step-by-step example + +Given you have configured the first version of your app with all +the needed and appropriately customized Bootleg configuration files, +you can go through the following steps to release and run the +first version and subsequentely hot upgrade it to the newest +versions: + +First version of your app: + + # Step 1 - deploy the first version of your app + edit the version number of your in the mix.exs file + (or in the file if you use an external reference), + to the first version, e.g. 0.1.0; + + # Step 2 - Commit + commit the changes you've made in step 1; + + # Step 3 - Build the first version + use `mix bootleg.build` (not bootleg.build_upgrade!) to build + your first version; + + # Step 4 - Deploy the first version + use `mix bootleg.deploy` (not bootleg.build_upgrade!) to deploy + your first version; + + # Step 5 - Run the first version + use `mix bootleg.start` to run the app + + now your first version is up and running. To upgrade it + to the future version, you have to follow these steps instead. + +Following versions: + + # Step 1 - update the version number + e.g. 0.2.0 + + # Step 2 - Commit + + # Step 3 - Build the new version + use `mix bootleg.build_upgrade` + + # Step 4 - Deploy the new version + use `mix bootleg.deploy_upgrade` + + # Step 5 - Hot upgrade the new version + use `mix bootleg.hot_upgrade` + + (or you can execute just the `bootleg.upgrade` + that packs the previous tasks together if you don't need to + manually adjust the created `appup` file) + + Now you have an upgraded version running. But if you stop + and restart it, the previous version will be launched instead + of the most recent. This is useful because if your new version + has some blocking bug, you can easily restart the service to the last + working release. + + If you are shure that you want to having the last version restarted, + just delete the folder `~/production/myapp/var`. This folder contains + the file `start_erl.data` that list the version number to start with. + Deleting the `var` folder will automatically create it next time the app + is started, with the last version number. + """ end From 8e2a81b44fba67345ae0bcc0a7bc47a30e201159 Mon Sep 17 00:00:00 2001 From: Guido Tripaldi Date: Tue, 23 Jul 2019 17:06:45 +0200 Subject: [PATCH 5/8] Changes the heredoc identation --- lib/mix/tasks/upgrade.ex | 220 +++++++++++++++++++-------------------- 1 file changed, 110 insertions(+), 110 deletions(-) diff --git a/lib/mix/tasks/upgrade.ex b/lib/mix/tasks/upgrade.ex index 8476289..232314f 100644 --- a/lib/mix/tasks/upgrade.ex +++ b/lib/mix/tasks/upgrade.ex @@ -59,117 +59,117 @@ defmodule Mix.Tasks.Bootleg.Upgrade do # Distillery - Appups https://hexdocs.pm/distillery/guides/appups.html -### Bootleg hot upgrade task - -In the following description we assume that the development -enviroinment is organized in this way (the build and -the production places can be the same machine): - - * the development machine - where you edit and - test locally your app source files; - - * the build machine - the computer where you will transfer to - and compile the committed source code; - - * the production server - the server where you will deploy - (transfer to and run) the code previously compiled on - the build machine. - -Bootleg helps you in the hot upgrade process providing -some specific tasks: - - * mix bootleg.build_upgrade - will tranfer the last committed source code of your application - from the development machine to the build directory of - your build machine, for example `~/build/myapp/`, then - it will clean the directory from the previous code deleting - every file but the `_buil` directory, it will generate the - `appup` file and compile the newest app release. - Please note th before you can use this task for the first time, - you have to deploy your _first version_ of your app using - `bootleg.build`, `bootleg.deploy` and `bootleg.start` - (or `bootleg.update`); - - * mix bootleg.deploy_upgrade - will transfer the tarball of the compiled app from the - build machine to the production directory of the production - machine, e.g. `~/production/myapp/` - - then will extract and setting up the needed files; - - * mix bootleg.hot_upgrade - will call `mix distillery upgrade ` that - will upgrade the running app to the last version. Notice that - you *cannot* use this task if the app is not running, or - if it there is a mismatch in the version numbers of the - deployed versions. - - * mix bootleg.upgrade - Call in sequences the above tasks in just one command. - -### A step-by-step example - -Given you have configured the first version of your app with all -the needed and appropriately customized Bootleg configuration files, -you can go through the following steps to release and run the -first version and subsequentely hot upgrade it to the newest -versions: - -First version of your app: - - # Step 1 - deploy the first version of your app - edit the version number of your in the mix.exs file - (or in the file if you use an external reference), - to the first version, e.g. 0.1.0; - - # Step 2 - Commit - commit the changes you've made in step 1; - - # Step 3 - Build the first version - use `mix bootleg.build` (not bootleg.build_upgrade!) to build - your first version; - - # Step 4 - Deploy the first version - use `mix bootleg.deploy` (not bootleg.build_upgrade!) to deploy - your first version; - - # Step 5 - Run the first version - use `mix bootleg.start` to run the app - - now your first version is up and running. To upgrade it - to the future version, you have to follow these steps instead. - -Following versions: - - # Step 1 - update the version number - e.g. 0.2.0 - - # Step 2 - Commit - - # Step 3 - Build the new version - use `mix bootleg.build_upgrade` - - # Step 4 - Deploy the new version - use `mix bootleg.deploy_upgrade` - - # Step 5 - Hot upgrade the new version - use `mix bootleg.hot_upgrade` - - (or you can execute just the `bootleg.upgrade` - that packs the previous tasks together if you don't need to - manually adjust the created `appup` file) - - Now you have an upgraded version running. But if you stop - and restart it, the previous version will be launched instead - of the most recent. This is useful because if your new version - has some blocking bug, you can easily restart the service to the last - working release. + ### Bootleg hot upgrade task + + In the following description we assume that the development + enviroinment is organized in this way (the build and + the production places can be the same machine): + + * the development machine - where you edit and + test locally your app source files; + + * the build machine - the computer where you will transfer to + and compile the committed source code; + + * the production server - the server where you will deploy + (transfer to and run) the code previously compiled on + the build machine. + + Bootleg helps you in the hot upgrade process providing + some specific tasks: + + * mix bootleg.build_upgrade + will tranfer the last committed source code of your application + from the development machine to the build directory of + your build machine, for example `~/build/myapp/`, then + it will clean the directory from the previous code deleting + every file but the `_buil` directory, it will generate the + `appup` file and compile the newest app release. + Please note th before you can use this task for the first time, + you have to deploy your _first version_ of your app using + `bootleg.build`, `bootleg.deploy` and `bootleg.start` + (or `bootleg.update`); + + * mix bootleg.deploy_upgrade + will transfer the tarball of the compiled app from the + build machine to the production directory of the production + machine, e.g. `~/production/myapp/` + + then will extract and setting up the needed files; + + * mix bootleg.hot_upgrade + will call `mix distillery upgrade ` that + will upgrade the running app to the last version. Notice that + you *cannot* use this task if the app is not running, or + if it there is a mismatch in the version numbers of the + deployed versions. + + * mix bootleg.upgrade + Call in sequences the above tasks in just one command. + + ### A step-by-step example + + Given you have configured the first version of your app with all + the needed and appropriately customized Bootleg configuration files, + you can go through the following steps to release and run the + first version and subsequentely hot upgrade it to the newest + versions: + + First version of your app: + + # Step 1 - deploy the first version of your app + edit the version number of your in the mix.exs file + (or in the file if you use an external reference), + to the first version, e.g. 0.1.0; + + # Step 2 - Commit + commit the changes you've made in step 1; + + # Step 3 - Build the first version + use `mix bootleg.build` (not bootleg.build_upgrade!) to build + your first version; + + # Step 4 - Deploy the first version + use `mix bootleg.deploy` (not bootleg.build_upgrade!) to deploy + your first version; + + # Step 5 - Run the first version + use `mix bootleg.start` to run the app + + now your first version is up and running. To upgrade it + to the future version, you have to follow these steps instead. + + Following versions: + + # Step 1 - update the version number + e.g. 0.2.0 + + # Step 2 - Commit + + # Step 3 - Build the new version + use `mix bootleg.build_upgrade` + + # Step 4 - Deploy the new version + use `mix bootleg.deploy_upgrade` + + # Step 5 - Hot upgrade the new version + use `mix bootleg.hot_upgrade` + + (or you can execute just the `bootleg.upgrade` + that packs the previous tasks together if you don't need to + manually adjust the created `appup` file) - If you are shure that you want to having the last version restarted, - just delete the folder `~/production/myapp/var`. This folder contains - the file `start_erl.data` that list the version number to start with. - Deleting the `var` folder will automatically create it next time the app - is started, with the last version number. + Now you have an upgraded version running. But if you stop + and restart it, the previous version will be launched instead + of the most recent. This is useful because if your new version + has some blocking bug, you can easily restart the service to the last + working release. + + If you are shure that you want to having the last version restarted, + just delete the folder `~/production/myapp/var`. This folder contains + the file `start_erl.data` that list the version number to start with. + Deleting the `var` folder will automatically create it next time the app + is started, with the last version number. """ end From 4d86d1122e0d16da405ce4c52e7b077ead91e8e3 Mon Sep 17 00:00:00 2001 From: Guido Tripaldi Date: Tue, 23 Jul 2019 17:18:13 +0200 Subject: [PATCH 6/8] Formats files --- lib/bootleg/tasks/build/remote.exs | 39 ++++++++++--------- lib/bootleg/tasks/deploy_upgrade.exs | 2 - lib/mix/tasks/upgrade.ex | 56 ++++++++++++++-------------- 3 files changed, 47 insertions(+), 50 deletions(-) diff --git a/lib/bootleg/tasks/build/remote.exs b/lib/bootleg/tasks/build/remote.exs index 8761b80..41b693c 100644 --- a/lib/bootleg/tasks/build/remote.exs +++ b/lib/bootleg/tasks/build/remote.exs @@ -313,26 +313,25 @@ end task :clean_for_upgrade do remote :build do - "ls" - end - |> Enum.map( fn result -> - with {:ok, stdout_list, _code, _host} when stdout_list != [] <- result do - - locations = - stdout_list - |> Keyword.get(:stdout) - |> String.split("\n") - |> Enum.drop(-1) - |> Enum.filter(fn el -> el != "_build" end) - |> Enum.join(" ") - - if locations != "" do - remote :build do - "rm -rvf #{locations}" - end - end + "ls" + end + |> Enum.map(fn result -> + with {:ok, stdout_list, _code, _host} when stdout_list != [] <- result do + locations = + stdout_list + |> Keyword.get(:stdout) + |> String.split("\n") + |> Enum.drop(-1) + |> Enum.filter(fn el -> el != "_build" end) + |> Enum.join(" ") + + if locations != "" do + remote :build do + "rm -rvf #{locations}" + end end - end) + end + end) end task :generate_upgrade_release do @@ -359,7 +358,7 @@ task :remote_hot_upgrade do app_name = "#{Config.app()}" UI.info("Upgrading #{app_name} to version: #{Config.version()}") - + remote :app do "bin/#{app_name} upgrade #{Config.version()}" end diff --git a/lib/bootleg/tasks/deploy_upgrade.exs b/lib/bootleg/tasks/deploy_upgrade.exs index 0392da6..68c7193 100644 --- a/lib/bootleg/tasks/deploy_upgrade.exs +++ b/lib/bootleg/tasks/deploy_upgrade.exs @@ -48,5 +48,3 @@ task :unpack_release_upgrade do UI.info("Unpacked release upgrade archive") end - - diff --git a/lib/mix/tasks/upgrade.ex b/lib/mix/tasks/upgrade.ex index 232314f..1fff0e1 100644 --- a/lib/mix/tasks/upgrade.ex +++ b/lib/mix/tasks/upgrade.ex @@ -26,7 +26,7 @@ defmodule Mix.Tasks.Bootleg.Upgrade do the code of a running application without the need to stopping and restarting it, i.e. mantaining active the service in production. - + This is one of the most interesting capabilities of Erlang/OTP, but it is a very complex process that *cannot* be fully automated, i.e. require a good knowledge of the tecnologies @@ -60,24 +60,24 @@ defmodule Mix.Tasks.Bootleg.Upgrade do https://hexdocs.pm/distillery/guides/appups.html ### Bootleg hot upgrade task - + In the following description we assume that the development enviroinment is organized in this way (the build and the production places can be the same machine): - + * the development machine - where you edit and test locally your app source files; - + * the build machine - the computer where you will transfer to and compile the committed source code; - + * the production server - the server where you will deploy (transfer to and run) the code previously compiled on the build machine. - + Bootleg helps you in the hot upgrade process providing some specific tasks: - + * mix bootleg.build_upgrade will tranfer the last committed source code of your application from the development machine to the build directory of @@ -89,76 +89,76 @@ defmodule Mix.Tasks.Bootleg.Upgrade do you have to deploy your _first version_ of your app using `bootleg.build`, `bootleg.deploy` and `bootleg.start` (or `bootleg.update`); - + * mix bootleg.deploy_upgrade will transfer the tarball of the compiled app from the build machine to the production directory of the production machine, e.g. `~/production/myapp/` - + then will extract and setting up the needed files; - + * mix bootleg.hot_upgrade will call `mix distillery upgrade ` that will upgrade the running app to the last version. Notice that you *cannot* use this task if the app is not running, or if it there is a mismatch in the version numbers of the deployed versions. - + * mix bootleg.upgrade Call in sequences the above tasks in just one command. - + ### A step-by-step example - + Given you have configured the first version of your app with all the needed and appropriately customized Bootleg configuration files, you can go through the following steps to release and run the first version and subsequentely hot upgrade it to the newest versions: - + First version of your app: - + # Step 1 - deploy the first version of your app edit the version number of your in the mix.exs file (or in the file if you use an external reference), to the first version, e.g. 0.1.0; - + # Step 2 - Commit commit the changes you've made in step 1; - + # Step 3 - Build the first version use `mix bootleg.build` (not bootleg.build_upgrade!) to build your first version; - + # Step 4 - Deploy the first version use `mix bootleg.deploy` (not bootleg.build_upgrade!) to deploy your first version; - + # Step 5 - Run the first version use `mix bootleg.start` to run the app - + now your first version is up and running. To upgrade it to the future version, you have to follow these steps instead. - + Following versions: - + # Step 1 - update the version number e.g. 0.2.0 - + # Step 2 - Commit - + # Step 3 - Build the new version use `mix bootleg.build_upgrade` - + # Step 4 - Deploy the new version use `mix bootleg.deploy_upgrade` - + # Step 5 - Hot upgrade the new version use `mix bootleg.hot_upgrade` - + (or you can execute just the `bootleg.upgrade` that packs the previous tasks together if you don't need to manually adjust the created `appup` file) - + Now you have an upgraded version running. But if you stop and restart it, the previous version will be launched instead of the most recent. This is useful because if your new version From 963f6b6b52339ee1ecaa5625da4f0e21446d7809 Mon Sep 17 00:00:00 2001 From: Guido Tripaldi Date: Wed, 24 Jul 2019 08:20:15 +0200 Subject: [PATCH 7/8] Fixs typos --- lib/mix/tasks/upgrade.ex | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/mix/tasks/upgrade.ex b/lib/mix/tasks/upgrade.ex index 1fff0e1..17b6353 100644 --- a/lib/mix/tasks/upgrade.ex +++ b/lib/mix/tasks/upgrade.ex @@ -18,7 +18,7 @@ defmodule Mix.Tasks.Bootleg.Upgrade do having first a good understand of how a hot upgrade is performed, its limitations and steps required. - See nex section for an overview of the hot upgrade process. + See next sections for an overview of the hot upgrade process. ## Hot upgrading a running application @@ -81,11 +81,12 @@ defmodule Mix.Tasks.Bootleg.Upgrade do * mix bootleg.build_upgrade will tranfer the last committed source code of your application from the development machine to the build directory of - your build machine, for example `~/build/myapp/`, then + your build machine (for example `~/build/myapp/`), then it will clean the directory from the previous code deleting - every file but the `_buil` directory, it will generate the + every file but the `_build` directory, it will generate the `appup` file and compile the newest app release. - Please note th before you can use this task for the first time, + + Please note that before you can use this task for the first time, you have to deploy your _first version_ of your app using `bootleg.build`, `bootleg.deploy` and `bootleg.start` (or `bootleg.update`); @@ -93,9 +94,8 @@ defmodule Mix.Tasks.Bootleg.Upgrade do * mix bootleg.deploy_upgrade will transfer the tarball of the compiled app from the build machine to the production directory of the production - machine, e.g. `~/production/myapp/` - - then will extract and setting up the needed files; + machine (e.g. `~/production/myapp/`), then it will extract + and setting up the needed files; * mix bootleg.hot_upgrade will call `mix distillery upgrade ` that @@ -112,15 +112,15 @@ defmodule Mix.Tasks.Bootleg.Upgrade do Given you have configured the first version of your app with all the needed and appropriately customized Bootleg configuration files, you can go through the following steps to release and run the - first version and subsequentely hot upgrade it to the newest + first version, and subsequentely hot upgrade it to the newest versions: - First version of your app: + First version of your app # Step 1 - deploy the first version of your app edit the version number of your in the mix.exs file (or in the file if you use an external reference), - to the first version, e.g. 0.1.0; + to the first version (e.g. 0.1.0); # Step 2 - Commit commit the changes you've made in step 1; @@ -137,9 +137,9 @@ defmodule Mix.Tasks.Bootleg.Upgrade do use `mix bootleg.start` to run the app now your first version is up and running. To upgrade it - to the future version, you have to follow these steps instead. + to the future version, you have to follow these steps instead: - Following versions: + Following versions # Step 1 - update the version number e.g. 0.2.0 @@ -167,7 +167,7 @@ defmodule Mix.Tasks.Bootleg.Upgrade do If you are shure that you want to having the last version restarted, just delete the folder `~/production/myapp/var`. This folder contains - the file `start_erl.data` that list the version number to start with. + the file `start_erl.data` that lists the version number to start with. Deleting the `var` folder will automatically create it next time the app is started, with the last version number. From a3651fa654162f98f1cb93dc0614c8d73ca738b8 Mon Sep 17 00:00:00 2001 From: Guido Tripaldi Date: Wed, 24 Jul 2019 08:42:44 +0200 Subject: [PATCH 8/8] Adds hot_upgrade to MkDocs documentation --- docs/reference/hot_upgrde.md | 158 +++++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 159 insertions(+) create mode 100644 docs/reference/hot_upgrde.md diff --git a/docs/reference/hot_upgrde.md b/docs/reference/hot_upgrde.md new file mode 100644 index 0000000..159f8b0 --- /dev/null +++ b/docs/reference/hot_upgrde.md @@ -0,0 +1,158 @@ + ## Caution + + Please never try to hot upgrade a running application without + having first a good understand of how a hot upgrade is performed, + its limitations and steps required. + + See next sections for an overview of the hot upgrade process. + + ## Hot upgrading a running application + + Hot upgrade is the process that allows to seamlessly change + the code of a running application without the need to + stopping and restarting it, i.e. mantaining active the + service in production. + + This is one of the most interesting capabilities of Erlang/OTP, + but it is a very complex process that *cannot* be fully + automated, i.e. require a good knowledge of the tecnologies + involved and the configurations files needed and their locations + at every stage of the process. You have also to know how to + recognize when a hot upgrade isn't an advisable action, + because it could have some severe limitations + and unwanted consequences in some circumstances. + + Therefore it is strongly advised you read the official + documentation about the hot upgrade stuff on the Erlang/OTP + website, and how Distillery, the technolgy underlying + the Bootleg task, accomplished that. + + Here it is a selected - but not exaustive - list of important + pieces of documentation to read: + + # OTP Design Principles - Releases + http://erlang.org/doc/design_principles/release_structure.html + + # OTP Design Principles - Release Handling + http://erlang.org/doc/design_principles/release_handling.html + + # System Architecture Support Libraries - appup + http://erlang.org/doc/man/appup.html + + # Distillery - Hot upgrades and downgrades + https://hexdocs.pm/distillery/guides/upgrades_and_downgrades.html + + # Distillery - Appups + https://hexdocs.pm/distillery/guides/appups.html + + ### Bootleg hot upgrade task + + In the following description we assume that the development + enviroinment is organized in this way (the build and + the production places can be the same machine): + + * the development machine - where you edit and + test locally your app source files; + + * the build machine - the computer where you will transfer to + and compile the committed source code; + + * the production server - the server where you will deploy + (transfer to and run) the code previously compiled on + the build machine. + + Bootleg helps you in the hot upgrade process providing + some specific tasks: + + * mix bootleg.build_upgrade + will tranfer the last committed source code of your application + from the development machine to the build directory of + your build machine (for example `~/build/myapp/`), then + it will clean the directory from the previous code deleting + every file but the `_build` directory, it will generate the + `appup` file and compile the newest app release. + + Please note that before you can use this task for the first time, + you have to deploy your _first version_ of your app using + `bootleg.build`, `bootleg.deploy` and `bootleg.start` + (or `bootleg.update`); + + * mix bootleg.deploy_upgrade + will transfer the tarball of the compiled app from the + build machine to the production directory of the production + machine (e.g. `~/production/myapp/`), then it will extract + and setting up the needed files; + + * mix bootleg.hot_upgrade + will call `mix distillery upgrade ` that + will upgrade the running app to the last version. Notice that + you *cannot* use this task if the app is not running, or + if it there is a mismatch in the version numbers of the + deployed versions. + + * mix bootleg.upgrade + Call in sequences the above tasks in just one command. + + ### A step-by-step example + + Given you have configured the first version of your app with all + the needed and appropriately customized Bootleg configuration files, + you can go through the following steps to release and run the + first version, and subsequentely hot upgrade it to the newest + versions: + + First version of your app + + # Step 1 - deploy the first version of your app + edit the version number of your in the mix.exs file + (or in the file if you use an external reference), + to the first version (e.g. 0.1.0); + + # Step 2 - Commit + commit the changes you've made in step 1; + + # Step 3 - Build the first version + use `mix bootleg.build` (not bootleg.build_upgrade!) to build + your first version; + + # Step 4 - Deploy the first version + use `mix bootleg.deploy` (not bootleg.build_upgrade!) to deploy + your first version; + + # Step 5 - Run the first version + use `mix bootleg.start` to run the app + + now your first version is up and running. To upgrade it + to the future version, you have to follow these steps instead: + + Following versions + + # Step 1 - update the version number + e.g. 0.2.0 + + # Step 2 - Commit + + # Step 3 - Build the new version + use `mix bootleg.build_upgrade` + + # Step 4 - Deploy the new version + use `mix bootleg.deploy_upgrade` + + # Step 5 - Hot upgrade the new version + use `mix bootleg.hot_upgrade` + + (or you can execute just the `bootleg.upgrade` + that packs the previous tasks together if you don't need to + manually adjust the created `appup` file) + + Now you have an upgraded version running. But if you stop + and restart it, the previous version will be launched instead + of the most recent. This is useful because if your new version + has some blocking bug, you can easily restart the service to the last + working release. + + If you are shure that you want to having the last version restarted, + just delete the folder `~/production/myapp/var`. This folder contains + the file `start_erl.data` that lists the version number to start with. + Deleting the `var` folder will automatically create it next time the app + is started, with the last version number. diff --git a/mkdocs.yml b/mkdocs.yml index d7162af..539020a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -60,6 +60,7 @@ nav: - 'Bootleg Task Providers': 'reference/task_providers.md' - 'Continuous Integration': 'reference/ci.md' - 'Customization': 'reference/customization.md' + - 'Hot Upgrade': 'reference/hot_upgrade.md' - 'More Resources': 'resources.md' - 'API Reference': 'api-reference.html'