Skip to content

Commit

Permalink
Merge pull request #1713 from zmrow/etc-hosts-template
Browse files Browse the repository at this point in the history
Add an entry to `/etc/hosts` for the current hostname
  • Loading branch information
zmrow authored Sep 9, 2021
2 parents a6f6262 + 1c25ade commit 084fc3b
Show file tree
Hide file tree
Showing 13 changed files with 250 additions and 9 deletions.
4 changes: 4 additions & 0 deletions Release.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,7 @@ version = "1.2.0"
"migrate_v1.2.0_container-registry-config-restarts.lz4",
"migrate_v1.2.0_admin-container-v0-7-2.lz4",
]
"(1.2.0, 1.2.1)" = [
"migrate_v1.2.1_etc-hosts-service.lz4",
"migrate_v1.2.1_hostname-affects-etc-hosts.lz4",
]
1 change: 1 addition & 0 deletions packages/release/hosts → packages/release/hosts.template
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
{{add_unresolvable_hostname settings.network.hostname}}
7 changes: 4 additions & 3 deletions packages/release/release.spec
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ Release: 0%{?dist}
Summary: Bottlerocket release
License: Apache-2.0 OR MIT

Source10: hosts
Source11: nsswitch.conf
Source97: release-sysctl.conf
Source98: release-systemd-system.conf
Expand All @@ -15,6 +14,7 @@ Source99: release-tmpfiles.conf
Source200: motd.template
Source201: proxy-env
Source202: hostname-env
Source203: hosts.template

Source1000: eth0.xml
Source1001: multi-user.target
Expand Down Expand Up @@ -91,7 +91,7 @@ Requires: %{_cross_os}wicked

%install
install -d %{buildroot}%{_cross_factorydir}%{_cross_sysconfdir}
install -p -m 0644 %{S:10} %{S:11} %{buildroot}%{_cross_factorydir}%{_cross_sysconfdir}
install -p -m 0644 %{S:11} %{buildroot}%{_cross_factorydir}%{_cross_sysconfdir}

install -d %{buildroot}%{_cross_factorydir}%{_cross_sysconfdir}/wicked/ifconfig
install -p -m 0644 %{S:1000} %{buildroot}%{_cross_factorydir}%{_cross_sysconfdir}/wicked/ifconfig
Expand Down Expand Up @@ -135,14 +135,14 @@ install -d %{buildroot}%{_cross_templatedir}
install -p -m 0644 %{S:200} %{buildroot}%{_cross_templatedir}/motd
install -p -m 0644 %{S:201} %{buildroot}%{_cross_templatedir}/proxy-env
install -p -m 0644 %{S:202} %{buildroot}%{_cross_templatedir}/hostname-env
install -p -m 0644 %{S:203} %{buildroot}%{_cross_templatedir}/hosts

install -d %{buildroot}%{_cross_udevrulesdir}
install -p -m 0644 %{S:1016} %{buildroot}%{_cross_udevrulesdir}/61-mount-cdrom.rules

ln -s %{_cross_unitdir}/preconfigured.target %{buildroot}%{_cross_unitdir}/default.target

%files
%{_cross_factorydir}%{_cross_sysconfdir}/hosts
%{_cross_factorydir}%{_cross_sysconfdir}/nsswitch.conf
%{_cross_factorydir}%{_cross_sysconfdir}/wicked/ifconfig/eth0.xml
%{_cross_sysctldir}/80-release.conf
Expand Down Expand Up @@ -175,6 +175,7 @@ ln -s %{_cross_unitdir}/preconfigured.target %{buildroot}%{_cross_unitdir}/defau
%{_cross_templatedir}/motd
%{_cross_templatedir}/proxy-env
%{_cross_templatedir}/hostname-env
%{_cross_templatedir}/hosts
%{_cross_udevrulesdir}/61-mount-cdrom.rules

%changelog
15 changes: 15 additions & 0 deletions sources/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions sources/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ members = [
"api/migration/migration-helpers",
"api/shibaken",

# "api/migration/migrations/vX.Y.Z/...
# (all migrations currently archived; replace this line with new ones)
# "api/migration/migrations/vX.Y.Z/..."
"api/migration/migrations/v1.2.1/etc-hosts-service",
"api/migration/migrations/v1.2.1/hostname-affects-etc-hosts",

"bottlerocket-release",

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "etc-hosts-service"
version = "0.1.0"
authors = ["Zac Mrowicki <[email protected]>"]
license = "Apache-2.0 OR MIT"
edition = "2018"
publish = false
# Don't rebuild crate just because of changes to README.
exclude = ["README.md"]

[dependencies]
migration-helpers = { path = "../../../migration-helpers" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#![deny(rust_2018_idioms)]

use migration_helpers::common_migrations::AddPrefixesMigration;
use migration_helpers::{migrate, Result};
use std::process;

/// We added a new setting and generator for configuring hostname
fn run() -> Result<()> {
migrate(AddPrefixesMigration(vec![
"services.hosts",
"configuration-files.hosts",
]))
}

// Returning a Result from main makes it print a Debug representation of the error, but with Snafu
// we have nice Display representations of the error, so we wrap "main" (run) and print any error.
// https://github.com/shepmaster/snafu/issues/110
fn main() {
if let Err(e) = run() {
eprintln!("{}", e);
process::exit(1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "hostname-affects-etc-hosts"
version = "0.1.0"
authors = ["Zac Mrowicki <[email protected]>"]
license = "Apache-2.0 OR MIT"
edition = "2018"
publish = false
# Don't rebuild crate just because of changes to README.
exclude = ["README.md"]

[dependencies]
migration-helpers = { path = "../../../migration-helpers" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#![deny(rust_2018_idioms)]

use migration_helpers::common_migrations::{
MetadataListReplacement, ReplaceMetadataListsMigration,
};
use migration_helpers::{migrate, Result};
use std::process;

/// We updated the 'affected-services' list metadata for 'settings.network.hostname' to include the
/// hosts "service" on upgrade, and to remove it on downgrade.
fn run() -> Result<()> {
migrate(ReplaceMetadataListsMigration(vec![
MetadataListReplacement {
setting: "settings.network.hostname",
metadata: "affected-services",
old_vals: &["hostname"],
new_vals: &["hostname", "hosts"],
},
]))
}

// Returning a Result from main makes it print a Debug representation of the error, but with Snafu
// we have nice Display representations of the error, so we wrap "main" (run) and print any error.
// https://github.com/shepmaster/snafu/issues/110
fn main() {
if let Err(e) = run() {
eprintln!("{}", e);
process::exit(1);
}
}
1 change: 1 addition & 0 deletions sources/api/schnauzer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ apiclient = { path = "../apiclient" }
base64 = "0.13"
constants = { path = "../../constants" }
bottlerocket-release = { path = "../../bottlerocket-release" }
dns-lookup = "1.0"
handlebars = "4.1"
http = "0.2"
lazy_static = "1.4"
Expand Down
134 changes: 131 additions & 3 deletions sources/api/schnauzer/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// be registered with the Handlebars library to assist in manipulating
// text at render time.

use dns_lookup::lookup_host;
use handlebars::{Context, Handlebars, Helper, Output, RenderContext, RenderError};
use lazy_static::lazy_static;
use num_cpus;
Expand All @@ -10,6 +11,7 @@ use snafu::{OptionExt, ResultExt};
use std::borrow::Borrow;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use url::Url;

lazy_static! {
Expand Down Expand Up @@ -98,6 +100,9 @@ const KUBE_RESERVE_3_CORES: f32 = KUBE_RESERVE_2_CORES + 5.0;
const KUBE_RESERVE_4_CORES: f32 = KUBE_RESERVE_3_CORES + 5.0;
const KUBE_RESERVE_ADDITIONAL: f32 = 2.5;

const IPV4_LOCALHOST: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
const IPV6_LOCALHOST: IpAddr = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));

/// Potential errors during helper execution
mod error {
use handlebars::RenderError;
Expand Down Expand Up @@ -869,6 +874,77 @@ pub fn kube_reserve_cpu(
Ok(())
}

/// Attempts to resolve the current hostname in DNS. If unsuccessful, returns an entry formatted
/// for `/etc/hosts` that aliases the hostname to both the IPV4/6 localhost addresses.
pub fn add_unresolvable_hostname(
helper: &Helper<'_, '_>,
_: &Handlebars,
_: &Context,
renderctx: &mut RenderContext<'_, '_>,
out: &mut dyn Output,
) -> Result<(), RenderError> {
// To give context to our errors, get the template name, if available.
trace!("Starting base64_decode helper");
let template_name = template_name(renderctx);
trace!("Template name: {}", &template_name);

// Check number of parameters, must be exactly one
trace!("Number of params: {}", helper.params().len());
check_param_count(helper, template_name, 1)?;

// Get the resolved key out of the template (param(0)). value() returns
// a serde_json::Value
let hostname_value = helper
.param(0)
.map(|v| v.value())
.context(error::Internal {
msg: "Found no params after confirming there is one param",
})?;
trace!("Hostname value from template: {}", hostname_value);

// Create an &str from the serde_json::Value
let hostname_str = hostname_value
.as_str()
.context(error::InvalidTemplateValue {
expected: "string",
value: hostname_value.to_owned(),
template: template_name.to_owned(),
})?;
trace!("Hostname string from template: {}", hostname_str);

// Attempt to resolve the hostname
let hostname_resolveable = match lookup_host(hostname_str) {
Ok(ip_list) => {
// If the list of IPs is empty or resolves to localhost, consider the hostname
// unresolvable
let resolves_to_localhost = ip_list
.iter()
.any(|ip| ip == &IPV4_LOCALHOST || ip == &IPV6_LOCALHOST);
if ip_list.is_empty() || resolves_to_localhost {
false
} else {
true
}
}
Err(e) => {
trace!("DNS hostname lookup failed: {},", e);
false
}
};

// Only write an entry to the template if the hostname is unresolvable
if !hostname_resolveable {
let ipv4_entry = format!("{} {}", IPV4_LOCALHOST, hostname_str);
let ipv6_entry = format!("{} {}", IPV6_LOCALHOST, hostname_str);
let entries = format!("{}\n{}", ipv4_entry, ipv6_entry);

out.write(&entries).context(error::TemplateWrite {
template: template_name.to_owned(),
})?;
}
Ok(())
}

// =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^=
// helpers to the helpers

Expand Down Expand Up @@ -935,8 +1011,9 @@ fn pause_registry<S: AsRef<str>>(region: S) -> String {
}

/// Calculates and returns the amount of CPU to reserve
fn kube_cpu_helper(num_cores: usize) -> Result<String, TemplateHelperError>{
let num_cores = u16::try_from(num_cores).context(error::ConvertUsizeToU16 { number: num_cores })?;
fn kube_cpu_helper(num_cores: usize) -> Result<String, TemplateHelperError> {
let num_cores =
u16::try_from(num_cores).context(error::ConvertUsizeToU16 { number: num_cores })?;
let millicores_unit = "m";
let cpu_to_reserve = match num_cores {
0 => 0.0,
Expand All @@ -949,7 +1026,11 @@ fn kube_cpu_helper(num_cores: usize) -> Result<String, TemplateHelperError>{
KUBE_RESERVE_4_CORES + ((num_cores - 4.0) * KUBE_RESERVE_ADDITIONAL)
}
};
Ok(format!("{}{}", cpu_to_reserve.floor().to_string(), millicores_unit))
Ok(format!(
"{}{}",
cpu_to_reserve.floor().to_string(),
millicores_unit
))
}

// =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^=
Expand Down Expand Up @@ -1607,3 +1688,50 @@ mod test_kube_cpu_helper {
}
}
}

#[cfg(test)]
mod test_add_unresolvable_hostname {
use super::*;
use handlebars::RenderError;
use serde::Serialize;
use serde_json::json;

// A thin wrapper around the handlebars render_template method that includes
// setup and registration of helpers
fn setup_and_render_template<T>(tmpl: &str, data: &T) -> Result<String, RenderError>
where
T: Serialize,
{
let mut registry = Handlebars::new();
registry.register_helper(
"add_unresolvable_hostname",
Box::new(add_unresolvable_hostname),
);

registry.render_template(tmpl, data)
}

#[test]
fn resolves_to_localhost_renders_entries() {
let result = setup_and_render_template(
"{{add_unresolvable_hostname name}}",
&json!({"name": "localhost"}),
)
.unwrap();
assert_eq!(
result,
"127.0.0.1 localhost
::1 localhost"
)
}

#[test]
fn resolvable_hostname_renders_nothing() {
let result = setup_and_render_template(
"{{add_unresolvable_hostname name}}",
&json!({"name": "amazon.com"}),
)
.unwrap();
assert_eq!(result, "")
}
}
5 changes: 5 additions & 0 deletions sources/api/schnauzer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ pub fn build_template_registry() -> Result<handlebars::Handlebars<'static>> {
// but isn't provided in the data given to the renderer
template_registry.set_strict_mode(true);

// Prefer snake case for helper names (we accidentally created a few with kabob case)
template_registry.register_helper("base64_decode", Box::new(helpers::base64_decode));
template_registry.register_helper("join_map", Box::new(helpers::join_map));
template_registry.register_helper("default", Box::new(helpers::default));
Expand All @@ -131,6 +132,10 @@ pub fn build_template_registry() -> Result<handlebars::Handlebars<'static>> {
"kube_reserve_memory",
Box::new(helpers::kube_reserve_memory),
);
template_registry.register_helper(
"add_unresolvable_hostname",
Box::new(helpers::add_unresolvable_hostname),
);

Ok(template_registry)
}
Expand Down
Loading

0 comments on commit 084fc3b

Please sign in to comment.