Skip to content

Commit

Permalink
Add platform uuid information.
Browse files Browse the repository at this point in the history
Signed-off-by: Jared Quick <[email protected]>
  • Loading branch information
jquick committed Mar 16, 2018
1 parent 3b9ca54 commit 1fcfdb0
Show file tree
Hide file tree
Showing 7 changed files with 299 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/train/platforms.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'train/platforms/common'
require 'train/platforms/family'
require 'train/platforms/platform'
require 'train/platforms/uuid'
require 'train/platforms/detect'
require 'train/platforms/detect/scanner'
require 'train/platforms/detect/specifications/os'
Expand Down
1 change: 1 addition & 0 deletions lib/train/platforms/detect/specifications/os.rb
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,7 @@ def self.load
plat.name('mac_os_x').title('macOS X').in_family('darwin')
.detect {
cmd = unix_file_contents('/System/Library/CoreServices/SystemVersion.plist')
@platform[:uuid_command] = "system_profiler SPHardwareDataType | awk '/UUID/ { print $3; }'"
true if cmd =~ /Mac OS X/i
}
plat.name('darwin').title('Darwin').in_family('darwin')
Expand Down
4 changes: 4 additions & 0 deletions lib/train/platforms/platform.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ def clean_name(force: false)
end
end

def uuid
@uuid ||= Train::Platforms::UUID.new(self).find_or_create_uuid.downcase
end

# This is for backwords compatability with
# the current inspec os resource.
def[](name)
Expand Down
135 changes: 135 additions & 0 deletions lib/train/platforms/uuid.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# encoding: utf-8

require 'digest/sha1'
require 'securerandom'
require 'json'

module Train::Platforms
class UUID
def initialize(platform)
@platform = platform
@backend = @platform.backend
end

def find_or_create_uuid
if @platform.unix?
unix
elsif @platform.windows?
windows
elsif @platform.name == 'aws'
aws
elsif @platform.name == 'azure'
azure
else
if @platform[:uuid_command]
result = @backend.run_command(@platform[:uuid_command])
return uuid_from_string(result.stdout.chomp) if result.exit_status == 0 && !result.stdout.empty?
end

raise 'Could not fine platform uuid! please set a uuid_command for your platform'
end
end

private

def aws
# parse the aws account id
client = @backend.aws_client(Aws::IAM::Client)
arn = client.get_user.user.arn
uuid_from_string(arn.split(':')[4])
end

def azure
# use subscription_id or tenant_id
@backend.options[:subscription_id] || @backend.options[:tenant_id]
end

def windows
# check for chef uuid
file = @backend.file("#{ENV['SYSTEMDRIVE']}\\chef\\cache\\data_collector_metadata.json")
if file.exist? && file.size != 0
json = JSON.parse(file.content)
return json['node_uuid'] if json['node_uuid']
end

result = @backend.run_command("wmic csproduct get UUID")
return result.stdout.split("\r\n")[-1].strip if result.exit_status == 0

cmd = '(Get-ItemProperty "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography" -Name "MachineGuid")."MachineGuid"'
result = @backend.run_command(cmd)
return result.stdout.chomp if result.exit_status == 0

# see if we have a custom uuid
%W(
#{ENV['SYSTEMROOT']}\\machine-uuid
#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}\\.system\\machine-uuid
).each do |path|
file = @backend.file(path)
return file.content.chomp if file.exist?
end

# cant find anything, try to write a uuid
uuid = SecureRandom.uuid
result = @backend.run_command("'#{uuid}' | Out-File -encoding ASCII #{ENV['SYSTEMROOT']}\\machine-uuid")
if result.exit_status !=0
local_uuid_path = "#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}\\.system\\machine-uuid"
warn "Cannot write uuid to `#{ENV['SYSTEMROOT']}\\machine-uuid`, falling back to #{local_uuid_path} instead"
# fallback to user location
result = @backend.run_command("new-item -force -path #{local_uuid_path} -value '#{uuid}' -type file")
raise "Cannot write uuid to `#{local_uuid_path}\\machine-uuid`" if result.exit_status !=0
end
uuid
end


def unix
# check for chef uuid
file = @backend.file('/var/chef/cache/data_collector_metadata.json')
if file.exist?
json = ::JSON.parse(file.content)
return json['node_uuid'] if json['node_uuid']
end

# check for standard machine-ids
%W(
/etc/machine-id
/var/lib/dbus/machine-id
/var/db/dbus/machine-id
/etc/machine-uuid
#{ENV['HOME']}/.local/etc/machine-uuid
).each do |path|
file = @backend.file(path)
if path =~ /uuid/
return file.content.chomp if file.exist? && file.size != 0
else
return uuid_from_string(file.content.chomp) if file.exist? && file.size != 0
end
end

if @platform[:uuid_command]
result = @backend.run_command(@platform[:uuid_command])
return uuid_from_string(result.stdout.chomp) if result.exit_status == 0 && !result.stdout.empty?
end

# cant find anything, try to write a config
uuid = SecureRandom.uuid
result = @backend.run_command("echo \"#{uuid}\" > /etc/machine-uuid")
if result.exit_status !=0
warn 'Cannot write uuid to `/etc/machine-uuid`, falling back to ~/.local/etc/machine-uuid instead'
# fallback to user location
result = @backend.run_command("mkdir -p #{ENV['HOME']}/.local/etc; echo \"#{uuid}\" > #{ENV['HOME']}/.local/etc/machine-uuid")
raise 'Cannot write uuid to `~/.local/etc/machine-uuid`' if result.exit_status !=0
end
uuid
end

def uuid_from_string(string)
hash = Digest::SHA1.new
hash.update(string)
ary = hash.digest.unpack("NnnnnN")
ary[2] = (ary[2] & 0x0FFF) | (5 << 12)
ary[3] = (ary[3] & 0x3FFF) | 0x8000
"%08x-%04x-%04x-%04x-%04x%08x" % ary
end
end
end
2 changes: 2 additions & 0 deletions lib/train/transports/azure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ def connection(_ = nil)
end

class Connection < BaseConnection
attr_reader :options

def initialize(options)
@apis = {}

Expand Down
2 changes: 2 additions & 0 deletions lib/train/transports/mock.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ def trace_calls

class Train::Transports::Mock
class Connection < BaseConnection
attr_reader :options

def initialize(conf = nil)
super(conf)
mock_os
Expand Down
154 changes: 154 additions & 0 deletions test/unit/platforms/uuid_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# encoding: utf-8

require 'helper'
require 'train/transports/mock'
require 'train/transports/aws'
require 'securerandom'

class TestFile
def initialize(string)
@string = string
end

def exist?
true
end

def size
@string.length
end

def content
@string
end
end

describe 'uuid' do
def mock_platform(name, commands = {}, files = {}, plat_options = {})
Train::Platforms.list[name] = nil
mock = Train::Transports::Mock::Connection.new
commands.each do |command, data|
mock.mock_command(command, data)
end

file_objects = {}
files.each do |path, content|
file_objects[path] = TestFile.new(content)
end

mock.files = file_objects
mock.direct_platform(name, plat_options)
end

it 'find linux uuid from chef entity_uuid' do
files = { '/var/chef/cache/data_collector_metadata.json' => '{"node_uuid":"d400073f-0920-41aa-8dd3-2ea59b18f5ce"}' }
plat = mock_platform('linux', {}, files)
plat.uuid.must_equal 'd400073f-0920-41aa-8dd3-2ea59b18f5ce'
end

it 'find windows uuid from chef entity_uuid' do
ENV['SYSTEMDRIVE'] = 'C:'
files = { 'C:\chef\cache\data_collector_metadata.json' => '{"node_uuid":"d400073f-0920-41aa-8dd3-2ea59b18f5ce"}' }
plat = mock_platform('windows', {}, files)
plat.uuid.must_equal 'd400073f-0920-41aa-8dd3-2ea59b18f5ce'
end

it 'find linux uuid from /etc/machine-id' do
files = { '/etc/machine-id' => '123141dsfadf' }
plat = mock_platform('linux', {}, files)
plat.uuid.must_equal '5e430326-b5aa-56f8-975f-c3ca1c21df91'
end

it 'find linux uuid from /var/lib/dbus/machine-id' do
files = {
'/etc/machine-id' => '',
'/var/lib/dbus/machine-id' => '123141dsfadf',
}
plat = mock_platform('linux', {}, files)
plat.uuid.must_equal '5e430326-b5aa-56f8-975f-c3ca1c21df91'
end

it 'find linux uuid from /etc/machine-uuid' do
files = { '/etc/machine-uuid' => 'd400073f-0920-41aa-8dd3-2ea59b18f5ce' }
plat = mock_platform('linux', {}, files)
plat.uuid.must_equal 'd400073f-0920-41aa-8dd3-2ea59b18f5ce'
end

it 'find linux uuid from uuid_command' do
plat_options = { uuid_command: "system_profiler SPHardwareDataType | awk '/UUID/ { print $3; }'"}
commands = { "system_profiler SPHardwareDataType | awk '/UUID/ { print $3; }'" => 'd400073f-0920-41aa-8dd3-2ea59b18f5ce' }
plat = mock_platform('mac_os_x', commands, {}, plat_options)
plat.uuid.must_equal '2ee3c95b-4023-570d-8177-f939901a3c51'
end

it 'find linux uuid from /etc/machine-id' do
files = { '/etc/machine-id' => '123141dsfadf' }
plat = mock_platform('linux', {}, files)
plat.uuid.must_equal '5e430326-b5aa-56f8-975f-c3ca1c21df91'
end

it 'find windows uuid from wmic' do
commands = { 'wmic csproduct get UUID' => "UUID\r\nd400073f-0920-41aa-8dd3-2ea59b18f5ce\r\n" }
plat = mock_platform('windows', commands)
plat.uuid.must_equal 'd400073f-0920-41aa-8dd3-2ea59b18f5ce'
end

it 'find windows uuid from registry' do
commands = { '(Get-ItemProperty "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography" -Name "MachineGuid")."MachineGuid"' => "d400073f-0920-41aa-8dd3-2ea59b18f5ce\r\n" }
plat = mock_platform('windows', commands)
plat.uuid.must_equal 'd400073f-0920-41aa-8dd3-2ea59b18f5ce'
end

it 'find windows uuid from C:\windows\machine-uuid' do
ENV['SYSTEMROOT'] = 'C:\windows'
files = { 'C:\windows\machine-uuid' => '5e430326-b5aa-56f8-975f-c3ca1c21df91' }
plat = mock_platform('windows', {}, files)
plat.uuid.must_equal '5e430326-b5aa-56f8-975f-c3ca1c21df91'
end

it 'find windows uuid from C:\Users\test\.system\machine-uuid' do
ENV['HOMEDRIVE'] = 'C:\\'
ENV['HOMEPATH'] = 'Users\test'
files = { 'C:\Users\test\.system\machine-uuid' => '5e430326-b5aa-56f8-975f-c3ca1c21df91' }
plat = mock_platform('windows', {}, files)
plat.uuid.must_equal '5e430326-b5aa-56f8-975f-c3ca1c21df91'
end

it 'uuid_from_string' do
plat = mock_platform('linux')
uuid = Train::Platforms::UUID.new(plat)
uuid.send(:uuid_from_string, '123141dsfadf').must_equal '5e430326-b5aa-56f8-975f-c3ca1c21df91'
end

# aws
class AwsArn
def arn
'arn:aws:iam::158551926027:user/test-fixture-maker'
end
end

class AwsUser
def user
AwsArn.new
end
end

class AwsClient
def get_user
AwsUser.new
end
end
it 'find aws uuid' do
plat = mock_platform('aws')
plat.backend.stubs(:aws_client).returns(AwsClient.new)
plat.uuid.must_equal '1d74ce61-ac15-5c48-9ee3-5aa8207ac37f'
end

# azure
it 'find azure uuid' do
plat = mock_platform('azure')
options = { subscription_id: '1d74ce61-ac15-5c48-9ee3-5aa8207ac37f' }
plat.backend.stubs(:options).returns(options)
plat.uuid.must_equal '1d74ce61-ac15-5c48-9ee3-5aa8207ac37f'
end
end

0 comments on commit 1fcfdb0

Please sign in to comment.