eryph-compute 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +47 -0
  3. data/LICENSE +21 -0
  4. data/README.md +144 -0
  5. data/eryph-clientruntime.gemspec +42 -0
  6. data/eryph-compute.gemspec +44 -0
  7. data/lib/eryph/clientruntime/client_credentials_lookup.rb +295 -0
  8. data/lib/eryph/clientruntime/config_store.rb +206 -0
  9. data/lib/eryph/clientruntime/config_stores_reader.rb +211 -0
  10. data/lib/eryph/clientruntime/endpoint_lookup.rb +226 -0
  11. data/lib/eryph/clientruntime/environment.rb +440 -0
  12. data/lib/eryph/clientruntime/local_identity_provider_info.rb +153 -0
  13. data/lib/eryph/clientruntime/token_provider.rb +275 -0
  14. data/lib/eryph/clientruntime/version.rb +6 -0
  15. data/lib/eryph/clientruntime.rb +79 -0
  16. data/lib/eryph/compute/client.rb +442 -0
  17. data/lib/eryph/compute/generated/Gemfile +9 -0
  18. data/lib/eryph/compute/generated/README.md +206 -0
  19. data/lib/eryph/compute/generated/Rakefile +10 -0
  20. data/lib/eryph/compute/generated/compute_client.gemspec +41 -0
  21. data/lib/eryph/compute/generated/docs/ApiVersion.md +20 -0
  22. data/lib/eryph/compute/generated/docs/ApiVersionResponse.md +18 -0
  23. data/lib/eryph/compute/generated/docs/Catlet.md +32 -0
  24. data/lib/eryph/compute/generated/docs/CatletConfigOperationResult.md +18 -0
  25. data/lib/eryph/compute/generated/docs/CatletConfigValidationResult.md +20 -0
  26. data/lib/eryph/compute/generated/docs/CatletConfiguration.md +18 -0
  27. data/lib/eryph/compute/generated/docs/CatletDrive.md +20 -0
  28. data/lib/eryph/compute/generated/docs/CatletDriveType.md +15 -0
  29. data/lib/eryph/compute/generated/docs/CatletList.md +18 -0
  30. data/lib/eryph/compute/generated/docs/CatletNetwork.md +30 -0
  31. data/lib/eryph/compute/generated/docs/CatletNetworkAdapter.md +20 -0
  32. data/lib/eryph/compute/generated/docs/CatletStatus.md +15 -0
  33. data/lib/eryph/compute/generated/docs/CatletStopMode.md +15 -0
  34. data/lib/eryph/compute/generated/docs/CatletsApi.md +863 -0
  35. data/lib/eryph/compute/generated/docs/DiskStatus.md +15 -0
  36. data/lib/eryph/compute/generated/docs/ExpandCatletConfigRequestBody.md +22 -0
  37. data/lib/eryph/compute/generated/docs/ExpandNewCatletConfigRequest.md +22 -0
  38. data/lib/eryph/compute/generated/docs/FloatingNetworkPort.md +26 -0
  39. data/lib/eryph/compute/generated/docs/Gene.md +30 -0
  40. data/lib/eryph/compute/generated/docs/GeneList.md +18 -0
  41. data/lib/eryph/compute/generated/docs/GeneType.md +15 -0
  42. data/lib/eryph/compute/generated/docs/GeneWithUsage.md +34 -0
  43. data/lib/eryph/compute/generated/docs/GenesApi.md +281 -0
  44. data/lib/eryph/compute/generated/docs/NewCatletRequest.md +20 -0
  45. data/lib/eryph/compute/generated/docs/NewProjectMemberBody.md +22 -0
  46. data/lib/eryph/compute/generated/docs/NewProjectRequest.md +20 -0
  47. data/lib/eryph/compute/generated/docs/NewVirtualDiskRequest.md +30 -0
  48. data/lib/eryph/compute/generated/docs/Operation.md +32 -0
  49. data/lib/eryph/compute/generated/docs/OperationList.md +18 -0
  50. data/lib/eryph/compute/generated/docs/OperationLogEntry.md +24 -0
  51. data/lib/eryph/compute/generated/docs/OperationResource.md +22 -0
  52. data/lib/eryph/compute/generated/docs/OperationResult.md +18 -0
  53. data/lib/eryph/compute/generated/docs/OperationStatus.md +15 -0
  54. data/lib/eryph/compute/generated/docs/OperationTask.md +30 -0
  55. data/lib/eryph/compute/generated/docs/OperationTaskReference.md +22 -0
  56. data/lib/eryph/compute/generated/docs/OperationTaskStatus.md +15 -0
  57. data/lib/eryph/compute/generated/docs/OperationsApi.md +157 -0
  58. data/lib/eryph/compute/generated/docs/PopulateCatletConfigVariablesRequest.md +20 -0
  59. data/lib/eryph/compute/generated/docs/ProblemDetails.md +26 -0
  60. data/lib/eryph/compute/generated/docs/Project.md +22 -0
  61. data/lib/eryph/compute/generated/docs/ProjectList.md +18 -0
  62. data/lib/eryph/compute/generated/docs/ProjectMemberRole.md +26 -0
  63. data/lib/eryph/compute/generated/docs/ProjectMemberRoleList.md +18 -0
  64. data/lib/eryph/compute/generated/docs/ProjectMembersApi.md +293 -0
  65. data/lib/eryph/compute/generated/docs/ProjectsApi.md +286 -0
  66. data/lib/eryph/compute/generated/docs/ResourceType.md +15 -0
  67. data/lib/eryph/compute/generated/docs/StopCatletRequestBody.md +18 -0
  68. data/lib/eryph/compute/generated/docs/TaskReferenceType.md +15 -0
  69. data/lib/eryph/compute/generated/docs/UpdateCatletRequestBody.md +20 -0
  70. data/lib/eryph/compute/generated/docs/UpdateProjectNetworksRequestBody.md +20 -0
  71. data/lib/eryph/compute/generated/docs/ValidateConfigRequest.md +18 -0
  72. data/lib/eryph/compute/generated/docs/ValidationIssue.md +20 -0
  73. data/lib/eryph/compute/generated/docs/VersionApi.md +69 -0
  74. data/lib/eryph/compute/generated/docs/VirtualDisk.md +42 -0
  75. data/lib/eryph/compute/generated/docs/VirtualDiskAttachedCatlet.md +20 -0
  76. data/lib/eryph/compute/generated/docs/VirtualDiskGeneInfo.md +22 -0
  77. data/lib/eryph/compute/generated/docs/VirtualDiskList.md +18 -0
  78. data/lib/eryph/compute/generated/docs/VirtualDisksApi.md +291 -0
  79. data/lib/eryph/compute/generated/docs/VirtualNetwork.md +28 -0
  80. data/lib/eryph/compute/generated/docs/VirtualNetworkConfiguration.md +18 -0
  81. data/lib/eryph/compute/generated/docs/VirtualNetworkList.md +18 -0
  82. data/lib/eryph/compute/generated/docs/VirtualNetworksApi.md +291 -0
  83. data/lib/eryph/compute/generated/git_push.sh +57 -0
  84. data/lib/eryph/compute/generated/lib/compute_client/api/catlets_api.rb +812 -0
  85. data/lib/eryph/compute/generated/lib/compute_client/api/genes_api.rb +262 -0
  86. data/lib/eryph/compute/generated/lib/compute_client/api/operations_api.rb +154 -0
  87. data/lib/eryph/compute/generated/lib/compute_client/api/project_members_api.rb +297 -0
  88. data/lib/eryph/compute/generated/lib/compute_client/api/projects_api.rb +269 -0
  89. data/lib/eryph/compute/generated/lib/compute_client/api/version_api.rb +79 -0
  90. data/lib/eryph/compute/generated/lib/compute_client/api/virtual_disks_api.rb +272 -0
  91. data/lib/eryph/compute/generated/lib/compute_client/api/virtual_networks_api.rb +282 -0
  92. data/lib/eryph/compute/generated/lib/compute_client/api_client.rb +437 -0
  93. data/lib/eryph/compute/generated/lib/compute_client/api_error.rb +58 -0
  94. data/lib/eryph/compute/generated/lib/compute_client/configuration.rb +392 -0
  95. data/lib/eryph/compute/generated/lib/compute_client/models/api_version.rb +263 -0
  96. data/lib/eryph/compute/generated/lib/compute_client/models/api_version_response.rb +237 -0
  97. data/lib/eryph/compute/generated/lib/compute_client/models/catlet.rb +400 -0
  98. data/lib/eryph/compute/generated/lib/compute_client/models/catlet_config_operation_result.rb +234 -0
  99. data/lib/eryph/compute/generated/lib/compute_client/models/catlet_config_validation_result.rb +251 -0
  100. data/lib/eryph/compute/generated/lib/compute_client/models/catlet_configuration.rb +223 -0
  101. data/lib/eryph/compute/generated/lib/compute_client/models/catlet_drive.rb +270 -0
  102. data/lib/eryph/compute/generated/lib/compute_client/models/catlet_drive_type.rb +43 -0
  103. data/lib/eryph/compute/generated/lib/compute_client/models/catlet_list.rb +239 -0
  104. data/lib/eryph/compute/generated/lib/compute_client/models/catlet_network.rb +318 -0
  105. data/lib/eryph/compute/generated/lib/compute_client/models/catlet_network_adapter.rb +247 -0
  106. data/lib/eryph/compute/generated/lib/compute_client/models/catlet_status.rb +43 -0
  107. data/lib/eryph/compute/generated/lib/compute_client/models/catlet_stop_mode.rb +41 -0
  108. data/lib/eryph/compute/generated/lib/compute_client/models/disk_status.rb +40 -0
  109. data/lib/eryph/compute/generated/lib/compute_client/models/expand_catlet_config_request_body.rb +243 -0
  110. data/lib/eryph/compute/generated/lib/compute_client/models/expand_new_catlet_config_request.rb +243 -0
  111. data/lib/eryph/compute/generated/lib/compute_client/models/floating_network_port.rb +313 -0
  112. data/lib/eryph/compute/generated/lib/compute_client/models/gene.rb +415 -0
  113. data/lib/eryph/compute/generated/lib/compute_client/models/gene_list.rb +239 -0
  114. data/lib/eryph/compute/generated/lib/compute_client/models/gene_type.rb +41 -0
  115. data/lib/eryph/compute/generated/lib/compute_client/models/gene_with_usage.rb +439 -0
  116. data/lib/eryph/compute/generated/lib/compute_client/models/new_catlet_request.rb +233 -0
  117. data/lib/eryph/compute/generated/lib/compute_client/models/new_project_member_body.rb +273 -0
  118. data/lib/eryph/compute/generated/lib/compute_client/models/new_project_request.rb +247 -0
  119. data/lib/eryph/compute/generated/lib/compute_client/models/new_virtual_disk_request.rb +345 -0
  120. data/lib/eryph/compute/generated/lib/compute_client/models/operation.rb +352 -0
  121. data/lib/eryph/compute/generated/lib/compute_client/models/operation_list.rb +239 -0
  122. data/lib/eryph/compute/generated/lib/compute_client/models/operation_log_entry.rb +299 -0
  123. data/lib/eryph/compute/generated/lib/compute_client/models/operation_resource.rb +311 -0
  124. data/lib/eryph/compute/generated/lib/compute_client/models/operation_result.rb +242 -0
  125. data/lib/eryph/compute/generated/lib/compute_client/models/operation_status.rb +42 -0
  126. data/lib/eryph/compute/generated/lib/compute_client/models/operation_task.rb +366 -0
  127. data/lib/eryph/compute/generated/lib/compute_client/models/operation_task_reference.rb +311 -0
  128. data/lib/eryph/compute/generated/lib/compute_client/models/operation_task_status.rb +42 -0
  129. data/lib/eryph/compute/generated/lib/compute_client/models/populate_catlet_config_variables_request.rb +233 -0
  130. data/lib/eryph/compute/generated/lib/compute_client/models/problem_details.rb +261 -0
  131. data/lib/eryph/compute/generated/lib/compute_client/models/project.rb +289 -0
  132. data/lib/eryph/compute/generated/lib/compute_client/models/project_list.rb +239 -0
  133. data/lib/eryph/compute/generated/lib/compute_client/models/project_member_role.rb +341 -0
  134. data/lib/eryph/compute/generated/lib/compute_client/models/project_member_role_list.rb +239 -0
  135. data/lib/eryph/compute/generated/lib/compute_client/models/resource_type.rb +42 -0
  136. data/lib/eryph/compute/generated/lib/compute_client/models/stop_catlet_request_body.rb +259 -0
  137. data/lib/eryph/compute/generated/lib/compute_client/models/task_reference_type.rb +40 -0
  138. data/lib/eryph/compute/generated/lib/compute_client/models/update_catlet_request_body.rb +233 -0
  139. data/lib/eryph/compute/generated/lib/compute_client/models/update_project_networks_request_body.rb +233 -0
  140. data/lib/eryph/compute/generated/lib/compute_client/models/validate_config_request.rb +223 -0
  141. data/lib/eryph/compute/generated/lib/compute_client/models/validation_issue.rb +249 -0
  142. data/lib/eryph/compute/generated/lib/compute_client/models/virtual_disk.rb +479 -0
  143. data/lib/eryph/compute/generated/lib/compute_client/models/virtual_disk_attached_catlet.rb +285 -0
  144. data/lib/eryph/compute/generated/lib/compute_client/models/virtual_disk_gene_info.rb +289 -0
  145. data/lib/eryph/compute/generated/lib/compute_client/models/virtual_disk_list.rb +239 -0
  146. data/lib/eryph/compute/generated/lib/compute_client/models/virtual_network.rb +351 -0
  147. data/lib/eryph/compute/generated/lib/compute_client/models/virtual_network_configuration.rb +223 -0
  148. data/lib/eryph/compute/generated/lib/compute_client/models/virtual_network_list.rb +239 -0
  149. data/lib/eryph/compute/generated/lib/compute_client/version.rb +15 -0
  150. data/lib/eryph/compute/generated/lib/compute_client.rb +101 -0
  151. data/lib/eryph/compute/generated/spec/api/catlets_api_spec.rb +182 -0
  152. data/lib/eryph/compute/generated/spec/api/genes_api_spec.rb +81 -0
  153. data/lib/eryph/compute/generated/spec/api/operations_api_spec.rb +62 -0
  154. data/lib/eryph/compute/generated/spec/api/project_members_api_spec.rb +86 -0
  155. data/lib/eryph/compute/generated/spec/api/projects_api_spec.rb +82 -0
  156. data/lib/eryph/compute/generated/spec/api/version_api_spec.rb +46 -0
  157. data/lib/eryph/compute/generated/spec/api/virtual_disks_api_spec.rb +83 -0
  158. data/lib/eryph/compute/generated/spec/api/virtual_networks_api_spec.rb +84 -0
  159. data/lib/eryph/compute/generated/spec/models/api_version_response_spec.rb +36 -0
  160. data/lib/eryph/compute/generated/spec/models/api_version_spec.rb +42 -0
  161. data/lib/eryph/compute/generated/spec/models/catlet_config_operation_result_spec.rb +36 -0
  162. data/lib/eryph/compute/generated/spec/models/catlet_config_validation_result_spec.rb +42 -0
  163. data/lib/eryph/compute/generated/spec/models/catlet_configuration_spec.rb +36 -0
  164. data/lib/eryph/compute/generated/spec/models/catlet_drive_spec.rb +42 -0
  165. data/lib/eryph/compute/generated/spec/models/catlet_drive_type_spec.rb +30 -0
  166. data/lib/eryph/compute/generated/spec/models/catlet_list_spec.rb +36 -0
  167. data/lib/eryph/compute/generated/spec/models/catlet_network_adapter_spec.rb +42 -0
  168. data/lib/eryph/compute/generated/spec/models/catlet_network_spec.rb +72 -0
  169. data/lib/eryph/compute/generated/spec/models/catlet_spec.rb +78 -0
  170. data/lib/eryph/compute/generated/spec/models/catlet_status_spec.rb +30 -0
  171. data/lib/eryph/compute/generated/spec/models/catlet_stop_mode_spec.rb +30 -0
  172. data/lib/eryph/compute/generated/spec/models/disk_status_spec.rb +30 -0
  173. data/lib/eryph/compute/generated/spec/models/expand_catlet_config_request_body_spec.rb +48 -0
  174. data/lib/eryph/compute/generated/spec/models/expand_new_catlet_config_request_spec.rb +48 -0
  175. data/lib/eryph/compute/generated/spec/models/floating_network_port_spec.rb +60 -0
  176. data/lib/eryph/compute/generated/spec/models/gene_list_spec.rb +36 -0
  177. data/lib/eryph/compute/generated/spec/models/gene_spec.rb +72 -0
  178. data/lib/eryph/compute/generated/spec/models/gene_type_spec.rb +30 -0
  179. data/lib/eryph/compute/generated/spec/models/gene_with_usage_spec.rb +84 -0
  180. data/lib/eryph/compute/generated/spec/models/new_catlet_request_spec.rb +42 -0
  181. data/lib/eryph/compute/generated/spec/models/new_project_member_body_spec.rb +48 -0
  182. data/lib/eryph/compute/generated/spec/models/new_project_request_spec.rb +42 -0
  183. data/lib/eryph/compute/generated/spec/models/new_virtual_disk_request_spec.rb +72 -0
  184. data/lib/eryph/compute/generated/spec/models/operation_list_spec.rb +36 -0
  185. data/lib/eryph/compute/generated/spec/models/operation_log_entry_spec.rb +54 -0
  186. data/lib/eryph/compute/generated/spec/models/operation_resource_spec.rb +48 -0
  187. data/lib/eryph/compute/generated/spec/models/operation_result_spec.rb +36 -0
  188. data/lib/eryph/compute/generated/spec/models/operation_spec.rb +78 -0
  189. data/lib/eryph/compute/generated/spec/models/operation_status_spec.rb +30 -0
  190. data/lib/eryph/compute/generated/spec/models/operation_task_reference_spec.rb +48 -0
  191. data/lib/eryph/compute/generated/spec/models/operation_task_spec.rb +72 -0
  192. data/lib/eryph/compute/generated/spec/models/operation_task_status_spec.rb +30 -0
  193. data/lib/eryph/compute/generated/spec/models/populate_catlet_config_variables_request_spec.rb +42 -0
  194. data/lib/eryph/compute/generated/spec/models/problem_details_spec.rb +60 -0
  195. data/lib/eryph/compute/generated/spec/models/project_list_spec.rb +36 -0
  196. data/lib/eryph/compute/generated/spec/models/project_member_role_list_spec.rb +36 -0
  197. data/lib/eryph/compute/generated/spec/models/project_member_role_spec.rb +60 -0
  198. data/lib/eryph/compute/generated/spec/models/project_spec.rb +48 -0
  199. data/lib/eryph/compute/generated/spec/models/resource_type_spec.rb +30 -0
  200. data/lib/eryph/compute/generated/spec/models/stop_catlet_request_body_spec.rb +36 -0
  201. data/lib/eryph/compute/generated/spec/models/task_reference_type_spec.rb +30 -0
  202. data/lib/eryph/compute/generated/spec/models/update_catlet_request_body_spec.rb +42 -0
  203. data/lib/eryph/compute/generated/spec/models/update_project_networks_request_body_spec.rb +42 -0
  204. data/lib/eryph/compute/generated/spec/models/validate_config_request_spec.rb +36 -0
  205. data/lib/eryph/compute/generated/spec/models/validation_issue_spec.rb +42 -0
  206. data/lib/eryph/compute/generated/spec/models/virtual_disk_attached_catlet_spec.rb +42 -0
  207. data/lib/eryph/compute/generated/spec/models/virtual_disk_gene_info_spec.rb +48 -0
  208. data/lib/eryph/compute/generated/spec/models/virtual_disk_list_spec.rb +36 -0
  209. data/lib/eryph/compute/generated/spec/models/virtual_disk_spec.rb +108 -0
  210. data/lib/eryph/compute/generated/spec/models/virtual_network_configuration_spec.rb +36 -0
  211. data/lib/eryph/compute/generated/spec/models/virtual_network_list_spec.rb +36 -0
  212. data/lib/eryph/compute/generated/spec/models/virtual_network_spec.rb +66 -0
  213. data/lib/eryph/compute/generated/spec/spec_helper.rb +111 -0
  214. data/lib/eryph/compute/generated.rb +137 -0
  215. data/lib/eryph/compute/operation_result.rb +255 -0
  216. data/lib/eryph/compute/operation_tracker.rb +247 -0
  217. data/lib/eryph/compute/problem_details_error.rb +139 -0
  218. data/lib/eryph/compute/version.rb +6 -0
  219. data/lib/eryph/compute.rb +40 -0
  220. data/lib/eryph/version.rb +4 -0
  221. data/lib/eryph.rb +68 -0
  222. metadata +329 -0
@@ -0,0 +1,247 @@
1
+ require 'set'
2
+
3
+ module Eryph
4
+ module Compute
5
+ # Advanced operation tracker with fluent callback registration
6
+ # Provides more control over operation tracking than the basic wait_for_operation
7
+ class OperationTracker
8
+ attr_reader :operation_id, :client
9
+
10
+ def initialize(client, operation_id)
11
+ @client = client
12
+ @operation_id = operation_id
13
+ @processed_log_ids = Set.new
14
+ @processed_task_ids = Set.new
15
+ @processed_resource_ids = Set.new
16
+ @last_timestamp = Time.parse('2018-01-01')
17
+ @callbacks = {}
18
+ @raw_json = nil
19
+ end
20
+
21
+ # Register callback for new log entries
22
+ # @yield [log_entry] callback for each new log entry
23
+ # @return [self] for method chaining
24
+ def on_log_entry(&block)
25
+ @callbacks[:log_entry] = block
26
+ self
27
+ end
28
+
29
+ # Register callback for new tasks
30
+ # @yield [task] callback for each new task discovered
31
+ # @return [self] for method chaining
32
+ def on_task_new(&block)
33
+ @callbacks[:task_new] = block
34
+ self
35
+ end
36
+
37
+ # Register callback for task updates (status/progress changes)
38
+ # @yield [task] callback for each task update
39
+ # @return [self] for method chaining
40
+ def on_task_update(&block)
41
+ @callbacks[:task_update] = block
42
+ self
43
+ end
44
+
45
+ # Register callback for new resources
46
+ # @yield [resource] callback for each new resource discovered
47
+ # @return [self] for method chaining
48
+ def on_resource_new(&block)
49
+ @callbacks[:resource_new] = block
50
+ self
51
+ end
52
+
53
+ # Register callback for status changes
54
+ # @yield [operation] callback for each status update
55
+ # @return [self] for method chaining
56
+ def on_status_change(&block)
57
+ @callbacks[:status_change] = block
58
+ self
59
+ end
60
+
61
+ # Single poll to check current state without blocking
62
+ # @return [Operation] the current operation state
63
+ def poll
64
+ # Get raw JSON using debug_return_type (same approach as Client)
65
+ begin
66
+ @raw_json = @client.operations.operations_get(
67
+ @operation_id,
68
+ expand: 'logs,tasks,resources',
69
+ log_time_stamp: @last_timestamp,
70
+ debug_return_type: 'String'
71
+ )
72
+ @client.logger.debug "Raw JSON captured: #{@raw_json ? 'YES' : 'NO'}"
73
+ rescue StandardError => e
74
+ @client.logger.debug "Failed to capture raw JSON: #{e.message}"
75
+ end
76
+
77
+ # Get the normal deserialized operation
78
+ operation = @client.operations.operations_get(
79
+ @operation_id,
80
+ expand: 'logs,tasks,resources',
81
+ log_time_stamp: @last_timestamp
82
+ )
83
+
84
+ process_updates(operation)
85
+ operation
86
+ end
87
+
88
+ # Track operation until completion with polling
89
+ # @param timeout [Integer] timeout in seconds (default: 300)
90
+ # @param poll_interval [Integer] polling interval in seconds (default: 5)
91
+ # @return [OperationResult] the completed operation result
92
+ # @raise [Timeout::Error] if the operation times out
93
+ def track_to_completion(timeout: 300, poll_interval: 5)
94
+ start_time = Time.now
95
+
96
+ @client.logger.info "Tracking operation #{@operation_id} to completion (timeout: #{timeout}s)..."
97
+
98
+ loop do
99
+ operation = poll
100
+
101
+ if operation.status == 'Completed'
102
+ @client.logger.info "Operation #{@operation_id} completed successfully!"
103
+ @client.logger.info "Raw JSON available: #{@raw_json ? 'YES' : 'NO'}"
104
+ @client.logger.info "Raw JSON length: #{@raw_json ? @raw_json.length : 'N/A'}"
105
+ return OperationResult.new(operation, @client, @raw_json)
106
+ elsif operation.status == 'Failed'
107
+ @client.logger.error "Operation #{@operation_id} failed: #{operation.status_message}"
108
+ @client.logger.info "Raw JSON available: #{@raw_json ? 'YES' : 'NO'}"
109
+ return OperationResult.new(operation, @client, @raw_json)
110
+ end
111
+
112
+ elapsed = Time.now - start_time
113
+ if elapsed > timeout
114
+ @client.logger.error "Operation #{@operation_id} timed out after #{timeout} seconds"
115
+ raise Timeout::Error, "Operation #{@operation_id} timed out after #{timeout} seconds"
116
+ end
117
+
118
+ @client.logger.debug "Operation #{@operation_id} status: #{operation.status} (#{elapsed.round(1)}s elapsed)"
119
+ sleep poll_interval
120
+ end
121
+ end
122
+
123
+ # Track with a single callback function (like wait_for_operation)
124
+ # @param timeout [Integer] timeout in seconds
125
+ # @param poll_interval [Integer] polling interval in seconds
126
+ # @yield [event_type, data] callback for operation events
127
+ # @return [OperationResult] the completed operation result
128
+ def track(timeout: 300, poll_interval: 5)
129
+ if block_given?
130
+ # Set up temporary callback
131
+ original_callbacks = @callbacks.dup
132
+
133
+ @callbacks[:log_entry] = ->(data) { yield(:log_entry, data) }
134
+ @callbacks[:task_new] = ->(data) { yield(:task_new, data) }
135
+ @callbacks[:task_update] = ->(data) { yield(:task_update, data) }
136
+ @callbacks[:resource_new] = ->(data) { yield(:resource_new, data) }
137
+ @callbacks[:status_change] = ->(data) { yield(:status, data) }
138
+
139
+ begin
140
+ result = track_to_completion(timeout: timeout, poll_interval: poll_interval)
141
+ ensure
142
+ # Restore original callbacks
143
+ @callbacks = original_callbacks
144
+ end
145
+
146
+ result
147
+ else
148
+ track_to_completion(timeout: timeout, poll_interval: poll_interval)
149
+ end
150
+ end
151
+
152
+ # Get current operation state without updating tracking state
153
+ # @return [Operation] the current operation
154
+ def current_state
155
+ @client.operations.operations_get(@operation_id)
156
+ end
157
+
158
+ # Reset tracking state (clears processed IDs and timestamp)
159
+ # @return [self] for method chaining
160
+ def reset_state
161
+ @processed_log_ids.clear
162
+ @processed_task_ids.clear
163
+ @processed_resource_ids.clear
164
+ @last_timestamp = Time.parse('2018-01-01')
165
+ self
166
+ end
167
+
168
+ # Get statistics about what has been processed so far
169
+ # @return [Hash] statistics hash
170
+ def stats
171
+ {
172
+ operation_id: @operation_id,
173
+ processed_logs: @processed_log_ids.size,
174
+ processed_tasks: @processed_task_ids.size,
175
+ processed_resources: @processed_resource_ids.size,
176
+ last_timestamp: @last_timestamp,
177
+ }
178
+ end
179
+
180
+ # String representation
181
+ def to_s
182
+ "#<OperationTracker operation_id=#{@operation_id} stats=#{stats}>"
183
+ end
184
+
185
+ def inspect
186
+ to_s
187
+ end
188
+
189
+ private
190
+
191
+ # Process updates from a polled operation
192
+ # @param operation [Operation] the operation to process
193
+ def process_updates(operation)
194
+ # Process new logs
195
+ operation.log_entries&.each do |log_entry|
196
+ next if @processed_log_ids.include?(log_entry.id)
197
+
198
+ @processed_log_ids.add(log_entry.id)
199
+ @last_timestamp = log_entry.timestamp if log_entry.timestamp > @last_timestamp
200
+
201
+ begin
202
+ @callbacks[:log_entry]&.call(log_entry)
203
+ rescue StandardError => e
204
+ @client.logger.error "Error in log_entry callback: #{e.message}"
205
+ end
206
+ end
207
+
208
+ # Process new and updated tasks
209
+ operation.tasks&.each do |task|
210
+ if @processed_task_ids.include?(task.id)
211
+ begin
212
+ @callbacks[:task_update]&.call(task)
213
+ rescue StandardError => e
214
+ @client.logger.error "Error in task_update callback: #{e.message}"
215
+ end
216
+ else
217
+ @processed_task_ids.add(task.id)
218
+ begin
219
+ @callbacks[:task_new]&.call(task)
220
+ rescue StandardError => e
221
+ @client.logger.error "Error in task_new callback: #{e.message}"
222
+ end
223
+ end
224
+ end
225
+
226
+ # Process new resources
227
+ operation.resources&.each do |resource|
228
+ next if @processed_resource_ids.include?(resource.id)
229
+
230
+ @processed_resource_ids.add(resource.id)
231
+ begin
232
+ @callbacks[:resource_new]&.call(resource)
233
+ rescue StandardError => e
234
+ @client.logger.error "Error in resource_new callback: #{e.message}"
235
+ end
236
+ end
237
+
238
+ # Status update
239
+ begin
240
+ @callbacks[:status_change]&.call(operation)
241
+ rescue StandardError => e
242
+ @client.logger.error "Error in status_change callback: #{e.message}"
243
+ end
244
+ end
245
+ end
246
+ end
247
+ end
@@ -0,0 +1,139 @@
1
+ require 'json'
2
+
3
+ module Eryph
4
+ module Compute
5
+ # Exception class for API errors that include ProblemDetails in the response
6
+ # Extends StandardError with parsed problem details information
7
+ class ProblemDetailsError < StandardError
8
+ attr_reader :problem_details, :problem_type, :title, :detail, :instance, :problem_status, :code,
9
+ :response_headers, :response_body
10
+
11
+ # Create a new ProblemDetailsError
12
+ # @param api_error [Object] the original API error (could be ComputeClient::ApiError or similar)
13
+ # @param problem_details [Object, Hash] the parsed problem details
14
+ def initialize(api_error, problem_details = nil)
15
+ # Extract basic error information
16
+ @code = api_error.respond_to?(:code) ? api_error.code : nil
17
+ @response_headers = api_error.respond_to?(:response_headers) ? api_error.response_headers : nil
18
+ @response_body = api_error.respond_to?(:response_body) ? api_error.response_body : nil
19
+
20
+ # Initialize with the original error's message or a default
21
+ original_message = api_error.respond_to?(:message) ? api_error.message : api_error.to_s
22
+ super(original_message)
23
+
24
+ @problem_details = problem_details
25
+
26
+ return unless problem_details
27
+
28
+ if problem_details.respond_to?(:type)
29
+ # ComputeClient::ProblemDetails object
30
+ @problem_type = problem_details.type
31
+ @title = problem_details.title
32
+ @detail = problem_details.detail
33
+ @instance = problem_details.instance
34
+ @problem_status = problem_details.status
35
+ elsif problem_details.is_a?(Hash)
36
+ # Hash representation
37
+ @problem_type = problem_details[:type] || problem_details['type']
38
+ @title = problem_details[:title] || problem_details['title']
39
+ @detail = problem_details[:detail] || problem_details['detail']
40
+ @instance = problem_details[:instance] || problem_details['instance']
41
+ @problem_status = problem_details[:status] || problem_details['status']
42
+ end
43
+ end
44
+
45
+ # Create a ProblemDetailsError from an ApiError by parsing the response body
46
+ # @param api_error [Object] the original API error
47
+ # @return [ProblemDetailsError, Object] parsed error or original if not parseable
48
+ def self.from_api_error(api_error)
49
+ response_body = api_error.respond_to?(:response_body) ? api_error.response_body : nil
50
+ problem_details = parse_problem_details(response_body)
51
+
52
+ if problem_details
53
+ new(api_error, problem_details)
54
+ else
55
+ api_error
56
+ end
57
+ end
58
+
59
+ # Parse ProblemDetails from a response body
60
+ # @param response_body [String] the HTTP response body
61
+ # @return [Object, Hash, nil] parsed problem details or nil if not parseable
62
+ def self.parse_problem_details(response_body)
63
+ return nil if response_body.nil? || response_body.empty?
64
+
65
+ begin
66
+ json_data = JSON.parse(response_body)
67
+
68
+ # Check if it looks like a problem details response
69
+ if json_data.is_a?(Hash) && (json_data.key?('type') || json_data.key?('title'))
70
+ # Try to create a proper ProblemDetails object if the class is available
71
+ begin
72
+ if defined?(ComputeClient::ProblemDetails)
73
+ ComputeClient::ProblemDetails.build_from_hash(json_data)
74
+ else
75
+ # Fall back to hash representation if class not available
76
+ json_data
77
+ end
78
+ rescue StandardError
79
+ # Fall back to hash representation if object creation fails
80
+ json_data
81
+ end
82
+ end
83
+ rescue JSON::ParserError
84
+ nil
85
+ end
86
+ end
87
+
88
+ # Check if this error has problem details information
89
+ # @return [Boolean] true if problem details are available
90
+ def problem_details?
91
+ !@problem_details.nil?
92
+ end
93
+
94
+ # Get a user-friendly error message
95
+ # @return [String] formatted error message
96
+ def friendly_message
97
+ if problem_details?
98
+ parts = []
99
+ parts << @title if @title && !@title.empty?
100
+ parts << @detail if @detail && !@detail.empty?
101
+
102
+ if parts.empty?
103
+ "API Error: #{@problem_type || 'Unknown error'}"
104
+ else
105
+ parts.join(': ')
106
+ end
107
+ else
108
+ super.message
109
+ end
110
+ end
111
+
112
+ # Override message to provide better error information
113
+ def message
114
+ if problem_details?
115
+ msg = friendly_message
116
+ msg += "\nProblem Type: #{@problem_type}" if @problem_type
117
+ msg += "\nInstance: #{@instance}" if @instance
118
+ msg += "\nHTTP Status: #{code}" if code
119
+ msg
120
+ else
121
+ super
122
+ end
123
+ end
124
+
125
+ # String representation
126
+ def to_s
127
+ friendly_message
128
+ end
129
+
130
+ def inspect
131
+ if problem_details?
132
+ "#<ProblemDetailsError problem_type=#{@problem_type.inspect} title=#{@title.inspect} status=#{code}>"
133
+ else
134
+ super
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,6 @@
1
+ module Eryph
2
+ module Compute
3
+ # Current version of the Eryph Compute Client
4
+ VERSION = '1.0.0'.freeze
5
+ end
6
+ end
@@ -0,0 +1,40 @@
1
+ # Eryph Compute Client
2
+ # High-level client for the Eryph Compute API
3
+
4
+ require_relative 'compute/version'
5
+ require_relative 'compute/operation_result'
6
+ require_relative 'compute/operation_tracker'
7
+ require_relative 'compute/problem_details_error'
8
+ require_relative 'compute/client'
9
+
10
+ module Eryph
11
+ # Compute API client module
12
+ module Compute
13
+ # Error raised when compute API operations fail
14
+ class ComputeError < StandardError; end
15
+
16
+ # Error raised when API responses are invalid
17
+ class ApiError < ComputeError
18
+ # @return [Integer] HTTP response code
19
+ attr_reader :code
20
+
21
+ # @return [String] response body
22
+ attr_reader :response_body
23
+
24
+ # @return [Hash] response headers
25
+ attr_reader :response_headers
26
+
27
+ # Initialize API error
28
+ # @param message [String] error message
29
+ # @param code [Integer] HTTP response code
30
+ # @param response_body [String] response body
31
+ # @param response_headers [Hash] response headers
32
+ def initialize(message, code: nil, response_body: nil, response_headers: nil)
33
+ super(message)
34
+ @code = code
35
+ @response_body = response_body
36
+ @response_headers = response_headers
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,4 @@
1
+ module Eryph
2
+ # Current version of the Eryph Ruby Compute Client
3
+ VERSION = '0.1.1'.freeze
4
+ end
data/lib/eryph.rb ADDED
@@ -0,0 +1,68 @@
1
+ # Eryph Ruby Compute Client
2
+ # Main entry point for the Eryph compute client library
3
+
4
+ require_relative 'eryph/version'
5
+ require_relative 'eryph/clientruntime'
6
+ require_relative 'eryph/compute'
7
+
8
+ # Only load generated client if dependencies are available
9
+ begin
10
+ require_relative 'eryph/compute/generated'
11
+ rescue LoadError => e
12
+ # Generated client dependencies not available, API clients will fall back to placeholder mode
13
+ warn "Warning: Generated compute client not available (#{e.message}). Run generate.ps1 to create the API client."
14
+ end
15
+
16
+ module Eryph
17
+ class << self
18
+ # Create compute client with automatic or specific credential discovery
19
+ # @param config_name [String, nil] configuration name for specific config, nil for automatic discovery
20
+ # @param client_id [String, nil] specific client ID to use
21
+ # @param options [Hash] additional options
22
+ # @option options [Logger] :logger logger instance
23
+ # @option options [Array<String>] :scopes OAuth2 scopes
24
+ # @option options [Hash] :ssl_config SSL configuration options
25
+ # @option options [Environment] :environment environment for dependency injection
26
+ # @return [Compute::Client] a new compute client instance
27
+ # @example Automatic discovery
28
+ # client = Eryph.compute_client
29
+ # @example Specific configuration
30
+ # client = Eryph.compute_client('zero')
31
+ # @example Specific client ID
32
+ # client = Eryph.compute_client('myconfig', client_id: 'my-client-id')
33
+ # @example With options
34
+ # client = Eryph.compute_client(logger: my_logger, ssl_config: { verify_ssl: false })
35
+ def compute_client(config_name = nil, client_id: nil, **options)
36
+ Compute::Client.new(
37
+ config_name,
38
+ client_id: client_id,
39
+ logger: options[:logger],
40
+ scopes: options[:scopes],
41
+ ssl_config: options[:ssl_config] || {},
42
+ environment: options[:environment]
43
+ )
44
+ end
45
+
46
+ # Test if credentials are available for the specified configuration
47
+ # @param config_name [String, nil] configuration name, nil for automatic discovery
48
+ # @return [Boolean] true if credentials are available
49
+ # @example
50
+ # Eryph.credentials_available? # automatic discovery
51
+ # Eryph.credentials_available?('zero') # specific config
52
+ def credentials_available?(config_name = nil)
53
+ ClientRuntime.credentials_available?(config_name: config_name)
54
+ end
55
+
56
+ # Check if eryph-zero is running locally
57
+ # @return [Boolean] true if eryph-zero is running
58
+ def zero_running?
59
+ ClientRuntime.zero_running?
60
+ end
61
+
62
+ # Get endpoints from running eryph-zero instance
63
+ # @return [Hash] endpoint name -> URL mapping
64
+ def zero_endpoints
65
+ ClientRuntime.zero_endpoints
66
+ end
67
+ end
68
+ end