knife-ec-backup 2.0.0.beta.4 → 2.0.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.
- 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
|