Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions app/controllers/userdata_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ def userdata
render_userdata_template
end

def vendordata
render_vendor_data_template
end

def metadata
data = {
:'instance-id' => "i-#{Digest::SHA1.hexdigest(@host.id.to_s)[0..17]}",
Expand Down Expand Up @@ -42,6 +46,16 @@ def render_userdata_template
safe_render(template)
end

def render_vendor_data_template
template = @host.provisioning_template(kind: 'vendor_data')
unless template
logger.info "No vendor-data template configured for host #{@host.name} running #{@host.operatingsystem}"
return render plain: ''
end

safe_render(template)
end

def skip_secure_headers
SecureHeaders.opt_out_of_all_protection(request)
end
Expand Down
2 changes: 2 additions & 0 deletions app/models/template_kind.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def self.default_template_labels
"finish" => N_("Finish template"),
"script" => N_("Script template"),
"user_data" => N_("User data template"),
"vendor_data" => N_("Vendor data template"),
"ZTP" => N_("ZTP PXE template"),
"POAP" => N_("POAP PXE template"),
"cloud-init" => N_("Cloud-init template"),
Expand All @@ -39,6 +40,7 @@ def self.default_template_descriptions
"finish" => N_("Post-install script for preseed-based or cloud instance. Connection is made via SSH, credentials or key must exist and inventory IP address must match. Only used when 'user data' is not set."),
"script" => N_("An arbitrary script, must be manually downloaded using wget/curl."),
"user_data" => N_("Template with seed data for virtual or cloud instances when 'user data' flag is set, typically cloud-init or ignition format."),
"vendor_data" => N_("Template with vendor data for virtual or cloud instances, typically cloud-init format."),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the benefit of adding a new kind that's (in my eyes) very similar to or almost the same as user data and cloud-init?

"ZTP" => N_("Provisioning Junos devices (Junos 12.2+)."),
"POAP" => N_("Provisioning for switches running NX-OS."),
"cloud-init" => N_("Template for cloud-init unattended endpoint."),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<%#
kind: vendor_data
name: VendorData default
model: ProvisioningTemplate
oses:
- AlmaLinux
- CentOS
- CentOS_Stream
- Fedora
- Rocky
- Debian
- Ubuntu
description: |
This template is served as vendor-data during image based provisioning alongside the user-data
template. It contains Foreman-specific configuration that is the responsibility of Foreman as

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This description does not reflect the content of the template.

Right now there is only echo, and that's all. IMO, we should add stuff similar to other default templates, like in cloud_init default or user data default.

the infrastructure vendor: notifying Foreman when the host is built.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

notifying Foreman when the host is built.

That's not true with the current content.

-%>
#cloud-config
runcmd:
- [ sh, -c, "echo $(date) ': hello cloud init foreman world!'" ]
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,7 @@
get 'unattended/(:kind/(:id(:format)))', controller: 'unattended', action: 'host_template', format: 'text'

get 'userdata/(:mac)/user-data', controller: 'userdata', action: 'userdata', format: 'text'
get 'userdata/(:mac)/vendor-data', controller: 'userdata', action: 'vendordata', format: 'text'
get 'userdata/(:mac)/meta-data', controller: 'userdata', action: 'metadata', format: 'text'

resources :tasks, only: [:show]
Expand Down
16 changes: 16 additions & 0 deletions db/migrate/20260709000000_add_vendor_data_template_kind.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class AddVendorDataTemplateKind < ActiveRecord::Migration[7.0]
def up
TemplateKind.unscoped.find_or_create_by(name: 'vendor_data') do |kind|
kind.description = TemplateKind.default_template_descriptions['vendor_data']
end
end

def down
kind = TemplateKind.unscoped.find_by(name: 'vendor_data')
return unless kind

kind.os_default_templates.destroy_all
kind.provisioning_templates.destroy_all
kind.destroy
end
end
78 changes: 78 additions & 0 deletions test/controllers/userdata_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,84 @@ class UserdataControllerTest < ActionController::TestCase
end
end

context '#vendor-data' do
let(:organization) { FactoryBot.create(:organization) }
let(:tax_location) { FactoryBot.create(:location) }
let(:vendor_data_content) { 'template content vendor_data' }
let(:vendor_data_template_kind) do
TemplateKind.where(name: 'vendor_data').first || FactoryBot.create(:template_kind, name: 'vendor_data')
end
let(:vendor_data_template) do
FactoryBot.create(
:provisioning_template,
template_kind: vendor_data_template_kind,
template: vendor_data_content,
locations: [tax_location],
organizations: [organization]
)
end
let(:os) do
FactoryBot.create(
:operatingsystem,
:with_associations,
family: 'Redhat',
provisioning_templates: [vendor_data_template]
)
end
let(:host) do
FactoryBot.create(
:host,
:managed,
operatingsystem: os,
organization: organization,
location: tax_location
)
end

setup do
FactoryBot.create(
:os_default_template,
template_kind: vendor_data_template_kind,
provisioning_template: vendor_data_template,
operatingsystem: os
)
@request.remote_ip = host.ip
end

test 'should get rendered vendor-data template' do
get :vendordata
assert_response :success
assert_equal vendor_data_content, @response.body
end

test 'should return an empty response when vendor-data template is not assigned' do
host.operatingsystem.os_default_templates.where(template_kind: vendor_data_template_kind).delete_all

get :vendordata
assert_response :success
assert_empty @response.body
end

test 'should return 404 for unknown ip address' do
@request.remote_ip = '198.51.100.1'
get :vendordata
assert_response :not_found
assert_includes @response.body, 'Could not find host for request 198.51.100.1'
end

test 'should get rendered vendor-data template when looking up host by mac' do
@request.remote_ip = '198.51.100.1'
get :vendordata, params: { mac: host.mac }
assert_response :success
assert_equal vendor_data_content, @response.body
end

test 'vendor-data route should route to vendordata action' do
assert_routing '/userdata/vendor-data', controller: 'userdata', action: 'vendordata', format: 'text'
assert_routing "/userdata/#{host.mac}/vendor-data", controller: 'userdata', action: 'vendordata', mac: host.mac, format: 'text'
end
end

context '#metadata' do
let(:host) { FactoryBot.create(:host, :managed) }
setup do
Expand Down
4 changes: 4 additions & 0 deletions test/fixtures/template_kinds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,7 @@ public:
user_data:
name: user_data
description: description for user_data template

vendor_data:
name: vendor_data
description: description for vendor_data template
Loading