smartmachine 0.8.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +661 -0
  3. data/README.md +147 -0
  4. data/bin/console +14 -0
  5. data/bin/setup +8 -0
  6. data/exe/smartmachine +3 -0
  7. data/lib/smart_machine/apps/app.rb +165 -181
  8. data/lib/smart_machine/apps/container.rb +120 -0
  9. data/lib/smart_machine/apps/manager.rb +182 -0
  10. data/lib/smart_machine/base.rb +24 -6
  11. data/lib/smart_machine/buildpackers/buildpacker.rb +95 -0
  12. data/lib/smart_machine/{engine/buildpacks → buildpackers}/rails/Dockerfile +3 -3
  13. data/lib/smart_machine/buildpackers/rails.rb +221 -0
  14. data/lib/smart_machine/commands/app.rb +112 -0
  15. data/lib/smart_machine/commands/buildpacker.rb +53 -0
  16. data/lib/smart_machine/commands/credentials.rb +17 -0
  17. data/lib/smart_machine/commands/docker.rb +23 -0
  18. data/lib/smart_machine/commands/engine.rb +27 -0
  19. data/lib/smart_machine/commands/grid.rb +33 -0
  20. data/lib/smart_machine/commands/grid_commands/elasticsearch.rb +68 -0
  21. data/lib/smart_machine/commands/grid_commands/minio.rb +65 -0
  22. data/lib/smart_machine/commands/grid_commands/mysql.rb +71 -0
  23. data/lib/smart_machine/commands/grid_commands/nginx.rb +53 -0
  24. data/lib/smart_machine/commands/grid_commands/prereceiver.rb +96 -0
  25. data/lib/smart_machine/commands/grid_commands/redis.rb +65 -0
  26. data/lib/smart_machine/commands/grid_commands/scheduler.rb +15 -0
  27. data/lib/smart_machine/commands/grid_commands/sub_thor.rb +15 -0
  28. data/lib/smart_machine/commands/machine.rb +24 -0
  29. data/lib/smart_machine/commands/syncer.rb +23 -0
  30. data/lib/smart_machine/commands/utilities.rb +37 -0
  31. data/lib/smart_machine/commands.rb +66 -0
  32. data/lib/smart_machine/configuration.rb +79 -0
  33. data/lib/smart_machine/credentials.rb +93 -95
  34. data/lib/smart_machine/docker.rb +144 -143
  35. data/lib/smart_machine/engine/Dockerfile +7 -5
  36. data/lib/smart_machine/engine.rb +104 -79
  37. data/lib/smart_machine/grids/elasticsearch.rb +70 -89
  38. data/lib/smart_machine/grids/minio.rb +79 -68
  39. data/lib/smart_machine/grids/mysql.rb +207 -202
  40. data/lib/smart_machine/grids/nginx.rb +176 -135
  41. data/lib/smart_machine/grids/prereceiver/Dockerfile +2 -2
  42. data/lib/smart_machine/grids/prereceiver/pre-receive +17 -0
  43. data/lib/smart_machine/grids/prereceiver.rb +169 -169
  44. data/lib/smart_machine/grids/redis.rb +75 -57
  45. data/lib/smart_machine/grids/scheduler/Dockerfile +1 -1
  46. data/lib/smart_machine/logger.rb +26 -26
  47. data/lib/smart_machine/machine.rb +128 -194
  48. data/lib/smart_machine/scp.rb +15 -0
  49. data/lib/smart_machine/ssh.rb +69 -40
  50. data/lib/smart_machine/syncer.rb +133 -0
  51. data/lib/smart_machine/templates/dotsmartmachine/Gemfile +7 -0
  52. data/lib/smart_machine/templates/dotsmartmachine/config/elasticsearch.yml +5 -0
  53. data/lib/smart_machine/templates/dotsmartmachine/config/environment.rb +0 -9
  54. data/lib/smart_machine/templates/dotsmartmachine/config/minio.yml +13 -0
  55. data/lib/smart_machine/templates/dotsmartmachine/config/mysql/schedule.rb +3 -3
  56. data/lib/smart_machine/templates/dotsmartmachine/config/mysql.yml +13 -0
  57. data/lib/smart_machine/templates/dotsmartmachine/config/prereceiver.yml +7 -0
  58. data/lib/smart_machine/templates/dotsmartmachine/config/redis.yml +15 -0
  59. data/lib/smart_machine/templates/dotsmartmachine/config/users.yml +1 -1
  60. data/lib/smart_machine/templates/dotsmartmachine/gitignore-template +47 -0
  61. data/lib/smart_machine/templates/dotsmartmachine/{grids/elasticsearch/data → vendor}/.keep +0 -0
  62. data/lib/smart_machine/version.rb +30 -6
  63. data/lib/smart_machine.rb +42 -16
  64. metadata +97 -47
  65. data/CHANGELOG.rdoc +0 -0
  66. data/MIT-LICENSE +0 -21
  67. data/README.rdoc +0 -87
  68. data/bin/buildpacker +0 -8
  69. data/bin/prereceiver +0 -8
  70. data/bin/scheduler +0 -18
  71. data/bin/smartmachine +0 -81
  72. data/bin/smartrunner +0 -33
  73. data/lib/smart_machine/apps/rails.rb +0 -250
  74. data/lib/smart_machine/apps.rb +0 -14
  75. data/lib/smart_machine/boot.rb +0 -32
  76. data/lib/smart_machine/buildpacker.rb +0 -106
  77. data/lib/smart_machine/gem_version.rb +0 -17
  78. data/lib/smart_machine/grids.rb +0 -18
  79. data/lib/smart_machine/sync.rb +0 -120
  80. data/lib/smart_machine/templates/dotsmartmachine/grids/elasticsearch/logs/.keep +0 -0
  81. data/lib/smart_machine/templates/dotsmartmachine/grids/minio/data/.keep +0 -0
  82. data/lib/smart_machine/templates/dotsmartmachine/grids/mysql/backups/.keep +0 -0
  83. data/lib/smart_machine/templates/dotsmartmachine/grids/mysql/data/.keep +0 -0
  84. data/lib/smart_machine/templates/dotsmartmachine/grids/prereceiver/pre-receive +0 -17
  85. data/lib/smart_machine/templates/dotsmartmachine/grids/redis/data/.keep +0 -0
  86. data/lib/smart_machine/user.rb +0 -38
data/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # SmartMachine
2
+
3
+ Git push should deploy.
4
+
5
+ SmartMachine is a full-stack deployment framework for rails optimized for admin programmer happiness and peaceful administration. It encourages natural simplicity by favoring convention over configuration.
6
+
7
+ Before you begin, you should install ruby on your system.
8
+
9
+ Deploy your Rails apps to your own server with - git push production main
10
+
11
+ ### How does it help?
12
+
13
+ After you run the below commands, you get.
14
+ 1. Setup of basic best practices of setting up and securing a VPS server.
15
+ 2. Setup and installation of Docker.
16
+ 3. Setup and installation of docker based Mysql, Solr, Nginx, App Prereceiver.
17
+ 4. Deployment of your Rails apps to your own server with - git push production main
18
+
19
+ ### Prerequisites
20
+
21
+ If using SmartMachine on a server, perform the below steps before proceeding.
22
+
23
+ Ensure that you have debian LTS installed on the server.
24
+ Running the below command should say some latest version of debian LTS.
25
+
26
+ $ cat /etc/issue
27
+
28
+ Then complete, getting started with linode.
29
+
30
+ https://www.linode.com/docs/getting-started/
31
+
32
+ And then secure your server.
33
+
34
+ https://www.linode.com/docs/security/securing-your-server/
35
+
36
+ ## Installation
37
+
38
+ Install SmartMachine at the command prompt:
39
+
40
+ $ gem install smartmachine
41
+
42
+ Then create a new machine and move into it:
43
+
44
+ $ smartmachine new yourmachinename
45
+ $ cd yourmachinename
46
+
47
+ here "yourmachinename" is the machine name you can choose
48
+
49
+ Add your credentials using:
50
+
51
+ $ smartmachine credentials:edit
52
+
53
+ Add your environment details using: - Coming Soon
54
+
55
+ $ smartmachine environment:edit
56
+
57
+ Add your users using:
58
+
59
+ $ smartmachine grid nginx users:edit
60
+
61
+ Install docker, and add UFW rules for Docker if specified at the end of installation.
62
+
63
+ $ smartmachine docker install
64
+
65
+ Install the engine and buildpacker:
66
+
67
+ $ smartmachine engine install
68
+ $ smartmachine buildpacker install rails
69
+
70
+ ## Usage
71
+
72
+ ### Choose Your Grids
73
+
74
+ Choose only the grids you need. You can start or stop a grid at anytime using <b>up</b> or <b>down</b> commands respectively.
75
+
76
+ #### 1. Nginx Grid
77
+ Lets you run a nginx web server fully equipped with https encryption using letsencrypt.
78
+
79
+ $ smartmachine grid nginx up
80
+ $ smartmachine grid nginx down
81
+
82
+ #### 2. Prereceiver Grid
83
+ Lets you push rails apps to your server without any additional configuration or downtime using <b>git push production main</b>.
84
+
85
+ $ smartmachine grid prereceiver install
86
+ $ smartmachine grid prereceiver up
87
+ $ smartmachine grid prereceiver down
88
+ $ smartmachine grid prereceiver uninstall
89
+
90
+ #### 3. Mysql Grid
91
+ Lets you run a mysql server instance with as many databases as you need.
92
+
93
+ $ smartmachine grid mysql up
94
+ $ smartmachine grid mysql down
95
+
96
+ #### 4. Minio Grid
97
+ Lets you run minio server instance with file storage persistance.
98
+
99
+ $ smartmachine grid minio up
100
+ $ smartmachine grid minio down
101
+
102
+ #### 5. Elasticsearch Grid
103
+ Lets you run elasticsearch server instance with data persistance.
104
+
105
+ $ smartmachine grid elasticsearch up
106
+ $ smartmachine grid elasticsearch down
107
+
108
+ #### 4. Redis Grid
109
+ Lets you run redis server instance for cache storage and job queueing.
110
+
111
+ $ smartmachine grid redis up
112
+ $ smartmachine grid redis down
113
+
114
+ #### 6. Scheduler Grid - Coming Soon
115
+ Lets you setup scheduling services like database backups, etc.
116
+
117
+ $ smartmachine grid scheduler install
118
+ $ smartmachine grid scheduler up
119
+ $ smartmachine grid scheduler down
120
+ $ smartmachine grid scheduler uninstall
121
+
122
+ ### Setup Your Apps
123
+
124
+ Create your apps and manage them.
125
+
126
+ #### 1. New App on the server
127
+ Lets you create a new bare app on the server.
128
+
129
+ $ smartmachine app create <APPNAME> <APPDOMAIN> <USERNAME>
130
+
131
+ ## Development
132
+
133
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
134
+
135
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
136
+
137
+ ## Contributing
138
+
139
+ Bug reports and pull requests are welcome on GitHub at https://github.com/plainsource/smartmachine. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/plainsource/smartmachine/blob/main/CODE_OF_CONDUCT.md).
140
+
141
+ ## License
142
+
143
+ The gem is available as open source under the terms of the [AGPL License](https://www.gnu.org/licenses/agpl-3.0.html).
144
+
145
+ ## Code of Conduct
146
+
147
+ Everyone interacting in the SmartMachine project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/plainsource/smartmachine/blob/main/CODE_OF_CONDUCT.md).
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "smart_machine"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/smartmachine ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'smart_machine/commands'
@@ -1,182 +1,166 @@
1
- # The main SmartMachine App driver
2
1
  module SmartMachine
3
- class Apps
4
- class App < SmartMachine::Base
5
- def initialize
6
- end
7
-
8
- # Creating App!
9
- #
10
- # Example:
11
- # >> SmartMachine::Apps::Rails.create
12
- # => Creation Complete
13
- #
14
- # Arguments:
15
- # appname => (String)
16
- # username => (String)
17
- def self.create(*args)
18
- args.flatten!
19
- appname = args.empty? ? '' : args.shift
20
- username = args.empty? ? '' : args.shift
21
-
22
- raise "Please provide appname and username" if appname.empty? || username.empty?
23
-
24
- if SmartMachine::Docker.running?
25
- repository_path = "#{SmartMachine.config.user_home_path}/.smartmachine/apps/repositories/#{appname}.git"
26
- container_path = "#{SmartMachine.config.user_home_path}/.smartmachine/apps/containers/#{appname}"
27
- print "-----> Creating Application ... "
28
-
29
- # Checking if app with given name already exists
30
- if Dir.exist?(repository_path)
31
- puts "failed. App with name '#{appname}' already exists."
32
- exit
33
- end
34
-
35
- # Creating Directories
36
- FileUtils.mkdir_p(repository_path)
37
- FileUtils.mkdir_p(container_path)
38
-
39
- # Initializing bare repo and pre-receive
40
- Dir.chdir(repository_path) do
41
- %x[git init --bare]
42
- %x[chmod +x #{SmartMachine.config.user_home_path}/.smartmachine/grids/prereceiver/pre-receive]
43
- %x[ln -s #{SmartMachine.config.user_home_path}/.smartmachine/grids/prereceiver/pre-receive #{repository_path}/hooks/pre-receive]
44
- puts "done"
45
- end
46
-
47
- # Creating Environment File
48
- if File.exist?("#{SmartMachine.config.user_home_path}/.smartmachine/config/environment.rb")
49
- require "#{SmartMachine.config.user_home_path}/.smartmachine/config/environment"
50
- end
51
- unless File.exist? "#{container_path}/env"
52
- print "-----> Creating App Environment ... "
53
- page = <<~HEREDOC
54
- ## System
55
- USERNAME=#{username}
56
- KEEP_RELEASES=3
57
-
58
- ## Docker
59
- VIRTUAL_HOST=#{appname}.#{SmartMachine.config.apps_domain}
60
- LETSENCRYPT_HOST=#{appname}.#{SmartMachine.config.apps_domain}
61
- LETSENCRYPT_EMAIL=#{SmartMachine.config.sysadmin_email}
62
- LETSENCRYPT_TEST=false
63
- HEREDOC
64
- puts "done" if system("echo '#{page}' > #{container_path}/env")
65
- end
66
- end
67
- end
68
-
69
- def self.destroy(*args)
70
- args.flatten!
71
- appname = args.empty? ? '' : args.shift
72
-
73
- raise "Please provide appname" if appname.empty?
74
-
75
- if SmartMachine::Docker.running?
76
- # Stopping & Removing old container
77
- self.stop(appname)
78
-
79
- # Destroying Directories
80
- print "-----> Deleting App #{appname} ... "
81
- repository_path = "#{SmartMachine.config.user_home_path}/.smartmachine/apps/repositories/#{appname}.git"
82
- container_path = "#{SmartMachine.config.user_home_path}/.smartmachine/apps/containers/#{appname}"
83
- FileUtils.rm_r(repository_path)
84
- FileUtils.rm_r(container_path)
85
- puts "done"
86
- end
87
- end
88
-
89
- def self.start(*args)
90
- args.flatten!
91
- appname = args.empty? ? '' : args.shift
92
- app_version = args.empty? ? 0 : args.shift.to_i
93
-
94
- raise "Please provide appname" if appname.empty?
95
-
96
- logger.formatter = proc do |severity, datetime, progname, message|
97
- severity_text = { "DEBUG" => "\u{1f527} #{severity}:", "INFO" => " \u{276f}", "WARN" => "\u{2757} #{severity}:",
98
- "ERROR" => "\u{274c} #{severity}:", "FATAL" => "\u{2b55} #{severity}:", "UNKNOWN" => "\u{2753} #{severity}:"
99
- }
100
- "\t\t\t\t#{severity_text[severity]} #{message}\n"
101
- end
102
-
103
- if SmartMachine::Docker.running?
104
- container_path = "#{SmartMachine.config.user_home_path}/.smartmachine/apps/containers/#{appname}"
105
-
106
- Dir.chdir("#{container_path}/releases") do
107
- # Getting App Version
108
- if app_version == 0
109
- app_versions = Dir.glob('*').select { |f| File.directory? f }.sort
110
- app_version = app_versions.last
111
- end
112
- container_path_with_version = "#{container_path}/releases/#{app_version}"
113
-
114
- logger.info "Launching Application ..."
115
-
116
- app = SmartMachine::Apps::Rails.new
117
- app.start(appname, container_path, container_path_with_version)
118
- end
119
- end
120
-
121
- logger.formatter = nil
122
- end
123
-
124
- def self.stop(*args)
125
- args.flatten!
126
- appname = args.empty? ? '' : args.shift
127
-
128
- raise "Please provide appname" if appname.empty?
129
-
130
- container_name = appname
131
-
132
- if SmartMachine::Docker.running?
133
- container_id = `docker ps -a -q --filter='name=^#{container_name}$'`.chomp
134
- unless container_id.empty?
135
- logger.debug "Stopping & Removing container #{container_name} ..."
136
- if system("docker stop #{container_name} && docker rm #{container_name}", out: File::NULL)
137
- logger.debug "Stopped & Removed container #{container_name} ..."
138
- end
139
- else
140
- logger.debug "Container '#{container_name}' does not exist to stop."
141
- end
142
- end
143
- end
144
-
145
- def self.clean_up(container_path)
146
- env_vars = SmartMachine::Apps::App.get_env_vars(container_path)
147
- return unless env_vars
148
-
149
- logger.info "Cleaning up ..."
150
-
151
- # Clean up very old versions
152
- Dir.chdir("#{container_path}/releases") do
153
- app_versions = Dir.glob('*').select { |f| File.directory? f }.sort
154
- destroy_count = app_versions.count - env_vars['KEEP_RELEASES'].to_i
155
- if destroy_count > 0
156
- logger.debug "Deleting older application releases ..."
157
- destroy_count.times do
158
- FileUtils.rm_r(File.join(Dir.pwd, app_versions.shift))
159
- end
160
- end
161
- end
162
- end
163
-
164
- def self.get_env_vars(container_path)
165
- unless File.exist? "#{container_path}/env"
166
- logger.fatal "Environment could not be loaded ... Failed."
167
- return false
168
- end
169
-
170
- env_vars = {}
171
- File.open("#{container_path}/env").each_line do |line|
172
- line.chomp!
173
- next if line.empty? || line.start_with?('#')
174
- key, value = line.split "="
175
- env_vars[key] = value
176
- end
177
-
178
- env_vars
179
- end
180
- end
181
- end
182
- end
2
+ module Apps
3
+ class App < SmartMachine::Base
4
+ def initialize(appname:, container_number: nil, username: nil)
5
+ @home_dir = File.expand_path('~')
6
+ @appname = appname
7
+ @container_number = container_number
8
+ @username = username
9
+
10
+ @repository_path = "#{@home_dir}/machine/apps/repositories/#{@appname}.git"
11
+ @container_path = "#{@home_dir}/machine/apps/containers/#{@appname}"
12
+ end
13
+
14
+ def creater(appdomain:, prereceiver_name:)
15
+ raise "Please provide appname and username" if @appname.empty? || @username.empty?
16
+ prereceiver_name = prereceiver_name.to_s
17
+
18
+ print "-----> Creating Application ... "
19
+
20
+ # Checking if app with given name already exists
21
+ if Dir.exist?(@repository_path)
22
+ puts "failed. App with name '#{@appname}' already exists."
23
+ exit
24
+ end
25
+
26
+ # Creating Directories
27
+ FileUtils.mkdir_p(@repository_path)
28
+ FileUtils.mkdir_p(@container_path)
29
+
30
+ # Initializing bare repo and pre-receive
31
+ Dir.chdir(@repository_path) do
32
+ %x[git init --bare]
33
+ %x[ln -s ../../../../grids/prereceiver/#{prereceiver_name}/pre-receive hooks/pre-receive]
34
+ puts "done"
35
+ end
36
+
37
+ # Creating Environment File
38
+ if File.exist?("#{@home_dir}/machine/config/environment.rb")
39
+ require "#{@home_dir}/machine/config/environment"
40
+ end
41
+ unless File.exist? "#{@container_path}/env"
42
+ print "-----> Creating App Environment ... "
43
+ page = <<~HEREDOC
44
+ ## System
45
+ USERNAME=#{@username}
46
+ KEEP_RELEASES=3
47
+
48
+ ## Docker
49
+ VIRTUAL_HOST=#{@appname}.#{appdomain}
50
+ LETSENCRYPT_HOST=#{@appname}.#{appdomain}
51
+ LETSENCRYPT_EMAIL=#{@username}
52
+ LETSENCRYPT_TEST=false
53
+ HEREDOC
54
+ puts "done" if system("echo '#{page}' > #{@container_path}/env")
55
+ end
56
+ end
57
+
58
+ def destroyer
59
+ raise "Please provide appname" if @appname.empty?
60
+
61
+ # Checking if app with given name exists
62
+ unless Dir.exist?(@repository_path)
63
+ raise "App with name '#{@appname}' does not exist. Please provide a valid appname."
64
+ end
65
+
66
+ container_id = `docker ps -a -q --filter='name=^#{@appname}$' --filter='status=running'`.chomp
67
+ if container_id.empty?
68
+ # Destroying Directories
69
+ print "-----> Deleting App #{@appname} ... "
70
+ FileUtils.rm_r(@repository_path)
71
+ FileUtils.rm_r(@container_path)
72
+ puts "done"
73
+ end
74
+ end
75
+
76
+ def uper(version:)
77
+ raise "Please provide appname" if @appname.empty?
78
+
79
+ # Checking if app with given name exists
80
+ unless Dir.exist?(@repository_path)
81
+ raise "App with name '#{@appname}' does not exist. Please provide a valid appname."
82
+ end
83
+
84
+ logger.formatter = proc do |severity, datetime, progname, message|
85
+ severity_text = { "DEBUG" => "\u{1f527} #{severity}:", "INFO" => " \u{276f}", "WARN" => "\u{2757} #{severity}:",
86
+ "ERROR" => "\u{274c} #{severity}:", "FATAL" => "\u{2b55} #{severity}:", "UNKNOWN" => "\u{2753} #{severity}:"
87
+ }
88
+ "\t\t\t\t#{severity_text[severity]} #{message}\n"
89
+ end
90
+
91
+ Dir.chdir("#{@container_path}/releases") do
92
+ # Getting App Version
93
+ if version == 0
94
+ versions = Dir.glob('*').select { |f| File.directory? f }.sort
95
+ version = versions.last
96
+ end
97
+
98
+ logger.info "Launching Application ..."
99
+
100
+ buildpacker_rails = SmartMachine::Buildpackers::Rails.new(appname: @appname, version: version)
101
+ buildpacker_rails.uper
102
+ end
103
+
104
+ logger.formatter = nil
105
+ end
106
+
107
+ def downer
108
+ raise "Please provide appname" if @appname.empty?
109
+
110
+ # Checking if app with given name exists
111
+ unless Dir.exist?(@repository_path)
112
+ raise "App with name '#{@appname}' does not exist. Please provide a valid appname."
113
+ end
114
+
115
+ container_name = @appname
116
+ container_name += "_" + @container_number if @container_number
117
+
118
+ container_id = `docker ps -a -q --filter='name=^#{container_name}$'`.chomp
119
+ unless container_id.empty?
120
+ logger.debug "Stopping & Removing container #{container_name} ..."
121
+ if system("docker stop #{container_name} && docker rm #{container_name}", out: File::NULL)
122
+ logger.debug "Stopped & Removed container #{container_name} ..."
123
+ end
124
+ else
125
+ logger.debug "Container '#{container_name}' does not exist to stop."
126
+ end
127
+ end
128
+
129
+ def cleaner
130
+ env_vars = get_env_vars
131
+ return unless env_vars
132
+
133
+ logger.info "Cleaning up ..."
134
+
135
+ # Clean up very old versions
136
+ Dir.chdir("#{@container_path}/releases") do
137
+ versions = Dir.glob('*').select { |f| File.directory? f }.sort
138
+ destroy_count = versions.count - env_vars['KEEP_RELEASES'].to_i
139
+ if destroy_count > 0
140
+ logger.debug "Deleting older application releases ..."
141
+ destroy_count.times do
142
+ FileUtils.rm_r(File.join(Dir.pwd, versions.shift))
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ def get_env_vars
149
+ unless File.exist? "#{@container_path}/env"
150
+ logger.fatal "Environment could not be loaded ... Failed."
151
+ return false
152
+ end
153
+
154
+ env_vars = {}
155
+ File.open("#{@container_path}/env").each_line do |line|
156
+ line.chomp!
157
+ next if line.empty? || line.start_with?('#')
158
+ key, value = line.split "="
159
+ env_vars[key] = value
160
+ end
161
+
162
+ env_vars
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,120 @@
1
+ module SmartMachine
2
+ module Apps
3
+ class Container < SmartMachine::Base
4
+ def initialize(name:, appname:, appversion:)
5
+ @name = name
6
+ @appname = appname
7
+ @appversion = appversion
8
+ @appimage = "smartmachine/apps/#{@appname}:#{@appversion}"
9
+
10
+ @home_dir = File.expand_path('~')
11
+ end
12
+
13
+ def create!(using_command:, using_buildpacker: false)
14
+ unless using_buildpacker
15
+ unless system("docker image inspect #{@appimage}", [:out, :err] => File::NULL)
16
+ raise "Error: App Image with name #{@appimage} not found. Could not create #{@name} container."
17
+ end
18
+ end
19
+
20
+ logger.debug "-----> Creating container #{@name} ... "
21
+ command = [
22
+ "docker create",
23
+ "--name='#{@name}'",
24
+ "--env-file='#{@home_dir}/machine/apps/containers/#{@appname}/env'",
25
+ "--user `id -u`:`id -g`",
26
+ "--workdir /app",
27
+ "--expose='3000'",
28
+ "--volume='#{@home_dir}/smartmachine/config/environment.rb:#{@home_dir}/machine/config/environment.rb'",
29
+ "--volume='#{@home_dir}/smartmachine/apps/containers/#{@appname}/releases/#{@appversion}:/app'",
30
+ "--volume='#{@home_dir}/smartmachine/apps/containers/#{@appname}/app/vendor/bundle:/app/vendor/bundle'",
31
+ "--volume='#{@home_dir}/smartmachine/apps/containers/#{@appname}/app/public/assets:/app/public/assets'",
32
+ "--volume='#{@home_dir}/smartmachine/apps/containers/#{@appname}/app/public/packs:/app/public/packs'",
33
+ "--volume='#{@home_dir}/smartmachine/apps/containers/#{@appname}/app/node_modules:/app/node_modules'",
34
+ "--volume='#{@home_dir}/smartmachine/apps/containers/#{@appname}/app/storage:/app/storage'",
35
+ "--restart='always'",
36
+ "--init",
37
+ # "--network='nginx-network'",
38
+ "#{using_buildpacker ? "smartmachine/buildpackers/rails:#{SmartMachine.version}" : @appimage}"
39
+ ]
40
+ command.push(using_command) if using_command.present?
41
+
42
+ if system(command.compact.join(" "), out: File::NULL)
43
+ logger.debug "done"
44
+ return true
45
+ else
46
+ raise "Error: Could not create #{@name} container"
47
+ end
48
+
49
+ return false
50
+ end
51
+
52
+ def start!
53
+ logger.debug "-----> Starting container #{@name} ... "
54
+ if system("docker start #{@name}", out: File::NULL)
55
+ logger.debug "done"
56
+ return true
57
+ else
58
+ raise "Error: Could not start #{@name} container"
59
+ end
60
+
61
+ return false
62
+ end
63
+
64
+ def stop!
65
+ container_id = `docker ps -a -q --filter='name=^#{@name}$'`.chomp
66
+ unless container_id.empty?
67
+ logger.debug "Stopping & Removing container #{@name} ..."
68
+ if system("docker stop #{@name} && docker rm #{@name}", out: File::NULL)
69
+ logger.debug "Stopped & Removed container #{@name} ..."
70
+ return true
71
+ else
72
+ raise "Error: Container '#{@name}' could not be stopped and removed."
73
+ end
74
+ else
75
+ raise "Error: Container '#{@name}' does not exist to stop."
76
+ end
77
+
78
+ return false
79
+ end
80
+
81
+ def connect_to_network!(network_name:)
82
+ unless `docker network ls -q --filter name=^#{network_name}$`.chomp.empty?
83
+ if system("docker network connect #{network_name} #{@name}", out: File::NULL)
84
+ logger.debug "Connected to network #{network_name}."
85
+ return true
86
+ else
87
+ raise "Error: Could not connect to #{network_name} network."
88
+ end
89
+ else
90
+ raise "Error: The network with name #{network_name} was not found."
91
+ end
92
+
93
+ return false
94
+ end
95
+
96
+ def commit_app_image!
97
+ if create!(using_command: "smartmachine buildpacker packer rails", using_buildpacker: true)
98
+ logger.debug "-----> Starting attached container #{@name} ... "
99
+ if system("docker start --attach #{@name}")
100
+ logger.debug "-----> Committing container #{@name} to image... "
101
+ if system("docker commit #{@name} #{@appimage}", out: File::NULL)
102
+ stop!
103
+ return true
104
+ else
105
+ stop!
106
+ raise "Error: Could not commit #{@name} container to image."
107
+ end
108
+ else
109
+ stop!
110
+ raise "Error: Could not start attached #{@name} container"
111
+ end
112
+ else
113
+ raise "Error: Could not create #{@name} container"
114
+ end
115
+
116
+ return false
117
+ end
118
+ end
119
+ end
120
+ end