Class: Chef::ApiClient::Registration

Inherits:
Object
  • Object
show all
Defined in:
lib/chef/api_client/registration.rb

Overview

Chef::ApiClient::Registration

Manages the process of creating or updating a Chef::ApiClient on the server and writing the resulting private key to disk. Registration uses the validator credentials for its API calls. This allows it to bootstrap a new client/node identity by borrowing the validator client identity when creating a new client.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, destination) ⇒ Registration

Returns a new instance of Registration.



37
38
39
40
41
# File 'lib/chef/api_client/registration.rb', line 37

def initialize(name, destination)
  @name = name
  @destination = destination
  @private_key = nil
end

Instance Attribute Details

#destinationObject (readonly)

Returns the value of attribute destination.



34
35
36
# File 'lib/chef/api_client/registration.rb', line 34

def destination
  @destination
end

#nameObject (readonly)

Returns the value of attribute name.



35
36
37
# File 'lib/chef/api_client/registration.rb', line 35

def name
  @name
end

#private_keyObject (readonly)

Returns the value of attribute private_key.



33
34
35
# File 'lib/chef/api_client/registration.rb', line 33

def private_key
  @private_key
end

Instance Method Details

#assert_destination_writable!Object



69
70
71
72
73
# File 'lib/chef/api_client/registration.rb', line 69

def assert_destination_writable!
  if (File.exists?(destination) && !File.writable?(destination)) or !File.writable?(File.dirname(destination))
    raise Chef::Exceptions::CannotWritePrivateKey, "I cannot write your private key to #{destination} - check permissions?"
  end
end

#createObject



92
93
94
95
96
# File 'lib/chef/api_client/registration.rb', line 92

def create
  response = http_api.post("clients", :name => name, :admin => false)
  @private_key = response["private_key"]
  response
end

#create_or_updateObject



83
84
85
86
87
88
89
90
# File 'lib/chef/api_client/registration.rb', line 83

def create_or_update
  create
rescue Net::HTTPServerException => e
  # If create fails because the client exists, attempt to update. This
  # requires admin privileges.
  raise unless e.response.code == "409"
  update
end

#file_flagsObject



116
117
118
119
120
121
# File 'lib/chef/api_client/registration.rb', line 116

def file_flags
  base_flags = File::CREAT|File::TRUNC|File::RDWR
  # Windows doesn't have symlinks, so it doesn't have NOFOLLOW
  base_flags |= File::NOFOLLOW if defined?(File::NOFOLLOW)
  base_flags
end

#http_apiObject



110
111
112
113
114
# File 'lib/chef/api_client/registration.rb', line 110

def http_api
  @http_api_as_validator ||= Chef::REST.new(Chef::Config[:chef_server_url],
                                            Chef::Config[:validation_client_name],
                                            Chef::Config[:validation_key])
end

#runObject

Runs the client registration process, including creating the client on the chef-server and writing its private key to disk. – If client creation fails with a 5xx, it is retried up to 5 times. These retries are on top of the retries with randomized exponential backoff built in to Chef::REST. The retries here are a workaround for failures caused by resource contention in Hosted Chef when creating a very large number of clients simultaneously, (e.g., spinning up 100s of ec2 nodes at once). Future improvements to the affected component should make these retries unnecessary.



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/chef/api_client/registration.rb', line 53

def run
  assert_destination_writable!
  retries = Config[:client_registration_retries] || 5
  begin
    create_or_update
  rescue Net::HTTPFatalError => e
    # HTTPFatalError implies 5xx.
    raise if retries <= 0
    retries -= 1
    Chef::Log.warn("Failed to register new client, #{retries} tries remaining")
    Chef::Log.warn("Response: HTTP #{e.response.code} - #{e}")
    retry
  end
  write_key
end

#updateObject



98
99
100
101
102
103
104
105
106
107
108
# File 'lib/chef/api_client/registration.rb', line 98

def update
  response = http_api.put("clients/#{name}", :name => name,
                                               :admin => false,
                                               :private_key => true)
  if response.respond_to?(:private_key) # Chef 11
    @private_key = response.private_key
  else # Chef 10
    @private_key = response["private_key"]
  end
  response
end

#write_keyObject



75
76
77
78
79
80
81
# File 'lib/chef/api_client/registration.rb', line 75

def write_key
  ::File.open(destination, file_flags, 0600) do |f|
    f.print(private_key)
  end
rescue IOError => e
  raise Chef::Exceptions::CannotWritePrivateKey, "Error writing private key to #{destination}: #{e}"
end