Module: Chef::Knife::EcBase

Included in:
EcBackup, EcRestore
Defined in:
lib/chef/knife/ec_base.rb

Defined Under Namespace

Classes: NoAdminFound, UnImplemented

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(includer) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/chef/knife/ec_base.rb', line 32

def self.included(includer)
  includer.class_eval do

    option :error_log_dir,
      :long => '--error-log-dir PATH',
      :description => 'Path to a directory where any errors will be logged'

    option :concurrency,
      :long => '--concurrency THREADS',
      :description => 'Maximum number of simultaneous requests to send (default: 10)'

    option :webui_key,
      :long => '--webui-key KEYPATH',
      :description => 'Path to the WebUI Key (default: Read from secrets store or /etc/opscode/webui_priv.pem or /hab/svc/automate-cs-oc-erchef/data/webui_priv.pem)'

    option :secrets_file_path,
      :long => '--secrets-file PATH',
      :description => 'Path to a valid private-chef-secrets.json file (default: /etc/opscode/private-chef-secrets.json)',
      :default => '/etc/opscode/private-chef-secrets.json'

    option :skip_useracl,
      :long => '--skip-useracl',
      :boolean => true,
      :default => false,
      :description => "Skip downloading/restoring User ACLs.  This is required for EC 11.0.2 and lower"

    option :skip_version,
      :long => '--skip-version-check',
      :boolean => true,
      :default => false,
      :description => "Skip Chef Infra Server version check.  This will also skip any auto-configured options"

    option :org,
      :long => "--only-org ORG",
      :description => "Only download/restore objects in the named organization (default: all orgs)"

    option :sql_host,
      :long => '--sql-host HOSTNAME',
      :description => 'PostgreSQL database hostname (default: localhost)',
      :default => "localhost"

    option :sql_port,
      :long => '--sql-port PORT',
      :description => 'PostgreSQL database port (default: 5432)',
      :default => 5432

    option :sql_db,
      :long => '--sql-db DBNAME',
      :description => 'PostgreSQL Chef Infra Server database name (default: opscode_chef or automate-cs-oc-erchef)'

    option :sql_user,
      :long => "--sql-user USERNAME",
      :description => 'User used to connect to the postgresql database.'

    option :sql_password,
      :long => "--sql-password PASSWORD",
      :description => 'Password used to connect to the postgresql database'

    option :sql_cert,
      :long => "--sql-cert ",
      :description => 'Path to client ssl cert'

    option :sql_key,
      :long => "--sql-key PATH",
      :description => 'Path to client ssl key'

    option :sql_rootcert,
    :long => "--sql-rootcert ",
    :description => 'Path to root ssl cert'

    option :with_user_sql,
      :long => '--with-user-sql',
      :description => 'Try direct data base access for user export/import.  Required to properly handle passwords, keys, and USAGs'

    option :with_key_sql,
      :long => '--with-key-sql',
      :description => 'Try direct data base access for key table export/import.  Required to properly handle rotated keys.'

    option :purge,
      :long => '--purge',
      :boolean => true | false,
      :default => false,
      :description => 'Syncs deletions from backup source to restore destination.'

    option :dry_run,
      :long => '--dry-run',
      :boolean => true | false,
      :default => false,
      :description => 'Report what actions would be taken without performing any.'
  end

  attr_accessor :dest_dir

  def configure_chef
    super
    Chef::Config[:concurrency] = config[:concurrency].to_i if config[:concurrency]
    if defined?(Chef::ChefFS::Parallelizer)
      Chef::ChefFS::Parallelizer.threads = (Chef::Config[:concurrency] || 10) - 1
    elsif defined?(ChefUtils::DefaultThreadPool)
      ChefUtils::DefaultThreadPool.instance.threads = (Chef::Config[:concurrency] || 10) - 1
    end
  end

  def org_admin
    rest = Chef::ServerAPI.new(Chef::Config.chef_server_url, {:api_version => "0"})
    admin_users = rest.get('groups/admins')['users']
    org_members = rest.get('users').map { |user| user['user']['username'] }
    admin_users.delete_if { |user| !org_members.include?(user) || user == 'pivotal' }
    if admin_users.empty?
      raise Chef::Knife::EcBase::NoAdminFound
    else
      admin_users[0]
    end
  rescue Net::HTTPClientException => ex
    knife_ec_error_handler.add(ex)
  end
end

Instance Method Details

#completion_bannerObject



271
272
273
# File 'lib/chef/knife/ec_base.rb', line 271

def completion_banner
  puts "#{ui.color("** Finished **", :magenta)}"
end

#configure_chefObject



125
126
127
128
129
130
131
132
133
# File 'lib/chef/knife/ec_base.rb', line 125

def configure_chef
  super
  Chef::Config[:concurrency] = config[:concurrency].to_i if config[:concurrency]
  if defined?(Chef::ChefFS::Parallelizer)
    Chef::ChefFS::Parallelizer.threads = (Chef::Config[:concurrency] || 10) - 1
  elsif defined?(ChefUtils::DefaultThreadPool)
    ChefUtils::DefaultThreadPool.instance.threads = (Chef::Config[:concurrency] || 10) - 1
  end
end

#ensure_webui_key_exists!Object



249
250
251
252
253
254
# File 'lib/chef/knife/ec_base.rb', line 249

def ensure_webui_key_exists!
  if !File.exist?(webui_key)
    ui.error("Webui Key (#{config[:webui_key]}) does not exist.")
    exit 1
  end
end

#knife_ec_error_handlerObject



182
183
184
185
# File 'lib/chef/knife/ec_base.rb', line 182

def knife_ec_error_handler
  error_dir = config[:error_log_dir] || dest_dir
  @knife_ec_error_handler ||= Chef::Knife::EcErrorHandler.new(error_dir, self.class)
end

#local_user_listObject



173
174
175
# File 'lib/chef/knife/ec_base.rb', line 173

def local_user_list
  @local_user_list ||= Dir.glob("#{dest_dir}/users/*\.json").map { |u| File.basename(u, '.json') }
end

#org_adminObject



135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/chef/knife/ec_base.rb', line 135

def org_admin
  rest = Chef::ServerAPI.new(Chef::Config.chef_server_url, {:api_version => "0"})
  admin_users = rest.get('groups/admins')['users']
  org_members = rest.get('users').map { |user| user['user']['username'] }
  admin_users.delete_if { |user| !org_members.include?(user) || user == 'pivotal' }
  if admin_users.empty?
    raise Chef::Knife::EcBase::NoAdminFound
  else
    admin_users[0]
  end
rescue Net::HTTPClientException => ex
  knife_ec_error_handler.add(ex)
end

#remote_user_listObject



169
170
171
# File 'lib/chef/knife/ec_base.rb', line 169

def remote_user_list
  @remote_user_list ||= remote_users.keys
end

#remote_usersObject



165
166
167
# File 'lib/chef/knife/ec_base.rb', line 165

def remote_users
  @remote_users ||= rest.get('/users')
end

#restObject

Since knife-ec-backup hasn’t been updated to use API V1 keys endpoints we should explicitly as for V0.



161
162
163
# File 'lib/chef/knife/ec_base.rb', line 161

def rest
  @rest ||= Chef::ServerAPI.new(server.root_url, {:api_version => "0"})
end

#serverObject



150
151
152
153
154
155
156
157
# File 'lib/chef/knife/ec_base.rb', line 150

def server
  @server ||= if Chef::Config.chef_server_root.nil?
                ui.warn("chef_server_root not found in knife configuration; using chef_server_url")
                Chef::Server.from_chef_server_url(Chef::Config.chef_server_url)
              else
                Chef::Server.new(Chef::Config.chef_server_root)
              end
end

#set_client_config!Object



202
203
204
205
206
# File 'lib/chef/knife/ec_base.rb', line 202

def set_client_config!
  Chef::Config.custom_http_headers = (Chef::Config.custom_http_headers || {}).merge({'x-ops-request-source' => 'web'})
  Chef::Config.node_name = 'pivotal'
  Chef::Config.client_key = webui_key
end

#set_dest_dir_from_args!Object



208
209
210
211
212
213
214
# File 'lib/chef/knife/ec_base.rb', line 208

def set_dest_dir_from_args!
  if name_args.length <= 0
    ui.error("Must specify backup directory as an argument.")
    exit 1
  end
  @dest_dir = name_args[0]
end

#set_skip_user_acl!Object



198
199
200
# File 'lib/chef/knife/ec_base.rb', line 198

def set_skip_user_acl!
  config[:skip_useracl] ||= !(server.supports_user_acls? || server.)
end

#temporary_webui_keyObject



237
238
239
240
241
242
243
244
245
246
247
# File 'lib/chef/knife/ec_base.rb', line 237

def temporary_webui_key
  @temp_webui_key ||= begin
                        key_data = veil.get("chef-server", "webui_key")
                        f = Tempfile.new("knife-ec-backup")
                        f.write(key_data)
                        f.flush
                        f.close
                        f
                      end
  @temp_webui_key.path
end

#user_acl_restObject



187
188
189
190
191
192
193
194
195
196
# File 'lib/chef/knife/ec_base.rb', line 187

def user_acl_rest
  @user_acl_rest ||= if config[:skip_version]
                       rest
                     elsif server.supports_user_acls?
                       rest
                     elsif server.
                       Chef::ServerAPI.new("http://127.0.0.1:9465", {:api_version => "0"})
                     end

end

#users_for_purgeObject



177
178
179
180
# File 'lib/chef/knife/ec_base.rb', line 177

def users_for_purge
  # not itended to be called from ec_base
  raise Chef::Knife::EcBase::UnImplemented
end

#veilObject



233
234
235
# File 'lib/chef/knife/ec_base.rb', line 233

def veil
  Veil::CredentialCollection.from_config(veil_config)
end

#veil_configObject



228
229
230
231
# File 'lib/chef/knife/ec_base.rb', line 228

def veil_config
  { provider: 'chef-secrets-file',
    path: config[:secrets_file_path] }
end

#warn_on_incorrect_clients_group(dir, op) ⇒ Object



256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/chef/knife/ec_base.rb', line 256

def warn_on_incorrect_clients_group(dir, op)
  orgs = Dir[::File.join(dir, 'organizations', '*')].map { |d| ::File.basename(d) }
  orgs.each do |org|
    clients_path = ::File.expand_path(::File.join(dir, 'organizations', org, 'clients'))
    clients_in_org = Dir[::File.join(clients_path, '*')].map { |d| ::File.basename(d, '.json') }
    clients_group_path = ::File.expand_path(::File.join(dir, 'organizations', org, 'groups', 'clients.json'))
    existing_group_data = FFI_Yajl::Parser.parse(::File.read(clients_group_path), symbolize_names: false)
    existing_group_data['clients'] = [] unless existing_group_data.key?('clients')
    if existing_group_data['clients'].length != clients_in_org.length
      ui.warn "There are #{(existing_group_data['clients'].length - clients_in_org.length).abs} missing clients in #{org}'s client group file #{clients_group_path}. If this is not intentional do NOT proceed with a restore until corrected. `knife tidy backup clean` will auto-correct this. https://github.com/chef-customers/knife-tidy"
      ui.confirm("\nDo you still wish to continue with the restore?") if op == "restore"
    end
  end
end

#webui_keyObject



216
217
218
219
220
221
222
223
224
225
226
# File 'lib/chef/knife/ec_base.rb', line 216

def webui_key
  if config[:webui_key]
    config[:webui_key]
  elsif Chef::Automate.is_installed?
    config[:webui_key] = Chef::Automate.config[:webui_key]
  elsif veil.exist?("chef-server", "webui_key")
    temporary_webui_key
  else
    '/etc/opscode/webui_priv.pem'
  end
end