Skip to content

Commit fdb3818

Browse files
committed
parse expiration time and reload config at time out
1 parent 1765c9f commit fdb3818

1 file changed

Lines changed: 158 additions & 1 deletion

File tree

plugins/s3_auth/s3_auth.cc

Lines changed: 158 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@
3737
#include <openssl/sha.h>
3838
#include <openssl/hmac.h>
3939

40+
#include <chrono>
41+
#include <atomic>
42+
#include <mutex>
43+
#include <shared_mutex>
44+
4045
#include <ts/ts.h>
4146
#include <ts/remap.h>
4247
#include "tscore/ink_config.h"
@@ -153,6 +158,7 @@ ConfigCache gConfCache;
153158
// One configuration setup
154159
//
155160
int event_handler(TSCont, TSEvent, void *); // Forward declaration
161+
int config_reloader(TSCont, TSEvent, void *);
156162

157163
class S3Config
158164
{
@@ -162,6 +168,9 @@ class S3Config
162168
if (get_cont) {
163169
_cont = TSContCreate(event_handler, nullptr);
164170
TSContDataSet(_cont, static_cast<void *>(this));
171+
172+
_conf_rld = TSContCreate(config_reloader, TSMutexCreate());
173+
TSContDataSet(_conf_rld, static_cast<void *>(this));
165174
}
166175
}
167176

@@ -171,9 +180,13 @@ class S3Config
171180
TSfree(_secret);
172181
TSfree(_keyid);
173182
TSfree(_token);
183+
TSfree(_conf_fname);
174184
if (_cont) {
175185
TSContDestroy(_cont);
176186
}
187+
if (_conf_rld) {
188+
TSContDestroy(_conf_rld);
189+
}
177190
}
178191

179192
// Is this configuration usable?
@@ -212,16 +225,19 @@ class S3Config
212225
copy_changes_from(const S3Config *src)
213226
{
214227
if (src->_secret) {
228+
TSfree(_secret);
215229
_secret = TSstrdup(src->_secret);
216230
_secret_len = src->_secret_len;
217231
}
218232

219233
if (src->_keyid) {
234+
TSfree(_keyid);
220235
_keyid = TSstrdup(src->_keyid);
221236
_keyid_len = src->_keyid_len;
222237
}
223238

224239
if (src->_token) {
240+
TSfree(_token);
225241
_token = TSstrdup(src->_token);
226242
_token_len = src->_token_len;
227243
}
@@ -250,6 +266,13 @@ class S3Config
250266
_region_map = src->_region_map;
251267
_region_map_modified = true;
252268
}
269+
270+
_expiration = src->_expiration;
271+
272+
if (src->_conf_fname) {
273+
TSfree(_conf_fname);
274+
_conf_fname = TSstrdup(src->_conf_fname);
275+
}
253276
}
254277

255278
// Getters
@@ -319,6 +342,24 @@ class S3Config
319342
return _region_map;
320343
}
321344

345+
long
346+
expiration() const
347+
{
348+
return _expiration;
349+
}
350+
351+
const char *
352+
conf_fname() const
353+
{
354+
return _conf_fname;
355+
}
356+
357+
int
358+
incr_conf_reload_count()
359+
{
360+
return _conf_reload_count++;
361+
}
362+
322363
// Setters
323364
void
324365
set_secret(const char *s)
@@ -380,6 +421,25 @@ class S3Config
380421
_region_map_modified = true;
381422
}
382423

424+
void
425+
set_expiration(const char *s)
426+
{
427+
_expiration = strtol(s, nullptr, 10);
428+
}
429+
430+
void
431+
set_conf_fname(const char *s)
432+
{
433+
TSfree(_conf_fname);
434+
_conf_fname = TSstrdup(s);
435+
}
436+
437+
void
438+
reset_conf_reload_count()
439+
{
440+
_conf_reload_count = 0;
441+
}
442+
383443
// Parse configs from an external file
384444
bool parse_config(const std::string &filename);
385445

@@ -391,6 +451,15 @@ class S3Config
391451
TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_REQUEST_HDR_HOOK, _cont);
392452
}
393453

454+
void
455+
schedule_conf_reload(long delay) const
456+
{
457+
TSContScheduleOnPool(_conf_rld, delay * 1000, TS_THREAD_POOL_NET);
458+
}
459+
460+
std::shared_mutex reload_mutex;
461+
std::atomic_bool reload_waiting = false;
462+
394463
private:
395464
char *_secret = nullptr;
396465
size_t _secret_len = 0;
@@ -403,12 +472,16 @@ class S3Config
403472
bool _version_modified = false;
404473
bool _virt_host_modified = false;
405474
TSCont _cont = nullptr;
475+
TSCont _conf_rld = nullptr;
406476
StringSet _v4includeHeaders;
407477
bool _v4includeHeaders_modified = false;
408478
StringSet _v4excludeHeaders;
409479
bool _v4excludeHeaders_modified = false;
410480
StringMap _region_map;
411481
bool _region_map_modified = false;
482+
long _expiration = 0;
483+
char *_conf_fname = nullptr;
484+
int _conf_reload_count = 0;
412485
};
413486

414487
bool
@@ -465,6 +538,8 @@ S3Config::parse_config(const std::string &config_fname)
465538
set_exclude_headers(pos2 + 19);
466539
} else if (0 == strncasecmp(pos2, "v4-region-map=", 14)) {
467540
set_region_map(pos2 + 14);
541+
} else if (0 == strncasecmp(pos2, "expiration=", 11)) {
542+
set_expiration(pos2 + 11);
468543
} else {
469544
// ToDo: warnings?
470545
}
@@ -499,6 +574,7 @@ ConfigCache::get(const char *fname)
499574
if (tv.tv_sec > (it->second.second + _ttl)) {
500575
// Update the cached configuration file.
501576
S3Config *s3 = new S3Config(false); // false == this config does not get the continuation
577+
s3->set_conf_fname(fname);
502578

503579
TSDebug(PLUGIN_NAME, "Configuration from %s is stale, reloading", config_fname.c_str());
504580
it->second.second = tv.tv_sec;
@@ -516,6 +592,7 @@ ConfigCache::get(const char *fname)
516592
} else {
517593
// Create a new cached file.
518594
S3Config *s3 = new S3Config(false); // false == this config does not get the continuation
595+
s3->set_conf_fname(fname);
519596

520597
if (s3->parse_config(config_fname)) {
521598
_cache[config_fname] = std::make_pair(s3, tv.tv_sec);
@@ -876,7 +953,13 @@ event_handler(TSCont cont, TSEvent event, void *edata)
876953
switch (event) {
877954
case TS_EVENT_HTTP_SEND_REQUEST_HDR:
878955
if (request.initialize()) {
879-
status = request.authorize(s3);
956+
while (true) {
957+
if (!s3->reload_waiting) {
958+
std::shared_lock lock(s3->reload_mutex);
959+
status = request.authorize(s3);
960+
break;
961+
}
962+
}
880963
}
881964

882965
if (TS_HTTP_STATUS_OK == status) {
@@ -897,6 +980,63 @@ event_handler(TSCont cont, TSEvent event, void *edata)
897980
return 0;
898981
}
899982

983+
// If the token has more than one hour to expire, reload is scheduled one hour before expiration.
984+
// If the token has less than one hour to expire, reload is scheduled 15 minutes before expiration.
985+
// If the token has less than 15 minutes to expire, reload is scheduled at the expiration time.
986+
static long
987+
cal_reload_delay(long time_diff)
988+
{
989+
if (time_diff > 3600) {
990+
return time_diff - 3600;
991+
} else if (time_diff > 900) {
992+
return time_diff - 900;
993+
} else {
994+
return time_diff;
995+
}
996+
}
997+
998+
int
999+
config_reloader(TSCont cont, TSEvent event, void *edata)
1000+
{
1001+
TSDebug(PLUGIN_NAME, "reloading configs");
1002+
S3Config *s3 = static_cast<S3Config *>(TSContDataGet(cont));
1003+
S3Config *file_config = gConfCache.get(s3->conf_fname());
1004+
1005+
if (!file_config->valid()) {
1006+
TSError("[%s] requires both shared and AWS secret configuration", PLUGIN_NAME);
1007+
return TS_ERROR;
1008+
}
1009+
1010+
s3->reload_waiting = true;
1011+
{
1012+
std::unique_lock lock(s3->reload_mutex);
1013+
s3->copy_changes_from(file_config);
1014+
}
1015+
s3->reload_waiting = false;
1016+
1017+
if (s3->expiration() == 0) {
1018+
TSDebug(PLUGIN_NAME, "disabling auto config reload");
1019+
} else {
1020+
// auto reload is scheduled to be 5 minutes before the expiration time to get some headroom
1021+
long time_diff = s3->expiration() -
1022+
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
1023+
if (time_diff > 0) {
1024+
long delay = cal_reload_delay(time_diff);
1025+
TSDebug(PLUGIN_NAME, "scheduling config reload with %ld seconds delay", delay);
1026+
s3->reset_conf_reload_count();
1027+
s3->schedule_conf_reload(delay);
1028+
} else {
1029+
TSDebug(PLUGIN_NAME, "config expiration time is in the past, re-checking in 1 minute");
1030+
if (s3->incr_conf_reload_count() == 10) {
1031+
TSError("[%s] tried to reload config automatically but failed, please try manual reloading the config", PLUGIN_NAME);
1032+
}
1033+
s3->schedule_conf_reload(60);
1034+
}
1035+
}
1036+
1037+
return TS_SUCCESS;
1038+
}
1039+
9001040
///////////////////////////////////////////////////////////////////////////////
9011041
// Initialize the plugin.
9021042
//
@@ -1000,6 +1140,23 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char * /* errbuf ATS_UNUSE
10001140
return TS_ERROR;
10011141
}
10021142

1143+
if (s3->expiration() == 0) {
1144+
TSDebug(PLUGIN_NAME, "disabling auto config reload");
1145+
} else {
1146+
// auto reload is scheduled to be 5 minutes before the expiration time to get some headroom
1147+
long time_diff = s3->expiration() -
1148+
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
1149+
if (time_diff > 0) {
1150+
long delay = cal_reload_delay(time_diff);
1151+
TSDebug(PLUGIN_NAME, "scheduling config reload with %ld seconds delay", delay);
1152+
s3->reset_conf_reload_count();
1153+
s3->schedule_conf_reload(delay);
1154+
} else {
1155+
TSDebug(PLUGIN_NAME, "config expiration time is in the past, re-checking in 1 minute");
1156+
s3->schedule_conf_reload(60);
1157+
}
1158+
}
1159+
10031160
*ih = static_cast<void *>(s3);
10041161
TSDebug(PLUGIN_NAME, "New rule: access_key=%s, virtual_host=%s, version=%d", s3->keyid(), s3->virt_host() ? "yes" : "no",
10051162
s3->version());

0 commit comments

Comments
 (0)