chef-provisioning-google 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +201 -0
  3. data/README.md +121 -0
  4. data/Rakefile +22 -0
  5. data/lib/chef/provider/google_key_pair.rb +172 -0
  6. data/lib/chef/provisioning/driver_init/google.rb +3 -0
  7. data/lib/chef/provisioning/google_driver.rb +5 -0
  8. data/lib/chef/provisioning/google_driver/client/global_operations.rb +18 -0
  9. data/lib/chef/provisioning/google_driver/client/google_base.rb +72 -0
  10. data/lib/chef/provisioning/google_driver/client/google_compute_error.rb +16 -0
  11. data/lib/chef/provisioning/google_driver/client/instance.rb +64 -0
  12. data/lib/chef/provisioning/google_driver/client/instances.rb +98 -0
  13. data/lib/chef/provisioning/google_driver/client/metadata.rb +112 -0
  14. data/lib/chef/provisioning/google_driver/client/operation.rb +23 -0
  15. data/lib/chef/provisioning/google_driver/client/operations_base.rb +44 -0
  16. data/lib/chef/provisioning/google_driver/client/projects.rb +39 -0
  17. data/lib/chef/provisioning/google_driver/client/zone_operations.rb +18 -0
  18. data/lib/chef/provisioning/google_driver/credentials.rb +63 -0
  19. data/lib/chef/provisioning/google_driver/driver.rb +313 -0
  20. data/lib/chef/provisioning/google_driver/resources.rb +0 -0
  21. data/lib/chef/provisioning/google_driver/version.rb +7 -0
  22. data/lib/chef/resource/google_key_pair.rb +50 -0
  23. data/spec/chef/provisioning/google_driver/client/instances_spec.rb +205 -0
  24. data/spec/chef/provisioning/google_driver/client/operations_spec.rb +62 -0
  25. data/spec/chef/provisioning/google_driver/client/projects_spec.rb +162 -0
  26. data/spec/chef/provisioning/google_driver/client/services_helper.rb +33 -0
  27. data/spec/spec_helper.rb +0 -0
  28. metadata +222 -0
@@ -0,0 +1,205 @@
1
+ require "chef/provisioning/google_driver/client/instances"
2
+ require "chef/provisioning/google_driver/client/google_compute_error"
3
+ require_relative "services_helper"
4
+
5
+ include Chef::Provisioning::GoogleDriver::Client
6
+
7
+ RSpec.describe Instances do
8
+ include ServicesHelper
9
+
10
+ before(:example) do
11
+ setup_service("instances", %w{get insert delete start stop})
12
+ @instances_client = Instances.new(@google, ServicesHelper::PROJECT,
13
+ ServicesHelper::ZONE)
14
+ end
15
+
16
+ context "when start is called" do
17
+
18
+ def expect_start(status, body)
19
+ expect_call("instances.start", { instance: "instance_name" },
20
+ nil, status, body)
21
+ end
22
+
23
+ it "raises an error if the API call failed" do
24
+ expect_start(400, {})
25
+ expect do
26
+ @instances_client.start("instance_name")
27
+ end.to raise_error(GoogleComputeError)
28
+ end
29
+
30
+ it "raises an error if the response contains errors" do
31
+ expect_start(200, { error: { errors: [{ message: "failure!" }] } })
32
+ expect do
33
+ @instances_client.start("instance_name")
34
+ end.to raise_error(GoogleComputeError)
35
+ end
36
+
37
+ it "returns a valid operation if everything goes well" do
38
+ expect_start(200, { name: "name", status: "LOL" })
39
+ operation = @instances_client.start("instance_name")
40
+ expect(operation.name).to eq("name")
41
+ expect(operation).not_to be_done
42
+ end
43
+
44
+ end
45
+
46
+ context "when stop is called" do
47
+
48
+ def expect_stop(status, body)
49
+ expect_call("instances.stop", { instance: "instance_name" },
50
+ nil, status, body)
51
+ end
52
+
53
+ it "raises an error if the API call failed" do
54
+ expect_stop(400, {})
55
+ expect do
56
+ @instances_client.stop("instance_name")
57
+ end.to raise_error(GoogleComputeError)
58
+ end
59
+
60
+ it "raises an error if the response contains errors" do
61
+ expect_stop(200, { error: { errors: [{ message: "failure!" }] } })
62
+ expect do
63
+ @instances_client.stop("instance_name")
64
+ end.to raise_error(GoogleComputeError)
65
+ end
66
+
67
+ it "returns a valid operation if everything goes well" do
68
+ expect_stop(200, { name: "name", status: "LOL" })
69
+ operation = @instances_client.stop("instance_name")
70
+ expect(operation.name).to eq("name")
71
+ expect(operation).not_to be_done
72
+ end
73
+
74
+ end
75
+
76
+ context "when delete is called" do
77
+
78
+ def expect_delete(status, body)
79
+ expect_call("instances.delete", { instance: "instance_name" },
80
+ nil, status, body)
81
+ end
82
+
83
+ it "raises an error if the API call failed" do
84
+ expect_delete(400, {})
85
+ expect do
86
+ @instances_client.delete("instance_name")
87
+ end.to raise_error(GoogleComputeError)
88
+ end
89
+
90
+ it "raises an error if the response contains errors" do
91
+ expect_delete(200, { error: { errors: [{ message: "failure!" }] } })
92
+ expect do
93
+ @instances_client.delete("instance_name")
94
+ end.to raise_error(GoogleComputeError)
95
+ end
96
+
97
+ it "returns a valid operation if everything goes well" do
98
+ expect_delete(200, { name: "name", status: "LOL" })
99
+ operation = @instances_client.delete("instance_name")
100
+ expect(operation.name).to eq("name")
101
+ expect(operation).not_to be_done
102
+ end
103
+
104
+ end
105
+
106
+ context "when create is called" do
107
+
108
+ def expect_create(status, body)
109
+ expect_call("instances.insert", {}, { some_option: 123 },
110
+ status, body)
111
+ end
112
+
113
+ it "raises an error if the API call failed" do
114
+ expect_create(400, {})
115
+ expect do
116
+ @instances_client.create(some_option: 123)
117
+ end.to raise_error(GoogleComputeError)
118
+ end
119
+
120
+ it "raises an error if the response contains errors" do
121
+ expect_create(200, { error: { errors: [{ message: "failure!" }] } })
122
+ expect do
123
+ @instances_client.create(some_option: 123)
124
+ end.to raise_error(GoogleComputeError)
125
+ end
126
+
127
+ it "returns a valid operation if everything goes well" do
128
+ expect_create(200, { name: "name", status: "DONE" })
129
+ operation = @instances_client.create(some_option: 123)
130
+ expect(operation.name).to eq("name")
131
+ expect(operation).to be_done
132
+ end
133
+
134
+ end
135
+
136
+ context "when get is called" do
137
+
138
+ def expect_get(status, body)
139
+ expect_call("instances.get", { instance: "instance_name" },
140
+ nil, status, body)
141
+ end
142
+
143
+ it 'returns nil if the instance couldn\'t be found' do
144
+ expect_get(404, {})
145
+ @instances_client.get("instance_name")
146
+ end
147
+
148
+ it "raises an error if the API call failed" do
149
+ expect_get(400, {})
150
+ expect do
151
+ @instances_client.get("instance_name")
152
+ end.to raise_error(GoogleComputeError)
153
+ end
154
+
155
+ it "raises an error if the response contains errors" do
156
+ expect_get(200, { error: { errors: [{ message: "failure!" }] } })
157
+ expect do
158
+ @instances_client.get("instance_name")
159
+ end.to raise_error(GoogleComputeError)
160
+ end
161
+
162
+ it "returns a valid instance if everything goes well" do
163
+ networkInterface = { accessConfigs: [{ natIP: "ip" }] }
164
+ expect_get(200, {
165
+ name: "instance_name",
166
+ id: "instance_id",
167
+ status: "RUNNING",
168
+ networkInterfaces: [networkInterface],
169
+ })
170
+ instance = @instances_client.get("instance_name")
171
+ expect(instance.name).to eq("instance_name")
172
+ expect(instance.id).to eq("instance_id")
173
+ expect(instance.status).to eq("RUNNING")
174
+ expect(instance).to be_running
175
+ expect(instance).not_to be_stopped
176
+ expect(instance).not_to be_stopping
177
+ expect(instance).not_to be_terminated
178
+ expect(instance.determine_remote_host).to eq("ip")
179
+ end
180
+
181
+ end
182
+
183
+ context "when wait_for_status is called" do
184
+
185
+ def expect_get(name, body)
186
+ expect_call("instances.get", { instance: name },
187
+ nil, 200, body)
188
+ end
189
+
190
+ before(:example) do
191
+ @action_handler = double("action_handler")
192
+ expect(@action_handler).to receive(:report_progress)
193
+ end
194
+
195
+ it "returns a valid instance if everything goes well" do
196
+ expect_get("instance_name", { name: "instance_name2" })
197
+ instance = @instances_client.get("instance_name")
198
+ expect_get("instance_name2", { name: "instance_name2", status: "RUNNING" })
199
+ running_instance = @instances_client.wait_for_status(@action_handler,
200
+ instance, "RUNNING")
201
+ expect(running_instance.name).to eq("instance_name2")
202
+ end
203
+ end
204
+
205
+ end
@@ -0,0 +1,62 @@
1
+ require "chef/provisioning/google_driver/client/global_operations"
2
+ require "chef/provisioning/google_driver/client/zone_operations"
3
+ require "chef/provisioning/google_driver/client/google_compute_error"
4
+ require "chef/provisioning/google_driver/client/operation"
5
+ require_relative "services_helper"
6
+
7
+ include Chef::Provisioning::GoogleDriver::Client
8
+
9
+ RSpec.shared_examples "Operations" do |service_name, client_class|
10
+
11
+ include ServicesHelper
12
+
13
+ before(:example) do
14
+ setup_service(service_name, ["get"])
15
+ @service_name = service_name
16
+ @operations_client = client_class.new(@google, ServicesHelper::PROJECT,
17
+ ServicesHelper::ZONE)
18
+ @operation = Operation.new({ body: { name: "op", status: "NOT_DONE" } })
19
+ end
20
+
21
+ context "when get is called" do
22
+
23
+ def expect_get(status, body)
24
+ expect_call("#{@service_name}.get", { operation: "op" }, nil, status, body)
25
+ end
26
+
27
+ it "raises an error if the API call failed" do
28
+ expect_get(400, {})
29
+ expect do
30
+ @operations_client.get(@operation)
31
+ end.to raise_error(GoogleComputeError)
32
+ end
33
+
34
+ it "raises an error if the response contains errors" do
35
+ expect_get(200, { error: { errors: [{ message: "failure!" }] } })
36
+ expect do
37
+ @operations_client.get(@operation)
38
+ end.to raise_error(GoogleComputeError)
39
+ end
40
+
41
+ it "returns a valid result if everything goes well" do
42
+ expect_get(200, { name: "op", status: "NOT_DONE" })
43
+ operation = @operations_client.get(@operation)
44
+ expect(operation.name).to eq("op")
45
+ expect(operation).not_to be_done
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+
52
+ RSpec.describe GlobalOperations do
53
+
54
+ it_behaves_like "Operations", "global_operations", GlobalOperations
55
+
56
+ end
57
+
58
+ RSpec.describe ZoneOperations do
59
+
60
+ it_behaves_like "Operations", "zone_operations", ZoneOperations
61
+
62
+ end
@@ -0,0 +1,162 @@
1
+ require "chef/provisioning/google_driver/client/projects"
2
+ require "chef/provisioning/google_driver/client/google_compute_error"
3
+ require_relative "services_helper"
4
+
5
+ include Chef::Provisioning::GoogleDriver::Client
6
+
7
+ RSpec.describe Projects do
8
+
9
+ FINGERPRINT = "asdf"
10
+ ITEMS = [{ key: "sshKeys", value: "testuser1:key1\ntestuser2:key2" },
11
+ { key: "chef-provisioning-google_ssh-mappings",
12
+ value: "testuser3:key3\ntestuser4:key4" }]
13
+ SSH_KEYS = [%w{testuser1 key1}, %w{testuser2 key2}]
14
+ SSH_MAPPINGS = { "testuser3" => "key3", "testuser4" => "key4" }
15
+
16
+ include ServicesHelper
17
+
18
+ before(:example) do
19
+ setup_service("projects", %w{get set_common_instance_metadata})
20
+ @projects_client = Projects.new(@google, ServicesHelper::PROJECT,
21
+ ServicesHelper::ZONE)
22
+ end
23
+
24
+ def expect_get(status, body)
25
+ expect_call("projects.get", {}, nil, status, body)
26
+ end
27
+
28
+ def expect_successful_get
29
+ expect_get(200, { name: "name", commonInstanceMetadata: {
30
+ fingerprint: FINGERPRINT,
31
+ items: ITEMS,
32
+ } })
33
+ end
34
+
35
+ context "when get is called" do
36
+
37
+ it "raises an error if the API call failed" do
38
+ expect_get(400, {})
39
+ expect do
40
+ @projects_client.get
41
+ end.to raise_error(GoogleComputeError)
42
+ end
43
+
44
+ it "raises an error if the response contains errors" do
45
+ expect_get(200, { error: { errors: [{ message: "failure!" }] } })
46
+ expect do
47
+ @projects_client.get
48
+ end.to raise_error(GoogleComputeError)
49
+ end
50
+
51
+ it "returns a valid result if everything goes well" do
52
+ expect_successful_get
53
+ metadata = @projects_client.get
54
+ expect(metadata).not_to be_changed
55
+ expect(metadata.ssh_keys).to eq(SSH_KEYS)
56
+ expect(metadata.ssh_mappings).to eq(SSH_MAPPINGS)
57
+ expect(metadata).not_to be_changed
58
+ end
59
+
60
+ end
61
+
62
+ context "when set_common_instance_metadata is called" do
63
+ before(:example) do
64
+ expect_successful_get
65
+ @metadata = @projects_client.get
66
+ end
67
+
68
+ def expect_set_common_instance_metadata(ssh_keys, ssh_mappings, status,
69
+ response_body)
70
+ request_body = {
71
+ items: [{ key: "chef-provisioning-google_ssh-mappings",
72
+ value: ssh_mappings },
73
+ { key: "sshKeys", value: ssh_keys }],
74
+ fingerprint: FINGERPRINT,
75
+ }
76
+ expect_call("projects.set_common_instance_metadata", {}, request_body,
77
+ status, response_body)
78
+ end
79
+
80
+ it 'does nothing if the metadata hasn\'t been changed' do
81
+ @metadata.ssh_keys
82
+ @metadata.ssh_mappings
83
+ @projects_client.set_common_instance_metadata(@metadata)
84
+ end
85
+
86
+ it "raises an error if the API call failed" do
87
+ expect_set_common_instance_metadata(
88
+ "testuser1:key1\ntestuser2:key2\nfoo:bar",
89
+ "testuser3:key3\ntestuser4:key4",
90
+ 400,
91
+ {})
92
+ # Change something such that the API call actually happens.
93
+ @metadata.ensure_key("foo", "bar")
94
+ expect do
95
+ @projects_client.set_common_instance_metadata(@metadata)
96
+ end.to raise_error(GoogleComputeError)
97
+ end
98
+
99
+ it "raises an error if the response contains errors" do
100
+ expect_set_common_instance_metadata(
101
+ "testuser1:key1\ntestuser2:key2\nfoo:bar",
102
+ "testuser3:key3\ntestuser4:key4",
103
+ 200,
104
+ { error: { errors: [{ message: "failure!" }] } })
105
+ # Change something such that the API call actually happens.
106
+ @metadata.ensure_key("foo", "bar")
107
+ expect do
108
+ @projects_client.set_common_instance_metadata(@metadata)
109
+ end.to raise_error(GoogleComputeError)
110
+ end
111
+
112
+ it "returns a valid result if an ssh key is added successfully" do
113
+ expect_set_common_instance_metadata(
114
+ "testuser1:key1\ntestuser2:key2\nfoo:bar",
115
+ "testuser3:key3\ntestuser4:key4",
116
+ 200,
117
+ { status: "DONE" })
118
+ @metadata.ensure_key("foo", "bar")
119
+ expect(@metadata).to be_changed
120
+ operation = @projects_client.set_common_instance_metadata(@metadata)
121
+ expect(operation).to be_done
122
+ end
123
+
124
+ it "returns a valid result if an ssh mapping is added successfully" do
125
+ expect_set_common_instance_metadata(
126
+ "testuser1:key1\ntestuser2:key2",
127
+ "testuser3:key3\ntestuser4:key4\nfoo:bar",
128
+ 200,
129
+ { status: "DONE" })
130
+ @metadata.set_ssh_mapping("foo", "bar")
131
+ expect(@metadata).to be_changed
132
+ operation = @projects_client.set_common_instance_metadata(@metadata)
133
+ expect(operation).to be_done
134
+ end
135
+
136
+ it "returns a valid result if an ssh key is removed successfully" do
137
+ expect_set_common_instance_metadata(
138
+ "testuser1:key1",
139
+ "testuser3:key3\ntestuser4:key4",
140
+ 200,
141
+ { status: "DONE" })
142
+ @metadata.delete_ssh_key("key2")
143
+ expect(@metadata).to be_changed
144
+ operation = @projects_client.set_common_instance_metadata(@metadata)
145
+ expect(operation).to be_done
146
+ end
147
+
148
+ it "returns a valid result if an ssh mapping is removed successfully" do
149
+ expect_set_common_instance_metadata(
150
+ "testuser1:key1\ntestuser2:key2",
151
+ "testuser3:key3",
152
+ 200,
153
+ { status: "DONE" })
154
+ @metadata.delete_ssh_mapping("testuser4")
155
+ expect(@metadata).to be_changed
156
+ operation = @projects_client.set_common_instance_metadata(@metadata)
157
+ expect(operation).to be_done
158
+ end
159
+
160
+ end
161
+
162
+ end
@@ -0,0 +1,33 @@
1
+ require "ffi_yajl"
2
+
3
+ # Helper module for testing classes that do calls to the Google API.
4
+ module ServicesHelper
5
+ PROJECT = "test-project"
6
+ ZONE = "test-zone"
7
+ DEFAULT_PARAMETERS = { project: PROJECT, zone: ZONE }
8
+
9
+ def setup_service(service_name, methods)
10
+ service = double(service_name)
11
+ methods.each do |m|
12
+ allow(service).to receive(m.to_sym).and_return("#{service_name}.#{m}")
13
+ end
14
+ compute = double("compute")
15
+ allow(compute).to receive(service_name.to_sym).and_return(service)
16
+ @google = double("google")
17
+ allow(@google).to receive(:discovered_api).and_return(compute)
18
+ end
19
+
20
+ def expect_call(method, parameters, request_body,
21
+ status, response_body)
22
+ inner_response = double("inner_response")
23
+ allow(inner_response).to receive(:status).and_return(status)
24
+ encoded_body = FFI_Yajl::Encoder.encode(response_body)
25
+ allow(inner_response).to receive(:body).and_return(encoded_body)
26
+ response = double("response")
27
+ allow(response).to receive(:response).and_return(inner_response)
28
+ allow(@google).to receive(:execute).with(
29
+ hash_including(api_method: method,
30
+ parameters: parameters.merge(DEFAULT_PARAMETERS),
31
+ body_object: request_body)).and_return(response)
32
+ end
33
+ end