Skip to content

Commit bfa65ea

Browse files
committed
Add nvidia-smi function
Signed-off-by: Han Verstraete (OpenFaaS Ltd) <[email protected]>
1 parent dab6684 commit bfa65ea

File tree

4 files changed

+159
-2
lines changed

4 files changed

+159
-2
lines changed

invoke-function/Gemfile

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
source 'https://rubygems.org'
2+

invoke-function/handler.rb

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
require 'net/http'
2+
require 'uri'
3+
require 'json'
4+
5+
class Handler
6+
def run(req)
7+
begin
8+
# Fetch a token from an Idp
9+
token = fetch_oauth_token
10+
11+
# Exchange the token for on OpenFaaS function token
12+
openfaas_token = exchange_oauth_token(token)
13+
puts openfaas_token
14+
15+
# Invoke a function with the token
16+
result = invoke_function(openfaas_token)
17+
return result
18+
rescue => e
19+
return "Error: #{e.message}"
20+
end
21+
end
22+
end
23+
24+
# Function to fetch OAuth token using client credentials
25+
def fetch_oauth_token
26+
# Retrieve client_id and client_secret from environment variables
27+
client_id = ENV['CLIENT_ID']
28+
client_secret = File.read("./client-secret.txt")
29+
30+
# Check if the client_id and client_secret are present
31+
if client_id.nil? || client_secret.nil?
32+
raise "Client ID or Client Secret is not set in environment variables."
33+
end
34+
35+
# OAuth authority token endpoint
36+
token_url = "https://auth.exit.welteki.dev/realms/openfaas/protocol/openid-connect/token"
37+
38+
# Create URI object
39+
uri = URI(token_url)
40+
41+
# Create the HTTP object
42+
http = Net::HTTP.new(uri.host, uri.port)
43+
http.use_ssl = true if uri.scheme == 'https'
44+
45+
# Define the request parameters
46+
request_params = {
47+
'grant_type' => 'client_credentials',
48+
'client_id' => client_id,
49+
'client_secret' => client_secret
50+
}
51+
52+
# Create the POST request
53+
request = Net::HTTP::Post.new(uri.request_uri)
54+
request.set_form_data(request_params)
55+
request['Content-Type'] = 'application/x-www-form-urlencoded'
56+
57+
# Execute the request
58+
response = http.request(request)
59+
60+
# Parse the response
61+
result = JSON.parse(response.body)
62+
63+
# Check the response for errors
64+
if response.code != '200'
65+
puts "Error: #{response.code}"
66+
raise "Failed to retrieve token: #{result['error_description'] || 'Unknown error'}"
67+
end
68+
69+
# Return the access token
70+
result['access_token']
71+
end
72+
73+
# Function to exchange an OAuth token for an OpenFaaS function token
74+
def exchange_oauth_token(token)
75+
# OAuth authority token endpoint
76+
token_url = "https://gw.exit.welteki.dev/oauth/token"
77+
78+
# Create URI object
79+
uri = URI(token_url)
80+
81+
# Create the HTTP object
82+
http = Net::HTTP.new(uri.host, uri.port)
83+
http.use_ssl = true if uri.scheme == 'https'
84+
85+
# Define the request parameters
86+
request_params = {
87+
'subject_token' => token,
88+
'subject_token_type' => 'urn:ietf:params:oauth:token-type:id_token',
89+
'grant_type' => 'urn:ietf:params:oauth:grant-type:token-exchange',
90+
'scope' => 'functions'
91+
}
92+
93+
# Create the POST request
94+
request = Net::HTTP::Post.new(uri.request_uri)
95+
request.set_form_data(request_params)
96+
request['Content-Type'] = 'application/x-www-form-urlencoded'
97+
98+
# Execute the request
99+
response = http.request(request)
100+
101+
# Parse the response
102+
result = JSON.parse(response.body)
103+
104+
# Check the response for errors
105+
if response.code != '200'
106+
puts "Error: #{response.code}"
107+
raise "Failed to retrieve token: #{result['error_description'] || 'Unknown error'}"
108+
end
109+
110+
# Return the access token
111+
result['access_token']
112+
end
113+
114+
def invoke_function(token)
115+
function_url = "https://gw.exit.welteki.dev/function/env"
116+
117+
# Create URI object
118+
uri = URI(function_url)
119+
120+
# Create the HTTP object
121+
http = Net::HTTP.new(uri.host, uri.port)
122+
http.use_ssl = true if uri.scheme == 'https'
123+
124+
# Create the GET request
125+
request = Net::HTTP::Get.new(uri.request_uri)
126+
request['Authorization'] = "Bearer: #{token}"
127+
128+
# Execute the request
129+
response = http.request(request)
130+
131+
# Check the response for errors
132+
if response.code != '200'
133+
puts "Error: #{response.code}"
134+
raise "Failed to invoke function: code #{response.code}, body: #{response.body}"
135+
end
136+
137+
# Return response body
138+
response.body
139+
end

nvidia-smi/Dockerfile

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
FROM ghcr.io/openfaas/classic-watchdog:0.3.2 AS watchdog
2+
3+
FROM ubuntu:24.04
4+
5+
COPY --from=watchdog /fwatchdog /usr/bin/fwatchdog
6+
RUN chmod +x /usr/bin/fwatchdog
7+
8+
USER 1000
9+
10+
ENV fprocess="nvidia-smi"
11+
12+
HEALTHCHECK --interval=3s CMD [ -e /tmp/.lock ] || exit 1
13+
CMD ["fwatchdog"]

stack.yml

+5-2
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,14 @@ functions:
100100
lang: golang-middleware
101101
handler: ./markdown
102102
image: ${SERVER:-ghcr.io}/${OWNER:-openfaas}/markdown-fn:${TAG:-latest}
103+
104+
nvidia-smi:
105+
lang: dockerfile
106+
handler: ./nvidia-smi
107+
image: ${SERVER:-ghcr.io}/${OWNER:-openfaas}/nvidia-smi:${TAG:-latest}
103108

104109

105110
configuration:
106111
templates:
107112
- name: golang-middleware
108113
source: https://github.com/openfaas/golang-http-template
109-
110-

0 commit comments

Comments
 (0)