1
#include "source/common/config/xds_context_params.h"
2

            
3
#include "source/common/common/macros.h"
4
#include "source/common/protobuf/utility.h"
5

            
6
namespace Envoy {
7
namespace Config {
8

            
9
namespace {
10

            
11
using RenderContextParamCb = std::function<std::string(const envoy::config::core::v3::Node& node)>;
12
using NodeContextRenderers = absl::flat_hash_map<std::string, RenderContextParamCb>;
13

            
14
12
RenderContextParamCb directStringFieldRenderer(const std::string& field) {
15
40
  return [field](const envoy::config::core::v3::Node& node) -> std::string {
16
39
    return MessageUtil::getStringField(node, field);
17
39
  };
18
12
}
19

            
20
9
RenderContextParamCb localityStringFieldRenderer(const std::string& field) {
21
11
  return [field](const envoy::config::core::v3::Node& node) -> std::string {
22
5
    return MessageUtil::getStringField(node.locality(), field);
23
5
  };
24
9
}
25

            
26
1
std::string buildSemanticVersionRenderer(const envoy::config::core::v3::Node& node) {
27
1
  const auto& semver = node.user_agent_build_version().version();
28
1
  return fmt::format("{}.{}.{}", semver.major_number(), semver.minor_number(), semver.patch());
29
1
}
30

            
31
94
const NodeContextRenderers& nodeParamCbs() {
32
94
  CONSTRUCT_ON_FIRST_USE(NodeContextRenderers, {"id", directStringFieldRenderer("id")},
33
94
                         {"cluster", directStringFieldRenderer("cluster")},
34
94
                         {"user_agent_name", directStringFieldRenderer("user_agent_name")},
35
94
                         {"user_agent_version", directStringFieldRenderer("user_agent_version")},
36
94
                         {"locality.region", localityStringFieldRenderer("region")},
37
94
                         {"locality.zone", localityStringFieldRenderer("zone")},
38
94
                         {"locality.sub_zone", localityStringFieldRenderer("sub_zone")},
39
94
                         {"user_agent_build_version.version", buildSemanticVersionRenderer});
40
94
}
41

            
42
void mergeMetadataJson(Protobuf::Map<std::string, std::string>& params,
43
2
                       const Protobuf::Struct& metadata, const std::string& prefix) {
44
2
#ifdef ENVOY_ENABLE_YAML
45
6
  for (const auto& it : metadata.fields()) {
46
6
    absl::StatusOr<std::string> json_or_error = MessageUtil::getJsonStringFromMessage(it.second);
47
6
    ENVOY_BUG(json_or_error.ok(), "Failed to parse json");
48
6
    if (json_or_error.ok()) {
49
6
      params[prefix + it.first] = json_or_error.value();
50
6
    }
51
6
  }
52
#else
53
  UNREFERENCED_PARAMETER(params);
54
  UNREFERENCED_PARAMETER(metadata);
55
  UNREFERENCED_PARAMETER(prefix);
56
  IS_ENVOY_BUG("JSON/YAML support compiled out");
57
#endif
58
2
}
59

            
60
} // namespace
61

            
62
xds::core::v3::ContextParams XdsContextParams::encodeResource(
63
    const xds::core::v3::ContextParams& node_context_params,
64
    const xds::core::v3::ContextParams& resource_context_params,
65
    const std::vector<std::string>& client_features,
66
87
    const absl::flat_hash_map<std::string, std::string>& extra_resource_params) {
67
87
  xds::core::v3::ContextParams context_params;
68
  // 1. Establish base layer of per-node context parameters.
69
87
  context_params.MergeFrom(node_context_params);
70

            
71
  // 2. Overlay with context parameters from resource name.
72
87
  auto& mutable_params = *context_params.mutable_params();
73
91
  for (const auto& it : resource_context_params.params()) {
74
19
    mutable_params[it.first] = it.second;
75
19
  }
76

            
77
  // 3. Overlay with per-resource type context parameters.
78
87
  for (const std::string& cf : client_features) {
79
3
    mutable_params["xds.client_feature." + cf] = "true";
80
3
  }
81

            
82
  // 4. Overlay with per-resource well-known attributes.
83
87
  for (const auto& it : extra_resource_params) {
84
3
    mutable_params["xds.resource." + it.first] = it.second;
85
3
  }
86

            
87
87
  return context_params;
88
87
}
89

            
90
xds::core::v3::ContextParams XdsContextParams::encodeNodeContext(
91
    const envoy::config::core::v3::Node& node,
92
10727
    const Protobuf::RepeatedPtrField<std::string>& node_context_params) {
93
  // E.g. if we have a node instance with contents
94
  //
95
  // user_agent_build_version:
96
  //   version:
97
  //     major_number: 1
98
  //     minor_number: 2
99
  //     patch: 3
100
  //   metadata:
101
  //     foo: true
102
  //     bar: "a"
103
  //     baz: 42
104
  //
105
  // and node_context_params [user_agent_build_version.version, user_agent_build_version.metadata],
106
  // we end up with the map:
107
  //
108
  // xds.node.user_agent_build_version.metadata.bar: "\"a\""
109
  // xds.node.user_agent_build_version.metadata.baz: "42"
110
  // xds.node.user_agent_build_version.metadata.foo: "true"
111
  // xds.node.user_agent_build_version.version: "1.2.3"
112
10727
  xds::core::v3::ContextParams context_params;
113
10727
  auto& mutable_params = *context_params.mutable_params();
114
10740
  for (const std::string& ncp : node_context_params) {
115
    // First attempt field accessors known ahead of time, if that fails we consider the cases of
116
    // metadata, either directly in the Node message, or nested in the user_agent_build_version.
117
47
    auto it = nodeParamCbs().find(ncp);
118
47
    if (it != nodeParamCbs().end()) {
119
45
      mutable_params["xds.node." + ncp] = (it->second)(node);
120
45
    } else if (ncp == "metadata") {
121
1
      mergeMetadataJson(mutable_params, node.metadata(), "xds.node.metadata.");
122
1
    } else if (ncp == "user_agent_build_version.metadata") {
123
1
      mergeMetadataJson(mutable_params, node.user_agent_build_version().metadata(),
124
1
                        "xds.node.user_agent_build_version.metadata.");
125
1
    }
126
47
  }
127
10727
  return context_params;
128
10727
}
129

            
130
} // namespace Config
131
} // namespace Envoy