partitions.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/common/array_list.h>
  6. #include <aws/common/byte_buf.h>
  7. #include <aws/common/hash_table.h>
  8. #include <aws/common/json.h>
  9. #include <aws/common/ref_count.h>
  10. #include <aws/common/string.h>
  11. #include <aws/sdkutils/partitions.h>
  12. #include <aws/sdkutils/private/endpoints_types_impl.h>
  13. #include <aws/sdkutils/private/endpoints_util.h>
  14. static struct aws_byte_cursor s_supported_version = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("1.0");
  15. struct aws_byte_cursor aws_partitions_get_supported_version(void) {
  16. return s_supported_version;
  17. }
  18. static void s_partitions_config_destroy(void *data) {
  19. if (data == NULL) {
  20. return;
  21. }
  22. struct aws_partitions_config *partitions = data;
  23. aws_json_value_destroy(partitions->json_root);
  24. aws_string_destroy(partitions->version);
  25. aws_hash_table_clean_up(&partitions->region_to_partition_info);
  26. aws_mem_release(partitions->allocator, partitions);
  27. }
  28. struct region_merge_wrapper {
  29. struct aws_json_value *outputs_node;
  30. struct aws_json_value *merge_node;
  31. };
  32. static int s_on_region_merge(
  33. const struct aws_byte_cursor *key,
  34. const struct aws_json_value *value,
  35. bool *out_should_continue,
  36. void *user_data) {
  37. (void)out_should_continue;
  38. struct region_merge_wrapper *merge = user_data;
  39. /*
  40. * Note: latest partitions file includes description on every region.
  41. * This results in a separate record created for every region, since any
  42. * overrides on region create a new record that is a merge of partition
  43. * default and override.
  44. * Description is not used by endpoints rule engine, hence lets ignore it
  45. * during merge for now to avoid creating numerous records that all have the
  46. * same data.
  47. * This decision can be revisited later if we decide to extend partitions
  48. * parsing for any other use cases.
  49. */
  50. if (aws_byte_cursor_eq_c_str(key, "description")) {
  51. return AWS_OP_SUCCESS;
  52. }
  53. if (merge->merge_node == NULL) {
  54. merge->merge_node = aws_json_value_duplicate(merge->outputs_node);
  55. }
  56. /*
  57. * Note: Its valid for region to add new field to default partition outputs
  58. * instead of overriding existing one. So only delete previous value if it exists.
  59. */
  60. if (aws_json_value_has_key(merge->merge_node, *key) && aws_json_value_remove_from_object(merge->merge_node, *key)) {
  61. AWS_LOGF_ERROR(AWS_LS_SDKUTILS_PARTITIONS_PARSING, "Failed to remove previous partition value.");
  62. return aws_raise_error(AWS_ERROR_SDKUTILS_PARTITIONS_PARSE_FAILED);
  63. }
  64. if (aws_json_value_add_to_object(merge->merge_node, *key, aws_json_value_duplicate(value))) {
  65. AWS_LOGF_ERROR(AWS_LS_SDKUTILS_PARTITIONS_PARSING, "Failed to overwrite partition data.");
  66. return aws_raise_error(AWS_ERROR_SDKUTILS_PARTITIONS_PARSE_FAILED);
  67. }
  68. return AWS_OP_SUCCESS;
  69. }
  70. struct partition_parse_wrapper {
  71. struct aws_partitions_config *partitions;
  72. struct aws_json_value *outputs_node;
  73. struct aws_string *outputs_str;
  74. };
  75. static int s_on_region_element(
  76. const struct aws_byte_cursor *key,
  77. const struct aws_json_value *value,
  78. bool *out_should_continue,
  79. void *user_data) {
  80. (void)out_should_continue;
  81. struct aws_partition_info *partition_info = NULL;
  82. struct partition_parse_wrapper *wrapper = user_data;
  83. struct region_merge_wrapper merge = {
  84. .outputs_node = wrapper->outputs_node,
  85. .merge_node = NULL,
  86. };
  87. if (aws_json_const_iterate_object(value, s_on_region_merge, &merge)) {
  88. AWS_LOGF_ERROR(AWS_LS_SDKUTILS_PARTITIONS_PARSING, "Failed to parse partitions.");
  89. return aws_raise_error(AWS_ERROR_SDKUTILS_PARTITIONS_PARSE_FAILED);
  90. }
  91. if (merge.merge_node != NULL) {
  92. partition_info = aws_partition_info_new(wrapper->partitions->allocator, *key);
  93. partition_info->info = aws_string_new_from_json(wrapper->partitions->allocator, merge.merge_node);
  94. aws_json_value_destroy(merge.merge_node);
  95. } else {
  96. partition_info = aws_partition_info_new(wrapper->partitions->allocator, *key);
  97. partition_info->info = wrapper->outputs_str;
  98. partition_info->is_copy = true;
  99. }
  100. if (aws_hash_table_put(
  101. &wrapper->partitions->region_to_partition_info, &partition_info->name, partition_info, NULL)) {
  102. AWS_LOGF_ERROR(AWS_LS_SDKUTILS_PARTITIONS_PARSING, "Failed to add partition info.");
  103. goto on_error;
  104. }
  105. return AWS_OP_SUCCESS;
  106. on_error:
  107. if (partition_info != NULL) {
  108. aws_partition_info_destroy(partition_info);
  109. }
  110. return aws_raise_error(AWS_ERROR_SDKUTILS_PARTITIONS_PARSE_FAILED);
  111. }
  112. static int s_on_partition_element(
  113. size_t idx,
  114. const struct aws_json_value *partition_node,
  115. bool *out_should_continue,
  116. void *user_data) {
  117. (void)out_should_continue;
  118. (void)idx;
  119. struct aws_partitions_config *partitions = user_data;
  120. struct aws_byte_cursor id_cur;
  121. struct aws_json_value *id_node = aws_json_value_get_from_object(partition_node, aws_byte_cursor_from_c_str("id"));
  122. if (id_node == NULL || aws_json_value_get_string(id_node, &id_cur)) {
  123. AWS_LOGF_ERROR(AWS_LS_SDKUTILS_PARTITIONS_PARSING, "Failed to extract id of partition.");
  124. goto on_error;
  125. }
  126. struct aws_json_value *outputs_node =
  127. aws_json_value_get_from_object(partition_node, aws_byte_cursor_from_c_str("outputs"));
  128. if (outputs_node == NULL) {
  129. AWS_LOGF_ERROR(AWS_LS_SDKUTILS_PARTITIONS_PARSING, "Failed to extract outputs of partition.");
  130. goto on_error;
  131. }
  132. struct aws_partition_info *partition_info = aws_partition_info_new(partitions->allocator, id_cur);
  133. partition_info->info = aws_string_new_from_json(partitions->allocator, outputs_node);
  134. if (partition_info->info == NULL) {
  135. AWS_LOGF_ERROR(AWS_LS_SDKUTILS_PARTITIONS_PARSING, "Failed to add partition info.");
  136. goto on_error;
  137. }
  138. if (aws_hash_table_put(&partitions->region_to_partition_info, &partition_info->name, partition_info, NULL)) {
  139. AWS_LOGF_ERROR(AWS_LS_SDKUTILS_PARTITIONS_PARSING, "Failed to add partition info.");
  140. goto on_error;
  141. }
  142. struct partition_parse_wrapper wrapper = {
  143. .outputs_node = outputs_node, .outputs_str = partition_info->info, .partitions = partitions};
  144. struct aws_json_value *regions_node =
  145. aws_json_value_get_from_object(partition_node, aws_byte_cursor_from_c_str("regions"));
  146. if (regions_node != NULL && aws_json_const_iterate_object(regions_node, s_on_region_element, &wrapper)) {
  147. AWS_LOGF_ERROR(AWS_LS_SDKUTILS_PARTITIONS_PARSING, "Failed to parse regions.");
  148. goto on_error;
  149. }
  150. return AWS_OP_SUCCESS;
  151. on_error:
  152. return aws_raise_error(AWS_ERROR_SDKUTILS_PARTITIONS_PARSE_FAILED);
  153. }
  154. static int s_init_partitions_config_from_json(
  155. struct aws_allocator *allocator,
  156. struct aws_partitions_config *partitions,
  157. struct aws_byte_cursor partitions_cur) {
  158. struct aws_json_value *root = aws_json_value_new_from_string(allocator, partitions_cur);
  159. if (root == NULL) {
  160. AWS_LOGF_ERROR(AWS_LS_SDKUTILS_ENDPOINTS_PARSING, "Failed to parse provided string as json.");
  161. return aws_raise_error(AWS_ERROR_SDKUTILS_PARTITIONS_PARSE_FAILED);
  162. }
  163. partitions->json_root = root;
  164. struct aws_byte_cursor version_cur;
  165. struct aws_json_value *version_node = aws_json_value_get_from_object(root, aws_byte_cursor_from_c_str("version"));
  166. if (version_node == NULL || aws_json_value_get_string(version_node, &version_cur)) {
  167. AWS_LOGF_ERROR(AWS_LS_SDKUTILS_PARTITIONS_PARSING, "Failed to extract version.");
  168. return aws_raise_error(AWS_ERROR_SDKUTILS_PARTITIONS_UNSUPPORTED);
  169. }
  170. #ifdef ENDPOINTS_VERSION_CHECK /* TODO: samples are currently inconsistent with versions. skip check for now */
  171. if (!aws_byte_cursor_eq_c_str(&version_cur, &s_supported_version)) {
  172. AWS_LOGF_ERROR(AWS_LS_SDKUTILS_PARTITIONS_PARSING, "Unsupported partitions version.");
  173. aws_raise_error(AWS_ERROR_SDKUTILS_PARTITIONS_UNSUPPORTED);
  174. goto on_error;
  175. }
  176. #endif
  177. struct aws_json_value *partitions_node =
  178. aws_json_value_get_from_object(root, aws_byte_cursor_from_c_str("partitions"));
  179. if (partitions_node == NULL || aws_json_const_iterate_array(partitions_node, s_on_partition_element, partitions)) {
  180. AWS_LOGF_ERROR(AWS_LS_SDKUTILS_PARTITIONS_PARSING, "Failed to parse partitions.");
  181. return aws_raise_error(AWS_ERROR_SDKUTILS_PARTITIONS_PARSE_FAILED);
  182. }
  183. return AWS_OP_SUCCESS;
  184. }
  185. static void s_callback_partition_info_destroy(void *data) {
  186. struct aws_partition_info *info = data;
  187. aws_partition_info_destroy(info);
  188. }
  189. struct aws_partitions_config *aws_partitions_config_new_from_string(
  190. struct aws_allocator *allocator,
  191. struct aws_byte_cursor json) {
  192. AWS_PRECONDITION(allocator);
  193. AWS_PRECONDITION(aws_byte_cursor_is_valid(&json));
  194. struct aws_partitions_config *partitions = aws_mem_calloc(allocator, 1, sizeof(struct aws_partitions_config));
  195. partitions->allocator = allocator;
  196. if (aws_hash_table_init(
  197. &partitions->region_to_partition_info,
  198. allocator,
  199. 20,
  200. aws_hash_byte_cursor_ptr,
  201. aws_endpoints_byte_cursor_eq,
  202. NULL,
  203. s_callback_partition_info_destroy)) {
  204. AWS_LOGF_ERROR(AWS_LS_SDKUTILS_PARTITIONS_PARSING, "Failed to init partition info map.");
  205. aws_raise_error(AWS_ERROR_SDKUTILS_PARTITIONS_PARSE_FAILED);
  206. goto on_error;
  207. }
  208. if (s_init_partitions_config_from_json(allocator, partitions, json)) {
  209. AWS_LOGF_ERROR(AWS_LS_SDKUTILS_PARTITIONS_PARSING, "Failed to init partition info from json.");
  210. goto on_error;
  211. }
  212. aws_ref_count_init(&partitions->ref_count, partitions, s_partitions_config_destroy);
  213. return partitions;
  214. on_error:
  215. s_partitions_config_destroy(partitions);
  216. return NULL;
  217. }
  218. struct aws_partitions_config *aws_partitions_config_acquire(struct aws_partitions_config *partitions) {
  219. AWS_PRECONDITION(partitions);
  220. if (partitions) {
  221. aws_ref_count_acquire(&partitions->ref_count);
  222. }
  223. return partitions;
  224. }
  225. struct aws_partitions_config *aws_partitions_config_release(struct aws_partitions_config *partitions) {
  226. if (partitions) {
  227. aws_ref_count_release(&partitions->ref_count);
  228. }
  229. return NULL;
  230. }