1
#pragma once
2

            
3
#include "envoy/common/pure.h"
4
#include "envoy/config/cluster/v3/cluster.pb.h"
5
#include "envoy/upstream/cluster_manager.h"
6

            
7
#include "source/common/common/cleanup.h"
8
#include "source/common/init/target_impl.h"
9

            
10
namespace Envoy {
11
namespace Extensions {
12
namespace Common {
13
namespace Aws {
14

            
15
class AwsManagedClusterUpdateCallbacks {
16
public:
17
205
  virtual ~AwsManagedClusterUpdateCallbacks() = default;
18

            
19
  virtual void onClusterAddOrUpdate() PURE;
20
};
21

            
22
class AwsManagedClusterUpdateCallbacksHandle
23
    : public RaiiListElement<AwsManagedClusterUpdateCallbacks*> {
24
public:
25
  AwsManagedClusterUpdateCallbacksHandle(Server::Configuration::ServerFactoryContext& context,
26
                                         AwsManagedClusterUpdateCallbacks& cb,
27
                                         std::list<AwsManagedClusterUpdateCallbacks*>& parent)
28
106
      : RaiiListElement<AwsManagedClusterUpdateCallbacks*>(parent, &cb), context_(context) {}
29

            
30
public:
31
  Server::Configuration::ServerFactoryContext& context_;
32
};
33
using AwsManagedClusterUpdateCallbacksHandlePtr =
34
    std::unique_ptr<AwsManagedClusterUpdateCallbacksHandle>;
35

            
36
class AwsClusterManager {
37

            
38
public:
39
209
  virtual ~AwsClusterManager() = default;
40

            
41
  virtual absl::Status
42
  addManagedCluster(absl::string_view cluster_name,
43
                    const envoy::config::cluster::v3::Cluster::DiscoveryType cluster_type,
44
                    absl::string_view uri) PURE;
45

            
46
  virtual absl::StatusOr<AwsManagedClusterUpdateCallbacksHandlePtr>
47
  addManagedClusterUpdateCallbacks(absl::string_view cluster_name,
48
                                   AwsManagedClusterUpdateCallbacks& cb) PURE;
49
  virtual absl::StatusOr<std::string> getUriFromClusterName(absl::string_view cluster_name) PURE;
50

            
51
private:
52
  virtual void createQueuedClusters() PURE;
53
};
54

            
55
/**
56
 * Manages clusters for any number of credentials provider instances
57
 *
58
 * Credentials providers in async mode require clusters to be created so that they can use the async
59
 * http client to retrieve credentials. The aws cluster manager is responsible for creating these
60
 * clusters, and notifying a credential provider when a cluster comes on line so they can begin
61
 * retrieving credentials.
62
 *
63
 * - For InstanceProfileCredentialsProvider, a cluster is created with the uri of the instance
64
 * metadata service. Only one cluster is required for any number of instantiations of the aws
65
 * request signing extension.
66
 *
67
 * - For ContainerCredentialsProvider (including ECS and EKS), a cluster is created with the uri of
68
 * the container agent. Only one cluster is required for any number of instantiations of the aws
69
 * request signing extension.
70
 *
71
 * - For WebIdentityCredentialsProvider, a cluster is required for the STS service in any region
72
 * configured. There may be many WebIdentityCredentialsProvider instances instances configured, each
73
 * with their own region, or their own role ARN or role session name. The aws cluster manager will
74
 * maintain only a single cluster per region, and notify all relevant WebIdentityCredentialsProvider
75
 * instances when their cluster is ready.
76
 *
77
 * - For IAMRolesAnywhere, this behaves similarly to WebIdentityCredentialsProvider, where there may
78
 * be many instantiations of the credential provider for different roles, regions and profiles. The
79
 * aws cluster manager will dedupe these clusters as required.
80
 */
81
class AwsClusterManagerImpl : public AwsClusterManager,
82
                              public Envoy::Singleton::Instance,
83
                              public Upstream::ClusterUpdateCallbacks,
84
                              public Logger::Loggable<Logger::Id::aws> {
85
  // Friend class for testing callbacks
86
  friend class AwsClusterManagerFriend;
87

            
88
public:
89
  AwsClusterManagerImpl(Server::Configuration::ServerFactoryContext& context);
90
111
  ~AwsClusterManagerImpl() override {
91
111
    if (cm_handle_) {
92
      // We exit last due to being pinned, so we must call cancel on the callbacks handle as it will
93
      // already be invalid by this time
94
27
      auto* handle = dynamic_cast<RaiiListElement<ClusterUpdateCallbacks*>*>(cm_handle_.get());
95
27
      handle->cancel();
96
27
    }
97
111
  };
98

            
99
  /**
100
   * Add a managed cluster to the aws cluster manager
101
   * @return absl::Status based on whether the cluster could be added to the cluster manager
102
   */
103

            
104
  absl::Status
105
  addManagedCluster(absl::string_view cluster_name,
106
                    const envoy::config::cluster::v3::Cluster::DiscoveryType cluster_type,
107
                    absl::string_view uri) override;
108

            
109
  /**
110
   * Add a callback to be signaled when a managed cluster comes online. This is used to kick off
111
   * credential refresh
112
   * @return RAII handle for the callback
113
   */
114

            
115
  absl::StatusOr<AwsManagedClusterUpdateCallbacksHandlePtr>
116
  addManagedClusterUpdateCallbacks(absl::string_view cluster_name,
117
                                   AwsManagedClusterUpdateCallbacks& cb) override;
118
  absl::StatusOr<std::string> getUriFromClusterName(absl::string_view cluster_name) override;
119

            
120
private:
121
  // Callbacks for cluster manager
122
  void onClusterAddOrUpdate(absl::string_view, Upstream::ThreadLocalClusterCommand&) override;
123
  void onClusterRemoval(const std::string&) override;
124

            
125
  /**
126
   * Create all queued clusters, if we were unable to create them in real time due to envoy cluster
127
   * manager initialization
128
   */
129

            
130
  void createQueuedClusters() override;
131
  struct CredentialsProviderCluster {
132
    CredentialsProviderCluster(envoy::config::cluster::v3::Cluster::DiscoveryType cluster_type,
133
                               std::string uri)
134
105
        : uri_(uri), cluster_type_(cluster_type) {};
135

            
136
    std::string uri_;
137
    envoy::config::cluster::v3::Cluster::DiscoveryType cluster_type_;
138
    // Atomic flag for cluster recreate
139
    std::atomic<bool> is_creating_ = false;
140
    std::list<AwsManagedClusterUpdateCallbacks*> update_callbacks_;
141
  };
142

            
143
  absl::flat_hash_map<std::string, std::unique_ptr<CredentialsProviderCluster>> managed_clusters_;
144
  std::atomic<bool> queue_clusters_ = true;
145
  Server::Configuration::ServerFactoryContext& context_;
146
  Upstream::ClusterUpdateCallbacksHandlePtr cm_handle_;
147
  std::unique_ptr<Init::TargetImpl> init_target_;
148
};
149

            
150
using AwsClusterManagerImplPtr = std::shared_ptr<AwsClusterManagerImpl>;
151
using AwsClusterManagerPtr = std::shared_ptr<AwsClusterManager>;
152

            
153
} // namespace Aws
154
} // namespace Common
155
} // namespace Extensions
156
} // namespace Envoy