cluster-fuck 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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