smartmachine 0.8.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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