Module: Chef::Knife::EcBase

Included in:
EcBackup, EcImport, 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
149
150
151
152
# 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: value from chef-server-running.json, or localhost)'

    option :sql_port,
      :long => '--sql-port PORT',
      :description => 'PostgreSQL database port (default: value from chef-server-running.json, or 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.'

    option :skip_frozen_cookbook_status,
      :long => '--skip-frozen-cookbook-status',
      :boolean => true | false,
      :default => false,
      :description => "This will skip creating a status.json file for each cookbook, which includes the frozen status. This is useful when you dont want to persist cookbook's frozen status."
  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



275
276
277
# File 'lib/chef/knife/ec_base.rb', line 275

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

#configure_chefObject



129
130
131
132
133
134
135
136
137
# File 'lib/chef/knife/ec_base.rb', line 129

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



253
254
255
256
257
258
# File 'lib/chef/knife/ec_base.rb', line 253

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



186
187
188
189
# File 'lib/chef/knife/ec_base.rb', line 186

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



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

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

#org_adminObject



139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/chef/knife/ec_base.rb', line 139

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



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

def remote_user_list
  @remote_user_list ||= remote_users.keys
end

#remote_usersObject



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

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.



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

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

#serverObject



154
155
156
157
158
159
160
161
# File 'lib/chef/knife/ec_base.rb', line 154

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



206
207
208
209
210
# File 'lib/chef/knife/ec_base.rb', line 206

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



212
213
214
215
216
217
218
# File 'lib/chef/knife/ec_base.rb', line 212

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



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

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

#temporary_webui_keyObject



241
242
243
244
245
246
247
248
249
250
251
# File 'lib/chef/knife/ec_base.rb', line 241

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



191
192
193
194
195
196
197
198
199
200
# File 'lib/chef/knife/ec_base.rb', line 191

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



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

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

#veilObject



237
238
239
# File 'lib/chef/knife/ec_base.rb', line 237

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

#veil_configObject



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

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

#warn_on_incorrect_clients_group(dir, op) ⇒ Object



260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/chef/knife/ec_base.rb', line 260

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



220
221
222
223
224
225
226
227
228
229
230
# File 'lib/chef/knife/ec_base.rb', line 220

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