chef-provisioning-google 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.
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