cluster-fuck 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea
19
+ .DS_Store
20
+
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm 1.9.3@cluster-fuck --create
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cluster-fuck.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'guard-rspec'
8
+ gem 'rb-fsevent', '~> 0.9.1'
9
+ gem 'rake'
10
+ gem 'fakes3'
11
+ gem 'pry'
12
+ gem 'ci_reporter', '~> 1.7.3'
13
+ end
@@ -0,0 +1,24 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec' do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+
9
+ # Rails example
10
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
11
+ watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
12
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
13
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
14
+ watch('config/routes.rb') { "spec/routing" }
15
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
16
+
17
+ # Capybara features specs
18
+ watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
19
+
20
+ # Turnip features and steps
21
+ watch(%r{^spec/acceptance/(.+)\.feature$})
22
+ watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
23
+ end
24
+
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Topper Bowers
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,52 @@
1
+ # Cluster::Fuck
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'cluster-fuck'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install cluster-fuck
18
+
19
+
20
+ ### setup your credential file
21
+ If you are not on an EC2 instance then you should setup your ~/.cluster-fuck file with the followng yaml:
22
+
23
+ ```yaml
24
+ :access_key_id: access_key
25
+ :secret_access_key: secret_key
26
+ ```
27
+
28
+ It will also look for a ~/.fog file with the following syntax
29
+
30
+ ```yaml
31
+ :default:
32
+ :aws_access_key_id: access_key
33
+ :aws_secret_access_key: secret_key
34
+ ```
35
+
36
+ Otherwise, it expects you to be on an EC2 instance and not have to setup credentials.
37
+
38
+
39
+ ## Usage
40
+
41
+ ```ruby
42
+ reader = ClusterFuck::Reader.new
43
+ reader[:stripe][:api_key] loads "config_bucket/amicus_env/stripe and returns the api_key from the hash
44
+ ```
45
+ The ClusterFuck::Reader instance will automatically load the configuration for
46
+ the environment stored in the AMICUS_ENV environment variable on the host.
47
+
48
+ 1. Fork it
49
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
50
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
51
+ 4. Push to the branch (`git push origin my-new-feature`)
52
+ 5. Create new Pull Request
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ begin
7
+ require 'ci/reporter/rake/rspec'
8
+ rescue LoadError => ex
9
+ puts "Failed to load the ci_reporter gem. Make sure it is available in your loadpaths"
10
+ end
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env ruby
2
+ require 'commander/import'
3
+
4
+ $LOAD_PATH.unshift(File.join(File.expand_path(File.dirname(__FILE__)), "../lib"))
5
+ require "cluster-fuck/cli"
6
+
7
+ program :version, ClusterFuck::VERSION
8
+ program :description, 'Amicus environment-aware configuration manager'
9
+
10
+ #TODO: (topper) - fill in the help stuff
11
+
12
+ command :edit do |c|
13
+ c.syntax = 'clusterfuck edit [options]'
14
+ c.summary = ''
15
+ c.description = ''
16
+ c.example 'description', 'command example'
17
+ c.option '--force', 'force edit even if local file present'
18
+ c.when_called ClusterFuck::Commands::Edit, :run_command
19
+ end
20
+
21
+ command :create do |c|
22
+ c.syntax = 'clusterfuck create [options]'
23
+ c.summary = ''
24
+ c.description = ''
25
+ c.example 'description', 'command example'
26
+ #c.option '--some-switch', 'Some switch that does something'
27
+ c.when_called ClusterFuck::Commands::Create, :run_command
28
+ end
29
+
30
+ command :list do |c|
31
+ c.syntax = 'clusterfuck list'
32
+ c.summary = ''
33
+ c.description = ''
34
+ c.example 'description', 'command example'
35
+ #c.option '--some-switch', 'Some switch that does something'
36
+ c.when_called ClusterFuck::Commands::List, :run_command
37
+ end
38
+
39
+ command :override do |c|
40
+ c.syntax = 'clusterfuck override [options]'
41
+ c.summary = ''
42
+ c.description = ''
43
+ c.example 'description', 'command example'
44
+ #c.option '--some-switch', 'Some switch that does something'
45
+ c.when_called ClusterFuck::Commands::Override, :run_command
46
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cluster-fuck/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "cluster-fuck"
8
+ gem.version = ClusterFuck::VERSION
9
+ gem.authors = ["Topper Bowers"]
10
+ gem.email = ["topper@amicushq.com"]
11
+ gem.description = "cluster-aware S3 and ZK based config getter/setter"
12
+ gem.summary = ""
13
+ gem.homepage = ""
14
+
15
+ gem.add_dependency "aws-sdk"
16
+ gem.add_dependency "hashie"
17
+ gem.add_dependency "commander", "~> 4.1.0"
18
+
19
+ gem.add_development_dependency "rspec"
20
+
21
+ gem.files = `git ls-files`.split($/)
22
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
23
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
24
+ gem.require_paths = ["lib"]
25
+ end
@@ -0,0 +1,25 @@
1
+ require "cluster-fuck/version"
2
+
3
+ module ClusterFuck
4
+ CONFIG_BUCKET = 'amicus-config'
5
+ AMICUS_ENV = ENV['AMICUS_ENV'] || 'development'
6
+
7
+ def self.logger=(logger)
8
+ @logger = logger
9
+ end
10
+
11
+ def self.logger
12
+ @logger ||= Logger.new($stdout)
13
+ end
14
+
15
+ end
16
+
17
+ require 'aws-sdk'
18
+ require 'hashie'
19
+ require 'yaml'
20
+
21
+ require 'cluster-fuck/credential_grabber'
22
+ require 'cluster-fuck/s3_methods'
23
+ require 'cluster-fuck/configuration'
24
+ require 'cluster-fuck/reader'
25
+ require 'cluster-fuck/writer'
@@ -0,0 +1,5 @@
1
+ require_relative "../cluster-fuck"
2
+ require_relative "commands/edit"
3
+ require_relative "commands/create"
4
+ require_relative "commands/list"
5
+ require_relative "commands/override"
@@ -0,0 +1,20 @@
1
+ require 'commander'
2
+ require_relative "edit"
3
+
4
+ module ClusterFuck
5
+ module Commands
6
+ class Create
7
+ include ClusterFuck::S3Methods
8
+
9
+ attr_reader :key
10
+ def run_command(args, options = {})
11
+ @key = args.first
12
+ obj = s3_object(key)
13
+ raise ConflictError, "#{key} already exists!" if obj.exists?
14
+ obj.write('')
15
+ Edit.new.run_command(args)
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ require 'commander'
2
+
3
+ module ClusterFuck
4
+ module Commands
5
+ class Edit
6
+ include Commander::UI
7
+
8
+ attr_reader :key, :options
9
+ def run_command(args, options = Hashie::Mash.new)
10
+ @key = args.first
11
+ @options = options
12
+ raise ArgumentError, "File #{key} is overridden locally! use --force to force" if reader.has_local_override? and !options.force
13
+
14
+ new_yaml = ask_editor(YAML.dump(reader.read(remote_only: true).to_hash))
15
+ writer.set(Configuration.from_yaml(new_yaml), reader.version_count)
16
+ end
17
+
18
+ def writer
19
+ @writer ||= ClusterFuck::Writer.new(key)
20
+ end
21
+
22
+ def reader
23
+ @reader ||= ClusterFuck::Reader.new(key)
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,14 @@
1
+ require 'commander'
2
+
3
+ module ClusterFuck
4
+ module Commands
5
+ class List
6
+ include ClusterFuck::S3Methods
7
+
8
+ def run_command(args, options = {})
9
+ $stdout.puts(all_files.join("\n"))
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,27 @@
1
+ require 'commander'
2
+ require 'fileutils'
3
+
4
+ module ClusterFuck
5
+ module Commands
6
+ class Override
7
+ include ClusterFuck::S3Methods
8
+
9
+ attr_reader :key
10
+ def run_command(args, options = {})
11
+ @key = args.first
12
+ contents = reader.read(remote_only: true)
13
+ FileUtils.mkdir_p(File.dirname(reader.local_override_path))
14
+ File.open(reader.local_override_path, "w") do |f|
15
+ f << contents.to_yaml
16
+ end
17
+ $stdout.puts("wrote #{reader.local_override_path} with contents of #{reader.full_s3_path(key)}")
18
+ end
19
+
20
+ private
21
+ def reader
22
+ @reader ||= ClusterFuck::Reader.new(key)
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ module ClusterFuck
2
+ class Configuration < Hashie::Mash
3
+
4
+ def self.from_yaml(yaml)
5
+ new(YAML.load(yaml))
6
+ end
7
+
8
+ def to_yaml
9
+ YAML.dump(to_hash)
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,39 @@
1
+ module ClusterFuck
2
+
3
+ class CredentialGrabber
4
+ FOG_PATH = "~/.fog"
5
+ CF_PATH = "~/.cluster-fuck"
6
+
7
+ def self.find
8
+ new.find
9
+ end
10
+
11
+ def find
12
+ from_cluster_fuck_file || from_fog_file
13
+ end
14
+
15
+ def from_fog_file
16
+ if exists?(File.expand_path(FOG_PATH))
17
+ fog_credentials = YAML.load_file(File.expand_path(FOG_PATH))
18
+ {
19
+ access_key_id: fog_credentials[:default][:aws_access_key_id],
20
+ secret_access_key: fog_credentials[:default][:aws_secret_access_key],
21
+ }
22
+ end
23
+ end
24
+
25
+ def from_cluster_fuck_file
26
+ if exists?(CF_PATH)
27
+ YAML.load_file(CF_PATH)
28
+ end
29
+ end
30
+
31
+ private
32
+ def exists?(path)
33
+ File.exist?(path)
34
+ end
35
+
36
+ end
37
+
38
+
39
+ end
@@ -0,0 +1,44 @@
1
+ module ClusterFuck
2
+ class Reader
3
+ include S3Methods
4
+
5
+ LOCAL_OVERRIDE_DIR = "cluster-fuck"
6
+
7
+ attr_reader :amicus_env, :key, :version_count
8
+ def initialize(key, opts={})
9
+ @key = key
10
+ @amicus_env = opts[:amicus_env] || ClusterFuck::AMICUS_ENV
11
+ @ignore_local = opts[:ignore_local]
12
+ end
13
+
14
+ def read(opts = {})
15
+ yaml = data_for_key(opts)
16
+ if yaml
17
+ @version_count = stored_object.versions.count unless has_local_override?
18
+ Configuration.from_yaml(yaml)
19
+ end
20
+ end
21
+
22
+ def has_local_override?
23
+ File.exists?(local_override_path)
24
+ end
25
+
26
+ def local_override_path
27
+ File.join(LOCAL_OVERRIDE_DIR, full_s3_path(key))
28
+ end
29
+
30
+ private
31
+ def data_for_key(opts = {})
32
+ if has_local_override? and !opts[:remote_only]
33
+ File.read(local_override_path)
34
+ else
35
+ stored_object.read
36
+ end
37
+ end
38
+
39
+ def stored_object
40
+ @stored_object ||= s3_object(key)
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,42 @@
1
+ module ClusterFuck
2
+ module S3Methods
3
+
4
+ class ConflictError < StandardError; end
5
+
6
+ def s3_object(object_name)
7
+ bucket.objects[full_s3_path(object_name)]
8
+ end
9
+
10
+ def bucket
11
+ @bucket ||= s3.buckets[CONFIG_BUCKET]
12
+ end
13
+
14
+ def credentials
15
+ @credentials ||= CredentialGrabber.find
16
+ end
17
+
18
+ def s3
19
+ if credentials
20
+ AWS::S3.new(credentials) #could be nil, especially if on EC2
21
+ else
22
+ AWS::S3.new
23
+ end
24
+ end
25
+
26
+ def all_files
27
+ bucket.objects.with_prefix(amicus_env).collect(&:key)
28
+ end
29
+
30
+ def full_s3_path(key)
31
+ "#{amicus_env}/#{key}"
32
+ end
33
+
34
+ protected
35
+
36
+
37
+ def amicus_env
38
+ AMICUS_ENV
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,3 @@
1
+ module ClusterFuck
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,30 @@
1
+ module ClusterFuck
2
+ class Writer
3
+ include S3Methods
4
+
5
+ attr_reader :amicus_env, :key
6
+ def initialize(key, opts = {})
7
+ @key = key
8
+ @amicus_env = opts[:amicus_env] || ClusterFuck::AMICUS_ENV
9
+ end
10
+
11
+ #todo, thread safety and process safety please
12
+ def set(val, version_count = nil)
13
+ raise_unless_version_count_is_good(version_count)
14
+ stored_object.write(val.to_yaml)
15
+ raise_unless_version_count_is_good(version_count + 1) if version_count
16
+ end
17
+
18
+ def raise_unless_version_count_is_good(version_count)
19
+ if version_count and stored_object.versions.count > version_count
20
+ raise ConflictError, "File #{key} changed underneath you, version_count expected to be #{version_count} but was #{stored_object.versions.count}"
21
+ end
22
+ end
23
+
24
+ def stored_object
25
+ @stored_object ||= s3_object(key)
26
+ end
27
+
28
+
29
+ end
30
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+ require 'commander'
3
+ require 'cluster-fuck/cli'
4
+
5
+ module ClusterFuck::Commands
6
+ describe Create do
7
+
8
+ let(:amicus_env) { "test" }
9
+ let(:args) { ["test-key"] }
10
+ let(:mock_s3_object) do
11
+ mock("s3_obj",
12
+ :"exists?" => false,
13
+ :write => true
14
+ )
15
+ end
16
+
17
+ before do
18
+ ClusterFuck::Commands::Edit.any_instance.stub(:run_command).and_return(true)
19
+ subject.stub(:s3_object).and_return(mock_s3_object)
20
+ end
21
+
22
+ it "should create the file" do
23
+ mock_s3_object.should_receive(:write).with("")
24
+ subject.run_command(args)
25
+ end
26
+
27
+ it "should open the editor with the file" do
28
+ ClusterFuck::Commands::Edit.any_instance.should_receive(:run_command).with(args).and_return(true)
29
+ subject.run_command(args)
30
+ end
31
+
32
+ it "should error and not write if the file already exists" do
33
+ mock_s3_object.stub(:exists?).and_return(true)
34
+ mock_s3_object.should_not_receive(:write)
35
+ ->() { subject.run_command(args) }.should raise_error(ClusterFuck::S3Methods::ConflictError)
36
+ end
37
+
38
+ end
39
+
40
+
41
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+ require 'commander'
3
+ require 'cluster-fuck/cli'
4
+
5
+ module ClusterFuck::Commands
6
+ describe Edit do
7
+ let(:key) { "test-key" }
8
+ let(:mock_writer) { mock("writer", set: true) }
9
+
10
+ let(:dummy_val) do
11
+ {
12
+ key => {
13
+ 'test' => true
14
+ }
15
+ }
16
+ end
17
+
18
+ let(:dummy_yaml) { YAML.dump(dummy_val) }
19
+ let(:mock_s3_obj) do
20
+ mock(:s3_object, {
21
+ read: dummy_yaml,
22
+ versions: mock('versions', count: 3)
23
+ })
24
+ end
25
+
26
+ before do
27
+ ClusterFuck::Reader.any_instance.stub(:s3_object).and_return(mock_s3_obj)
28
+ end
29
+
30
+ describe "with a key" do
31
+ let(:args) { [key] }
32
+
33
+ it "should open the yaml version of the key in an editor" do
34
+ subject.should_receive(:ask_editor).with(dummy_yaml).and_return(dummy_yaml)
35
+ subject.stub(:writer).and_return(mock_writer)
36
+ mock_writer.should_receive(:set).with(dummy_val, mock_s3_obj.versions.count).once
37
+
38
+ subject.run_command(args)
39
+ end
40
+
41
+ it "should error when it is locally overriden" do
42
+ subject.reader.stub(:has_local_override?).and_return(true)
43
+ ->() { subject.run_command(args) }.should raise_error(ArgumentError)
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+ require 'commander'
3
+ require 'cluster-fuck/cli'
4
+
5
+ module ClusterFuck::Commands
6
+ describe Override do
7
+ let(:mock_reader) do
8
+ mock("reader",
9
+ read: "contents",
10
+ local_override_path: "/path/to/nowhere",
11
+ full_s3_path: "test/test-key"
12
+ )
13
+ end
14
+ let(:args) { ["test-key"] }
15
+
16
+ before do
17
+ ClusterFuck::Reader.should_receive(:new).with(args.first).and_return(mock_reader)
18
+ end
19
+
20
+ it "should read the remote contents and write the local contents" do
21
+ FileUtils.should_receive(:mkdir_p).with(File.dirname(mock_reader.local_override_path))
22
+ File.should_receive(:open).with(mock_reader.local_override_path, "w")
23
+ subject.run_command(args)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ module ClusterFuck
4
+ describe Configuration do
5
+
6
+ it "should be a mash" do
7
+ Configuration.new.should be_a(Hashie::Mash)
8
+ end
9
+
10
+ it "should load from yaml" do
11
+ yaml = YAML.dump({foo: 'bar'})
12
+ Configuration.from_yaml(yaml)[:foo].should == 'bar'
13
+ end
14
+
15
+ # TODO (topper): this isn't explicitly testing the right thing
16
+ # but it does *actually* test the right thing
17
+ it "should dump to yaml" do
18
+ hsh = {'foo' => 'bar'}
19
+ Configuration.new(hsh).to_yaml.should == YAML.dump(hsh)
20
+ end
21
+
22
+ end
23
+
24
+
25
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ module ClusterFuck
4
+
5
+ describe CredentialGrabber do
6
+ let(:credential_grabber) { CredentialGrabber.new }
7
+
8
+ before do
9
+ credential_grabber.stub(:exists?).and_return(false)
10
+ end
11
+
12
+ it "should have a singleton find" do
13
+ fake_credential_grabber = mock(:credential_grabber)
14
+ fake_credential_grabber.should_receive(:find).and_return nil
15
+ CredentialGrabber.should_receive(:new).and_return(fake_credential_grabber)
16
+ CredentialGrabber.find
17
+ end
18
+
19
+ let(:cf_credentials) do
20
+ {
21
+ access_key_id: 'cf_access_key',
22
+ secret_access_key: 'cf_secret_key'
23
+ }
24
+ end
25
+
26
+ let(:fog_credentials) do
27
+ {
28
+ default: {
29
+ aws_access_key_id: 'fog_access_key',
30
+ aws_secret_access_key: 'fog_secret_key'
31
+ }
32
+ }
33
+ end
34
+
35
+ describe "when there is a ~/.fog file but no ~/.cluster-fuck" do
36
+ before do
37
+ File.stub(:expand_path).with(CredentialGrabber::FOG_PATH).and_return(CredentialGrabber::FOG_PATH)
38
+ credential_grabber.should_receive(:exists?).with(CredentialGrabber::FOG_PATH).and_return(true)
39
+ YAML.should_receive(:load_file).with(CredentialGrabber::FOG_PATH).and_return(fog_credentials)
40
+ end
41
+
42
+ it "should return the credentials" do
43
+ credential_grabber.find.should == {
44
+ access_key_id: 'fog_access_key',
45
+ secret_access_key: 'fog_secret_key',
46
+ }
47
+ end
48
+ end
49
+
50
+ describe "when there is a ~/.cluster-fuck file and no ~/.fog file" do
51
+ before do
52
+ credential_grabber.should_receive(:exists?).with(CredentialGrabber::CF_PATH).and_return(true)
53
+ YAML.should_receive(:load_file).with(CredentialGrabber::CF_PATH).and_return(cf_credentials)
54
+ end
55
+
56
+ it "should return the credentials" do
57
+ credential_grabber.find.should == cf_credentials
58
+ end
59
+ end
60
+
61
+ describe "when there is both a ~/.cluster-fuck and a ~/.fog file" do
62
+ before do
63
+ credential_grabber.should_receive(:exists?).and_return(true)
64
+ YAML.should_receive(:load_file).with(CredentialGrabber::CF_PATH).and_return(cf_credentials)
65
+ end
66
+
67
+ it "should return the cluster-fuck credentials" do
68
+ credential_grabber.find.should == cf_credentials
69
+ end
70
+ end
71
+
72
+ describe "when there are no credential files" do
73
+
74
+ it "should return nil" do
75
+ credential_grabber.find.should be_nil
76
+ end
77
+
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ module ClusterFuck
4
+
5
+ describe Reader do
6
+ let(:amicus_env) { 'test' }
7
+ let(:key) { "test-key" }
8
+ let(:reader) { Reader.new(key) }
9
+ let(:mock_s3_obj) { mock(:s3_object, read: nil) }
10
+
11
+
12
+ it "should set amicus_env" do
13
+ reader.amicus_env.should == amicus_env
14
+ end
15
+
16
+ describe "when there is a local override" do
17
+ let(:local_yaml) { YAML.dump(foo: true) }
18
+ let(:local_path) { "cluster-fuck/#{amicus_env}/#{key}" }
19
+
20
+ before do
21
+ File.should_receive(:exists?).at_least(1).times.with(local_path).and_return(true)
22
+ File.stub(:read).with(local_path).and_return(local_yaml)
23
+ end
24
+
25
+ it "should #has_local_override?" do
26
+ reader.has_local_override?.should be_true
27
+ end
28
+
29
+ it "should use the local override when it exists" do
30
+ reader.read.should == Configuration.from_yaml(local_yaml)
31
+ end
32
+ end
33
+
34
+ describe "#read" do
35
+ let(:version_count) { 3 }
36
+
37
+ before do
38
+ mock_s3_obj.stub({
39
+ read: YAML.dump({
40
+ foo: 'bar'
41
+ }),
42
+ versions: mock('versions', count: version_count)
43
+ })
44
+ reader.stub(:s3_object).with(key).and_return(mock_s3_obj)
45
+ reader.stub(:s3_object).with(key.to_sym).and_return(mock_s3_obj)
46
+ end
47
+
48
+ it "should load the remote file and yaml parse it" do
49
+ reader.read['foo'].should == 'bar'
50
+ end
51
+
52
+ it "should set the version count" do
53
+ reader.read
54
+ reader.version_count.should == version_count
55
+ end
56
+
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ module ClusterFuck
4
+ class DummyClass
5
+ include S3Methods
6
+ end
7
+
8
+ describe S3Methods do
9
+ let(:amicus_env) { 'test' }
10
+ let(:config_bucket) { ClusterFuck::CONFIG_BUCKET }
11
+ let(:key) { 'test_key' }
12
+ let(:dummy_instance) { DummyClass.new }
13
+
14
+ let(:mock_listing) { mock('obj', key: "#{amicus_env}/#{key}") }
15
+ let(:bucket_objects) { mock('bucket_objects', with_prefix: [mock_listing]) }
16
+ let(:mock_bucket) { mock(:bucket, objects: bucket_objects) }
17
+
18
+ before do
19
+ DummyClass.any_instance.stub(:bucket).and_return(mock_bucket)
20
+ end
21
+
22
+ it "should qualify the keys it gets with the namespace" do
23
+ mock_bucket.stub(:objects) { { key => 'fail',
24
+ "#{amicus_env}/#{key}" => 'pass'} }
25
+ dummy_instance.stub(:bucket) { mock_bucket }
26
+ dummy_instance.s3_object(key).should == 'pass'
27
+ end
28
+
29
+ it "should enumerate all files with the prefix" do
30
+ bucket_objects.should_receive(:with_prefix).with(amicus_env).and_return([mock_listing])
31
+ dummy_instance.all_files.should == [mock_listing.key]
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+
3
+ module ClusterFuck
4
+
5
+ describe Writer do
6
+ let(:amicus_env) { 'development' }
7
+ let(:key) { 'tester' }
8
+ let(:reader) { Reader.new(key) }
9
+ let(:mock_s3_obj) { mock(:s3_object, read: nil) }
10
+ let(:writer) { Writer.new(key) }
11
+ let(:initial_version_count) { 3 }
12
+
13
+ before do
14
+ mock_s3_obj.stub(
15
+ read: YAML.dump({
16
+ foo: 'bar',
17
+ deep: {
18
+ works: 'too',
19
+ even: 'here'
20
+ }
21
+ }),
22
+ versions: mock('versions', count: initial_version_count)
23
+ )
24
+ Reader.any_instance.stub(:s3_object).with("#{key}").and_return(mock_s3_obj)
25
+ writer.stub(:s3_object).and_return(mock_s3_obj)
26
+ end
27
+
28
+ describe "#set" do
29
+ it "should write the configuration to the file" do
30
+ writer.should_receive(:s3_object).with(key).and_return(mock_s3_obj)
31
+
32
+ mock_s3_obj.should_receive(:write).with(YAML.dump({
33
+ 'deep' => {
34
+ 'even' => 'changed'
35
+ }
36
+ }))
37
+
38
+ do_write
39
+ end
40
+
41
+ describe "with version counts" do
42
+
43
+ it "should work normally when nothing has changed underneath" do
44
+ mock_write
45
+ mock_s3_obj.versions.stub(:count).and_return(initial_version_count, (initial_version_count + 1))
46
+
47
+ do_write
48
+ end
49
+
50
+ it "should raise an error and not write when it has changed before the set" do
51
+ mock_s3_obj.should_not_receive(:write)
52
+ mock_s3_obj.versions.stub(:count).and_return(initial_version_count + 1)
53
+
54
+ ->() { do_write }.should raise_error(ClusterFuck::S3Methods::ConflictError)
55
+ end
56
+
57
+ it "should raise an error if the version count has hopped AFTER our write" do
58
+ mock_write
59
+ mock_s3_obj.versions.stub(:count).and_return(initial_version_count, (initial_version_count + 2))
60
+
61
+ ->() { do_write }.should raise_error(ClusterFuck::S3Methods::ConflictError)
62
+ end
63
+
64
+ def mock_write
65
+ mock_s3_obj.should_receive(:write).with(YAML.dump({
66
+ 'deep' => {
67
+ 'even' => 'changed'
68
+ }
69
+ }))
70
+ end
71
+ end
72
+
73
+ def do_write
74
+ writer.set(Configuration.new({
75
+ deep: {
76
+ even: 'changed'
77
+ }
78
+ }), initial_version_count)
79
+ end
80
+
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,23 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ require 'pry'
3
+ require 'cluster-fuck'
4
+
5
+ Dir["spec/support/**/*.rb"].each {|f| require File.expand_path(f)}
6
+
7
+ # This file was generated by the `rspec --init` command. Conventionally, all
8
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
9
+ # Require this file using `require "spec_helper"` to ensure that it is only
10
+ # loaded once.
11
+ #
12
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
13
+ RSpec.configure do |config|
14
+ config.treat_symbols_as_metadata_keys_with_true_values = true
15
+ config.run_all_when_everything_filtered = true
16
+ config.filter_run :focus
17
+
18
+ # Run specs in random order to surface order dependencies. If you find an
19
+ # order dependency and want to debug it, you can fix the order by providing
20
+ # the seed, which is printed after each run.
21
+ # --seed 1234
22
+ config.order = 'random'
23
+ end
@@ -0,0 +1,6 @@
1
+ RSpec.configure do |config|
2
+ config.before(:suite) do
3
+ ENV['AMICUS_ENV'] = 'test'
4
+ ClusterFuck::AMICUS_ENV = 'test'
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,152 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cluster-fuck
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Topper Bowers
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: aws-sdk
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: hashie
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: commander
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 4.1.0
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 4.1.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: cluster-aware S3 and ZK based config getter/setter
79
+ email:
80
+ - topper@amicushq.com
81
+ executables:
82
+ - clusterfuck
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - .gitignore
87
+ - .rspec
88
+ - .rvmrc
89
+ - Gemfile
90
+ - Guardfile
91
+ - LICENSE.txt
92
+ - README.md
93
+ - Rakefile
94
+ - bin/clusterfuck
95
+ - cluster-fuck.gemspec
96
+ - lib/cluster-fuck.rb
97
+ - lib/cluster-fuck/cli.rb
98
+ - lib/cluster-fuck/commands/create.rb
99
+ - lib/cluster-fuck/commands/edit.rb
100
+ - lib/cluster-fuck/commands/list.rb
101
+ - lib/cluster-fuck/commands/override.rb
102
+ - lib/cluster-fuck/configuration.rb
103
+ - lib/cluster-fuck/credential_grabber.rb
104
+ - lib/cluster-fuck/reader.rb
105
+ - lib/cluster-fuck/s3_methods.rb
106
+ - lib/cluster-fuck/version.rb
107
+ - lib/cluster-fuck/writer.rb
108
+ - spec/lib/cluster-fuck/commands/create_spec.rb
109
+ - spec/lib/cluster-fuck/commands/edit_spec.rb
110
+ - spec/lib/cluster-fuck/commands/override_spec.rb
111
+ - spec/lib/cluster-fuck/configuration_spec.rb
112
+ - spec/lib/cluster-fuck/credential_grabber_spec.rb
113
+ - spec/lib/cluster-fuck/reader_spec.rb
114
+ - spec/lib/cluster-fuck/s3_methods_spec.rb
115
+ - spec/lib/cluster-fuck/writer_spec.rb
116
+ - spec/spec_helper.rb
117
+ - spec/support/amicus_env_setter.rb
118
+ homepage: ''
119
+ licenses: []
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ! '>='
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ! '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubyforge_project:
138
+ rubygems_version: 1.8.24
139
+ signing_key:
140
+ specification_version: 3
141
+ summary: ''
142
+ test_files:
143
+ - spec/lib/cluster-fuck/commands/create_spec.rb
144
+ - spec/lib/cluster-fuck/commands/edit_spec.rb
145
+ - spec/lib/cluster-fuck/commands/override_spec.rb
146
+ - spec/lib/cluster-fuck/configuration_spec.rb
147
+ - spec/lib/cluster-fuck/credential_grabber_spec.rb
148
+ - spec/lib/cluster-fuck/reader_spec.rb
149
+ - spec/lib/cluster-fuck/s3_methods_spec.rb
150
+ - spec/lib/cluster-fuck/writer_spec.rb
151
+ - spec/spec_helper.rb
152
+ - spec/support/amicus_env_setter.rb