volcano-sdk 0.32.7 → 0.33.0

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -2
  3. data/lib/volcano/client.rb +3 -2
  4. data/lib/volcano/generated/lib/volcano-generated/api/sandboxes_api.rb +1433 -0
  5. data/lib/volcano/generated/lib/volcano-generated/models/create_sandbox_session_request.rb +294 -0
  6. data/lib/volcano/generated/lib/volcano-generated/models/create_sandbox_template_request.rb +247 -0
  7. data/lib/volcano/generated/lib/volcano-generated/models/publish_sandbox_preset_request.rb +268 -0
  8. data/lib/volcano/generated/lib/volcano-generated/models/sandbox_access.rb +216 -0
  9. data/lib/volcano/generated/lib/volcano-generated/models/sandbox_access_request.rb +221 -0
  10. data/lib/volcano/generated/lib/volcano-generated/models/sandbox_capacity.rb +199 -0
  11. data/lib/volcano/generated/lib/volcano-generated/models/sandbox_capacity_list.rb +166 -0
  12. data/lib/volcano/generated/lib/volcano-generated/models/sandbox_command_request.rb +251 -0
  13. data/lib/volcano/generated/lib/volcano-generated/models/sandbox_command_result.rb +294 -0
  14. data/lib/volcano/generated/lib/volcano-generated/models/sandbox_deployment.rb +242 -0
  15. data/lib/volcano/generated/lib/volcano-generated/models/sandbox_deployment_page.rb +192 -0
  16. data/lib/volcano/generated/lib/volcano-generated/models/sandbox_execution_request.rb +329 -0
  17. data/lib/volcano/generated/lib/volcano-generated/models/sandbox_execution_result.rb +381 -0
  18. data/lib/volcano/generated/lib/volcano-generated/models/sandbox_file_read_request.rb +182 -0
  19. data/lib/volcano/generated/lib/volcano-generated/models/sandbox_file_result.rb +164 -0
  20. data/lib/volcano/generated/lib/volcano-generated/models/sandbox_file_write_request.rb +217 -0
  21. data/lib/volcano/generated/lib/volcano-generated/models/sandbox_pagination.rb +199 -0
  22. data/lib/volcano/generated/lib/volcano-generated/models/sandbox_preset.rb +294 -0
  23. data/lib/volcano/generated/lib/volcano-generated/models/sandbox_preset_list.rb +166 -0
  24. data/lib/volcano/generated/lib/volcano-generated/models/sandbox_session.rb +407 -0
  25. data/lib/volcano/generated/lib/volcano-generated/models/sandbox_session_page.rb +192 -0
  26. data/lib/volcano/generated/lib/volcano-generated/models/sandbox_subject_grant_request.rb +164 -0
  27. data/lib/volcano/generated/lib/volcano-generated/models/sandbox_template.rb +321 -0
  28. data/lib/volcano/generated/lib/volcano-generated/models/sandbox_template_page.rb +192 -0
  29. data/lib/volcano/generated/lib/volcano-generated/models/update_sandbox_template_request.rb +175 -0
  30. data/lib/volcano/generated/lib/volcano-generated.rb +26 -0
  31. data/lib/volcano/generated_transport.rb +1 -0
  32. data/lib/volcano/generated_transport_sandbox.rb +69 -0
  33. data/lib/volcano/sandbox_files.rb +37 -0
  34. data/lib/volcano/sandbox_models.rb +24 -0
  35. data/lib/volcano/sandbox_request.rb +92 -0
  36. data/lib/volcano/sandbox_response.rb +74 -0
  37. data/lib/volcano/sandbox_session.rb +78 -0
  38. data/lib/volcano/sandboxes.rb +67 -0
  39. data/lib/volcano/version.rb +1 -1
  40. data/lib/volcano.rb +7 -0
  41. data/sig/client.rbs +1 -0
  42. data/sig/sandboxes.rbs +83 -0
  43. metadata +50 -2
@@ -0,0 +1,175 @@
1
+ =begin
2
+ #Volcano Hosting API
3
+
4
+ #Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations.
5
+
6
+ The version of the OpenAPI document: 3.0.0
7
+ Contact: support@volcano.dev
8
+ Generated by: https://openapi-generator.tech
9
+ Generator version: 7.17.0
10
+
11
+ =end
12
+
13
+ require 'date'
14
+ require 'time'
15
+
16
+ module Volcano::Generated
17
+ class UpdateSandboxTemplateRequest < ApiModelBase
18
+ attr_accessor :name
19
+
20
+ # Attribute mapping from ruby-style variable name to JSON key.
21
+ def self.attribute_map
22
+ {
23
+ :'name' => :'name'
24
+ }
25
+ end
26
+
27
+ # Returns attribute mapping this model knows about
28
+ def self.acceptable_attribute_map
29
+ attribute_map
30
+ end
31
+
32
+ # Returns all the JSON keys this model knows about
33
+ def self.acceptable_attributes
34
+ acceptable_attribute_map.values
35
+ end
36
+
37
+ # Attribute type mapping.
38
+ def self.openapi_types
39
+ {
40
+ :'name' => :'String'
41
+ }
42
+ end
43
+
44
+ # List of attributes with nullable: true
45
+ def self.openapi_nullable
46
+ Set.new([
47
+ ])
48
+ end
49
+
50
+ # Initializes the object
51
+ # @param [Hash] attributes Model attributes in the form of hash
52
+ def initialize(attributes = {})
53
+ if (!attributes.is_a?(Hash))
54
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::UpdateSandboxTemplateRequest` initialize method"
55
+ end
56
+
57
+ # check to see if the attribute exists and convert string to symbol for hash key
58
+ acceptable_attribute_map = self.class.acceptable_attribute_map
59
+ attributes = attributes.each_with_object({}) { |(k, v), h|
60
+ if (!acceptable_attribute_map.key?(k.to_sym))
61
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::UpdateSandboxTemplateRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
62
+ end
63
+ h[k.to_sym] = v
64
+ }
65
+
66
+ if attributes.key?(:'name')
67
+ self.name = attributes[:'name']
68
+ else
69
+ self.name = nil
70
+ end
71
+ end
72
+
73
+ # Show invalid properties with the reasons. Usually used together with valid?
74
+ # @return Array for valid properties with the reasons
75
+ def list_invalid_properties
76
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
77
+ invalid_properties = Array.new
78
+ if @name.nil?
79
+ invalid_properties.push('invalid value for "name", name cannot be nil.')
80
+ end
81
+
82
+ pattern = Regexp.new(/^[a-z][a-z0-9-]{0,62}$/)
83
+ if @name !~ pattern
84
+ invalid_properties.push("invalid value for \"name\", must conform to the pattern #{pattern}.")
85
+ end
86
+
87
+ invalid_properties
88
+ end
89
+
90
+ # Check to see if the all the properties in the model are valid
91
+ # @return true if the model is valid
92
+ def valid?
93
+ warn '[DEPRECATED] the `valid?` method is obsolete'
94
+ return false if @name.nil?
95
+ return false if @name !~ Regexp.new(/^[a-z][a-z0-9-]{0,62}$/)
96
+ true
97
+ end
98
+
99
+ # Custom attribute writer method with validation
100
+ # @param [Object] name Value to be assigned
101
+ def name=(name)
102
+ if name.nil?
103
+ fail ArgumentError, 'name cannot be nil'
104
+ end
105
+
106
+ pattern = Regexp.new(/^[a-z][a-z0-9-]{0,62}$/)
107
+ if name !~ pattern
108
+ fail ArgumentError, "invalid value for \"name\", must conform to the pattern #{pattern}."
109
+ end
110
+
111
+ @name = name
112
+ end
113
+
114
+ # Checks equality by comparing each attribute.
115
+ # @param [Object] Object to be compared
116
+ def ==(o)
117
+ return true if self.equal?(o)
118
+ self.class == o.class &&
119
+ name == o.name
120
+ end
121
+
122
+ # @see the `==` method
123
+ # @param [Object] Object to be compared
124
+ def eql?(o)
125
+ self == o
126
+ end
127
+
128
+ # Calculates hash code according to all attributes.
129
+ # @return [Integer] Hash code
130
+ def hash
131
+ [name].hash
132
+ end
133
+
134
+ # Builds the object from hash
135
+ # @param [Hash] attributes Model attributes in the form of hash
136
+ # @return [Object] Returns the model itself
137
+ def self.build_from_hash(attributes)
138
+ return nil unless attributes.is_a?(Hash)
139
+ attributes = attributes.transform_keys(&:to_sym)
140
+ transformed_hash = {}
141
+ openapi_types.each_pair do |key, type|
142
+ if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
143
+ transformed_hash["#{key}"] = nil
144
+ elsif type =~ /\AArray<(.*)>/i
145
+ # check to ensure the input is an array given that the attribute
146
+ # is documented as an array but the input is not
147
+ if attributes[attribute_map[key]].is_a?(Array)
148
+ transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
149
+ end
150
+ elsif !attributes[attribute_map[key]].nil?
151
+ transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
152
+ end
153
+ end
154
+ new(transformed_hash)
155
+ end
156
+
157
+ # Returns the object in the form of hash
158
+ # @return [Hash] Returns the object in the form of hash
159
+ def to_hash
160
+ hash = {}
161
+ self.class.attribute_map.each_pair do |attr, param|
162
+ value = self.send(attr)
163
+ if value.nil?
164
+ is_nullable = self.class.openapi_nullable.include?(attr)
165
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
166
+ end
167
+
168
+ hash[param] = _to_hash(value)
169
+ end
170
+ hash
171
+ end
172
+
173
+ end
174
+
175
+ end
@@ -94,6 +94,8 @@ Volcano::Generated.autoload :CreateFrontendCustomDomainRequest, 'volcano-generat
94
94
  Volcano::Generated.autoload :CreateFunctionSchedulerRequest, 'volcano-generated/models/create_function_scheduler_request'
95
95
  Volcano::Generated.autoload :CreateOAuthConfigRequest, 'volcano-generated/models/create_o_auth_config_request'
96
96
  Volcano::Generated.autoload :CreateProjectRequest, 'volcano-generated/models/create_project_request'
97
+ Volcano::Generated.autoload :CreateSandboxSessionRequest, 'volcano-generated/models/create_sandbox_session_request'
98
+ Volcano::Generated.autoload :CreateSandboxTemplateRequest, 'volcano-generated/models/create_sandbox_template_request'
97
99
  Volcano::Generated.autoload :CreateServiceKeyRequest, 'volcano-generated/models/create_service_key_request'
98
100
  Volcano::Generated.autoload :CreateStorageBucketRequest, 'volcano-generated/models/create_storage_bucket_request'
99
101
  Volcano::Generated.autoload :CreateStoragePolicyRequest, 'volcano-generated/models/create_storage_policy_request'
@@ -324,6 +326,7 @@ Volcano::Generated.autoload :ProjectSourceExportOmission, 'volcano-generated/mod
324
326
  Volcano::Generated.autoload :ProjectSourceExportSkip, 'volcano-generated/models/project_source_export_skip'
325
327
  Volcano::Generated.autoload :ProjectSourceExportState, 'volcano-generated/models/project_source_export_state'
326
328
  Volcano::Generated.autoload :ProjectUsageResponse, 'volcano-generated/models/project_usage_response'
329
+ Volcano::Generated.autoload :PublishSandboxPresetRequest, 'volcano-generated/models/publish_sandbox_preset_request'
327
330
  Volcano::Generated.autoload :RealtimeConfig, 'volcano-generated/models/realtime_config'
328
331
  Volcano::Generated.autoload :RealtimePlanLimits, 'volcano-generated/models/realtime_plan_limits'
329
332
  Volcano::Generated.autoload :RealtimeStats, 'volcano-generated/models/realtime_stats'
@@ -332,6 +335,27 @@ Volcano::Generated.autoload :ReplaceSharedVariablesRequest, 'volcano-generated/m
332
335
  Volcano::Generated.autoload :ResetDatabasePassword200Response, 'volcano-generated/models/reset_database_password200_response'
333
336
  Volcano::Generated.autoload :ResolveFunctionResponse, 'volcano-generated/models/resolve_function_response'
334
337
  Volcano::Generated.autoload :ResourceReference, 'volcano-generated/models/resource_reference'
338
+ Volcano::Generated.autoload :SandboxAccess, 'volcano-generated/models/sandbox_access'
339
+ Volcano::Generated.autoload :SandboxAccessRequest, 'volcano-generated/models/sandbox_access_request'
340
+ Volcano::Generated.autoload :SandboxCapacity, 'volcano-generated/models/sandbox_capacity'
341
+ Volcano::Generated.autoload :SandboxCapacityList, 'volcano-generated/models/sandbox_capacity_list'
342
+ Volcano::Generated.autoload :SandboxCommandRequest, 'volcano-generated/models/sandbox_command_request'
343
+ Volcano::Generated.autoload :SandboxCommandResult, 'volcano-generated/models/sandbox_command_result'
344
+ Volcano::Generated.autoload :SandboxDeployment, 'volcano-generated/models/sandbox_deployment'
345
+ Volcano::Generated.autoload :SandboxDeploymentPage, 'volcano-generated/models/sandbox_deployment_page'
346
+ Volcano::Generated.autoload :SandboxExecutionRequest, 'volcano-generated/models/sandbox_execution_request'
347
+ Volcano::Generated.autoload :SandboxExecutionResult, 'volcano-generated/models/sandbox_execution_result'
348
+ Volcano::Generated.autoload :SandboxFileReadRequest, 'volcano-generated/models/sandbox_file_read_request'
349
+ Volcano::Generated.autoload :SandboxFileResult, 'volcano-generated/models/sandbox_file_result'
350
+ Volcano::Generated.autoload :SandboxFileWriteRequest, 'volcano-generated/models/sandbox_file_write_request'
351
+ Volcano::Generated.autoload :SandboxPagination, 'volcano-generated/models/sandbox_pagination'
352
+ Volcano::Generated.autoload :SandboxPreset, 'volcano-generated/models/sandbox_preset'
353
+ Volcano::Generated.autoload :SandboxPresetList, 'volcano-generated/models/sandbox_preset_list'
354
+ Volcano::Generated.autoload :SandboxSession, 'volcano-generated/models/sandbox_session'
355
+ Volcano::Generated.autoload :SandboxSessionPage, 'volcano-generated/models/sandbox_session_page'
356
+ Volcano::Generated.autoload :SandboxSubjectGrantRequest, 'volcano-generated/models/sandbox_subject_grant_request'
357
+ Volcano::Generated.autoload :SandboxTemplate, 'volcano-generated/models/sandbox_template'
358
+ Volcano::Generated.autoload :SandboxTemplatePage, 'volcano-generated/models/sandbox_template_page'
335
359
  Volcano::Generated.autoload :ScheduleRequest, 'volcano-generated/models/schedule_request'
336
360
  Volcano::Generated.autoload :ServiceKey, 'volcano-generated/models/service_key'
337
361
  Volcano::Generated.autoload :SetProjectGitProductionBranchRequest, 'volcano-generated/models/set_project_git_production_branch_request'
@@ -360,6 +384,7 @@ Volcano::Generated.autoload :UpdateOAuthConfigRequest, 'volcano-generated/models
360
384
  Volcano::Generated.autoload :UpdateProjectGitDeploySettingsRequest, 'volcano-generated/models/update_project_git_deploy_settings_request'
361
385
  Volcano::Generated.autoload :UpdateProjectRequest, 'volcano-generated/models/update_project_request'
362
386
  Volcano::Generated.autoload :UpdateRealtimeConfigRequest, 'volcano-generated/models/update_realtime_config_request'
387
+ Volcano::Generated.autoload :UpdateSandboxTemplateRequest, 'volcano-generated/models/update_sandbox_template_request'
363
388
  Volcano::Generated.autoload :UpdateStorageBucketRequest, 'volcano-generated/models/update_storage_bucket_request'
364
389
  Volcano::Generated.autoload :UpdateVariableRequest, 'volcano-generated/models/update_variable_request'
365
390
  Volcano::Generated.autoload :UploadSessionPart, 'volcano-generated/models/upload_session_part'
@@ -388,6 +413,7 @@ Volcano::Generated.autoload :OAuthConfigurationApi, 'volcano-generated/api/o_aut
388
413
  Volcano::Generated.autoload :ProjectImportsApi, 'volcano-generated/api/project_imports_api'
389
414
  Volcano::Generated.autoload :ProjectsApi, 'volcano-generated/api/projects_api'
390
415
  Volcano::Generated.autoload :RealtimeApi, 'volcano-generated/api/realtime_api'
416
+ Volcano::Generated.autoload :SandboxesApi, 'volcano-generated/api/sandboxes_api'
391
417
  Volcano::Generated.autoload :ServiceKeysApi, 'volcano-generated/api/service_keys_api'
392
418
  Volcano::Generated.autoload :StorageAdminApi, 'volcano-generated/api/storage_admin_api'
393
419
  Volcano::Generated.autoload :StorageBucketsApi, 'volcano-generated/api/storage_buckets_api'
@@ -29,6 +29,7 @@ module Volcano
29
29
  require_relative 'generated_transport_logs'
30
30
  require_relative 'generated_transport_functions'
31
31
  require_relative 'generated_transport_durable'
32
+ require_relative 'generated_transport_sandbox'
32
33
  require_relative 'generated_transport_storage'
33
34
  require_relative 'generated_transport_upload_sessions'
34
35
 
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Volcano
4
+ # Dispatch only the generated operations exposed by the Sandbox facade.
5
+ class GeneratedTransport
6
+ SANDBOX_OPERATIONS = {
7
+ list_sandbox_presets: lambda { |api, _request|
8
+ api.list_sandbox_presets_with_http_info(debug_return_type: 'Object')
9
+ },
10
+ create_sandbox_session: lambda { |api, request|
11
+ api.create_sandbox_session_with_http_info(request.resource_id, request.request_id, request.body,
12
+ debug_return_type: 'Object')
13
+ },
14
+ execute_sandbox: lambda { |api, request|
15
+ api.execute_sandbox_with_http_info(request.resource_id, request.request_id, request.body,
16
+ debug_return_type: 'Object')
17
+ },
18
+ get_sandbox_session: lambda { |api, request|
19
+ api.get_sandbox_session_with_http_info(request.resource_id, debug_return_type: 'Object')
20
+ },
21
+ execute_sandbox_session: lambda { |api, request|
22
+ api.execute_sandbox_session_with_http_info(request.resource_id, request.request_id, request.body,
23
+ debug_return_type: 'Object')
24
+ },
25
+ suspend_sandbox_session: lambda { |api, request|
26
+ api.suspend_sandbox_session_with_http_info(request.resource_id, debug_return_type: 'Object')
27
+ },
28
+ resume_sandbox_session: lambda { |api, request|
29
+ api.resume_sandbox_session_with_http_info(request.resource_id, debug_return_type: 'Object')
30
+ },
31
+ terminate_sandbox_session: lambda { |api, request|
32
+ api.terminate_sandbox_session_with_http_info(request.resource_id, debug_return_type: 'Object')
33
+ },
34
+ create_sandbox_session_access: lambda { |api, request|
35
+ api.create_sandbox_session_access_with_http_info(request.resource_id, request.body, debug_return_type: 'Object')
36
+ },
37
+ read_sandbox_session_file: lambda { |api, request|
38
+ api.read_sandbox_session_file_with_http_info(request.resource_id, request.body, debug_return_type: 'Object')
39
+ },
40
+ write_sandbox_session_file: lambda { |api, request|
41
+ api.write_sandbox_session_file_with_http_info(request.resource_id, request.body, debug_return_type: 'Object')
42
+ },
43
+ grant_sandbox_session: lambda { |api, request|
44
+ api.grant_sandbox_session_with_http_info(request.resource_id, request.subject_id, request.body,
45
+ debug_return_type: 'Object')
46
+ },
47
+ revoke_sandbox_session: lambda { |api, request|
48
+ api.revoke_sandbox_session_with_http_info(request.resource_id, request.subject_id, debug_return_type: 'Object')
49
+ }
50
+ }.freeze
51
+ private_constant :SANDBOX_OPERATIONS
52
+
53
+ def sandbox_request(authorization:, request:)
54
+ invoke do
55
+ api = sandbox_api(authorization, request.timeout)
56
+ data, status, headers = SANDBOX_OPERATIONS.fetch(request.operation).call(api, request)
57
+ response(data, status, headers)
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def sandbox_api(authorization, timeout)
64
+ configuration = generated_configuration(authorization)
65
+ configuration.timeout = [configuration.timeout, (timeout * 1_000).round].max
66
+ Generated::SandboxesApi.new(ApiClient.new(configuration))
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+
5
+ module Volcano
6
+ # Byte-preserving files inside a Sandbox session.
7
+ class SandboxFiles
8
+ FILE_LIMIT = 8 * 1024 * 1024
9
+ private_constant :FILE_LIMIT
10
+
11
+ def initialize(requests, session_id)
12
+ @requests = requests
13
+ @session_id = session_id
14
+ end
15
+
16
+ def inspect = "#<Volcano::SandboxFiles session_id=#{@session_id.inspect}>"
17
+
18
+ def read(path)
19
+ response = @requests.call(request(:read_sandbox_session_file, { path: path }))
20
+ Base64.strict_decode64(SandboxResponse.text(Transport.json_object(response)['data']))
21
+ end
22
+
23
+ def write(path, data)
24
+ raise Error::ValidationError, 'Sandbox files are limited to 8 MiB' if data.bytesize > FILE_LIMIT
25
+
26
+ body = { path: path, data: Base64.strict_encode64(data) }
27
+ @requests.call(request(:write_sandbox_session_file, body), status: 204)
28
+ nil
29
+ end
30
+
31
+ private
32
+
33
+ def request(operation, body)
34
+ SandboxRequest.new(operation: operation, resource_id: @session_id, body: body)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Volcano
4
+ # Command output preserves nonzero exit codes as data.
5
+ SandboxCommandResult = Data.define(:stdout, :stderr, :exit_code, :timed_out, :stdout_truncated, :stderr_truncated)
6
+ # One-shot output is returned after confirmed guest reclamation.
7
+ SandboxExecutionResult = Data.define(:stdout, :stderr, :exit_code, :timed_out, :stdout_truncated,
8
+ :stderr_truncated, :session_id, :region, :duration_ms)
9
+ # A published preset and its available memory and regions.
10
+ SandboxPreset = Data.define(:id, :memory_mb, :regions)
11
+ # Expiring HTTP access credentials are redacted from inspection.
12
+ class SandboxAccess
13
+ attr_reader :url, :token, :expires_at
14
+
15
+ def initialize(url:, token:, expires_at:)
16
+ @url = url.dup.freeze
17
+ @token = token.dup.freeze
18
+ @expires_at = expires_at.dup.freeze
19
+ freeze
20
+ end
21
+
22
+ def inspect = "#<Volcano::SandboxAccess expires_at=#{expires_at.inspect}>"
23
+ end
24
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require 'time'
5
+
6
+ module Volcano
7
+ # Immutable addressing and payload for a generated Sandbox operation.
8
+ class SandboxRequest
9
+ def initialize(options)
10
+ @options = options.dup
11
+ body = options[:body]
12
+ @options[:body] = ImmutableRequestValue.capture(body) if body
13
+ @options.freeze
14
+ freeze
15
+ end
16
+
17
+ def operation = @options.fetch(:operation)
18
+ def resource_id = @options[:resource_id]
19
+ def subject_id = @options[:subject_id]
20
+ def body = @options[:body]
21
+ def request_id = @options[:request_id]
22
+ def timeout = @options.fetch(:timeout, 0)
23
+ end
24
+
25
+ # Shared validation and credentials for Sandbox facade operations.
26
+ class SandboxRequests
27
+ UUID = /\A[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}\z/i
28
+ USER_OPERATIONS = %i[get_sandbox_session execute_sandbox_session read_sandbox_session_file
29
+ write_sandbox_session_file create_sandbox_session_access].freeze
30
+ private_constant :UUID, :USER_OPERATIONS
31
+
32
+ def initialize(client, transport, service_only: false)
33
+ @client = client
34
+ @transport = transport
35
+ @service_only = service_only
36
+ end
37
+
38
+ def service_scope = SandboxRequests.new(@client, @transport, service_only: true)
39
+
40
+ def call(request, status: 200)
41
+ response = if request.operation == :list_sandbox_presets
42
+ dispatch(request, '')
43
+ elsif user_request?(request)
44
+ @client.session_request { |token| dispatch(request, token) }
45
+ else
46
+ dispatch(request, @client.service_token)
47
+ end
48
+ Transport.body(response, status)
49
+ end
50
+
51
+ def self.expiry(value)
52
+ parsed = value.is_a?(Time) ? value : Time.iso8601(value)
53
+ parsed.getutc.iso8601
54
+ rescue ArgumentError
55
+ raise Error::ValidationError, 'Sandbox grant expiry must be a Time or ISO8601 timestamp'
56
+ end
57
+
58
+ def self.identifier(value)
59
+ return value.dup.freeze if value.is_a?(String) && UUID.match?(value)
60
+
61
+ raise Error::ValidationError, 'Sandbox resource and request IDs must be UUIDs'
62
+ end
63
+
64
+ def self.request_id(options)
65
+ identifier(options.fetch(:request_id) { SecureRandom.uuid })
66
+ end
67
+
68
+ def self.selector(options)
69
+ if options.key?(:preset) == options.key?(:sandbox_id)
70
+ raise Error::ValidationError, 'Choose exactly one preset or sandbox_id'
71
+ end
72
+
73
+ result = options.slice(:region, :preset, :sandbox_id, :memory_mb)
74
+ result[:sandbox_id] = identifier(result[:sandbox_id]) if result.key?(:sandbox_id)
75
+ result
76
+ end
77
+
78
+ def self.command(command, options)
79
+ options.slice(:timeout_seconds, :environment).merge(command: command)
80
+ end
81
+
82
+ private
83
+
84
+ def user_request?(request)
85
+ !@service_only && USER_OPERATIONS.include?(request.operation) && !@client.current_session.nil?
86
+ end
87
+
88
+ def dispatch(request, token)
89
+ Transport.invoke { @transport.sandbox_request(authorization: token, request: request) }
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Volcano
4
+ # Validate wire values before exposing language-native Sandbox results.
5
+ module SandboxResponse
6
+ STATES = %w[starting running suspending suspended resuming terminating terminated unknown].freeze
7
+ private_constant :STATES
8
+
9
+ def self.text(value)
10
+ return value if value.is_a?(String)
11
+
12
+ raise TypeError, 'Invalid Sandbox text field'
13
+ end
14
+
15
+ def self.timestamp(value)
16
+ Time.iso8601(text(value)).freeze
17
+ rescue ArgumentError
18
+ raise TypeError, 'Invalid Sandbox timestamp'
19
+ end
20
+
21
+ def self.integer(value)
22
+ return value if value.is_a?(Integer)
23
+
24
+ raise TypeError, 'Invalid Sandbox integer field'
25
+ end
26
+
27
+ def self.flag(value)
28
+ return value if value.is_a?(TrueClass) || value.is_a?(FalseClass)
29
+
30
+ raise TypeError, 'Invalid Sandbox flag'
31
+ end
32
+
33
+ def self.state(value)
34
+ result = text(value)
35
+ return result.dup.freeze if STATES.include?(result)
36
+
37
+ raise TypeError, 'Invalid Sandbox state'
38
+ end
39
+
40
+ def self.command(value)
41
+ data = Transport.json_object(value)
42
+ SandboxCommandResult.new(
43
+ stdout: text(data['stdout']), stderr: text(data['stderr']), exit_code: integer(data['exit_code']),
44
+ timed_out: flag(data['timed_out']), stdout_truncated: flag(data['stdout_truncated']),
45
+ stderr_truncated: flag(data['stderr_truncated'])
46
+ )
47
+ end
48
+
49
+ def self.execution(value)
50
+ data = Transport.json_object(value)
51
+ SandboxExecutionResult.new(
52
+ **command(data).to_h, session_id: text(data['session_id']),
53
+ region: text(data['region']), duration_ms: integer(data['duration_ms'])
54
+ )
55
+ end
56
+
57
+ def self.access(value)
58
+ data = Transport.json_object(value)
59
+ SandboxAccess.new(url: text(data['url']), token: text(data['token']), expires_at: timestamp(data['expires_at']))
60
+ end
61
+
62
+ def self.preset(value)
63
+ data = Transport.json_object(value)
64
+ regions = array(data['regions']).map { |region| text(region) }.freeze
65
+ SandboxPreset.new(id: text(data['id']), memory_mb: integer(data['memory_mb']), regions: regions)
66
+ end
67
+
68
+ def self.array(value)
69
+ return value if value.is_a?(Array)
70
+
71
+ raise TypeError, 'Invalid Sandbox list'
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Volcano
4
+ # A session handle whose observed state changes only after validated responses.
5
+ class SandboxSession
6
+ attr_reader :id, :project_id, :region, :state, :expires_at, :files
7
+
8
+ def initialize(requests, value)
9
+ data = Transport.json_object(value)
10
+ @requests = requests
11
+ @id = SandboxRequests.identifier(data['id'])
12
+ @project_id = SandboxRequests.identifier(data['project_id'])
13
+ @region = SandboxResponse.text(data['region'])
14
+ assign_state(data)
15
+ @files = SandboxFiles.new(requests, @id)
16
+ end
17
+
18
+ def refresh = update(:get_sandbox_session)
19
+ def suspend = update(:suspend_sandbox_session, status: 202)
20
+ def resume = update(:resume_sandbox_session, status: 202)
21
+ def terminate = update(:terminate_sandbox_session, status: 202)
22
+
23
+ def inspect
24
+ "#<Volcano::SandboxSession id=#{@id.inspect} project_id=#{@project_id.inspect} " \
25
+ "region=#{@region.inspect} state=#{@state.inspect}>"
26
+ end
27
+
28
+ def exec(command, timeout_seconds: 60, environment: nil, request_id: nil)
29
+ options = { timeout_seconds: timeout_seconds, environment: environment, request_id: request_id }.compact
30
+ request = SandboxRequest.new(operation: :execute_sandbox_session, resource_id: @id,
31
+ body: SandboxRequests.command(command, options),
32
+ request_id: SandboxRequests.request_id(options),
33
+ timeout: SandboxResponse.integer(options.fetch(:timeout_seconds, 60)) + 120)
34
+ SandboxResponse.command(@requests.call(request))
35
+ end
36
+
37
+ def access(port)
38
+ request = SandboxRequest.new(operation: :create_sandbox_session_access, resource_id: @id, body: { port: port })
39
+ SandboxResponse.access(@requests.call(request))
40
+ end
41
+
42
+ def use
43
+ # @type var unwinding: bool
44
+ unwinding = true
45
+ result = yield self
46
+ unwinding = false
47
+ result
48
+ ensure
49
+ cleanup(unwinding) unless %w[terminating terminated].include?(@state)
50
+ end
51
+
52
+ private
53
+
54
+ def cleanup(unwinding)
55
+ terminate
56
+ rescue Error::NotFoundError
57
+ nil
58
+ rescue StandardError
59
+ raise unless unwinding
60
+ end
61
+
62
+ def update(operation, status: 200)
63
+ response = @requests.call(SandboxRequest.new(operation: operation, resource_id: @id), status: status)
64
+ data = Transport.json_object(response)
65
+ raise TypeError, 'Sandbox session identity changed' unless data['id'] == @id
66
+
67
+ assign_state(data)
68
+ self
69
+ end
70
+
71
+ def assign_state(data)
72
+ next_state = SandboxResponse.state(data['state'])
73
+ expiry = SandboxResponse.timestamp(data['expires_at'])
74
+ @state = next_state
75
+ @expires_at = expiry
76
+ end
77
+ end
78
+ end