1
#pragma once
2

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

            
6
#include "envoy/config/core/v3/base.pb.h"
7
#include "envoy/config/typed_metadata.h"
8
#include "envoy/event/dispatcher.h"
9
#include "envoy/registry/registry.h"
10
#include "envoy/singleton/manager.h"
11
#include "envoy/type/metadata/v3/metadata.pb.h"
12

            
13
#include "source/common/protobuf/protobuf.h"
14
#include "source/common/shared_pool/shared_pool.h"
15

            
16
#include "absl/container/node_hash_map.h"
17

            
18
namespace Envoy {
19
namespace Config {
20

            
21
using ConstMetadataSharedPoolSharedPtr =
22
    std::shared_ptr<SharedPool::ObjectSharedPool<const envoy::config::core::v3::Metadata,
23
                                                 MessageUtil, MessageUtil>>;
24

            
25
/**
26
 * MetadataKey presents the key name and path to retrieve value from metadata.
27
 */
28
struct MetadataKey {
29
  std::string key_;
30
  std::vector<std::string> path_;
31

            
32
  MetadataKey(const envoy::type::metadata::v3::MetadataKey& metadata_key);
33
};
34

            
35
/**
36
 * Config metadata helpers.
37
 */
38
class Metadata {
39
public:
40
  /**
41
   * Lookup value by a multi-key path in a Struct. If path is empty will return the entire struct.
42
   * @param struct_value reference.
43
   * @param path multi-key path.
44
   * @return const Protobuf::Value& value if found, empty if not found.
45
   */
46
  static const Protobuf::Value& structValue(const Protobuf::Struct& struct_value,
47
                                            const std::vector<std::string>& path);
48

            
49
  /**
50
   * Lookup value of a key for a given filter in Metadata.
51
   * @param metadata reference.
52
   * @param filter name.
53
   * @param key for filter metadata.
54
   * @return const Protobuf::Value& value if found, empty if not found.
55
   */
56
  static const Protobuf::Value& metadataValue(const envoy::config::core::v3::Metadata* metadata,
57
                                              const std::string& filter, const std::string& key);
58
  /**
59
   * Lookup value by a multi-key path for a given filter in Metadata. If path is empty
60
   * will return the empty struct.
61
   * @param metadata reference.
62
   * @param filter name.
63
   * @param path multi-key path.
64
   * @return const Protobuf::Value& value if found, empty if not found.
65
   */
66
  static const Protobuf::Value& metadataValue(const envoy::config::core::v3::Metadata* metadata,
67
                                              const std::string& filter,
68
                                              const std::vector<std::string>& path);
69
  /**
70
   * Lookup the value by a metadata key from a Metadata.
71
   * @param metadata reference.
72
   * @param metadata_key with key name and path to retrieve the value.
73
   * @return const Protobuf::Value& value if found, empty if not found.
74
   */
75
  static const Protobuf::Value& metadataValue(const envoy::config::core::v3::Metadata* metadata,
76
                                              const MetadataKey& metadata_key);
77

            
78
  /**
79
   * Obtain mutable reference to metadata value for a given filter and key.
80
   * @param metadata reference.
81
   * @param filter name.
82
   * @param key for filter metadata.
83
   * @return Protobuf::Value&. A Value message is created if not found.
84
   */
85
  static Protobuf::Value& mutableMetadataValue(envoy::config::core::v3::Metadata& metadata,
86
                                               const std::string& filter, const std::string& key);
87

            
88
  using LabelSet = std::vector<std::pair<std::string, Protobuf::Value>>;
89

            
90
  /**
91
   * Returns whether a set of the labels match a particular host's metadata.
92
   * @param label_set the target label key/value pair set.
93
   * @param host_metadata a given host's metadata.
94
   * @param filter_key identifies the entry in the metadata entry for the match.
95
   * @param list_as_any if the metadata value entry is a list, and any one of
96
   * the element equals to the input label_set, it's considered as match.
97
   */
98
  static bool metadataLabelMatch(const LabelSet& label_set,
99
                                 const envoy::config::core::v3::Metadata* host_metadata,
100
                                 const std::string& filter_key, bool list_as_any);
101
  /**
102
   * Returns an ObjectSharedPool to store const Metadata
103
   * @param manager used to create singleton
104
   * @param dispatcher the dispatcher object reference to the thread that created the
105
   * ObjectSharedPool
106
   */
107
  static ConstMetadataSharedPoolSharedPtr getConstMetadataSharedPool(Singleton::Manager& manager,
108
                                                                     Event::Dispatcher& dispatcher);
109
};
110

            
111
template <typename factoryClass> class TypedMetadataImpl : public TypedMetadata {
112
public:
113
  static_assert(std::is_base_of<Config::TypedMetadataFactory, factoryClass>::value,
114
                "Factory type must be inherited from Envoy::Config::TypedMetadataFactory.");
115
24813
  TypedMetadataImpl(const envoy::config::core::v3::Metadata& metadata) { populateFrom(metadata); }
116

            
117
53
  const TypedMetadata::Object* getData(const std::string& key) const override {
118
53
    const auto& it = data_.find(key);
119
53
    return it == data_.end() ? nullptr : it->second.get();
120
53
  }
121

            
122
protected:
123
  /* Attempt to run each of the registered factories for TypedMetadata, to
124
   * populate the data_ map.
125
   */
126
24813
  void populateFrom(const envoy::config::core::v3::Metadata& metadata) {
127
24813
    auto& data_by_key = metadata.filter_metadata();
128
24813
    auto& typed_data_by_key = metadata.typed_filter_metadata();
129
24813
    for (const auto& [factory_name, factory] :
130
24840
         Registry::FactoryRegistry<factoryClass>::factories()) {
131
108
      const auto& typed_meta_iter = typed_data_by_key.find(factory_name);
132
      // If the key exists in Any metadata, and parse() does not return nullptr,
133
      // populate data_.
134
108
      if (typed_meta_iter != typed_data_by_key.end()) {
135
30
        auto result = factory->parse(typed_meta_iter->second);
136
30
        if (result != nullptr) {
137
27
          data_[factory->name()] = std::move(result);
138
27
          continue;
139
27
        }
140
30
      }
141
      // Fall back cases to parsing Struct metadata and populate data_.
142
81
      const auto& meta_iter = data_by_key.find(factory_name);
143
81
      if (meta_iter != data_by_key.end()) {
144
28
        data_[factory->name()] = factory->parse(meta_iter->second);
145
28
      }
146
81
    }
147
24813
  }
148

            
149
  absl::node_hash_map<std::string, std::unique_ptr<const TypedMetadata::Object>> data_;
150
};
151

            
152
// MetadataPack is struct that contains both the proto and typed metadata.
153
template <class FactoryClass> struct MetadataPack {
154
  MetadataPack(const envoy::config::core::v3::Metadata& metadata)
155
12381
      : proto_metadata_(metadata), typed_metadata_(proto_metadata_) {}
156
11024
  MetadataPack() : proto_metadata_(), typed_metadata_(proto_metadata_) {}
157

            
158
  const envoy::config::core::v3::Metadata proto_metadata_;
159
  const TypedMetadataImpl<FactoryClass> typed_metadata_;
160
};
161

            
162
template <class FactoryClass> using MetadataPackPtr = std::unique_ptr<MetadataPack<FactoryClass>>;
163
template <class FactoryClass>
164
using MetadataPackSharedPtr = std::shared_ptr<MetadataPack<FactoryClass>>;
165

            
166
} // namespace Config
167
} // namespace Envoy