Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edcfc77d95 | ||
|
|
a71e167a03 | ||
|
|
2daaf442fa | ||
|
|
61b7dc90f2 | ||
|
|
f6442513ae | ||
|
|
5e8df58e6b | ||
|
|
9d5a6d1321 | ||
|
|
ecfd258093 | ||
|
|
313f89a108 | ||
|
|
9ab448e186 | ||
|
|
e1433f3895 | ||
|
|
a29e188c90 | ||
|
|
95e3915991 | ||
|
|
30d342183d | ||
|
|
83f5f3f053 | ||
|
|
e6ca270537 | ||
|
|
cd88c49c42 | ||
|
|
d03195ce1c | ||
|
|
da1c049829 | ||
|
|
4095e1853d | ||
|
|
dbc9989730 | ||
|
|
e493369453 | ||
|
|
e760cfa457 | ||
|
|
f8d651af0d | ||
|
|
08172be375 | ||
|
|
a3cc2317e2 | ||
|
|
2746a48e88 | ||
|
|
9a501867b4 | ||
|
|
c5397ff51e | ||
|
|
4950f61a87 | ||
|
|
08d8790851 | ||
|
|
02256ac8fe | ||
|
|
dadd8225da | ||
|
|
aa28ee0f3e | ||
|
|
2007ab475e | ||
|
|
4df3389d09 | ||
|
|
21b13bf8d3 | ||
|
|
6e6f696717 | ||
|
|
98c12a254e | ||
|
|
f0301d2007 | ||
|
|
d3f5e9efe8 | ||
|
|
d9b3fac17a | ||
|
|
cd5c41ddbe | ||
|
|
a14c6141e5 | ||
|
|
95d6ee5031 | ||
|
|
80a4ca4f8a | ||
|
|
12ca865e71 |
@@ -1,7 +1,7 @@
|
|||||||
PATH
|
PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
mrsk (0.13.2)
|
mrsk (0.15.1)
|
||||||
activesupport (>= 7.0)
|
activesupport (>= 7.0)
|
||||||
bcrypt_pbkdf (~> 1.0)
|
bcrypt_pbkdf (~> 1.0)
|
||||||
dotenv (~> 2.8)
|
dotenv (~> 2.8)
|
||||||
|
|||||||
88
README.md
88
README.md
@@ -63,6 +63,24 @@ This will:
|
|||||||
|
|
||||||
Voila! All the servers are now serving the app on port 80. If you're just running a single server, you're ready to go. If you're running multiple servers, you need to put a load balancer in front of them. For subsequent deploys, or if your servers already have Docker and curl installed, you can just run `mrsk deploy`.
|
Voila! All the servers are now serving the app on port 80. If you're just running a single server, you're ready to go. If you're running multiple servers, you need to put a load balancer in front of them. For subsequent deploys, or if your servers already have Docker and curl installed, you can just run `mrsk deploy`.
|
||||||
|
|
||||||
|
### Rails <7 usage
|
||||||
|
|
||||||
|
MRSK is not needed to be in your application Gemfile to be used. However, if you want to guarantee specific MRSK version in your CI/CD workflows, you can create a separate Gemfile for MRSK, for example, `gemfile/mrsk.gemfile`:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
|
gem 'mrsk', '~> 0.14'
|
||||||
|
```
|
||||||
|
|
||||||
|
Bundle with `BUNDLE_GEMFILE=gemfiles/mrsk.gemfile bundle`.
|
||||||
|
|
||||||
|
After this MRSK can be used for deployment:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
BUNDLE_GEMFILE=gemfiles/mrsk.gemfile bundle exec mrsk deploy
|
||||||
|
```
|
||||||
|
|
||||||
## Vision
|
## Vision
|
||||||
|
|
||||||
In the past decade+, there's been an explosion in commercial offerings that make deploying web apps easier. Heroku kicked it off with an incredible offering that stayed ahead of the competition seemingly forever. These days we have excellent alternatives like Fly.io and Render. And hosted Kubernetes is making things easier too on AWS, GCP, Digital Ocean, and elsewhere. But these are all offerings that have you renting computers in the cloud at a premium. If you want to run on your own hardware, or even just have a clear migration path to do so in the future, you need to carefully consider how locked in you get to these commercial platforms. Preferably before the bills swallow your business whole!
|
In the past decade+, there's been an explosion in commercial offerings that make deploying web apps easier. Heroku kicked it off with an incredible offering that stayed ahead of the competition seemingly forever. These days we have excellent alternatives like Fly.io and Render. And hosted Kubernetes is making things easier too on AWS, GCP, Digital Ocean, and elsewhere. But these are all offerings that have you renting computers in the cloud at a premium. If you want to run on your own hardware, or even just have a clear migration path to do so in the future, you need to carefully consider how locked in you get to these commercial platforms. Preferably before the bills swallow your business whole!
|
||||||
@@ -123,6 +141,8 @@ This template can safely be checked into git. Then everyone deploying the app ca
|
|||||||
|
|
||||||
If you need separate env variables for different destinations, you can set them with `.env.destination.erb` for the template, which will generate `.env.staging` when run with `mrsk envify -d staging`.
|
If you need separate env variables for different destinations, you can set them with `.env.destination.erb` for the template, which will generate `.env.staging` when run with `mrsk envify -d staging`.
|
||||||
|
|
||||||
|
Note: If you utilize biometrics with 1Password you can remove the `session_token` related parts in the example and just call `op read op://Vault/Docker Hub/password -n`.
|
||||||
|
|
||||||
#### Bitwarden as a secret store
|
#### Bitwarden as a secret store
|
||||||
|
|
||||||
If you are using open source secret store like bitwarden, you can create `.env.erb` as a template which looks up the secrets.
|
If you are using open source secret store like bitwarden, you can create `.env.erb` as a template which looks up the secrets.
|
||||||
@@ -206,13 +226,13 @@ ssh:
|
|||||||
user: app
|
user: app
|
||||||
```
|
```
|
||||||
|
|
||||||
If you are using non-root user, you need to bootstrap your servers manually, before using them with MRSK. On Ubuntu, you'd do:
|
If you are using non-root user (`app` as above example), you need to bootstrap your servers manually, before using them with MRSK. On Ubuntu, you'd do:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt upgrade -y
|
sudo apt upgrade -y
|
||||||
sudo apt install -y docker.io curl git
|
sudo apt install -y docker.io curl git
|
||||||
sudo usermod -a -G docker ubuntu
|
sudo usermod -a -G docker app
|
||||||
```
|
```
|
||||||
|
|
||||||
### Using a proxy SSH host
|
### Using a proxy SSH host
|
||||||
@@ -380,6 +400,16 @@ servers:
|
|||||||
|
|
||||||
That'll start the job containers with `docker run ... --cap-add --cpu-count 4 ...`.
|
That'll start the job containers with `docker run ... --cap-add --cpu-count 4 ...`.
|
||||||
|
|
||||||
|
### Setting a minimum version
|
||||||
|
|
||||||
|
You can set the minimum MRSK version with:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
minimum_version: 0.13.3
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: versions <= 0.13.2 will ignore this setting.
|
||||||
|
|
||||||
### Configuring logging
|
### Configuring logging
|
||||||
|
|
||||||
You can configure the logging driver and options passed to Docker using `logging`:
|
You can configure the logging driver and options passed to Docker using `logging`:
|
||||||
@@ -463,6 +493,37 @@ builder:
|
|||||||
context: ".."
|
context: ".."
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Using multistage builder cache
|
||||||
|
|
||||||
|
Docker multistage build cache can singlehandedly speed up your builds by a lot. Currently MRSK only supports using the GHA cache or the Registry cache:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Using GHA cache
|
||||||
|
builder:
|
||||||
|
cache:
|
||||||
|
type: gha
|
||||||
|
|
||||||
|
# Using Registry cache
|
||||||
|
builder:
|
||||||
|
cache:
|
||||||
|
type: registry
|
||||||
|
|
||||||
|
# Using Registry cache with different cache image
|
||||||
|
builder:
|
||||||
|
cache:
|
||||||
|
type: registry
|
||||||
|
# default image name is <image>-build-cache
|
||||||
|
image: application-cache-image
|
||||||
|
|
||||||
|
# Using Registry cache with additinonal cache-to options
|
||||||
|
builder:
|
||||||
|
cache:
|
||||||
|
type: registry
|
||||||
|
options: mode=max,image-manifest=true,oci-mediatypes=true
|
||||||
|
```
|
||||||
|
|
||||||
|
For further insights into build cache optimization, check out documentation on Docker's official website: https://docs.docker.com/build/cache/.
|
||||||
|
|
||||||
### Using build secrets for new images
|
### Using build secrets for new images
|
||||||
|
|
||||||
Some images need a secret passed in during build time, like a GITHUB_TOKEN, to give access to private gem repositories. This can be done by having the secret in ENV, then referencing it in the builder configuration:
|
Some images need a secret passed in during build time, like a GITHUB_TOKEN, to give access to private gem repositories. This can be done by having the secret in ENV, then referencing it in the builder configuration:
|
||||||
@@ -581,6 +642,16 @@ traefik:
|
|||||||
entrypoints.otherentrypoint.address: ':9000'
|
entrypoints.otherentrypoint.address: ':9000'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Rebooting Traefik
|
||||||
|
|
||||||
|
If you make changes to Traefik args or labels, you'll need to reboot with:
|
||||||
|
|
||||||
|
`mrsk traefik reboot`
|
||||||
|
|
||||||
|
In production, reboot the Traefik containers one by one with a slower but safer approach, using a rolling reboot:
|
||||||
|
|
||||||
|
`mrsk traefik reboot --rolling`
|
||||||
|
|
||||||
### Configuring build args for new images
|
### Configuring build args for new images
|
||||||
|
|
||||||
Build arguments that aren't secret can also be configured:
|
Build arguments that aren't secret can also be configured:
|
||||||
@@ -825,7 +896,7 @@ If you wish to remove the entire application, including Traefik, containers, ima
|
|||||||
|
|
||||||
## Locking
|
## Locking
|
||||||
|
|
||||||
Commands that are unsafe to run concurrently will take a deploy lock while they run. The lock is the `mrsk_lock` directory on the primary server.
|
Commands that are unsafe to run concurrently will take a deploy lock while they run. The lock is the `mrsk_lock-<service>` directory on the primary server.
|
||||||
|
|
||||||
You can check the lock status with:
|
You can check the lock status with:
|
||||||
|
|
||||||
@@ -881,8 +952,8 @@ firing a JSON webhook. These variables include:
|
|||||||
- `MRSK_RECORDED_AT` - UTC timestamp in ISO 8601 format, e.g. `2023-04-14T17:07:31Z`
|
- `MRSK_RECORDED_AT` - UTC timestamp in ISO 8601 format, e.g. `2023-04-14T17:07:31Z`
|
||||||
- `MRSK_PERFORMER` - the local user performing the command (from `whoami`)
|
- `MRSK_PERFORMER` - the local user performing the command (from `whoami`)
|
||||||
- `MRSK_SERVICE_VERSION` - an abbreviated service and version for use in messages, e.g. app@150b24f
|
- `MRSK_SERVICE_VERSION` - an abbreviated service and version for use in messages, e.g. app@150b24f
|
||||||
- `MRSK_VERSION` - an full version being deployed
|
- `MRSK_VERSION` - the full version being deployed
|
||||||
- `MRSK_HOSTS` - a comma separated list of the hosts targeted by the command
|
- `MRSK_HOSTS` - a comma-separated list of the hosts targeted by the command
|
||||||
- `MRSK_COMMAND` - The command we are running
|
- `MRSK_COMMAND` - The command we are running
|
||||||
- `MRSK_SUBCOMMAND` - optional: The subcommand we are running
|
- `MRSK_SUBCOMMAND` - optional: The subcommand we are running
|
||||||
- `MRSK_DESTINATION` - optional: destination, e.g. "staging"
|
- `MRSK_DESTINATION` - optional: destination, e.g. "staging"
|
||||||
@@ -899,9 +970,8 @@ Used for pre-build checks - e.g. there are no uncommitted changes or that CI has
|
|||||||
3. pre-deploy
|
3. pre-deploy
|
||||||
For final checks before deploying, e.g. checking CI completed
|
For final checks before deploying, e.g. checking CI completed
|
||||||
|
|
||||||
3. post-deploy - run after a deploy, redeploy or rollback
|
3. post-deploy - run after a deploy, redeploy or rollback.
|
||||||
|
This hook is also passed a `MRSK_RUNTIME` env variable set to the total seconds the deploy took.
|
||||||
This hook is also passed a `MRSK_RUNTIME` env variable.
|
|
||||||
|
|
||||||
This could be used to broadcast a deployment message, or register the new version with an APM.
|
This could be used to broadcast a deployment message, or register the new version with an APM.
|
||||||
|
|
||||||
@@ -912,7 +982,7 @@ The command could look something like:
|
|||||||
curl -q -d content="[My App] ${MRSK_PERFORMER} Rolled back to version ${MRSK_VERSION}" https://3.basecamp.com/XXXXX/integrations/XXXXX/buckets/XXXXX/chats/XXXXX/lines
|
curl -q -d content="[My App] ${MRSK_PERFORMER} Rolled back to version ${MRSK_VERSION}" https://3.basecamp.com/XXXXX/integrations/XXXXX/buckets/XXXXX/chats/XXXXX/lines
|
||||||
```
|
```
|
||||||
|
|
||||||
That'll post a line like follows to a preconfigured chatbot in Basecamp:
|
That'll post a line like the following to a preconfigured chatbot in Basecamp:
|
||||||
|
|
||||||
```
|
```
|
||||||
[My App] [dhh] Rolled back to version d264c4e92470ad1bd18590f04466787262f605de
|
[My App] [dhh] Rolled back to version d264c4e92470ad1bd18590f04466787262f605de
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
||||||
desc "boot [NAME]", "Boot new accessory service on host (use NAME=all to boot all accessories)"
|
desc "boot [NAME]", "Boot new accessory service on host (use NAME=all to boot all accessories)"
|
||||||
def boot(name)
|
def boot(name, login: true)
|
||||||
with_lock do
|
mutating do
|
||||||
if name == "all"
|
if name == "all"
|
||||||
MRSK.accessory_names.each { |accessory_name| boot(accessory_name) }
|
MRSK.accessory_names.each { |accessory_name| boot(accessory_name) }
|
||||||
else
|
else
|
||||||
@@ -10,7 +10,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|||||||
upload(name)
|
upload(name)
|
||||||
|
|
||||||
on(accessory.hosts) do
|
on(accessory.hosts) do
|
||||||
execute *MRSK.registry.login
|
execute *MRSK.registry.login if login
|
||||||
execute *MRSK.auditor.record("Booted #{name} accessory"), verbosity: :debug
|
execute *MRSK.auditor.record("Booted #{name} accessory"), verbosity: :debug
|
||||||
execute *accessory.run
|
execute *accessory.run
|
||||||
end
|
end
|
||||||
@@ -21,7 +21,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "upload [NAME]", "Upload accessory files to host", hide: true
|
desc "upload [NAME]", "Upload accessory files to host", hide: true
|
||||||
def upload(name)
|
def upload(name)
|
||||||
with_lock do
|
mutating do
|
||||||
with_accessory(name) do |accessory|
|
with_accessory(name) do |accessory|
|
||||||
on(accessory.hosts) do
|
on(accessory.hosts) do
|
||||||
accessory.files.each do |(local, remote)|
|
accessory.files.each do |(local, remote)|
|
||||||
@@ -38,7 +38,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "directories [NAME]", "Create accessory directories on host", hide: true
|
desc "directories [NAME]", "Create accessory directories on host", hide: true
|
||||||
def directories(name)
|
def directories(name)
|
||||||
with_lock do
|
mutating do
|
||||||
with_accessory(name) do |accessory|
|
with_accessory(name) do |accessory|
|
||||||
on(accessory.hosts) do
|
on(accessory.hosts) do
|
||||||
accessory.directories.keys.each do |host_path|
|
accessory.directories.keys.each do |host_path|
|
||||||
@@ -51,18 +51,22 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "reboot [NAME]", "Reboot existing accessory on host (stop container, remove container, start new container)"
|
desc "reboot [NAME]", "Reboot existing accessory on host (stop container, remove container, start new container)"
|
||||||
def reboot(name)
|
def reboot(name)
|
||||||
with_lock do
|
mutating do
|
||||||
with_accessory(name) do |accessory|
|
with_accessory(name) do |accessory|
|
||||||
|
on(accessory.hosts) do
|
||||||
|
execute *MRSK.registry.login
|
||||||
|
end
|
||||||
|
|
||||||
stop(name)
|
stop(name)
|
||||||
remove_container(name)
|
remove_container(name)
|
||||||
boot(name)
|
boot(name, login: false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "start [NAME]", "Start existing accessory container on host"
|
desc "start [NAME]", "Start existing accessory container on host"
|
||||||
def start(name)
|
def start(name)
|
||||||
with_lock do
|
mutating do
|
||||||
with_accessory(name) do |accessory|
|
with_accessory(name) do |accessory|
|
||||||
on(accessory.hosts) do
|
on(accessory.hosts) do
|
||||||
execute *MRSK.auditor.record("Started #{name} accessory"), verbosity: :debug
|
execute *MRSK.auditor.record("Started #{name} accessory"), verbosity: :debug
|
||||||
@@ -74,7 +78,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "stop [NAME]", "Stop existing accessory container on host"
|
desc "stop [NAME]", "Stop existing accessory container on host"
|
||||||
def stop(name)
|
def stop(name)
|
||||||
with_lock do
|
mutating do
|
||||||
with_accessory(name) do |accessory|
|
with_accessory(name) do |accessory|
|
||||||
on(accessory.hosts) do
|
on(accessory.hosts) do
|
||||||
execute *MRSK.auditor.record("Stopped #{name} accessory"), verbosity: :debug
|
execute *MRSK.auditor.record("Stopped #{name} accessory"), verbosity: :debug
|
||||||
@@ -86,7 +90,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "restart [NAME]", "Restart existing accessory container on host"
|
desc "restart [NAME]", "Restart existing accessory container on host"
|
||||||
def restart(name)
|
def restart(name)
|
||||||
with_lock do
|
mutating do
|
||||||
with_accessory(name) do
|
with_accessory(name) do
|
||||||
stop(name)
|
stop(name)
|
||||||
start(name)
|
start(name)
|
||||||
@@ -165,7 +169,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|||||||
desc "remove [NAME]", "Remove accessory container, image and data directory from host (use NAME=all to remove all accessories)"
|
desc "remove [NAME]", "Remove accessory container, image and data directory from host (use NAME=all to remove all accessories)"
|
||||||
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
||||||
def remove(name)
|
def remove(name)
|
||||||
with_lock do
|
mutating do
|
||||||
if name == "all"
|
if name == "all"
|
||||||
MRSK.accessory_names.each { |accessory_name| remove(accessory_name) }
|
MRSK.accessory_names.each { |accessory_name| remove(accessory_name) }
|
||||||
else
|
else
|
||||||
@@ -183,7 +187,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "remove_container [NAME]", "Remove accessory container from host", hide: true
|
desc "remove_container [NAME]", "Remove accessory container from host", hide: true
|
||||||
def remove_container(name)
|
def remove_container(name)
|
||||||
with_lock do
|
mutating do
|
||||||
with_accessory(name) do |accessory|
|
with_accessory(name) do |accessory|
|
||||||
on(accessory.hosts) do
|
on(accessory.hosts) do
|
||||||
execute *MRSK.auditor.record("Remove #{name} accessory container"), verbosity: :debug
|
execute *MRSK.auditor.record("Remove #{name} accessory container"), verbosity: :debug
|
||||||
@@ -195,7 +199,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "remove_image [NAME]", "Remove accessory image from host", hide: true
|
desc "remove_image [NAME]", "Remove accessory image from host", hide: true
|
||||||
def remove_image(name)
|
def remove_image(name)
|
||||||
with_lock do
|
mutating do
|
||||||
with_accessory(name) do |accessory|
|
with_accessory(name) do |accessory|
|
||||||
on(accessory.hosts) do
|
on(accessory.hosts) do
|
||||||
execute *MRSK.auditor.record("Removed #{name} accessory image"), verbosity: :debug
|
execute *MRSK.auditor.record("Removed #{name} accessory image"), verbosity: :debug
|
||||||
@@ -207,7 +211,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "remove_service_directory [NAME]", "Remove accessory directory used for uploaded files and data directories from host", hide: true
|
desc "remove_service_directory [NAME]", "Remove accessory directory used for uploaded files and data directories from host", hide: true
|
||||||
def remove_service_directory(name)
|
def remove_service_directory(name)
|
||||||
with_lock do
|
mutating do
|
||||||
with_accessory(name) do |accessory|
|
with_accessory(name) do |accessory|
|
||||||
on(accessory.hosts) do
|
on(accessory.hosts) do
|
||||||
execute *accessory.remove_service_directory
|
execute *accessory.remove_service_directory
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
class Mrsk::Cli::App < Mrsk::Cli::Base
|
class Mrsk::Cli::App < Mrsk::Cli::Base
|
||||||
desc "boot", "Boot app on servers (or reboot app if already running)"
|
desc "boot", "Boot app on servers (or reboot app if already running)"
|
||||||
def boot
|
def boot
|
||||||
with_lock do
|
mutating do
|
||||||
hold_lock_on_error do
|
hold_lock_on_error do
|
||||||
say "Get most recent version available as an image...", :magenta unless options[:version]
|
say "Get most recent version available as an image...", :magenta unless options[:version]
|
||||||
using_version(version_or_latest) do |version|
|
using_version(version_or_latest) do |version|
|
||||||
@@ -43,7 +43,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "start", "Start existing app container on servers"
|
desc "start", "Start existing app container on servers"
|
||||||
def start
|
def start
|
||||||
with_lock do
|
mutating do
|
||||||
on(MRSK.hosts) do |host|
|
on(MRSK.hosts) do |host|
|
||||||
roles = MRSK.roles_on(host)
|
roles = MRSK.roles_on(host)
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "stop", "Stop app container on servers"
|
desc "stop", "Stop app container on servers"
|
||||||
def stop
|
def stop
|
||||||
with_lock do
|
mutating do
|
||||||
on(MRSK.hosts) do |host|
|
on(MRSK.hosts) do |host|
|
||||||
roles = MRSK.roles_on(host)
|
roles = MRSK.roles_on(host)
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|||||||
desc "stale_containers", "Detect app stale containers"
|
desc "stale_containers", "Detect app stale containers"
|
||||||
option :stop, aliases: "-s", type: :boolean, default: false, desc: "Stop the stale containers found"
|
option :stop, aliases: "-s", type: :boolean, default: false, desc: "Stop the stale containers found"
|
||||||
def stale_containers
|
def stale_containers
|
||||||
with_lock do
|
mutating do
|
||||||
stop = options[:stop]
|
stop = options[:stop]
|
||||||
|
|
||||||
cli = self
|
cli = self
|
||||||
@@ -202,7 +202,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "remove", "Remove app containers and images from servers"
|
desc "remove", "Remove app containers and images from servers"
|
||||||
def remove
|
def remove
|
||||||
with_lock do
|
mutating do
|
||||||
stop
|
stop
|
||||||
remove_containers
|
remove_containers
|
||||||
remove_images
|
remove_images
|
||||||
@@ -211,7 +211,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "remove_container [VERSION]", "Remove app container with given version from servers", hide: true
|
desc "remove_container [VERSION]", "Remove app container with given version from servers", hide: true
|
||||||
def remove_container(version)
|
def remove_container(version)
|
||||||
with_lock do
|
mutating do
|
||||||
on(MRSK.hosts) do |host|
|
on(MRSK.hosts) do |host|
|
||||||
roles = MRSK.roles_on(host)
|
roles = MRSK.roles_on(host)
|
||||||
|
|
||||||
@@ -225,7 +225,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "remove_containers", "Remove all app containers from servers", hide: true
|
desc "remove_containers", "Remove all app containers from servers", hide: true
|
||||||
def remove_containers
|
def remove_containers
|
||||||
with_lock do
|
mutating do
|
||||||
on(MRSK.hosts) do |host|
|
on(MRSK.hosts) do |host|
|
||||||
roles = MRSK.roles_on(host)
|
roles = MRSK.roles_on(host)
|
||||||
|
|
||||||
@@ -239,7 +239,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "remove_images", "Remove all app images from servers", hide: true
|
desc "remove_images", "Remove all app images from servers", hide: true
|
||||||
def remove_images
|
def remove_images
|
||||||
with_lock do
|
mutating do
|
||||||
on(MRSK.hosts) do
|
on(MRSK.hosts) do
|
||||||
execute *MRSK.auditor.record("Removed all app images"), verbosity: :debug
|
execute *MRSK.auditor.record("Removed all app images"), verbosity: :debug
|
||||||
execute *MRSK.app.remove_images
|
execute *MRSK.app.remove_images
|
||||||
|
|||||||
@@ -72,28 +72,28 @@ module Mrsk::Cli
|
|||||||
puts " Finished all in #{sprintf("%.1f seconds", runtime)}"
|
puts " Finished all in #{sprintf("%.1f seconds", runtime)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_lock
|
def mutating
|
||||||
if MRSK.holding_lock?
|
return yield if MRSK.holding_lock?
|
||||||
|
|
||||||
|
MRSK.config.ensure_env_available
|
||||||
|
|
||||||
|
run_hook "pre-connect"
|
||||||
|
|
||||||
|
acquire_lock
|
||||||
|
|
||||||
|
begin
|
||||||
yield
|
yield
|
||||||
else
|
rescue
|
||||||
run_hook "pre-connect"
|
if MRSK.hold_lock_on_error?
|
||||||
|
error " \e[31mDeploy lock was not released\e[0m"
|
||||||
acquire_lock
|
else
|
||||||
|
release_lock
|
||||||
begin
|
|
||||||
yield
|
|
||||||
rescue
|
|
||||||
if MRSK.hold_lock_on_error?
|
|
||||||
error " \e[31mDeploy lock was not released\e[0m"
|
|
||||||
else
|
|
||||||
release_lock
|
|
||||||
end
|
|
||||||
|
|
||||||
raise
|
|
||||||
end
|
end
|
||||||
|
|
||||||
release_lock
|
raise
|
||||||
end
|
end
|
||||||
|
|
||||||
|
release_lock
|
||||||
end
|
end
|
||||||
|
|
||||||
def acquire_lock
|
def acquire_lock
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "deliver", "Build app and push app image to registry then pull image on servers"
|
desc "deliver", "Build app and push app image to registry then pull image on servers"
|
||||||
def deliver
|
def deliver
|
||||||
with_lock do
|
mutating do
|
||||||
push
|
push
|
||||||
pull
|
pull
|
||||||
end
|
end
|
||||||
@@ -11,7 +11,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "push", "Build and push app image to registry"
|
desc "push", "Build and push app image to registry"
|
||||||
def push
|
def push
|
||||||
with_lock do
|
mutating do
|
||||||
cli = self
|
cli = self
|
||||||
|
|
||||||
verify_local_dependencies
|
verify_local_dependencies
|
||||||
@@ -37,7 +37,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "pull", "Pull app image from registry onto servers"
|
desc "pull", "Pull app image from registry onto servers"
|
||||||
def pull
|
def pull
|
||||||
with_lock do
|
mutating do
|
||||||
on(MRSK.hosts) do
|
on(MRSK.hosts) do
|
||||||
execute *MRSK.auditor.record("Pulled image with version #{MRSK.config.version}"), verbosity: :debug
|
execute *MRSK.auditor.record("Pulled image with version #{MRSK.config.version}"), verbosity: :debug
|
||||||
execute *MRSK.builder.clean, raise_on_non_zero_exit: false
|
execute *MRSK.builder.clean, raise_on_non_zero_exit: false
|
||||||
@@ -48,7 +48,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "create", "Create a build setup"
|
desc "create", "Create a build setup"
|
||||||
def create
|
def create
|
||||||
with_lock do
|
mutating do
|
||||||
run_locally do
|
run_locally do
|
||||||
begin
|
begin
|
||||||
debug "Using builder: #{MRSK.builder.name}"
|
debug "Using builder: #{MRSK.builder.name}"
|
||||||
@@ -67,7 +67,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "remove", "Remove build setup"
|
desc "remove", "Remove build setup"
|
||||||
def remove
|
def remove
|
||||||
with_lock do
|
mutating do
|
||||||
run_locally do
|
run_locally do
|
||||||
debug "Using builder: #{MRSK.builder.name}"
|
debug "Using builder: #{MRSK.builder.name}"
|
||||||
execute *MRSK.builder.remove
|
execute *MRSK.builder.remove
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|||||||
desc "setup", "Setup all accessories and deploy app to servers"
|
desc "setup", "Setup all accessories and deploy app to servers"
|
||||||
def setup
|
def setup
|
||||||
print_runtime do
|
print_runtime do
|
||||||
with_lock do
|
mutating do
|
||||||
invoke "mrsk:cli:server:bootstrap"
|
invoke "mrsk:cli:server:bootstrap"
|
||||||
invoke "mrsk:cli:accessory:boot", [ "all" ]
|
invoke "mrsk:cli:accessory:boot", [ "all" ]
|
||||||
deploy
|
deploy
|
||||||
@@ -14,7 +14,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|||||||
option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
|
option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
|
||||||
def deploy
|
def deploy
|
||||||
runtime = print_runtime do
|
runtime = print_runtime do
|
||||||
with_lock do
|
mutating do
|
||||||
invoke_options = deploy_options
|
invoke_options = deploy_options
|
||||||
|
|
||||||
say "Log into image registry...", :magenta
|
say "Log into image registry...", :magenta
|
||||||
@@ -53,7 +53,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|||||||
option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
|
option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
|
||||||
def redeploy
|
def redeploy
|
||||||
runtime = print_runtime do
|
runtime = print_runtime do
|
||||||
with_lock do
|
mutating do
|
||||||
invoke_options = deploy_options
|
invoke_options = deploy_options
|
||||||
|
|
||||||
if options[:skip_push]
|
if options[:skip_push]
|
||||||
@@ -83,7 +83,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|||||||
def rollback(version)
|
def rollback(version)
|
||||||
rolled_back = false
|
rolled_back = false
|
||||||
runtime = print_runtime do
|
runtime = print_runtime do
|
||||||
with_lock do
|
mutating do
|
||||||
invoke_options = deploy_options
|
invoke_options = deploy_options
|
||||||
|
|
||||||
MRSK.config.version = version
|
MRSK.config.version = version
|
||||||
@@ -180,7 +180,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|||||||
desc "remove", "Remove Traefik, app, accessories, and registry session from servers"
|
desc "remove", "Remove Traefik, app, accessories, and registry session from servers"
|
||||||
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
||||||
def remove
|
def remove
|
||||||
with_lock do
|
mutating do
|
||||||
if options[:confirmed] || ask("This will remove all containers and images. Are you sure?", limited_to: %w( y N ), default: "N") == "y"
|
if options[:confirmed] || ask("This will remove all containers and images. Are you sure?", limited_to: %w( y N ), default: "N") == "y"
|
||||||
invoke "mrsk:cli:traefik:remove", [], options.without(:confirmed)
|
invoke "mrsk:cli:traefik:remove", [], options.without(:confirmed)
|
||||||
invoke "mrsk:cli:app:remove", [], options.without(:confirmed)
|
invoke "mrsk:cli:app:remove", [], options.without(:confirmed)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
class Mrsk::Cli::Prune < Mrsk::Cli::Base
|
class Mrsk::Cli::Prune < Mrsk::Cli::Base
|
||||||
desc "all", "Prune unused images and stopped containers"
|
desc "all", "Prune unused images and stopped containers"
|
||||||
def all
|
def all
|
||||||
with_lock do
|
mutating do
|
||||||
containers
|
containers
|
||||||
images
|
images
|
||||||
end
|
end
|
||||||
@@ -9,7 +9,7 @@ class Mrsk::Cli::Prune < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "images", "Prune dangling images"
|
desc "images", "Prune dangling images"
|
||||||
def images
|
def images
|
||||||
with_lock do
|
mutating do
|
||||||
on(MRSK.hosts) do
|
on(MRSK.hosts) do
|
||||||
execute *MRSK.auditor.record("Pruned images"), verbosity: :debug
|
execute *MRSK.auditor.record("Pruned images"), verbosity: :debug
|
||||||
execute *MRSK.prune.dangling_images
|
execute *MRSK.prune.dangling_images
|
||||||
@@ -20,7 +20,7 @@ class Mrsk::Cli::Prune < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "containers", "Prune all stopped containers, except the last 5"
|
desc "containers", "Prune all stopped containers, except the last 5"
|
||||||
def containers
|
def containers
|
||||||
with_lock do
|
mutating do
|
||||||
on(MRSK.hosts) do
|
on(MRSK.hosts) do
|
||||||
execute *MRSK.auditor.record("Pruned containers"), verbosity: :debug
|
execute *MRSK.auditor.record("Pruned containers"), verbosity: :debug
|
||||||
execute *MRSK.prune.containers
|
execute *MRSK.prune.containers
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
# A sample pre-deploy hook
|
# A sample pre-deploy hook
|
||||||
#
|
#
|
||||||
@@ -16,8 +16,6 @@
|
|||||||
# MRSK_ROLE (if set)
|
# MRSK_ROLE (if set)
|
||||||
# MRSK_DESTINATION (if set)
|
# MRSK_DESTINATION (if set)
|
||||||
|
|
||||||
#!/usr/bin/env ruby
|
|
||||||
|
|
||||||
# Only check the build status for production deployments
|
# Only check the build status for production deployments
|
||||||
if ENV["MRSK_COMMAND"] == "rollback" || ENV["MRSK_DESTINATION"] != "production"
|
if ENV["MRSK_COMMAND"] == "rollback" || ENV["MRSK_DESTINATION"] != "production"
|
||||||
exit 0
|
exit 0
|
||||||
@@ -41,41 +39,70 @@ def exit_with_error(message)
|
|||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
def first_status_url(combined_status, state)
|
class GithubStatusChecks
|
||||||
first_status = combined_status[:statuses].find { |status| status[:state] == state }
|
attr_reader :remote_url, :git_sha, :github_client, :combined_status
|
||||||
first_status && first_status[:target_url]
|
|
||||||
|
def initialize
|
||||||
|
@remote_url = `git config --get remote.origin.url`.strip.delete_prefix("https://github.com/")
|
||||||
|
@git_sha = `git rev-parse HEAD`.strip
|
||||||
|
@github_client = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"])
|
||||||
|
refresh!
|
||||||
|
end
|
||||||
|
|
||||||
|
def refresh!
|
||||||
|
@combined_status = github_client.combined_status(remote_url, git_sha)
|
||||||
|
end
|
||||||
|
|
||||||
|
def state
|
||||||
|
combined_status[:state]
|
||||||
|
end
|
||||||
|
|
||||||
|
def first_status_url
|
||||||
|
first_status = combined_status[:statuses].find { |status| status[:state] == state }
|
||||||
|
first_status && first_status[:target_url]
|
||||||
|
end
|
||||||
|
|
||||||
|
def complete_count
|
||||||
|
combined_status[:statuses].count { |status| status[:state] != "pending"}
|
||||||
|
end
|
||||||
|
|
||||||
|
def total_count
|
||||||
|
combined_status[:statuses].count
|
||||||
|
end
|
||||||
|
|
||||||
|
def current_status
|
||||||
|
if total_count > 0
|
||||||
|
"Completed #{complete_count}/#{total_count} checks, see #{first_status_url} ..."
|
||||||
|
else
|
||||||
|
"Build not started..."
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
remote_url = `git config --get remote.origin.url`.strip.delete_prefix("https://github.com/")
|
|
||||||
git_sha = `git rev-parse HEAD`.strip
|
|
||||||
|
|
||||||
repository = Octokit::Repository.from_url(remote_url)
|
$stdout.sync = true
|
||||||
github_client = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"])
|
|
||||||
|
puts "Checking build status..."
|
||||||
attempts = 0
|
attempts = 0
|
||||||
|
checks = GithubStatusChecks.new
|
||||||
|
|
||||||
begin
|
begin
|
||||||
loop do
|
loop do
|
||||||
combined_status = github_client.combined_status(remote_url, git_sha)
|
case checks.state
|
||||||
state = combined_status[:state]
|
|
||||||
first_status_url = first_status_url(combined_status, state)
|
|
||||||
|
|
||||||
case state
|
|
||||||
when "success"
|
when "success"
|
||||||
puts "Build passed, see #{first_status_url}"
|
puts "Checks passed, see #{checks.first_status_url}"
|
||||||
exit 0
|
exit 0
|
||||||
when "failure"
|
when "failure"
|
||||||
exit_with_error "Build failed, see #{first_status_url}"
|
exit_with_error "Checks failed, see #{checks.first_status_url}"
|
||||||
when "pending"
|
when "pending"
|
||||||
attempts += 1
|
attempts += 1
|
||||||
end
|
end
|
||||||
|
|
||||||
puts "Waiting #{ATTEMPTS_GAP} more seconds for build to complete#{", see #{first_status_url}" if first_status_url}..."
|
exit_with_error "Checks are still pending, gave up after #{MAX_ATTEMPTS * ATTEMPTS_GAP} seconds" if attempts == MAX_ATTEMPTS
|
||||||
|
|
||||||
if attempts == MAX_ATTEMPTS
|
|
||||||
exit_with_error "Build status is still pending, gave up after #{MAX_ATTEMPTS * ATTEMPTS_GAP} seconds"
|
|
||||||
end
|
|
||||||
|
|
||||||
|
puts checks.current_status
|
||||||
sleep(ATTEMPTS_GAP)
|
sleep(ATTEMPTS_GAP)
|
||||||
|
checks.refresh!
|
||||||
end
|
end
|
||||||
rescue Octokit::NotFound
|
rescue Octokit::NotFound
|
||||||
exit_with_error "Build status could not be found"
|
exit_with_error "Build status could not be found"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
||||||
desc "boot", "Boot Traefik on servers"
|
desc "boot", "Boot Traefik on servers"
|
||||||
def boot
|
def boot
|
||||||
with_lock do
|
mutating do
|
||||||
on(MRSK.traefik_hosts) do
|
on(MRSK.traefik_hosts) do
|
||||||
execute *MRSK.registry.login
|
execute *MRSK.registry.login
|
||||||
execute *MRSK.traefik.run, raise_on_non_zero_exit: false
|
execute *MRSK.traefik.run, raise_on_non_zero_exit: false
|
||||||
@@ -10,17 +10,22 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
desc "reboot", "Reboot Traefik on servers (stop container, remove container, start new container)"
|
desc "reboot", "Reboot Traefik on servers (stop container, remove container, start new container)"
|
||||||
|
option :rolling, type: :boolean, default: false, desc: "Reboot traefik on hosts in sequence, rather than in parallel"
|
||||||
def reboot
|
def reboot
|
||||||
with_lock do
|
mutating do
|
||||||
stop
|
on(MRSK.traefik_hosts, in: options[:rolling] ? :sequence : :parallel) do
|
||||||
remove_container
|
execute *MRSK.auditor.record("Rebooted traefik"), verbosity: :debug
|
||||||
boot
|
execute *MRSK.registry.login
|
||||||
|
execute *MRSK.traefik.stop, raise_on_non_zero_exit: false
|
||||||
|
execute *MRSK.traefik.remove_container
|
||||||
|
execute *MRSK.traefik.run, raise_on_non_zero_exit: false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "start", "Start existing Traefik container on servers"
|
desc "start", "Start existing Traefik container on servers"
|
||||||
def start
|
def start
|
||||||
with_lock do
|
mutating do
|
||||||
on(MRSK.traefik_hosts) do
|
on(MRSK.traefik_hosts) do
|
||||||
execute *MRSK.auditor.record("Started traefik"), verbosity: :debug
|
execute *MRSK.auditor.record("Started traefik"), verbosity: :debug
|
||||||
execute *MRSK.traefik.start, raise_on_non_zero_exit: false
|
execute *MRSK.traefik.start, raise_on_non_zero_exit: false
|
||||||
@@ -30,7 +35,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "stop", "Stop existing Traefik container on servers"
|
desc "stop", "Stop existing Traefik container on servers"
|
||||||
def stop
|
def stop
|
||||||
with_lock do
|
mutating do
|
||||||
on(MRSK.traefik_hosts) do
|
on(MRSK.traefik_hosts) do
|
||||||
execute *MRSK.auditor.record("Stopped traefik"), verbosity: :debug
|
execute *MRSK.auditor.record("Stopped traefik"), verbosity: :debug
|
||||||
execute *MRSK.traefik.stop, raise_on_non_zero_exit: false
|
execute *MRSK.traefik.stop, raise_on_non_zero_exit: false
|
||||||
@@ -40,7 +45,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "restart", "Restart existing Traefik container on servers"
|
desc "restart", "Restart existing Traefik container on servers"
|
||||||
def restart
|
def restart
|
||||||
with_lock do
|
mutating do
|
||||||
stop
|
stop
|
||||||
start
|
start
|
||||||
end
|
end
|
||||||
@@ -77,7 +82,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "remove", "Remove Traefik container and image from servers"
|
desc "remove", "Remove Traefik container and image from servers"
|
||||||
def remove
|
def remove
|
||||||
with_lock do
|
mutating do
|
||||||
stop
|
stop
|
||||||
remove_container
|
remove_container
|
||||||
remove_image
|
remove_image
|
||||||
@@ -86,7 +91,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "remove_container", "Remove Traefik container from servers", hide: true
|
desc "remove_container", "Remove Traefik container from servers", hide: true
|
||||||
def remove_container
|
def remove_container
|
||||||
with_lock do
|
mutating do
|
||||||
on(MRSK.traefik_hosts) do
|
on(MRSK.traefik_hosts) do
|
||||||
execute *MRSK.auditor.record("Removed traefik container"), verbosity: :debug
|
execute *MRSK.auditor.record("Removed traefik container"), verbosity: :debug
|
||||||
execute *MRSK.traefik.remove_container
|
execute *MRSK.traefik.remove_container
|
||||||
@@ -96,7 +101,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|||||||
|
|
||||||
desc "remove_image", "Remove Traefik image from servers", hide: true
|
desc "remove_image", "Remove Traefik image from servers", hide: true
|
||||||
def remove_image
|
def remove_image
|
||||||
with_lock do
|
mutating do
|
||||||
on(MRSK.traefik_hosts) do
|
on(MRSK.traefik_hosts) do
|
||||||
execute *MRSK.auditor.record("Removed traefik image"), verbosity: :debug
|
execute *MRSK.auditor.record("Removed traefik image"), verbosity: :debug
|
||||||
execute *MRSK.traefik.remove_image
|
execute *MRSK.traefik.remove_image
|
||||||
|
|||||||
@@ -13,7 +13,11 @@ module Mrsk::Commands
|
|||||||
|
|
||||||
def run_over_ssh(*command, host:)
|
def run_over_ssh(*command, host:)
|
||||||
"ssh".tap do |cmd|
|
"ssh".tap do |cmd|
|
||||||
cmd << " -J #{config.ssh_proxy.jump_proxies}" if config.ssh_proxy
|
if config.ssh_proxy && config.ssh_proxy.is_a?(Net::SSH::Proxy::Jump)
|
||||||
|
cmd << " -J #{config.ssh_proxy.jump_proxies}"
|
||||||
|
elsif config.ssh_proxy && config.ssh_proxy.is_a?(Net::SSH::Proxy::Command)
|
||||||
|
cmd << " -o ProxyCommand='#{config.ssh_proxy.command_line_template}'"
|
||||||
|
end
|
||||||
cmd << " -t #{config.ssh_user}@#{host} '#{command.join(" ")}'"
|
cmd << " -t #{config.ssh_user}@#{host} '#{command.join(" ")}'"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,11 +7,13 @@ class Mrsk::Commands::Builder < Mrsk::Commands::Base
|
|||||||
|
|
||||||
def target
|
def target
|
||||||
case
|
case
|
||||||
when config.builder && config.builder["multiarch"] == false
|
when !config.builder.multiarch? && !config.builder.cached?
|
||||||
native
|
native
|
||||||
when config.builder && config.builder["local"] && config.builder["remote"]
|
when !config.builder.multiarch? && config.builder.cached?
|
||||||
|
native_cached
|
||||||
|
when config.builder.local? && config.builder.remote?
|
||||||
multiarch_remote
|
multiarch_remote
|
||||||
when config.builder && config.builder["remote"]
|
when config.builder.remote?
|
||||||
native_remote
|
native_remote
|
||||||
else
|
else
|
||||||
multiarch
|
multiarch
|
||||||
@@ -22,6 +24,10 @@ class Mrsk::Commands::Builder < Mrsk::Commands::Base
|
|||||||
@native ||= Mrsk::Commands::Builder::Native.new(config)
|
@native ||= Mrsk::Commands::Builder::Native.new(config)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def native_cached
|
||||||
|
@native ||= Mrsk::Commands::Builder::Native::Cached.new(config)
|
||||||
|
end
|
||||||
|
|
||||||
def native_remote
|
def native_remote
|
||||||
@native ||= Mrsk::Commands::Builder::Native::Remote.new(config)
|
@native ||= Mrsk::Commands::Builder::Native::Remote.new(config)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ class Mrsk::Commands::Builder::Base < Mrsk::Commands::Base
|
|||||||
class BuilderError < StandardError; end
|
class BuilderError < StandardError; end
|
||||||
|
|
||||||
delegate :argumentize, to: Mrsk::Utils
|
delegate :argumentize, to: Mrsk::Utils
|
||||||
|
delegate :args, :secrets, :dockerfile, :local_arch, :local_host, :remote_arch, :remote_host, :cache_from, :cache_to, to: :builder_config
|
||||||
|
|
||||||
def clean
|
def clean
|
||||||
docker :image, :rm, "--force", config.absolute_image
|
docker :image, :rm, "--force", config.absolute_image
|
||||||
@@ -13,11 +14,11 @@ class Mrsk::Commands::Builder::Base < Mrsk::Commands::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def build_options
|
def build_options
|
||||||
[ *build_tags, *build_labels, *build_args, *build_secrets, *build_dockerfile ]
|
[ *build_tags, *build_cache, *build_labels, *build_args, *build_secrets, *build_dockerfile ]
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_context
|
def build_context
|
||||||
context
|
config.builder.context
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -26,6 +27,13 @@ class Mrsk::Commands::Builder::Base < Mrsk::Commands::Base
|
|||||||
[ "-t", config.absolute_image, "-t", config.latest_image ]
|
[ "-t", config.absolute_image, "-t", config.latest_image ]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def build_cache
|
||||||
|
if cache_to && cache_from
|
||||||
|
["--cache-to", cache_to,
|
||||||
|
"--cache-from", cache_from]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def build_labels
|
def build_labels
|
||||||
argumentize "--label", { service: config.service }
|
argumentize "--label", { service: config.service }
|
||||||
end
|
end
|
||||||
@@ -46,19 +54,7 @@ class Mrsk::Commands::Builder::Base < Mrsk::Commands::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def args
|
def builder_config
|
||||||
(config.builder && config.builder["args"]) || {}
|
config.builder
|
||||||
end
|
|
||||||
|
|
||||||
def secrets
|
|
||||||
(config.builder && config.builder["secrets"]) || []
|
|
||||||
end
|
|
||||||
|
|
||||||
def dockerfile
|
|
||||||
(config.builder && config.builder["dockerfile"]) || "Dockerfile"
|
|
||||||
end
|
|
||||||
|
|
||||||
def context
|
|
||||||
(config.builder && config.builder["context"]) || "."
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -22,17 +22,17 @@ class Mrsk::Commands::Builder::Multiarch::Remote < Mrsk::Commands::Builder::Mult
|
|||||||
end
|
end
|
||||||
|
|
||||||
def create_local_buildx
|
def create_local_buildx
|
||||||
docker :buildx, :create, "--name", builder_name, builder_name_with_arch(local["arch"]), "--platform", "linux/#{local["arch"]}"
|
docker :buildx, :create, "--name", builder_name, builder_name_with_arch(local_arch), "--platform", "linux/#{local_arch}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def append_remote_buildx
|
def append_remote_buildx
|
||||||
docker :buildx, :create, "--append", "--name", builder_name, builder_name_with_arch(remote["arch"]), "--platform", "linux/#{remote["arch"]}"
|
docker :buildx, :create, "--append", "--name", builder_name, builder_name_with_arch(remote_arch), "--platform", "linux/#{remote_arch}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_contexts
|
def create_contexts
|
||||||
combine \
|
combine \
|
||||||
create_context(local["arch"], local["host"]),
|
create_context(local_arch, local_host),
|
||||||
create_context(remote["arch"], remote["host"])
|
create_context(remote_arch, remote_host)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_context(arch, host)
|
def create_context(arch, host)
|
||||||
@@ -41,19 +41,11 @@ class Mrsk::Commands::Builder::Multiarch::Remote < Mrsk::Commands::Builder::Mult
|
|||||||
|
|
||||||
def remove_contexts
|
def remove_contexts
|
||||||
combine \
|
combine \
|
||||||
remove_context(local["arch"]),
|
remove_context(local_arch),
|
||||||
remove_context(remote["arch"])
|
remove_context(remote_arch)
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_context(arch)
|
def remove_context(arch)
|
||||||
docker :context, :rm, builder_name_with_arch(arch)
|
docker :context, :rm, builder_name_with_arch(arch)
|
||||||
end
|
end
|
||||||
|
|
||||||
def local
|
|
||||||
config.builder["local"]
|
|
||||||
end
|
|
||||||
|
|
||||||
def remote
|
|
||||||
config.builder["remote"]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
class Mrsk::Commands::Builder::Native < Mrsk::Commands::Builder::Base
|
class Mrsk::Commands::Builder::Native < Mrsk::Commands::Builder::Base
|
||||||
def create
|
def create
|
||||||
# No-op on native
|
# No-op on native without cache
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove
|
def remove
|
||||||
# No-op on native
|
# No-op on native without cache
|
||||||
end
|
end
|
||||||
|
|
||||||
def push
|
def push
|
||||||
|
|||||||
16
lib/mrsk/commands/builder/native/cached.rb
Normal file
16
lib/mrsk/commands/builder/native/cached.rb
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
class Mrsk::Commands::Builder::Native::Cached < Mrsk::Commands::Builder::Native
|
||||||
|
def create
|
||||||
|
docker :buildx, :create, "--use", "--driver=docker-container"
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove
|
||||||
|
docker :buildx, :rm, builder_name
|
||||||
|
end
|
||||||
|
|
||||||
|
def push
|
||||||
|
docker :buildx, :build,
|
||||||
|
"--push",
|
||||||
|
*build_options,
|
||||||
|
build_context
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -28,29 +28,21 @@ class Mrsk::Commands::Builder::Native::Remote < Mrsk::Commands::Builder::Native
|
|||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
def arch
|
|
||||||
config.builder["remote"]["arch"]
|
|
||||||
end
|
|
||||||
|
|
||||||
def host
|
|
||||||
config.builder["remote"]["host"]
|
|
||||||
end
|
|
||||||
|
|
||||||
def builder_name
|
def builder_name
|
||||||
"mrsk-#{config.service}-native-remote"
|
"mrsk-#{config.service}-native-remote"
|
||||||
end
|
end
|
||||||
|
|
||||||
def builder_name_with_arch
|
def builder_name_with_arch
|
||||||
"#{builder_name}-#{arch}"
|
"#{builder_name}-#{remote_arch}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def platform
|
def platform
|
||||||
"linux/#{arch}"
|
"linux/#{remote_arch}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_context
|
def create_context
|
||||||
docker :context, :create,
|
docker :context, :create,
|
||||||
builder_name_with_arch, "--description", "'#{builder_name} #{arch} native host'", "--docker", "'host=#{host}'"
|
builder_name_with_arch, "--description", "'#{builder_name} #{remote_arch} native host'", "--docker", "'host=#{remote_host}'"
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_context
|
def remove_context
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class Mrsk::Commands::Lock < Mrsk::Commands::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def lock_dir
|
def lock_dir
|
||||||
:mrsk_lock
|
"mrsk_lock-#{config.service}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def lock_details_file
|
def lock_details_file
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ class Mrsk::Commands::Traefik < Mrsk::Commands::Base
|
|||||||
|
|
||||||
DEFAULT_IMAGE = "traefik:v2.9"
|
DEFAULT_IMAGE = "traefik:v2.9"
|
||||||
CONTAINER_PORT = 80
|
CONTAINER_PORT = 80
|
||||||
|
DEFAULT_ARGS = {
|
||||||
|
'log.level' => 'DEBUG'
|
||||||
|
}
|
||||||
|
|
||||||
def run
|
def run
|
||||||
docker :run, "--name traefik",
|
docker :run, "--name traefik",
|
||||||
@@ -16,7 +19,6 @@ class Mrsk::Commands::Traefik < Mrsk::Commands::Base
|
|||||||
*docker_options_args,
|
*docker_options_args,
|
||||||
image,
|
image,
|
||||||
"--providers.docker",
|
"--providers.docker",
|
||||||
"--log.level=DEBUG",
|
|
||||||
*cmd_option_args
|
*cmd_option_args
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -86,9 +88,9 @@ class Mrsk::Commands::Traefik < Mrsk::Commands::Base
|
|||||||
|
|
||||||
def cmd_option_args
|
def cmd_option_args
|
||||||
if args = config.traefik["args"]
|
if args = config.traefik["args"]
|
||||||
optionize args, with: "="
|
optionize DEFAULT_ARGS.merge(args), with: "="
|
||||||
else
|
else
|
||||||
[]
|
optionize DEFAULT_ARGS, with: "="
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ require "erb"
|
|||||||
require "net/ssh/proxy/jump"
|
require "net/ssh/proxy/jump"
|
||||||
|
|
||||||
class Mrsk::Configuration
|
class Mrsk::Configuration
|
||||||
delegate :service, :image, :servers, :env, :labels, :registry, :builder, :stop_wait_time, :hooks_path, to: :raw_config, allow_nil: true
|
delegate :service, :image, :servers, :env, :labels, :registry, :stop_wait_time, :hooks_path, to: :raw_config, allow_nil: true
|
||||||
delegate :argumentize, :argumentize_env_with_secrets, :optionize, to: Mrsk::Utils
|
delegate :argumentize, :argumentize_env_with_secrets, :optionize, to: Mrsk::Utils
|
||||||
|
|
||||||
attr_accessor :destination
|
attr_accessor :destination
|
||||||
@@ -165,8 +165,12 @@ class Mrsk::Configuration
|
|||||||
raw_config.readiness_delay || 7
|
raw_config.readiness_delay || 7
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def minimum_version
|
||||||
|
raw_config.minimum_version
|
||||||
|
end
|
||||||
|
|
||||||
def valid?
|
def valid?
|
||||||
ensure_required_keys_present && ensure_env_available
|
ensure_required_keys_present && ensure_valid_mrsk_version
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -182,7 +186,7 @@ class Mrsk::Configuration
|
|||||||
env_args: env_args,
|
env_args: env_args,
|
||||||
volume_args: volume_args,
|
volume_args: volume_args,
|
||||||
ssh_options: ssh_options,
|
ssh_options: ssh_options,
|
||||||
builder: raw_config.builder,
|
builder: builder.to_h,
|
||||||
accessories: raw_config.accessories,
|
accessories: raw_config.accessories,
|
||||||
logging: logging_args,
|
logging: logging_args,
|
||||||
healthcheck: healthcheck
|
healthcheck: healthcheck
|
||||||
@@ -197,6 +201,18 @@ class Mrsk::Configuration
|
|||||||
raw_config.hooks_path || ".mrsk/hooks"
|
raw_config.hooks_path || ".mrsk/hooks"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def builder
|
||||||
|
Mrsk::Configuration::Builder.new(config: self)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Will raise KeyError if any secret ENVs are missing
|
||||||
|
def ensure_env_available
|
||||||
|
env_args
|
||||||
|
roles.each(&:env_args)
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
# Will raise ArgumentError if any required config keys are missing
|
# Will raise ArgumentError if any required config keys are missing
|
||||||
def ensure_required_keys_present
|
def ensure_required_keys_present
|
||||||
@@ -221,14 +237,15 @@ class Mrsk::Configuration
|
|||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
# Will raise KeyError if any secret ENVs are missing
|
def ensure_valid_mrsk_version
|
||||||
def ensure_env_available
|
if minimum_version && Gem::Version.new(minimum_version) > Gem::Version.new(Mrsk::VERSION)
|
||||||
env_args
|
raise ArgumentError, "Current version is #{Mrsk::VERSION}, minimum required is #{minimum_version}"
|
||||||
roles.each(&:env_args)
|
end
|
||||||
|
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def role_names
|
def role_names
|
||||||
raw_config.servers.is_a?(Array) ? [ "web" ] : raw_config.servers.keys.sort
|
raw_config.servers.is_a?(Array) ? [ "web" ] : raw_config.servers.keys.sort
|
||||||
end
|
end
|
||||||
|
|||||||
114
lib/mrsk/configuration/builder.rb
Normal file
114
lib/mrsk/configuration/builder.rb
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
class Mrsk::Configuration::Builder
|
||||||
|
def initialize(config:)
|
||||||
|
@options = config.raw_config.builder || {}
|
||||||
|
@image = config.image
|
||||||
|
@server = config.registry["server"]
|
||||||
|
|
||||||
|
valid?
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_h
|
||||||
|
@options
|
||||||
|
end
|
||||||
|
|
||||||
|
def multiarch?
|
||||||
|
@options["multiarch"] != false
|
||||||
|
end
|
||||||
|
|
||||||
|
def local?
|
||||||
|
!!@options["local"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def remote?
|
||||||
|
!!@options["remote"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def cached?
|
||||||
|
!!@options["cache"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def args
|
||||||
|
@options["args"] || {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def secrets
|
||||||
|
@options["secrets"] || []
|
||||||
|
end
|
||||||
|
|
||||||
|
def dockerfile
|
||||||
|
@options["dockerfile"] || "Dockerfile"
|
||||||
|
end
|
||||||
|
|
||||||
|
def context
|
||||||
|
@options["context"] || "."
|
||||||
|
end
|
||||||
|
|
||||||
|
def local_arch
|
||||||
|
@options["local"]["arch"] if local?
|
||||||
|
end
|
||||||
|
|
||||||
|
def local_host
|
||||||
|
@options["local"]["host"] if local?
|
||||||
|
end
|
||||||
|
|
||||||
|
def remote_arch
|
||||||
|
@options["remote"]["arch"] if remote?
|
||||||
|
end
|
||||||
|
|
||||||
|
def remote_host
|
||||||
|
@options["remote"]["host"] if remote?
|
||||||
|
end
|
||||||
|
|
||||||
|
def cache_from
|
||||||
|
if cached?
|
||||||
|
case @options["cache"]["type"]
|
||||||
|
when "gha"
|
||||||
|
cache_from_config_for_gha
|
||||||
|
when "registry"
|
||||||
|
cache_from_config_for_registry
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def cache_to
|
||||||
|
if cached?
|
||||||
|
case @options["cache"]["type"]
|
||||||
|
when "gha"
|
||||||
|
cache_to_config_for_gha
|
||||||
|
when "registry"
|
||||||
|
cache_to_config_for_registry
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def valid?
|
||||||
|
if @options["local"] && !@options["remote"]
|
||||||
|
raise ArgumentError, "You must specify both local and remote builder config for remote multiarch builds"
|
||||||
|
end
|
||||||
|
|
||||||
|
if @options["cache"] && @options["cache"]["type"]
|
||||||
|
raise ArgumentError, "Invalid cache type: #{@options["cache"]["type"]}" unless ["gha", "registry"].include?(@options["cache"]["type"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def cache_image
|
||||||
|
@options["cache"]&.fetch("image", nil) || "#{@image}-build-cache"
|
||||||
|
end
|
||||||
|
|
||||||
|
def cache_from_config_for_gha
|
||||||
|
"type=gha"
|
||||||
|
end
|
||||||
|
|
||||||
|
def cache_from_config_for_registry
|
||||||
|
[ "type=registry", "ref=#{@server}/#{cache_image}" ].compact.join(",")
|
||||||
|
end
|
||||||
|
|
||||||
|
def cache_to_config_for_gha
|
||||||
|
[ "type=gha", @options["cache"]&.fetch("options", nil)].compact.join(",")
|
||||||
|
end
|
||||||
|
|
||||||
|
def cache_to_config_for_registry
|
||||||
|
[ "type=registry", @options["cache"]&.fetch("options", nil), "ref=#{@server}/#{cache_image}" ].compact.join(",")
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
module Mrsk
|
module Mrsk
|
||||||
VERSION = "0.13.2"
|
VERSION = "0.15.1"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -40,9 +40,10 @@ class CliAccessoryTest < CliTestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "reboot" do
|
test "reboot" do
|
||||||
|
Mrsk::Commands::Registry.any_instance.expects(:login)
|
||||||
Mrsk::Cli::Accessory.any_instance.expects(:stop).with("mysql")
|
Mrsk::Cli::Accessory.any_instance.expects(:stop).with("mysql")
|
||||||
Mrsk::Cli::Accessory.any_instance.expects(:remove_container).with("mysql")
|
Mrsk::Cli::Accessory.any_instance.expects(:remove_container).with("mysql")
|
||||||
Mrsk::Cli::Accessory.any_instance.expects(:boot).with("mysql")
|
Mrsk::Cli::Accessory.any_instance.expects(:boot).with("mysql", login: false)
|
||||||
|
|
||||||
run_command("reboot", "mysql")
|
run_command("reboot", "mysql")
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ class CliTestCase < ActiveSupport::TestCase
|
|||||||
|
|
||||||
def stub_locking
|
def stub_locking
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
|
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
|
||||||
.with { |arg1, arg2| arg1 == :mkdir && arg2 == :mrsk_lock }
|
.with { |arg1, arg2| arg1 == :mkdir && arg2 == "mrsk_lock-app" }
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
|
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
|
||||||
.with { |arg1, arg2| arg1 == :rm && arg2 == "mrsk_lock/details" }
|
.with { |arg1, arg2| arg1 == :rm && arg2 == "mrsk_lock-app/details" }
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_hook_ran(hook, output, version:, service_version:, hosts:, command:, subcommand: nil, runtime: nil)
|
def assert_hook_ran(hook, output, version:, service_version:, hosts:, command:, subcommand: nil, runtime: nil)
|
||||||
|
|||||||
@@ -63,11 +63,11 @@ class CliMainTest < CliTestCase
|
|||||||
Thread.report_on_exception = false
|
Thread.report_on_exception = false
|
||||||
|
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
|
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
|
||||||
.with { |*arg| arg[0..1] == [:mkdir, :mrsk_lock] }
|
.with { |*arg| arg[0..1] == [:mkdir, 'mrsk_lock-app'] }
|
||||||
.raises(RuntimeError, "mkdir: cannot create directory ‘mrsk_lock’: File exists")
|
.raises(RuntimeError, "mkdir: cannot create directory ‘mrsk_lock-app’: File exists")
|
||||||
|
|
||||||
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_debug)
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_debug)
|
||||||
.with(:stat, :mrsk_lock, ">", "/dev/null", "&&", :cat, "mrsk_lock/details", "|", :base64, "-d")
|
.with(:stat, 'mrsk_lock-app', ">", "/dev/null", "&&", :cat, "mrsk_lock-app/details", "|", :base64, "-d")
|
||||||
|
|
||||||
assert_raises(Mrsk::Cli::LockError) do
|
assert_raises(Mrsk::Cli::LockError) do
|
||||||
run_command("deploy")
|
run_command("deploy")
|
||||||
@@ -78,7 +78,7 @@ class CliMainTest < CliTestCase
|
|||||||
Thread.report_on_exception = false
|
Thread.report_on_exception = false
|
||||||
|
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
|
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
|
||||||
.with { |*arg| arg[0..1] == [:mkdir, :mrsk_lock] }
|
.with { |*arg| arg[0..1] == [:mkdir, 'mrsk_lock-app'] }
|
||||||
.raises(SocketError, "getaddrinfo: nodename nor servname provided, or not known")
|
.raises(SocketError, "getaddrinfo: nodename nor servname provided, or not known")
|
||||||
|
|
||||||
assert_raises(SSHKit::Runner::ExecuteError) do
|
assert_raises(SSHKit::Runner::ExecuteError) do
|
||||||
@@ -116,6 +116,12 @@ class CliMainTest < CliTestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "deploy with missing secrets" do
|
||||||
|
assert_raises(KeyError) do
|
||||||
|
run_command("deploy", config_file: "deploy_with_secrets")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "redeploy" do
|
test "redeploy" do
|
||||||
invoke_options = { "config_file" => "test/fixtures/deploy_simple.yml", "version" => "999", "skip_hooks" => false }
|
invoke_options = { "config_file" => "test/fixtures/deploy_simple.yml", "version" => "999", "skip_hooks" => false }
|
||||||
|
|
||||||
|
|||||||
@@ -4,16 +4,24 @@ class CliTraefikTest < CliTestCase
|
|||||||
test "boot" do
|
test "boot" do
|
||||||
run_command("boot").tap do |output|
|
run_command("boot").tap do |output|
|
||||||
assert_match "docker login", output
|
assert_match "docker login", output
|
||||||
assert_match "docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{Mrsk::Commands::Traefik::DEFAULT_IMAGE} --providers.docker --log.level=DEBUG", output
|
assert_match "docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{Mrsk::Commands::Traefik::DEFAULT_IMAGE} --providers.docker --log.level=\"DEBUG\"", output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "reboot" do
|
test "reboot" do
|
||||||
Mrsk::Cli::Traefik.any_instance.expects(:stop)
|
Mrsk::Commands::Registry.any_instance.expects(:login).twice
|
||||||
Mrsk::Cli::Traefik.any_instance.expects(:remove_container)
|
|
||||||
Mrsk::Cli::Traefik.any_instance.expects(:boot)
|
|
||||||
|
|
||||||
run_command("reboot")
|
run_command("reboot").tap do |output|
|
||||||
|
assert_match "docker container stop traefik", output
|
||||||
|
assert_match "docker container prune --force --filter label=org.opencontainers.image.title=Traefik", output
|
||||||
|
assert_match "docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{Mrsk::Commands::Traefik::DEFAULT_IMAGE} --providers.docker --log.level=\"DEBUG\"", output
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "reboot --rolling" do
|
||||||
|
run_command("reboot", "--rolling").tap do |output|
|
||||||
|
assert_match "Running docker container prune --force --filter label=org.opencontainers.image.title=Traefik on 1.1.1.1", output.lines[3]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "start" do
|
test "start" do
|
||||||
|
|||||||
@@ -211,6 +211,10 @@ class CommandsAppTest < ActiveSupport::TestCase
|
|||||||
assert_equal "ssh -J root@2.2.2.2 -t app@1.1.1.1 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
|
assert_equal "ssh -J root@2.2.2.2 -t app@1.1.1.1 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "run over ssh with proxy_command" do
|
||||||
|
@config[:ssh] = { "proxy_command" => "ssh -W %h:%p user@proxy-server" }
|
||||||
|
assert_equal "ssh -o ProxyCommand='ssh -W %h:%p user@proxy-server' -t root@1.1.1.1 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
|
||||||
|
end
|
||||||
|
|
||||||
test "current_running_container_id" do
|
test "current_running_container_id" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ class CommandsBuilderTest < ActiveSupport::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "target multiarch by default" do
|
test "target multiarch by default" do
|
||||||
builder = new_builder_command
|
builder = new_builder_command(builder: { "cache" => { "type" => "gha" }})
|
||||||
assert_equal "multiarch", builder.name
|
assert_equal "multiarch", builder.name
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker buildx build --push --platform linux/amd64,linux/arm64 --builder mrsk-app-multiarch -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --file Dockerfile .",
|
"docker buildx build --push --platform linux/amd64,linux/arm64 --builder mrsk-app-multiarch -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile .",
|
||||||
builder.push.join(" ")
|
builder.push.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -21,19 +21,27 @@ class CommandsBuilderTest < ActiveSupport::TestCase
|
|||||||
builder.push.join(" ")
|
builder.push.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "target native cached when multiarch is off and cache is set" do
|
||||||
|
builder = new_builder_command(builder: { "multiarch" => false, "cache" => { "type" => "gha" }})
|
||||||
|
assert_equal "native/cached", builder.name
|
||||||
|
assert_equal \
|
||||||
|
"docker buildx build --push -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile .",
|
||||||
|
builder.push.join(" ")
|
||||||
|
end
|
||||||
|
|
||||||
test "target multiarch remote when local and remote is set" do
|
test "target multiarch remote when local and remote is set" do
|
||||||
builder = new_builder_command(builder: { "local" => { }, "remote" => { } })
|
builder = new_builder_command(builder: { "local" => { }, "remote" => { }, "cache" => { "type" => "gha" } })
|
||||||
assert_equal "multiarch/remote", builder.name
|
assert_equal "multiarch/remote", builder.name
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker buildx build --push --platform linux/amd64,linux/arm64 --builder mrsk-app-multiarch-remote -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --file Dockerfile .",
|
"docker buildx build --push --platform linux/amd64,linux/arm64 --builder mrsk-app-multiarch-remote -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile .",
|
||||||
builder.push.join(" ")
|
builder.push.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "target native remote when only remote is set" do
|
test "target native remote when only remote is set" do
|
||||||
builder = new_builder_command(builder: { "remote" => { "arch" => "amd64" } })
|
builder = new_builder_command(builder: { "remote" => { "arch" => "amd64" }, "cache" => { "type" => "gha" } })
|
||||||
assert_equal "native/remote", builder.name
|
assert_equal "native/remote", builder.name
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker buildx build --push --platform linux/amd64 --builder mrsk-app-native-remote -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --file Dockerfile .",
|
"docker buildx build --push --platform linux/amd64 --builder mrsk-app-native-remote -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile .",
|
||||||
builder.push.join(" ")
|
builder.push.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -10,19 +10,19 @@ class CommandsLockTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
test "status" do
|
test "status" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"stat mrsk_lock > /dev/null && cat mrsk_lock/details | base64 -d",
|
"stat mrsk_lock-app > /dev/null && cat mrsk_lock-app/details | base64 -d",
|
||||||
new_command.status.join(" ")
|
new_command.status.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "acquire" do
|
test "acquire" do
|
||||||
assert_match \
|
assert_match \
|
||||||
/mkdir mrsk_lock && echo ".*" > mrsk_lock\/details/m,
|
/mkdir mrsk_lock-app && echo ".*" > mrsk_lock-app\/details/m,
|
||||||
new_command.acquire("Hello", "123").join(" ")
|
new_command.acquire("Hello", "123").join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "release" do
|
test "release" do
|
||||||
assert_match \
|
assert_match \
|
||||||
"rm mrsk_lock/details && rm -r mrsk_lock",
|
"rm mrsk_lock-app/details && rm -r mrsk_lock-app",
|
||||||
new_command.release.join(" ")
|
new_command.release.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -18,67 +18,67 @@ class CommandsTraefikTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
test "run" do
|
test "run" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{@image} --providers.docker --log.level=DEBUG --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
||||||
new_command.run.join(" ")
|
new_command.run.join(" ")
|
||||||
|
|
||||||
@config[:traefik]["host_port"] = "8080"
|
@config[:traefik]["host_port"] = "8080"
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name traefik --detach --restart unless-stopped --publish 8080:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{@image} --providers.docker --log.level=DEBUG --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
"docker run --name traefik --detach --restart unless-stopped --publish 8080:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
||||||
new_command.run.join(" ")
|
new_command.run.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "run with ports configured" do
|
test "run with ports configured" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{@image} --providers.docker --log.level=DEBUG --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
||||||
new_command.run.join(" ")
|
new_command.run.join(" ")
|
||||||
|
|
||||||
@config[:traefik]["options"] = {"publish" => %w[9000:9000 9001:9001]}
|
@config[:traefik]["options"] = {"publish" => %w[9000:9000 9001:9001]}
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" --publish \"9000:9000\" --publish \"9001:9001\" #{@image} --providers.docker --log.level=DEBUG --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" --publish \"9000:9000\" --publish \"9001:9001\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
||||||
new_command.run.join(" ")
|
new_command.run.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "run with volumes configured" do
|
test "run with volumes configured" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{@image} --providers.docker --log.level=DEBUG --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
||||||
new_command.run.join(" ")
|
new_command.run.join(" ")
|
||||||
|
|
||||||
@config[:traefik]["options"] = {"volume" => %w[./letsencrypt/acme.json:/letsencrypt/acme.json] }
|
@config[:traefik]["options"] = {"volume" => %w[./letsencrypt/acme.json:/letsencrypt/acme.json] }
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" --volume \"./letsencrypt/acme.json:/letsencrypt/acme.json\" #{@image} --providers.docker --log.level=DEBUG --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" --volume \"./letsencrypt/acme.json:/letsencrypt/acme.json\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
||||||
new_command.run.join(" ")
|
new_command.run.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "run with several options configured" do
|
test "run with several options configured" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{@image} --providers.docker --log.level=DEBUG --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
||||||
new_command.run.join(" ")
|
new_command.run.join(" ")
|
||||||
|
|
||||||
@config[:traefik]["options"] = {"volume" => %w[./letsencrypt/acme.json:/letsencrypt/acme.json], "publish" => %w[8080:8080], "memory" => "512m"}
|
@config[:traefik]["options"] = {"volume" => %w[./letsencrypt/acme.json:/letsencrypt/acme.json], "publish" => %w[8080:8080], "memory" => "512m"}
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" --volume \"./letsencrypt/acme.json:/letsencrypt/acme.json\" --publish \"8080:8080\" --memory \"512m\" #{@image} --providers.docker --log.level=DEBUG --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" --volume \"./letsencrypt/acme.json:/letsencrypt/acme.json\" --publish \"8080:8080\" --memory \"512m\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
||||||
new_command.run.join(" ")
|
new_command.run.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "run with labels configured" do
|
test "run with labels configured" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{@image} --providers.docker --log.level=DEBUG --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
||||||
new_command.run.join(" ")
|
new_command.run.join(" ")
|
||||||
|
|
||||||
@config[:traefik]["labels"] = { "traefik.http.routers.dashboard.service" => "api@internal", "traefik.http.routers.dashboard.middlewares" => "auth" }
|
@config[:traefik]["labels"] = { "traefik.http.routers.dashboard.service" => "api@internal", "traefik.http.routers.dashboard.middlewares" => "auth" }
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" --label traefik.http.routers.dashboard.service=\"api@internal\" --label traefik.http.routers.dashboard.middlewares=\"auth\" #{@image} --providers.docker --log.level=DEBUG --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" --label traefik.http.routers.dashboard.service=\"api@internal\" --label traefik.http.routers.dashboard.middlewares=\"auth\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
||||||
new_command.run.join(" ")
|
new_command.run.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "run with env configured" do
|
test "run with env configured" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{@image} --providers.docker --log.level=DEBUG --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
||||||
new_command.run.join(" ")
|
new_command.run.join(" ")
|
||||||
|
|
||||||
@config[:traefik]["env"] = { "secret" => %w[EXAMPLE_API_KEY] }
|
@config[:traefik]["env"] = { "secret" => %w[EXAMPLE_API_KEY] }
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock -e EXAMPLE_API_KEY=\"456\" --log-opt max-size=\"10m\" #{@image} --providers.docker --log.level=DEBUG --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock -e EXAMPLE_API_KEY=\"456\" --log-opt max-size=\"10m\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
||||||
new_command.run.join(" ")
|
new_command.run.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ class CommandsTraefikTest < ActiveSupport::TestCase
|
|||||||
@config.delete(:traefik)
|
@config.delete(:traefik)
|
||||||
|
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{Mrsk::Commands::Traefik::DEFAULT_IMAGE} --providers.docker --log.level=DEBUG",
|
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{Mrsk::Commands::Traefik::DEFAULT_IMAGE} --providers.docker --log.level=\"DEBUG\"",
|
||||||
new_command.run.join(" ")
|
new_command.run.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -94,7 +94,15 @@ class CommandsTraefikTest < ActiveSupport::TestCase
|
|||||||
@config[:logging] = { "driver" => "local", "options" => { "max-size" => "100m", "max-file" => "3" } }
|
@config[:logging] = { "driver" => "local", "options" => { "max-size" => "100m", "max-file" => "3" } }
|
||||||
|
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-driver \"local\" --log-opt max-size=\"100m\" --log-opt max-file=\"3\" #{@image} --providers.docker --log.level=DEBUG --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-driver \"local\" --log-opt max-size=\"100m\" --log-opt max-file=\"3\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
||||||
|
new_command.run.join(" ")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "run with default args overriden" do
|
||||||
|
@config[:traefik]["args"]["log.level"] = "ERROR"
|
||||||
|
|
||||||
|
assert_equal \
|
||||||
|
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{@image} --providers.docker --log.level=\"ERROR\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
|
||||||
new_command.run.join(" ")
|
new_command.run.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
151
test/configuration/builder_test.rb
Normal file
151
test/configuration/builder_test.rb
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class ConfigurationBuilderTest < ActiveSupport::TestCase
|
||||||
|
setup do
|
||||||
|
@deploy = {
|
||||||
|
service: "app", image: "dhh/app", registry: { "username" => "dhh", "password" => "secret" },
|
||||||
|
servers: [ "1.1.1.1" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@config = Mrsk::Configuration.new(@deploy)
|
||||||
|
|
||||||
|
@deploy_with_builder_option = {
|
||||||
|
service: "app", image: "dhh/app", registry: { "username" => "dhh", "password" => "secret" },
|
||||||
|
servers: [ "1.1.1.1" ],
|
||||||
|
builder: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@config_with_builder_option = Mrsk::Configuration.new(@deploy_with_builder_option)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "multiarch?" do
|
||||||
|
assert_equal true, @config.builder.multiarch?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "setting multiarch to false" do
|
||||||
|
@deploy_with_builder_option[:builder] = { "multiarch" => false }
|
||||||
|
|
||||||
|
assert_equal false, @config_with_builder_option.builder.multiarch?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "local?" do
|
||||||
|
assert_equal false, @config.builder.local?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "remote?" do
|
||||||
|
assert_equal false, @config.builder.remote?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "remote_arch" do
|
||||||
|
assert_nil @config.builder.remote_arch
|
||||||
|
end
|
||||||
|
|
||||||
|
test "remote_host" do
|
||||||
|
assert_nil @config.builder.remote_host
|
||||||
|
end
|
||||||
|
|
||||||
|
test "remote config is missing when local is specified" do
|
||||||
|
@deploy_with_builder_option[:builder] = { "local" => { "arch" => "arm64", "host" => "unix:///Users/<%= `whoami`.strip %>/.docker/run/docker.sock" } }
|
||||||
|
|
||||||
|
assert_raises(ArgumentError) do
|
||||||
|
@config_with_builder_option.builder
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "setting both local and remote configs" do
|
||||||
|
@deploy_with_builder_option[:builder] = {
|
||||||
|
"local" => { "arch" => "arm64", "host" => "unix:///Users/<%= `whoami`.strip %>/.docker/run/docker.sock" },
|
||||||
|
"remote" => { "arch" => "amd64", "host" => "ssh://root@192.168.0.1" }
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_equal true, @config_with_builder_option.builder.local?
|
||||||
|
assert_equal true, @config_with_builder_option.builder.remote?
|
||||||
|
|
||||||
|
assert_equal "amd64", @config_with_builder_option.builder.remote_arch
|
||||||
|
assert_equal "ssh://root@192.168.0.1", @config_with_builder_option.builder.remote_host
|
||||||
|
|
||||||
|
assert_equal "arm64", @config_with_builder_option.builder.local_arch
|
||||||
|
assert_equal "unix:///Users/<%= `whoami`.strip %>/.docker/run/docker.sock", @config_with_builder_option.builder.local_host
|
||||||
|
end
|
||||||
|
|
||||||
|
test "cached?" do
|
||||||
|
assert_equal false, @config.builder.cached?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "invalid cache type specified" do
|
||||||
|
@deploy_with_builder_option[:builder] = { "cache" => { "type" => "invalid" } }
|
||||||
|
|
||||||
|
assert_raises(ArgumentError) do
|
||||||
|
@config_with_builder_option.builder
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "cache_from" do
|
||||||
|
assert_nil @config.builder.cache_from
|
||||||
|
end
|
||||||
|
|
||||||
|
test "cache_to" do
|
||||||
|
assert_nil @config.builder.cache_to
|
||||||
|
end
|
||||||
|
|
||||||
|
test "setting gha cache" do
|
||||||
|
@deploy_with_builder_option[:builder] = { "cache" => { "type" => "gha", "options" => "mode=max" } }
|
||||||
|
|
||||||
|
assert_equal "type=gha", @config_with_builder_option.builder.cache_from
|
||||||
|
assert_equal "type=gha,mode=max", @config_with_builder_option.builder.cache_to
|
||||||
|
end
|
||||||
|
|
||||||
|
test "setting registry cache" do
|
||||||
|
@deploy_with_builder_option[:builder] = { "cache" => { "type" => "registry", "options" => "mode=max,image-manifest=true,oci-mediatypes=true" } }
|
||||||
|
|
||||||
|
assert_equal "type=registry,ref=/dhh/app-build-cache", @config_with_builder_option.builder.cache_from
|
||||||
|
assert_equal "type=registry,mode=max,image-manifest=true,oci-mediatypes=true,ref=/dhh/app-build-cache", @config_with_builder_option.builder.cache_to
|
||||||
|
end
|
||||||
|
|
||||||
|
test "setting registry cache with image" do
|
||||||
|
@deploy_with_builder_option[:builder] = { "cache" => { "type" => "registry", "image" => "mrsk", "options" => "mode=max" } }
|
||||||
|
|
||||||
|
assert_equal "type=registry,ref=/mrsk", @config_with_builder_option.builder.cache_from
|
||||||
|
assert_equal "type=registry,mode=max,ref=/mrsk", @config_with_builder_option.builder.cache_to
|
||||||
|
end
|
||||||
|
|
||||||
|
test "args" do
|
||||||
|
assert_equal({}, @config.builder.args)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "setting args" do
|
||||||
|
@deploy_with_builder_option[:builder] = { "args" => { "key" => "value" } }
|
||||||
|
|
||||||
|
assert_equal({ "key" => "value" }, @config_with_builder_option.builder.args)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "secrets" do
|
||||||
|
assert_equal [], @config.builder.secrets
|
||||||
|
end
|
||||||
|
|
||||||
|
test "setting secrets" do
|
||||||
|
@deploy_with_builder_option[:builder] = { "secrets" => ["GITHUB_TOKEN"] }
|
||||||
|
|
||||||
|
assert_equal ["GITHUB_TOKEN"], @config_with_builder_option.builder.secrets
|
||||||
|
end
|
||||||
|
|
||||||
|
test "dockerfile" do
|
||||||
|
assert_equal "Dockerfile", @config.builder.dockerfile
|
||||||
|
end
|
||||||
|
|
||||||
|
test "setting dockerfile" do
|
||||||
|
@deploy_with_builder_option[:builder] = { "dockerfile" => "Dockerfile.dev" }
|
||||||
|
|
||||||
|
assert_equal "Dockerfile.dev", @config_with_builder_option.builder.dockerfile
|
||||||
|
end
|
||||||
|
|
||||||
|
test "context" do
|
||||||
|
assert_equal ".", @config.builder.context
|
||||||
|
end
|
||||||
|
|
||||||
|
test "setting context" do
|
||||||
|
@deploy_with_builder_option[:builder] = { "context" => ".." }
|
||||||
|
|
||||||
|
assert_equal "..", @config_with_builder_option.builder.context
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -166,7 +166,7 @@ class ConfigurationTest < ActiveSupport::TestCase
|
|||||||
assert_raises(KeyError) do
|
assert_raises(KeyError) do
|
||||||
config = Mrsk::Configuration.new(@deploy.tap { |c| c.merge!({
|
config = Mrsk::Configuration.new(@deploy.tap { |c| c.merge!({
|
||||||
env: { "secret" => [ "PASSWORD" ] }
|
env: { "secret" => [ "PASSWORD" ] }
|
||||||
}) })
|
}) }).ensure_env_available
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -266,6 +266,22 @@ class ConfigurationTest < ActiveSupport::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "to_h" do
|
test "to_h" do
|
||||||
assert_equal({ :roles=>["web"], :hosts=>["1.1.1.1", "1.1.1.2"], :primary_host=>"1.1.1.1", :version=>"missing", :repository=>"dhh/app", :absolute_image=>"dhh/app:missing", :service_with_version=>"app-missing", :env_args=>["-e", "REDIS_URL=\"redis://x/y\""], :ssh_options=>{:user=>"root", :auth_methods=>["publickey"]}, :volume_args=>["--volume", "/local/path:/container/path"], :logging=>["--log-opt", "max-size=\"10m\""], :healthcheck=>{"path"=>"/up", "port"=>3000, "max_attempts" => 7 }}, @config.to_h)
|
assert_equal({ :roles=>["web"], :hosts=>["1.1.1.1", "1.1.1.2"], :primary_host=>"1.1.1.1", :version=>"missing", :repository=>"dhh/app", :absolute_image=>"dhh/app:missing", :service_with_version=>"app-missing", :env_args=>["-e", "REDIS_URL=\"redis://x/y\""], :ssh_options=>{:user=>"root", :auth_methods=>["publickey"]}, :volume_args=>["--volume", "/local/path:/container/path"], :builder=>{}, :logging=>["--log-opt", "max-size=\"10m\""], :healthcheck=>{"path"=>"/up", "port"=>3000, "max_attempts" => 7 }}, @config.to_h)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "min version is lower" do
|
||||||
|
config = Mrsk::Configuration.new(@deploy.tap { |c| c.merge!(minimum_version: "0.0.1") })
|
||||||
|
assert_equal "0.0.1", config.minimum_version
|
||||||
|
end
|
||||||
|
|
||||||
|
test "min version is equal" do
|
||||||
|
config = Mrsk::Configuration.new(@deploy.tap { |c| c.merge!(minimum_version: Mrsk::VERSION) })
|
||||||
|
assert_equal Mrsk::VERSION, config.minimum_version
|
||||||
|
end
|
||||||
|
|
||||||
|
test "min version is higher" do
|
||||||
|
assert_raises(ArgumentError) do
|
||||||
|
Mrsk::Configuration.new(@deploy.tap { |c| c.merge!(minimum_version: "10000.0.0") })
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
11
test/fixtures/deploy_with_secrets.yml
vendored
Normal file
11
test/fixtures/deploy_with_secrets.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
service: app
|
||||||
|
image: dhh/app
|
||||||
|
servers:
|
||||||
|
- "1.1.1.1"
|
||||||
|
- "1.1.1.2"
|
||||||
|
registry:
|
||||||
|
username: user
|
||||||
|
password: pw
|
||||||
|
env:
|
||||||
|
secret:
|
||||||
|
- PASSWORD
|
||||||
@@ -17,6 +17,7 @@ RUN apt-get update --fix-missing && apt-get install -y docker-ce docker-ce-cli c
|
|||||||
COPY *.sh .
|
COPY *.sh .
|
||||||
COPY app/ .
|
COPY app/ .
|
||||||
|
|
||||||
|
RUN rm -rf /root/.ssh
|
||||||
RUN ln -s /shared/ssh /root/.ssh
|
RUN ln -s /shared/ssh /root/.ssh
|
||||||
RUN mkdir -p /etc/docker/certs.d/registry:4443 && ln -s /shared/certs/domain.crt /etc/docker/certs.d/registry:4443/ca.crt
|
RUN mkdir -p /etc/docker/certs.d/registry:4443 && ln -s /shared/certs/domain.crt /etc/docker/certs.d/registry:4443/ca.crt
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,13 @@ class IntegrationTest < ActiveSupport::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
teardown do
|
teardown do
|
||||||
|
unless passed?
|
||||||
|
[:deployer, :vm1, :vm2, :shared, :load_balancer, :registry].each do |container|
|
||||||
|
puts
|
||||||
|
puts "Logs for #{container}:"
|
||||||
|
docker_compose :logs, container
|
||||||
|
end
|
||||||
|
end
|
||||||
docker_compose "down -t 1"
|
docker_compose "down -t 1"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user