Skip to content

Commit eed385a

Browse files
committed
(GH-121) Add Azure Arc support
1 parent 7ac842e commit eed385a

File tree

12 files changed

+106
-107
lines changed

12 files changed

+106
-107
lines changed

.fixtures.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
fixtures:
55
forge_modules:
66
repositories:
7-
provision: 'https://github.com/puppetlabs/provision.git'
8-
puppet_agent: 'https://github.com/puppetlabs/puppetlabs-puppet_agent.git'
9-
facts: 'https://github.com/puppetlabs/puppetlabs-facts.git'
7+
provision: 'git@github.com:puppetlabs/provision.git'
8+
puppet_agent: 'git@github.com:puppetlabs/puppetlabs-puppet_agent'
9+
facts: 'git@github.com:puppetlabs/puppetlabs-facts'
1010
# Should go away when Attempting to work around PDK-1137(https://tickets.puppetlabs.com/browse/PDK-1137)
1111
# is resolved.
1212
symlinks:

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,9 @@
2626
.envrc
2727
/inventory.yaml
2828
/spec/fixtures/litmus_inventory.yaml
29+
.resource_types
30+
.modules
31+
.task_cache.json
32+
.plan_cache.json
33+
.rerun.json
34+
bolt-debug.log

.pdkignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,16 @@
2626
.envrc
2727
/inventory.yaml
2828
/spec/fixtures/litmus_inventory.yaml
29+
.resource_types
30+
.modules
31+
.task_cache.json
32+
.plan_cache.json
33+
.rerun.json
34+
bolt-debug.log
2935
/.fixtures.yml
3036
/Gemfile
3137
/.gitattributes
38+
/.github/
3239
/.gitignore
3340
/.pdkignore
3441
/.puppet-lint.rc

.rubocop.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ require:
33
- rubocop-performance
44
- rubocop-rspec
55
AllCops:
6+
NewCops: enable
67
DisplayCopNames: true
78
TargetRubyVersion: '2.6'
89
Include:
@@ -527,6 +528,8 @@ Lint/DuplicateBranch:
527528
Enabled: false
528529
Lint/DuplicateMagicComment:
529530
Enabled: false
531+
Lint/DuplicateMatchPattern:
532+
Enabled: false
530533
Lint/DuplicateRegexpCharacterClassElement:
531534
Enabled: false
532535
Lint/EmptyBlock:
@@ -643,6 +646,8 @@ Style/ComparableClamp:
643646
Enabled: false
644647
Style/ConcatArrayLiterals:
645648
Enabled: false
649+
Style/DataInheritance:
650+
Enabled: false
646651
Style/DirEmpty:
647652
Enabled: false
648653
Style/DocumentDynamicEvalDefinition:
@@ -711,6 +716,8 @@ Style/RedundantHeredocDelimiterQuotes:
711716
Enabled: false
712717
Style/RedundantInitialize:
713718
Enabled: false
719+
Style/RedundantLineContinuation:
720+
Enabled: false
714721
Style/RedundantSelfAssignmentBranch:
715722
Enabled: false
716723
Style/RedundantStringEscape:

.vscode/extensions.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"recommendations": [
33
"puppet.puppet-vscode",
4-
"rebornix.Ruby"
4+
"Shopify.ruby-lsp"
55
]
66
}

Gemfile

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,32 @@ group :development do
2020
gem "json", '= 2.6.1', require: false if Gem::Requirement.create(['>= 3.1.0', '< 3.1.3']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup))
2121
gem "json", '= 2.6.3', require: false if Gem::Requirement.create(['>= 3.2.0', '< 4.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup))
2222
gem "racc", '~> 1.4.0', require: false if Gem::Requirement.create(['>= 2.7.0', '< 3.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup))
23+
gem "deep_merge", '~> 1.2.2', require: false
2324
gem "voxpupuli-puppet-lint-plugins", '~> 5.0', require: false
24-
gem "facterdb", '~> 1.18', require: false
25-
gem "metadata-json-lint", '~> 3.0', require: false
26-
gem "puppetlabs_spec_helper", '~> 6.0', require: false
27-
gem "rspec-puppet-facts", '~> 2.0', require: false
28-
gem "codecov", '~> 0.2', require: false
25+
gem "facterdb", '~> 1.26', require: false
26+
gem "metadata-json-lint", '~> 4.0', require: false
27+
gem "rspec-puppet-facts", '~> 3.0', require: false
2928
gem "dependency_checker", '~> 1.0.0', require: false
3029
gem "parallel_tests", '= 3.12.1', require: false
3130
gem "pry", '~> 0.10', require: false
32-
gem "simplecov-console", '~> 0.5', require: false
31+
gem "simplecov-console", '~> 0.9', require: false
3332
gem "puppet-debugger", '~> 1.0', require: false
34-
gem "rubocop", '= 1.48.1', require: false
33+
gem "rubocop", '~> 1.50.0', require: false
3534
gem "rubocop-performance", '= 1.16.0', require: false
3635
gem "rubocop-rspec", '= 2.19.0', require: false
37-
gem "puppet-strings", '~> 4.0', require: false
3836
gem "rb-readline", '= 0.5.5', require: false, platforms: [:mswin, :mingw, :x64_mingw]
37+
gem "rexml", '>= 3.0.0', '< 3.2.7', require: false
3938
gem "github_changelog_generator", require: false if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.2.2')
4039
gem "webmock", require: false
4140
end
41+
group :development, :release_prep do
42+
gem "puppet-strings", '~> 4.0', require: false
43+
gem "puppetlabs_spec_helper", '~> 7.0', require: false
44+
end
4245
group :system_tests do
43-
gem "puppet_litmus", '~> 1.0', require: false, platforms: [:ruby, :x64_mingw]
44-
gem "serverspec", '~> 2.41', require: false
46+
gem "puppet_litmus", '~> 1.0', require: false, platforms: [:ruby, :x64_mingw]
47+
gem "CFPropertyList", '< 3.0.7', require: false, platforms: [:mswin, :mingw, :x64_mingw]
48+
gem "serverspec", '~> 2.41', require: false
4549
end
4650

4751
puppet_version = ENV['PUPPET_GEM_VERSION']

Rakefile

Lines changed: 0 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -4,86 +4,8 @@ require 'bundler'
44
require 'puppet_litmus/rake_tasks' if Gem.loaded_specs.key? 'puppet_litmus'
55
require 'puppetlabs_spec_helper/rake_tasks'
66
require 'puppet-syntax/tasks/puppet-syntax'
7-
require 'github_changelog_generator/task' if Gem.loaded_specs.key? 'github_changelog_generator'
87
require 'puppet-strings/tasks' if Gem.loaded_specs.key? 'puppet-strings'
98
require 'puppet-strings/tasks'
109

11-
def changelog_user
12-
return unless Rake.application.top_level_tasks.include? "changelog"
13-
returnVal = nil || JSON.load(File.read('metadata.json'))['author']
14-
raise "unable to find the changelog_user in .sync.yml, or the author in metadata.json" if returnVal.nil?
15-
puts "GitHubChangelogGenerator user:#{returnVal}"
16-
returnVal
17-
end
18-
19-
def changelog_project
20-
return unless Rake.application.top_level_tasks.include? "changelog"
21-
22-
returnVal = nil
23-
returnVal ||= begin
24-
metadata_source = JSON.load(File.read('metadata.json'))['source']
25-
metadata_source_match = metadata_source && metadata_source.match(%r{.*\/([^\/]*?)(?:\.git)?\Z})
26-
27-
metadata_source_match && metadata_source_match[1]
28-
end
29-
30-
raise "unable to find the changelog_project in .sync.yml or calculate it from the source in metadata.json" if returnVal.nil?
31-
32-
puts "GitHubChangelogGenerator project:#{returnVal}"
33-
returnVal
34-
end
35-
36-
def changelog_future_release
37-
return unless Rake.application.top_level_tasks.include? "changelog"
38-
returnVal = "v%s" % JSON.load(File.read('metadata.json'))['version']
39-
raise "unable to find the future_release (version) in metadata.json" if returnVal.nil?
40-
puts "GitHubChangelogGenerator future_release:#{returnVal}"
41-
returnVal
42-
end
43-
4410
PuppetLint.configuration.send('disable_relative')
4511

46-
47-
if Gem.loaded_specs.key? 'github_changelog_generator'
48-
GitHubChangelogGenerator::RakeTask.new :changelog do |config|
49-
raise "Set CHANGELOG_GITHUB_TOKEN environment variable eg 'export CHANGELOG_GITHUB_TOKEN=valid_token_here'" if Rake.application.top_level_tasks.include? "changelog" and ENV['CHANGELOG_GITHUB_TOKEN'].nil?
50-
config.user = "#{changelog_user}"
51-
config.project = "#{changelog_project}"
52-
config.future_release = "#{changelog_future_release}"
53-
config.exclude_labels = ['maintenance']
54-
config.header = "# Change log\n\nAll notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org)."
55-
config.add_pr_wo_labels = true
56-
config.issues = false
57-
config.merge_prefix = "### UNCATEGORIZED PRS; LABEL THEM ON GITHUB"
58-
config.configure_sections = {
59-
"Changed" => {
60-
"prefix" => "### Changed",
61-
"labels" => ["backwards-incompatible"],
62-
},
63-
"Added" => {
64-
"prefix" => "### Added",
65-
"labels" => ["enhancement", "feature"],
66-
},
67-
"Fixed" => {
68-
"prefix" => "### Fixed",
69-
"labels" => ["bug", "documentation", "bugfix"],
70-
},
71-
}
72-
end
73-
else
74-
desc 'Generate a Changelog from GitHub'
75-
task :changelog do
76-
raise <<EOM
77-
The changelog tasks depends on recent features of the github_changelog_generator gem.
78-
Please manually add it to your .sync.yml for now, and run `pdk update`:
79-
---
80-
Gemfile:
81-
optional:
82-
':development':
83-
- gem: 'github_changelog_generator'
84-
version: '~> 1.15'
85-
condition: "Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.3.0')"
86-
EOM
87-
end
88-
end
89-

lib/puppet/functions/azure_key_vault/lookup.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
Optional[metadata_api_version] => String,
1010
confine_to_keys => Array[String],
1111
Optional[key_replacement_token] => String,
12-
Optional[service_principal_credentials] => String
12+
Optional[service_principal_credentials] => String,
13+
Optional[use_azure_arc_authentication] => Boolean
1314
}]', :options
1415
param 'Puppet::LookupContext', :context
1516
return_type 'Variant[Sensitive, Undef]'
@@ -44,6 +45,8 @@ def lookup_key(secret_name, options, context)
4445
if access_token.nil?
4546
metadata_api_version = options['metadata_api_version']
4647
service_principal_credentials = options['service_principal_credentials']
48+
use_azure_arc_authentication = options['use_azure_arc_authentication']
49+
4750
if metadata_api_version && service_principal_credentials
4851
raise ArgumentError, 'metadata_api_version and service_principal_credentials cannot be used together'
4952
end
@@ -54,6 +57,8 @@ def lookup_key(secret_name, options, context)
5457
if service_principal_credentials
5558
credentials = YAML.load_file(service_principal_credentials)
5659
access_token = TragicCode::Azure.get_access_token_service_principal(credentials)
60+
elsif use_azure_arc_authentication
61+
access_token = TragicCode::Azure.get_access_token_azure_arc(metadata_api_version)
5762
else
5863
access_token = TragicCode::Azure.get_access_token(metadata_api_version)
5964
end

lib/puppet/functions/azure_key_vault/secret.rb

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
tenant_id => String,
1919
client_id => String,
2020
client_secret => String
21-
}]
21+
}],
22+
Optional[use_azure_arc_authentication] => Boolean
2223
}]', :api_endpoint_hash
2324
optional_param 'String', :secret_version
2425
return_type 'Sensitive[String]'
@@ -34,23 +35,40 @@ def secret(cache, vault_name, secret_name, api_endpoint_hash, secret_version = '
3435
partial_credentials = api_endpoint_hash['service_principal_credentials'].slice('tenant_id', 'client_id')
3536
Puppet.debug("service_principal_credentials: #{partial_credentials}")
3637
end
38+
Puppet.debug("use_azure_arc_authentication: #{api_endpoint_hash['use_azure_arc_authentication']}")
3739
cache_hash = cache.retrieve(self)
3840
access_token_id = :"access_token_#{vault_name}"
3941
unless cache_hash.key?(access_token_id)
4042
Puppet.debug("retrieving access token since it's not in the cache")
4143
metadata_api_version = api_endpoint_hash['metadata_api_version']
4244
service_principal_credentials = api_endpoint_hash['service_principal_credentials']
45+
use_azure_arc_authentication = api_endpoint_hash['use_azure_arc_authentication']
46+
47+
if !metadata_api_version && !service_principal_credentials
48+
raise ArgumentError, 'hash must contain at least one of metadata_api_version or service_principal_credentials.'
49+
end
50+
51+
# Service principal validation
4352
if metadata_api_version && service_principal_credentials
44-
raise ArgumentError, 'metadata_api_version and service_principal_credentials cannot be used together'
53+
raise ArgumentError, 'metadata_api_version and service_principal_credentials cannot be used together.'
4554
end
4655

47-
if service_principal_credentials
48-
access_token = TragicCode::Azure.get_access_token_service_principal(service_principal_credentials)
49-
elsif metadata_api_version
50-
access_token = TragicCode::Azure.get_access_token(metadata_api_version)
51-
else
52-
raise ArgumentError, 'hash must contain at least one of metadata_api_version or service_principal_credentials'
56+
# Azure arc validation
57+
if service_principal_credentials && use_azure_arc_authentication
58+
raise ArgumentError, 'service_principal_credentials and use_azure_arc_authentication cannot be used together.'
5359
end
60+
61+
if !metadata_api_version && use_azure_arc_authentication
62+
raise ArgumentError, 'use_azure_arc_authentication must be used together with metadata_api_version.'
63+
end
64+
65+
access_token = if service_principal_credentials
66+
TragicCode::Azure.get_access_token_service_principal(service_principal_credentials)
67+
elsif use_azure_arc_authentication
68+
TragicCode::Azure.get_access_token_azure_arc(metadata_api_version)
69+
else
70+
TragicCode::Azure.get_access_token(metadata_api_version)
71+
end
5472
cache_hash[access_token_id] = access_token
5573
end
5674

lib/puppet_x/tragiccode/azure.rb

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,43 @@
44
module TragicCode
55
# Azure API functions
66
class Azure
7+
AZURE_ARC_INSTANCE_METADATA_ENPOINT_IP = '127.0.0.1'.freeze
8+
79
def self.normalize_object_name(object_name, replacement)
810
object_name.gsub(%r{[^0-9a-zA-Z-]}, replacement)
911
end
1012

13+
def self.get_access_token_azure_arc(api_version)
14+
# Generate File and Read Challenge Token
15+
uri = URI("http://#{self.AZURE_ARC_INSTANCE_METADATA_ENPOINT_IP}/metadata/identity/oauth2/token?api-version=#{api_version}&resource=https%3A%2F%2Fvault.azure.net")
16+
req = Net::HTTP::Get.new(uri.request_uri)
17+
req['Metadata'] = 'true'
18+
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
19+
http.request(req)
20+
end
21+
22+
# 403 is expected here. we do not provide ANY key
23+
raise res.body unless res.is_a?(Net::HTTPUnauthorized)
24+
raise 'Response header Www-Authenticate is missing' unless res['Www-Authenticate']
25+
26+
challenge_token_file_path = res['Www-Authenticate'].sub(%r{\s*Basic\s+realm=}, '')
27+
challenge_token = File.read(challenge_token_file_path)
28+
29+
# Get Access Token using challenge token
30+
internal_get_access_token(api_version, self.AZURE_ARC_INSTANCE_METADATA_ENPOINT_IP, { 'Authorization' => "Basic #{challenge_token}" })
31+
end
32+
1133
def self.get_access_token(api_version)
12-
uri = URI("http://169.254.169.254/metadata/identity/oauth2/token?api-version=#{api_version}&resource=https%3A%2F%2Fvault.azure.net")
34+
internal_get_access_token(api_version, '169.254.169.254')
35+
end
36+
37+
def self.internal_get_access_token(api_version, instance_metadata_service_endpoint = '169.254.169.254', extra_http_headers_hash = {})
38+
uri = URI("http://#{instance_metadata_service_endpoint}/metadata/identity/oauth2/token?api-version=#{api_version}&resource=https%3A%2F%2Fvault.azure.net")
1339
req = Net::HTTP::Get.new(uri.request_uri)
1440
req['Metadata'] = 'true'
41+
extra_http_headers_hash.each do |key, value|
42+
req[key] = value
43+
end
1544
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
1645
http.request(req)
1746
end

0 commit comments

Comments
 (0)