knife-ec-backup 2.0.0.beta.4 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +26 -1
- data/lib/chef/knife/ec_backup.rb +6 -4
- data/lib/chef/knife/ec_base.rb +5 -0
- data/lib/chef/knife/ec_key_base.rb +11 -1
- data/lib/chef/knife/ec_key_export.rb +27 -6
- data/lib/chef/knife/ec_key_import.rb +119 -5
- data/lib/chef/knife/ec_restore.rb +41 -13
- data/lib/chef/org_id_cache.rb +50 -0
- data/lib/chef/org_id_cache.rb~ +45 -0
- data/lib/knife_ec_backup/version.rb +1 -1
- data/spec/chef/knife/ec_key_export_spec.rb +104 -0
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f8f08431a9c4bb9de895e7f1de514e586603cf00
|
4
|
+
data.tar.gz: bcc07b67d433964eb6b8b080650110b98c7291c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 964129af4f8e76e3d339f4a10e2d682fbc864d1e2799b6274a2d4fb4f7b4ad0cb2d31deda74fa11f2d62033b60f0aa170e397c44d07bea56294b296abbe1887f
|
7
|
+
data.tar.gz: f9de155880bf15cac8afafdcfd81365e8c2764b15065faa501ee92cb7c10772c6259e24434701dddcbe24404c74414c964b4308ddc43fce73ea37f1e52cb2f20
|
data/README.md
CHANGED
@@ -13,13 +13,21 @@ tool.
|
|
13
13
|
|
14
14
|
# Requirements
|
15
15
|
|
16
|
-
This knife plugin requires Chef 11.8+.
|
16
|
+
This knife plugin requires Chef Client 11.8+.
|
17
17
|
|
18
18
|
## Chef 10
|
19
19
|
|
20
20
|
Users who are still using Chef 10 can use the most recent 1.x version
|
21
21
|
of this gem. Version 1.x additionally depends on knife-essentials.
|
22
22
|
|
23
|
+
## Server Support
|
24
|
+
|
25
|
+
This plugin currently supports Enterprise Chef 11 and Chef Server 12.
|
26
|
+
Support for the beta key rotation features is provided via the
|
27
|
+
`--with-keys-sql` flag, but users of this feature should note that
|
28
|
+
this may change once the Chef Server supports an API-based export of
|
29
|
+
the key data.
|
30
|
+
|
23
31
|
# Installation
|
24
32
|
|
25
33
|
## Chef Server Install (Recommended)
|
@@ -86,6 +94,13 @@ The following options are supported across all subcommands:
|
|
86
94
|
Server. This is required to correctly handle user passwords and
|
87
95
|
to ensure user-specific association groups are not duplicated.
|
88
96
|
|
97
|
+
* `--with-key-sql`: Whether to backup/restore key data directly
|
98
|
+
from the database. This requires access to the listening
|
99
|
+
postgresql port on the Chef Server. This is required to correctly
|
100
|
+
handle keys in Chef Servers with multikey support. This option
|
101
|
+
will only work on `restore` if it was also used during the
|
102
|
+
`backup`.
|
103
|
+
|
89
104
|
* `--skip-useracl`:
|
90
105
|
Skip download/restore of the user ACLs. User ACLs are the
|
91
106
|
permissions that actors have *on other global users*. These are
|
@@ -187,6 +202,13 @@ Private Chef server. DEST_DIR should be a backup directory created by
|
|
187
202
|
duplicated. This option will only work on `restore` if it was also
|
188
203
|
used during the `backup`.
|
189
204
|
|
205
|
+
* `--with-key-sql`: Whether to backup/restore key data directly
|
206
|
+
from the database. This requires access to the listening
|
207
|
+
postgresql port on the Chef Server. This is required to correctly
|
208
|
+
handle keys in Chef Servers with multikey support. This option
|
209
|
+
will only work on `restore` if it was also used during the
|
210
|
+
`backup`.
|
211
|
+
|
190
212
|
* `--skip-useracl`:
|
191
213
|
Skip download/restore of the user ACLs. User ACLs are the
|
192
214
|
permissions that actors have *on other global users*. These are
|
@@ -217,6 +239,9 @@ Please note, most user should use `knife ec restore` with the
|
|
217
239
|
|
218
240
|
# Known Bugs
|
219
241
|
|
242
|
+
- knife-ec-backup cannot be installed in the embedded gemset of Chef
|
243
|
+
Server 12. This will be resolved in a future Chef Server release.
|
244
|
+
|
220
245
|
- `knife ec restore` can fail to restore cookbooks, failing with an
|
221
246
|
internal server error. A common cause of this problem is a
|
222
247
|
concurrency bug in Chef Server. Setting `--concurrency 1` can often
|
data/lib/chef/knife/ec_backup.rb
CHANGED
@@ -33,8 +33,8 @@ class Chef
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
if config[:with_user_sql]
|
37
|
-
|
36
|
+
if config[:with_user_sql] || config[:with_key_sql]
|
37
|
+
export_from_sql
|
38
38
|
end
|
39
39
|
|
40
40
|
ensure_dir("#{dest_dir}/organizations")
|
@@ -86,15 +86,17 @@ class Chef
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
-
def
|
89
|
+
def export_from_sql
|
90
90
|
require 'chef/knife/ec_key_export'
|
91
91
|
Chef::Knife::EcKeyExport.deps
|
92
92
|
k = Chef::Knife::EcKeyExport.new
|
93
|
-
k.name_args = ["#{dest_dir}/key_dump.json"]
|
93
|
+
k.name_args = ["#{dest_dir}/key_dump.json", "#{dest_dir}/key_table_dump.json"]
|
94
94
|
k.config[:sql_host] = config[:sql_host]
|
95
95
|
k.config[:sql_port] = config[:sql_port]
|
96
96
|
k.config[:sql_user] = config[:sql_user]
|
97
97
|
k.config[:sql_password] = config[:sql_password]
|
98
|
+
k.config[:skip_users_table] = !config[:with_user_sql]
|
99
|
+
k.config[:skip_keys_table] = !config[:with_key_sql]
|
98
100
|
k.run
|
99
101
|
end
|
100
102
|
|
data/lib/chef/knife/ec_base.rb
CHANGED
@@ -72,6 +72,11 @@ class Chef
|
|
72
72
|
option :with_user_sql,
|
73
73
|
:long => '--with-user-sql',
|
74
74
|
:description => 'Try direct data base access for user export/import. Required to properly handle passwords, keys, and USAGs'
|
75
|
+
|
76
|
+
option :with_key_sql,
|
77
|
+
:long => '--with-key-sql',
|
78
|
+
:description => 'Try direct data base access for key table export/import. Required to properly handle rotated keys.'
|
79
|
+
|
75
80
|
end
|
76
81
|
|
77
82
|
attr_accessor :dest_dir
|
@@ -47,6 +47,16 @@ class Chef
|
|
47
47
|
option :sql_password,
|
48
48
|
:long => "--sql-password PASSWORD",
|
49
49
|
:description => 'Password used to connect to the postgresql database'
|
50
|
+
|
51
|
+
option :skip_keys_table,
|
52
|
+
:long => "--skip-keys-table",
|
53
|
+
:description => "Skip Chef 12-only keys table",
|
54
|
+
:default => false
|
55
|
+
|
56
|
+
option :skip_users_table,
|
57
|
+
:long => "--skip-users-table",
|
58
|
+
:description => "Skip users table",
|
59
|
+
:default => false
|
50
60
|
end
|
51
61
|
end
|
52
62
|
|
@@ -54,7 +64,7 @@ class Chef
|
|
54
64
|
@db ||= begin
|
55
65
|
require 'sequel'
|
56
66
|
server_string = "#{config[:sql_user]}:#{config[:sql_password]}@#{config[:sql_host]}:#{config[:sql_port]}/opscode_chef"
|
57
|
-
::Sequel.connect("postgres://#{server_string}")
|
67
|
+
::Sequel.connect("postgres://#{server_string}", :convert_infinite_timestamps => :string)
|
58
68
|
end
|
59
69
|
end
|
60
70
|
|
@@ -25,20 +25,41 @@ class Chef
|
|
25
25
|
|
26
26
|
include Knife::EcKeyBase
|
27
27
|
|
28
|
-
banner "knife ec key export [
|
28
|
+
banner "knife ec key export [USER_DATA_PATH] [KEY_DATA_PATH]"
|
29
29
|
|
30
30
|
def run
|
31
31
|
if config[:sql_user].nil? || config[:sql_password].nil?
|
32
32
|
load_config_from_file!
|
33
33
|
end
|
34
34
|
|
35
|
-
|
36
|
-
|
35
|
+
# user_data_path defaults to key_dump.json to support
|
36
|
+
# older knife-ec-backup exports
|
37
|
+
user_data_path = @name_args[0] || "key_dump.json"
|
38
|
+
key_data_path = @name_args[1] || "key_table_dump.json"
|
39
|
+
|
40
|
+
export(:users, user_data_path) unless config[:skip_users_table]
|
41
|
+
|
42
|
+
begin
|
43
|
+
export_keys(key_data_path) unless config[:skip_keys_table]
|
44
|
+
rescue Sequel::DatabaseError => e
|
45
|
+
if e.message =~ /^PG::UndefinedTable/
|
46
|
+
ui.error "Keys table not found. The keys table only exists on Chef Server 12."
|
47
|
+
ui.error "Chef Server 11 users should use the --skip-keys-table option to avoid this error."
|
48
|
+
exit 1
|
49
|
+
else
|
50
|
+
raise
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def export_keys(path)
|
56
|
+
data = db.fetch('SELECT keys_by_name.*, orgs.name AS "org_name" FROM keys_by_name LEFT JOIN orgs ON keys_by_name.org_id=orgs.id')
|
57
|
+
File.open(path, 'w') { |file| file.write(data.all.to_json) }
|
37
58
|
end
|
38
59
|
|
39
|
-
def export(path)
|
40
|
-
|
41
|
-
File.open(path, 'w') { |file| file.write(
|
60
|
+
def export(table, path)
|
61
|
+
data = db.select.from(table)
|
62
|
+
File.open(path, 'w') { |file| file.write(data.all.to_json) }
|
42
63
|
end
|
43
64
|
end
|
44
65
|
end
|
@@ -18,6 +18,7 @@
|
|
18
18
|
|
19
19
|
require 'chef/knife'
|
20
20
|
require 'chef/knife/ec_key_base'
|
21
|
+
require 'chef/org_id_cache'
|
21
22
|
|
22
23
|
class Chef
|
23
24
|
class Knife
|
@@ -25,7 +26,7 @@ class Chef
|
|
25
26
|
|
26
27
|
include Knife::EcKeyBase
|
27
28
|
|
28
|
-
banner "knife ec key import [
|
29
|
+
banner "knife ec key import [USER_DATA_PATH] [KEY_DATA_PATH]"
|
29
30
|
|
30
31
|
option :skip_pivotal,
|
31
32
|
:long => "--[no-]skip-pivotal",
|
@@ -39,17 +40,130 @@ class Chef
|
|
39
40
|
:boolean => true,
|
40
41
|
:description => "Upload user ids."
|
41
42
|
|
43
|
+
option :users_only,
|
44
|
+
:long => "--users-only",
|
45
|
+
:default => false,
|
46
|
+
:description => "Only update users (skip client key data)"
|
47
|
+
|
48
|
+
option :clients_only,
|
49
|
+
:log => "--clients-only",
|
50
|
+
:default => false,
|
51
|
+
:description => "Only update client key data. Implies --skip-users-table"
|
52
|
+
|
42
53
|
def run
|
43
54
|
if config[:sql_user].nil? || config[:sql_password].nil?
|
44
55
|
load_config_from_file!
|
45
56
|
end
|
46
57
|
|
47
|
-
|
48
|
-
|
58
|
+
@org_id_cache = Chef::OrgIdCache.new(db)
|
59
|
+
|
60
|
+
# user_data_path defaults to key_dump.json to support
|
61
|
+
# older knife-ec-backup exports
|
62
|
+
user_data_path = @name_args[0] || "key_dump.json"
|
63
|
+
key_data_path = @name_args[1] || "key_table_dump.json"
|
64
|
+
import_user_data(user_data_path) unless (config[:skip_users_table] || config[:clients_only])
|
65
|
+
import_key_data(key_data_path) unless config[:skip_keys_table]
|
49
66
|
end
|
50
67
|
|
68
|
+
def import_key_data(path)
|
69
|
+
key_data = JSON.parse(File.read(path))
|
70
|
+
key_data.each do |d|
|
71
|
+
case d['type']
|
72
|
+
when 'client'
|
73
|
+
next if config[:users_only]
|
74
|
+
insert_key_data_for_client(d)
|
75
|
+
when 'user'
|
76
|
+
next if config[:clients_only]
|
77
|
+
insert_key_data_for_user(d)
|
78
|
+
else
|
79
|
+
ui.warn "Unkown actor type #{d['type']} for #{d['name']}"
|
80
|
+
next
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# If a given key_name already exists for the client, update it,
|
86
|
+
# otherwise insert a new key into the key table.
|
87
|
+
#
|
88
|
+
# Unlike users, we never want to keep the internal ID from the
|
89
|
+
# backup.
|
90
|
+
#
|
91
|
+
# The org_id is likely different than that stored in the backup.
|
92
|
+
# We query the new org_id based on org_name, caching it to avoid
|
93
|
+
# multiple lookups in a large org.
|
94
|
+
def insert_key_data_for_client(d)
|
95
|
+
ui.msg "Updating key data for client[#{d['name']}]"
|
96
|
+
|
97
|
+
org_id = @org_id_cache.fetch(d['org_name'])
|
98
|
+
if org_id.nil?
|
99
|
+
ui.warn "Could not find organization for client[#{d['name']}], skipping."
|
100
|
+
ui.warn "Organizations much be restored before key data can be imported."
|
101
|
+
return
|
102
|
+
end
|
103
|
+
|
104
|
+
existing_client = db[:clients].where(:org_id => org_id, :name => d['name']).first
|
105
|
+
if existing_client.nil?
|
106
|
+
ui.warn "Did not find existing client record for #{d['name']}, skipping."
|
107
|
+
ui.warn "Client records must be restored before key data can be imported."
|
108
|
+
return
|
109
|
+
end
|
110
|
+
|
111
|
+
new_id = existing_client[:id]
|
112
|
+
Chef::Log.debug("Found client id for #{d['name']}: #{new_id}")
|
113
|
+
upsert_key_record(key_record_for_db(d, new_id))
|
114
|
+
end
|
115
|
+
|
116
|
+
# Insert key data for each user record
|
117
|
+
#
|
118
|
+
# When :skip_id's is set, we are not using the ids from the
|
119
|
+
# backup. In this case, look up the user id in the users table.
|
120
|
+
#
|
121
|
+
# When :skip_id's is not set, we are using the ids from the
|
122
|
+
# backup. The update_key trigger on the users table should
|
123
|
+
# ensure that the user ids have already been replaced and should
|
124
|
+
# match what we have in the backup.
|
125
|
+
def insert_key_data_for_user(d)
|
126
|
+
if d['name'] == 'pivotal' && config[:skip_pivotal]
|
127
|
+
ui.warn "Skipping pivotal user."
|
128
|
+
return
|
129
|
+
end
|
130
|
+
ui.msg "Updating key data for user[#{d['name']}]"
|
131
|
+
new_id = if config[:skip_ids]
|
132
|
+
db[:users].where(:username => d['name']).first[:id]
|
133
|
+
else
|
134
|
+
d['id']
|
135
|
+
end
|
136
|
+
Chef::Log.debug("Found user id for #{d['name']}: #{new_id}")
|
137
|
+
upsert_key_record(key_record_for_db(d, new_id))
|
138
|
+
end
|
139
|
+
|
140
|
+
def upsert_key_record(r)
|
141
|
+
key_records_to_update = db[:keys].where(:key_name => r[:key_name], :id => r[:id])
|
142
|
+
case key_records_to_update.count
|
143
|
+
when 0
|
144
|
+
Chef::Log.debug("No existing records found for #{r[:key_name]}, #{r[:id]}")
|
145
|
+
db[:keys].insert(r)
|
146
|
+
when 1
|
147
|
+
Chef::Log.debug("1 record found for #{r[:key_name]}, #{r[:id]}")
|
148
|
+
key_records_to_update.update(r)
|
149
|
+
else
|
150
|
+
ui.warn "Found too many records for actor id #{r[:id]} key #{d[:key_name]}"
|
151
|
+
return
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def key_record_for_db(d, new_id=nil)
|
156
|
+
{
|
157
|
+
:id => new_id || d['id'],
|
158
|
+
:key_name => d['key_name'],
|
159
|
+
:public_key => d['public_key'],
|
160
|
+
:key_version => d['key_version'],
|
161
|
+
:created_at => Time.now,
|
162
|
+
:expires_at => d['expires_at']
|
163
|
+
}
|
164
|
+
end
|
51
165
|
|
52
|
-
def
|
166
|
+
def import_user_data(path)
|
53
167
|
key_data = JSON.parse(File.read(path))
|
54
168
|
key_data.each do |d|
|
55
169
|
if d['username'] == 'pivotal' && config[:skip_pivotal]
|
@@ -57,7 +171,7 @@ class Chef
|
|
57
171
|
next
|
58
172
|
end
|
59
173
|
|
60
|
-
ui.msg "Updating
|
174
|
+
ui.msg "Updating user record for #{d['username']}"
|
61
175
|
users_to_update = db[:users].where(:username => d['username'])
|
62
176
|
|
63
177
|
if users_to_update.count != 1
|
@@ -44,12 +44,15 @@ class Chef
|
|
44
44
|
restore_user_sql if config[:with_user_sql]
|
45
45
|
|
46
46
|
for_each_organization do |orgname|
|
47
|
+
ui.msg "Restoring organization[#{orgname}]"
|
47
48
|
create_organization(orgname)
|
48
49
|
restore_open_invitations(orgname)
|
49
50
|
add_users_to_org(orgname)
|
50
51
|
upload_org_data(orgname)
|
51
52
|
end
|
52
53
|
|
54
|
+
restore_key_sql if config[:with_key_sql]
|
55
|
+
|
53
56
|
if config[:skip_useracl]
|
54
57
|
ui.warn("Skipping user ACL update. To update user ACLs, remove --skip-useracl or upgrade your Enterprise Chef Server.")
|
55
58
|
else
|
@@ -98,7 +101,7 @@ class Chef
|
|
98
101
|
end
|
99
102
|
|
100
103
|
def restore_user_acls
|
101
|
-
ui.msg "Restoring user ACLs
|
104
|
+
ui.msg "Restoring user ACLs"
|
102
105
|
for_each_user do |name|
|
103
106
|
user_acl = JSONCompat.from_json(File.read("#{dest_dir}/user_acls/#{name}.json"))
|
104
107
|
put_acl(user_acl_rest, "users/#{name}/_acl", user_acl)
|
@@ -126,7 +129,7 @@ class Chef
|
|
126
129
|
end
|
127
130
|
|
128
131
|
def restore_users
|
129
|
-
ui.msg "Restoring users
|
132
|
+
ui.msg "Restoring users"
|
130
133
|
for_each_user do |name|
|
131
134
|
user = JSONCompat.from_json(File.read("#{dest_dir}/users/#{name}.json"))
|
132
135
|
begin
|
@@ -144,16 +147,35 @@ class Chef
|
|
144
147
|
end
|
145
148
|
end
|
146
149
|
|
150
|
+
def ec_key_import
|
151
|
+
@ec_key_import ||= begin
|
152
|
+
require 'chef/knife/ec_key_import'
|
153
|
+
k = Chef::Knife::EcKeyImport.new
|
154
|
+
k.name_args = ["#{dest_dir}/key_dump.json", "#{dest_dir}/key_table_dump.json"]
|
155
|
+
k.config[:skip_pivotal] = true
|
156
|
+
k.config[:skip_ids] = false
|
157
|
+
k.config[:sql_host] = config[:sql_host]
|
158
|
+
k.config[:sql_port] = config[:sql_port]
|
159
|
+
k.config[:sql_user] = config[:sql_user]
|
160
|
+
k.config[:sql_password] = config[:sql_password]
|
161
|
+
k
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
147
165
|
def restore_user_sql
|
148
|
-
|
149
|
-
k =
|
150
|
-
k.
|
151
|
-
k.config[:
|
152
|
-
k.
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
k
|
166
|
+
k = ec_key_import
|
167
|
+
k.config[:skip_users_table] = false
|
168
|
+
k.config[:skip_keys_table] = false
|
169
|
+
k.config[:users_only] = true
|
170
|
+
k.run
|
171
|
+
end
|
172
|
+
|
173
|
+
def restore_key_sql
|
174
|
+
k = ec_key_import
|
175
|
+
k.config[:skip_users_table] = true
|
176
|
+
k.config[:skip_keys_table] = false
|
177
|
+
k.config[:users_only] = false
|
178
|
+
k.config[:clients_only] = true
|
157
179
|
k.run
|
158
180
|
end
|
159
181
|
|
@@ -172,7 +194,7 @@ class Chef
|
|
172
194
|
Chef::Config.chef_server_url = "#{server.root_url}/organizations/#{name}"
|
173
195
|
|
174
196
|
# Upload the admins group and billing-admins acls
|
175
|
-
ui.msg "Restoring
|
197
|
+
ui.msg "Restoring org admin data"
|
176
198
|
chef_fs_config = Chef::ChefFS::Config.new
|
177
199
|
|
178
200
|
# Handle Admins and Billing Admins seperately
|
@@ -215,7 +237,13 @@ class Chef
|
|
215
237
|
group_acl_paths = Chef::ChefFS::FileSystem.list(chef_fs_config.local_fs, Chef::ChefFS::FilePattern.new('/acls/groups/*')).select { |entry| entry.name != 'billing-admins.json' }.map { |entry| entry.path }
|
216
238
|
acl_paths = Chef::ChefFS::FileSystem.list(chef_fs_config.local_fs, Chef::ChefFS::FilePattern.new('/acls/*')).select { |entry| entry.name != 'groups' }.map { |entry| entry.path }
|
217
239
|
|
218
|
-
|
240
|
+
|
241
|
+
# Store organization data in a particular order:
|
242
|
+
# - clients must be uploaded before groups (in top_level_paths)
|
243
|
+
# - groups must be uploaded before any acl's
|
244
|
+
# - groups must be uploaded twice to account for Chef Server versions that don't
|
245
|
+
# accept group members on POST
|
246
|
+
(top_level_paths + group_paths*2 + group_acl_paths + acl_paths).each do |path|
|
219
247
|
Chef::ChefFS::FileSystem.copy_to(Chef::ChefFS::FilePattern.new(path), chef_fs_config.local_fs, chef_fs_config.chef_fs, nil, config, ui, proc { |entry| chef_fs_config.format_path(entry) })
|
220
248
|
end
|
221
249
|
|
@@ -0,0 +1,50 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Steven Danna (<steve@chef.io>)
|
3
|
+
# Copyright:: Copyright (c) 2015 Chef Software, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
class Chef
|
20
|
+
class OrgIdCache
|
21
|
+
NO_ORG = "not here"
|
22
|
+
|
23
|
+
attr_accessor :db, :cache
|
24
|
+
def initialize(db)
|
25
|
+
@db = db
|
26
|
+
@cache = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
def fetch(org_name)
|
30
|
+
if cache.key?(org_name) && cache[org_name] != NO_ORG
|
31
|
+
cache[org_name]
|
32
|
+
elsif cache.key?(org_name) && cache[org_name] == NO_ORG
|
33
|
+
nil
|
34
|
+
else
|
35
|
+
r = db.select(:id).from(:orgs).where(:name => org_name).first
|
36
|
+
if r.nil?
|
37
|
+
store(org_name, NO_ORG)
|
38
|
+
nil
|
39
|
+
else
|
40
|
+
store(org_name, r[:id])
|
41
|
+
r[:id]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def store(org_name, org_guid)
|
47
|
+
cache[org_name] = org_guid
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Steven Danna (<steve@chef.io>)
|
3
|
+
# Copyright:: Copyright (c) 2014 Chef Software, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
class OrgIdCache
|
20
|
+
class NoSuchOrg < RuntimeError; end
|
21
|
+
|
22
|
+
attr_accesor :db, :cache
|
23
|
+
def initialize(db)
|
24
|
+
@db = db
|
25
|
+
@cache = {}
|
26
|
+
end
|
27
|
+
|
28
|
+
def fetch(org_name)
|
29
|
+
if cache.key?(org_name)
|
30
|
+
cache[org_name]
|
31
|
+
else
|
32
|
+
r = db.get(:id).from(:orgs).where(:org_name => org_name)
|
33
|
+
if r.nil?
|
34
|
+
raise NoSuchOrg
|
35
|
+
else
|
36
|
+
store(org_name, r[:id])
|
37
|
+
r[:id]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def store(org_name, org_guid)
|
43
|
+
cache[org_name] = org_guid
|
44
|
+
end
|
45
|
+
end
|
@@ -1,2 +1,106 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
|
2
2
|
require 'chef/knife/ec_key_export'
|
3
|
+
require 'sequel'
|
4
|
+
require 'json'
|
5
|
+
require 'securerandom'
|
6
|
+
require 'fakefs/spec_helpers'
|
7
|
+
|
8
|
+
def user_record(name)
|
9
|
+
{ "id" => SecureRandom.uuid,
|
10
|
+
"authz_id" => SecureRandom.uuid,
|
11
|
+
"username" => name,
|
12
|
+
"email" => "#{name}@example.com",
|
13
|
+
"pubkey_version" => 0,
|
14
|
+
"public_key" => "BEGIN RSA PUBLIC KEY",
|
15
|
+
"serialized_object" => "{ \"a string\" => \"of JSONNNN\"}",
|
16
|
+
"last_updated_by" => "a time",
|
17
|
+
"created_at" => "a time",
|
18
|
+
"updated_at" => "a time",
|
19
|
+
"external_authentication_uid" => nil,
|
20
|
+
"recovery_authentication_enabled" => false,
|
21
|
+
"admin" => false,
|
22
|
+
"hashed_password" => "alkdsj5834751934",
|
23
|
+
"salt" => "01943891jgfkjf",
|
24
|
+
"hash_type" => "not good 1.0" }
|
25
|
+
end
|
26
|
+
|
27
|
+
def key_record(name, type, key_name)
|
28
|
+
{
|
29
|
+
"id" => SecureRandom.uuid,
|
30
|
+
"org_id" => SecureRandom.uuid,
|
31
|
+
"name" => name,
|
32
|
+
"authz_id" => SecureRandom.uuid,
|
33
|
+
"type" => type.to_s,
|
34
|
+
"key_name" => key_name,
|
35
|
+
"public_key" => "BEGIN RSA PUBLIC KEY",
|
36
|
+
"key_version" => 0,
|
37
|
+
"expires_at" => "a time"
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
describe Chef::Knife::EcKeyExport do
|
42
|
+
include FakeFS::SpecHelpers
|
43
|
+
let(:users_table) do
|
44
|
+
[ user_record("jane"),
|
45
|
+
user_record("bob") ]
|
46
|
+
end
|
47
|
+
|
48
|
+
let(:keys_by_name_table) do
|
49
|
+
[ key_record("bob", :user, "default"),
|
50
|
+
key_record("jane", :user, "default"),
|
51
|
+
key_record("rabbit", :client, "default") ]
|
52
|
+
end
|
53
|
+
|
54
|
+
let(:db) do
|
55
|
+
# This seem to be the way to vary fetched data on output
|
56
|
+
# See https://groups.google.com/forum/#!topic/sequel-talk/NZuxcoXN30M
|
57
|
+
output = Proc.new do |query|
|
58
|
+
case query
|
59
|
+
when /SELECT.*users/ then users_table
|
60
|
+
when /SELECT.*keys_by_name/ then keys_by_name_table
|
61
|
+
end
|
62
|
+
end
|
63
|
+
d = Sequel.mock(:fetch => output)
|
64
|
+
allow(Sequel).to receive(:connect).and_return(d)
|
65
|
+
d
|
66
|
+
end
|
67
|
+
|
68
|
+
let(:knife) do
|
69
|
+
Chef::Knife::EcKeyExport.deps
|
70
|
+
k = Chef::Knife::EcKeyExport.new
|
71
|
+
k.config[:sql_user] = "opscode_chef"
|
72
|
+
k.config[:sql_password] = "apassword"
|
73
|
+
k.config[:sql_host] = "localhost"
|
74
|
+
k.config[:sql_port] = 5432
|
75
|
+
k
|
76
|
+
end
|
77
|
+
|
78
|
+
it "writes the users table to json" do
|
79
|
+
db; knife.run
|
80
|
+
expect(JSON.parse(File.read("key_dump.json"))).to eq(users_table)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "writes the keys_by_name table to json" do
|
84
|
+
db; knife.run
|
85
|
+
expect(JSON.parse(File.read("key_table_dump.json"))).to eq(keys_by_name_table)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "does not write the users table if :skip_users_table is set" do
|
89
|
+
knife.config[:skip_users_table] = true
|
90
|
+
db; knife.run
|
91
|
+
expect(File.exist?("key_dump.json")).to eq(false)
|
92
|
+
end
|
93
|
+
|
94
|
+
it "does not write the keys_by_name table if :skip_keys_table is set" do
|
95
|
+
knife.config[:skip_keys_table] = true
|
96
|
+
db; knife.run
|
97
|
+
expect(File.exist?("key_table_dump.json")).to eq(false)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "writes the tables to the specified files when given arguments" do
|
101
|
+
knife.name_args = ["user_table.json", "key_table.json"]
|
102
|
+
db; knife.run
|
103
|
+
expect(JSON.parse(File.read("user_table.json"))).to eq(users_table)
|
104
|
+
expect(JSON.parse(File.read("key_table.json"))).to eq(keys_by_name_table)
|
105
|
+
end
|
106
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: knife-ec-backup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Keiser
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-01-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -125,6 +125,8 @@ files:
|
|
125
125
|
- lib/chef/knife/ec_key_export.rb
|
126
126
|
- lib/chef/knife/ec_key_import.rb
|
127
127
|
- lib/chef/knife/ec_restore.rb
|
128
|
+
- lib/chef/org_id_cache.rb
|
129
|
+
- lib/chef/org_id_cache.rb~
|
128
130
|
- lib/chef/server.rb
|
129
131
|
- lib/chef/tsorter.rb
|
130
132
|
- lib/knife_ec_backup/version.rb
|
@@ -152,9 +154,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
152
154
|
version: '0'
|
153
155
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
154
156
|
requirements:
|
155
|
-
- - "
|
157
|
+
- - ">="
|
156
158
|
- !ruby/object:Gem::Version
|
157
|
-
version:
|
159
|
+
version: '0'
|
158
160
|
requirements: []
|
159
161
|
rubyforge_project:
|
160
162
|
rubygems_version: 2.4.1
|