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

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,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