knife-ec-backup 2.0.0.beta.2 → 2.0.0.beta.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,38 @@
1
+ require 'uri'
2
+ require 'openssl'
3
+
4
+ class Chef
5
+ class Server
6
+
7
+ attr_accessor :root_url
8
+ def initialize(root_url)
9
+ @root_url = root_url
10
+ end
11
+
12
+ def self.from_chef_server_url(url)
13
+ url = url.gsub(/\/organizations\/+[^\/]+\/*$/, '')
14
+ Chef::Server.new(url)
15
+ end
16
+
17
+ def version
18
+ @version ||= begin
19
+ uri = URI.parse("#{root_url}/version")
20
+ ver_line = open(uri, {ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE}).each_line.first
21
+ ver_string = ver_line.split(' ').last
22
+ ver_string = ver_string.gsub(/\+.*$/, '')
23
+ Gem::Version.new(ver_string)
24
+ end
25
+ end
26
+
27
+ def supports_user_acls?
28
+ version >= Gem::Version.new("11.0.1")
29
+ end
30
+
31
+ def direct_account_access?
32
+ Chef::REST.new("http://127.0.0.1:9465").get("users")
33
+ true
34
+ rescue
35
+ false
36
+ end
37
+ end
38
+ end
@@ -1,3 +1,3 @@
1
1
  module KnifeECBackup
2
- VERSION = '2.0.0.beta.2'
2
+ VERSION = '2.0.0.beta.3'
3
3
  end
@@ -0,0 +1,158 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
2
+ require 'chef/knife/ec_backup'
3
+ require 'fakefs/spec_helpers'
4
+
5
+ describe Chef::Knife::EcBackup do
6
+ USER_RESPONSE = {
7
+ "foo" => "organizations/bar/users/foo",
8
+ "bar" => "organizations/bar/users/bar"
9
+ }
10
+
11
+ ORG_RESPONSE = {
12
+ "foo" => "organizations/bar",
13
+ "bar" => "organizations/foo"
14
+ }
15
+
16
+ def org_response(name, unassigned=false)
17
+ r = { "name" => name,
18
+ "full_name" => name,
19
+ "org_type" => "Business",
20
+ "clientname" => "#{name}-validator",
21
+ "guid" => "994191e436b740c9b229e64dc9a57517",
22
+ "chargify_subscription_id" => nil,
23
+ "chargify_customer_id" => nil,
24
+ "billing_plan" => "platform-free"
25
+ }
26
+ r['assigned_at'] = "2014/11/21 11:23:57 +0000" unless unassigned
27
+ r
28
+ end
29
+
30
+ before(:each) do
31
+ Chef::Knife::EcBackup.load_deps
32
+ @knife = Chef::Knife::EcBackup.new
33
+ @rest = double('Chef::Rest')
34
+ allow(@knife).to receive(:rest).and_return(@rest)
35
+ allow(@knife).to receive(:user_acl_rest).and_return(@rest)
36
+ end
37
+
38
+ describe "#for_each_user" do
39
+ it "iterates over remote users" do
40
+ allow(@rest).to receive(:get_rest).with("/users").and_return(USER_RESPONSE)
41
+ expect{ |b| @knife.for_each_user(&b) }.to yield_successive_args(["foo", USER_RESPONSE["foo"]], ["bar", USER_RESPONSE["bar"]])
42
+ end
43
+ end
44
+
45
+ describe "#for_each_organization" do
46
+ before(:each) do
47
+ allow(@rest).to receive(:get_rest).with("/organizations").and_return(ORG_RESPONSE)
48
+ end
49
+
50
+ it "iterates over remote organizations" do
51
+ allow(@rest).to receive(:get_rest).with("organizations/bar").and_return(org_response("bar"))
52
+ allow(@rest).to receive(:get_rest).with("organizations/foo").and_return(org_response("foo"))
53
+ expect{ |b| @knife.for_each_organization(&b) }.to yield_successive_args(org_response("bar"), org_response("foo"))
54
+ end
55
+
56
+ it "skips unassigned (precreated) organizations on Chef Server 11" do
57
+ server = double('Chef::Server')
58
+ allow(Chef::Server).to receive(:new).and_return(server)
59
+ allow(server).to receive(:version).and_return(Gem::Version.new("11.12.3"))
60
+ allow(@rest).to receive(:get_rest).with("organizations/bar").and_return(org_response("bar"))
61
+ allow(@rest).to receive(:get_rest).with("organizations/foo").and_return(org_response("foo", true))
62
+ expect{ |b| @knife.for_each_organization(&b) }.to yield_successive_args(org_response("bar"))
63
+ end
64
+
65
+ it "includes *all* organizations on Chef Server 12" do
66
+ server = double('Chef::Server')
67
+ allow(Chef::Server).to receive(:new).and_return(server)
68
+ allow(server).to receive(:version).and_return(Gem::Version.new("12.0.0"))
69
+ allow(@rest).to receive(:get_rest).with("organizations/bar").and_return(org_response("bar"))
70
+ allow(@rest).to receive(:get_rest).with("organizations/foo").and_return(org_response("foo", true))
71
+ expect{ |b| @knife.for_each_organization(&b) }.to yield_successive_args(org_response("bar"),
72
+ org_response("foo", true))
73
+ end
74
+ end
75
+
76
+ describe "#download_user" do
77
+ include FakeFS::SpecHelpers
78
+ let (:username) { "foo" }
79
+ let (:url) { "users/foo" }
80
+
81
+ it "downloads a named user from the api" do
82
+ expect(@rest).to receive(:get_rest).with(url)
83
+ @knife.download_user(username, url)
84
+ end
85
+
86
+ it "writes it to a json file in the destination directory" do
87
+ user_response = {"username" => "foo"}
88
+ allow(@rest).to receive(:get_rest).with(url).and_return(user_response)
89
+ @knife.download_user(username, url)
90
+ expect(JSON.parse(File.read("/users/foo.json"))).to eq(user_response)
91
+ end
92
+ end
93
+
94
+ describe "#download_user_acl" do
95
+ include FakeFS::SpecHelpers
96
+ let (:username) {"foo"}
97
+
98
+ it "downloads a user acl from the API" do
99
+ expect(@rest).to receive(:get_rest).with("users/#{username}/_acl")
100
+ @knife.download_user_acl(username)
101
+ end
102
+
103
+ it "writes it to a json file in the destination directory" do
104
+ user_acl_response = {"create" => {}}
105
+ allow(@rest).to receive(:get_rest).with("users/#{username}/_acl").and_return(user_acl_response)
106
+ @knife.download_user_acl(username)
107
+ expect(JSON.parse(File.read("/user_acls/foo.json"))).to eq(user_acl_response)
108
+ end
109
+ end
110
+
111
+ describe "#write_org_object_to_disk" do
112
+ include FakeFS::SpecHelpers
113
+ it "writes the given object to disk" do
114
+ org_object = { "name" => "bob" }
115
+ @knife.write_org_object_to_disk(org_object)
116
+ expect(JSON.parse(File.read("/organizations/bob/org.json"))).to eq(org_object)
117
+ end
118
+ end
119
+
120
+ describe "#download_org_members" do
121
+ include FakeFS::SpecHelpers
122
+ let (:users) {
123
+ [ {
124
+ "user" => { "username" => "john" }
125
+ },
126
+ {
127
+ "user" => { "username" => "mary" }
128
+ }
129
+ ]
130
+ }
131
+
132
+ it "downloads org members for a given org" do
133
+ expect(@rest).to receive(:get_rest).with("/organizations/bob/users").and_return(users)
134
+ @knife.download_org_members("bob")
135
+ end
136
+
137
+ it "writes the org members to a JSON file" do
138
+ expect(@rest).to receive(:get_rest).with("/organizations/bob/users").and_return(users)
139
+ @knife.download_org_members("bob")
140
+ expect(JSON.parse(File.read("/organizations/bob/members.json"))).to eq(users)
141
+ end
142
+ end
143
+
144
+ describe "#download_org_inivitations" do
145
+ include FakeFS::SpecHelpers
146
+ let(:invites) { {"a json" => "maybe"} }
147
+ it "downloads invitations for a given org" do
148
+ expect(@rest).to receive(:get_rest).with("/organizations/bob/association_requests").and_return(invites)
149
+ @knife.download_org_invitations("bob")
150
+ end
151
+
152
+ it "writes the invitations to a JSON file" do
153
+ expect(@rest).to receive(:get_rest).with("/organizations/bob/association_requests").and_return(invites)
154
+ @knife.download_org_invitations("bob")
155
+ expect(JSON.parse(File.read("/organizations/bob/invitations.json"))).to eq(invites)
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,73 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
2
+ require 'chef/knife/ec_base'
3
+ require 'chef/knife'
4
+ require 'chef/config'
5
+ require 'stringio'
6
+
7
+ class Tester < Chef::Knife
8
+ include Chef::Knife::EcBase
9
+ end
10
+
11
+ describe Chef::Knife::EcBase do
12
+ let(:o) { Tester.new }
13
+ before(:each) do
14
+ @rest = double('rest')
15
+ @stderr = StringIO.new
16
+ allow(o.ui).to receive(:stderr).and_return(@stderr)
17
+ allow(Chef::REST).to receive(:new).and_return(@rest)
18
+ end
19
+
20
+ context "org_admin" do
21
+ it "selects an admin from an org" do
22
+ allow(@rest).to receive(:get_rest).with("groups/admins").and_return({"users" => ["bob"]})
23
+ allow(@rest).to receive(:get_rest).with("users").and_return([{"user" => { "username" => "bob"}}])
24
+ expect(o.org_admin).to eq("bob")
25
+ end
26
+
27
+ it "refuses to return pivotal" do
28
+ allow(@rest).to receive(:get_rest).with("groups/admins").and_return({"users" => ["pivotal"]})
29
+ allow(@rest).to receive(:get_rest).with("users").and_return([{"user" => { "username" => "pivotal"}}])
30
+ expect{o.org_admin}.to raise_error(Chef::Knife::EcBase::NoAdminFound)
31
+ end
32
+
33
+ it "refuses to return users not in the org" do
34
+ allow(@rest).to receive(:get_rest).with("groups/admins").and_return({"users" => ["bob"]})
35
+ allow(@rest).to receive(:get_rest).with("users").and_return([{"user" => { "username" => "sally"}}])
36
+ expect{o.org_admin}.to raise_error(Chef::Knife::EcBase::NoAdminFound)
37
+ end
38
+ end
39
+
40
+ context "set_dest_dir_from_args!" do
41
+ it "sets dest_dir from its arguments" do
42
+ o.name_args = ["foo"]
43
+ o.set_dest_dir_from_args!
44
+ expect(o.dest_dir).to eq("foo")
45
+ end
46
+
47
+ it "exits with an error message if no argument is given" do
48
+ expect {o.set_dest_dir_from_args!}.to raise_error(SystemExit)
49
+ end
50
+ end
51
+
52
+ context "set_client_config!" do
53
+ it "sets the node_name to pivotal" do
54
+ Chef::Config.node_name = "foobar"
55
+ o.set_client_config!
56
+ expect(Chef::Config.node_name).to eq("pivotal")
57
+ end
58
+
59
+ it "sets the client key to config[:webui_key]" do
60
+ Chef::Config.client_key = "blargblarg"
61
+ o.config[:webui_key] = "foobar"
62
+ o.set_client_config!
63
+ expect(Chef::Config.client_key).to eq("foobar")
64
+ end
65
+ end
66
+
67
+ context "ensure_webui_key_exists!" do
68
+ it "exits when the webui_key doesn't exist" do
69
+ o.config[:webui_key] = "dne"
70
+ expect{o.ensure_webui_key_exists!}.to raise_error(SystemExit)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,2 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
2
+ require 'chef/knife/ec_key_base'
@@ -0,0 +1,2 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
2
+ require 'chef/knife/ec_key_export'
@@ -0,0 +1,2 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
2
+ require 'chef/knife/ec_key_import'
@@ -0,0 +1,138 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
2
+ require 'chef/knife/ec_restore'
3
+ require 'fakefs/spec_helpers'
4
+
5
+ def make_user(username)
6
+ FileUtils.mkdir_p("/users")
7
+ File.write("/users/#{username}.json", "{\"username\": \"#{username}\"}")
8
+ end
9
+
10
+ def make_org(orgname)
11
+ FileUtils.mkdir_p("/organizations/#{orgname}")
12
+ File.write("/organizations/#{orgname}/org.json", "{\"name\": \"#{orgname}\"}")
13
+ File.write("/organizations/#{orgname}/invitations.json", "[{\"username\": \"bob\"}, {\"username\": \"jane\"}]")
14
+ end
15
+
16
+ def net_exception(code)
17
+ s = double("status", :code => code.to_s)
18
+ Net::HTTPServerException.new("I'm not real!", s)
19
+ end
20
+
21
+ describe Chef::Knife::EcRestore do
22
+
23
+ before(:each) do
24
+ Chef::Knife::EcRestore.load_deps
25
+ @knife = Chef::Knife::EcRestore.new
26
+ @rest = double('Chef::Rest')
27
+ allow(@knife).to receive(:rest).and_return(@rest)
28
+ allow(@knife).to receive(:user_acl_rest).and_return(@rest)
29
+ end
30
+
31
+ describe "#create_organization" do
32
+ include FakeFS::SpecHelpers
33
+ it "posts a given org to the API from data on disk" do
34
+ make_org "foo"
35
+ org = JSON.parse(File.read("/organizations/foo/org.json"))
36
+ expect(@rest).to receive(:post_rest).with("organizations", org)
37
+ @knife.create_organization("foo")
38
+ end
39
+
40
+ it "updates a given org if it already exists" do
41
+ make_org "foo"
42
+ org = JSON.parse(File.read("/organizations/foo/org.json"))
43
+ allow(@rest).to receive(:post_rest).with("organizations", org).and_raise(net_exception(409))
44
+ expect(@rest).to receive(:put_rest).with("organizations/foo", org)
45
+ @knife.create_organization("foo")
46
+ end
47
+ end
48
+
49
+ describe "#restore_open_invitations" do
50
+ include FakeFS::SpecHelpers
51
+
52
+ it "reads the invitations from disk and posts them to the API" do
53
+ make_org "foo"
54
+ expect(@rest).to receive(:post_rest).with("organizations/foo/association_requests", {"user" => "bob"})
55
+ expect(@rest).to receive(:post_rest).with("organizations/foo/association_requests", {"user" => "jane"})
56
+ @knife.restore_open_invitations("foo")
57
+ end
58
+
59
+ it "does NOT fail if an inivitation already exists" do
60
+ make_org "foo"
61
+ allow(@rest).to receive(:post_rest).with("organizations/foo/association_requests", {"user" => "bob"}).and_return(net_exception(409))
62
+ allow(@rest).to receive(:post_rest).with("organizations/foo/association_requests", {"user" => "jane"}).and_return(net_exception(409))
63
+ expect {@knife.restore_open_invitations("foo")}.to_not raise_error
64
+ end
65
+ end
66
+
67
+ describe "#for_each_user" do
68
+ include FakeFS::SpecHelpers
69
+ it "iterates over all users with files on disk" do
70
+ make_user("bob")
71
+ make_user("jane")
72
+ expect{|b| @knife.for_each_user &b }.to yield_successive_args("bob", "jane")
73
+ end
74
+
75
+ it "skips pivotal when config[:overwrite_pivotal] is false" do
76
+ @knife.config[:overwrite_pivotal] = false
77
+ make_user("bob")
78
+ make_user("pivotal")
79
+ make_user("jane")
80
+ expect{|b| @knife.for_each_user &b }.to yield_successive_args("bob", "jane")
81
+ end
82
+
83
+ it "does not skip pivotal when config[:overwrite_pivotal] is true" do
84
+ @knife.config[:overwrite_pivotal] = true
85
+ make_user("bob")
86
+ make_user("pivotal")
87
+ make_user("jane")
88
+ expect{|b| @knife.for_each_user &b }.to yield_successive_args("bob", "pivotal", "jane")
89
+ end
90
+
91
+ it "does not return non-json files in the directory" do
92
+ make_user("bob")
93
+ make_user("jane")
94
+ File.write("/users/nonono", "")
95
+ expect{|b| @knife.for_each_user &b }.to yield_successive_args("bob", "jane")
96
+ end
97
+ end
98
+
99
+ describe "#for_each_organization" do
100
+ include FakeFS::SpecHelpers
101
+ it "iterates over all organizations with a folder on disk" do
102
+ make_org "acme"
103
+ make_org "wombats"
104
+ expect{|b| @knife.for_each_organization &b }.to yield_successive_args("acme", "wombats")
105
+ end
106
+
107
+ it "only return config[:org] when the option is specified" do
108
+ make_org "acme"
109
+ make_org "wombats"
110
+ @knife.config[:org] = "wombats"
111
+ expect{|b| @knife.for_each_organization &b }.to yield_with_args("wombats")
112
+ end
113
+ end
114
+
115
+ describe "#restore_users" do
116
+ include FakeFS::SpecHelpers
117
+
118
+ it "reads the user from disk and posts it to the API" do
119
+ make_user "jane"
120
+ expect(@rest).to receive(:post_rest).with("users", anything)
121
+ @knife.restore_users
122
+ end
123
+
124
+ it "sets a random password for users" do
125
+ make_user "jane"
126
+ # FIX ME: How can we test this better?
127
+ expect(@rest).to receive(:post_rest).with("users", {"username" => "jane", "password" => anything})
128
+ @knife.restore_users
129
+ end
130
+
131
+ it "updates the user if it already exists" do
132
+ make_user "jane"
133
+ allow(@rest).to receive(:post_rest).with("users", anything).and_raise(net_exception(409))
134
+ expect(@rest).to receive(:put_rest).with("users/jane", {"username" => "jane"})
135
+ @knife.restore_users
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,50 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
2
+ require 'chef/server'
3
+ require 'chef/rest'
4
+ require 'stringio'
5
+
6
+ describe Chef::Server do
7
+
8
+ it "infers root url from a Chef Server url" do
9
+ s = Chef::Server.from_chef_server_url("http://api.example.com/organizations/foobar")
10
+ expect(s.root_url).to eq("http://api.example.com")
11
+ end
12
+
13
+ it "determines the server version" do
14
+ s = Chef::Server.new("http://api.example.com")
15
+ allow(s).to receive(:open).and_return(StringIO.new("Chef Server 1.8.1\nother stuff\nother stuff"))
16
+ expect(s.version.to_s).to eq("1.8.1")
17
+ end
18
+
19
+ it "ignores git tags when determining the version" do
20
+ s = Chef::Server.new("http://api.example.com")
21
+ allow(s).to receive(:open).and_return(StringIO.new("Chef Server 1.8.1+20141024080718.git.16.08098a5\nother stuff\nother stuff"))
22
+ expect(s.version.to_s).to eq("1.8.1")
23
+ end
24
+
25
+ it "knows whether the server supports user ACLs via ngingx" do
26
+ s1 = Chef::Server.new("http://api.example.com")
27
+ s2 = Chef::Server.new("http://api.example.com")
28
+ allow(s1).to receive(:open).and_return(StringIO.new("Chef Server 11.0.0\nother stuff\nother stuff"))
29
+ allow(s2).to receive(:open).and_return(StringIO.new("Chef Server 11.0.2\nother stuff\nother stuff"))
30
+
31
+ expect(s1.supports_user_acls?).to eq(false)
32
+ expect(s2.supports_user_acls?).to eq(true)
33
+ end
34
+
35
+ it "knows when account is directly accessible" do
36
+ s = Chef::Server.new("http://api.example.com")
37
+ rest = double('rest')
38
+ allow(Chef::REST).to receive(:new).and_return(rest)
39
+ allow(rest).to receive(:get).and_return("")
40
+ expect(s.direct_account_access?).to eq(true)
41
+ end
42
+
43
+ it "knows when account is not directly accessible" do
44
+ s = Chef::Server.new("http://api.example.com")
45
+ rest = double('rest')
46
+ allow(Chef::REST).to receive(:new).and_return(rest)
47
+ allow(rest).to receive(:get).and_raise(Errno::ECONNREFUSED)
48
+ expect(s.direct_account_access?).to eq(false)
49
+ end
50
+ end