faastruby 0.4.18 → 0.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -3
- data/Gemfile.lock +28 -4
- data/README.md +63 -5
- data/faastruby.gemspec +5 -1
- data/lib/faastruby.rb +1 -0
- data/lib/faastruby/api.rb +154 -6
- data/lib/faastruby/base.rb +3 -9
- data/lib/faastruby/cli.rb +39 -12
- data/lib/faastruby/cli/base_command.rb +66 -0
- data/lib/faastruby/cli/commands.rb +122 -59
- data/lib/faastruby/cli/commands/account/base_command.rb +10 -0
- data/lib/faastruby/cli/commands/account/confirm.rb +94 -0
- data/lib/faastruby/cli/commands/account/login.rb +86 -0
- data/lib/faastruby/cli/commands/account/logout.rb +59 -0
- data/lib/faastruby/cli/commands/account/signup.rb +76 -0
- data/lib/faastruby/cli/commands/{function.rb → function/base_command.rb} +2 -11
- data/lib/faastruby/cli/commands/function/build.rb +18 -11
- data/lib/faastruby/cli/commands/function/deploy_to.rb +100 -37
- data/lib/faastruby/cli/commands/function/new.rb +89 -36
- data/lib/faastruby/cli/commands/function/remove_from.rb +21 -6
- data/lib/faastruby/cli/commands/function/run.rb +15 -15
- data/lib/faastruby/cli/commands/function/test.rb +5 -4
- data/lib/faastruby/cli/commands/function/update_context.rb +10 -3
- data/lib/faastruby/cli/commands/function/upgrade.rb +62 -61
- data/lib/faastruby/cli/commands/help.rb +33 -20
- data/lib/faastruby/cli/commands/project/base_command.rb +14 -0
- data/lib/faastruby/cli/commands/project/deploy.rb +114 -0
- data/lib/faastruby/cli/commands/project/down.rb +58 -0
- data/lib/faastruby/cli/commands/project/new.rb +237 -0
- data/lib/faastruby/cli/commands/workspace/cp.rb +107 -0
- data/lib/faastruby/cli/commands/workspace/create.rb +35 -27
- data/lib/faastruby/cli/commands/workspace/destroy.rb +14 -7
- data/lib/faastruby/cli/commands/workspace/list.rb +15 -6
- data/lib/faastruby/cli/commands/workspace/migrate.rb +93 -0
- data/lib/faastruby/cli/commands/workspace/rm.rb +81 -0
- data/lib/faastruby/cli/commands/workspace/update.rb +62 -0
- data/lib/faastruby/cli/credentials.rb +58 -57
- data/lib/faastruby/cli/new_credentials.rb +63 -0
- data/lib/faastruby/cli/package.rb +1 -0
- data/lib/faastruby/cli/template.rb +7 -7
- data/lib/faastruby/local.rb +188 -0
- data/lib/faastruby/local/crystal_runtime.cr +170 -0
- data/lib/faastruby/local/functions.rb +7 -0
- data/lib/faastruby/local/functions/crystal.rb +64 -0
- data/lib/faastruby/local/functions/function.rb +173 -0
- data/lib/faastruby/local/functions/ruby.rb +28 -0
- data/lib/faastruby/local/listeners.rb +5 -0
- data/lib/faastruby/local/listeners/listener.rb +104 -0
- data/lib/faastruby/local/logger.rb +37 -0
- data/lib/faastruby/local/monkey_patch.rb +38 -0
- data/lib/faastruby/local/processors.rb +7 -0
- data/lib/faastruby/local/processors/function.rb +151 -0
- data/lib/faastruby/local/processors/processor.rb +116 -0
- data/lib/faastruby/local/processors/static_file.rb +48 -0
- data/lib/faastruby/local/static_files.rb +5 -0
- data/lib/faastruby/local/static_files/static_file.rb +59 -0
- data/lib/faastruby/server.rb +44 -3
- data/lib/faastruby/server/app.rb +107 -0
- data/lib/faastruby/server/concurrency_controller.rb +50 -50
- data/lib/faastruby/server/config.ru +2 -0
- data/lib/faastruby/server/event.rb +3 -0
- data/lib/faastruby/server/event_hub.rb +7 -6
- data/lib/faastruby/server/local.rb +22 -0
- data/lib/faastruby/server/logger.rb +50 -0
- data/lib/faastruby/server/project_config.rb +44 -0
- data/lib/faastruby/server/puma.rb +4 -0
- data/lib/faastruby/server/response.rb +40 -0
- data/lib/faastruby/server/runner.rb +116 -21
- data/lib/faastruby/server/runner_methods.rb +17 -16
- data/lib/faastruby/server/sentinel.rb +496 -0
- data/lib/faastruby/supported_runtimes.rb +8 -0
- data/lib/faastruby/user.rb +77 -0
- data/lib/faastruby/version.rb +1 -1
- data/lib/faastruby/workspace.rb +36 -3
- data/templates/crystal/example-blank/handler.cr +3 -0
- data/templates/crystal/example/spec/handler_spec.cr +11 -6
- data/templates/public-web/assets/images/background.png +0 -0
- data/templates/public-web/assets/images/ruby.png +0 -0
- data/templates/public-web/assets/javascripts/main.js +1 -0
- data/templates/public-web/assets/stylesheets/main.css +70 -0
- data/templates/public-web/favicon.ico +0 -0
- data/templates/ruby/api-404/handler.rb +6 -0
- data/templates/ruby/api-root/handler.rb +6 -0
- data/templates/ruby/example-blank/handler.rb +0 -23
- data/templates/ruby/web-404/404.html +36 -0
- data/templates/ruby/web-404/handler.rb +3 -0
- data/templates/ruby/web-root/handler.rb +10 -0
- data/templates/ruby/web-root/index.html.erb +37 -0
- data/templates/ruby/web-root/template.rb +13 -0
- metadata +102 -21
- data/exe/faastruby-server +0 -76
- data/lib/faastruby/cli/commands/credentials.rb +0 -11
- data/lib/faastruby/cli/commands/credentials/add.rb +0 -58
- data/lib/faastruby/cli/commands/credentials/list.rb +0 -58
- data/lib/faastruby/cli/commands/workspace.rb +0 -13
- data/lib/faastruby/cli/commands/workspace/deploy.rb +0 -50
- data/templates/crystal/example-blank/README.md +0 -22
- data/templates/crystal/example-blank/spec/handler_spec.cr +0 -8
- data/templates/crystal/example-blank/spec/spec_helper.cr +0 -4
- data/templates/crystal/example-blank/src/handler.cr +0 -25
- data/templates/ruby/example-blank/Gemfile +0 -7
- data/templates/ruby/example-blank/README.md +0 -22
- data/templates/ruby/example-blank/spec/handler_spec.rb +0 -16
- data/templates/ruby/example-blank/spec/spec_helper.rb +0 -3
data/exe/faastruby-server
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
require 'yaml'
|
|
3
|
-
require 'oj'
|
|
4
|
-
require 'faastruby-rpc'
|
|
5
|
-
require 'base64'
|
|
6
|
-
require 'faastruby/server'
|
|
7
|
-
require 'sinatra'
|
|
8
|
-
require 'sinatra/multi_route'
|
|
9
|
-
require 'colorize'
|
|
10
|
-
|
|
11
|
-
FaaStRuby::EventHub.listen_for_events!
|
|
12
|
-
|
|
13
|
-
set :port, 3000
|
|
14
|
-
set :bind, '0.0.0.0'
|
|
15
|
-
case ARGV.shift
|
|
16
|
-
when '-p'
|
|
17
|
-
set :port, ARGV.shift
|
|
18
|
-
when '-b', '-o'
|
|
19
|
-
set :bind, ARGV.shift
|
|
20
|
-
end
|
|
21
|
-
set :server, %w[puma]
|
|
22
|
-
set :show_exceptions, true
|
|
23
|
-
set :run, true
|
|
24
|
-
|
|
25
|
-
register Sinatra::MultiRoute
|
|
26
|
-
|
|
27
|
-
route :head, :get, :post, :put, :patch, :delete, '/:workspace_name/:function_name' do
|
|
28
|
-
path = "#{params[:workspace_name]}/#{params[:function_name]}"
|
|
29
|
-
headers = env.select { |key, value| key.include?('HTTP_') || ['CONTENT_TYPE', 'CONTENT_LENGTH', 'REMOTE_ADDR', 'REQUEST_METHOD', 'QUERY_STRING'].include?(key) }
|
|
30
|
-
if headers.has_key?("HTTP_FAASTRUBY_RPC")
|
|
31
|
-
body = nil
|
|
32
|
-
rpc_args = parse_body(request.body.read, headers['CONTENT_TYPE'], request.request_method) || []
|
|
33
|
-
else
|
|
34
|
-
body = parse_body(request.body.read, headers['CONTENT_TYPE'], request.request_method)
|
|
35
|
-
rpc_args = []
|
|
36
|
-
end
|
|
37
|
-
query_params = parse_query(request.query_string)
|
|
38
|
-
context = set_context(params[:workspace_name], params[:function_name])
|
|
39
|
-
event = FaaStRuby::Event.new(body: body, query_params: query_params, headers: headers, context: context)
|
|
40
|
-
response = FaaStRuby::Runner.new.call(params[:workspace_name], params[:function_name], event, rpc_args)
|
|
41
|
-
status response.status
|
|
42
|
-
headers response.headers
|
|
43
|
-
if response.binary?
|
|
44
|
-
response_body = Base64.urlsafe_decode64(response.body)
|
|
45
|
-
else
|
|
46
|
-
response_body = response.body
|
|
47
|
-
end
|
|
48
|
-
puts "[#{path}] #=> status=#{response.status} body=#{response_body.inspect} headers=#{Oj.dump response.headers}".light_blue
|
|
49
|
-
body response_body
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def parse_body(body, content_type, method)
|
|
53
|
-
return nil if method == 'GET'
|
|
54
|
-
return {} if body.nil? && method != 'GET'
|
|
55
|
-
return Oj.load(body) if content_type == 'application/json'
|
|
56
|
-
return body
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def set_context(workspace_name, function_name)
|
|
60
|
-
return nil unless File.file?('context.yml')
|
|
61
|
-
yaml = YAML.load(File.read('context.yml'))
|
|
62
|
-
return nil unless yaml.has_key?(workspace_name)
|
|
63
|
-
yaml[workspace_name][function_name]
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def parse_query(query_string)
|
|
67
|
-
hash = {}
|
|
68
|
-
query_string.split('&').each do |param|
|
|
69
|
-
key, value = param.split('=')
|
|
70
|
-
hash[key] = value
|
|
71
|
-
end
|
|
72
|
-
hash
|
|
73
|
-
end
|
|
74
|
-
def self.run?
|
|
75
|
-
true
|
|
76
|
-
end
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
module FaaStRuby
|
|
2
|
-
module Command
|
|
3
|
-
module Credentials
|
|
4
|
-
class Add < CredentialsBaseCommand
|
|
5
|
-
def initialize(args)
|
|
6
|
-
@args = args
|
|
7
|
-
@workspace_name = @args.shift
|
|
8
|
-
FaaStRuby::CLI.error(['Missing argument: WORKSPACE_NAME'.red, usage], color: nil) if @workspace_name.nil? || @workspace_name =~ /^-.*/
|
|
9
|
-
parse_options
|
|
10
|
-
@options['credentials_file'] ||= FaaStRuby.credentials_file
|
|
11
|
-
@missing_args = []
|
|
12
|
-
FaaStRuby::CLI.error(@missing_args, color: nil) if missing_args.any?
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def run
|
|
16
|
-
new_credentials = {'api_key' => @options['api_key'], 'api_secret' => @options['api_secret']}
|
|
17
|
-
FaaStRuby::Credentials.add(@workspace_name, new_credentials, @options['credentials_file'])
|
|
18
|
-
puts "Credentials file updated."
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def self.help
|
|
22
|
-
"add-credentials".light_cyan + " WORKSPACE_NAME -k API_KEY -s API_SECRET [-c CREDENTIALS_FILE]"
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def usage
|
|
26
|
-
"Usage: faastruby #{self.class.help}"
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
private
|
|
30
|
-
|
|
31
|
-
def missing_args
|
|
32
|
-
@missing_args << "Missing argument: API_KEY".red unless @options['api_key']
|
|
33
|
-
@missing_args << "Missing argument: API_SECRET".red unless @options['api_secret']
|
|
34
|
-
@missing_args << "Missing argument: CREDENTIALS_FILE".red unless @options['credentials_file']
|
|
35
|
-
@missing_args << usage if @missing_args.any?
|
|
36
|
-
@missing_args
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def parse_options
|
|
40
|
-
@options = {}
|
|
41
|
-
while @args.any?
|
|
42
|
-
option = @args.shift
|
|
43
|
-
case option
|
|
44
|
-
when '-k', '--api-key'
|
|
45
|
-
@options['api_key'] = @args.shift
|
|
46
|
-
when '-c', '--credentials-file'
|
|
47
|
-
@options['credentials_file'] = @args.shift
|
|
48
|
-
when '-s', '--api-secret'
|
|
49
|
-
@options['api_secret'] = @args.shift
|
|
50
|
-
else
|
|
51
|
-
FaaStRuby::CLI.error(["Unknown argument: #{option}".red, usage], color: nil)
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
module FaaStRuby
|
|
2
|
-
module Command
|
|
3
|
-
module Credentials
|
|
4
|
-
class List < CredentialsBaseCommand
|
|
5
|
-
def initialize(args)
|
|
6
|
-
@args = args
|
|
7
|
-
parse_options
|
|
8
|
-
@options['credentials_file'] ||= FaaStRuby.credentials_file
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def run
|
|
12
|
-
FaaStRuby::CLI.error("The file '#{@options['credentials_file']}' does not exist.") unless File.file?(@options['credentials_file'])
|
|
13
|
-
rows = []
|
|
14
|
-
credentials = FaaStRuby::Credentials.load_credentials_file(@options['credentials_file'])
|
|
15
|
-
FaaStRuby::CLI.error("The credentials file '#{@options['credentials_file']}' is empty.") unless credentials.any?
|
|
16
|
-
credentials.each do |workspace, credentials|
|
|
17
|
-
if credentials
|
|
18
|
-
rows << [workspace, credentials['api_key'], credentials['api_secret']]
|
|
19
|
-
else
|
|
20
|
-
FaaStRuby::Credentials.remove(workspace, @options['credentials_file'])
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
table = TTY::Table.new(['Workspace','API_KEY', 'API_SECRET'], rows)
|
|
24
|
-
puts table.render(:basic)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def self.help
|
|
28
|
-
"list-credentials".light_cyan + " [-c CREDENTIALS_FILE]"
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def usage
|
|
32
|
-
"Usage: faastruby #{self.class.help}"
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
private
|
|
36
|
-
|
|
37
|
-
def missing_args
|
|
38
|
-
@missing_args << "Missing argument: CREDENTIALS_FILE".red unless @options['credentials_file']
|
|
39
|
-
@missing_args << usage if @missing_args.any?
|
|
40
|
-
@missing_args
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def parse_options
|
|
44
|
-
@options = {}
|
|
45
|
-
while @args.any?
|
|
46
|
-
option = @args.shift
|
|
47
|
-
case option
|
|
48
|
-
when '-c', '--credentials-file'
|
|
49
|
-
@options['credentials_file'] = @args.shift
|
|
50
|
-
else
|
|
51
|
-
FaaStRuby::CLI.error(["Unknown argument: #{option}".red, usage], color: nil)
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
module FaaStRuby
|
|
2
|
-
module Command
|
|
3
|
-
module Workspace
|
|
4
|
-
class WorkspaceBaseCommand < BaseCommand
|
|
5
|
-
end
|
|
6
|
-
end
|
|
7
|
-
end
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
require 'faastruby/cli/commands/workspace/create'
|
|
11
|
-
require 'faastruby/cli/commands/workspace/destroy'
|
|
12
|
-
require 'faastruby/cli/commands/workspace/list'
|
|
13
|
-
require 'faastruby/cli/commands/workspace/deploy'
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
module FaaStRuby
|
|
2
|
-
module Command
|
|
3
|
-
module Workspace
|
|
4
|
-
class Deploy < WorkspaceBaseCommand
|
|
5
|
-
def initialize(args)
|
|
6
|
-
@errors = []
|
|
7
|
-
if args.any?
|
|
8
|
-
@args = args
|
|
9
|
-
else
|
|
10
|
-
@args = Dir.glob('*').select{|f| File.directory?(f)}
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def run
|
|
15
|
-
result = []
|
|
16
|
-
errors = false
|
|
17
|
-
@args.each do |workspace|
|
|
18
|
-
Dir.chdir workspace
|
|
19
|
-
functions = Dir.glob('*').select{|f| File.directory?(f)}
|
|
20
|
-
functions.each do |function|
|
|
21
|
-
puts "[deploy] Entering folder #{workspace}/#{function}"
|
|
22
|
-
Dir.chdir function
|
|
23
|
-
if system("faastruby deploy-to #{workspace}")
|
|
24
|
-
result << "* #{workspace}/#{function} [Deploy OK]".green
|
|
25
|
-
else
|
|
26
|
-
result << "* #{workspace}/#{function} [Deploy FAILED]".red
|
|
27
|
-
errors = true
|
|
28
|
-
end
|
|
29
|
-
Dir.chdir '..'
|
|
30
|
-
end
|
|
31
|
-
Dir.chdir '..'
|
|
32
|
-
end
|
|
33
|
-
puts "\nResult:"
|
|
34
|
-
FaaStRuby::CLI.error(result, color: nil) if errors
|
|
35
|
-
puts result
|
|
36
|
-
exit 0
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def self.help
|
|
40
|
-
"deploy".light_cyan + " [WORKSPACE_FOLDER1] [WORKSPACE_FOLDER2]... # Deploy all workspaces in the current directory and their functions"
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def usage
|
|
44
|
-
"Usage: faastruby #{self.class.help}"
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# <%= @function_name %>
|
|
2
|
-
One Paragraph of function description goes here.
|
|
3
|
-
## Getting started
|
|
4
|
-
### Prerequisites
|
|
5
|
-
* [FaaStRuby](https://faastruby.io) version <%= FaaStRuby::VERSION %> or higher.
|
|
6
|
-
```
|
|
7
|
-
# To install
|
|
8
|
-
~$ gem install faastruby
|
|
9
|
-
|
|
10
|
-
# To update
|
|
11
|
-
~$ gem update faastruby
|
|
12
|
-
```
|
|
13
|
-
### How to deploy this function
|
|
14
|
-
```
|
|
15
|
-
~$ cd FUNCTION_FOLDER
|
|
16
|
-
~/FUNCTION_FOLDER$ faastruby deploy-to WORKSPACE_NAME
|
|
17
|
-
```
|
|
18
|
-
### How to use this function as a template
|
|
19
|
-
```
|
|
20
|
-
~$ faastruby new FUNCTION_NAME --template github:user/repository
|
|
21
|
-
```
|
|
22
|
-
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# To deploy this function, cd into its folder and run:
|
|
2
|
-
# faastruby deploy-to WORKSPACE_NAME
|
|
3
|
-
def handler(event : FaaStRuby::Event) : FaaStRuby::Response
|
|
4
|
-
# event.body : String | Nil
|
|
5
|
-
# event.headers : Hash(String, String)
|
|
6
|
-
# event.context : String | Nil
|
|
7
|
-
# query_params : Hash(String, String)
|
|
8
|
-
|
|
9
|
-
# FUNCTION RESPONSE
|
|
10
|
-
#
|
|
11
|
-
# You can render text, json, yaml, html or js. Example:
|
|
12
|
-
# render html: "<p>Hello World!</p>"
|
|
13
|
-
# render yaml: {"hello" => "world!"}
|
|
14
|
-
#
|
|
15
|
-
# Status:
|
|
16
|
-
# The default status is 200. You can set a custom status like this:
|
|
17
|
-
# render json: {"error" => "Could not perform the action"}, status: 422
|
|
18
|
-
#
|
|
19
|
-
# Headers:
|
|
20
|
-
# The 'Content-Type' header is automatically set when you use 'render'.
|
|
21
|
-
# You can set custom headers using a Hash(String, String). Example:
|
|
22
|
-
# render text: "It Works!", headers: {"TransactionId" => 23928}
|
|
23
|
-
|
|
24
|
-
# TODO: Write code here!
|
|
25
|
-
end
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# <%= @function_name %>
|
|
2
|
-
One Paragraph of function description goes here.
|
|
3
|
-
## Getting started
|
|
4
|
-
### Prerequisites
|
|
5
|
-
* [FaaStRuby](https://faastruby.io) version <%= FaaStRuby::VERSION %> or higher.
|
|
6
|
-
```
|
|
7
|
-
# To install
|
|
8
|
-
~$ gem install faastruby
|
|
9
|
-
|
|
10
|
-
# To update
|
|
11
|
-
~$ gem update faastruby
|
|
12
|
-
```
|
|
13
|
-
### How to deploy this function
|
|
14
|
-
```
|
|
15
|
-
~$ cd FUNCTION_FOLDER
|
|
16
|
-
~/FUNCTION_FOLDER$ faastruby deploy-to WORKSPACE_NAME
|
|
17
|
-
```
|
|
18
|
-
### How to use this function as a template
|
|
19
|
-
```
|
|
20
|
-
~$ faastruby new FUNCTION_NAME --template github:user/repository
|
|
21
|
-
```
|
|
22
|
-
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
require 'handler'
|
|
3
|
-
|
|
4
|
-
describe 'handler(event)' do
|
|
5
|
-
let(:event) {Event.new(
|
|
6
|
-
body: nil,
|
|
7
|
-
query_params: {},
|
|
8
|
-
headers: {},
|
|
9
|
-
context: nil
|
|
10
|
-
)}
|
|
11
|
-
|
|
12
|
-
xit 'write some tests here' do
|
|
13
|
-
# function_return = handler(event).call
|
|
14
|
-
expect(true).to eq(false)
|
|
15
|
-
end
|
|
16
|
-
end
|