Class: OodCore::Job::Adapters::Coder::Batch

Inherits:
Object
  • Object
show all
Defined in:
lib/ood_core/job/adapters/coder/batch.rb

Overview

Utility class for the Coder adapter to interact with the Coders API.

Defined Under Namespace

Classes: Error

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ Batch

Returns a new instance of Batch.



8
9
10
11
# File 'lib/ood_core/job/adapters/coder/batch.rb', line 8

def initialize(config)
  @host = config[:host]
  @token = config[:token]
end

Instance Method Details

#api_call(method, endpoint, headers, body = nil) ⇒ Object



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/ood_core/job/adapters/coder/batch.rb', line 138

def api_call(method, endpoint, headers, body = nil)
  uri = URI(endpoint)

  case method.downcase
  when 'get'
    request = Net::HTTP::Get.new(uri, headers)
  when 'post'
    request = Net::HTTP::Post.new(uri, headers)
  when 'delete'
    request = Net::HTTP::Delete.new(uri, headers)
  else
    raise ArgumentError, "Invalid HTTP method: #{method}"
  end

  request.body = body.to_json if body

  response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
    http.request(request)
  end

  case response
  when Net::HTTPSuccess
    JSON.parse(response.body)
  else
    raise Error, "HTTP Error: #{response.code} #{response.message}  for request #{endpoint} and body #{body}"
  end
end

#build_coder_job_info(json_data, status) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/ood_core/job/adapters/coder/batch.rb', line 93

def build_coder_job_info(json_data, status)
   = json_data["latest_build"]["resources"]
  &.find { |resource| resource["name"] == "coder_output" }
  &.dig("metadata")
  coder_output_hash = &.map { |meta| [meta["key"].to_sym, meta["value"]] }&.to_h || {}
  OodCore::Job::Adapters::Coder::CoderJobInfo.new(**{
    id: json_data["id"],
    job_name: json_data["workspace_name"],
    status: OodCore::Job::Status.new(state: status),
    job_owner: json_data["workspace_owner_name"],
    submission_time: json_data["created_at"],
    dispatch_time: json_data.dig("updated_at"),
    wallclock_time: wallclock_time(json_data, status),
    ood_connection_info: { host: coder_output_hash[:floating_ip], port: 80 },
    native: coder_output_hash
})
end

#coder_state_to_ood_status(coder_state) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/ood_core/job/adapters/coder/batch.rb', line 76

def coder_state_to_ood_status(coder_state)
  case coder_state
  when "starting"
    "queued"
  when "failed"
    "suspended"
  when "running"
    "running"
  when "deleted"
    "completed"
  when "stopped"
    "completed"
  else
    "undetermined"
  end
end

#delete(id) ⇒ Object



60
61
62
63
64
65
66
67
68
# File 'lib/ood_core/job/adapters/coder/batch.rb', line 60

def delete(id)
  endpoint = "https://#{@host}/api/v2/workspaces/#{id}/builds"
  headers = get_headers(@token)
  body = {
    'orphan' => false,
    'transition' => 'delete'
  }
  res = api_call('post', endpoint, headers, body)
end

#end_time(json_data, status) ⇒ Object



122
123
124
125
126
127
128
129
130
# File 'lib/ood_core/job/adapters/coder/batch.rb', line 122

def end_time(json_data, status)
  if status == 'deleted'
    end_time_string = json_data["latest_build"].dig("updated_at") 
    et = DateTime.parse(end_time_string).to_time.to_i
  else
    et = DateTime.now.to_time.to_i
  end
  et
end

#get_headers(coder_token) ⇒ Object



34
35
36
37
38
39
40
# File 'lib/ood_core/job/adapters/coder/batch.rb', line 34

def get_headers(coder_token)
  {
    'Content-Type' => 'application/json',
    'Accept' => 'application/json',
    'Coder-Session-Token' => coder_token
  }
end

#get_os_app_credentials(username, project_id) ⇒ Object



13
14
15
16
17
# File 'lib/ood_core/job/adapters/coder/batch.rb', line 13

def get_os_app_credentials(username, project_id)
  credentials_file = File.read("/home/#{username}/application_credentials.json")
  credentials = JSON.parse(credentials_file)
  credentials.find { |cred| cred["project_id"] == project_id }
end

#get_rich_parameters(coder_parameters, project_id, os_app_credentials) ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/ood_core/job/adapters/coder/batch.rb', line 19

def get_rich_parameters(coder_parameters, project_id, os_app_credentials)
  rich_parameter_values = [
    { name: "application_credential_name", value: os_app_credentials["name"] },
    { name: "application_credential_id", value: os_app_credentials["id"] },
    { name: "application_credential_secret", value: os_app_credentials["secret"] },
    {name: "project_id", value: project_id }
  ]
  if coder_parameters
    coder_parameters.each do |key, value|
      rich_parameter_values << { name: key, value: value.to_s}
    end
  end
  rich_parameter_values
end

#info(id) ⇒ Object



70
71
72
73
74
# File 'lib/ood_core/job/adapters/coder/batch.rb', line 70

def info(id)
  endpoint = "https://#{@host}/api/v2/workspaces/#{id}?include_deleted=true"
  headers = get_headers(@token)
  workspace_info_from_json(api_call('get', endpoint, headers))
end

#start_time(json_data) ⇒ Object



117
118
119
120
# File 'lib/ood_core/job/adapters/coder/batch.rb', line 117

def start_time(json_data)
  start_time_string = json_data.dig("updated_at")
  DateTime.parse(start_time_string).to_time.to_i
end

#submit(script) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/ood_core/job/adapters/coder/batch.rb', line 42

def submit(script)
  org_id = script.native[:org_id]
  project_id = script.native[:project_id]
  coder_parameters = script.native[:coder_parameters]
  endpoint = "https://#{@host}/api/v2/organizations/#{org_id}/members/#{username}/workspaces"
  os_app_credentials = get_os_app_credentials(username, project_id)
  headers = get_headers(@token)
  body = {
    template_id: script.native[:template_id],
    template_version_name: script.native[:template_version_name],
    name: "#{username}-#{script.native[:workspace_name]}-#{rand(2_821_109_907_456).to_s(36)}",
    rich_parameter_values: get_rich_parameters(coder_parameters, project_id, os_app_credentials),
  }

  resp = api_call('post', endpoint, headers, body)
  resp["id"]
end

#usernameObject



166
167
168
# File 'lib/ood_core/job/adapters/coder/batch.rb', line 166

def username
  @username ||= Etc.getlogin
end

#wallclock_time(json_data, status) ⇒ Object



111
112
113
114
115
# File 'lib/ood_core/job/adapters/coder/batch.rb', line 111

def wallclock_time(json_data, status)
  start_time = start_time(json_data) 
  end_time = end_time(json_data, status)
  end_time - start_time
end

#workspace_info_from_json(json_data) ⇒ Object



132
133
134
135
136
# File 'lib/ood_core/job/adapters/coder/batch.rb', line 132

def workspace_info_from_json(json_data)
  state = json_data.dig("latest_build", "status") || json_data.dig("latest_build", "job", "status")
  status = coder_state_to_ood_status(state)
  build_coder_job_info(json_data, status)
end