1
#include "source/server/admin/runtime_handler.h"
2

            
3
#include <string>
4
#include <vector>
5

            
6
#include "source/common/common/empty_string.h"
7
#include "source/common/http/headers.h"
8
#include "source/common/http/utility.h"
9
#include "source/server/admin/utils.h"
10

            
11
#include "absl/container/node_hash_map.h"
12

            
13
namespace Envoy {
14
namespace Server {
15

            
16
10735
RuntimeHandler::RuntimeHandler(Server::Instance& server) : HandlerContextBase(server) {}
17

            
18
Http::Code RuntimeHandler::handlerRuntime(Http::ResponseHeaderMap& response_headers,
19
120
                                          Buffer::Instance& response, AdminStream& admin_stream) {
20
120
  const Http::Utility::QueryParamsMulti params = admin_stream.queryParams();
21
120
  response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json);
22

            
23
  // TODO(jsedgwick): Use proto to structure this output instead of arbitrary JSON.
24
120
  const auto& layers = server_.runtime().snapshot().getLayers();
25

            
26
120
  std::vector<Protobuf::Value> layer_names;
27
120
  layer_names.reserve(layers.size());
28
120
  std::map<std::string, std::vector<std::string>> entries;
29
339
  for (const auto& layer : layers) {
30
339
    layer_names.push_back(ValueUtil::stringValue(layer->name()));
31
590
    for (const auto& value : layer->values()) {
32
590
      const auto found = entries.find(value.first);
33
590
      if (found == entries.end()) {
34
552
        entries.emplace(value.first, std::vector<std::string>{});
35
552
      }
36
590
    }
37
339
  }
38

            
39
339
  for (const auto& layer : layers) {
40
1578
    for (auto& entry : entries) {
41
1578
      const auto found = layer->values().find(entry.first);
42
1578
      const auto& entry_value =
43
1578
          found == layer->values().end() ? EMPTY_STRING : found->second.raw_string_value_;
44
1578
      entry.second.push_back(entry_value);
45
1578
    }
46
339
  }
47

            
48
120
  Protobuf::Struct layer_entries;
49
120
  auto* layer_entry_fields = layer_entries.mutable_fields();
50
552
  for (const auto& entry : entries) {
51
552
    std::vector<Protobuf::Value> layer_entry_values;
52
552
    layer_entry_values.reserve(entry.second.size());
53
552
    std::string final_value;
54
1578
    for (const auto& value : entry.second) {
55
1578
      if (!value.empty()) {
56
590
        final_value = value;
57
590
      }
58
1578
      layer_entry_values.push_back(ValueUtil::stringValue(value));
59
1578
    }
60

            
61
552
    Protobuf::Struct layer_entry_value;
62
552
    auto* layer_entry_value_fields = layer_entry_value.mutable_fields();
63

            
64
552
    (*layer_entry_value_fields)["final_value"] = ValueUtil::stringValue(final_value);
65
552
    (*layer_entry_value_fields)["layer_values"] = ValueUtil::listValue(layer_entry_values);
66
552
    (*layer_entry_fields)[entry.first] = ValueUtil::structValue(layer_entry_value);
67
552
  }
68

            
69
120
  Protobuf::Struct runtime;
70
120
  auto* fields = runtime.mutable_fields();
71

            
72
120
  (*fields)["layers"] = ValueUtil::listValue(layer_names);
73
120
  (*fields)["entries"] = ValueUtil::structValue(layer_entries);
74

            
75
120
  response.add(MessageUtil::getJsonStringFromMessageOrError(runtime, true, true));
76
120
  return Http::Code::OK;
77
120
}
78

            
79
Http::Code RuntimeHandler::handlerRuntimeModify(Http::ResponseHeaderMap&,
80
                                                Buffer::Instance& response,
81
7
                                                AdminStream& admin_stream) {
82
7
  Http::Utility::QueryParamsMulti params = admin_stream.queryParams();
83
7
  if (params.data().empty()) {
84
1
    response.add("usage: /runtime_modify?key1=value1&key2=value2&keyN=valueN\n");
85
1
    response.add("       or send the parameters as form values\n");
86
1
    response.add("use an empty value to remove a previously added override");
87
1
    return Http::Code::BadRequest;
88
1
  }
89
6
  absl::node_hash_map<std::string, std::string> overrides;
90
11
  for (const auto& it : params.data()) {
91
11
    overrides.insert({it.first, it.second[0]});
92
11
  }
93
6
  TRY_ASSERT_MAIN_THREAD { THROW_IF_NOT_OK(server_.runtime().mergeValues(overrides)); }
94
6
  END_TRY
95
6
  catch (const EnvoyException& e) {
96
1
    ENVOY_LOG_MISC(error, "{}", e.what());
97
1
    response.add(e.what());
98
1
    return Http::Code::ServiceUnavailable;
99
1
  }
100
5
  response.add("OK\n");
101
5
  return Http::Code::OK;
102
6
}
103

            
104
} // namespace Server
105
} // namespace Envoy