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

            
3
#include "envoy/config/core/v3/config_source.pb.h"
4
#include "envoy/config/xds_resources_delegate.h"
5

            
6
#include "source/common/config/custom_config_validators_impl.h"
7
#include "source/common/config/resource_name.h"
8
#include "source/common/config/type_to_endpoint.h"
9
#include "source/common/config/utility.h"
10
#include "source/common/config/xds_resource.h"
11
#include "source/common/http/utility.h"
12
#include "source/common/protobuf/protobuf.h"
13
#include "source/common/protobuf/utility.h"
14

            
15
namespace Envoy {
16
namespace Config {
17

            
18
SubscriptionFactoryImpl::SubscriptionFactoryImpl(
19
    const LocalInfo::LocalInfo& local_info, Event::Dispatcher& dispatcher,
20
    Upstream::ClusterManager& cm, ProtobufMessage::ValidationVisitor& validation_visitor,
21
    Api::Api& api, const Server::Instance& server,
22
    XdsResourcesDelegateOptRef xds_resources_delegate, XdsConfigTrackerOptRef xds_config_tracker)
23
10812
    : local_info_(local_info), dispatcher_(dispatcher), cm_(cm),
24
10812
      validation_visitor_(validation_visitor), api_(api), server_(server),
25
10812
      xds_resources_delegate_(xds_resources_delegate), xds_config_tracker_(xds_config_tracker) {}
26

            
27
absl::StatusOr<SubscriptionPtr> SubscriptionFactoryImpl::subscriptionFromConfigSource(
28
    const envoy::config::core::v3::ConfigSource& config, absl::string_view type_url,
29
    Stats::Scope& scope, SubscriptionCallbacks& callbacks,
30
12664
    OpaqueResourceDecoderSharedPtr resource_decoder, const SubscriptionOptions& options) {
31
12664
  RETURN_IF_NOT_OK(Config::Utility::checkLocalInfo(type_url, local_info_));
32

            
33
12664
  std::string subscription_type = "";
34
12664
  ConfigSubscriptionFactory::SubscriptionData data{local_info_,
35
12664
                                                   dispatcher_,
36
12664
                                                   cm_,
37
12664
                                                   validation_visitor_,
38
12664
                                                   api_,
39
12664
                                                   server_,
40
12664
                                                   xds_resources_delegate_,
41
12664
                                                   xds_config_tracker_,
42
12664
                                                   config,
43
12664
                                                   type_url,
44
12664
                                                   scope,
45
12664
                                                   callbacks,
46
12664
                                                   resource_decoder,
47
12664
                                                   options,
48
12664
                                                   absl::nullopt,
49
12664
                                                   Utility::generateStats(scope),
50
12664
                                                   cm_.adsMux()};
51

            
52
12664
  switch (config.config_source_specifier_case()) {
53
2
  case envoy::config::core::v3::ConfigSource::ConfigSourceSpecifierCase::kPath: {
54
2
    RETURN_IF_NOT_OK(Utility::checkFilesystemSubscriptionBackingPath(config.path(), api_));
55
1
    subscription_type = "envoy.config_subscription.filesystem";
56
1
    break;
57
2
  }
58
10190
  case envoy::config::core::v3::ConfigSource::ConfigSourceSpecifierCase::kPathConfigSource: {
59
10190
    RETURN_IF_NOT_OK(
60
10190
        Utility::checkFilesystemSubscriptionBackingPath(config.path_config_source().path(), api_));
61
10190
    subscription_type = "envoy.config_subscription.filesystem";
62
10190
    break;
63
10190
  }
64
1162
  case envoy::config::core::v3::ConfigSource::ConfigSourceSpecifierCase::kApiConfigSource: {
65
1162
    const envoy::config::core::v3::ApiConfigSource& api_config_source = config.api_config_source();
66
1162
    if (type_url !=
67
1162
        Envoy::Config::getTypeUrl<envoy::extensions::transport_sockets::tls::v3::Secret>()) {
68
1053
      RETURN_IF_NOT_OK(Utility::checkApiConfigSourceSubscriptionBackingCluster(
69
1053
          cm_.primaryClusters(), api_config_source));
70
1042
    }
71
1151
    RETURN_IF_NOT_OK(Utility::checkTransportVersion(api_config_source));
72
1146
    switch (api_config_source.api_type()) {
73
      PANIC_ON_PROTO_ENUM_SENTINEL_VALUES;
74
1
    case envoy::config::core::v3::ApiConfigSource::AGGREGATED_GRPC:
75
1
      return absl::InvalidArgumentError("Unsupported config source AGGREGATED_GRPC");
76
1
    case envoy::config::core::v3::ApiConfigSource::AGGREGATED_DELTA_GRPC:
77
1
      return absl::InvalidArgumentError("Unsupported config source AGGREGATED_DELTA_GRPC");
78
    case envoy::config::core::v3::ApiConfigSource::DEPRECATED_AND_UNAVAILABLE_DO_NOT_USE:
79
      return absl::InvalidArgumentError(
80
          "REST_LEGACY no longer a supported ApiConfigSource. "
81
          "Please specify an explicit supported api_type in the following config:\n" +
82
          config.DebugString());
83
4
    case envoy::config::core::v3::ApiConfigSource::REST:
84
4
      subscription_type = "envoy.config_subscription.rest";
85
4
      break;
86
899
    case envoy::config::core::v3::ApiConfigSource::GRPC:
87
899
      subscription_type = "envoy.config_subscription.grpc";
88
899
      break;
89
241
    case envoy::config::core::v3::ApiConfigSource::DELTA_GRPC:
90
241
      subscription_type = "envoy.config_subscription.delta_grpc";
91
241
      break;
92
1146
    }
93
1144
    if (subscription_type.empty()) {
94
      return absl::InvalidArgumentError("Invalid API config source API type");
95
    }
96
1144
    break;
97
1144
  }
98
2344
  case envoy::config::core::v3::ConfigSource::ConfigSourceSpecifierCase::kAds: {
99
1308
    subscription_type = "envoy.config_subscription.ads";
100
1308
    break;
101
1144
  }
102
2
  default:
103
2
    return absl::InvalidArgumentError(
104
2
        "Missing config source specifier in envoy::config::core::v3::ConfigSource");
105
12664
  }
106
12643
  ConfigSubscriptionFactory* factory =
107
12643
      Registry::FactoryRegistry<ConfigSubscriptionFactory>::getFactory(subscription_type);
108
12643
  if (factory == nullptr) {
109
    return absl::InvalidArgumentError(fmt::format(
110
        "Didn't find a registered config subscription factory implementation for name: '{}'",
111
        subscription_type));
112
  }
113
12643
  return factory->create(data);
114
12643
}
115

            
116
absl::StatusOr<SubscriptionPtr> createFromFactory(ConfigSubscriptionFactory::SubscriptionData& data,
117
76
                                                  absl::string_view subscription_type) {
118
76
  ConfigSubscriptionFactory* factory =
119
76
      Registry::FactoryRegistry<ConfigSubscriptionFactory>::getFactory(subscription_type);
120
76
  if (factory == nullptr) {
121
    return absl::InvalidArgumentError(fmt::format(
122
        "Didn't find a registered config subscription factory implementation for name: '{}'",
123
        subscription_type));
124
  }
125
76
  return factory->create(data);
126
76
}
127

            
128
absl::StatusOr<SubscriptionPtr> SubscriptionFactoryImpl::subscriptionOverAdsGrpcMux(
129
    GrpcMuxSharedPtr& ads_grpc_mux, const envoy::config::core::v3::ConfigSource& config,
130
    absl::string_view type_url, Stats::Scope& scope, SubscriptionCallbacks& callbacks,
131
77
    OpaqueResourceDecoderSharedPtr resource_decoder, const SubscriptionOptions& options) {
132
77
  RETURN_IF_NOT_OK(Config::Utility::checkLocalInfo(type_url, local_info_));
133

            
134
77
  ConfigSubscriptionFactory::SubscriptionData data{local_info_,
135
77
                                                   dispatcher_,
136
77
                                                   cm_,
137
77
                                                   validation_visitor_,
138
77
                                                   api_,
139
77
                                                   server_,
140
77
                                                   xds_resources_delegate_,
141
77
                                                   xds_config_tracker_,
142
77
                                                   config,
143
77
                                                   type_url,
144
77
                                                   scope,
145
77
                                                   callbacks,
146
77
                                                   resource_decoder,
147
77
                                                   options,
148
77
                                                   absl::nullopt,
149
77
                                                   Utility::generateStats(scope),
150
77
                                                   ads_grpc_mux};
151
77
  static constexpr absl::string_view subscription_type = "envoy.config_subscription.ads";
152
77
  ConfigSubscriptionFactory* factory =
153
77
      Registry::FactoryRegistry<ConfigSubscriptionFactory>::getFactory(subscription_type);
154
77
  if (factory == nullptr) {
155
    return absl::InvalidArgumentError(fmt::format(
156
        "Didn't find a registered config subscription factory implementation for name: '{}'",
157
        subscription_type));
158
  }
159
77
  return factory->create(data);
160
77
}
161

            
162
absl::StatusOr<SubscriptionPtr> SubscriptionFactoryImpl::collectionSubscriptionFromUrl(
163
    const xds::core::v3::ResourceLocator& collection_locator,
164
    const envoy::config::core::v3::ConfigSource& config, absl::string_view resource_type,
165
    Stats::Scope& scope, SubscriptionCallbacks& callbacks,
166
83
    OpaqueResourceDecoderSharedPtr resource_decoder) {
167
83
  SubscriptionOptions options;
168
83
  envoy::config::core::v3::ConfigSource factory_config = config;
169
83
  ConfigSubscriptionFactory::SubscriptionData data{local_info_,
170
83
                                                   dispatcher_,
171
83
                                                   cm_,
172
83
                                                   validation_visitor_,
173
83
                                                   api_,
174
83
                                                   server_,
175
83
                                                   xds_resources_delegate_,
176
83
                                                   xds_config_tracker_,
177
83
                                                   factory_config,
178
83
                                                   "",
179
83
                                                   scope,
180
83
                                                   callbacks,
181
83
                                                   resource_decoder,
182
83
                                                   options,
183
83
                                                   {collection_locator},
184
83
                                                   Utility::generateStats(scope),
185
83
                                                   cm_.adsMux()};
186
83
  switch (collection_locator.scheme()) {
187
3
  case xds::core::v3::ResourceLocator::FILE: {
188
3
    const std::string path = Http::Utility::localPathFromFilePath(collection_locator.id());
189
3
    RETURN_IF_NOT_OK(Utility::checkFilesystemSubscriptionBackingPath(path, api_));
190
2
    factory_config.set_path(path);
191
2
    auto ptr_or_error = createFromFactory(data, "envoy.config_subscription.filesystem_collection");
192
2
    RETURN_IF_NOT_OK(ptr_or_error.status());
193
2
    return std::move(ptr_or_error.value());
194
2
  }
195
80
  case xds::core::v3::ResourceLocator::XDSTP: {
196
80
    if (resource_type != collection_locator.resource_type()) {
197
2
      return absl::InvalidArgumentError(
198
2
          fmt::format("xdstp:// type does not match {} in {}", resource_type,
199
2
                      Config::XdsResourceIdentifier::encodeUrl(collection_locator)));
200
2
    }
201
78
    switch (config.config_source_specifier_case()) {
202
62
    case envoy::config::core::v3::ConfigSource::ConfigSourceSpecifierCase::kApiConfigSource: {
203
62
      const envoy::config::core::v3::ApiConfigSource& api_config_source =
204
62
          config.api_config_source();
205
62
      RETURN_IF_NOT_OK(Utility::checkApiConfigSourceSubscriptionBackingCluster(
206
62
          cm_.primaryClusters(), api_config_source));
207
      // All Envoy collections currently are xDS resource graph roots and require node context
208
      // parameters.
209
62
      options.add_xdstp_node_context_params_ = true;
210
62
      switch (api_config_source.api_type()) {
211
28
      case envoy::config::core::v3::ApiConfigSource::DELTA_GRPC: {
212
28
        std::string type_url = TypeUtil::descriptorFullNameToTypeUrl(resource_type);
213
28
        data.type_url_ = type_url;
214
28
        auto ptr_or_error =
215
28
            createFromFactory(data, "envoy.config_subscription.delta_grpc_collection");
216
28
        RETURN_IF_NOT_OK(ptr_or_error.status());
217
28
        return std::move(ptr_or_error.value());
218
28
      }
219
6
      case envoy::config::core::v3::ApiConfigSource::AGGREGATED_GRPC:
220
6
        FALLTHRU;
221
32
      case envoy::config::core::v3::ApiConfigSource::AGGREGATED_DELTA_GRPC: {
222
32
        auto ptr_or_error =
223
32
            createFromFactory(data, "envoy.config_subscription.aggregated_grpc_collection");
224
32
        RETURN_IF_NOT_OK(ptr_or_error.status());
225
32
        return std::move(ptr_or_error.value());
226
32
      }
227
2
      default:
228
2
        return absl::InvalidArgumentError(fmt::format("Unknown xdstp:// transport API type in {}",
229
2
                                                      api_config_source.DebugString()));
230
62
      }
231
62
    }
232
14
    case envoy::config::core::v3::ConfigSource::ConfigSourceSpecifierCase::kAds: {
233
      // TODO(adisuissa): verify that the ADS is set up in delta-xDS mode.
234
      // All Envoy collections currently are xDS resource graph roots and require node context
235
      // parameters.
236
14
      options.add_xdstp_node_context_params_ = true;
237
14
      auto ptr_or_error = createFromFactory(data, "envoy.config_subscription.ads_collection");
238
14
      RETURN_IF_NOT_OK(ptr_or_error.status());
239
14
      return std::move(ptr_or_error.value());
240
14
    }
241
2
    default:
242
2
      return absl::InvalidArgumentError(
243
2
          "Missing or not supported config source specifier in "
244
2
          "envoy::config::core::v3::ConfigSource for a collection. Only ADS and "
245
2
          "gRPC in delta-xDS mode are supported.");
246
78
    }
247
78
  }
248
  default:
249
    // TODO(htuch): Implement HTTP semantics for collection ResourceLocators.
250
    return absl::InvalidArgumentError("Unsupported code path");
251
83
  }
252
83
}
253

            
254
} // namespace Config
255
} // namespace Envoy