AOSharedServiceLibrary
network_app_profile.h
1 /*
2 MIT License Block
3 
4 Copyright (c) 2016 Alex Barry
5 
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12 
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
15 
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 THE SOFTWARE.
23 */
24 
25 #include <string>
26 #include <unordered_map>
27 #include <iostream>
28 #include <vector>
29 #include <atomic>
30 
31 #include "aossl/consul/include/consul_interface.h"
32 #include "aossl/consul/include/factory_consul.h"
33 #include "aossl/profile/include/tiered_app_profile.h"
34 
35 #ifndef AOSSL_PROFILE_INCLUDE_NETWORK_APP_PROFILE_H_
36 #define AOSSL_PROFILE_INCLUDE_NETWORK_APP_PROFILE_H_
37 
38 namespace AOSSL {
39 
41 
45  ConsulComponentFactory consul_factory;
46  std::atomic<int> last_returned_index{-1};
47  // Pull a particular service out of a document listing available services,
48  // and populate the service into the return_service. If filter_metadata
49  // is true, then we need to return a service with a metadata entry matching
50  // the specified key and value.
51  inline void find_service_in_doc(ServiceInterface *return_service, \
52  rapidjson::Document& doc, std::string service_identifier, \
53  std::string metadata_key, std::string metadata_value, bool filter_metadata) {
54  if (doc.IsObject()) {
55  // We now have a parsed JSON Object which contains
56  // a list of known services to our local Consul Agent
57  bool service_found = false;
58  bool loop_ended_early = false;
59  int discovery_index = 0;
60 
61  // Iterate over the objects in the document
62  for (auto& itr : doc.GetObject()) {
63 
64  // If we found a service and it's next up in the round robin,
65  // then break out of the loop and return it
66  if (service_found && discovery_index > last_returned_index) {
67  last_returned_index = discovery_index - 1;
68  loop_ended_early = true;
69  break;
70  }
71 
72  std::vector<std::string> current_obj_tags;
73 
74  // Is the service name equal to our target?
75  rapidjson::Value::ConstMemberIterator service_itr = \
76  itr.value.FindMember("Service");
77  if (service_itr != itr.value.MemberEnd()) {
78  if (!(service_itr->value.IsNull())) {
79  std::string service_name(service_itr->value.GetString());
80  if ((service_name == service_identifier)) {
81 
82  // Determine if the matched service has the necessary tags
83  rapidjson::Value::ConstMemberIterator tag_itr = \
84  itr.value.FindMember("Tags");
85  bool metadata_matches = false;
86  if (tag_itr != itr.value.MemberEnd()) {
87  if (tag_itr->value.IsArray()) {
88  for (auto& tagv : tag_itr->value.GetArray()) {
89  if (tagv.IsString()) {
90  if (tagv.GetString() == metadata_key + std::string("=") + metadata_value) {
91  metadata_matches = true;
92  }
93  }
94  }
95  }
96  }
97 
98  // Set the return service information
99  if (!filter_metadata || metadata_matches) {
100  service_found = true;
101  for (auto& tag : current_obj_tags) {
102  return_service->add_tag(tag);
103  }
104  rapidjson::Value::ConstMemberIterator address_itr = \
105  itr.value.FindMember("Address");
106  rapidjson::Value::ConstMemberIterator port_itr = \
107  itr.value.FindMember("Port");
108  rapidjson::Value::ConstMemberIterator id_itr = \
109  itr.value.FindMember("ID");
110  return_service->set_address(address_itr->value.GetString());
111  return_service->set_port(std::to_string(port_itr->value.GetInt()));
112  return_service->set_id(id_itr->value.GetString());
113  return_service->set_name(service_identifier);
114  }
115  }
116  }
117  }
118  discovery_index++;
119  }
120  if (!loop_ended_early) {
121  last_returned_index = -1;
122  }
123  if (!service_found) {
124  if (return_service) delete return_service;
125  throw std::invalid_argument("Unable to find Service Instance");
126  }
127  }
128  }
129  public:
130  NetworkApplicationProfile(int argc, char* argv[]) : \
131  TieredApplicationProfile(argc, argv) {}
132 
133  NetworkApplicationProfile(int argc, char* argv[], std::string app_name, \
134  std::string prof_name) : \
135  TieredApplicationProfile(argc, argv, app_name, prof_name) {}
136 
137  explicit NetworkApplicationProfile(const std::vector<std::string>& args) : \
138  TieredApplicationProfile(args) {}
139 
140  NetworkApplicationProfile(const std::vector<std::string>& args, \
141  std::string app_name, std::string prof_name) : \
142  TieredApplicationProfile(args, app_name, prof_name) {}
143 
144  NetworkApplicationProfile(std::string app_name, std::string prof_name) : \
145  TieredApplicationProfile(app_name, prof_name) {}
147 
148  // TO-DO: Add option to get service by cluster name
149  inline ServiceInterface* get_service_by_metadata(std::string service_identifier, \
150  std::string metadata_key, std::string metadata_value) {
151  ServiceInterface* return_service = consul_factory.get_service_interface();
152  AOSSL::StringBuffer *services_buf = \
153  ApplicationProfile::get_consul()->services();
154 
155  // Parse the Services List that we get back from the agent
156  rapidjson::Document doc;
157  doc.Parse<rapidjson::kParseStopWhenDoneFlag>(services_buf->val.c_str());
158  if (doc.HasParseError()) {
159  delete return_service;
160  delete services_buf;
161  throw std::invalid_argument(GetParseError_En(doc.GetParseError()));
162  } else {
163  find_service_in_doc(return_service, doc, service_identifier, metadata_key, metadata_value, true);
164  }
165  delete services_buf;
166  return return_service;
167  }
168 
169  inline ServiceInterface* get_service(std::string service_identifier) {
170  ServiceInterface* return_service = consul_factory.get_service_interface();
171  AOSSL::StringBuffer *services_buf = \
172  ApplicationProfile::get_consul()->services();
173 
174  // Parse the Services List that we get back from the agent
175  rapidjson::Document doc;
176  doc.Parse<rapidjson::kParseStopWhenDoneFlag>(services_buf->val.c_str());
177  if (doc.HasParseError()) {
178  delete return_service;
179  delete services_buf;
180  throw std::invalid_argument(GetParseError_En(doc.GetParseError()));
181  } else {
182  find_service_in_doc(return_service, doc, service_identifier, std::string(""), std::string(""), false);
183  }
184  delete services_buf;
185  return return_service;
186  }
187 };
188 
189 } // namespace AOSSL
190 
191 #endif // AOSSL_PROFILE_INCLUDE_NETWORK_APP_PROFILE_H_
A Structure for holding a single value.
Definition: buffers.h:42
std::string val
Value stored.
Definition: buffers.h:44
Biased Application Session which implements tiered configuration.
Definition: tiered_app_profile.h:63
A Service class which can be registered with Consul for each app instance.
Definition: consul_interface.h:57
virtual void add_tag(std::string new_tag)=0
Add a tag.
The Consul Service Component Factory.
Definition: factory_consul.h:44
virtual void set_id(std::string new_id)=0
Set the Service ID.
virtual void set_name(std::string new_name)=0
Set the Service Name.
virtual void set_address(std::string new_address)=0
Set the Service Address.
Extends the TieredApplicationProfile, adding service discovery logic.
Definition: network_app_profile.h:44
virtual void set_port(std::string new_port)=0
Set the Service Port.
Definition: cli.h:35