puppetfactory 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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,88 @@
1
+ require 'puppetfactory'
2
+
3
+ # inherit from Puppetfactory::Plugins
4
+ class Puppetfactory::Plugins::Example < Puppetfactory::Plugins
5
+ attr_reader :weight
6
+
7
+ def initialize(options)
8
+ super(options) # call the superclass to initialize it
9
+
10
+ @weight = 1
11
+ @example = options[:example] || '/tmp/example'
12
+ end
13
+
14
+ # include one or more of the following methods. Any method you implement
15
+ # will be called when the corresponding task is invoked.
16
+
17
+ def create(username, password)
18
+ $logger.info "User #{username} created."
19
+
20
+ # Log an error with $logger.error
21
+ # fail user creation with a fatal error by raising an exception
22
+
23
+ # return true if our action succeeded
24
+ true
25
+ end
26
+
27
+ def delete(username)
28
+ $logger.info "User #{username} deleted."
29
+
30
+ # return true if our action succeeded
31
+ true
32
+ end
33
+
34
+ def userinfo(username, extended = false)
35
+ # we can bail if we don't want to add to the basic user object.
36
+ # for example, if these are heavy operations.
37
+ return unless extended
38
+
39
+ # return a hash with the :username key
40
+ {
41
+ :username => username,
42
+ :example => username.upcase,
43
+ }
44
+ end
45
+
46
+ def deploy(username)
47
+ environment = Puppetfactory::Helpers.environment_name(username)
48
+ $logger.info "Deployed environment #{environment} for #{username}"
49
+
50
+ # return true if our action succeeded
51
+ true
52
+ end
53
+
54
+ def redeploy(username)
55
+ begin
56
+ if username == 'production'
57
+ raise "Can't redeploy production environment"
58
+ end
59
+ delete(username)
60
+ deploy(username)
61
+
62
+ rescue => e
63
+ raise "Error redeploying environment #{username}: #{e.message}"
64
+ end
65
+
66
+ # return true if our action succeeded
67
+ true
68
+ end
69
+
70
+ # used by container plugins to rebuild them
71
+ def repair(username)
72
+ $logger.info "Container #{username} repaired"
73
+ true
74
+ end
75
+
76
+ # hook called when users log in. Only one can be active at any time.
77
+ def login
78
+ $logger.info 'Logging in with the default system shell'
79
+ exec('bash --login')
80
+ end
81
+
82
+ # returns an array of all user accounts. Only one can be active at any time.
83
+ def users
84
+ usernames = Dir.glob('/home/*').map { |path| File.basename path }
85
+ usernames.reject { |username| ['centos', 'training', 'showoff'].include? username }
86
+ end
87
+
88
+ end
@@ -0,0 +1,102 @@
1
+ require 'octokit'
2
+ require 'puppetfactory'
3
+
4
+ class Puppetfactory::Plugins::Github < Puppetfactory::Plugins
5
+
6
+ def initialize(options)
7
+ super(options)
8
+
9
+ @gitserver = options[:gitserver]
10
+ @gituser = options[:gituser]
11
+ @controlrepo = options[:controlrepo]
12
+ @repomodel = options[:repomodel]
13
+ @githubtoken = options[:githubtoken]
14
+
15
+ # chomp so we can support repo names with or without the .git
16
+ @controlrepo.chomp!('.git')
17
+
18
+ if @githubtoken
19
+ @client = Octokit::Client.new(:access_token => @githubtoken)
20
+ @client.user.login
21
+ else
22
+ @client = Octokit::Client.new()
23
+ end
24
+ end
25
+
26
+ def create(username, password)
27
+ # can only do anything on our own repo, and only if we're authorized!
28
+ return true unless @githubtoken and @repomodel == :single
29
+
30
+ begin
31
+ # can only do anything on our own repo!
32
+ repo = "#{@gituser}/#{@controlrepo}"
33
+ sha = @client.branches(repo).select { |branch| branch[:name] == 'production' }.first[:commit][:sha]
34
+ @client.create_ref(repo, "heads/#{username}", sha)
35
+ $logger.info "Created Github user branch for #{username}"
36
+
37
+ @client.add_collaborator(repo, username)
38
+ $logger.info "Added #{username} as a collaborator to #{repo}."
39
+
40
+ rescue => e
41
+ $logger.error "Error creating Github user branch for #{username}"
42
+ $logger.error e.message
43
+ return false
44
+ end
45
+
46
+ true
47
+ end
48
+
49
+ def delete(username)
50
+ # can only do anything on our own repo, and only if we're authorized!
51
+ return true unless @githubtoken and @repomodel == :single
52
+
53
+ begin
54
+ @client.delete_branch("#{@gituser}/#{@controlrepo}", username)
55
+ $logger.info "Deleted Github user branch for #{username}"
56
+
57
+ @client.remove_collaborator(repo, username)
58
+ $logger.info "Removed #{username} as a collaborator on #{repo}."
59
+
60
+ rescue => e
61
+ $logger.error "Error deleting Github user branch for #{username}"
62
+ $logger.error e.message
63
+ return false
64
+ end
65
+
66
+ true
67
+ end
68
+
69
+ def userinfo(username, extended = false)
70
+ if @repomodel == :single
71
+ repo = "#{@gituser}/#{@controlrepo}"
72
+ url = "#{@gitserver}/#{@gituser}/#{@controlrepo}/tree/#{username}"
73
+ else
74
+ repo = "#{@username}/#{@controlrepo}"
75
+ url = "#{@gitserver}/#{username}/#{@controlrepo}"
76
+ end
77
+
78
+ userinfo = {
79
+ :username => username,
80
+ :controlrepo => url,
81
+ }
82
+ userinfo[:latestcommit] = latest_commit(repo, username) if extended
83
+ userinfo
84
+ end
85
+
86
+ private
87
+ def latest_commit(repo, username)
88
+ begin
89
+ commit = @client.commits(repo, :author => username).first
90
+ return if commit.nil?
91
+
92
+ {
93
+ :url => commit[:html_url],
94
+ :message => commit[:commit][:message].trim(62),
95
+ :time => Puppetfactory::Helpers.approximate_time_difference(commit[:commit][:author][:date]),
96
+ }
97
+ rescue => e
98
+ $logger.error "Cannot get commits for #{repo}."
99
+ $logger.error e.message
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,62 @@
1
+ require 'json'
2
+ require 'puppetfactory'
3
+
4
+ class Puppetfactory::Plugins::Gitlab < Puppetfactory::Plugins
5
+
6
+ def initialize(options)
7
+ super(options)
8
+
9
+ @suffix = options[:usersuffix]
10
+
11
+ begin
12
+ # Use default gitlab root password to get session token
13
+ login = {:login => 'root', :password => '5iveL!fe'}
14
+ resp = JSON.parse(RestClient.post('http://localhost:8888/api/v3/session', login))
15
+ @token = resp['private_token']
16
+ rescue => e
17
+ raise "GitLab authentication error! (#{e.message})"
18
+ end
19
+ end
20
+
21
+ def create(username, password)
22
+ begin
23
+ if password.length < 8
24
+ raise "Password must be at least 8 characters"
25
+ end
26
+
27
+ RestClient.post('http://localhost:8888/api/v3/users',
28
+ {
29
+ :email => "#{username}.#{@suffix}",
30
+ :password => password,
31
+ :username => username,
32
+ :name => username,
33
+ :confirm => false,
34
+ :private_token => @token # TODO: this invocation does not look like the invocation below?
35
+ })
36
+ end
37
+
38
+ $logger.info "Created GitLab user #{username}."
39
+ rescue => e
40
+ $logger.error "Error creating GitLab user #{username}: #{e.message}"
41
+ return false
42
+ end
43
+
44
+ true
45
+ end
46
+
47
+ def delete(username)
48
+ begin
49
+ users = JSON.parse(RestClient.get('http://localhost:8888/api/v3/users', {"PRIVATE-TOKEN" => @token}))
50
+ userid = users.select { |record| record['username'] == username }['id']
51
+ RestClient.delete("http://localhost:8888/api/v3/users/#{userid}" , {"PRIVATE-TOKEN" => @token})
52
+
53
+ $logger.info "Removed GitLab user #{username}."
54
+ rescue => e
55
+ $logger.error "Error removing GitLab user #{username}: #{e.message}"
56
+ return false
57
+ end
58
+
59
+ true
60
+ end
61
+
62
+ end
@@ -0,0 +1,46 @@
1
+ require 'json'
2
+ require 'puppetfactory'
3
+
4
+ class Puppetfactory::Plugins::Hooks < Puppetfactory::Plugins
5
+ attr_reader :weight
6
+
7
+ def initialize(options)
8
+ super(options)
9
+
10
+ @weight = 1
11
+ @path = options[:hooks_path] || '/etc/puppetfactory/hooks'
12
+ end
13
+
14
+ def create(username)
15
+ call_hooks(:create, username)
16
+ end
17
+
18
+ def delete(username)
19
+ call_hooks(:delete, username)
20
+ end
21
+
22
+ private
23
+ def call_hooks(hook_type, username)
24
+ success = true
25
+ # the .to_s allows us to accept strings or symbols
26
+ Dir.glob("#{HOOKS_PATH}/#{hook_type.to_s}/*") do |hook|
27
+ next unless File.file?(hook)
28
+ next unless File.executable?(hook)
29
+
30
+ begin
31
+ output, status = Open3.capture2e(hook, username)
32
+ raise "Execution error: #{output}" unless status.success?
33
+ $logger.info output
34
+
35
+ rescue => e
36
+ $logger.error "Error running hook: #{hook}"
37
+ $logger.error e.message
38
+ success = false
39
+ end
40
+ end
41
+
42
+ # only true if all hooks succeeded.
43
+ success
44
+ end
45
+
46
+ end
@@ -0,0 +1,10 @@
1
+ class Puppetfactory::Plugins::LoginShell < Puppetfactory::Plugins
2
+ def initialize(options)
3
+ super(options)
4
+ end
5
+
6
+ def login
7
+ $logger.info 'Logging in with the default system shell'
8
+ exec('bash --login')
9
+ end
10
+ end
@@ -0,0 +1,34 @@
1
+ require 'puppetfactory'
2
+
3
+ class Puppetfactory::Plugins::Logs < Puppetfactory::Plugins
4
+
5
+ def initialize(options)
6
+ super(options)
7
+ return unless options[:puppetfactory]
8
+
9
+ @logfile = options[:logfile]
10
+ server = options[:puppetfactory]
11
+
12
+ # Add a web route to the puppetfactory server. Must happen in the initializer
13
+ server.get '/logs' do
14
+ protected!
15
+ erb :logs
16
+ end
17
+
18
+ server.get '/logs/data' do
19
+ protected!
20
+ plugin(:Logs, :data)
21
+ end
22
+ end
23
+
24
+ def tabs(privileged = false)
25
+ return unless privileged # only show this tab to admin users
26
+
27
+ # url path => display title
28
+ { 'logs' => 'Logs' }
29
+ end
30
+
31
+ def data
32
+ File.read(@logfile) rescue "Cannot read logfile #{@logfile}"
33
+ end
34
+ end
@@ -0,0 +1,112 @@
1
+ require 'puppetfactory'
2
+
3
+ class Puppetfactory::Plugins::R10k < Puppetfactory::Plugins
4
+
5
+ def initialize(options)
6
+ super(options)
7
+
8
+ @gitserver = options[:gitserver]
9
+ @repomodel = options[:repomodel]
10
+ @controlrepo = options[:controlrepo]
11
+ @environments = options[:environments]
12
+ @r10k_config = '/etc/puppetlabs/r10k/r10k.yaml'
13
+ @pattern = "#{@gitserver}/%s/#{@controlrepo}"
14
+
15
+ # the rest of this method is for the big boys only
16
+ return unless Process.euid == 0
17
+
18
+ # in case this is the first run and it doesn't exist yet
19
+ unless File.exist? @r10k_config
20
+ File.open(@r10k_config, 'w') { |f| f.write({'sources' => {}}.to_yaml) }
21
+ end
22
+ end
23
+
24
+ def create(username, password)
25
+ begin
26
+ environment = "#{@environments}/#{Puppetfactory::Helpers.environment_name(username)}"
27
+ FileUtils.mkdir_p environment
28
+ FileUtils.chown_R(username, 'pe-puppet', environment)
29
+ FileUtils.chmod(0750, environment)
30
+
31
+ File.open(@r10k_config) do |file|
32
+ # make sure we don't have any concurrency issues
33
+ file.flock(File::LOCK_EX)
34
+
35
+ r10k = YAML.load_file(@r10k_config)
36
+ r10k['sources'][username] = {
37
+ 'remote' => sprintf(@pattern, username),
38
+ 'basedir' => @environments,
39
+ 'prefix' => (@repomodel == :peruser),
40
+ }
41
+
42
+ # Ruby 1.8.7, why don't you just go away now
43
+ File.open(@r10k_config, 'w') { |f| f.write(r10k.to_yaml) }
44
+ $logger.info "Created r10k source for #{username}"
45
+ end
46
+ rescue => e
47
+ $logger.error "Cannot create r10k source for #{username}"
48
+ $logger.error e.backtrace
49
+ return false
50
+ end
51
+
52
+ true
53
+ end
54
+
55
+ def delete(username)
56
+ begin
57
+ environment = "#{@environments}/#{Puppetfactory::Helpers.environment_name(username)}"
58
+ FileUtils.rm_rf environment
59
+
60
+ # also delete any prefixed environments. Is this even a good idea?
61
+ FileUtils.rm_rf "#{@environments}/#{username}_*" if @repomodel == :peruser
62
+
63
+ File.open(@r10k_config) do |file|
64
+ # make sure we don't have any concurrency issues
65
+ file.flock(File::LOCK_EX)
66
+
67
+ r10k = YAML.load_file(@r10k_config)
68
+ r10k['sources'].delete username
69
+
70
+ # Ruby 1.8.7, why don't you just go away now
71
+ File.open(@r10k_config, 'w') { |f| f.write(r10k.to_yaml) }
72
+ $logger.info "Created r10k source for #{username}"
73
+ end
74
+ rescue => e
75
+ $logger.error "Cannot remove r10k source for #{username}"
76
+ $logger.error e.backtrace
77
+ return false
78
+ end
79
+
80
+ true
81
+ end
82
+
83
+ def deploy(username)
84
+ environment = Puppetfactory::Helpers.environment_name(username)
85
+
86
+ output, status = Open3.capture2e('r10k', 'deploy', 'environment', environment)
87
+ unless status.success?
88
+ $logger.error "Failed to deploy environment #{environment} for #{username}"
89
+ $logger.error output
90
+ return false
91
+ end
92
+
93
+ $logger.info "Deployed environment #{environment} for #{username}"
94
+ true
95
+ end
96
+
97
+ def redeploy(username)
98
+ begin
99
+ if username == 'production'
100
+ raise "Can't redeploy production environment"
101
+ end
102
+ delete(username)
103
+ deploy(username)
104
+
105
+ rescue => e
106
+ raise "Error redeploying environment #{username}: #{e.message}"
107
+ end
108
+
109
+ true
110
+ end
111
+
112
+ end