Skip to content

Commit d944777

Browse files
author
Manish
authored
Added support for configurable socket address in filesystem for hot restart (envoyproxy#12712)
Fixes envoyproxy#2908 Signed-off-by: Manish Kumar <[email protected]>
1 parent 5875f23 commit d944777

24 files changed

+192
-39
lines changed

api/envoy/admin/v3/server_info.proto

+7-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ message ServerInfo {
5454
CommandLineOptions command_line_options = 6;
5555
}
5656

57-
// [#next-free-field: 35]
57+
// [#next-free-field: 37]
5858
message CommandLineOptions {
5959
option (udpa.annotations.versioning).previous_message_type =
6060
"envoy.admin.v2alpha.CommandLineOptions";
@@ -179,4 +179,10 @@ message CommandLineOptions {
179179

180180
// See :option:`--enable-fine-grain-logging` for details.
181181
bool enable_fine_grain_logging = 34;
182+
183+
// See :option:`--socket-path` for details.
184+
string socket_path = 35;
185+
186+
// See :option:`--socket-mode` for details.
187+
uint32 socket_mode = 36;
182188
}

api/envoy/admin/v4alpha/server_info.proto

+7-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/root/operations/cli.rst

+15
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,21 @@ following are the command line options that Envoy supports.
192192
more. The administration interface usage is similar. Please see `Administration interface
193193
<https://www.envoyproxy.io/docs/envoy/latest/operations/admin>`_ for more detail.
194194

195+
.. option:: --socket-path <path string>
196+
197+
*(optional)* The output file path to the socket address for :ref:`hot restart <arch_overview_hot_restart>`.
198+
Default to "@envoy_domain_socket" which will be created in the abstract namespace. Suffix _{role}_{id}
199+
is appended to provide name. All envoy processes wanting to participate in hot-restart together must
200+
use the same value for this option.
201+
202+
**NOTE**: The path started with "@" will be created in the abstract namespace.
203+
204+
.. option:: --socket-mode <string>
205+
206+
*(optional)* The socket file permission for :ref:`hot restart <arch_overview_hot_restart>`.
207+
This must be a valid octal file permission, such as 644. The default value is 600.
208+
This flag may not be used when :option:`--socket-path` is start with "@" or not set.
209+
195210
.. option:: --hot-restart-version
196211

197212
*(optional)* Outputs an opaque hot restart compatibility version for the binary. This can be

docs/root/version_history/current.rst

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ New Features
7979
* grpc-json: support specifying `response_body` field in for `google.api.HttpBody` message.
8080
* hds: added :ref:`cluster_endpoints_health <envoy_v3_api_field_service.health.v3.EndpointHealthResponse.cluster_endpoints_health>` to HDS responses, keeping endpoints in the same groupings as they were configured in the HDS specifier by cluster and locality instead of as a flat list.
8181
* hds: added :ref:`transport_socket_matches <envoy_v3_api_field_service.health.v3.ClusterHealthCheck.transport_socket_matches>` to HDS cluster health check specifier, so the existing match filter :ref:`transport_socket_match_criteria <envoy_v3_api_field_config.core.v3.HealthCheck.transport_socket_match_criteria>` in the repeated field :ref:`health_checks <envoy_v3_api_field_service.health.v3.ClusterHealthCheck.health_checks>` has context to match against. This unblocks support for health checks over HTTPS and HTTP/2.
82+
* hot restart: added :option:`--socket-path` and :option:`--socket-mode` to configure UDS path in the filesystem and set permission to it.
8283
* http: added support for :ref:`%DOWNSTREAM_PEER_FINGERPRINT_1% <config_http_conn_man_headers_custom_request_headers>` as custom header.
8384
* http: added :ref:`allow_chunked_length <envoy_v3_api_field_config.core.v3.Http1ProtocolOptions.allow_chunked_length>` configuration option for HTTP/1 codec to allow processing requests/responses with both Content-Length and Transfer-Encoding: chunked headers. If such message is served and option is enabled - per RFC Content-Length is ignored and removed.
8485
* http: introduced new HTTP/1 and HTTP/2 codec implementations that will remove the use of exceptions for control flow due to high risk factors and instead use error statuses. The old behavior is used by default, but the new codecs can be enabled for testing by setting the runtime feature `envoy.reloadable_features.new_codec_behavior` to true. The new codecs will be in development for one month, and then enabled by default while the old codecs are deprecated.

generated_api_shadow/envoy/admin/v3/server_info.proto

+7-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

generated_api_shadow/envoy/admin/v4alpha/server_info.proto

+7-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

include/envoy/server/options.h

+10
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,16 @@ class Options {
254254
* @return CommandLineOptionsPtr the protobuf representation of the options.
255255
*/
256256
virtual CommandLineOptionsPtr toCommandLineOptions() const PURE;
257+
258+
/**
259+
* @return the path of socket file.
260+
*/
261+
virtual const std::string& socketPath() const PURE;
262+
263+
/**
264+
* @return the mode of socket file.
265+
*/
266+
virtual mode_t socketMode() const PURE;
257267
};
258268

259269
} // namespace Server

source/exe/main_common.cc

+4-2
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ void MainCommonBase::configureHotRestarter(Random::RandomGenerator& random_gener
120120
base_id = static_cast<uint32_t>(random_generator.random()) & 0x0FFFFFFF;
121121

122122
try {
123-
restarter = std::make_unique<Server::HotRestartImpl>(base_id, 0);
123+
restarter = std::make_unique<Server::HotRestartImpl>(base_id, 0, options_.socketPath(),
124+
options_.socketMode());
124125
} catch (Server::HotRestartDomainSocketInUseException& ex) {
125126
// No luck, try again.
126127
ENVOY_LOG_MISC(debug, "dynamic base id: {}", ex.what());
@@ -133,7 +134,8 @@ void MainCommonBase::configureHotRestarter(Random::RandomGenerator& random_gener
133134

134135
restarter_.swap(restarter);
135136
} else {
136-
restarter_ = std::make_unique<Server::HotRestartImpl>(base_id, options_.restartEpoch());
137+
restarter_ = std::make_unique<Server::HotRestartImpl>(
138+
base_id, options_.restartEpoch(), options_.socketPath(), options_.socketMode());
137139
}
138140

139141
// Write the base-id to the requested path whether we selected it

source/server/hot_restart_impl.cc

+4-3
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,11 @@ void initializeMutex(pthread_mutex_t& mutex) {
9595
// performed the multiplication in OptionsImpl which produced incorrect server info output.
9696
// TODO(zuercher): ideally, the base_id would be separated from the restart_epoch in
9797
// the socket names to entirely prevent collisions between consecutive base ids.
98-
HotRestartImpl::HotRestartImpl(uint32_t base_id, uint32_t restart_epoch)
98+
HotRestartImpl::HotRestartImpl(uint32_t base_id, uint32_t restart_epoch,
99+
const std::string& socket_path, mode_t socket_mode)
99100
: base_id_(base_id), scaled_base_id_(base_id * 10),
100-
as_child_(HotRestartingChild(scaled_base_id_, restart_epoch)),
101-
as_parent_(HotRestartingParent(scaled_base_id_, restart_epoch)),
101+
as_child_(HotRestartingChild(scaled_base_id_, restart_epoch, socket_path, socket_mode)),
102+
as_parent_(HotRestartingParent(scaled_base_id_, restart_epoch, socket_path, socket_mode)),
102103
shmem_(attachSharedMemory(scaled_base_id_, restart_epoch)), log_lock_(shmem_->log_lock_),
103104
access_log_lock_(shmem_->access_log_lock_) {
104105
// If our parent ever goes away just terminate us so that we don't have to rely on ops/launching

source/server/hot_restart_impl.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ class ProcessSharedMutex : public Thread::BasicLockable {
9898
*/
9999
class HotRestartImpl : public HotRestart {
100100
public:
101-
HotRestartImpl(uint32_t base_id, uint32_t restart_epoch);
101+
HotRestartImpl(uint32_t base_id, uint32_t restart_epoch, const std::string& socket_path,
102+
mode_t socket_mode);
102103

103104
// Server::HotRestart
104105
void drainParentListeners() override;

source/server/hot_restarting_base.cc

+12-9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "common/api/os_sys_calls_impl.h"
44
#include "common/common/utility.h"
5+
#include "common/network/address_impl.h"
56
#include "common/stats/utility.h"
67

78
namespace Envoy {
@@ -24,28 +25,30 @@ void HotRestartingBase::initDomainSocketAddress(sockaddr_un* address) {
2425
address->sun_family = AF_UNIX;
2526
}
2627

27-
sockaddr_un HotRestartingBase::createDomainSocketAddress(uint64_t id, const std::string& role) {
28+
sockaddr_un HotRestartingBase::createDomainSocketAddress(uint64_t id, const std::string& role,
29+
const std::string& socket_path,
30+
mode_t socket_mode) {
2831
// Right now we only allow a maximum of 3 concurrent envoy processes to be running. When the third
2932
// starts up it will kill the oldest parent.
3033
static constexpr uint64_t MaxConcurrentProcesses = 3;
3134
id = id % MaxConcurrentProcesses;
32-
33-
// This creates an anonymous domain socket name (where the first byte of the name of \0).
3435
sockaddr_un address;
3536
initDomainSocketAddress(&address);
36-
StringUtil::strlcpy(&address.sun_path[1],
37-
fmt::format("envoy_domain_socket_{}_{}", role, base_id_ + id).c_str(),
38-
sizeof(address.sun_path) - 1);
39-
address.sun_path[0] = 0;
37+
Network::Address::PipeInstance addr(fmt::format(socket_path + "_{}_{}", role, base_id_ + id),
38+
socket_mode, nullptr);
39+
memcpy(&address, addr.sockAddr(), addr.sockAddrLen());
40+
fchmod(my_domain_socket_, socket_mode);
4041
return address;
4142
}
4243

43-
void HotRestartingBase::bindDomainSocket(uint64_t id, const std::string& role) {
44+
void HotRestartingBase::bindDomainSocket(uint64_t id, const std::string& role,
45+
const std::string& socket_path, mode_t socket_mode) {
4446
Api::OsSysCalls& os_sys_calls = Api::OsSysCallsSingleton::get();
4547
// This actually creates the socket and binds it. We use the socket in datagram mode so we can
4648
// easily read single messages.
4749
my_domain_socket_ = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0);
48-
sockaddr_un address = createDomainSocketAddress(id, role);
50+
sockaddr_un address = createDomainSocketAddress(id, role, socket_path, socket_mode);
51+
unlink(address.sun_path);
4952
Api::SysCallIntResult result =
5053
os_sys_calls.bind(my_domain_socket_, reinterpret_cast<sockaddr*>(&address), sizeof(address));
5154
if (result.rc_ != 0) {

source/server/hot_restarting_base.h

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#pragma once
22

33
#include <fcntl.h>
4+
#include <sys/stat.h>
45
#include <sys/un.h>
6+
#include <unistd.h>
57

68
#include <array>
79
#include <atomic>
@@ -28,8 +30,10 @@ class HotRestartingBase {
2830
~HotRestartingBase();
2931

3032
void initDomainSocketAddress(sockaddr_un* address);
31-
sockaddr_un createDomainSocketAddress(uint64_t id, const std::string& role);
32-
void bindDomainSocket(uint64_t id, const std::string& role);
33+
sockaddr_un createDomainSocketAddress(uint64_t id, const std::string& role,
34+
const std::string& socket_path, mode_t socket_mode);
35+
void bindDomainSocket(uint64_t id, const std::string& role, const std::string& socket_path,
36+
mode_t socket_mode);
3337
int myDomainSocket() const { return my_domain_socket_; }
3438

3539
// Protocol description:

source/server/hot_restarting_child.cc

+5-3
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ namespace Server {
77

88
using HotRestartMessage = envoy::HotRestartMessage;
99

10-
HotRestartingChild::HotRestartingChild(int base_id, int restart_epoch)
10+
HotRestartingChild::HotRestartingChild(int base_id, int restart_epoch,
11+
const std::string& socket_path, mode_t socket_mode)
1112
: HotRestartingBase(base_id), restart_epoch_(restart_epoch) {
1213
initDomainSocketAddress(&parent_address_);
1314
if (restart_epoch_ != 0) {
14-
parent_address_ = createDomainSocketAddress(restart_epoch_ + -1, "parent");
15+
parent_address_ =
16+
createDomainSocketAddress(restart_epoch_ + -1, "parent", socket_path, socket_mode);
1517
}
16-
bindDomainSocket(restart_epoch_, "child");
18+
bindDomainSocket(restart_epoch_, "child", socket_path, socket_mode);
1719
}
1820

1921
int HotRestartingChild::duplicateParentListenSocket(const std::string& address) {

source/server/hot_restarting_child.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ namespace Server {
1212
*/
1313
class HotRestartingChild : HotRestartingBase, Logger::Loggable<Logger::Id::main> {
1414
public:
15-
HotRestartingChild(int base_id, int restart_epoch);
15+
HotRestartingChild(int base_id, int restart_epoch, const std::string& socket_path,
16+
mode_t socket_mode);
1617

1718
int duplicateParentListenSocket(const std::string& address);
1819
std::unique_ptr<envoy::HotRestartMessage> getParentStats();

source/server/hot_restarting_parent.cc

+4-3
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ namespace Server {
1515

1616
using HotRestartMessage = envoy::HotRestartMessage;
1717

18-
HotRestartingParent::HotRestartingParent(int base_id, int restart_epoch)
18+
HotRestartingParent::HotRestartingParent(int base_id, int restart_epoch,
19+
const std::string& socket_path, mode_t socket_mode)
1920
: HotRestartingBase(base_id), restart_epoch_(restart_epoch) {
20-
child_address_ = createDomainSocketAddress(restart_epoch_ + 1, "child");
21-
bindDomainSocket(restart_epoch_, "parent");
21+
child_address_ = createDomainSocketAddress(restart_epoch_ + 1, "child", socket_path, socket_mode);
22+
bindDomainSocket(restart_epoch_, "parent", socket_path, socket_mode);
2223
}
2324

2425
void HotRestartingParent::initialize(Event::Dispatcher& dispatcher, Server::Instance& server) {

source/server/hot_restarting_parent.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ namespace Server {
1414
*/
1515
class HotRestartingParent : HotRestartingBase, Logger::Loggable<Logger::Id::main> {
1616
public:
17-
HotRestartingParent(int base_id, int restart_epoch);
17+
HotRestartingParent(int base_id, int restart_epoch, const std::string& socket_path,
18+
mode_t socket_mode);
1819
void initialize(Event::Dispatcher& dispatcher, Server::Instance& server);
1920
void shutdown();
2021

source/server/options_impl.cc

+22-1
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,12 @@ OptionsImpl::OptionsImpl(std::vector<std::string> args,
160160
"Comma-separated list of extensions to disable",
161161
false, "", "string", cmd);
162162

163+
TCLAP::ValueArg<std::string> socket_path("", "socket-path", "Path to hot restart socket file",
164+
false, "@envoy_domain_socket", "string", cmd);
165+
166+
TCLAP::ValueArg<std::string> socket_mode("", "socket-mode", "Socket file permission", false,
167+
"600", "string", cmd);
168+
163169
cmd.setExceptionHandling(false);
164170
try {
165171
cmd.parse(args);
@@ -263,6 +269,18 @@ OptionsImpl::OptionsImpl(std::vector<std::string> args,
263269
file_flush_interval_msec_ = std::chrono::milliseconds(file_flush_interval_msec.getValue());
264270
drain_time_ = std::chrono::seconds(drain_time_s.getValue());
265271
parent_shutdown_time_ = std::chrono::seconds(parent_shutdown_time_s.getValue());
272+
socket_path_ = socket_path.getValue();
273+
274+
if (socket_path_.at(0) == '@') {
275+
socket_mode_ = 0;
276+
} else {
277+
uint64_t socket_mode_helper;
278+
if (!StringUtil::atoull(socket_mode.getValue().c_str(), socket_mode_helper, 8)) {
279+
throw MalformedArgvException(
280+
fmt::format("error: invalid socket-mode '{}'", socket_mode.getValue()));
281+
}
282+
socket_mode_ = socket_mode_helper;
283+
}
266284

267285
if (drain_strategy.getValue() == "immediate") {
268286
drain_strategy_ = Server::DrainStrategy::Immediate;
@@ -393,6 +411,8 @@ Server::CommandLineOptionsPtr OptionsImpl::toCommandLineOptions() const {
393411
for (const auto& e : disabledExtensions()) {
394412
command_line_options->add_disabled_extensions(e);
395413
}
414+
command_line_options->set_socket_path(socketPath());
415+
command_line_options->set_socket_mode(socketMode());
396416
return command_line_options;
397417
}
398418

@@ -406,7 +426,8 @@ OptionsImpl::OptionsImpl(const std::string& service_cluster, const std::string&
406426
service_zone_(service_zone), file_flush_interval_msec_(10000), drain_time_(600),
407427
parent_shutdown_time_(900), drain_strategy_(Server::DrainStrategy::Gradual),
408428
mode_(Server::Mode::Serve), hot_restart_disabled_(false), signal_handling_enabled_(true),
409-
mutex_tracing_enabled_(false), cpuset_threads_(false), fake_symbol_table_enabled_(false) {}
429+
mutex_tracing_enabled_(false), cpuset_threads_(false), fake_symbol_table_enabled_(false),
430+
socket_path_("@envoy_domain_socket"), socket_mode_(0) {}
410431

411432
void OptionsImpl::disableExtensions(const std::vector<std::string>& names) {
412433
for (const auto& name : names) {

source/server/options_impl.h

+8
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ class OptionsImpl : public Server::Options, protected Logger::Loggable<Logger::I
104104
fake_symbol_table_enabled_ = fake_symbol_table_enabled;
105105
}
106106

107+
void setSocketPath(const std::string& socket_path) { socket_path_ = socket_path; }
108+
109+
void setSocketMode(mode_t socket_mode) { socket_mode_ = socket_mode; }
110+
107111
// Server::Options
108112
uint64_t baseId() const override { return base_id_; }
109113
bool useDynamicBaseId() const override { return use_dynamic_base_id_; }
@@ -154,6 +158,8 @@ class OptionsImpl : public Server::Options, protected Logger::Loggable<Logger::I
154158
return disabled_extensions_;
155159
}
156160
uint32_t count() const;
161+
const std::string& socketPath() const override { return socket_path_; }
162+
mode_t socketMode() const override { return socket_mode_; }
157163

158164
/**
159165
* disableExtensions parses the given set of extension names of
@@ -206,6 +212,8 @@ class OptionsImpl : public Server::Options, protected Logger::Loggable<Logger::I
206212
// Initialization added here to avoid integration_admin_test failure caused by uninitialized
207213
// enable_fine_grain_logging_.
208214
bool enable_fine_grain_logging_ = false;
215+
std::string socket_path_;
216+
mode_t socket_mode_;
209217
};
210218

211219
/**

0 commit comments

Comments
 (0)