├── .rspec ├── test ├── fixtures │ ├── cookbooks │ │ ├── metadata.rb │ │ ├── recipes │ │ │ └── default.rb │ │ └── files │ │ │ └── default │ │ │ ├── user_pub.pem │ │ │ ├── client_key_pub.pem │ │ │ ├── validator_pub.pem │ │ │ ├── client_key.pem │ │ │ ├── user_key.pem │ │ │ └── validator.pem │ └── data_bags │ │ └── chef │ │ ├── non-chef-user.json │ │ ├── keyless-user.json │ │ ├── inception_llc.json │ │ ├── test-node.json │ │ ├── populator.json │ │ ├── non-admin-user.json │ │ └── test-user.json ├── unit │ ├── spec_helper.rb │ ├── default_spec.rb │ ├── configurator_spec.rb │ ├── backups_spec.rb │ ├── restore_spec.rb │ ├── org_spec.rb │ ├── client_spec.rb │ └── solo_spec.rb └── integration │ ├── default │ └── serverspec │ │ └── default_spec.rb │ ├── data-bag │ └── serverspec │ │ └── default_spec.rb │ ├── backups │ └── serverspec │ │ └── default_spec.rb │ └── helpers │ └── serverspec │ └── spec_helper.rb ├── .gitignore ├── metadata.rb ├── Gemfile ├── Cheffile ├── .nellie ├── Rakefile ├── recipes ├── default.rb ├── backups.rb ├── org.rb ├── restore.rb ├── configurator.rb ├── solo.rb └── client.rb ├── chefignore ├── attributes └── default.rb ├── templates └── default │ └── chef-server-backup.rb.erb ├── .kitchen.yml ├── CHANGELOG.md ├── examples ├── chef-server.erb ├── chef-12-server.erb └── chef-server-restore.erb └── README.md /.rspec: -------------------------------------------------------------------------------- 1 | --default-path test/unit 2 | --color 3 | --format documentation -------------------------------------------------------------------------------- /test/fixtures/cookbooks/metadata.rb: -------------------------------------------------------------------------------- 1 | name 'test' 2 | version '0.1.0' 3 | -------------------------------------------------------------------------------- /test/unit/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'chefspec' 2 | require 'chefspec/librarian' 3 | -------------------------------------------------------------------------------- /test/fixtures/data_bags/chef/non-chef-user.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "non-chef-user" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .kitchen/ 2 | .kitchen.local.yml 3 | .librarian/ 4 | tmp/ 5 | ./cookbooks 6 | Gemfile.lock 7 | Cheffile.lock 8 | Berksfile.lock 9 | -------------------------------------------------------------------------------- /metadata.rb: -------------------------------------------------------------------------------- 1 | name 'chef-server-populator' 2 | description 'Populate chef server with stuff you want' 3 | maintainer 'Heavywater' 4 | maintainer_email 'support@hw-ops.com' 5 | version '2.0.3' 6 | 7 | depends 'chef-server', '~> 5.0' 8 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'chef' 4 | gem 'chefspec', '~> 4.2.0' 5 | gem 'librarian-chef' 6 | gem 'rake' 7 | 8 | group :integration do 9 | gem 'test-kitchen', '~> 1.3.0' 10 | gem 'kitchen-vagrant', '~> 0.16.0' 11 | end 12 | -------------------------------------------------------------------------------- /Cheffile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # ^syntax detection 3 | 4 | site 'https://supermarket.getchef.com/api/v1' 5 | 6 | cookbook 'chef-server-populator', path: '.' 7 | cookbook 'test', path: 'test/fixtures/cookbooks' 8 | cookbook 'apt' 9 | cookbook 'build-essential' 10 | -------------------------------------------------------------------------------- /.nellie: -------------------------------------------------------------------------------- 1 | { 2 | "commands": [ 3 | "apt-get update", 4 | "apt-get install -y libxml2-dev libxslt-dev zlib1g-dev", 5 | "bundle install --without integration", 6 | "bundle exec rake" 7 | ], 8 | "environment": { 9 | "CI": "true" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | 3 | require 'rspec/core/rake_task' 4 | RSpec::Core::RakeTask.new(:unit) do |t| 5 | t.pattern = ['test/unit/**/*_spec.rb'] 6 | end 7 | 8 | begin 9 | require 'kitchen/rake_tasks' 10 | Kitchen::RakeTasks.new 11 | rescue LoadError 12 | puts '>>>>> Kitchen gem not loaded, omitting tasks' unless ENV['CI'] 13 | end 14 | 15 | task default: [:unit] 16 | -------------------------------------------------------------------------------- /test/fixtures/data_bags/chef/keyless-user.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "keyless-user", 3 | "chef_server": { 4 | "client_key": "", 5 | "full_name": "Keyless User", 6 | "email": "keylessuser@example.com", 7 | "type": [ 8 | "user" 9 | ], 10 | "orgs": { 11 | "inception_llc": { 12 | "enabled": true, 13 | "admin": true 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/recipes/default.rb: -------------------------------------------------------------------------------- 1 | # This is a test helper recipe that will create public and private 2 | # keys to test populator features. 3 | 4 | directory '/tmp/chef-server-populator' 5 | 6 | %w(client_key.pem client_key_pub.pem validator.pem validator_pub.pem user_key.pem user_pub.pem).each do |file| 7 | cookbook_file file do 8 | path "/tmp/chef-server-populator/#{file}" 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /recipes/default.rb: -------------------------------------------------------------------------------- 1 | require 'open-uri' 2 | require 'openssl' 3 | 4 | if Chef::Config[:solo] 5 | include_recipe 'chef-server-populator::solo' 6 | else 7 | include_recipe 'chef-server-populator::client' 8 | end 9 | 10 | if !node['chef_server_populator']['restore']['file'].empty? && 11 | node['chef_server_populator']['restore']['file'] != 'none' 12 | include_recipe 'chef-server-populator::restore' 13 | end 14 | -------------------------------------------------------------------------------- /test/integration/default/serverspec/default_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative './spec_helper' 2 | 3 | describe 'chef-server-default-org' do 4 | describe file('/etc/opscode/chef-server.rb') do 5 | its(:content) { should match /default_orgname\("inception_llc"\)/ } 6 | end 7 | end 8 | 9 | describe 'chef-server-populator-cookbook-upload' do 10 | describe command('knife cookbook list -c /etc/opscode/pivotal.rb') do 11 | its(:stdout) { should match /chef-server-populator/ } 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/files/default/user_pub.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4CeiY3E99UYQFm/xpBJL 3 | xrmd/zrmCH6yoQva2tXza1+AxOTfSWQcmXWjFMkO1h0w3ElAvieyinRThE9Rl6DE 4 | oGPzJnLzc8AmAMSSdU6gAn8Uto3jQMGk8ByYv+nd1rGMoCl1H29OJuG7g+bkychL 5 | o1sEqQkAn/J+zZ4RHI1E6rXmuEaIRM49j4M0ejh5+zw7YCYiAN/Owz5zrF14P7GL 6 | i96i5Tek7ndXxAfDOkRiam+I+08rZNspNAVdv0ORHy7sydra/0Y4odC+7f/WrAhE 7 | HxaPfiUA7/slHmbrZK9/gD7nZf7tpooeaA+nJKVTwWebCVo75APW/KLw7ErYEGyy 8 | 0QIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/files/default/client_key_pub.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4CeiY3E99UYQFm/xpBJL 3 | xrmd/zrmCH6yoQva2tXza1+AxOTfSWQcmXWjFMkO1h0w3ElAvieyinRThE9Rl6DE 4 | oGPzJnLzc8AmAMSSdU6gAn8Uto3jQMGk8ByYv+nd1rGMoCl1H29OJuG7g+bkychL 5 | o1sEqQkAn/J+zZ4RHI1E6rXmuEaIRM49j4M0ejh5+zw7YCYiAN/Owz5zrF14P7GL 6 | i96i5Tek7ndXxAfDOkRiam+I+08rZNspNAVdv0ORHy7sydra/0Y4odC+7f/WrAhE 7 | HxaPfiUA7/slHmbrZK9/gD7nZf7tpooeaA+nJKVTwWebCVo75APW/KLw7ErYEGyy 8 | 0QIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/files/default/validator_pub.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwKOeqn4dK6b+SF7mvc+N 3 | cSpceoxpkQr+Wu1AShxOD0YenD9aCpzmK5rTKlqT7Ko42q0IrSLAeQHKRGNH9ayI 4 | LOmRmk2do+fjVpXHSSlraEXKNNFSXXRjlVubDBLGcQ6Qx+GL09g4vVOBwcQb0D/E 5 | X2+f9owRzbcSJma6bzniWWYbWQEa6g2pB9E/hX5KSKj+byVDgUuc/tJsDXtmL2hS 6 | yQTUtpC6suxDqltBVxNRrmaIna8eTOGw7RiEmpyeeynvOfbMCDE57m1x6RFdscZT 7 | BpiJW4pHgbx0QgSU9dWH/kUzG/B76idzQuYZVWDBI987brlzmCU2m5teaez1BWva 8 | eQIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /test/integration/data-bag/serverspec/default_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative './spec_helper' 2 | 3 | describe 'Create user with correct fields' do 4 | describe command('chef-server-ctl user-show test-user') do 5 | its(:exit_status) { should eq 0 } 6 | its(:stdout) { should contain 'Test User' } 7 | its(:stdout) { should contain 'testuser@example.com' } 8 | end 9 | end 10 | describe 'NO OPs' do 11 | describe command('chef-server-ctl user-list') do 12 | its(:stdout) { should_not match /keyless-user/ } 13 | its(:stdout) { should_not match /non-chef-user/ } 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /test/fixtures/data_bags/chef/inception_llc.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "inception_llc", 3 | "chef_server": { 4 | "client_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwKOeqn4dK6b+SF7mvc+N\ncSpceoxpkQr+Wu1AShxOD0YenD9aCpzmK5rTKlqT7Ko42q0IrSLAeQHKRGNH9ayI\nLOmRmk2do+fjVpXHSSlraEXKNNFSXXRjlVubDBLGcQ6Qx+GL09g4vVOBwcQb0D/E\nX2+f9owRzbcSJma6bzniWWYbWQEa6g2pB9E/hX5KSKj+byVDgUuc/tJsDXtmL2hS\nyQTUtpC6suxDqltBVxNRrmaIna8eTOGw7RiEmpyeeynvOfbMCDE57m1x6RFdscZT\nBpiJW4pHgbx0QgSU9dWH/kUzG/B76idzQuYZVWDBI987brlzmCU2m5teaez1BWva\neQIDAQAB\n-----END PUBLIC KEY-----", 5 | "type": [ 6 | "org" 7 | ], 8 | "enabled": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/fixtures/data_bags/chef/test-node.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "test-node", 3 | "chef_server": { 4 | "client_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4CeiY3E99UYQFm/xpBJL\nxrmd/zrmCH6yoQva2tXza1+AxOTfSWQcmXWjFMkO1h0w3ElAvieyinRThE9Rl6DE\noGPzJnLzc8AmAMSSdU6gAn8Uto3jQMGk8ByYv+nd1rGMoCl1H29OJuG7g+bkychL\no1sEqQkAn/J+zZ4RHI1E6rXmuEaIRM49j4M0ejh5+zw7YCYiAN/Owz5zrF14P7GL\ni96i5Tek7ndXxAfDOkRiam+I+08rZNspNAVdv0ORHy7sydra/0Y4odC+7f/WrAhE\nHxaPfiUA7/slHmbrZK9/gD7nZf7tpooeaA+nJKVTwWebCVo75APW/KLw7ErYEGyy\n0QIDAQAB\n-----END PUBLIC KEY-----", 5 | "type": [ 6 | "client" 7 | ], 8 | "orgs": [ "inception_llc" ] 9 | } 10 | } 11 | 12 | -------------------------------------------------------------------------------- /test/fixtures/data_bags/chef/populator.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "populator", 3 | "full_name": "Populator User", 4 | "chef_server": { 5 | "client_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4CeiY3E99UYQFm/xpBJL\nxrmd/zrmCH6yoQva2tXza1+AxOTfSWQcmXWjFMkO1h0w3ElAvieyinRThE9Rl6DE\noGPzJnLzc8AmAMSSdU6gAn8Uto3jQMGk8ByYv+nd1rGMoCl1H29OJuG7g+bkychL\no1sEqQkAn/J+zZ4RHI1E6rXmuEaIRM49j4M0ejh5+zw7YCYiAN/Owz5zrF14P7GL\ni96i5Tek7ndXxAfDOkRiam+I+08rZNspNAVdv0ORHy7sydra/0Y4odC+7f/WrAhE\nHxaPfiUA7/slHmbrZK9/gD7nZf7tpooeaA+nJKVTwWebCVo75APW/KLw7ErYEGyy\n0QIDAQAB\n-----END PUBLIC KEY-----", 6 | "type": [ 7 | "user" 8 | ], 9 | "orgs": { 10 | "inception_llc": { 11 | "enabled": true, 12 | "admin": true 13 | } 14 | } 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /test/fixtures/data_bags/chef/non-admin-user.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "non-admin-user", 3 | "full_name": "Non-Admin User", 4 | "chef_server": { 5 | "client_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4CeiY3E99UYQFm/xpBJL\nxrmd/zrmCH6yoQva2tXza1+AxOTfSWQcmXWjFMkO1h0w3ElAvieyinRThE9Rl6DE\noGPzJnLzc8AmAMSSdU6gAn8Uto3jQMGk8ByYv+nd1rGMoCl1H29OJuG7g+bkychL\no1sEqQkAn/J+zZ4RHI1E6rXmuEaIRM49j4M0ejh5+zw7YCYiAN/Owz5zrF14P7GL\ni96i5Tek7ndXxAfDOkRiam+I+08rZNspNAVdv0ORHy7sydra/0Y4odC+7f/WrAhE\nHxaPfiUA7/slHmbrZK9/gD7nZf7tpooeaA+nJKVTwWebCVo75APW/KLw7ErYEGyy\n0QIDAQAB\n-----END PUBLIC KEY-----", 6 | "type": [ 7 | "user" 8 | ], 9 | "orgs": { 10 | "inception_llc": { 11 | "enabled": true, 12 | "admin": false 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/fixtures/data_bags/chef/test-user.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "test-user", 3 | "chef_server": { 4 | "full_name": "Test User", 5 | "email": "testuser@example.com", 6 | "client_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4CeiY3E99UYQFm/xpBJL\nxrmd/zrmCH6yoQva2tXza1+AxOTfSWQcmXWjFMkO1h0w3ElAvieyinRThE9Rl6DE\noGPzJnLzc8AmAMSSdU6gAn8Uto3jQMGk8ByYv+nd1rGMoCl1H29OJuG7g+bkychL\no1sEqQkAn/J+zZ4RHI1E6rXmuEaIRM49j4M0ejh5+zw7YCYiAN/Owz5zrF14P7GL\ni96i5Tek7ndXxAfDOkRiam+I+08rZNspNAVdv0ORHy7sydra/0Y4odC+7f/WrAhE\nHxaPfiUA7/slHmbrZK9/gD7nZf7tpooeaA+nJKVTwWebCVo75APW/KLw7ErYEGyy\n0QIDAQAB\n-----END PUBLIC KEY-----", 7 | "type": [ 8 | "user" 9 | ], 10 | "orgs": { 11 | "inception_llc": { 12 | "enabled": true, 13 | "admin": true 14 | } 15 | } 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /test/integration/backups/serverspec/default_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative './spec_helper' 2 | 3 | describe 'backup-script-installed' do 4 | describe file('/usr/local/bin/chef-server-backup') do 5 | it { should be_file } 6 | it { should be_owned_by 'root' } 7 | it { should be_executable.by('owner') } 8 | end 9 | end 10 | 11 | describe 'backups-configured' do 12 | describe file('/etc/chef-server/populator/backup.json') do 13 | it { should be_file } 14 | it { should be_owned_by 'root' } 15 | its(:content) { should match %r{"dir": "/tmp/chef-server/backup"} } 16 | its(:content) { should match /"filename": "chef-server-full"/ } 17 | it { should be_readable.by('owner') } 18 | end 19 | end 20 | 21 | describe 'creates-backups' do 22 | describe command('PATH=/opt/chef/embedded/bin:$PATH /usr/local/bin/chef-server-backup') do 23 | its(:exit_status) { should eq 0 } 24 | end 25 | end 26 | 27 | describe 'creates-cron-job' do 28 | describe cron do 29 | it { should have_entry '33 3 * * * /usr/local/bin/chef-server-backup' } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /chefignore: -------------------------------------------------------------------------------- 1 | # Put files/directories that should be ignored in this file when uploading 2 | # or sharing to the community site. 3 | # Lines that start with '# ' are comments. 4 | 5 | # OS generated files # 6 | ###################### 7 | .DS_Store 8 | Icon? 9 | nohup.out 10 | ehthumbs.db 11 | Thumbs.db 12 | 13 | # SASS # 14 | ######## 15 | .sass-cache 16 | 17 | # EDITORS # 18 | ########### 19 | \#* 20 | .#* 21 | *~ 22 | *.sw[a-z] 23 | *.bak 24 | REVISION 25 | TAGS* 26 | tmtags 27 | *_flymake.* 28 | *_flymake 29 | *.tmproj 30 | .project 31 | .settings 32 | mkmf.log 33 | 34 | ## COMPILED ## 35 | ############## 36 | a.out 37 | *.o 38 | *.pyc 39 | *.so 40 | *.com 41 | *.class 42 | *.dll 43 | *.exe 44 | */rdoc/ 45 | 46 | # Testing # 47 | ########### 48 | .watchr 49 | .rspec 50 | spec/* 51 | spec/fixtures/* 52 | test/* 53 | features/* 54 | examples/* 55 | Guardfile 56 | Procfile 57 | test/* 58 | spec/* 59 | 60 | # SCM # 61 | ####### 62 | .git 63 | */.git 64 | .gitignore 65 | .gitmodules 66 | .gitconfig 67 | .gitattributes 68 | .svn 69 | */.bzr/* 70 | */.hg/* 71 | */.svn/* 72 | 73 | # Berkshelf # 74 | ############# 75 | Berksfile 76 | Berksfile.lock 77 | cookbooks/* 78 | tmp 79 | 80 | # Cookbooks # 81 | ############# 82 | CONTRIBUTING 83 | CHANGELOG* 84 | 85 | # Strainer # 86 | ############ 87 | Colanderfile 88 | Strainerfile 89 | .colander 90 | .strainer 91 | 92 | # Vagrant # 93 | ########### 94 | .vagrant 95 | Vagrantfile 96 | 97 | # Travis # 98 | ########## 99 | .travis.yml 100 | .nellie 101 | -------------------------------------------------------------------------------- /test/unit/default_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | 3 | describe 'chef-server-populator::default' do 4 | let(:chef_solo_run) { ChefSpec::SoloRunner.new.converge(described_recipe) } 5 | let(:chef_client_run) { ChefSpec::ServerRunner.new.converge(described_recipe) } 6 | 7 | before do 8 | allow_any_instance_of(Chef::Recipe).to receive(:include_recipe).and_call_original 9 | allow_any_instance_of(Chef::Recipe).to receive(:include_recipe).with('chef-server-populator::solo') 10 | allow_any_instance_of(Chef::Recipe).to receive(:include_recipe).with('chef-server-populator::client') 11 | allow_any_instance_of(Chef::Recipe).to receive(:include_recipe).with('chef-server-populator::restore') 12 | end 13 | 14 | context 'when running under chef-solo' do 15 | it 'includes solo recipe' do 16 | expect_any_instance_of(Chef::Recipe).to receive(:include_recipe).with('chef-server-populator::solo') 17 | chef_solo_run 18 | end 19 | end 20 | 21 | context 'when running under chef-client' do 22 | it 'includes client recipe' do 23 | expect_any_instance_of(Chef::Recipe).to receive(:include_recipe).with('chef-server-populator::client') 24 | chef_client_run 25 | end 26 | end 27 | 28 | context 'when provided values for restore file attribute' do 29 | it 'includes restore recipe' do 30 | chef_solo_run.node.normal['chef_server_populator']['restore'][:file] = '/tmp/latest.tgz' 31 | expect_any_instance_of(Chef::Recipe).to receive(:include_recipe).with('chef-server-populator::restore') 32 | chef_solo_run.converge(described_recipe) 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /recipes/backups.rb: -------------------------------------------------------------------------------- 1 | directory node['chef_server_populator']['backup']['dir'] do 2 | recursive true 3 | owner 'opscode-pgsql' 4 | mode '0755' 5 | end 6 | 7 | # Upload to Remote Storage 8 | # Include fog 9 | case node['platform_family'] 10 | when 'debian' 11 | packages = %w(gcc libxml2 libxml2-dev libxslt-dev) 12 | when 'rhel' 13 | packages = %w(gcc libxml2 libxml2-devel libxslt libxslt-devel patch) 14 | end 15 | packages.each do |fog_dep| 16 | package fog_dep 17 | end 18 | 19 | node['chef_server_populator']['backup_gems'].each_pair do |gem_name, gem_version| 20 | gem_package gem_name do 21 | unless gem_version.nil? 22 | version gem_version 23 | end 24 | retries 2 25 | end 26 | end 27 | 28 | directory node['chef_server_populator']['configuration_directory'] do 29 | recursive true 30 | owner 'root' 31 | mode '700' 32 | end 33 | 34 | file File.join(node['chef_server_populator']['configuration_directory'], 'backup.json') do 35 | content Chef::JSONCompat.to_json_pretty( 36 | node['chef_server_populator']['backup'].merge( 37 | cookbook_version: node.run_context.cookbook_collection['chef-server-populator'].version 38 | ) 39 | ) 40 | owner 'root' 41 | mode '600' 42 | end 43 | 44 | template '/usr/local/bin/chef-server-backup' do 45 | source 'chef-server-backup.rb.erb' 46 | mode '0700' 47 | retries 3 48 | end 49 | 50 | cron 'Chef Server Backups' do 51 | command '/usr/local/bin/chef-server-backup' 52 | node['chef_server_populator']['backup']['schedule'].each do |k, v| 53 | send(k, v) 54 | end 55 | path '/opt/chef/embedded/bin/:/usr/bin:/usr/local/bin:/bin' 56 | end 57 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/files/default/client_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEA4CeiY3E99UYQFm/xpBJLxrmd/zrmCH6yoQva2tXza1+AxOTf 3 | SWQcmXWjFMkO1h0w3ElAvieyinRThE9Rl6DEoGPzJnLzc8AmAMSSdU6gAn8Uto3j 4 | QMGk8ByYv+nd1rGMoCl1H29OJuG7g+bkychLo1sEqQkAn/J+zZ4RHI1E6rXmuEaI 5 | RM49j4M0ejh5+zw7YCYiAN/Owz5zrF14P7GLi96i5Tek7ndXxAfDOkRiam+I+08r 6 | ZNspNAVdv0ORHy7sydra/0Y4odC+7f/WrAhEHxaPfiUA7/slHmbrZK9/gD7nZf7t 7 | pooeaA+nJKVTwWebCVo75APW/KLw7ErYEGyy0QIDAQABAoIBAHscCYoINfkx48AO 8 | 924fRlL8uxPkq1ZbD4x30FmETE7agwSglyndu/9kpo7R5j4ehilWaiKF9v8n6H6w 9 | eaWHfLaCbpbdZ2xZSgX98FpRNnxog13IC9oTAJ2HdzVgIqC5nvyQCG2dWhTswWou 10 | M9XHFhO/941HDnrKWm5ftBI0XHdzLKlAhdCjhqpJGYeyHBFKoRMqkUZQJ5BOzBeb 11 | UR/E8O//YkSbGfz6A+lCkg3MHbDCLhLN7cQc8gGYiRNuyeIv9hXbEYcuWfVv7+00 12 | 4jqULkyaxUyt4lgdhQjcFyNEFbUcIGWj0gV5EzpG2XQAlC5XtHpAcWNiF45EAj/o 13 | OmFh20kCgYEA/FjrJyuUlUTkojs2/1l9PyiOovgVVygJZFMqyBZ48ZfjNYfwOmc6 14 | Nh1B7T8sxmmNtlhZMfc1aCMa+a6YgYiGXct78+qWYN1ugLxk+TqQBHfJoSIsP+uF 15 | XyptRzKjPaXvC4KrwXjGm+UZBAy2IN3dsB6LHZcO52/wGSAn0Xg6uGMCgYEA42Y/ 16 | WWiz5O6oupwfXdhy6NSa+mXhdHzgu9U9m3/KoEMY135/dPCr5FzjB5RRfi629Ut+ 17 | aZ4MeBcOh+IDM4qVNhASds3n+qz/0DN6ES6RjEabvGuuc58VFJzBlRCrFQXIkoTq 18 | K0X+n0rjuGVYKScT8n/OHGj0WInti0/3igSvPDsCgYB0sh4U4Cd3HAPrLh0R2u17 19 | nqLPpJAh3Qby2S8IlrPZbZcJ81JZf9FAoykQsM1g+AEr2RFudNbC0LEx09b82Ajj 20 | KS2qL8rZAq2OZREA3F8rYcXheS8jZCQqTTF54mXxJWI1XXlWICcggsKdx88VxeQ2 21 | BPnqdifTSUWsKN2hFct55wKBgGBcBH4jow9gnnktDRGcVd0Zsm5m26mb/TM7Ajcx 22 | aLCOnik6vlHUZlKHleCxVfMNYpQBIJkq0wfUZWDpQggGMoJNPzW+vQ15X5FfLtu3 23 | CxARH48xZiQyhiFM5679+DEZWUvVLNGXgHUJrAOIVlph5877n2BkjR4znEhLY47F 24 | u2ffAoGAEStwVs5OMKpL8SWgxL/W4sqUzHaKczUs5pUOb2M8WiamcxbWpo4Hqv70 25 | Oap99uZqXSpVV9/EVMnejtDqHoA3zZLpebI6nPWO3S5jEFBc+WC2Sa3THj4OdqnN 26 | YBUKmmQUlf7w2BVab1bLznJX0hSx6fcsKmYlFw+BkTWdHCdVglA= 27 | -----END RSA PRIVATE KEY----- -------------------------------------------------------------------------------- /test/fixtures/cookbooks/files/default/user_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEA4CeiY3E99UYQFm/xpBJLxrmd/zrmCH6yoQva2tXza1+AxOTf 3 | SWQcmXWjFMkO1h0w3ElAvieyinRThE9Rl6DEoGPzJnLzc8AmAMSSdU6gAn8Uto3j 4 | QMGk8ByYv+nd1rGMoCl1H29OJuG7g+bkychLo1sEqQkAn/J+zZ4RHI1E6rXmuEaI 5 | RM49j4M0ejh5+zw7YCYiAN/Owz5zrF14P7GLi96i5Tek7ndXxAfDOkRiam+I+08r 6 | ZNspNAVdv0ORHy7sydra/0Y4odC+7f/WrAhEHxaPfiUA7/slHmbrZK9/gD7nZf7t 7 | pooeaA+nJKVTwWebCVo75APW/KLw7ErYEGyy0QIDAQABAoIBAHscCYoINfkx48AO 8 | 924fRlL8uxPkq1ZbD4x30FmETE7agwSglyndu/9kpo7R5j4ehilWaiKF9v8n6H6w 9 | eaWHfLaCbpbdZ2xZSgX98FpRNnxog13IC9oTAJ2HdzVgIqC5nvyQCG2dWhTswWou 10 | M9XHFhO/941HDnrKWm5ftBI0XHdzLKlAhdCjhqpJGYeyHBFKoRMqkUZQJ5BOzBeb 11 | UR/E8O//YkSbGfz6A+lCkg3MHbDCLhLN7cQc8gGYiRNuyeIv9hXbEYcuWfVv7+00 12 | 4jqULkyaxUyt4lgdhQjcFyNEFbUcIGWj0gV5EzpG2XQAlC5XtHpAcWNiF45EAj/o 13 | OmFh20kCgYEA/FjrJyuUlUTkojs2/1l9PyiOovgVVygJZFMqyBZ48ZfjNYfwOmc6 14 | Nh1B7T8sxmmNtlhZMfc1aCMa+a6YgYiGXct78+qWYN1ugLxk+TqQBHfJoSIsP+uF 15 | XyptRzKjPaXvC4KrwXjGm+UZBAy2IN3dsB6LHZcO52/wGSAn0Xg6uGMCgYEA42Y/ 16 | WWiz5O6oupwfXdhy6NSa+mXhdHzgu9U9m3/KoEMY135/dPCr5FzjB5RRfi629Ut+ 17 | aZ4MeBcOh+IDM4qVNhASds3n+qz/0DN6ES6RjEabvGuuc58VFJzBlRCrFQXIkoTq 18 | K0X+n0rjuGVYKScT8n/OHGj0WInti0/3igSvPDsCgYB0sh4U4Cd3HAPrLh0R2u17 19 | nqLPpJAh3Qby2S8IlrPZbZcJ81JZf9FAoykQsM1g+AEr2RFudNbC0LEx09b82Ajj 20 | KS2qL8rZAq2OZREA3F8rYcXheS8jZCQqTTF54mXxJWI1XXlWICcggsKdx88VxeQ2 21 | BPnqdifTSUWsKN2hFct55wKBgGBcBH4jow9gnnktDRGcVd0Zsm5m26mb/TM7Ajcx 22 | aLCOnik6vlHUZlKHleCxVfMNYpQBIJkq0wfUZWDpQggGMoJNPzW+vQ15X5FfLtu3 23 | CxARH48xZiQyhiFM5679+DEZWUvVLNGXgHUJrAOIVlph5877n2BkjR4znEhLY47F 24 | u2ffAoGAEStwVs5OMKpL8SWgxL/W4sqUzHaKczUs5pUOb2M8WiamcxbWpo4Hqv70 25 | Oap99uZqXSpVV9/EVMnejtDqHoA3zZLpebI6nPWO3S5jEFBc+WC2Sa3THj4OdqnN 26 | YBUKmmQUlf7w2BVab1bLznJX0hSx6fcsKmYlFw+BkTWdHCdVglA= 27 | -----END RSA PRIVATE KEY----- -------------------------------------------------------------------------------- /test/fixtures/cookbooks/files/default/validator.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAwKOeqn4dK6b+SF7mvc+NcSpceoxpkQr+Wu1AShxOD0YenD9a 3 | CpzmK5rTKlqT7Ko42q0IrSLAeQHKRGNH9ayILOmRmk2do+fjVpXHSSlraEXKNNFS 4 | XXRjlVubDBLGcQ6Qx+GL09g4vVOBwcQb0D/EX2+f9owRzbcSJma6bzniWWYbWQEa 5 | 6g2pB9E/hX5KSKj+byVDgUuc/tJsDXtmL2hSyQTUtpC6suxDqltBVxNRrmaIna8e 6 | TOGw7RiEmpyeeynvOfbMCDE57m1x6RFdscZTBpiJW4pHgbx0QgSU9dWH/kUzG/B7 7 | 6idzQuYZVWDBI987brlzmCU2m5teaez1BWvaeQIDAQABAoIBADPDQ304igoSwz0b 8 | ExFp1e3Unijn1e39cD9qhN3lISyFgPAnLcCwTEDnR1qYICt1yfUoja5IKkPZsUoT 9 | C2D+TVEUKeW2eWdzhejcMkJ8eLn6Fs1+eY1XGvMlPFJQMTZr3Yd4GaSA8y4cZD92 10 | vKqGl/O1ANy04ovOZ3geHnaIrUOr7soObtZbjS8oS32QlmeKcVbVu8cXRd2Zk4uG 11 | 5xVkZZqWhhp++FhFoQVN0qvAbFWhQlZcFI1jTv209Wf2FfwYYZ7ANaYXuN/H87bc 12 | AQrS+ahfTOyUxCT+FCRXppY28FZBJ1I/HzbTVAxw8fzICzCPQo7Z7sXFtGQFpqtA 13 | LCmCeAECgYEA4uhmwRhAQmTZ+BiO2+AncSjaadw8eCvGBn9eBy/uQQ1YDrFvJnim 14 | Qe0RK+6yShM9oU0Xi/aPi3g1sNFZnbqadGS7q1WOKwfK2YlHspEi0WGodkoOGVPd 15 | XSnrvSVVv4v1UGM+xjtMFlUC7VB4wAi6Wvj8E3XJOY/6cfCJJcqK3oECgYEA2VZy 16 | gYYO0zCnK23ObqsEFUQYoTp8eo1WDfePn+NAzqxZ0HFjenqOrImy/AC980DU4QpQ 17 | LkVX7TZCss1tP7ITe0+MpYFV/zyu7hpN15H4bPtcrXR85Y4L2SjYzsY2tgZspXhu 18 | 7wS8GGd9uaT9SYWFTOBvhRAfbJQpTTh7ze0f7/kCgYAPJqV1z0C7LjC/uCQR2m3H 19 | yIqM9v+ypnmahXap1DF8dn+iu4ZIc1XmoQW8gTu2ZoJ++r7Pwa7kCSrMm634MjWO 20 | AAvn7NaJWQPacqgu7DH++R5eh6NZXZgyWFI9VvjayWWf8ICIi/0QoKyJXA40BGEt 21 | IeLzCbJd/ZcjdIQz2cpcAQKBgQDVDZaAfF9KDlbCDwmfmUwOYMvYOZJPPQAP/xAv 22 | h8I/F9uPp6gCJ+R3JSFtQnjy3ioRVV/WjCmuo1NtHk4rc7D/X7Mbh5c690zwop6O 23 | ZUzn4N0wOJI7Ii1hO1aWFN8LP28AIvh0nOqa7dDkzOVzTk2nWBhak7yKXw/B4E4V 24 | R4/YWQKBgQCuZEE3nuh4CGr4dwJtB7R5VkBPSiNJVJgvXFSVDN3nKhcWB8GFcqDe 25 | 7nXGwMt5ZJxMJ4hxIwycTnKfYnqubaVtsh8nwrm3VKzQBobvNp+sKiSyfda5/gw/ 26 | f/q7FqsmnrSOz2pQaxbDZMdzCgKxGdrloPiQz/nYmf+a91LB7XysVQ== 27 | -----END RSA PRIVATE KEY----- -------------------------------------------------------------------------------- /test/integration/helpers/serverspec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'serverspec' 2 | 3 | set :backend, :exec 4 | 5 | describe 'chef-server-populator-configurator' do 6 | describe file('/etc/opscode/pivotal.rb') do 7 | it { should be_file } 8 | end 9 | 10 | describe file('/etc/opscode/chef-server.rb') do 11 | its(:content) { should match /api_fqdn "localhost"/ } 12 | end 13 | end 14 | 15 | describe 'chef-server-org-creation' do 16 | describe command('chef-server-ctl org-list') do 17 | its(:stdout) { should match /inception_llc/ } 18 | end 19 | 20 | describe command('chef-server-ctl list-user-keys populator') do 21 | its(:stdout) { should match /populator/ } 22 | end 23 | end 24 | 25 | describe 'chef-server-user-creation' do 26 | describe command('chef-server-ctl user-list') do 27 | its(:stdout) { should match /pivotal\npopulator/ } 28 | end 29 | 30 | describe command('chef-server-ctl list-user-keys populator -v') do 31 | its(:stdout) { should include "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4CeiY3E99UYQFm/xpBJL\nxrmd/zrmCH6yoQva2tXza1+AxOTfSWQcmXWjFMkO1h0w3ElAvieyinRThE9Rl6DE\noGPzJnLzc8AmAMSSdU6gAn8Uto3jQMGk8ByYv+nd1rGMoCl1H29OJuG7g+bkychL\no1sEqQkAn/J+zZ4RHI1E6rXmuEaIRM49j4M0ejh5+zw7YCYiAN/Owz5zrF14P7GL\ni96i5Tek7ndXxAfDOkRiam+I+08rZNspNAVdv0ORHy7sydra/0Y4odC+7f/WrAhE\nHxaPfiUA7/slHmbrZK9/gD7nZf7tpooeaA+nJKVTwWebCVo75APW/KLw7ErYEGyy\n0QIDAQAB\n-----END PUBLIC KEY-----" } 32 | end 33 | 34 | describe command('sudo chef-server-ctl org-user-add inception_llc populator') do 35 | its(:stdout) { should match /User populator already associated with organization inception_llc/ } 36 | end 37 | end 38 | 39 | describe 'populator-solo-client-creation' do 40 | describe command('chef-server-ctl list-client-keys inception_llc test-node -v') do 41 | its(:stdout) { should include "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4CeiY3E99UYQFm/xpBJL\nxrmd/zrmCH6yoQva2tXza1+AxOTfSWQcmXWjFMkO1h0w3ElAvieyinRThE9Rl6DE\noGPzJnLzc8AmAMSSdU6gAn8Uto3jQMGk8ByYv+nd1rGMoCl1H29OJuG7g+bkychL\no1sEqQkAn/J+zZ4RHI1E6rXmuEaIRM49j4M0ejh5+zw7YCYiAN/Owz5zrF14P7GL\ni96i5Tek7ndXxAfDOkRiam+I+08rZNspNAVdv0ORHy7sydra/0Y4odC+7f/WrAhE\nHxaPfiUA7/slHmbrZK9/gD7nZf7tpooeaA+nJKVTwWebCVo75APW/KLw7ErYEGyy\n0QIDAQAB\n-----END PUBLIC KEY-----" } 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /attributes/default.rb: -------------------------------------------------------------------------------- 1 | default[:chef_server_populator][:configuration_directory] = '/etc/chef-server/populator' 2 | default[:chef_server_populator][:base_path] = '/tmp/chef-server-populator' 3 | default[:chef_server_populator][:clients] = {} 4 | default[:chef_server_populator][:knife_exec] = '/usr/bin/knife' 5 | default[:chef_server_populator][:user] = 'admin' 6 | default[:chef_server_populator][:pem] = '/etc/chef-server/admin.pem' 7 | default[:chef_server_populator][:databag] = nil 8 | default[:chef_server_populator][:user_databag] = nil 9 | 10 | default[:chef_server_populator][:endpoint] = nil 11 | 12 | default[:chef_server_populator][:backup_gems][:miasma] = '~> 0.2' 13 | 14 | # Deprecated in favor of endpoint 15 | default[:chef_server_populator][:servername_override] = nil 16 | 17 | # The :chef_server attribute is passed to chef-server cookbook 18 | # Default the ttl since it kills runs with 403s on templates with 19 | # annoying frequency 20 | default[:chef_server_populator][:chef_server][:configuration][:opscode_erchef][:s3_url_ttl] = 3600 21 | 22 | default[:chef_server_populator][:cookbook_auto_install] = true 23 | 24 | default[:chef_server_populator][:restore][:file] = '' 25 | default[:chef_server_populator][:restore][:data] = '' 26 | default[:chef_server_populator][:restore][:local_path] = '/tmp/' 27 | 28 | default[:chef_server_populator][:backup][:dir] = '/tmp/chef-server/backup' 29 | default[:chef_server_populator][:backup][:filename] = 'chef-server-full' 30 | default[:chef_server_populator][:backup][:remote][:connection] = nil 31 | default[:chef_server_populator][:backup][:remote][:directory] = nil 32 | default[:chef_server_populator][:backup][:remote][:file_prefix] = nil 33 | default[:chef_server_populator][:backup][:schedule] = { 34 | minute: '33', 35 | hour: '3', 36 | } 37 | 38 | # The following attributes are provided as examples. In almost every 39 | # imaginable case you will want to replace some or all of these with 40 | # your own values. 41 | 42 | default[:chef_server_populator][:solo_org] = { 43 | org_name: 'inception_llc', 44 | full_name: 'Chef Inception Organization', 45 | validator_pub_key: 'validator_pub.pem', 46 | } 47 | 48 | default[:chef_server_populator][:solo_org_user] = { 49 | name: 'populator', 50 | first: 'Populator', 51 | last: 'User', 52 | email: 'pop@example.com', 53 | pub_key: 'user_pub.pem', 54 | } 55 | 56 | default[:chef_server_populator][:server_org] = 'inception_llc' 57 | # If this is set to nil, the configurator recipe will set it to the server_org. 58 | default[:chef_server_populator][:default_org] = nil 59 | -------------------------------------------------------------------------------- /recipes/org.rb: -------------------------------------------------------------------------------- 1 | include_recipe 'chef-server-populator::configurator' 2 | include_recipe 'chef-server' 3 | 4 | conf_dir = node['chef_server_populator']['base_path'] 5 | 6 | org = node['chef_server_populator']['solo_org'] 7 | user = node['chef_server_populator']['solo_org_user'] 8 | pass = user[:pass] || SecureRandom.urlsafe_base64(23).gsub(/^\-*/, '') 9 | 10 | execute 'create populator user' do 11 | command "chef-server-ctl user-create #{user[:name]} #{user[:first]} #{user[:last]} #{user[:email]} #{pass}" 12 | not_if "chef-server-ctl user-show #{user[:name]}" 13 | end 14 | 15 | execute 'set populator user key' do 16 | if node['chef-server']['version'].to_f >= 12.1 || node['chef-server']['version'].to_f == 0.0 17 | command "chef-server-ctl add-user-key #{user[:name]} --public-key-path #{conf_dir}/#{user[:pub_key]} --key-name populator" 18 | else 19 | command "chef-server-ctl add-user-key #{user[:name]} #{conf_dir}/#{user[:pub_key]} --key-name populator" 20 | end 21 | not_if "chef-server-ctl list-user-keys #{user[:name]} | grep 'name: populator$'" 22 | end 23 | 24 | execute 'delete default user key' do 25 | command "chef-server-ctl delete-user-key #{user[:name]} default" 26 | only_if "chef-server-ctl list-user-keys #{user[:name]} | grep 'name: default$'" 27 | end 28 | 29 | execute 'reconfigure for populator org create' do 30 | command 'chef-server-ctl reconfigure' 31 | action :nothing 32 | end 33 | 34 | execute 'create populator org' do 35 | command "chef-server-ctl org-create #{org[:org_name]} #{org[:full_name]} -a #{user[:name]}" 36 | not_if "chef-server-ctl org-list | grep '^#{org[:org_name]}$'" 37 | if org[:org_name] == node['chef_server_populator']['default_org'] 38 | notifies :run, 'execute[reconfigure for populator org create]', :immediately 39 | end 40 | end 41 | 42 | execute 'add populator org validator key' do 43 | if node['chef-server']['version'].to_f >= 12.1 || node['chef-server']['version'].to_f == 0.0 44 | command "chef-server-ctl add-client-key #{org[:org_name]} #{org[:org_name]}-validator --public-key-path #{conf_dir}/#{org[:validator_pub_key]} --key-name populator" 45 | else 46 | command "chef-server-ctl add-client-key #{org[:org_name]} #{org[:org_name]}-validator #{conf_dir}/#{org[:validator_pub_key]} --key-name populator" 47 | end 48 | not_if "chef-server-ctl list-client-keys #{org[:org_name]} #{org[:org_name]}-validator | grep 'name: populator$'" 49 | end 50 | 51 | execute 'remove populator org default validator key' do 52 | command "chef-server-ctl delete-client-key #{org[:org_name]} #{org[:org_name]}-validator default" 53 | only_if "chef-server-ctl list-client-keys #{org[:org_name]} #{org[:org_name]}-validator | grep 'name: default$'" 54 | end 55 | -------------------------------------------------------------------------------- /recipes/restore.rb: -------------------------------------------------------------------------------- 1 | # Determine if we're using a remote file or a local file. 2 | if URI(node['chef_server_populator']['restore']['file']).scheme 3 | local_file = File.join(node['chef_server_populator']['restore']['local_path'], 'chef_database_restore.dump') 4 | remote_file local_file do 5 | source node['chef_server_populator']['restore']['file'] 6 | end 7 | file = local_file 8 | else 9 | file = node['chef_server_populator']['restore']['file'] 10 | end 11 | 12 | if URI(node['chef_server_populator']['restore']['data']).scheme 13 | local_data = File.join(node['chef_server_populator']['restore']['local_path'], 'chef_data_restore.tar.gz') 14 | remote_file local_data do 15 | source node['chef_server_populator']['restore']['data'] 16 | end 17 | data = local_data 18 | else 19 | data = node['chef_server_populator']['restore']['data'] 20 | end 21 | 22 | execute 'backup chef server stop' do 23 | command 'chef-server-ctl stop' 24 | creates '/etc/opscode/restore.json' 25 | end 26 | 27 | execute 'restore chef server start postgres' do 28 | command 'chef-server-ctl start postgresql' 29 | creates '/etc/opscode/restore.json' 30 | end 31 | 32 | execute 'restore chef server wait for postgresql' do 33 | command 'sleep 10' 34 | creates '/etc/opscode/restore.json' 35 | end 36 | 37 | # Drop and Restore entire chef database from file 38 | execute 'restoring chef data' do 39 | command "/opt/opscode/embedded/bin/psql -f #{file} postgres" 40 | user 'opscode-pgsql' 41 | creates '/etc/opscode/restore.json' 42 | end 43 | 44 | execute 'shutdown postgresql after restore' do 45 | command 'chef-server-ctl stop postgresql' 46 | creates '/etc/opscode/restore.json' 47 | end 48 | 49 | execute 'wait for postgresql to stop' do 50 | command 'sleep 20' 51 | creates '/etc/opscode/restore.json' 52 | end 53 | 54 | execute 'remove existing data' do 55 | command 'rm -rf /var/opt/opscode /etc/opscode' 56 | creates '/etc/opscode/restore.json' 57 | end 58 | 59 | execute 'restore tarball data' do 60 | command "tar xzf #{data} -C /" 61 | creates '/etc/opscode/restore.json' 62 | end 63 | 64 | execute 'restore chef server restart' do 65 | command 'chef-server-ctl restart' 66 | creates '/etc/opscode/restore.json' 67 | end 68 | 69 | execute 'restore chef server wait for opscode-erchef' do 70 | command 'sleep 30' 71 | creates '/etc/opscode/restore.json' 72 | end 73 | 74 | execute 'restore chef server reindex' do 75 | command 'for org in $(chef-server-ctl org-list) ; do chef-server-ctl reindex $org ; done' 76 | creates '/etc/opscode/restore.json' 77 | end 78 | 79 | directory '/etc/opscode' 80 | 81 | file '/etc/opscode/restore.json' do 82 | content JSONCompat.to_json_pretty( 83 | date: Time.now.to_i, 84 | file: file 85 | ) 86 | end 87 | -------------------------------------------------------------------------------- /recipes/configurator.rb: -------------------------------------------------------------------------------- 1 | if node['chef_server_populator']['default_org'] 2 | node.default['chef_server_populator']['chef_server']['configuration'][:default_orgname] = node['chef_server_populator']['default_org'] 3 | end 4 | 5 | unless node['chef_server_populator']['endpoint'] 6 | node.default['chef_server_populator'][:endpoint] = node['chef_server_populator']['servername_override'] 7 | end 8 | 9 | if node['chef_server_populator']['endpoint'] 10 | node.normal['chef-server'][:api_fqdn] = 11 | node.normal['chef_server_populator']['chef_server']['configuration']['nginx'][:server_name] = 12 | node.normal['chef_server_populator']['chef_server']['configuration']['bookshelf'][:vip] = 13 | node.normal['chef_server_populator']['chef_server']['configuration']['lb'][:api_fqdn] = 14 | node.normal['chef_server_populator']['chef_server']['configuration']['lb'][:web_ui_fqdn] = node['chef_server_populator']['endpoint'] 15 | node.normal['chef_server_populator']['chef_server']['configuration']['nginx'][:url] = 16 | node.normal['chef_server_populator']['chef_server']['configuration']['bookshelf'][:url] = "https://#{node['chef_server_populator']['endpoint']}" 17 | else 18 | node.normal['chef-server'][:api_fqdn] = 19 | node.normal['chef_server_populator']['chef_server']['configuration']['nginx'][:server_name] = 20 | node.normal['chef_server_populator']['chef_server']['configuration']['bookshelf'][:vip] = 21 | node.normal['chef_server_populator']['chef_server']['configuration']['lb'][:api_fqdn] = 22 | node.normal['chef_server_populator']['chef_server']['configuration']['lb'][:web_ui_fqdn] = node['fqdn'] 23 | node.normal['chef_server_populator']['chef_server']['configuration']['nginx'][:url] = 24 | node.normal['chef_server_populator']['chef_server']['configuration']['bookshelf'][:url] = "https://#{node['fqdn']}" 25 | end 26 | 27 | mash_maker = lambda do |x| 28 | if x.is_a?(Hash) 29 | x = Mash.new(x) 30 | x.keys.each do |key| 31 | x[key] = mash_maker.call(x[key]) 32 | end 33 | elsif x.is_a?(Array) 34 | x = x.map do |value| 35 | mash_maker.call(value) 36 | end 37 | end 38 | x 39 | end 40 | 41 | current_server_config = mash_maker.call(node['chef-server']) 42 | populator_server_config = mash_maker.call(node['chef_server_populator']['chef_server'] || {}) 43 | 44 | if current_server_config[:configuration].is_a?(Hash) 45 | populator_server_config[:configuration] = Chef::Mixin::DeepMerge.deep_merge( 46 | current_server_config[:configuration], 47 | populator_server_config.fetch(:configuration, Mash.new) 48 | ) 49 | end 50 | 51 | if populator_server_config[:configuration] 52 | populator_server_config[:configuration] = populator_server_config[:configuration].map do |k, v| 53 | "#{k}(#{v.inspect})" 54 | end.join("\n") 55 | end 56 | 57 | current_server_config.delete(:configuration) 58 | 59 | node.normal['chef-server'] = Chef::Mixin::DeepMerge.deep_merge( 60 | current_server_config, 61 | populator_server_config 62 | ) 63 | 64 | include_recipe 'chef-server' 65 | -------------------------------------------------------------------------------- /test/unit/configurator_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'chef-server-populator::configurator' do 4 | let(:fqdn) { 'amazing-chef-server.example.com' } 5 | let(:endpoint) { 'amazing-chef-816064413.us-west-1.elb.amazonaws.com' } 6 | 7 | let(:chef_run) do 8 | ChefSpec::SoloRunner.new do |node| 9 | node.automatic[:fqdn] = fqdn 10 | node.set[:chef_server_populator][:chef_server][:version] = '12.0.5' 11 | node.set[:chef_server_populator][:chef_server][:foo] = 'bar' 12 | end.converge(described_recipe) 13 | end 14 | 15 | it 'overrides chef-server attributes with those from the chef_server_populator.chef_server hash' do 16 | expect(chef_run.node['chef-server']['foo']).to eq('bar') 17 | end 18 | 19 | context 'with a specified endpoint' do 20 | before do 21 | chef_run.node.normal['chef_server_populator'][:endpoint] = endpoint 22 | chef_run.converge(described_recipe) 23 | end 24 | 25 | it 'overrides the values of a number of chef-server attributes with the specified endpoint' do 26 | expect(chef_run.node['chef_server_populator']['chef_server']['configuration']['nginx']['server_name']).to eq(endpoint) 27 | expect(chef_run.node['chef_server_populator']['chef_server']['configuration']['bookshelf']['vip']).to eq(endpoint) 28 | expect(chef_run.node['chef_server_populator']['chef_server']['configuration']['lb']['api_fqdn']).to eq(endpoint) 29 | expect(chef_run.node['chef_server_populator']['chef_server']['configuration']['lb']['web_ui_fqdn']).to eq(endpoint) 30 | 31 | expect(chef_run.node['chef_server_populator']['chef_server']['configuration']['nginx']['url']).to eq("https://#{endpoint}") 32 | expect(chef_run.node['chef_server_populator']['chef_server']['configuration']['bookshelf']['url']).to eq("https://#{endpoint}") 33 | expect(chef_run.node['chef-server']['configuration']).to include(endpoint) 34 | end 35 | end 36 | 37 | context 'without a specified endpoint' do 38 | it 'overrides the values of a number of chef-server attributes with the node\'s fqdn' do 39 | expect(chef_run.node['chef_server_populator']['chef_server']['configuration']['nginx']['server_name']).to eq(fqdn) 40 | expect(chef_run.node['chef_server_populator']['chef_server']['configuration']['bookshelf']['vip']).to eq(fqdn) 41 | expect(chef_run.node['chef_server_populator']['chef_server']['configuration']['lb']['api_fqdn']).to eq(fqdn) 42 | expect(chef_run.node['chef_server_populator']['chef_server']['configuration']['lb']['web_ui_fqdn']).to eq(fqdn) 43 | 44 | expect(chef_run.node['chef_server_populator']['chef_server']['configuration']['nginx']['url']).to eq("https://#{fqdn}") 45 | expect(chef_run.node['chef_server_populator']['chef_server']['configuration']['bookshelf']['url']).to eq("https://#{fqdn}") 46 | expect(chef_run.node['chef-server']['configuration']).to include(fqdn) 47 | end 48 | end 49 | 50 | it 'includes chef-server recipe' do 51 | expect(chef_run).to include_recipe('chef-server') 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /test/unit/backups_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'chef-server-populator::backups' do 4 | let(:data_dir) { '/tmp/chef-backups' } 5 | let(:config_dir) { '/etc/populator/config' } 6 | let(:backup_script) { '/usr/local/bin/chef-server-backup' } 7 | let(:backup_schedule) { Mash.new(hour: '0', minute: '20') } 8 | let(:gems) { Mash.new('hasversion' => '~> 0.1', 'hasnoversion' => nil) } 9 | let(:apt_packages) { %w(gcc libxml2 libxml2-dev libxslt-dev) } 10 | let(:yum_packages) { %w(gcc libxml2 libxml2-devel libxslt libxslt-devel patch) } 11 | 12 | let(:chef_run) do 13 | ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '14.04') do |node| 14 | node.set[:chef_server_populator][:backup][:dir] = data_dir 15 | node.set[:chef_server_populator][:configuration_directory] = config_dir 16 | node.set[:chef_server_populator][:backup_gems] = gems 17 | node.set[:chef_server_populator][:backup][:schedule] = backup_schedule 18 | node.set[:chef_server_populator][:backup][:remote][:connection] = {} 19 | end.converge(described_recipe) 20 | end 21 | 22 | it 'creates a directory for storing backup data' do 23 | expect(chef_run).to create_directory(data_dir) 24 | end 25 | 26 | it 'creates a directory for storing backup script configuration' do 27 | expect(chef_run).to create_directory(config_dir) 28 | end 29 | 30 | it 'installs platform-specific build dependencies for transmitting backup data to a remote service' do 31 | centos_chef_run = ChefSpec::SoloRunner.new(platform: 'centos', version: '6.5') do |node| 32 | node.set[:chef_server_populator][:backup][:remote][:connection] = {} 33 | end.converge(described_recipe) 34 | 35 | apt_packages.each do |pkg| 36 | expect(chef_run).to install_package(pkg) 37 | end 38 | 39 | yum_packages.each do |pkg| 40 | expect(centos_chef_run).to install_package(pkg) 41 | end 42 | end 43 | 44 | context 'when installing gems defined in `backup_gems` node attribute' do 45 | context 'when the gem has a version defined' do 46 | it 'installs the specified version of the gem' do 47 | expect(chef_run).to install_gem_package('hasversion').with(version: '~> 0.1') 48 | end 49 | end 50 | 51 | context 'when the gem has no specified version' do 52 | it 'installs any version of the gem' do 53 | expect(chef_run).to install_gem_package('hasnoversion') 54 | end 55 | end 56 | end 57 | 58 | it 'creates the backup script configuration' do 59 | expect(chef_run).to render_file(File.join(config_dir, 'backup.json')) 60 | end 61 | 62 | it 'creates the backup script' do 63 | expect(chef_run).to render_file(backup_script) 64 | end 65 | 66 | it 'creates a crontab entry for the backup script' do 67 | expect(chef_run).to create_cron('Chef Server Backups').with( 68 | command: backup_script, 69 | minute: backup_schedule[:minute], 70 | hour: backup_schedule[:hour], 71 | path: '/opt/chef/embedded/bin/:/usr/bin:/usr/local/bin:/bin' 72 | ) 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /templates/default/chef-server-backup.rb.erb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'miasma' 4 | require 'multi_json' 5 | require 'mixlib/shellout' 6 | 7 | DEFAULT_CONFIGURATION_PATH = '<%= node[:chef_server_populator][:configuration_directory] %>/backup.json' 8 | 9 | if(ARGV.size > 1 || (ARGV.first && !File.exists?(ARGV.first.to_s))) 10 | puts 'Usage: chef-server-backup CONFIG_FILE_PATH' 11 | exit 12 | else 13 | config = MultiJson.load( 14 | File.read( 15 | ARGV.first || DEFAULT_CONFIGURATION_PATH 16 | ), 17 | :symbolize_keys => true 18 | ) 19 | end 20 | 21 | server_manifest = MultiJson.load( 22 | File.read('/opt/opscode/version-manifest.json'), 23 | :symbolize_keys => true 24 | ) 25 | 26 | prefix = [ 27 | Time.now.to_i, 28 | "ver_#{server_manifest[:build_version]}", 29 | config[:filename] 30 | ].join('-') 31 | 32 | db_file = File.join( 33 | config[:dir], 34 | "#{prefix}.dump" 35 | ) 36 | 37 | data_file = File.join( 38 | config[:dir], 39 | "#{prefix}.tgz" 40 | ) 41 | 42 | # stop services that write data we're backing up 43 | %w( bookshelf opscode-erchef ).each do |svc| 44 | stop_service = Mixlib::ShellOut.new("chef-server-ctl stop #{svc}") 45 | stop_service.run_command 46 | stop_service.error! 47 | end 48 | 49 | begin 50 | backup = Mixlib::ShellOut.new( 51 | "/opt/opscode/embedded/bin/pg_dumpall -c -f #{db_file}", 52 | :user => 'opscode-pgsql' 53 | ) 54 | 55 | backup.run_command 56 | backup.error! 57 | 58 | 59 | backup_data = Mixlib::ShellOut.new( 60 | "tar -czf #{data_file} /var/opt/opscode /etc/opscode" 61 | ) 62 | backup_data.run_command 63 | backup_data.error! 64 | ensure 65 | start_service = Mixlib::ShellOut.new('chef-server-ctl start') 66 | start_service.run_command 67 | start_service.error! 68 | end 69 | 70 | remote_creds = [:remote, :connection].inject(config) do |memo, key| 71 | memo[key] || break 72 | end 73 | 74 | remote_directory = [:remote, :directory].inject(config) do |memo, key| 75 | memo[key] || break 76 | end 77 | 78 | remote_prefix = [:remote, :file_prefix].inject(config) do |memo, key| 79 | memo[key] || break 80 | end 81 | 82 | if(remote_creds) 83 | if(remote_directory) 84 | remote = Miasma.api(:provider => remote_creds[:provider].to_s.downcase, :type => 'storage', :credentials => remote_creds[:credentials]) 85 | directory = remote.buckets.get(remote_directory) 86 | [db_file, data_file].each do |file| 87 | [ :date_stamped, :latest ].each do |type| 88 | remote_file = Miasma::Models::Storage::File.new(directory) 89 | remote_file.name = File.join(*[ 90 | remote_prefix, 91 | type == :date_stamped ? File.basename(file) : "latest#{File.extname(file)}" 92 | ].flatten.compact 93 | ) 94 | remote_file.body = File.open(file, 'r') 95 | remote_file.save 96 | end 97 | end 98 | else 99 | $stderr.puts 'ERROR: No remote directory defined for backup storage!' 100 | exit -1 101 | end 102 | else 103 | puts 'WARN: No remote credentials found. Backup is local only!' 104 | end 105 | -------------------------------------------------------------------------------- /.kitchen.yml: -------------------------------------------------------------------------------- 1 | --- 2 | driver: 3 | name: vagrant 4 | customize: 5 | memory: 2048 6 | 7 | provisioner: 8 | name: chef_solo 9 | 10 | platforms: 11 | - name: ubuntu-14.04 12 | run_list: 13 | - recipe[apt] 14 | - name: ubuntu-12.04 15 | run_list: 16 | - recipe[apt] 17 | - name: centos-6.6 18 | - name: centos-6.4 19 | driver: 20 | box: opscode-centos-6.4 21 | box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-6.4_chef-provisionerless.box 22 | 23 | suites: 24 | - name: default 25 | run_list: 26 | - recipe[test] 27 | - recipe[chef-server-populator] 28 | attributes: 29 | chef_server_populator: 30 | endpoint: localhost 31 | solo_org: 32 | validator_pub_key: validator_pub.pem 33 | org_users: 34 | pub_key: user_pub.pem 35 | clients: 36 | test-node: client_key_pub.pem 37 | - name: default-specific-chef 38 | run_list: 39 | - recipe[test] 40 | - recipe[chef-server-populator] 41 | attributes: 42 | chef-server: 43 | version: 12.5.0 44 | chef_server_populator: 45 | endpoint: localhost 46 | solo_org: 47 | validator_pub_key: validator_pub.pem 48 | org_users: 49 | pub_key: user_pub.pem 50 | clients: 51 | test-node: client_key_pub.pem 52 | - name: default-old-chef 53 | run_list: 54 | - recipe[test] 55 | - recipe[chef-server-populator] 56 | attributes: 57 | chef-server: 58 | version: 12.0.7 59 | chef_server_populator: 60 | endpoint: localhost 61 | solo_org: 62 | validator_pub_key: validator_pub.pem 63 | org_users: 64 | pub_key: user_pub.pem 65 | clients: 66 | test-node: client_key_pub.pem 67 | - name: data-bag 68 | provisioner: 69 | name: chef_zero 70 | data_bags_path: test/fixtures/data_bags 71 | run_list: 72 | - recipe[test] 73 | - recipe[chef-server-populator] 74 | attributes: 75 | chef_server_populator: 76 | endpoint: localhost 77 | databag: chef 78 | - name: data-bag-old-chef 79 | provisioner: 80 | name: chef_zero 81 | data_bags_path: test/fixtures/data_bags 82 | run_list: 83 | - recipe[test] 84 | - recipe[chef-server-populator] 85 | attributes: 86 | chef-server: 87 | version: 12.0.7 88 | chef_server_populator: 89 | endpoint: localhost 90 | databag: chef 91 | - name: backups 92 | run_list: 93 | - recipe[test] 94 | - recipe[build-essential] 95 | - recipe[chef-server-populator] 96 | - recipe[chef-server-populator::backups] 97 | attributes: 98 | chef_server_populator: 99 | endpoint: localhost 100 | solo_org: 101 | validator_pub_key: validator_pub.pem 102 | org_users: 103 | pub_key: user_pub.pem 104 | clients: 105 | test-node: client_key_pub.pem 106 | backup: 107 | remote: 108 | connection: false 109 | directory: false 110 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # chef-server-populator CHANGELOG 2 | 3 | ## Unreleased changes 4 | 5 | ## v2.0.2 6 | * Configure $PATH to fix backups unit test 7 | * Don't create users if no public key is set 8 | * Use correct client-key flag when no version is set 9 | * Update chef-server dependency b/c packagecloud repos are gone 10 | * Add Nellie for CI 11 | 12 | ## v2.0.0 13 | * Support chef-server 4.x versions 14 | * Make chef-server constraint tighter to prevent breakage 15 | * Remove resource notifications to resources outside our recipes 16 | * Refactor configuration setup to support hash style attribute and auto-convert to string 17 | 18 | ## v1.2.2 19 | * Adds test for user key 20 | * Fixes org recipe user key in 12.1.x 21 | 22 | ## v1.2.0 23 | * Adds support for both 12.0.x and 12.1.x versions, client key 24 | commands got a new flag which broke things. 25 | * Prevents randomly generated passwords from starting with a '-' which 26 | will register as a flag and cause an error. 27 | * Fixes backup recipe when using remote backups 28 | * Fixes backup recipe when running as cron 29 | * Updates and extends the integration tests to cover backups 30 | 31 | ## v1.1.4 32 | * Fixes all users created as admins 33 | * Updates user creation to require explicit enabled setting 34 | * Updates client admin to default to false 35 | 36 | ## v1.1.2 37 | * Adds myriad unit tests. 38 | * Fixes issue #23 where users were not assigned to orgs. 39 | * Fixes issue #22 where client recipe failed if no chef_server hash set. 40 | 41 | ## v1.1.0 42 | * Fixes and loosens idempotency tests to account for Chef Server 43 | version differences 44 | * Updates Client recipe to create orgs, then users, then clients 45 | * Fixes non-existent org attribute in solo recipe 46 | * Fixes missing user keys in test suite 47 | * Moves common org/user/client creation specs to the spec helper 48 | * Refactors Backup/Restore to work with Chef 12. Uses Miasma rather 49 | than Fog. 50 | 51 | ## v1.0.2 52 | * Org recipe only included for solo run, since client run expects data 53 | bag items. 54 | 55 | ## v1.0.0 56 | * Updates to support Chef 12 57 | * Removes support for Chef 11 58 | * Adds support for organization creation in solo and client contexts 59 | * Updates backup/restore recipes for new psql path and new table & 60 | field names. (Not fully tested) 61 | * Replaces many knife and psql commands with native chef-server-clt 62 | management commands 63 | 64 | ## v0.4.0 65 | * Allow for creation of clients, users, or both 66 | * Store backup configuration in separate JSON file 67 | * Provide proper retries to account for temporary server unavailability 68 | * Include full server restart on restore from backup 69 | * Provide 'latest' backup files along with named files 70 | * Convert backup script from template to cookbook file 71 | * Make service restarts more consistent 72 | 73 | ## v0.3.2 74 | * Add client creation retries to stabilize initial bootstrap 75 | * Updates to example bootstrap script 76 | * Add support for backup/restore (thanks @luckymike!) 77 | 78 | ## v0.3.0 79 | * Include chef-server dependency 80 | * Update configuration overrides for chef-server 81 | * Use `:endpoint` attribute for custom hostname/ip 82 | 83 | ## v0.2.0 84 | * Provide distinct solo vs. client recipes 85 | * Client recipe configures dna.json and uses ctl for reconfigure 86 | -------------------------------------------------------------------------------- /test/unit/restore_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | 3 | describe 'chef-server-populator::restore' do 4 | let(:restore_path) { '/tmp/chef_restore' } 5 | let(:restore_lock) { '/etc/opscode/restore.json' } 6 | let(:db_restore_user) { 'opscode-pgsql' } 7 | let(:chef_run) do 8 | ChefSpec::SoloRunner.new do |node| 9 | node.set[:chef_server_populator][:restore][:local_path] = restore_path 10 | end.converge(described_recipe) 11 | end 12 | 13 | context 'when provided a URL for the database dump' do 14 | it 'downloads the remote file' do 15 | chef_run.node.normal['chef_server_populator']['restore'][:file] = 'https://www.example.com/restore.dump' 16 | chef_run.converge(described_recipe) 17 | expect(chef_run).to create_remote_file(File.join(restore_path, 'chef_database_restore.dump')) 18 | end 19 | end 20 | 21 | context 'when provided a URL for the tarball' do 22 | it 'downloads the remote file' do 23 | chef_run.node.normal['chef_server_populator']['restore'][:data] = 'https://www.example.com/restore.tgz' 24 | chef_run.converge(described_recipe) 25 | expect(chef_run).to create_remote_file(File.join(restore_path, 'chef_data_restore.tar.gz')) 26 | end 27 | end 28 | 29 | context 'when provided a local path for the database dump' do 30 | it 'does not download a remote file' do 31 | expect(chef_run).to_not create_remote_file(File.join(restore_path, 'chef_data_restore.dump')) 32 | end 33 | end 34 | 35 | context 'when provided a local path for the tarball' do 36 | it 'does not download a remote file' do 37 | expect(chef_run).to_not create_remote_file(File.join(restore_path, 'chef_data_restore.tar.gz')) 38 | end 39 | end 40 | 41 | it 'stops all chef server services before restoring' do 42 | expect(chef_run).to run_execute('backup chef server stop').with( 43 | creates: restore_lock 44 | ) 45 | end 46 | 47 | it 'starts postgres before restoring' do 48 | expect(chef_run).to run_execute('restore chef server start postgres').with( 49 | creates: restore_lock 50 | ) 51 | end 52 | 53 | it 'restores database dump to postgres' do 54 | expect(chef_run).to run_execute('restoring chef data').with( 55 | user: db_restore_user, 56 | creates: restore_lock 57 | ) 58 | end 59 | 60 | it 'removes existing data' do 61 | expect(chef_run).to run_execute('remove existing data').with( 62 | creates: restore_lock 63 | ) 64 | end 65 | 66 | it 'restores data from tarball' do 67 | expect(chef_run).to run_execute('restore tarball data').with( 68 | creates: restore_lock 69 | ) 70 | end 71 | 72 | it 'restarts all chef server services' do 73 | expect(chef_run).to run_execute('restore chef server restart').with( 74 | creates: restore_lock 75 | ) 76 | end 77 | 78 | it 'pauses to give opscode-erchef time to start' do 79 | expect(chef_run).to run_execute('restore chef server wait for opscode-erchef').with( 80 | creates: restore_lock 81 | ) 82 | end 83 | 84 | it 'reindexes all orgs on the server' do 85 | expect(chef_run).to run_execute('restore chef server reindex').with( 86 | creates: restore_lock 87 | ) 88 | end 89 | 90 | it 'creates directory for restore lockfile' do 91 | expect(chef_run).to create_directory(File.dirname(restore_lock)) 92 | end 93 | 94 | it 'creates restore lockfile' do 95 | expect(chef_run).to render_file(restore_lock) 96 | end 97 | end 98 | -------------------------------------------------------------------------------- /recipes/solo.rb: -------------------------------------------------------------------------------- 1 | if node['chef_server_populator']['default_org'].nil? 2 | node.default['chef_server_populator'][:default_org] = node['chef_server_populator']['server_org'] 3 | end 4 | 5 | include_recipe 'chef-server-populator::configurator' 6 | 7 | # if backup pull files include restore 8 | 9 | if node['chef_server_populator']['backup']['remote']['connection'] 10 | chef_gem 'miasma' 11 | require 'miasma' 12 | remote_creds = node['chef_server_populator']['backup']['remote']['connection'] 13 | remote_directory = node['chef_server_populator']['backup']['remote']['directory'] 14 | remote = Miasma.api(provider: remote_creds[:provider].to_s.downcase, type: 'storage', credentials: remote_creds[:credentials]) 15 | remote_bucket = remote.buckets.get(remote_directory) 16 | if remote_bucket && gz_file = remote_bucket.files.get(File.join(node['chef_server_populator']['backup']['remote']['file_prefix'], 'latest.tgz')) 17 | dump_file = remote_bucket.files.get(File.join(node['chef_server_populator']['backup']['remote']['file_prefix'], 'latest.dump')) 18 | local_gz = '/tmp/latest.tgz' 19 | local_dump = '/tmp/latest.dump' 20 | File.open(local_gz, 'wb') do |file| 21 | while (data = gz_file.body.readpartial(2048)) 22 | file.print data 23 | end 24 | end 25 | File.open(local_dump, 'wb') do |file| 26 | while (data = dump_file.body.readpartial(2048)) 27 | file.print data 28 | end 29 | end 30 | node.normal['chef_server_populator']['restore'][:file] = local_dump 31 | node.normal['chef_server_populator']['restore'][:data] = local_gz 32 | end 33 | end 34 | 35 | if local_gz && local_dump 36 | 37 | include_recipe 'chef-server-populator::restore' 38 | 39 | else 40 | 41 | include_recipe 'chef-server-populator::org' 42 | 43 | knife_cmd = "#{node['chef_server_populator']['knife_exec']}" 44 | knife_opts = "-s https://127.0.0.1/organizations/#{node['chef_server_populator']['server_org']} -c /etc/opscode/pivotal.rb" 45 | 46 | node['chef_server_populator']['clients'].each do |client, pub_key| 47 | execute "create client: #{client}" do 48 | command "#{knife_cmd} client create #{client} --admin -d #{knife_opts} > /dev/null 2>&1" 49 | not_if "#{knife_cmd} client list #{knife_opts}| tr -d ' ' | grep '^#{client}$'" 50 | retries 5 51 | end 52 | next unless pub_key && node['chef_server_populator']['base_path'] 53 | pub_key_path = File.join(node['chef_server_populator']['base_path'], pub_key) 54 | execute "remove default public key for #{client}" do 55 | command "chef-server-ctl delete-client-key #{node['chef_server_populator']['server_org']} #{client} default" 56 | only_if "chef-server-ctl list-client-keys #{node['chef_server_populator']['server_org']} #{client} | grep 'name: default$'" 57 | end 58 | execute "set public key for: #{client}" do 59 | if node['chef-server']['version'].to_f >= 12.1 || node['chef-server']['version'].to_f == 0.0 60 | command "chef-server-ctl add-client-key #{node['chef_server_populator']['server_org']} #{client} --public-key-path #{pub_key_path} --key-name populator" 61 | else 62 | command "chef-server-ctl add-client-key #{node['chef_server_populator']['server_org']} #{client} #{pub_key_path} --key-name populator" 63 | end 64 | not_if "chef-server-ctl list-client-keys #{node['chef_server_populator']['server_org']} #{client} | grep 'name: populator$'" 65 | end 66 | end 67 | 68 | execute 'install chef-server-populator cookbook' do 69 | command "#{knife_cmd} cookbook upload chef-server-populator #{knife_opts} -o #{Chef::Config[:cookbook_path].join(':')} --include-dependencies" 70 | only_if do 71 | node['chef_server_populator']['cookbook_auto_install'] 72 | end 73 | retries 5 74 | end 75 | 76 | end 77 | -------------------------------------------------------------------------------- /examples/chef-server.erb: -------------------------------------------------------------------------------- 1 | ##### Example bootstrap template ##### 2 | # This bootstrap will create a brand new erchef server and 3 | # use your existing settings to set things up. It will automatically 4 | # disable the webui, create a client named after the client in 5 | # your existing configuration, and update the public keys for your 6 | # client, as well as the chef-validator. Afterwards, it will install 7 | # the cookbooks it requires for itself, and then register itself with 8 | # itself. How meta. 9 | # 10 | # NOTE: It uses the node IP address for configuration, rather than the 11 | # FQDN. You can see it being done down there in the json building. The 12 | # example is based on eth0. Modify or remove as needed. 13 | 14 | sudo bash -c ' 15 | 16 | curl -L https://www.opscode.com/chef/install.sh | bash 17 | mkdir -p /var/chef/cache /var/chef/cookbooks/chef-server /var/chef/cookbooks/chef-server-populator 18 | wget -qO- https://github.com/opscode-cookbooks/chef-server/archive/master.tar.gz | tar xvzC /var/chef/cookbooks/chef-server --strip-components=1 19 | wget -qO- https://github.com/hw-cookbooks/chef-server-populator/tarball/master | tar xvzC /var/chef/cookbooks/chef-server-populator --strip-components=1 20 | 21 | mkdir -p /tmp/chef-server-setup 22 | ( 23 | cat <<'EOP' 24 | <%= IO.read(Chef::Config[:validation_key]) %> 25 | EOP 26 | ) > /tmp/chef-server-setup/validation.pem 27 | ( 28 | cat <<'EOP' 29 | <%= %x{openssl rsa -in #{Chef::Config[:validation_key]} -pubout} %> 30 | EOP 31 | ) > /tmp/chef-server-setup/validation_pub.pem 32 | ( 33 | cat <<'EOP' 34 | <%= %x{openssl rsa -in #{Chef::Config[:client_key]} -pubout} %> 35 | EOP 36 | ) > /tmp/chef-server-setup/client_key_pub.pem 37 | 38 | mkdir -p /etc/chef 39 | cp /tmp/chef-server-setup/validation.pem /etc/chef/validation.pem 40 | chmod 600 /etc/chef/validation.pem 41 | 42 | IPADDR=`curl http://169.254.169.254/latest/meta-data/public-ipv4` 43 | if [ $? -ne 0 ] 44 | then 45 | IPADDR=`ip addr show eth0 | grep "inet " | awk -F" " '\''{print $2}'\'' | sed -e "s/\/..//"` 46 | fi 47 | 48 | ( 49 | echo " 50 | { 51 | \"chef-server\": { 52 | \"version\": \"11.0.8\", 53 | \"configuration\": { 54 | \"chef_server_webui\": { 55 | \"enable\": false 56 | } 57 | } 58 | }, 59 | \"chef_server_populator\": { 60 | \"clients\": { 61 | \"<%= Chef::Config[:node_name] %>\": \"client_key_pub.pem\", 62 | \"<%= Chef::Config[:validation_client_name] %>\": \"validation_pub.pem\" 63 | }, 64 | \"servername_override\": \"${IPADDR}\", 65 | \"base_path\": \"/tmp/chef-server-setup\" 66 | }, 67 | \"run_list\": [ \"recipe[chef-server]\", \"recipe[chef-server-populator]\" ] 68 | } 69 | " 70 | ) > /tmp/chef-server.json 71 | 72 | chef-solo -j /tmp/chef-server.json 73 | 74 | rm -rf /tmp/chef-server-setup 75 | 76 | ( 77 | cat <<'EOP' 78 | log_level :info 79 | log_location STDOUT 80 | chef_server_url "https://127.0.0.1" 81 | validation_client_name "<%= Chef::Config[:validation_client_name] %>" 82 | force_logger true 83 | <% if @config[:chef_node_name] == nil %> 84 | # Using default node name 85 | <% else %> 86 | node_name "<%= @config[:chef_node_name] %>" 87 | <% end %> 88 | EOP 89 | ) > /etc/chef/client.rb 90 | 91 | ( 92 | echo " 93 | { 94 | \"chef_server\": { 95 | \"chef_server_webui\": { 96 | \"enable\": false 97 | } 98 | }, 99 | \"chef_server_populator\": { 100 | \"servername_override\": \"${IPADDR}\" 101 | }, 102 | \"run_list\": [ \"recipe[chef-server-populator]\" ] 103 | } 104 | " 105 | ) > /etc/chef/first-boot.json 106 | 107 | <%= start_chef %> 108 | echo "**********************************************" 109 | echo "* NOTE: Update chef_server_url in knife.rb! *" 110 | echo "* IP: ${IPADDR} *" 111 | echo "**********************************************" 112 | ' 113 | -------------------------------------------------------------------------------- /examples/chef-12-server.erb: -------------------------------------------------------------------------------- 1 | ##### Example bootstrap template ##### 2 | # This bootstrap will create a brand new chef 12 server and 3 | # use your existing settings to set things up. It will create 4 | # a client named after the client in 5 | # your existing configuration, and update the public keys for your 6 | # client, as well as the chef-validator. Afterwards, it will install 7 | # the cookbooks it requires for itself, and then register itself with 8 | # itself. How meta. 9 | # 10 | # NOTE: It uses the node IP address for configuration, rather than the 11 | # FQDN. You can see it being done down there in the json building. The 12 | # example is based on eth0. Modify or remove as needed. 13 | 14 | sudo bash -c ' 15 | 16 | curl -L https://www.chef.io/chef/install.sh | bash 17 | mkdir -p /var/chef/cache /var/chef/cookbooks /var/chef/cookbooks/chef-server-populator 18 | wget -qO- https://supermarket.chef.io/cookbooks/chef-server/download | tar xvzC /var/chef/cookbooks/ 19 | wget -qO- https://supermarket.chef.io/cookbooks/chef-server-ingredient/download | tar xvzC /var/chef/cookbooks/ 20 | wget -qO- https://supermarket.chef.io/cookbooks/packagecloud/download | tar xvzC /var/chef/cookbooks/ 21 | wget -qO- https://github.com/hw-cookbooks/chef-server-populator/tarball/feature/chef-12 | tar xvzC /var/chef/cookbooks/chef-server-populator --strip-components=1 22 | 23 | mkdir -p /tmp/chef-server-setup 24 | ( 25 | cat <<'EOP' 26 | <%= IO.read(Chef::Config[:validation_key]) %> 27 | EOP 28 | ) > /tmp/chef-server-setup/validator.pem 29 | ( 30 | cat <<'EOP' 31 | <%= %x{openssl rsa -in #{Chef::Config[:validation_key]} -pubout} %> 32 | EOP 33 | ) > /tmp/chef-server-setup/validator_pub.pem 34 | ( 35 | cat <<'EOP' 36 | <%= %x{openssl rsa -in #{Chef::Config[:client_key]} -pubout} %> 37 | EOP 38 | ) > /tmp/chef-server-setup/user_pub.pem 39 | 40 | mkdir -p /etc/chef 41 | cp /tmp/chef-server-setup/validator.pem /etc/chef/validator.pem 42 | chmod 600 /etc/chef/validator.pem 43 | 44 | IPADDR=`curl http://169.254.169.254/latest/meta-data/public-ipv4` 45 | if [ $? -ne 0 ] 46 | then 47 | IPADDR=`ip addr show eth0 | grep "inet " | awk -F" " '\''{print $2}'\'' | sed -e "s/\/..//"` 48 | fi 49 | 50 | ( 51 | echo " 52 | { 53 | \"chef-server\": { 54 | \"version\": \"12.0.5-1\", 55 | \"api_fqdn\": \"${IPADDR}\" 56 | }, 57 | \"chef_server_populator\": { 58 | \"orgs\": { 59 | \"inception_llc\": { 60 | \"validator_pub_key\": \"validator_pub.pem\" 61 | } 62 | }, 63 | \"org_users\": { 64 | \"inception_llc\": { 65 | \"pub_key\": \"user_pub.pem\" 66 | } 67 | }, 68 | \"servername_override\": \"${IPADDR}\", 69 | \"base_path\": \"/tmp/chef-server-setup\" 70 | }, 71 | \"run_list\": [ \"recipe[chef-server-populator]\" ] 72 | } 73 | " 74 | ) > /tmp/chef-server.json 75 | 76 | chef-solo -j /tmp/chef-server.json 77 | 78 | rm -rf /tmp/chef-server-setup 79 | 80 | ( 81 | cat <<'EOP' 82 | log_level :info 83 | log_location STDOUT 84 | chef_server_url "https://127.0.0.1/organizations/inception_llc" 85 | validation_client_name "inception_llc-validator" 86 | force_logger true 87 | ssl_verify_mode :verify_none 88 | <% if @config[:chef_node_name] == nil %> 89 | # Using default node name 90 | <% else %> 91 | node_name "<%= @config[:chef_node_name] %>" 92 | <% end %> 93 | EOP 94 | ) > /etc/chef/client.rb 95 | 96 | ( 97 | echo " 98 | { 99 | \"chef_server_populator\": { 100 | \"servername_override\": \"${IPADDR}\" 101 | }, 102 | \"run_list\": [ \"recipe[chef-server-populator]\" ] 103 | } 104 | " 105 | ) > /etc/chef/first-boot.json 106 | 107 | <%= start_chef %> 108 | echo "**********************************************" 109 | echo "* NOTE: Update chef_server_url in knife.rb! *" 110 | echo "* IP: https://${IPADDR} *" 111 | echo "**********************************************" 112 | ' 113 | -------------------------------------------------------------------------------- /examples/chef-server-restore.erb: -------------------------------------------------------------------------------- 1 | ##### Example bootstrap template ##### 2 | # This bootstrap will create a new erchef server and restore it using 3 | # a backup of your existing chef server. It will automatically 4 | # disable the webui, restore the entire chef database, and reset the public 5 | # key for the local admin user. It will also restore all the cookbooks 6 | # from the previous/existing chef server, so all cookbook versions will be 7 | # available. Finally, it will register itself with itself. How meta. 8 | # 9 | # This expects two remote files, one a is pg_dump of your existing chef server 10 | # database, and one is a tarball of the bookshelf files from that chef server. 11 | # 12 | # NOTE: It uses the node IP address for configuration, rather than the 13 | # FQDN. You can see it being done down there in the json building. The 14 | # example is based on eth0. Modify or remove as needed. 15 | 16 | sudo bash -c ' 17 | 18 | curl -L https://www.opscode.com/chef/install.sh | bash 19 | mkdir -p /var/chef/cache /var/chef/cookbooks/chef-server /var/chef/cookbooks/chef-server-populator 20 | wget -qO- https://github.com/opscode-cookbooks/chef-server/archive/master.tar.gz | tar xvzC /var/chef/cookbooks/chef-server --strip-components=1 21 | wget -qO- https://github.com/hw-cookbooks/chef-server-populator/tarball/master | tar xvzC /var/chef/cookbooks/chef-server-populator --strip-components=1 22 | 23 | mkdir -p /tmp/chef-server-setup 24 | ( 25 | cat <<'EOP' 26 | <%= IO.read(Chef::Config[:validation_key]) %> 27 | EOP 28 | ) > /tmp/chef-server-setup/validation.pem 29 | ( 30 | cat <<'EOP' 31 | <%= %x{openssl rsa -in #{Chef::Config[:validation_key]} -pubout} %> 32 | EOP 33 | ) > /tmp/chef-server-setup/validation_pub.pem 34 | ( 35 | cat <<'EOP' 36 | <%= %x{openssl rsa -in #{Chef::Config[:client_key]} -pubout} %> 37 | EOP 38 | ) > /tmp/chef-server-setup/client_key_pub.pem 39 | 40 | mkdir -p /etc/chef 41 | cp /tmp/chef-server-setup/validation.pem /etc/chef/validation.pem 42 | chmod 600 /etc/chef/validation.pem 43 | 44 | IPADDR=`curl http://169.254.169.254/latest/meta-data/public-ipv4` 45 | if [ $? -ne 0 ] 46 | then 47 | IPADDR=`ip addr show eth0 | grep "inet " | awk -F" " '\''{print $2}'\'' | sed -e "s/\/..//"` 48 | fi 49 | 50 | ( 51 | echo " 52 | { 53 | \"chef-server\": { 54 | \"version\": \"11.0.8\", 55 | \"configuration\": { 56 | \"chef_server_webui\": { 57 | \"enable\": false 58 | } 59 | } 60 | }, 61 | \"chef_server_populator\": { 62 | \"restore\": { 63 | \"file\": \"\", 64 | \"data\": \"\" 65 | }, 66 | \"clients\": { 67 | \"<%= Chef::Config[:node_name] %>\": \"client_key_pub.pem\", 68 | \"<%= Chef::Config[:validation_client_name] %>\": \"validation_pub.pem\" 69 | }, 70 | \"servername_override\": \"${IPADDR}\", 71 | \"base_path\": \"/tmp/chef-server-setup\" 72 | }, 73 | \"run_list\": [ \"recipe[chef-server]\", \"recipe[chef-server-populator]\" ] 74 | } 75 | " 76 | ) > /tmp/chef-server.json 77 | 78 | chef-solo -j /tmp/chef-server.json 79 | 80 | rm -rf /tmp/chef-server-setup 81 | 82 | ( 83 | cat <<'EOP' 84 | log_level :info 85 | log_location STDOUT 86 | chef_server_url "https://127.0.0.1" 87 | validation_client_name "<%= Chef::Config[:validation_client_name] %>" 88 | force_logger true 89 | <% if @config[:chef_node_name] == nil %> 90 | # Using default node name 91 | <% else %> 92 | node_name "<%= @config[:chef_node_name] %>" 93 | <% end %> 94 | EOP 95 | ) > /etc/chef/client.rb 96 | 97 | ( 98 | echo " 99 | { 100 | \"chef_server\": { 101 | \"chef_server_webui\": { 102 | \"enable\": false 103 | } 104 | }, 105 | \"chef_server_populator\": { 106 | \"servername_override\": \"${IPADDR}\" 107 | }, 108 | \"run_list\": [ \"recipe[chef-server-populator]\" ] 109 | } 110 | " 111 | ) > /etc/chef/first-boot.json 112 | 113 | <%= start_chef %> 114 | echo "**********************************************" 115 | echo "* NOTE: Update chef_server_url in knife.rb! *" 116 | echo "* IP: ${IPADDR} *" 117 | echo "**********************************************" 118 | ' 119 | -------------------------------------------------------------------------------- /test/unit/org_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | 3 | describe 'chef-server-populator::org' do 4 | let(:default_org) { 'nasa' } 5 | 6 | let(:test_org) do 7 | Mash.new( 8 | org_name: 'endurance', 9 | full_name: 'Endurance Shuttle Mission', 10 | validator_pub_key: 'validation_pub.pem' 11 | ) 12 | end 13 | 14 | let(:test_org_user) do 15 | Mash.new( 16 | name: 'murph', 17 | first: 'Murphy', 18 | last: 'Cooper', 19 | email: 'murph@nasa.gov' 20 | ) 21 | end 22 | 23 | let(:list_user_keys_cmd) do 24 | "chef-server-ctl list-user-keys #{test_org_user[:name]}" 25 | end 26 | 27 | let(:list_validator_keys_cmd) do 28 | "chef-server-ctl list-client-keys #{test_org[:org_name]} #{test_org[:org_name]}-validator" 29 | end 30 | 31 | let(:chef_run) do 32 | ChefSpec::ServerRunner.new do |node, _server| 33 | node.set[:chef_server_populator][:solo_org] = test_org 34 | node.set[:chef_server_populator][:solo_org_user] = test_org_user 35 | node.set[:chef_server_populator][:default_org] = default_org 36 | end.converge(described_recipe) 37 | end 38 | 39 | let(:execute_create_populator_org) do 40 | chef_run.execute('create populator org') 41 | end 42 | 43 | before do 44 | stub_command("chef-server-ctl user-show #{test_org_user[:name]}").and_return(false) 45 | stub_command("#{list_user_keys_cmd} | grep 'name: populator$'").and_return(false) 46 | stub_command("#{list_user_keys_cmd} | grep 'name: default$'").and_return(false) 47 | stub_command("chef-server-ctl org-list | grep '^#{test_org[:org_name]}$'").and_return(false) 48 | stub_command("#{list_validator_keys_cmd} | grep 'name: populator$'").and_return(false) 49 | stub_command("#{list_validator_keys_cmd} | grep 'name: default$'").and_return(false) 50 | stub_command("chef-server-ctl list-client-keys #{test_org[:org_name]} #{test_org[:org_name]}-validator | grep 'name: populator$'").and_return(false) 51 | stub_command("chef-server-ctl list-client-keys #{test_org[:org_name]} #{test_org[:org_name]}-validator | grep 'name: default$'").and_return(true) 52 | end 53 | 54 | it 'overrides the chef-server default_orgname' do 55 | expect(chef_run.node['chef-server']['configuration']).to include(default_org) 56 | end 57 | 58 | it 'creates the populator user' do 59 | expect(chef_run).to run_execute('create populator user') 60 | end 61 | 62 | context 'when the populator user has a default key' do 63 | it 'deletes the populator user\'s default key' do 64 | stub_command("#{list_user_keys_cmd} | grep 'name: default$'").and_return(true) 65 | chef_run.converge(described_recipe) 66 | expect(chef_run).to run_execute('delete default user key') 67 | end 68 | end 69 | 70 | context 'when the populator user does not have a default key' do 71 | it 'does not delete the populator user\'s default key' do 72 | expect(chef_run).to_not run_execute('delete default user key') 73 | end 74 | end 75 | 76 | context 'when the populator org does not exist' do 77 | it 'creates the populator organization' do 78 | expect(chef_run).to run_execute('create populator org') 79 | end 80 | 81 | context 'when the populator org is also the default org' do 82 | it 'notifies chef-server to reconfigure immediately' do 83 | chef_run.node.normal['chef_server_populator'][:default_org] = test_org[:org_name] 84 | chef_run.converge(described_recipe) 85 | expect(execute_create_populator_org).to notify('execute[reconfigure for populator org create]').to(:run).immediately 86 | end 87 | end 88 | end 89 | 90 | context 'when the populator org does not have a "populator" validator key' do 91 | it 'adds a validator key for the populator org' do 92 | stub_command("#{list_validator_keys_cmd} | grep 'name: populator$'").and_return(false) 93 | expect(chef_run).to run_execute('add populator org validator key') 94 | end 95 | end 96 | 97 | context 'when the populator org has a "populator" validator key' do 98 | it 'does not add a validator key for the populator org' do 99 | stub_command("#{list_validator_keys_cmd} | grep 'name: populator$'").and_return(true) 100 | chef_run.converge(described_recipe) 101 | expect(chef_run).to_not run_execute('add populator org validator key') 102 | end 103 | end 104 | 105 | context 'when the populator org has a default validator key' do 106 | it 'removes the populator default validator key' do 107 | stub_command("#{list_validator_keys_cmd} | grep 'name: default$'").and_return(true) 108 | chef_run.converge(described_recipe) 109 | expect(chef_run).to run_execute('remove populator org default validator key') 110 | end 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Chef Server Populator 2 | 3 | Creates orgs, clients, and admin users and installs provided public keys. Simplifies managing and 4 | recreating Chef Server nodes. Provides backup and restore recipes for 5 | complete Chef Server recovery. 6 | 7 | ### New Chef 12 Support 8 | Chef 12 is supported in version 1.0 and above. If you need Chef 11 9 | support, please pin your environment to version 0.4.0. 10 | 11 | ### Usage 12 | 13 | **When bootstrapping with the chef-server cookbook and chef-solo:** 14 | 15 | * Download and unpack chef-server, chef-server-ingredient, packagecloud, and chef-server-populator cookbooks 16 | * Upload public keys to be used by users, org-validator, and clients (optionally) 17 | * Create json for organization, user, and (optionally) client(s) 18 | * Run chef-solo 19 | 20 | See the `default[:chef_server_populator][:solo_org]` and 21 | `default[:chef_server_populator][:solo_org_user]` attribute hashes in 22 | `attributes/default.rb` for the required attribute structure. 23 | 24 | **When converging with chef-client:** 25 | 26 | * Create data bag to hold data bag items with user, org, and client information 27 | * Create data bag items with user, org, and client information 28 | * Set data bag related attributes 29 | 30 | Applicable attributes: 31 | 32 | * `node[:chef_server_populator][:databag]` - name of the data bag 33 | 34 | Structure of the data bag item: 35 | 36 | User: 37 | ```json 38 | { 39 | "id": "user_name", 40 | "chef_server": { 41 | "full_name": "User Name", 42 | "email": "name@domain.tld", 43 | "client_key": "public key contents", 44 | "type": [ 45 | "user" 46 | ], 47 | "orgs": { 48 | "organization": { 49 | "enabled": true, 50 | "admin": true 51 | } 52 | } 53 | } 54 | } 55 | ``` 56 | Note: While users can belong to multiple organizations, and the above 57 | hash structure allows you to define multiple associations, the 58 | chef-server-populator currently only supports the first organization 59 | that is defined in the data bag. 60 | 61 | Client: 62 | ```json 63 | { 64 | "id": "client_name", 65 | "chef_server": { 66 | "client_key": "public key contents", 67 | "type": [ 68 | "client" 69 | ], 70 | "orgs": [ "organization" ] 71 | } 72 | } 73 | ``` 74 | Note: If no organization is specified for a client, it will be added 75 | to the default organization. The client `enabled` and `admin` settings 76 | can be set at the top level of the `chef_server` hash or in and `orgs` 77 | hash as in the User example. 78 | 79 | Org: 80 | ```json 81 | { 82 | "id": "org_name", 83 | "chef_server": { 84 | "full_name": "Organization Name", 85 | "client_key": "public key contents", 86 | "type": [ 87 | "org" 88 | ], 89 | "enabled": true 90 | } 91 | } 92 | ``` 93 | Note: Creating the org will create a client called `-validator` which uses the public key specified when 94 | creating the org. 95 | In addition, there is currently a bug in Chef server 12.1 which means only the first word in 96 | the full name will be used, as the option is not parsed correctly 97 | 98 | **Restoring from a backup:** 99 | 100 | * Set path to restore file with node[:chef_server_populator][:restore][:file] 101 | * The restore recipe is run if a restore file is set 102 | * The restore file can be remote or local 103 | 104 | **When enabling backups:** 105 | 106 | * Include chef-server-populator::restore recipe 107 | * Set backup cron interval with node[:chef_server_populator][:schedule] 108 | * Optionally set a remote storage location with node[:chef_server_populator][:backup][:remote][:connection] 109 | * Backups include both a pg_dump of the entire chef database and a tarball of the Chef data directory 110 | 111 | ## Public Key Format 112 | 113 | The format of the public key specified with the json object needs to be a single line string with new lines 114 | represented with the \n character 115 | 116 | You can use one of the below commands to convert your public key file into the correct string format (credit 117 | to the certificates cookbook for these) 118 | ``` 119 | cat | sed s/$/\\\\n/ | tr -d '\n' 120 | -OR- 121 | /usr/bin/env ruby -e 'p ARGF.read' 122 | -OR- 123 | perl -pe 's!(\x0d)?\x0a!\\n!g' 124 | ``` 125 | If you need to obtain the public key string for your private key first, then run the following on the .pem 126 | file containing the private key 127 | ``` 128 | openssl rsa -in .pem -pubout 129 | ``` 130 | 131 | ## Extras 132 | 133 | Need to use the IP address of the node for a bit, or another name instead of 134 | having `node[:fqdn]`? 135 | 136 | * `node[:chef_server_populator][:servername_override]` 137 | 138 | Keep chef server configured via chef client: 139 | 140 | * `node[:chef_server_populator][:chef_server]` 141 | 142 | If the hash is non-empty, it will write the chef-server `dna.json` and trigger a 143 | `reconfigure` when ever the attributes are updated. 144 | 145 | ## Known Issues 146 | 147 | * As mentioned above, user and client data bag items currently only 148 | support the first organization provided. Multi-org support is 149 | forthcoming. 150 | 151 | ## Examples 152 | 153 | Take a look in the `examples` directory for basic bootstrap templates that will 154 | build a new erchef server, using existing keys and client, and 155 | register itself, or restore an existing chef server from a backup. 156 | 157 | ## Info 158 | * Repository: https://github.com/hw-cookbooks/chef-server-populator 159 | * IRC: Freenode @ #heavywater 160 | -------------------------------------------------------------------------------- /test/unit/client_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | 3 | describe 'chef-server-populator::client' do 4 | let(:populator_data_bag) { 'populator' } 5 | 6 | let(:knife_cmd) { '/opt/chef/bin/knife' } 7 | let(:knife_opts) { '-c /etc/opscode/pivotal.rb' } 8 | let(:list_clients_cmd) { "#{knife_cmd} client list #{knife_opts}" } 9 | let(:list_client_keys_cmd) { 'chef-server-ctl list-client-keys' } 10 | let(:list_user_keys_cmd) { 'chef-server-ctl list-user-keys' } 11 | let(:list_orgs_cmd) { 'chef-server-ctl org-list' } 12 | let(:test_user_name) { 'murph' } 13 | let(:test_user_pub_key_path) { "#{Chef::Config[:file_cache_path]}/#{test_user_name}.pub" } 14 | let(:test_user_item) do 15 | Mash.new( 16 | 'chef_server' => { 17 | 'email' => 'murphcooper@nasa.gov', 18 | 'full_name' => 'Murphy Cooper', 19 | 'enabled' => true, 20 | 'password' => 'vHeBu6baW4PXIKVNDsE-APweIVpdLLU', 21 | 'client_key' => 'a-non-empty-rsa-key', 22 | 'type' => ['user'], 23 | 'orgs' => { 24 | 'nasa' => { 25 | 'enabled' => true, 26 | 'admin' => true, 27 | }, 28 | }, 29 | } 30 | ) 31 | end 32 | let(:keyless_user_name) { 'amelia' } 33 | let(:keyless_user_item) do 34 | Mash.new( 35 | 'chef_server' => { 36 | 'email' => 'ameliabrand@nasa.gov', 37 | 'full_name' => 'Amelia Brand', 38 | 'enabled' => true, 39 | 'password' => 'vHeBu6baW4PXIKVNDsE-APweIVpdLLU', 40 | 'client_key' => '', 41 | 'type' => ['user'], 42 | 'orgs' => { 43 | 'nasa' => { 44 | 'enabled' => true, 45 | 'admin' => true, 46 | }, 47 | }, 48 | } 49 | ) 50 | end 51 | 52 | before do 53 | stub_command(/#{list_clients_cmd} | tr -d ' ' | grep '^(.*)$'/).and_return(false) 54 | stub_command(/#{list_client_keys_cmd} .* | grep 'name: populator$'/).and_return(false) 55 | stub_command(/#{list_orgs_cmd} | grep \'\^.*\$\'/).and_return(false) 56 | stub_command("#{list_user_keys_cmd} #{test_user_name} | grep 'name: populator$'").and_return(false) 57 | stub_command("#{list_user_keys_cmd} #{test_user_name} | grep 'name: default$'").and_return(true) 58 | end 59 | 60 | let(:chef_run) do 61 | ChefSpec::ServerRunner.new do |node, server| 62 | node.set[:chef_server_populator][:databag] = populator_data_bag 63 | server.create_data_bag(populator_data_bag, { 64 | test_user_name => test_user_item, 65 | 'case' => { 66 | 'chef_server' => { 67 | 'enabled' => true, 68 | 'client_key' => '', 69 | 'type' => ['client'], 70 | }, 71 | 'orgs' => ['nasa'], 72 | }, 73 | 'tars' => { 74 | 'chef_server' => { 75 | 'enabled' => true, 76 | 'client_key' => '', 77 | 'type' => ['client'], 78 | }, 79 | 'orgs' => ['endeavor'], 80 | }, 81 | 'nasa' => { 82 | 'chef_server' => { 83 | 'full_name' => 'National Aeronautics and Space Administration', 84 | 'client_key' => '', 85 | 'type' => ['org'], 86 | 'enabled' => true, 87 | }, 88 | }, 89 | 'endeavor' => { 90 | 'chef_server' => { 91 | 'full_name' => 'Endeavor Space Mission', 92 | 'client_key' => '', 93 | 'type' => ['org'], 94 | 'enabled' => true, 95 | }, 96 | }, 97 | 'millers_planet' => { 98 | 'chef_server' => { 99 | 'full_name' => 'Miller\'s Planet', 100 | 'client_key' => '', 101 | 'type' => ['org'], 102 | 'enabled' => false, 103 | }, 104 | }, 105 | }) 106 | end.converge(described_recipe) 107 | end 108 | 109 | context 'when an organization is defined in the data bag' do 110 | context 'when the organization is enabled' do 111 | it 'creates the organization' do 112 | expect(chef_run).to run_execute('create org: nasa').with( 113 | command: 'chef-server-ctl org-create nasa National Aeronautics and Space Administration' 114 | ) 115 | end 116 | 117 | it 'adds the organziation validator key' do 118 | expect(chef_run).to run_execute('add org validator key: nasa').with( 119 | command: "chef-server-ctl add-client-key nasa nasa-validator --public-key-path #{Chef::Config[:file_cache_path]}/nasa.pub --key-name populator" 120 | ) 121 | end 122 | end 123 | 124 | context 'when the organization is disabled' do 125 | it 'does not create the organization' do 126 | expect(chef_run).to_not run_execute('create org: millers_planet') 127 | end 128 | end 129 | end 130 | 131 | context 'a user is defined in the data bag' do 132 | context 'the user is enabled' do 133 | context 'the user has a client key specified' do 134 | it 'creates the user' do 135 | expect(chef_run).to run_execute("create user: #{test_user_name}").with( 136 | command: "chef-server-ctl user-create #{test_user_name} #{test_user_item['chef_server']['full_name'].split(' ').first} #{test_user_item['chef_server']['full_name'].split(' ').last} #{test_user_item['chef_server']['email']} #{test_user_item['chef_server']['password']} > /dev/null 2>&1" 137 | ) 138 | end 139 | 140 | it 'creates the user\'s client key file' do 141 | expect(chef_run).to create_file(test_user_pub_key_path).with( 142 | content: test_user_item['chef_server']['client_key'] 143 | ) 144 | end 145 | 146 | it 'sets inserts the client key as the user\'s populator key' do 147 | expect(chef_run).to run_execute("set user key: #{test_user_name}").with( 148 | command: "chef-server-ctl add-user-key #{test_user_name} --public-key-path #{test_user_pub_key_path} --key-name populator" 149 | ) 150 | end 151 | 152 | it 'deletes the user\'s default key' do 153 | expect(chef_run).to run_execute("delete default user key: #{test_user_name}") 154 | end 155 | end 156 | 157 | context 'the user has an org specified' do 158 | end 159 | end 160 | 161 | context 'the user does not have a client key specified' do 162 | it 'skips the user' do 163 | expect(chef_run).to_not run_execute("create user: #{keyless_user_name}") 164 | end 165 | end 166 | end 167 | 168 | context 'when a client is defined in the data bag' do 169 | end 170 | end 171 | -------------------------------------------------------------------------------- /test/unit/solo_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | 3 | describe 'chef-server-populator::solo' do 4 | let(:server_org) { 'nasa' } 5 | let(:default_org) { 'endurance' } 6 | let(:base_path) { '/tmp/populator' } 7 | let(:endpoint) { 'amazing-chef-816064413.us-west-1.elb.amazonaws.com' } 8 | 9 | let(:test_org) do 10 | Mash.new( 11 | org_name: 'endurance', 12 | full_name: 'Endurance Shuttle Mission', 13 | validator_pub_key: 'validation_pub.pem' 14 | ) 15 | end 16 | 17 | let(:test_org_user) do 18 | Mash.new( 19 | name: 'murph', 20 | first: 'Murphy', 21 | last: 'Cooper', 22 | email: 'murph@nasa.gov' 23 | ) 24 | end 25 | 26 | let(:list_user_keys_cmd) { "chef-server-ctl list-user-keys #{test_org_user[:name]}" } 27 | 28 | let(:list_validator_keys_cmd) { "chef-server-ctl list-client-keys #{test_org[:org_name]} #{test_org[:org_name]}-validator" } 29 | 30 | let(:list_client_keys_cmd) { "chef-server-ctl list-client-keys #{server_org} #{test_org_user[:name]}" } 31 | 32 | let(:list_validator_client_keys) { "chef-server-ctl list-client-keys #{test_org[:org_name]} #{test_org[:org_name]}-validator" } 33 | 34 | let(:chef_run) do 35 | ChefSpec::SoloRunner.new do |node| 36 | node.set[:chef_server_populator][:server_org] = server_org 37 | node.set[:chef_server_populator][:default_org] = default_org 38 | node.set[:chef_server_populator][:solo_org] = test_org 39 | node.set[:chef_server_populator][:solo_org_user] = test_org_user 40 | end.converge(described_recipe) 41 | end 42 | 43 | let(:execute_create_populator_org) { chef_run.execute('create populator org') } 44 | 45 | before do 46 | stub_command("chef-server-ctl user-show #{test_org_user[:name]}").and_return(false) 47 | stub_command("#{list_user_keys_cmd} | grep 'name: #{test_org_user[:name]}$'").and_return(false) 48 | stub_command("#{list_user_keys_cmd} | grep 'name: default$'").and_return(false) 49 | stub_command("#{list_user_keys_cmd} | grep 'name: populator$'").and_return(false) 50 | stub_command("chef-server-ctl org-list | grep '^#{test_org[:org_name]}$'").and_return(false) 51 | stub_command("#{list_validator_client_keys} | grep 'name: populator$'").and_return(false) 52 | stub_command("#{list_validator_client_keys} | grep 'name: default$'").and_return(true) 53 | stub_command("#{list_client_keys_cmd} | grep 'name: default$'").and_return(false) 54 | stub_command("/usr/bin/knife client list -s https://127.0.0.1/organizations/#{server_org} -c /etc/opscode/pivotal.rb| tr -d ' ' | grep '^#{test_org_user[:name]}$'").and_return(false) 55 | stub_command("#{list_client_keys_cmd} | grep 'name: populator$'").and_return(false) 56 | end 57 | 58 | it 'includes the configurator recipe' do 59 | expect(chef_run).to include_recipe('chef-server-populator::configurator') 60 | end 61 | 62 | context 'without a default_org specified' do 63 | before do 64 | chef_run.node.normal['chef_server_populator'][:default_org] = nil 65 | chef_run.converge(described_recipe) 66 | end 67 | 68 | it 'assigns the server_org as the default org' do 69 | expect(chef_run.node['chef_server_populator']['default_org']).to eq(server_org) 70 | end 71 | end 72 | 73 | it 'includes the org recipe' do 74 | expect(chef_run).to include_recipe('chef-server-populator::org') 75 | end 76 | 77 | # The following tests cover behavior in the 'org' recipe. They are included in this spec 78 | # because: 79 | # 80 | # a) placing these tests in a seperate spec made us unable to see the chef_server_ingredient 81 | # resource inserted by including the configurator recipe in the 'solo' recipe, 82 | # making it difficult to ensure a required notification to such a resource is triggered. 83 | # 84 | # b) the only intended use for the 'org' recipe is to run in sequence with the rest of the 'solo' 85 | # recipe described here. 86 | # 87 | 88 | it 'overrides the chef-server default_orgname' do 89 | expect(chef_run.node['chef-server']['configuration']).to include(default_org) 90 | end 91 | 92 | it 'creates the populator user' do 93 | expect(chef_run).to run_execute('create populator user') 94 | end 95 | 96 | context 'with a specified endpoint' do 97 | before do 98 | chef_run.node.normal['chef_server_populator'][:endpoint] = endpoint 99 | chef_run.converge(described_recipe) 100 | end 101 | 102 | it 'overrides the chef server endpoints to specified endpoint' do 103 | expect(chef_run.node['chef-server']['configuration']).to include(endpoint) 104 | end 105 | end 106 | 107 | context 'when the populator user has a default key' do 108 | before do 109 | stub_command("#{list_user_keys_cmd} | grep 'name: default$'").and_return(true) 110 | end 111 | 112 | it 'deletes the populator user\'s default key' do 113 | expect(chef_run).to run_execute('delete default user key') 114 | end 115 | end 116 | 117 | context 'when the populator user does not have a default key' do 118 | before do 119 | stub_command("#{list_user_keys_cmd} | grep 'name: default$'").and_return(false) 120 | end 121 | 122 | it 'does not delete the populator user\'s default key' do 123 | expect(chef_run).to_not run_execute('delete default user key') 124 | end 125 | end 126 | 127 | context 'when the populator org does not exist' do 128 | it 'creates the populator organization' do 129 | expect(chef_run).to run_execute('create populator org') 130 | end 131 | 132 | context 'when the populator org is also the default org' do 133 | before do 134 | chef_run.node.normal['chef_server_populator'][:default_org] = test_org[:org_name] 135 | chef_run.converge(described_recipe) 136 | end 137 | 138 | it 'notifies chef-server to reconfigure immediately' do 139 | expect(execute_create_populator_org).to notify('execute[reconfigure for populator org create]').to(:run).immediately 140 | end 141 | end 142 | end 143 | 144 | context 'when the populator org does not have a "populator" validator key' do 145 | before do 146 | stub_command("#{list_validator_keys_cmd} | grep 'name: populator$'").and_return(false) 147 | end 148 | 149 | it 'adds a validator key for the populator org' do 150 | expect(chef_run).to run_execute('add populator org validator key') 151 | end 152 | end 153 | 154 | context 'when the populator org has a "populator" validator key' do 155 | before do 156 | stub_command("#{list_validator_keys_cmd} | grep 'name: populator$'").and_return(true) 157 | end 158 | 159 | it 'does not add a validator key for the populator org' do 160 | expect(chef_run).to_not run_execute('add populator org validator key') 161 | end 162 | end 163 | 164 | context 'when the populator org has a default validator key' do 165 | before do 166 | stub_command("#{list_validator_keys_cmd} | grep 'name: default$'").and_return(true) 167 | end 168 | 169 | it 'removes the populator default validator key' do 170 | expect(chef_run).to run_execute('remove populator org default validator key') 171 | end 172 | end 173 | 174 | # End tests covering 'org' recipe 175 | 176 | context 'for each client defined in attributes' do 177 | before do 178 | chef_run.node.normal['chef_server_populator'][:clients] = { 179 | test_org_user[:name] => "-----BEGIN PUBLIC KEY-----\n-----END PUBLIC KEY-----\n", 180 | } 181 | 182 | stub_command("#{list_client_keys_cmd} | grep 'name: default$'").and_return(false) 183 | chef_run.converge(described_recipe) 184 | end 185 | 186 | it 'creates a client for each client defined in attributes' do 187 | expect(chef_run).to run_execute("create client: #{test_org_user[:name]}") 188 | end 189 | 190 | context 'when the client has a default key on the server' do 191 | before do 192 | stub_command("#{list_client_keys_cmd} | grep 'name: default$'").and_return(true) 193 | chef_run.converge(described_recipe) 194 | end 195 | 196 | it 'removes the client\'s default public key' do 197 | expect(chef_run).to run_execute("remove default public key for #{test_org_user[:name]}") 198 | end 199 | end 200 | 201 | it 'sets the client\'s public key' do 202 | expect(chef_run).to run_execute("set public key for: #{test_org_user[:name]}") 203 | end 204 | end 205 | 206 | it 'uploads the chef-server-populator cookbook to the new Chef server' do 207 | expect(chef_run).to run_execute('install chef-server-populator cookbook').with( 208 | retries: 5 209 | ) 210 | end 211 | end 212 | -------------------------------------------------------------------------------- /recipes/client.rb: -------------------------------------------------------------------------------- 1 | include_recipe 'chef-server-populator::configurator' 2 | 3 | knife_cmd = "#{node['chef_server_populator']['knife_exec']}" 4 | knife_opts = '-c /etc/opscode/pivotal.rb' 5 | 6 | ssl_port = %w(chef-server configuration nginx ssl_port).inject(node) do |memo, key| 7 | memo[key] || break 8 | end 9 | ssl_port = ":#{ssl_port}" if ssl_port 10 | 11 | pg_cmd = '/opt/chef-server/embedded/bin/psql -d opscode_chef' 12 | 13 | if node['chef_server_populator']['databag'] 14 | begin 15 | items = data_bag(node['chef_server_populator']['databag']).map do |bag_item| 16 | item = data_bag_item(node['chef_server_populator']['databag'], bag_item).fetch('chef_server', {}) 17 | if item.empty? 18 | Chef::Log.info("No chef-server data for #{bag_item['id']}") 19 | end 20 | item.merge('client' => data_bag_item(node['chef_server_populator']['databag'], bag_item)['id'], 21 | 'pub_key' => item['client_key'], 22 | 'enabled' => item['enabled'], 23 | 'admin' => item.fetch('admin', false), 24 | 'password' => item.fetch('password', SecureRandom.urlsafe_base64(23).gsub(/^\-*/, '')), 25 | 'orgs' => item.fetch('orgs', {})) 26 | end 27 | orgs = items.select { |item| item.fetch('type', []).include?('org') } 28 | users = items.select { |item| item.fetch('type', []).include?('user') } 29 | clients = items.select { |item| item.fetch('type', []).include?('client') } 30 | 31 | # Org Setup 32 | orgs.each do |item| 33 | if item['enabled'] == true 34 | item['full_name'] = item.fetch('full_name', item['client'].capitalize) 35 | execute "create org: #{item['client']}" do 36 | item.merge('full_name' => item.fetch('full_name', item['client'].capitalize)) 37 | command "chef-server-ctl org-create #{item['client']} #{item['full_name']}" 38 | not_if "chef-server-ctl org-list | grep '^#{item['client']}$'" 39 | if item['client'] == node['chef_server_populator']['default_org'] 40 | notifies :reconfigure, 'chef_server_ingredient[chef-server-core]', :immediately 41 | end 42 | end 43 | if item['pub_key'] 44 | key_file = "#{Chef::Config[:file_cache_path]}/#{item['client']}.pub" 45 | file key_file do 46 | backup false 47 | content item['pub_key'] 48 | mode '0400' 49 | end 50 | end 51 | execute "add org validator key: #{item['client']}" do 52 | if node['chef-server']['version'].to_f >= 12.1 || node['chef-server']['version'].to_f == 0.0 53 | command "chef-server-ctl add-client-key #{item['client']} #{item['client']}-validator --public-key-path #{key_file} --key-name populator" 54 | else 55 | command "chef-server-ctl add-client-key #{item['client']} #{item['client']}-validator #{key_file} --key-name populator" 56 | end 57 | not_if "chef-server-ctl list-client-keys #{item['client']} #{item['client']}-validator | grep 'name: populator$'" 58 | end 59 | execute "remove org default validator key: #{item['client']}" do 60 | command "chef-server-ctl delete-client-key #{item['client']} #{item['client']}-validator default" 61 | only_if "chef-server-ctl list-client-keys #{item['client']} #{item['client']}-validator | grep 'name: default$'" 62 | end 63 | else 64 | Chef::Log.info("#{item['client']} is not enabled, skipping.") 65 | end 66 | end 67 | # User Setup 68 | users.each do |item| 69 | org, options = item['orgs'].first 70 | item['org'] = org 71 | if options 72 | if options.has_key?('enabled') 73 | item['enabled'] = options['enabled'] 74 | end 75 | if options.has_key?('admin') 76 | item['admin'] = options['admin'] 77 | end 78 | end 79 | if item['enabled'] == false 80 | execute "remove user: #{item['client']} from #{item['org']}" do 81 | command "chef-server-ctl org-user-remove #{item['org']} #{item['client']}" 82 | end 83 | execute "delete user: #{item['client']}" do 84 | command "chef-server-ctl user-delete #{item['client']}" 85 | only_if "chef-server-list user-list | tr -d ' ' | grep '^#{item['client']}$'" 86 | end 87 | elsif item['enabled'] == true 88 | if item['pub_key'] 89 | unless item['pub_key'].to_s.empty? 90 | key_file = "#{Chef::Config[:file_cache_path]}/#{item['client']}.pub" 91 | file key_file do 92 | backup false 93 | content item['pub_key'] 94 | mode '0400' 95 | end 96 | item['full_name'] = item.fetch('full_name', item['client'].capitalize) 97 | first_name = item['full_name'].split(' ').first.capitalize 98 | last_name = item['full_name'].split(' ').last.capitalize 99 | email = item.fetch('email', "#{item['client']}@example.com") 100 | execute "create user: #{item['client']}" do 101 | command "chef-server-ctl user-create #{item['client']} #{first_name} #{last_name} #{email} #{item['password']} > /dev/null 2>&1" 102 | not_if "chef-server-ctl user-list | grep '^#{item['client']}$'" 103 | end 104 | execute "set user key: #{item['client']}" do 105 | if node['chef-server']['version'].to_f >= 12.1 || node['chef-server']['version'].to_f == 0.0 106 | command "chef-server-ctl add-user-key #{item['client']} --public-key-path #{key_file} --key-name populator" 107 | else 108 | command "chef-server-ctl add-user-key #{item['client']} #{key_file} --key-name populator" 109 | end 110 | not_if "chef-server-ctl list-user-keys #{item['client']} | grep 'name: populator$'" 111 | end 112 | execute "delete default user key: #{item['client']}" do 113 | command "chef-server-ctl delete-user-key #{item['client']} default" 114 | only_if "chef-server-ctl list-user-keys #{item['client']} | grep 'name: default$'" 115 | end 116 | execute "set user org: #{item['client']}" do 117 | command "chef-server-ctl org-user-add #{item['org']} #{item['client']} #{'--admin' if item['admin']}" 118 | end 119 | end 120 | end 121 | end 122 | end 123 | # Client Setup 124 | clients.each do |item| 125 | org, options = item['orgs'].first 126 | knife_url = if org 127 | "-s https://127.0.0.1/organizations/#{org}" 128 | else 129 | '-s https://127.0.0.1' 130 | end 131 | if options 132 | if options.has_key?('enabled') 133 | item[:enabled] = options[:enabled] 134 | end 135 | if options.has_key?('admin') 136 | item[:admin] = options[:admin] 137 | end 138 | end 139 | if item['enabled'] == false 140 | execute "delete client: #{item['client']}" do 141 | command "#{knife_cmd} client delete #{item['client']} -d #{knife_opts} #{knife_url}" 142 | only_if "#{knife_cmd} client list #{knife_opts} #{knife_url} | tr -d ' ' | grep '^#{item['client']}$'" 143 | retries 10 144 | end 145 | else 146 | if item['pub_key'] 147 | key_file = "#{Chef::Config[:file_cache_path]}/#{item['client']}.pub" 148 | file key_file do 149 | backup false 150 | content item['pub_key'] 151 | mode '0400' 152 | end 153 | end 154 | execute "create client: #{item['client']}" do 155 | command "#{knife_cmd} client create #{item['client']}#{' --admin' if item['admin']} -d #{knife_url} #{knife_opts}" 156 | not_if "#{knife_cmd} client list #{knife_url} #{knife_opts} | tr -d ' ' | grep '^#{item['client']}$'" 157 | retries 10 158 | end 159 | if item['pub_key'] 160 | execute "set client key: #{item['client']}" do 161 | if node['chef-server']['version'].to_f >= 12.1 || node['chef-server']['version'].to_f == 0.0 162 | command "chef-server-ctl add-client-key #{org || node['chef_server_populator']['default_org']} #{item['client']} --public-key-path #{key_file} --key-name populator" 163 | else 164 | command "chef-server-ctl add-client-key #{org || node['chef_server_populator']['default_org']} #{item['client']} #{key_file} --key-name populator" 165 | end 166 | not_if "chef-server-ctl list-client-keys #{org || node['chef_server_populator']['default_org']} #{item['client']} | grep 'name: populator$'" 167 | end 168 | execute "delete default client key: #{item['client']}" do 169 | command "chef-server-ctl delete-client-key #{org || node['chef_server_populator']['default_org']} #{item['client']} default" 170 | only_if "chef-server-ctl list-client-keys #{org || node['chef_server_populator']['default_org']} #{item['client']} | grep 'name: default$'" 171 | end 172 | end 173 | end 174 | end 175 | rescue Net::HTTPServerException 176 | Chef::Log.warn 'Chef server populator failed to locate population data bag' 177 | end 178 | end 179 | --------------------------------------------------------------------------------