Skip to content

Commit d9d6296

Browse files
author
Himani Anil Deshpande
committed
[DevSetting] Add install_proxy_url which will allow ParallelCluster to set Proxy environment for Build Image installation
* Adding a recipe to setup proxy env as part of cookbook to avoid Sizing issue in parallelcluster.yaml * Add s3 global endpoint to overcome cfn_bootstrap script installation * Add s3 enpoint for no proxy list so it goes to VPC endpoint * addding old style s3 naming convention bucket-name.s3-eu-west-1.amazonaws.com to handle S3 redirecting (307 Temporary Redirect) when we use non-us-east-1 region * Add cdn for amazonlinux in no_proxy list to re-direct to VPC endpoint * add unit test
1 parent b00c861 commit d9d6296

File tree

4 files changed

+186
-1
lines changed

4 files changed

+186
-1
lines changed

cookbooks/aws-parallelcluster-entrypoints/recipes/install.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
return if node['conditions']['ami_bootstrapped']
1818

1919
include_recipe "aws-parallelcluster-shared::setup_envars"
20+
include_recipe "aws-parallelcluster-shared::detect_proxy" if node['cluster']['install_proxy_url']
2021

2122
include_recipe 'aws-parallelcluster-platform::install'
2223
include_recipe 'aws-parallelcluster-environment::install'

cookbooks/aws-parallelcluster-entrypoints/spec/unit/recipes/install_spec.rb

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@
2323
aws-parallelcluster-awsbatch::install
2424
)
2525

26+
detect_proxy_recipe = 'aws-parallelcluster-shared::detect_proxy'
27+
2628
before do
2729
@included_recipes = []
28-
all_recipes.each do |recipe_name|
30+
(all_recipes + [detect_proxy_recipe]).each do |recipe_name|
2931
allow_any_instance_of(Chef::Recipe).to receive(:include_recipe).with(recipe_name) do
3032
@included_recipes << recipe_name
3133
end
@@ -61,6 +63,7 @@
6163
it "includes all recipes in the right order" do
6264
chef_run
6365
expect(@included_recipes).to eq(all_recipes)
66+
expect(@included_recipes).not_to include(detect_proxy_recipe)
6467
end
6568
end
6669

@@ -78,6 +81,22 @@
7881
expect(@included_recipes).to eq(all_recipes - %w(aws-parallelcluster-awsbatch::install))
7982
end
8083
end
84+
85+
context "when install_proxy_url is set" do
86+
cached(:chef_run) do
87+
runner = runner(platform: platform, version: version) do |node|
88+
node.override['conditions']['ami_bootstrapped'] = false
89+
node.override['cluster']['skip_awsbatch_cli_install'] = false
90+
node.override['cluster']['install_proxy_url'] = 'http://10.0.0.109:8888'
91+
end
92+
runner.converge(described_recipe)
93+
end
94+
95+
it "includes detect_proxy recipe" do
96+
chef_run
97+
expect(@included_recipes).to include(detect_proxy_recipe)
98+
end
99+
end
81100
end
82101
end
83102
end
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# frozen_string_literal: true
2+
3+
#
4+
# Cookbook:: aws-parallelcluster
5+
# Recipe:: detect_proxy
6+
#
7+
# Copyright:: 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
8+
#
9+
# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with the
10+
# License. A copy of the License is located at
11+
#
12+
# http://aws.amazon.com/apache2.0/
13+
#
14+
# or in the "LICENSE.txt" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
15+
# OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
18+
# This recipe configures proxy environment variables for build-image in isolated networks.
19+
#
20+
# It reads the proxy URL from node['cluster']['install_proxy_url'] (set via ExtraChefAttributes)
21+
# and configures http_proxy/https_proxy ENV vars for the Chef run. This makes all subsequent
22+
# Chef resources (remote_file, bash, execute, etc.) use the explicit proxy for HTTPS traffic
23+
# instead of trying direct connections that would fail in an isolated network.
24+
#
25+
# The no_proxy list excludes S3 endpoints so downloads from S3 go through the VPC Gateway
26+
# Endpoint directly, not through the proxy.
27+
# S3 endpoints are excluded so cookbook/dependency downloads from S3 go through
28+
# the S3 VPC Gateway Endpoint directly, not through the proxy.
29+
# Both regional (s3.{region}.amazonaws.com) and global (s3.amazonaws.com) endpoints
30+
# are included because some resources use the global endpoint (e.g., cloudformation-examples
31+
# bucket uses https://s3.amazonaws.com/cloudformation-examples/...).
32+
# Note: only the regional S3 endpoint is in no_proxy because the S3 VPC Gateway Endpoint
33+
# handles regional endpoints correctly. The global s3.amazonaws.com endpoint does NOT work
34+
# through the VPC Gateway Endpoint (SSL errors), so it is intentionally left out of no_proxy
35+
# and instead goes through the proxy which has internet access. The proxy allowlist in
36+
# proxy_stack.yaml must include s3.amazonaws.com for this to work.
37+
# IMDS (169.254.169.254) and ECS task metadata (169.254.170.2) are also excluded.
38+
#
39+
# This recipe only runs when install_proxy_url is set — normal builds are unaffected.
40+
41+
ruby_block 'configure proxy from install_proxy_url' do
42+
block do
43+
proxy_url = node['cluster']['install_proxy_url']
44+
45+
if proxy_url && !proxy_url.empty?
46+
# Validate proxy URL format: must be http://host:port
47+
unless proxy_url.match?(%r{^https?://[^/:]+:\d+/?$})
48+
raise "Invalid install_proxy_url '#{proxy_url}'. Expected format: http://host:port"
49+
end
50+
51+
region = node['cluster']['region']
52+
53+
# S3 endpoints bypass the proxy and use the VPC Gateway Endpoint.
54+
# Includes regional (s3.{region}), dash-style (s3-{region}), global (s3.amazonaws.com),
55+
# and dualstack (s3.dualstack.{region}) variants used by different AWS services and repos.
56+
no_proxy = [
57+
"localhost",
58+
"127.0.0.1",
59+
"169.254.169.254",
60+
"169.254.170.2",
61+
".s3.#{region}.amazonaws.com",
62+
"s3.#{region}.amazonaws.com",
63+
".s3-#{region}.amazonaws.com",
64+
"s3-#{region}.amazonaws.com",
65+
".s3.amazonaws.com",
66+
".s3.dualstack.#{region}.amazonaws.com",
67+
"s3.dualstack.#{region}.amazonaws.com",
68+
].join(",")
69+
70+
Chef::Log.info("Configuring proxy: #{proxy_url}")
71+
72+
ENV['http_proxy'] = proxy_url
73+
ENV['https_proxy'] = proxy_url
74+
ENV['HTTP_PROXY'] = proxy_url
75+
ENV['HTTPS_PROXY'] = proxy_url
76+
ENV['no_proxy'] = no_proxy
77+
ENV['NO_PROXY'] = no_proxy
78+
else
79+
Chef::Log.info("No install_proxy_url set, skipping proxy configuration")
80+
end
81+
end
82+
end
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
describe 'aws-parallelcluster-shared::detect_proxy' do
6+
for_all_oses do |platform, version|
7+
context "on #{platform}#{version}" do
8+
before(:each) do
9+
# Clean proxy ENV vars between tests to prevent leakage
10+
%w(http_proxy https_proxy HTTP_PROXY HTTPS_PROXY no_proxy NO_PROXY).each do |var|
11+
ENV.delete(var)
12+
end
13+
end
14+
15+
context 'when install_proxy_url is set with valid URL' do
16+
cached(:test_region) { 'test-region-1' }
17+
cached(:chef_run) do
18+
runner(platform: platform, version: version) do |node|
19+
node.override['cluster'] = { 'install_proxy_url' => 'http://10.0.0.109:8888', 'region' => test_region }
20+
end.converge(described_recipe)
21+
end
22+
23+
it 'configures proxy environment variables' do
24+
expect(chef_run).to run_ruby_block('configure proxy from install_proxy_url')
25+
end
26+
27+
it 'sets proxy env vars in the ruby block' do
28+
chef_run
29+
block = chef_run.ruby_block('configure proxy from install_proxy_url')
30+
block.block.call
31+
32+
expect(ENV['http_proxy']).to eq('http://10.0.0.109:8888')
33+
expect(ENV['https_proxy']).to eq('http://10.0.0.109:8888')
34+
expect(ENV['HTTP_PROXY']).to eq('http://10.0.0.109:8888')
35+
expect(ENV['HTTPS_PROXY']).to eq('http://10.0.0.109:8888')
36+
expect(ENV['no_proxy']).to include(".s3.#{test_region}.amazonaws.com")
37+
expect(ENV['no_proxy']).to include("s3.#{test_region}.amazonaws.com")
38+
expect(ENV['no_proxy']).to include(".s3-#{test_region}.amazonaws.com")
39+
expect(ENV['no_proxy']).to include('.s3.amazonaws.com')
40+
expect(ENV['no_proxy']).to include(".s3.dualstack.#{test_region}.amazonaws.com")
41+
expect(ENV['no_proxy']).to include('169.254.169.254')
42+
expect(ENV['no_proxy']).to include('localhost')
43+
end
44+
end
45+
46+
[nil, ''].each do |proxy_value|
47+
context "when install_proxy_url is #{proxy_value.nil? ? 'not set' : 'empty string'}" do
48+
cached(:chef_run) do
49+
runner(platform: platform, version: version) do |node|
50+
attrs = { 'region' => 'test-region-1' }
51+
attrs['install_proxy_url'] = proxy_value unless proxy_value.nil?
52+
node.override['cluster'] = attrs
53+
end.converge(described_recipe)
54+
end
55+
56+
it 'does not configure proxy' do
57+
chef_run
58+
block = chef_run.ruby_block('configure proxy from install_proxy_url')
59+
block.block.call
60+
61+
expect(ENV['http_proxy']).to be_nil
62+
end
63+
end
64+
end
65+
66+
{ 'not-a-valid-url' => 'invalid format', 'http://10.0.0.109' => 'missing port' }.each do |url, description|
67+
context "when install_proxy_url has #{description}" do
68+
cached(:chef_run) do
69+
runner(platform: platform, version: version) do |node|
70+
node.override['cluster'] = { 'install_proxy_url' => url, 'region' => 'test-region-1' }
71+
end.converge(described_recipe)
72+
end
73+
74+
it 'raises an error' do
75+
chef_run
76+
block = chef_run.ruby_block('configure proxy from install_proxy_url')
77+
expect { block.block.call }.to raise_error(RuntimeError, /Invalid install_proxy_url/)
78+
end
79+
end
80+
end
81+
end
82+
end
83+
end

0 commit comments

Comments
 (0)