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


31
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
# File 'lib/chef/knife/ec_base.rb', line 31

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)'

    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 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 Server database name (default: opscode_chef)',
      :default => "opscode_chef"

    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 :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]
    Chef::ChefFS::Parallelizer.threads = (Chef::Config[:concurrency] || 10) - 1
  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::HTTPServerException => ex
    knife_ec_error_handler.add(ex)
  end
end

Instance Method Details

#completion_bannerObject


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

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

#configure_chefObject


113
114
115
116
117
# File 'lib/chef/knife/ec_base.rb', line 113

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

#ensure_webui_key_exists!Object


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

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


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

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


157
158
159
# File 'lib/chef/knife/ec_base.rb', line 157

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

#org_adminObject


119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/chef/knife/ec_base.rb', line 119

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::HTTPServerException => ex
  knife_ec_error_handler.add(ex)
end

#remote_user_listObject


153
154
155
# File 'lib/chef/knife/ec_base.rb', line 153

def remote_user_list
  @remote_user_list ||= remote_users.keys
end

#remote_usersObject


149
150
151
# File 'lib/chef/knife/ec_base.rb', line 149

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.


145
146
147
# File 'lib/chef/knife/ec_base.rb', line 145

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

#serverObject


134
135
136
137
138
139
140
141
# File 'lib/chef/knife/ec_base.rb', line 134

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


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

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


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

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


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

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

#temporary_webui_keyObject


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

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


171
172
173
174
175
176
177
178
179
180
# File 'lib/chef/knife/ec_base.rb', line 171

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


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

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

#veilObject


215
216
217
# File 'lib/chef/knife/ec_base.rb', line 215

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

#veil_configObject


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

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

#warn_on_incorrect_clients_group(dir, op) ⇒ Object


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

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


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

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