Skip to content
This repository has been archived by the owner on Jan 10, 2023. It is now read-only.

Commit

Permalink
Merge pull request #126 from legal90/redesign-data-bags
Browse files Browse the repository at this point in the history
  • Loading branch information
linc01n committed Feb 13, 2016
1 parent 8903b14 commit ea25775
Show file tree
Hide file tree
Showing 11 changed files with 193 additions and 95 deletions.
58 changes: 28 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,37 +192,35 @@ port | Tomcat HTTP port | Fixnum | 7990

### Stash Server Data Bag

For securely overriding attributes on Hosted Chef, create a `stash/stash` encrypted data bag with the model below. Chef Solo can override the same attributes with a `stash/stash` unencrypted data bag of the same information.

_required:_
* `['database']['type']` "hsqldb" (not recommended), "mysql", "postgresql", or "sqlserver"
* `['database']['host']` FQDN or "127.0.0.1" (127.0.0.1 automatically
installs `['database']['type']` server)
* `['database']['name']` Name of Stash database
* `['database']['user']` Stash database username
* `['database']['password']` Stash database username password

_optional:_
* `['backup_client']['user']` Stash administrative username for backup client
* `['backup_client']['password']` Stash administrative password for backup client
* `['database']['port']` Database port, standard database port for
`['database']['type']`
* `['plugin']['KEY']` plugin.`KEY`=`VALUE` to be inserted in stash-config.properties

Repeat for other Chef environments as necessary. Example:

{
"id": "stash"
"development": {
"database": {
"type": "postgresql",
"host": "127.0.0.1",
"name": "stash",
"user": "stash",
"password": "stash_db_password",
}
}
For security purposes it is recommended to use data bag for storing secrets
like passwords and database credentials.

You can override any attributes from the `['stash']` namespace using the
`stash/stash` data bag. It could be either encrypted or not
encrypted by your choice.

Example:
```json
{
"id": "stash",
"stash": {
"database": {
"type": "postgresql",
"host": "127.0.0.1",
"name": "stash",
"user": "stash",
"password": "stash_db_password",
}
}
}
```
_(Note - `"stash"` nesting level is required!)_

These credentials will be used for your stash installation instead of
appropriate attribute values.

Data bag's and item's names are optional and can be changed by overriding
attributes `['stash']['data_bag_name']` and `['stash']['data_bag_item']`

### Stash Server Default Installation

Expand Down
4 changes: 4 additions & 0 deletions attributes/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@
when '4.3.2' then '7d29f1dc5960547528856d54a2d498be5e3220d741a8500e6160f08bc4dec2b3'
end

# Data bag where credentials and other sensitive data could be stored (optional)
default['stash']['data_bag_name'] = 'stash'
default['stash']['data_bag_item'] = 'stash'

default['stash']['apache2']['access_log'] = ''
default['stash']['apache2']['error_log'] = ''
default['stash']['apache2']['port'] = 80
Expand Down
105 changes: 51 additions & 54 deletions libraries/stash.rb
Original file line number Diff line number Diff line change
@@ -1,67 +1,64 @@
# Chef class
class Chef
# Chef::Recipe class
class Recipe
# Chef::Recipe::Stash class
class Stash
# rubocop:disable Metrics/AbcSize
def self.settings(node)
begin
if Chef::Config[:solo]
begin
databag_item = Chef::DataBagItem.load('stash', 'stash')['local']
rescue
Chef::Log.info('No stash data bag found')
end
else
begin
databag_item = Chef::EncryptedDataBagItem.load('stash', 'stash')[node.chef_environment]
rescue
Chef::Log.info('No stash encrypted data bag found')
end
end
ensure
databag_item ||= {}
settings = Chef::Mixin::DeepMerge.deep_merge(databag_item, node['stash'].to_hash)
settings['database']['port'] ||= Stash.default_database_port(settings['database']['type'])
end

settings
end
# rubocop:enable Metrics/AbcSize
module Stash
# Stash::Library module
module Library
# Merges Stash settings from data bag and node attributes.
# Data dag settings always has a higher priority.
#
# @return [Hash] Settings hash
def merge_stash_settings
@settings_from_data_bag ||= settings_from_data_bag
settings = Chef::Mixin::DeepMerge.deep_merge(
@settings_from_data_bag,
node['stash'].to_hash)

def self.default_database_port(type)
case type
when 'mysql'
3306
when 'postgresql'
5432
when 'sqlserver'
1433
else
Chef::Log.warn("Unsupported database type (#{type}) in Stash cookbook.")
Chef::Log.warn('Please add to Stash cookbook or hard set Stash database port.')
nil
settings['database']['port'] ||=
case settings['database']['type']
when 'mysql' then 3306
when 'postgresql' then 5432
when 'sqlserver' then 1433
else fatal "Unsupported database type: #{settings['database']['type']}"
end
end

def self.check_for_old_attributes!(node)
backup_attrs = %w(backup_path baseurl password user cron)
show_warn = false
backup_attrs.each do |attr|
next if node['stash']['backup_client'][attr].nil?
settings
end

node.default['stash']['backup'][attr] = node['stash']['backup_client'][attr]
Chef::Log.warn "node['stash']['backup_client']['#{attr}'] has been changed to node['stash']['backup']['#{attr}']"
show_warn = true
end
Chef::Log.warn <<-EOH if show_warn
# rubocop:disable Metrics/AbcSize
def handle_old_stash_attributes!
backup_attrs = %w(backup_path baseurl password user cron)
show_warn = false
backup_attrs.each do |attr|
next if node['stash']['backup_client'][attr].nil?

node.default['stash']['backup'][attr] = node['stash']['backup_client'][attr]
Chef::Log.warn "node['stash']['backup_client']['#{attr}'] has been changed to node['stash']['backup']['#{attr}']"
show_warn = true
end
Chef::Log.warn <<-EOH if show_warn
This renaming introduces the common approach for both of backup strategies:
'backup_client' and 'backup_diy'. Attributes mentioned above will be gracefully
converted for you, but this warning and conversion will be removed in the next
major release of the 'stash' cookbook.
EOH
end
# rubocop:enable Metrics/AbcSize

private

# Fetches Stash settings from the data bag
#
# @return [Hash] Settings hash
def settings_from_data_bag
begin
item = data_bag_item(node['stash']['data_bag_name'],
node['stash']['data_bag_item'])['stash']
return item if item.is_a?(Hash)
rescue
Chef::Log.info('No stash data bag found')
end
{}
end
end
end

::Chef::Recipe.send(:include, Stash::Library)
::Chef::Resource.send(:include, Stash::Library)
4 changes: 2 additions & 2 deletions recipes/backup_client.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Stash.check_for_old_attributes!(node)
handle_old_stash_attributes!

settings = Stash.settings(node)
settings = merge_stash_settings

package 'unzip'
package 'rsync'
Expand Down
4 changes: 2 additions & 2 deletions recipes/backup_client_cron.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Stash.check_for_old_attributes!(node)
handle_old_stash_attributes!

Chef::Log.warn <<-EOH
Recipe 'stash::backup_client_cron' is deprecated. Please, set attrite
Expand All @@ -7,7 +7,7 @@
of the 'stash' cookbook.
EOH

settings = Stash.settings(node)
settings = settings = merge_stash_settings

cron_d 'atlassian-stash-backup-client' do
hour settings['backup']['cron']['hour']
Expand Down
4 changes: 2 additions & 2 deletions recipes/backup_diy.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Stash.check_for_old_attributes!(node)
handle_old_stash_attributes!

settings = Stash.settings(node)
settings = merge_stash_settings

include_recipe 'yum-epel' if node['platform_family'] == 'rhel'

Expand Down
2 changes: 1 addition & 1 deletion recipes/configuration.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
settings = Stash.settings(node)
settings = merge_stash_settings
stash_version = Chef::Version.new(node['stash']['version'])

# Config path changed to shared/ from 3.2.0
Expand Down
2 changes: 1 addition & 1 deletion recipes/database.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
settings = Stash.settings(node)
settings = merge_stash_settings

database_connection = {
:host => settings['database']['host'],
Expand Down
2 changes: 1 addition & 1 deletion recipes/default.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
platform = 'windows' if node['platform_family'] == 'windows'
platform ||= 'linux'
settings = Stash.settings(node)
settings = merge_stash_settings

if node['platform_family'] == 'rhel' && node['platform_version'].to_f < 7
include_recipe 'git::source'
Expand Down
2 changes: 1 addition & 1 deletion recipes/linux_standalone.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
settings = Stash.settings(node)
settings = merge_stash_settings

directory File.dirname(node['stash']['home_path']) do
owner 'root'
Expand Down
101 changes: 100 additions & 1 deletion spec/database_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,105 @@

describe 'stash::database' do
let(:chef_run) do
ChefSpec::Runner.new.converge(described_recipe)
ChefSpec::SoloRunner.new do |node|
node.set['stash']['data_bag_name'] = 'apps'
node.set['stash']['data_bag_item'] = 'test_stash'
node.set['stash']['database']['name'] = 'test_database'
node.set['stash']['database']['user'] = 'foo'
node.set['stash']['database']['password'] = 'bar'
node.set['mysql']['server_root_password'] = 'mysql_root_pass'
node.set['postgresql']['password']['postgres'] = 'postgres_pass'
end.converge(described_recipe)
end

before do
# Required for "postgresql::server" converge
stub_command(%r{ls \/.*\/recovery.conf}).and_return(false)
end

it 'raises an error unsupported database type is specified' do
chef_run.node.set['stash']['database']['type'] = 'unicorn'
expect { chef_run.converge(described_recipe) }.to raise_error
end

context 'When data bag does not exit' do
let(:connection) do
{
host: '127.0.0.1',
port: 5432,
username: 'postgres',
password: 'postgres_pass'
}
end

it 'sets up PostgreSQL service' do
expect(chef_run).to include_recipe('postgresql::server')
expect(chef_run).to include_recipe('database::postgresql')
end

it 'creates PostgreSQL user' do
expect(chef_run).to create_postgresql_database_user('foo').with(
password: 'bar',
connection: connection
)
end

it 'creates PostgeSQL database' do
expect(chef_run).to create_postgresql_database('test_database').with(
encoding: 'utf8',
connection: connection
)
end
end

context 'When data bag exists' do
before do
data_bag = {
'id' => 'test_stash',
'stash' => {
'database' => {
'type' => 'mysql',
'user' => 'db_user',
'password' => 'db_password'
}
}
}
stub_data_bag('apps').and_return(['test_stash'])
stub_data_bag_item('apps', 'test_stash').and_return(data_bag)
end

let(:connection) do
{
host: '127.0.0.1',
port: 3306,
username: 'root',
password: 'mysql_root_pass'
}
end

it 'sets up MySQL service' do
expect(chef_run).to create_mysql_service('default').with(
bind_address: '127.0.0.1',
port: '3306',
initial_root_password: 'mysql_root_pass'
)
end

it 'creates MySQL database' do
expect(chef_run).to create_mysql_database('test_database').with(
collation: 'utf8_bin',
encoding: 'utf8',
connection: connection
)
end

it 'creates MySQL user' do
expect(chef_run).to create_mysql_database_user('db_user').with(
host: '%',
password: 'db_password',
database_name: 'test_database',
connection: connection
)
end
end
end

0 comments on commit ea25775

Please sign in to comment.