gcloud 0.0.2 → 0.0.4

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 (118) hide show
  1. data.tar.gz.sig +2 -3
  2. data/CHANGELOG +4 -0
  3. data/LICENSE +674 -0
  4. data/Manifest +111 -0
  5. data/README.md +4 -3
  6. data/bin/gcutil +53 -0
  7. data/gcloud.gemspec +4 -3
  8. data/packages/gcutil-1.7.1/CHANGELOG +197 -0
  9. data/packages/gcutil-1.7.1/LICENSE +202 -0
  10. data/packages/gcutil-1.7.1/VERSION +1 -0
  11. data/packages/gcutil-1.7.1/gcutil +53 -0
  12. data/packages/gcutil-1.7.1/lib/google_api_python_client/LICENSE +23 -0
  13. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/__init__.py +1 -0
  14. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/discovery.py +743 -0
  15. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/errors.py +123 -0
  16. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/ext/__init__.py +0 -0
  17. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/http.py +1443 -0
  18. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/mimeparse.py +172 -0
  19. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/model.py +385 -0
  20. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/schema.py +303 -0
  21. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/__init__.py +1 -0
  22. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/anyjson.py +32 -0
  23. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/appengine.py +528 -0
  24. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.py +1139 -0
  25. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/clientsecrets.py +105 -0
  26. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/crypt.py +244 -0
  27. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/django_orm.py +124 -0
  28. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/file.py +107 -0
  29. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/locked_file.py +343 -0
  30. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/multistore_file.py +379 -0
  31. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/tools.py +174 -0
  32. data/packages/gcutil-1.7.1/lib/google_api_python_client/uritemplate/__init__.py +147 -0
  33. data/packages/gcutil-1.7.1/lib/google_apputils/LICENSE +202 -0
  34. data/packages/gcutil-1.7.1/lib/google_apputils/google/__init__.py +3 -0
  35. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/__init__.py +3 -0
  36. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/app.py +356 -0
  37. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/appcommands.py +783 -0
  38. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/basetest.py +1260 -0
  39. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/datelib.py +421 -0
  40. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/debug.py +60 -0
  41. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/file_util.py +181 -0
  42. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/resources.py +67 -0
  43. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/run_script_module.py +217 -0
  44. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/setup_command.py +159 -0
  45. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/shellutil.py +49 -0
  46. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/stopwatch.py +204 -0
  47. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/__init__.py +0 -0
  48. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auth_helper.py +140 -0
  49. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auth_helper_test.py +149 -0
  50. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auto_auth.py +130 -0
  51. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auto_auth_test.py +75 -0
  52. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/basic_cmds.py +128 -0
  53. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/basic_cmds_test.py +111 -0
  54. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/command_base.py +1808 -0
  55. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/command_base_test.py +1651 -0
  56. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/compute/v1beta13.json +2851 -0
  57. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/compute/v1beta14.json +3361 -0
  58. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/disk_cmds.py +342 -0
  59. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/disk_cmds_test.py +474 -0
  60. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/firewall_cmds.py +344 -0
  61. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/firewall_cmds_test.py +231 -0
  62. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/flags_cache.py +274 -0
  63. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/gcutil +89 -0
  64. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/gcutil_logging.py +69 -0
  65. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/image_cmds.py +262 -0
  66. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/image_cmds_test.py +172 -0
  67. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/instance_cmds.py +1506 -0
  68. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/instance_cmds_test.py +1904 -0
  69. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/kernel_cmds.py +91 -0
  70. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/kernel_cmds_test.py +56 -0
  71. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/machine_type_cmds.py +106 -0
  72. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/machine_type_cmds_test.py +59 -0
  73. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/metadata.py +96 -0
  74. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/metadata_lib.py +357 -0
  75. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/metadata_test.py +84 -0
  76. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/mock_api.py +420 -0
  77. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/mock_metadata.py +58 -0
  78. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/move_cmds.py +824 -0
  79. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/move_cmds_test.py +307 -0
  80. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/network_cmds.py +178 -0
  81. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/network_cmds_test.py +133 -0
  82. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/operation_cmds.py +181 -0
  83. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/operation_cmds_test.py +196 -0
  84. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/path_initializer.py +38 -0
  85. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/project_cmds.py +173 -0
  86. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/project_cmds_test.py +111 -0
  87. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/scopes.py +61 -0
  88. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/scopes_test.py +50 -0
  89. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/snapshot_cmds.py +276 -0
  90. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/snapshot_cmds_test.py +260 -0
  91. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/ssh_keys.py +266 -0
  92. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/ssh_keys_test.py +128 -0
  93. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/table_formatter.py +563 -0
  94. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/thread_pool.py +188 -0
  95. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/thread_pool_test.py +88 -0
  96. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/utils.py +208 -0
  97. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/utils_test.py +193 -0
  98. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/version.py +17 -0
  99. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/version_checker.py +246 -0
  100. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/version_checker_test.py +271 -0
  101. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/zone_cmds.py +151 -0
  102. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/zone_cmds_test.py +60 -0
  103. data/packages/gcutil-1.7.1/lib/httplib2/LICENSE +21 -0
  104. data/packages/gcutil-1.7.1/lib/httplib2/httplib2/__init__.py +1630 -0
  105. data/packages/gcutil-1.7.1/lib/httplib2/httplib2/cacerts.txt +714 -0
  106. data/packages/gcutil-1.7.1/lib/httplib2/httplib2/iri2uri.py +110 -0
  107. data/packages/gcutil-1.7.1/lib/httplib2/httplib2/socks.py +438 -0
  108. data/packages/gcutil-1.7.1/lib/iso8601/LICENSE +20 -0
  109. data/packages/gcutil-1.7.1/lib/iso8601/iso8601/__init__.py +1 -0
  110. data/packages/gcutil-1.7.1/lib/iso8601/iso8601/iso8601.py +102 -0
  111. data/packages/gcutil-1.7.1/lib/iso8601/iso8601/test_iso8601.py +111 -0
  112. data/packages/gcutil-1.7.1/lib/python_gflags/AUTHORS +2 -0
  113. data/packages/gcutil-1.7.1/lib/python_gflags/LICENSE +28 -0
  114. data/packages/gcutil-1.7.1/lib/python_gflags/gflags.py +2862 -0
  115. data/packages/gcutil-1.7.1/lib/python_gflags/gflags2man.py +544 -0
  116. data/packages/gcutil-1.7.1/lib/python_gflags/gflags_validators.py +187 -0
  117. metadata +118 -5
  118. metadata.gz.sig +0 -0
@@ -0,0 +1,260 @@
1
+ #!/usr/bin/python
2
+ #
3
+ # Copyright 2012 Google Inc. All Rights Reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ """Unit tests for the persistent disk snapshot commands."""
18
+
19
+
20
+
21
+ import path_initializer
22
+ path_initializer.InitializeSysPath()
23
+
24
+ import copy
25
+ import sys
26
+
27
+ import gflags as flags
28
+ import unittest
29
+
30
+ from gcutil import command_base
31
+ from gcutil import mock_api
32
+ from gcutil import snapshot_cmds
33
+
34
+
35
+ FLAGS = flags.FLAGS
36
+
37
+
38
+ class SnapshotCmdsTest(unittest.TestCase):
39
+
40
+ def _DoTestAddSnapshotGeneratesCorrectRequest(self, service_version):
41
+ flag_values = copy.deepcopy(FLAGS)
42
+
43
+ command = snapshot_cmds.AddSnapshot('addsnapshot', flag_values)
44
+
45
+ expected_project = 'test_project'
46
+ expected_snapshot = 'test_snapshot'
47
+ expected_description = 'test snapshot'
48
+ submitted_source_disk = 'disk1'
49
+ submitted_zone = 'myzone'
50
+ flag_values.service_version = service_version
51
+ flag_values.source_disk = submitted_source_disk
52
+ flag_values.project = expected_project
53
+ flag_values.description = expected_description
54
+
55
+ command.SetFlags(flag_values)
56
+ command.SetApi(mock_api.MockApi())
57
+
58
+ if command._IsUsingAtLeastApiVersion('v1beta14'):
59
+ flag_values.zone = submitted_zone
60
+
61
+ expected_source_disk = command.NormalizePerZoneResourceName(
62
+ expected_project,
63
+ submitted_zone,
64
+ 'disks',
65
+ submitted_source_disk)
66
+
67
+ result = command.Handle(expected_snapshot)
68
+
69
+ self.assertEqual(result['project'], expected_project)
70
+ self.assertEqual(result['body']['name'], expected_snapshot)
71
+ self.assertEqual(result['body']['description'], expected_description)
72
+ self.assertEqual(result['body']['sourceDisk'], expected_source_disk)
73
+
74
+ def testAddSnapshotGeneratesCorrectRequest(self):
75
+ for version in command_base.SUPPORTED_VERSIONS:
76
+ self._DoTestAddSnapshotGeneratesCorrectRequest(version)
77
+
78
+ def _DoTestAddSnapshotWithoutZoneGeneratesCorrectRequest(self,
79
+ service_version):
80
+ flag_values = copy.deepcopy(FLAGS)
81
+
82
+ command = snapshot_cmds.AddSnapshot('addsnapshot', flag_values)
83
+
84
+ expected_project = 'test_project'
85
+ expected_snapshot = 'test_snapshot'
86
+ expected_description = 'test snapshot'
87
+ submitted_source_disk = 'disk1'
88
+ disk_zone = 'us-east-a'
89
+ api_base = 'https://www.googleapis.com/compute/%s' % service_version
90
+ disk_self_link = '%s/projects/%s/zones/%s/disks/%s' % (
91
+ api_base, expected_project, disk_zone, submitted_source_disk)
92
+
93
+ flag_values.service_version = service_version
94
+ flag_values.source_disk = submitted_source_disk
95
+ flag_values.project = expected_project
96
+ flag_values.description = expected_description
97
+
98
+ zones = {'items': [{'name': disk_zone}]}
99
+ disks = {'items': [{'name': 'disk1',
100
+ 'selfLink': disk_self_link}]}
101
+
102
+ class MockZonesApi(object):
103
+ def list(self, **unused_kwargs):
104
+ return mock_api.MockRequest(zones)
105
+
106
+ class MockDisksApi(object):
107
+ def list(self, **unused_kwargs):
108
+ return mock_api.MockRequest(disks)
109
+
110
+ api = mock_api.MockApi()
111
+ api.zones = MockZonesApi
112
+ api.disks = MockDisksApi
113
+
114
+ command.SetFlags(flag_values)
115
+ command.SetApi(api)
116
+
117
+ expected_source_disk = command.NormalizePerZoneResourceName(
118
+ expected_project,
119
+ disk_zone,
120
+ 'disks',
121
+ submitted_source_disk)
122
+
123
+ result = command.Handle(expected_snapshot)
124
+
125
+ self.assertEqual(result['project'], expected_project)
126
+ self.assertEqual(result['body']['name'], expected_snapshot)
127
+ self.assertEqual(result['body']['description'], expected_description)
128
+ self.assertEqual(result['body']['sourceDisk'], expected_source_disk)
129
+
130
+ def testAddSnapshotWithoutZoneGeneratesCorrectRequest(self):
131
+ for version in command_base.SUPPORTED_VERSIONS:
132
+ self._DoTestAddSnapshotWithoutZoneGeneratesCorrectRequest(version)
133
+
134
+ def testAddSnapshotRequiresSourceDisk(self):
135
+ flag_values = copy.deepcopy(FLAGS)
136
+
137
+ command = snapshot_cmds.AddSnapshot('addsnapshot', flag_values)
138
+
139
+ expected_project = 'test_project'
140
+ expected_snapshot = 'test_snapshot'
141
+ expected_description = 'test snapshot'
142
+ submitted_version = command_base.CURRENT_VERSION
143
+ submitted_source_disk = 'disk1'
144
+
145
+ flag_values.service_version = submitted_version
146
+ flag_values.project = expected_project
147
+ flag_values.description = expected_description
148
+
149
+ command.SetFlags(flag_values)
150
+
151
+ def GetDiskPath(disk_name):
152
+ disk_path = 'projects/test_project/disks/%s' % (disk_name)
153
+ if command._IsUsingAtLeastApiVersion('v1beta14'):
154
+ disk_path = 'projects/test_project/zones/zone-a/disks/%s' % (disk_name)
155
+ return disk_path
156
+
157
+ disks = {
158
+ 'items': [
159
+ {'name': GetDiskPath('disk1')},
160
+ {'name': GetDiskPath('disk2')},
161
+ {'name': GetDiskPath('disk3')}]}
162
+
163
+ class MockDisksApi(object):
164
+ def list(self, **unused_kwargs):
165
+ return mock_api.MockRequest(disks)
166
+
167
+ api = mock_api.MockApi()
168
+ api.disks = MockDisksApi
169
+ command.SetApi(api)
170
+
171
+ expected_disk = command.NormalizePerZoneResourceName(
172
+ expected_project,
173
+ 'zone-a',
174
+ 'disks',
175
+ submitted_source_disk)
176
+
177
+ mock_output = mock_api.MockOutput()
178
+ mock_input = mock_api.MockInput('1\n\r')
179
+ oldin = sys.stdin
180
+ sys.stdin = mock_input
181
+ oldout = sys.stdout
182
+ sys.stdout = mock_output
183
+
184
+ result = command.Handle(expected_snapshot)
185
+ self.assertEqual(result['body']['sourceDisk'], expected_disk)
186
+ sys.stdin = oldin
187
+ sys.stdout = oldout
188
+
189
+ def _DoTestGetSnapshotGeneratesCorrectRequest(self, service_version):
190
+ flag_values = copy.deepcopy(FLAGS)
191
+
192
+ command = snapshot_cmds.GetSnapshot('getsnapshot', flag_values)
193
+
194
+ expected_project = 'test_project'
195
+ expected_snapshot = 'test_snapshot'
196
+ flag_values.project = expected_project
197
+ flag_values.service_version = service_version
198
+
199
+ command.SetFlags(flag_values)
200
+ command.SetApi(mock_api.MockApi())
201
+
202
+ result = command.Handle(expected_snapshot)
203
+
204
+ self.assertEqual(result['project'], expected_project)
205
+ self.assertEqual(result['snapshot'], expected_snapshot)
206
+
207
+ def testGetSnapshotGeneratesCorrectRequest(self):
208
+ for version in command_base.SUPPORTED_VERSIONS:
209
+ self._DoTestGetSnapshotGeneratesCorrectRequest(version)
210
+
211
+ def _DoTestDeleteSnapshotGeneratesCorrectRequest(self, service_version):
212
+ flag_values = copy.deepcopy(FLAGS)
213
+
214
+ command = snapshot_cmds.DeleteSnapshot('deletesnapshot', flag_values)
215
+
216
+ expected_project = 'test_project'
217
+ expected_snapshot = 'test_snapshot'
218
+ flag_values.project = expected_project
219
+
220
+ command.SetFlags(flag_values)
221
+ command.SetApi(mock_api.MockApi())
222
+ command._credential = mock_api.MockCredential()
223
+ flag_values.service_version = service_version
224
+
225
+ results, exceptions = command.Handle(expected_snapshot)
226
+ self.assertEquals(exceptions, [])
227
+ self.assertEquals(len(results['items']), 1)
228
+ result = results['items'][0]
229
+
230
+ self.assertEqual(result['project'], expected_project)
231
+ self.assertEqual(result['snapshot'], expected_snapshot)
232
+
233
+ def testDeleteSnapshotGeneratesCorrectRequest(self):
234
+ for version in command_base.SUPPORTED_VERSIONS:
235
+ self._DoTestDeleteSnapshotGeneratesCorrectRequest(version)
236
+
237
+ def testDeleteMultipleSnapshots(self):
238
+ flag_values = copy.deepcopy(FLAGS)
239
+ command = snapshot_cmds.DeleteSnapshot('deletesnapshot', flag_values)
240
+
241
+ expected_project = 'test_project'
242
+ expected_snapshots = ['test-snapshot-%02d' % x for x in xrange(100)]
243
+ flag_values.project = expected_project
244
+
245
+ command.SetFlags(flag_values)
246
+ command.SetApi(mock_api.MockApi())
247
+ command._credential = mock_api.MockCredential()
248
+
249
+ results, exceptions = command.Handle(*expected_snapshots)
250
+ self.assertEqual(exceptions, [])
251
+ results = results['items']
252
+ self.assertEqual(len(results), len(expected_snapshots))
253
+
254
+ for expected_snapshot, result in zip(expected_snapshots, results):
255
+ self.assertEqual(result['project'], expected_project)
256
+ self.assertEqual(result['snapshot'], expected_snapshot)
257
+
258
+
259
+ if __name__ == '__main__':
260
+ unittest.main()
@@ -0,0 +1,266 @@
1
+ # Copyright 2012 Google Inc. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Utility class for creating/storing SSH keys."""
16
+
17
+ from __future__ import with_statement
18
+
19
+
20
+
21
+ import base64
22
+ import os
23
+ import struct
24
+ import subprocess
25
+
26
+
27
+ import gflags as flags
28
+
29
+ from gcutil import command_base
30
+ from gcutil import gcutil_logging
31
+
32
+
33
+ PRIVATE_KEY_FILE = 'google_compute_engine'
34
+ PUBLIC_KEY_FILE = PRIVATE_KEY_FILE + '.pub'
35
+
36
+ flags.DEFINE_string(
37
+ 'public_key_file',
38
+ os.path.expanduser('~/.ssh/' + PUBLIC_KEY_FILE),
39
+ 'The location of the default (generated) public ssh key for use '
40
+ 'with Google Cloud Compute instances.')
41
+
42
+ flags.DEFINE_string(
43
+ 'private_key_file',
44
+ os.path.expanduser('~/.ssh/' + PRIVATE_KEY_FILE),
45
+ 'The location of the default (generated) private ssh key for use '
46
+ 'with Google Cloud Compute instances.')
47
+
48
+ flags.DEFINE_string(
49
+ 'ssh_user',
50
+ os.getenv('USER'),
51
+ 'The default ssh user for the instance.')
52
+
53
+ FLAGS = flags.FLAGS
54
+ LOGGER = gcutil_logging.LOGGER
55
+
56
+
57
+ class Error(Exception):
58
+ pass
59
+
60
+
61
+ class UserSetupError(Error):
62
+ """Raised the users environment isn't set up correctly."""
63
+
64
+ def __init__(self, msg):
65
+ Error.__init__(self)
66
+ self.msg = msg
67
+
68
+ def __str__(self):
69
+ return self.msg
70
+
71
+
72
+ class SshKeys(object):
73
+ """Collection of methods that work with Google Compute Engine SSH Keys."""
74
+
75
+ @staticmethod
76
+ def GetAuthorizedUserKeys(use_compute_key=True,
77
+ authorized_ssh_keys=None):
78
+ """Get a typical list of ssh user/key dictionaries.
79
+
80
+ Args:
81
+ use_compute_key: authorize using ~/.ssh/compute.pub
82
+ authorized_ssh_keys: key string user1:keyfile1,user2:keyfile2...
83
+
84
+ Returns:
85
+ A list of {'user': ..., 'key': ...} dictionaries.
86
+ """
87
+ user_keys = []
88
+
89
+ if use_compute_key:
90
+ user_keys.append(SshKeys.GetPublicKey())
91
+
92
+ if authorized_ssh_keys:
93
+ for user_key_file_pair in authorized_ssh_keys:
94
+ user, key_file = user_key_file_pair.split(':')
95
+ user_keys.append({'user': user,
96
+ 'key': SshKeys.GetKeyFromFile(key_file)})
97
+ return user_keys
98
+
99
+ @staticmethod
100
+ def GetAuthorizedUserKeysFromMetadata(metadata):
101
+ """Get the set of authorized user keys from the given metadata.
102
+
103
+ Args:
104
+ metadata: list of {'key': ..., 'value': ...} dictionaries.
105
+ Returns:
106
+ A list of {'user': ..., 'key':...} dictionaries.
107
+ """
108
+
109
+ def GetAuthorizedUserKeyFromLine(line):
110
+ line_parts = line.split(':')
111
+ return {'user': line_parts[0], 'key': line_parts[1]}
112
+
113
+ for metadata_entry in metadata:
114
+ key = metadata_entry['key']
115
+ value = metadata_entry['value']
116
+ if key == 'sshKeys':
117
+ lines = value.split('\n')
118
+ return [GetAuthorizedUserKeyFromLine(line)
119
+ for line in lines if ':' in line]
120
+ return []
121
+
122
+ @staticmethod
123
+ def SetAuthorizedUserKeysInMetadata(metadata, authorized_user_keys):
124
+ """Add the authorized public ssh keys to the given metadata.
125
+
126
+ Args:
127
+ metadata: A list of {'key': ..., 'value': ...} dictionaries.
128
+ authorized_user_keys: A list of {'user': ..., 'key':...} dictionaries.
129
+ Returns:
130
+ The metadata updated to include exactly one 'sshKeys' entry that
131
+ matches the given authorized user keys.
132
+ """
133
+
134
+ all_user_keys_string = '\n'.join(
135
+ ['%(user)s:%(key)s' % user_keys for user_keys in authorized_user_keys])
136
+ for metadata_entry in metadata:
137
+ if metadata_entry['key'] == 'sshKeys':
138
+ metadata_entry['value'] = all_user_keys_string
139
+ return
140
+ metadata.append({'key': 'sshKeys', 'value': all_user_keys_string})
141
+
142
+ @staticmethod
143
+ def GetPublicKey():
144
+ """Returns the standard Compute key for the current user.
145
+
146
+ If the key doesn't exist, it will be created and will
147
+ interactively prompt the user.
148
+
149
+ Returns:
150
+ A dictionary of an user/key pair for the user's ssh key.
151
+ """
152
+ SshKeys.EnsureSshKeyCreated()
153
+ if (not FLAGS['ssh_user'].present) and FLAGS.ssh_user == 'root':
154
+ LOGGER.warn('Logging into root is not supported on default images. '
155
+ 'Please specify a different user account with --ssh_user. '
156
+ 'Use this flag for addinstance and all ssh based commands.')
157
+ return {'user': FLAGS.ssh_user,
158
+ 'key': SshKeys.GetKeyFromFile(FLAGS.public_key_file)}
159
+
160
+ @staticmethod
161
+ def EnsureSshKeyCreated():
162
+ """Ensures that the ssh key actually exists.
163
+
164
+ This will create a public/private key pair if no existing
165
+ key pair is found.
166
+
167
+ Raises:
168
+ UserSetupError: Error when generating the ssh key
169
+ """
170
+ if (os.path.exists(FLAGS.public_key_file) and
171
+ os.path.exists(FLAGS.private_key_file)):
172
+ return
173
+
174
+ LOGGER.warn('You don\'t have an ssh key for Google Compute Engine. '
175
+ 'Creating one now...')
176
+ command_line = [
177
+ 'ssh-keygen',
178
+ '-t', 'rsa',
179
+ '-q',
180
+ '-f', FLAGS.private_key_file,
181
+ ]
182
+
183
+ LOGGER.debug(' '.join(command_line))
184
+ try:
185
+ process = subprocess.Popen(command_line)
186
+ process.communicate()
187
+ if process.wait() != 0:
188
+ raise UserSetupError('Error generating compute ssh key.')
189
+ except OSError as e:
190
+ raise UserSetupError('There was a problem running ssh-keygen: %s' % e)
191
+
192
+ @staticmethod
193
+ def GetKeyFromFile(key_file):
194
+ """Read an ssh key from key_file, and return it.
195
+
196
+ Args:
197
+ key_file: the file containing the ssh key
198
+
199
+ Returns:
200
+ A the ssh key stored in the file.
201
+ """
202
+ key_file = os.path.expanduser(key_file)
203
+ with open(key_file) as f:
204
+ return SshKeys._ValidateSshKey(f.read().strip(), key_file)
205
+
206
+ @staticmethod
207
+ def _ValidateSshKey(key, key_file):
208
+ """Validates the public ssh key format (OpenSSH).
209
+
210
+ Args:
211
+ key: string containing the public ssh key.
212
+ key_file: filename whence the key cometh.
213
+
214
+ Returns:
215
+ the ssh key value (value of key parameter) if validation has passed.
216
+
217
+ Raises:
218
+ UserSetupError: if the key validation fails.
219
+ """
220
+ if not key:
221
+ raise UserSetupError(
222
+ 'Public key file (%s) doesn\'t contain a key.' % key_file)
223
+ if '\n' in key:
224
+ raise UserSetupError(
225
+ 'Public key file (%s) has invalid format. '
226
+ 'It must only contain single line.\n%s' % (key_file, key))
227
+ # Validate the OpenSSH key format
228
+ parts = key.split()
229
+ if len(parts) != 3:
230
+ raise UserSetupError(
231
+ 'Public key file (%s) doesn\'t contain an OpenSSH public key. '
232
+ 'The key must consist of exactly three space separated parts.\n%s' %
233
+ (key_file, key))
234
+
235
+ key_type, key_value, _ = parts
236
+
237
+ try:
238
+ key_value = base64.b64decode(key_value)
239
+ except TypeError:
240
+ raise UserSetupError(
241
+ 'Public key file (%s) doesn\'t contain an OpenSSH public key. '
242
+ 'The key is not a valid base64 encoded value.\n%s' %
243
+ (key_file, key))
244
+
245
+ if len(key_value) < 4:
246
+ raise UserSetupError(
247
+ 'Public key file (%s) doesn\'t contain an OpenSSH public key. '
248
+ 'The key has invalid length.\n%s' %
249
+ (key_file, key))
250
+
251
+ # First 4 bytes is the length of key type.
252
+ decoded_length = struct.unpack_from('>I', key_value)[0]
253
+ if len(key_value) < 4 + decoded_length:
254
+ raise UserSetupError(
255
+ 'Public key file (%s) doesn\'t contain an OpenSSH public key. '
256
+ 'The key doesn\'t have a valid type.\n%s' %
257
+ (key_file, key))
258
+
259
+ decoded_type = key_value[4:4 + decoded_length]
260
+ if key_type != decoded_type:
261
+ raise UserSetupError(
262
+ 'Public key file (%s) doesn\'t contain an OpenSSH public key. '
263
+ 'The decoded key type doesn\'t match.\n%s' %
264
+ (key_file, key))
265
+
266
+ return key