Skip to content

Commit 3b22e8d

Browse files
committed
Revert "Extensive cleanup after instance NAT removal"
This reverts commit 2cd156d.
1 parent ad49992 commit 3b22e8d

24 files changed

+544
-17
lines changed

.rubocop_todo.yml

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Metrics/MethodLength:
3535
# Blacklist: (?-mix:(^|\s)(EO[A-Z]{1}|END)(\s|$))
3636
Naming/HeredocDelimiterNaming:
3737
Exclude:
38+
- 'bin/gcloud-nats-by-zone'
3839
- 'bin/generate-latest-gce-images'
3940
- 'bin/write-config-files'
4041

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[Init]
2+
blocktype = DROP
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[Definition]
2+
failregex = ^.*time=\S+ level=WARNING over threshold=\d+ src=<HOST> dst=\S+ count=\d+.*$
3+
ignoreregex =
4+
5+
[Init]
6+
journalmatch = _SYSTEMD_UNIT=nat-conntracker.service
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[nat-conntracker]
2+
backend = systemd
3+
banaction = iptables-allports
4+
bantime = 30
5+
chain = FORWARD
6+
enabled = true
7+
ignoreip = 127.0.0.1/8 10.10.0.0/16
8+
maxretry = 1
9+
protocol = all
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[Definition]
2+
logtarget = SYSLOG

assets/nat/var/tmp/nftables.conf

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
table ip nat {
2+
chain prerouting {
3+
type nat hook prerouting priority 0; policy accept;
4+
}
5+
6+
chain postrouting {
7+
type nat hook postrouting priority 100; policy accept;
8+
masquerade
9+
}
10+
}

bin/gce-export-net

+3
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,12 @@ def main
3131
command = %W[#{terraform} state rm] + (
3232
%w[
3333
aws_route53_record.bastion-b
34+
aws_route53_record.nat-b
3435
google_compute_address.bastion-b
3536
google_compute_address.bastion[0]
37+
google_compute_address.nat-b
3638
google_compute_firewall.allow_internal
39+
google_compute_firewall.allow_jobs_nat
3740
google_compute_firewall.allow_public_icmp
3841
google_compute_firewall.allow_public_ssh
3942
google_compute_firewall.deny_target_ip

bin/gce-import-net

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ def main
4949
{
5050
'google_compute_address.bastion[0]' => 'bastion-b',
5151
'google_compute_firewall.allow_internal' => 'allow-internal',
52+
'google_compute_firewall.allow_jobs_nat' => 'allow-jobs-nat',
5253
'google_compute_firewall.allow_public_icmp' => 'allow-public-icmp',
5354
'google_compute_firewall.allow_public_ssh' => 'allow-public-ssh',
5455
'google_compute_firewall.deny_target_ip' => 'deny-target-ip',

bin/gcloud-nats-by-zone

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#!/usr/bin/env ruby
2+
# frozen_string_literal: true
3+
4+
require 'json'
5+
6+
NOTSET = '<notset>'
7+
8+
def main(argv: ARGV)
9+
return usage if $stdin.tty? || argv.include?('-h') || argv.include?('--help')
10+
11+
in_args = JSON.parse($stdin.read)
12+
zones = in_args.fetch('zones', '').split(',').map(&:strip).reject(&:empty?)
13+
count = Integer(in_args.fetch('count', zones.length))
14+
project = in_args.fetch('project')
15+
region = in_args.fetch('region')
16+
retries = Integer(in_args.fetch('retries', '4'))
17+
retry_sleep = Integer(in_args.fetch('retry_sleep', '120'))
18+
accounting = %w[1 yes on true].include?(
19+
ENV.fetch(
20+
'ACCOUNTING', in_args.fetch('accounting', true)
21+
).to_s
22+
)
23+
retried = 0
24+
25+
instances = {}
26+
while retried <= retries
27+
instances = fetch_nats_by_zone(zones, count, project, region)
28+
break unless accounting
29+
break if instances.length == count
30+
31+
warn(
32+
"#{$PROGRAM_NAME}: sleeping=#{retry_sleep}s " \
33+
"instances_length=#{instances.length} count=#{count}"
34+
)
35+
36+
sleep(retry_sleep)
37+
retried += 1
38+
end
39+
40+
$stdout.puts(JSON.pretty_generate(instances))
41+
0
42+
end
43+
44+
def fetch_nats_by_zone(zones, count, project, region)
45+
instances = {}
46+
instances_command = %w[
47+
gcloud compute instance-groups list-instances
48+
]
49+
50+
zones.each do |zone|
51+
(count / zones.length).times do |count_index|
52+
full_command = instances_command + %W[
53+
nat-#{zone}-#{count_index + 1}
54+
--project=#{project}
55+
--zone=#{region}-#{zone}
56+
--format="value(instance)"
57+
]
58+
instance_name = `#{full_command.join(' ')}`.strip
59+
next if instance_name.empty?
60+
61+
instances["nat-#{zone}-#{count_index + 1}"] = %W[
62+
projects
63+
#{project}
64+
zones
65+
#{region}-#{zone}
66+
instances
67+
#{instance_name}
68+
].join('/')
69+
rescue StandardError => e
70+
warn e
71+
end
72+
end
73+
74+
instances
75+
end
76+
77+
def usage
78+
warn <<~EOF
79+
Usage: #{$PROGRAM_NAME} [-h|--help]
80+
81+
Print a mapping of {zone}-{index}=>{instance-id} given a JSON blob
82+
containing the following arguments provided via stdin:
83+
84+
accounting - boolean controlling count check and retries, default=false
85+
count - total number of expected instances, default=zones.length
86+
project - the gcloud project name *REQUIRED*
87+
region - the region in which to look for nat instances *REQUIRED*
88+
retries - number of total retries, default=4
89+
retry_sleep - sleep interval between retries, default=120
90+
zones - a comma-delimited list of zones within the region
91+
EOF
92+
2
93+
end
94+
95+
exit(main) if $PROGRAM_NAME == __FILE__

bin/gcloud-recreate-nat-instances

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#!/usr/bin/env ruby
2+
# frozen_string_literal: true
3+
4+
require 'optparse'
5+
require 'json'
6+
7+
def main
8+
options = { env: '', noop: false }
9+
OptionParser.new do |opts|
10+
opts.banner = <<~BANNER
11+
Usage: gcloud-recreate-nat-instances [options]
12+
13+
Recreates the NAT instances managed by the instance groups specified using
14+
the GCE_NAT_GROUPS environment variable. Also requires GCE_NAT_PROJECT
15+
and GCE_NAT_REGION to be set.
16+
17+
BANNER
18+
19+
opts.on('-E', '--env=ENV_FILE') do |f|
20+
options[:env] = f.strip
21+
end
22+
23+
opts.on('-n', '--noop') do
24+
options[:noop] = true
25+
end
26+
27+
opts.on('--help') do
28+
puts opts
29+
exit
30+
end
31+
end.parse!
32+
33+
env = Hash[ENV]
34+
env.merge!(source_env(options[:env])) unless options[:env].empty?
35+
36+
project = env.fetch('GCE_NAT_PROJECT')
37+
region = env.fetch('GCE_NAT_REGION')
38+
groups = env.fetch('GCE_NAT_GROUPS').split(',').map(&:strip)
39+
40+
groups_zones = groups.map do |group|
41+
[group, "#{region}-#{group.split('-').fetch(1)}"]
42+
end
43+
groups_zones = Hash[groups_zones]
44+
45+
groups_zones.each do |instance_group, zone|
46+
instance_list = list_instances(instance_group, zone, project)
47+
instances = instance_list.map { |r| r.fetch('instance').split('/').last }
48+
command = %W[
49+
gcloud compute instance-groups managed recreate-instances
50+
#{instance_group} --zone=#{zone} --project=#{project}
51+
--instances=#{instances.join(',')}
52+
]
53+
run_command(command, options.fetch(:noop))
54+
end
55+
56+
0
57+
end
58+
59+
def list_instances(instance_group, zone, project)
60+
command = %W[
61+
gcloud compute instance-groups list-instances
62+
#{instance_group}
63+
--zone=#{zone}
64+
--project=#{project}
65+
--format=json
66+
]
67+
JSON.parse(`#{command.join(' ')}`)
68+
end
69+
70+
def run_command(command, noop)
71+
if noop
72+
puts "---> NOOP: #{command.join(' ').inspect}"
73+
else
74+
puts "---> RUNNING: #{command.join(' ').inspect}"
75+
system(*command)
76+
end
77+
end
78+
79+
def source_env(env_file)
80+
base_env = `bash -c 'printenv'`.split($RS).map do |l|
81+
l.strip.split('=', 2)
82+
end
83+
base_env = Hash[base_env]
84+
sourced_env = `bash -c "source #{env_file}; printenv"`.split($RS).map do |l|
85+
l.strip.split('=', 2)
86+
end
87+
sourced_env = Hash[sourced_env]
88+
base_env.keys.each { |k| sourced_env.delete(k) }
89+
sourced_env
90+
end
91+
92+
exit(main) if $PROGRAM_NAME == __FILE__

bin/generate-latest-docker-image-tags

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ def main
3232
end
3333

3434
%w[
35+
gesund
36+
nat-conntracker
3537
worker
3638
].each do |image|
3739
tag = latest_docker_image_tag("travisci/#{image}")

bin/nat-conntracker-configure

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#!/usr/bin/env ruby
2+
# frozen_string_literal: true
3+
4+
require 'json'
5+
require 'net/http'
6+
require 'net/https'
7+
require 'optparse'
8+
9+
def main
10+
options = {
11+
app: '',
12+
dst_ignore: [],
13+
heroku_api_hostname: 'api.heroku.com',
14+
heroku_api_key: ENV.fetch('HEROKU_API_KEY', ''),
15+
src_ignore: []
16+
}
17+
18+
OptionParser.new do |opts|
19+
opts.on(
20+
'-a', '--app=APP',
21+
'name of nat-conntracker heroku app'
22+
) { |v| options[:app] = v.strip }
23+
24+
opts.on(
25+
'-d', '--dst-ignore=CIDRS',
26+
'comma-delimited list of destination CIDRs to ignore'
27+
) { |v| options[:dst_ignore] = v.split(',').map(&:strip) }
28+
29+
opts.on(
30+
'-s', '--src-ignore=CIDRS',
31+
'comma-delimited list of source CIDRs to ignore'
32+
) { |v| options[:src_ignore] = v.split(',').map(&:strip) }
33+
34+
opts.on(
35+
'--heroku-api-hostname=HOSTNAME',
36+
'hostname of Heroku API from which to fetch stuff'
37+
) { |v| options[:heroku_api_hostname] = v.strip }
38+
39+
opts.on(
40+
'--heroku-api-key=KEY',
41+
'API key to use with Heroku API'
42+
) { |v| options[:heroku_api_key] = v.strip }
43+
end.parse!
44+
45+
expand_private!(options[:dst_ignore])
46+
expand_private!(options[:src_ignore])
47+
48+
unless Array(options[:dst_ignore]).empty?
49+
dst_command = %W[
50+
trvs redis-cli #{options[:app]} REDIS_URL SADD nat-conntracker:dst-ignore
51+
] + Array(options[:dst_ignore])
52+
warn("---> #{dst_command.join(' ')}")
53+
system(*dst_command)
54+
end
55+
56+
unless Array(options[:src_ignore]).empty?
57+
src_command = %W[
58+
trvs redis-cli #{options[:app]} REDIS_URL SADD nat-conntracker:src-ignore
59+
] + Array(options[:src_ignore])
60+
warn("---> #{src_command.join(' ')}")
61+
system(*src_command)
62+
end
63+
64+
0
65+
end
66+
67+
def expand_private!(cidrs)
68+
cidrs.map! do |cidr|
69+
if cidr == 'private'
70+
PRIVATE_SUBNETS
71+
else
72+
cidr
73+
end
74+
end
75+
cidrs.flatten!
76+
cidrs
77+
end
78+
79+
PRIVATE_SUBNETS = %w[
80+
10.0.0.0/8
81+
127.0.0.0/8
82+
169.254.0.0/16
83+
172.16.0.0/12
84+
192.0.2.0/24
85+
192.168.0.0/16
86+
].freeze
87+
88+
exit(main) if $PROGRAM_NAME == __FILE__

bin/write-config-files

+16-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ def main(argv: ARGV)
3636
)
3737
end
3838

39+
if opts[:write_nat]
40+
fwrite(
41+
'config/nat.env',
42+
trvs_gen(
43+
"#{opts[:infra]}-nat", opts[:env_short],
44+
pro: true, prefix: "#{opts[:infra].upcase}_NAT"
45+
)
46+
)
47+
end
48+
3949
{
4050
'config/travis-org.env' => "travis-#{opts[:env_short]}",
4151
'config/travis-build-org.env' => "travis-build-#{opts[:env_short]}",
@@ -104,7 +114,8 @@ def parse_argv(argv)
104114
job_board_host: '',
105115
amqp_url_com_varname: '',
106116
amqp_url_org_varname: '',
107-
write_bastion: false
117+
write_bastion: false,
118+
write_nat: false
108119
}
109120

110121
OptionParser.new do |o|
@@ -143,6 +154,10 @@ def parse_argv(argv)
143154
o.on('--write-bastion') do
144155
opts[:write_bastion] = true
145156
end
157+
158+
o.on('--write-nat') do
159+
opts[:write_nat] = true
160+
end
146161
end.parse(argv)
147162

148163
opts

0 commit comments

Comments
 (0)