puppetfactory 0.4.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 (95) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +13 -0
  3. data/README.md +0 -0
  4. data/bin/pfsh +31 -0
  5. data/bin/puppetfactory +153 -0
  6. data/lib/puppetfactory.rb +300 -0
  7. data/lib/puppetfactory/cli.rb +114 -0
  8. data/lib/puppetfactory/dashboard/rake_tasks.rb +69 -0
  9. data/lib/puppetfactory/dashboard/serverspec_helper.rb +84 -0
  10. data/lib/puppetfactory/dashboard/spec_helper.rb +26 -0
  11. data/lib/puppetfactory/helpers.rb +37 -0
  12. data/lib/puppetfactory/monkeypatches.rb +30 -0
  13. data/lib/puppetfactory/plugins.rb +11 -0
  14. data/lib/puppetfactory/plugins/certificates.rb +28 -0
  15. data/lib/puppetfactory/plugins/classification.rb +75 -0
  16. data/lib/puppetfactory/plugins/code_manager.rb +156 -0
  17. data/lib/puppetfactory/plugins/console_user.rb +62 -0
  18. data/lib/puppetfactory/plugins/dashboard.rb +128 -0
  19. data/lib/puppetfactory/plugins/docker.rb +193 -0
  20. data/lib/puppetfactory/plugins/example.rb +88 -0
  21. data/lib/puppetfactory/plugins/github.rb +102 -0
  22. data/lib/puppetfactory/plugins/gitlab.rb +62 -0
  23. data/lib/puppetfactory/plugins/hooks.rb +46 -0
  24. data/lib/puppetfactory/plugins/login_shell.rb +10 -0
  25. data/lib/puppetfactory/plugins/logs.rb +34 -0
  26. data/lib/puppetfactory/plugins/r10k.rb +112 -0
  27. data/lib/puppetfactory/plugins/shell_user.rb +69 -0
  28. data/lib/puppetfactory/plugins/user_environment.rb +77 -0
  29. data/public/dashboard.js +100 -0
  30. data/public/font-awesome/css/font-awesome.css +2199 -0
  31. data/public/font-awesome/css/font-awesome.min.css +4 -0
  32. data/public/font-awesome/fonts/FontAwesome.otf +0 -0
  33. data/public/font-awesome/fonts/fontawesome-webfont.eot +0 -0
  34. data/public/font-awesome/fonts/fontawesome-webfont.svg +685 -0
  35. data/public/font-awesome/fonts/fontawesome-webfont.ttf +0 -0
  36. data/public/font-awesome/fonts/fontawesome-webfont.woff +0 -0
  37. data/public/font-awesome/fonts/fontawesome-webfont.woff2 +0 -0
  38. data/public/gitviz/LICENSE.md +20 -0
  39. data/public/gitviz/README.md +13 -0
  40. data/public/gitviz/css/explaingit.css +227 -0
  41. data/public/gitviz/css/vendor/1140.css +130 -0
  42. data/public/gitviz/images/forkme_right_red_aa0000.png +0 -0
  43. data/public/gitviz/images/grippy-close.png +0 -0
  44. data/public/gitviz/images/grippy.png +0 -0
  45. data/public/gitviz/images/prompt.gif +0 -0
  46. data/public/gitviz/index.html +734 -0
  47. data/public/gitviz/js/controlbox.js +459 -0
  48. data/public/gitviz/js/explaingit.js +74 -0
  49. data/public/gitviz/js/historyview.js +979 -0
  50. data/public/gitviz/js/main.js +56 -0
  51. data/public/gitviz/js/vendor/d3.min.js +4 -0
  52. data/public/gitviz/js/vendor/jquery-latest.min.js +6 -0
  53. data/public/gitviz/js/vendor/normalize.css +396 -0
  54. data/public/gitviz/js/vendor/require.min.js +35 -0
  55. data/public/gitviz/memtest.html +44 -0
  56. data/public/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  57. data/public/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  58. data/public/images/ui-bg_flat_10_000000_40x100.png +0 -0
  59. data/public/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  60. data/public/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  61. data/public/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  62. data/public/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  63. data/public/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  64. data/public/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  65. data/public/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  66. data/public/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  67. data/public/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  68. data/public/images/ui-icons_222222_256x240.png +0 -0
  69. data/public/images/ui-icons_228ef1_256x240.png +0 -0
  70. data/public/images/ui-icons_454545_256x240.png +0 -0
  71. data/public/images/ui-icons_ef8c08_256x240.png +0 -0
  72. data/public/images/ui-icons_ffd27a_256x240.png +0 -0
  73. data/public/images/ui-icons_ffffff_256x240.png +0 -0
  74. data/public/jquery-1.11.1.min.js +4 -0
  75. data/public/jquery-ui.css +464 -0
  76. data/public/jquery-ui.min.css +7 -0
  77. data/public/jquery-ui.min.js +13 -0
  78. data/public/jquery-ui.structure.min.css +5 -0
  79. data/public/jquery-ui.theme.min.css +5 -0
  80. data/public/jquery.activity-indicator-1.0.0.min.js +10 -0
  81. data/public/jquery.js +9789 -0
  82. data/public/loginscripts.js +18 -0
  83. data/public/scripts.js +36 -0
  84. data/public/style.css +193 -0
  85. data/public/usermanagement.js +133 -0
  86. data/templates/init_scripts.erb +10 -0
  87. data/templates/puppet.conf.erb +10 -0
  88. data/templates/site.pp.erb +50 -0
  89. data/views/dashboard.erb +62 -0
  90. data/views/home.erb +43 -0
  91. data/views/index.erb +29 -0
  92. data/views/logs.erb +26 -0
  93. data/views/shell.erb +35 -0
  94. data/views/users.erb +69 -0
  95. metadata +256 -0
@@ -0,0 +1,156 @@
1
+ require 'puppetfactory'
2
+ require 'hocon'
3
+ require 'hocon/parser/config_document_factory'
4
+ require 'hocon/config_value_factory'
5
+
6
+ class Puppetfactory::Plugins::CodeManager < Puppetfactory::Plugins
7
+
8
+ def initialize(options)
9
+ super(options)
10
+
11
+ @puppet = options[:puppet]
12
+ @gitserver = options[:gitserver]
13
+ @repomodel = options[:repomodel]
14
+ @controlrepo = options[:controlrepo]
15
+ @environments = options[:environments]
16
+ @sources = '/etc/puppetlabs/puppet/hieradata/sources.yaml' # we can hardcode these assumptions
17
+ @meep = '/etc/puppetlabs/enterprise/conf.d/common.conf' # because CM is PE only.
18
+ @pattern = "#{@gitserver}/%s/#{@controlrepo}"
19
+
20
+ # the rest of this method is for the big boys only
21
+ return unless Process.euid == 0
22
+
23
+ # in case this is the first run and these doesn't exist yet
24
+ unless File.exist? @sources
25
+ FileUtils.mkdir_p File.dirname @sources
26
+ File.open(@sources, 'w') { |f| f.write( { 'puppet_enterprise::master::code_manager::sources' => {} }.to_yaml) }
27
+ end
28
+ File.open(@meep, 'w') { |f| f.write('') } unless File.exist? @meep
29
+
30
+ if options[:codedir]
31
+ # ensure sane file permissions
32
+ FileUtils.chown_R('pe-puppet', 'pe-puppet', options[:codedir])
33
+ FileUtils.chmod(0755, options[:codedir])
34
+ end
35
+ end
36
+
37
+ def create(username, password)
38
+ begin
39
+ environment = "#{@environments}/#{Puppetfactory::Helpers.environment_name(username)}"
40
+ FileUtils.mkdir_p environment
41
+ FileUtils.chown_R(username, 'pe-puppet', environment)
42
+ FileUtils.chmod(0750, environment)
43
+
44
+ File.open(@sources) do |file|
45
+ # make sure we don't have any concurrency issues
46
+ file.flock(File::LOCK_EX)
47
+
48
+ # We need to duplicate the sources list in the MEEP config for recovery options
49
+ # I'd like to add it to code-manager.conf too and avoid the delay of running
50
+ # puppet, but that's a race condition that we cannot accept.
51
+ File.open(@meep) do |anotherfile|
52
+ anotherfile.flock(File::LOCK_EX)
53
+
54
+ source = {
55
+ 'remote' => sprintf(@pattern, username),
56
+ 'prefix' => (@repomodel == :peruser),
57
+ }
58
+
59
+ sources = YAML.load_file(@sources)
60
+ meep = Hocon::Parser::ConfigDocumentFactory.parse_file(@meep)
61
+
62
+ sources['puppet_enterprise::master::code_manager::sources'][username] = source
63
+ meep = meep.set_config_value(
64
+ "\"puppet_enterprise::master::code_manager::sources\".#{username}",
65
+ Hocon::ConfigValueFactory.from_any_ref(source)
66
+ )
67
+
68
+ # Ruby 1.8.7, why don't you just go away now
69
+ File.open(@sources, 'w') { |f| f.write(sources.to_yaml) }
70
+ File.open(@meep, 'w') { |f| f.write(meep.render) }
71
+ $logger.info "Created Code Manager source for #{username}"
72
+ end
73
+ end
74
+ rescue => e
75
+ $logger.error "Cannot create Code Manager source for #{username}"
76
+ $logger.error e.backtrace
77
+ return false
78
+ end
79
+
80
+ true
81
+ end
82
+
83
+ def delete(username)
84
+ begin
85
+ environment = "#{@environments}/#{Puppetfactory::Helpers.environment_name(username)}"
86
+ FileUtils.rm_rf environment
87
+
88
+ # also delete any prefixed environments. Is this even a good idea?
89
+ FileUtils.rm_rf "#{@environments}/#{username}_*" if @repomodel == :peruser
90
+
91
+ File.open(@sources) do |file|
92
+ # make sure we don't have any concurrency issues
93
+ file.flock(File::LOCK_EX)
94
+
95
+ # We need to duplicate the sources list in the MEEP config for recovery options
96
+ # I'd like to add it to code-manager.conf too and avoid the delay of running
97
+ # puppet, but that's a race condition that we cannot accept.
98
+ File.open(@meep) do |anotherfile|
99
+ anotherfile.flock(File::LOCK_EX)
100
+
101
+ source = {
102
+ 'remote' => sprintf(@pattern, username),
103
+ 'prefix' => (@repomodel == :peruser),
104
+ }
105
+
106
+ sources = YAML.load_file(@sources)
107
+ sources['puppet_enterprise::master::code_manager::sources'].delete username rescue nil
108
+
109
+ meep = Hocon::Parser::ConfigDocumentFactory.parse_file(@meep)
110
+ meep = meep.remove_value("\"puppet_enterprise::master::code_manager::sources\".#{username}")
111
+
112
+ # Ruby 1.8.7, why don't you just go away now
113
+ File.open(@sources, 'w') { |f| f.write(sources.to_yaml) }
114
+ File.open(@meep, 'w') { |f| f.write(meep.render) }
115
+ $logger.info "Removed Code Manager source for #{username}"
116
+ end
117
+ end
118
+ rescue => e
119
+ $logger.error "Cannot remove Code Manager source for #{username}"
120
+ $logger.error e.backtrace
121
+ return false
122
+ end
123
+
124
+ true
125
+ end
126
+
127
+ def deploy(username)
128
+ environment = Puppetfactory::Helpers.environment_name(username)
129
+
130
+ output, status = Open3.capture2e(@puppet, 'code', 'deploy', environment, '--wait')
131
+ unless status.success?
132
+ $logger.error "Failed to deploy environment #{environment} for #{username}"
133
+ $logger.error output
134
+ return false
135
+ end
136
+
137
+ $logger.info "Deployed environment #{environment} for #{username}"
138
+ true
139
+ end
140
+
141
+ def redeploy(username)
142
+ begin
143
+ if username == 'production'
144
+ raise "Can't redeploy production environment"
145
+ end
146
+ delete(username)
147
+ deploy(username)
148
+
149
+ rescue => e
150
+ raise "Error redeploying environment #{username}: #{e.message}"
151
+ end
152
+
153
+ true
154
+ end
155
+
156
+ end
@@ -0,0 +1,62 @@
1
+ require 'json'
2
+ require 'puppetfactory'
3
+
4
+ class Puppetfactory::Plugins::ConsoleUser < Puppetfactory::Plugins
5
+
6
+ def initialize(options)
7
+ super(options)
8
+
9
+ @puppet = options[:puppet]
10
+ @suffix = options[:usersuffix]
11
+ auth_info = options[:auth_info] || {}
12
+
13
+ @ca_certificate_path = auth_info[:ca_certificate_path] || "#{options[:confdir]}/ssl/ca/ca_crt.pem",
14
+ @certificate_path = auth_info[:certificate_path] || "#{options[:confdir]}/ssl/certs/#{options[:master]}.pem",
15
+ @private_key_path = auth_info[:private_key_path] || "#{options[:confdir]}/ssl/private_keys/#{options[:master]}.pem"
16
+ @classifier_url = options[:classifier] || "http://#{options[:master]}:4433/classifier-api"
17
+ end
18
+
19
+ def create(username, password)
20
+ output, status = Open3.capture2e(@puppet, 'resource', 'rbac_user', username,
21
+ 'ensure=present',
22
+ "display_name=#{username}",
23
+ 'roles=Operators',
24
+ "email=#{username}@#{@suffix}",
25
+ "password=#{password}")
26
+
27
+ unless status.success
28
+ $logger.error "Could not create PE Console user #{username}: #{output}"
29
+ return false
30
+ end
31
+
32
+ $logger.info "Console user #{username} created successfully"
33
+ true
34
+ end
35
+
36
+ def delete(username)
37
+ output, status = Open3.capture2e(@puppet, 'resource', 'rbac_user', username, 'ensure=absent')
38
+ unless status.success?
39
+ $logger.warn "Could not remove PE Console user #{username}: #{output}"
40
+ return false
41
+ end
42
+
43
+ $logger.info "Console user #{username} removed successfully"
44
+ true
45
+ end
46
+
47
+ def userinfo(username, extended = false)
48
+ return unless extended
49
+
50
+ output, status = Open3.capture2e(PUPPET, 'resource', 'rbac_user', username)
51
+ unless status.success?
52
+ $logger.error "Could not query Puppet user #{username}: #{output}"
53
+ return false
54
+ end
55
+
56
+ {
57
+ :username => username,
58
+ :console_user => output =~ /present/,
59
+ }
60
+ end
61
+
62
+ end
@@ -0,0 +1,128 @@
1
+ require 'puppetfactory'
2
+
3
+ class Puppetfactory::Plugins::Dashboard < Puppetfactory::Plugins
4
+ attr_accessor :current_test
5
+
6
+ def initialize(options)
7
+ super(options)
8
+ return unless options[:puppetfactory]
9
+
10
+ @server = options[:puppetfactory]
11
+ @path = options[:dashboard_path] || '/etc/puppetfactory/dashboard'
12
+ @interval = options[:dashboard_interval] || 5 * 60 # test interval in seconds
13
+
14
+ # TODO: finish a real mutex implementation and avoid the current (small) race condition
15
+ #set :semaphore, Mutex.new
16
+ @current_test = 'summary'
17
+ @test_running = false
18
+
19
+ start_testing()
20
+
21
+ @server.get '/dashboard' do
22
+ protected!
23
+
24
+ # we can't call methods directly, because this block will execute in the scope
25
+ # of the Puppetfactory server. Use the plugin system instead.
26
+ @current = plugin(:Dashboard, :current_test)
27
+ @available = plugin(:Dashboard, :available_tests)
28
+ @test_data = plugin(:Dashboard, :test_data)
29
+
30
+ return 'No testing data' unless @available and @test_data
31
+
32
+ erb :dashboard
33
+ end
34
+
35
+ @server.get '/dashboard/details/:user' do |user|
36
+ plugin(:Dashboard, :user_test_html, user)
37
+ end
38
+
39
+ @server.get '/dashboard/details/:user/:result' do |user, result|
40
+ plugin(:Dashboard, :user_test_html, user, result)
41
+ end
42
+
43
+ @server.get '/dashboard/update' do
44
+ $logger.info "Triggering dashboard update."
45
+
46
+ if plugin(:Dashboard, :update_results)
47
+ {'status' => 'success'}.to_json
48
+ else
49
+ {'status' => 'fail', 'message' => 'Already running'}.to_json
50
+ end
51
+ end
52
+
53
+ @server.get '/dashboard/set/:current' do |current|
54
+ $logger.info "Setting current test to #{current}."
55
+
56
+ plugin(:Dashboard, :current_test=, current)
57
+
58
+ {'status' => 'success'}.to_json
59
+ end
60
+
61
+ end
62
+
63
+ def tabs(privileged = false)
64
+ return unless privileged
65
+
66
+ { 'dashboard' => 'Testing Dashboard' }
67
+ end
68
+
69
+ def update_results()
70
+ return false if @test_running
71
+ @test_running = true
72
+
73
+ Dir.chdir(@path) do
74
+ case @current_test
75
+ when 'all', 'summary'
76
+ system('rake', 'generate')
77
+ else
78
+ system('rake', 'generate', "current_test=#{@current_test}")
79
+ end
80
+ end
81
+
82
+ @test_running = false
83
+ true
84
+ end
85
+
86
+ def available_tests()
87
+ Dir.chdir(@path) { `rake list`.split } rescue []
88
+ end
89
+
90
+ def test_data()
91
+ JSON.parse(File.read("#{@path}/output/summary.json")) rescue {}
92
+ end
93
+
94
+ def user_test_html(user, result = @current_test)
95
+ begin
96
+ if result == 'summary'
97
+ File.read("#{@path}/output/html/#{user}.html")
98
+ else
99
+ File.read("#{@path}/output/html/#{result}/#{user}.html")
100
+ end
101
+ rescue Errno::ENOENT
102
+ 'No results found'
103
+ end
104
+ end
105
+
106
+ # class method so the template can call it
107
+ def self.test_completion(data)
108
+ total = data['example_count'] rescue 0
109
+ failed = data['failure_count'] rescue 0
110
+ passed = total - failed
111
+ percent = passed.to_f / total * 100.0 rescue 0
112
+
113
+ [total, passed, percent]
114
+ end
115
+
116
+ private
117
+ def start_testing()
118
+ Thread.new do
119
+ loop do
120
+ $logger.info "Updating dashboard after #{@interval} seconds."
121
+ update_results()
122
+ sleep(@interval)
123
+ end
124
+ end
125
+ end
126
+
127
+
128
+ end
@@ -0,0 +1,193 @@
1
+ require 'erb'
2
+ require 'time'
3
+ require 'docker'
4
+ require 'etc'
5
+ require 'json'
6
+ require 'puppetfactory'
7
+
8
+ class Puppetfactory::Plugins::Docker < Puppetfactory::Plugins
9
+
10
+ def initialize(options)
11
+ super(options)
12
+
13
+ @weight = 5
14
+
15
+ @confdir = options[:confdir]
16
+ @environments = options[:environments]
17
+ @puppetcode = options[:puppetcode]
18
+ @master = options[:master]
19
+ @modulepath = options[:modulepath]
20
+ @templatedir = options[:templatedir]
21
+ @container = options[:container_name] || 'centosagent'
22
+ @group = options[:docker_group] || 'docker'
23
+ @docker_ip = options[:docker_ip] || `facter ipaddress_docker0`.strip
24
+ @privileged = options[:privileged] || true
25
+ end
26
+
27
+ def create(username, password)
28
+ begin
29
+ environment = "#{@environments}/#{Puppetfactory::Helpers.environment_name(username)}"
30
+
31
+ binds = [
32
+ "/var/yum:/var/yum",
33
+ "/home/#{username}/puppet:#{@confdir}",
34
+ "/sys/fs/cgroup:/sys/fs/cgroup:ro"
35
+ ]
36
+
37
+ case @modulepath
38
+ when :readonly
39
+ binds.push("#{environment}:#{@puppetcode}:ro")
40
+ when :readwrite
41
+ binds.push("#{environment}:#{@puppetcode}")
42
+ when :none
43
+ #pass
44
+ else
45
+ raise "Uknown modulepath setting (#{@modulepath})"
46
+ end
47
+
48
+ container = ::Docker::Container.create(
49
+ "Cmd" => [
50
+ "/usr/lib/systemd/systemd"
51
+ ],
52
+ "Tty" => true,
53
+ "Domainname" => "puppetlabs.vm",
54
+ "Env" => [
55
+ "RUNLEVEL=3",
56
+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
57
+ "HOME=/root/",
58
+ "TERM=xterm"
59
+ ],
60
+ "ExposedPorts" => {
61
+ "80/tcp" => {
62
+ }
63
+ },
64
+ "Hostname" => "#{username}",
65
+ "Image" => "#{@container}",
66
+ "HostConfig" => {
67
+ "Privileged" => @privileged,
68
+ "Binds" => binds,
69
+ "ExtraHosts" => [
70
+ "#{@master} puppet:#{@docker_ip}"
71
+ ],
72
+ "PortBindings" => {
73
+ "80/tcp" => [
74
+ {
75
+ "HostPort" => "#{user_port(username)}"
76
+ }
77
+ ]
78
+ },
79
+ },
80
+ "Name" => "#{username}"
81
+ )
82
+
83
+ container.rename(username) # Set container name so we can identify it
84
+ init_scripts(username) # Create init scripts so container restarts on boot
85
+ container.start
86
+
87
+ rescue => e
88
+ # fatal error, so we stop execution here
89
+ raise "Error creating container #{username}: #{e.message}"
90
+ end
91
+
92
+ $logger.info "Container #{username} created"
93
+ true
94
+ end
95
+
96
+ def delete(username)
97
+ begin
98
+ remove_init_scripts(username)
99
+
100
+ container = ::Docker::Container.get(username)
101
+ output = container.delete(:force => true)
102
+ rescue => e
103
+ $logger.warn "Error removing container #{username}: #{e.message}"
104
+ return false
105
+ end
106
+
107
+ $logger.info "Container #{username} removed"
108
+ true
109
+ end
110
+
111
+ def repair(username)
112
+ begin
113
+ container = ::Docker::Container.get(username)
114
+ container.delete(:force => true)
115
+
116
+ create(username, nil)
117
+ rescue => e
118
+ raise "Error reparing container #{username}: #{e.message}"
119
+ end
120
+
121
+ $logger.info "Container #{username} repaired"
122
+ true
123
+ end
124
+
125
+ def userinfo(username, extended = false)
126
+ user = {
127
+ :username => username,
128
+ :port => user_port(username),
129
+ :url => sandbox_url(username),
130
+ }
131
+
132
+ if extended
133
+ user_container = ::Docker::Container.get(username).json rescue {}
134
+ user[:container_status] = massage_container_state(user_container['State'])
135
+ end
136
+
137
+ user
138
+ end
139
+
140
+ def login
141
+ require 'etc'
142
+ username = Etc.getpwuid(Process.euid).name
143
+ exec("docker exec -it #{username} su -")
144
+ end
145
+
146
+ private
147
+ def sandbox_url(username)
148
+ "/port/#{user_port(username)}"
149
+ end
150
+
151
+ # TODO: We need a better way of doing this, since we're not guaranteed to always have system users.
152
+ # See plugins/shell_user.rb for the other side of this coupling.
153
+ def user_port(username)
154
+ Etc.getpwnam(username).uid + 3000
155
+ end
156
+
157
+ def massage_container_state(state)
158
+ return {'Description' => 'No container.'} if state.nil?
159
+
160
+ if state['OOMKilled']
161
+ state['Description'] = 'Halted because host machine is out of memory.'
162
+ elsif state['Restarting']
163
+ state['Description'] = 'Container is restarting.'
164
+ elsif state['Paused']
165
+ state['Description'] = 'Container is paused.'
166
+ elsif state['Running']
167
+ state['Description'] = "Running since #{Time.parse(state['StartedAt']).strftime("%b %d at %I:%M%p")}"
168
+ else
169
+ state['Description'] = 'Halted.'
170
+ end
171
+ state
172
+ end
173
+
174
+ def init_scripts(username)
175
+ service_file = "/etc/systemd/system/docker-#{username}.service"
176
+ File.open(service_file,"w") do |f|
177
+ f.write ERB.new(File.read("#{@templatedir}/init_scripts.erb")).result(binding)
178
+ end
179
+ File.chmod(0644, service_file)
180
+ system('chkconfig', "docker-#{username}", 'on')
181
+
182
+ $logger.info "Init scripts created and enabled for container #{username}"
183
+ end
184
+
185
+ def remove_init_scripts(username)
186
+ service_file = "/etc/systemd/system/docker-#{username}.service"
187
+ system('chkconfig', "docker-#{username}", 'off')
188
+ FileUtils.rm(service_file) if File.exist? service_file
189
+
190
+ $logger.info "Init scripts for container #{username} removed"
191
+ end
192
+
193
+ end