-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
v0.1.2: add Kubernetes resource requests/limits; refactor into Hairpi…
…nProxyController class for readability
- Loading branch information
Showing
3 changed files
with
66 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,54 +1,65 @@ | ||
#!/usr/bin/env ruby | ||
# frozen_string_literal: true | ||
|
||
STDOUT.sync = true | ||
|
||
require "k8s-client" | ||
require "logger" | ||
|
||
def ingress_hosts(k8s) | ||
all_ingresses = k8s.api("extensions/v1beta1").resource("ingresses").list | ||
|
||
all_tls_blocks = all_ingresses.map { |r| r.spec.tls }.flatten.compact | ||
class HairpinProxyController | ||
COREDNS_CONFIGMAP_LINE_SUFFIX = "# Added by hairpin-proxy" | ||
DNS_REWRITE_DESTINATION = "hairpin-proxy.hairpin-proxy.svc.cluster.local" | ||
POLL_INTERVAL = ENV.fetch("POLL_INTERVAL", "15").to_i.clamp(1..) | ||
|
||
all_tls_blocks.map(&:hosts).flatten.compact.sort.uniq | ||
end | ||
def initialize | ||
@k8s = K8s::Client.in_cluster_config | ||
|
||
def rewrite_coredns_corefile(cf, hosts) | ||
cflines = cf.strip.split("\n").reject { |line| line.strip.end_with?("# Added by hairpin-proxy") } | ||
STDOUT.sync = true | ||
@log = Logger.new(STDOUT) | ||
end | ||
|
||
main_server_line = cflines.index { |line| line.strip.start_with?(".:53 {") } | ||
raise "Can't find main server line! '.:53 {' in Corefile" if main_server_line.nil? | ||
def fetch_ingress_hosts | ||
# Return a sorted Array of all unique hostnames mentioned in Ingress spec.tls.hosts blocks, in all namespaces. | ||
all_ingresses = @k8s.api("extensions/v1beta1").resource("ingresses").list | ||
all_tls_blocks = all_ingresses.map { |r| r.spec.tls }.flatten.compact | ||
all_tls_blocks.map(&:hosts).flatten.compact.sort.uniq | ||
end | ||
|
||
rewrite_lines = hosts.map { |host| " rewrite name #{host} hairpin-proxy.hairpin-proxy.svc.cluster.local # Added by hairpin-proxy" } | ||
def coredns_corefile_with_rewrite_rules(original_corefile, hosts) | ||
# Return a String representing the original CoreDNS Corefile, modified to include rewrite rules for each of *hosts. | ||
# This is an idempotent transformation because our rewrites are labeled with COREDNS_CONFIGMAP_LINE_SUFFIX. | ||
|
||
cflines.insert(main_server_line + 1, *rewrite_lines) | ||
# Extract base configuration, without our hairpin-proxy rewrites | ||
cflines = original_corefile.strip.split("\n").reject { |line| line.strip.end_with?(COREDNS_CONFIGMAP_LINE_SUFFIX) } | ||
|
||
cflines.join("\n") | ||
end | ||
# Create rewrite rules | ||
rewrite_lines = hosts.map { |host| " rewrite name #{host} #{DNS_REWRITE_DESTINATION} #{COREDNS_CONFIGMAP_LINE_SUFFIX}" } | ||
|
||
def main | ||
client = K8s::Client.in_cluster_config | ||
# Inject at the start of the main ".:53 { ... }" configuration block | ||
main_server_line = cflines.index { |line| line.strip.start_with?(".:53 {") } | ||
raise "Can't find main server line! '.:53 {' in Corefile" if main_server_line.nil? | ||
cflines.insert(main_server_line + 1, *rewrite_lines) | ||
|
||
loop do | ||
puts "#{Time.now}: Fetching..." | ||
cflines.join("\n") | ||
end | ||
|
||
hosts = ingress_hosts(client) | ||
cm = client.api.resource("configmaps", namespace: "kube-system").get("coredns") | ||
def main_loop | ||
@log.info("Starting main_loop with #{POLL_INTERVAL}s polling interval.") | ||
loop do | ||
@log.info("Polling all Ingress resources and CoreDNS configuration...") | ||
hosts = fetch_ingress_hosts | ||
cm = @k8s.api.resource("configmaps", namespace: "kube-system").get("coredns") | ||
|
||
old_corefile = cm.data.Corefile | ||
new_corefile = rewrite_coredns_corefile(old_corefile, hosts) | ||
old_corefile = cm.data.Corefile | ||
new_corefile = coredns_corefile_with_rewrite_rules(old_corefile, hosts) | ||
|
||
if old_corefile.strip != new_corefile.strip | ||
puts "#{Time.now}: Corefile changed!" | ||
puts new_corefile | ||
if old_corefile.strip != new_corefile.strip | ||
@log.info("Corefile has changed! New contents:\n#{new_corefile}\nSending updated ConfigMap to Kubernetes API server...") | ||
cm.data.Corefile = new_corefile | ||
@k8s.api.resource("configmaps", namespace: "kube-system").update_resource(cm) | ||
end | ||
|
||
puts "#{Time.now}: Updating ConfigMap." | ||
cm.data.Corefile = new_corefile | ||
client.api.resource("configmaps", namespace: "kube-system").update_resource(cm) | ||
sleep(POLL_INTERVAL) | ||
end | ||
|
||
sleep(15) | ||
end | ||
end | ||
|
||
main | ||
HairpinProxyController.new.main_loop if $PROGRAM_NAME == __FILE__ |