How to run multiple Rack::Handler::WEBrick in one Sinatra/Grape file - sinatra

Basically I have multiple API applications in one Sinatra / GrapeAPI file
require 'sinatra'
require 'grape'
require 'webrick'
require 'webrick/https'
require 'openssl'
CERT_PATH = '/opt/myCA/server/'
class WebApp < Sinatra::Base
post '/' do
"Hellow, world!"
end
end
class Api1 < Grape::API
get '/test1' do
{xyz: 'test1' }
end
end
class Api2 < Grape::API
get '/test2' do
{xyz: 'test2' }
end
end
webrick_options1 = {
:Port => 8443,
:Logger => WEBrick::Log::new($stderr, WEBrick::Log::DEBUG),
:DocumentRoot => "/ruby/htdocs",
:SSLEnable => true,
:SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE,
:SSLCertificate => OpenSSL::X509::Certificate.new( File.open(File.join(CERT_PATH, "my-server.crt")).read),
:SSLPrivateKey => OpenSSL::PKey::RSA.new( File.open(File.join(CERT_PATH, "my-server.key")).read),
:SSLCertName => [ [ "CN",WEBrick::Utils::getservername ] ]
}
webrick_options2 = {...}
Rack::Handler::WEBrick.run Api1, webrick_options1 # this will work
Rack::Handler::WEBrick.run Api2, webrick_options2 # but when I try another one,
# the other will not
Grape gem is recommending to use cascade:
run Rack::Cascade.new [Api1, Api2, WebApp]
However this will ignore my precious SSL settings.
How can I run multiple server configurations?

Related

Error when using the SoftLayer Ruby API to specify a flavor

I have a working ruby script that we have been using for a quite a while to order VSIs from SoftLayer. The script specifies a certain price item for CPU, one for memory, and another for disk. I am trying to modify the script to work with flavors but I have been unable to figure out what I am doing wrong. Basically I have removed the CPU, memory, and disk price items from the product order and added in a flavorKeyName in the supplementalCreateObjectOptions like this:
#!/usr/bin/ruby
require 'softlayer_api'
client = SoftLayer::Client.new(username: 'XXXXX', api_key: 'XXXXX')
productOrder = {
'virtualGuests' => [{
'hostname' => 'test',
'domain' => 'mycompany.com',
'primaryNetworkComponent' => { 'networkVlan' => { 'id' => XXXXXX } },
'primaryBackendNetworkComponent' => { 'networkVlan' => { 'id' => XXXXXX },
'supplementalCreateObjectOptions' => { 'flavorKeyName' => 'B1_1X2X100' } }
}],
'location' => XXXXXX,
'packageId' => 46,
'imageTemplateId' => XXXXX,
'useHourlyPricing' => true,
'prices' => [
{'id' => 34183 }, # 0 GB Bandwidth
{'id' => 24713 }, # 1 Gbps Public & Private Network Uplinks
{'id' => 34807 }, # 1 IP Address
{'id' => 33483 }, # Unlimited SSL VPN Users & 1 PPTP VPN User per account
{'id' => 34241 }, # Host Ping and TCP Service Monitoring
{'id' => 32500 }, # Email and Ticket
{'id' => 35310 }, # NESSUS_VULNERABILITY_ASSESSMENT_REPORTING
{'id' => 23070 }, # REBOOT_REMOTE_CONSOLE
{'id' => 32627 } # AUTOMATED_NOTIFICATION
]
}
order = client['Product_Order'].verifyOrder(productOrder)
but this fails with:
/usr/lib64/ruby/2.1.0/xmlrpc/client.rb:271:in `call': Internal Error (XMLRPC::FaultException)
from /usr/lib64/ruby/gems/2.1.0/gems/softlayer_api-3.2.2/lib/softlayer/Service.rb:269:in `call_softlayer_api_with_params'
from /usr/lib64/ruby/gems/2.1.0/gems/softlayer_api-3.2.2/lib/softlayer/Service.rb:198:in `method_missing'
from /tmp/yy2:34:in `<main>'
The error is not too helpful on what I might be specifying incorrectly or might be missing.
Does any one have a suggestions on what I might be doing wrong?
When using Softlayer_Product_Order::verifyOrder or Softlayer_Product_Order::placeOrder you need to use the package 835, and set the presetId parameter to specify what flavor configuration you want to order.
The supplementalCreateObjectOptions parameter is specified when using the SoftLayer_Virtual_Guest::createObject method.
Following are two ways to order virtual guest devices with a flavor configuration.
PlaceOrder
To get the list of available preset ids for package 835 you need to use the method SoftLayer_Product_Package::getActivePresets.
https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/835/getActivePresets
Check the keyName values to know which are Balanced, Memory, etc., they should start with:
B1 is for "Balanced"
BL1 is for "Balanced Local Storage"
BL2 is for "Balanced Local Storage - SSD"
C1 is for "Compute"
M1 is for "Memory"
These characters are followed by a short description of VSI configuration as following:
C1_2X2X100 for Compute VSI with "2 x 2.0 GHz Cores, 2 GB RAM, 100 GB Disk"
B1_8X16X25 for Balanced VSI with "8 x 2.0 GHz Cores, 16 GB RAM, 25 GB Disk"
If I'm not wrong the presetId 333 is for B1_1X2X100 which is the flavor configuration you want.
require 'rubygems'
require 'softlayer_api'
require 'json'
# Your SoftLayer API username and API Key.
USERNAME = 'set-me'
API_KEY = 'set-me'
# Location where server will be provisioned.
location = 'AMSTERDAM03'
# The id of the SoftLayer_Product_Package, use the 835 for VSI Families.
package_id = 835
# Following is the preset id used to complete this example.
preset_id = 333 # B1_1X2X100 (1 x 2.0 GHz Cores, 2 GB RAM, and primary disk of 25 GB)
# The number of servers you wish to order in this configuration.
quantity = 1
# Build a skeleton SoftLayer_Virtual_Guest object. If you set quantity greater than 1
# then you need to define one hostname/domain per server you wish to order.
virtual_guest = [
{
'hostname' => 'test-vsi',
'domain' => 'mycompany.com',
'primaryNetworkComponent' => { 'networkVlan' => { 'id' => 11111 } },
'primaryBackendNetworkComponent' => { 'networkVlan' => { 'id' => 22222 }}
}
]
# Specify the item prices. Note that you don't need to specify the item price for
# cpus, ram, and primary disk, and take into account that “Balanced Local Storage”
# and “Balanced Local Storage - SSD” requires a second disk, the system will select one
# if you don’t specify it.
prices = [
{'id' => 34183 }, # 0 GB Bandwidth
{'id' => 24713 }, # 1 Gbps Public & Private Network Uplinks
{'id' => 34807 }, # 1 IP Address
{'id' => 33483 }, # Unlimited SSL VPN Users & 1 PPTP VPN User per account
{'id' => 34241 }, # Host Ping and TCP Service Monitoring
{'id' => 32500 }, # Email and Ticket
{'id' => 35310 }, # NESSUS_VULNERABILITY_ASSESSMENT_REPORTING
{'id' => 23070 }, # REBOOT_REMOTE_CONSOLE
{'id' => 32627 } # AUTOMATED_NOTIFICATION
]
# Build a skeleton SoftLayer_Container_Product_Order object containing the order
# you wish to place.
order_template = {
'quantity' => quantity,
'location' => location,
'packageId' => package_id,
'presetId' => preset_id,
'imageTemplateId' => 1111111,
'useHourlyPricing' => true,
'prices' => prices,
'virtual_guest' => virtual_guest
}
# Declare the API client to use the SoftLayer_Product_Order API service
client = SoftLayer::Client.new(username: USERNAME, api_key: API_KEY)
product_order_service = client.service_named('SoftLayer_Product_Order')
begin
# verifyOrder() will check your order for errors. Replace this with placeOrder()
# when you're ready to order.
receipt = product_order_service.verifyOrder(order_template)
puts JSON.pretty_generate(receipt)
rescue StandardError => exception
puts "There was an error in your order: #{exception}"
end
CreateObject
Take account that createObject method is a simplified way to order virtual guest devices so you may not be able to set items like IPV6, secondary IP address, etc. See SoftLayer_Virtual_Guest::createObject to know which properties you can set.
The following example is to order a vsi family with flavor configuration B1_1X2X100, on this case it is necessary to set the parameter supplementalCreateObjectOptions
require 'rubygems'
require 'softlayer_api'
require 'json'
# Your SoftLayer API username and API Key.
USERNAME = 'set-me'
API_KEY = 'set-me'
# Build the skeleton of SoftLayer_Virtual_Guest object.
virtual_guest_template = {
'hostname' => 'test-vsi',
'domain' => 'mycompany.com',
'primaryNetworkComponent' => { 'networkVlan' => { 'id' => 11111 } },
'primaryBackendNetworkComponent' => { 'networkVlan' => { 'id' => 22222 }},
'datacenter' => { 'name' => 'dal05' },
'supplementalCreateObjectOptions' => {
'flavorKeyName' => 'B1_1X2X100'
},
'hourlyBillingFlag' => true,
# Following is to specify the imageTemplate you want to use. But on this case you need
# to set the globalIdentifier of imageTemplate.
'blockDeviceTemplateGroup' => {
'globalIdentifier' => '6x06c3x8-4158-4b69-ba5x-433c18x3xac3'
},
'networkComponents' => [
{ 'maxSpeed' => 1000} # 1 Gbps Public & Private Network Uplinks
]
}
# Declare the API client to use the SoftLayer_Virtual_Guest API service
client = SoftLayer::Client.new(username: USERNAME, api_key: API_KEY)
virtual_guest_service = client['SoftLayer_Virtual_Guest']
begin
# Call to createObject() when you're ready to order.
# Call to generateOrderTemplate() if you want to create an order container that can be
# used with the methods verifyOrder and placeOrder.
virtual_guest = virtual_guest_service.createObject(virtual_guest_template)
puts JSON.pretty_generate(virtual_guest)
rescue StandardError => exception
puts "There was an error in your order: #{exception}"
end

File Upload in Rest API yii2

my controller file inside api/v1/controller/
class ProfileController extends ActiveController
{
public $modelClass = 'app\models\Profile';
public function behaviors()
{
return [
[
'class' => 'yii\filters\ContentNegotiator',
'only' =>
['index', 'view', 'createnew','update','search'],
'formats' =>
['application/json' => Response::FORMAT_JSON,],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'index' => ['get'],
'view' => ['get'],
'createnew' => ['post'],
'update' => ['put'],
'delete' => ['delete'],
'deleteall' => ['post'],
'search' => ['get']
],
]
];
}
public function actionCreatenew() {
$model = new Profile();
$model->load(Yii::$app->request->post());
$model->asset = UploadedFile::getInstance($model, 'asset');
$name = $model->user_id;
if($model->asset) {
$model->asset->saveAs('uploads/'.$name.'.
'.$model->asset->extension);
$model->asset = $model->asset->name.'.'.
$model->asset->extension;
}
if($model->save()) {
echo json_encode(array('status'=>"Success",
'data'=>$model->attributes),JSON_PRETTY_PRINT);
} else {
echo json_encode(array('status'=>"Failure",
'error_code'=>400,
'errors'=>$model->errors),JSON_PRETTY_PRINT);
}
}
}
When I try to use access this from Postman like:
POST http://localhost/myapp/api/v1/profiles
I get Invalid Parameter – yii\base\InvalidParamException
Response content must not be an array.
What is the issue?? help would be grateful!! Thanks
You can easily receive single / multi-uploaded files using HTTP POST with form-data encoding in Yii2, directly in your Yii2 Controller / action.
Use this code:
$uploads = UploadedFile::getInstancesByName("upfile");
if (empty($uploads)){
return "Must upload at least 1 file in upfile form-data POST";
}
// $uploads now contains 1 or more UploadedFile instances
$savedfiles = [];
foreach ($uploads as $file){
$path = //Generate your save file path here;
$file->saveAs($path); //Your uploaded file is saved, you can process it further from here
}
If you use Postman API client to test how your API is working, you can configure the upload endpoint to work like this for multi-file uploads:
Note: The upfile[] square brackets are important! Postman will happily let you select multiple files for upload in one slot, but this will not actually work. Doing it the way shown in the screenshot makes an array of files available to the Yii2 action, through the UploadedFile mechanism. This is roughly equivalent to the standard PHP $_FILES superglobal variable but with easier handling.
Single files can be uploaded with or without the [] square brackets after the key name. And of course you can name upfile whatever you like, for your convention.
You should use \yii\web\UploadedFile::getInstanceByName('asset'); instead of getInstance() checkout this Link

vagrant postgresql and external db gui

I'm using PuPHPet/Puppet/Vagrant to set up a VM with nginx and postgresql. I'd like to be able to connect to the postgresql database with a GUI. I don't know the required steps to set this up though.
I think i need to forward port 5432 to 5432 on my local machine and then edit the pg_hba.conf to allow for outside connections but i don't know what that needs to look like.
Here's my current Vagrantfile(doesn't have the port forward)
Vagrant.configure("2") do |config|
config.vm.box = "precise64"
config.vm.box_url = "http://files.vagrantup.com/precise64.box"
config.vm.network :private_network, ip: "10.10.10.10"
config.ssh.forward_agent = true
config.vm.provider :virtualbox do |v|
v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
v.customize ["modifyvm", :id, "--memory", 1024]
v.customize ["modifyvm", :id, "--name", "NGINX_PostgreSQL"]
end
config.vm.synced_folder "./", "/var/www", id: "vagrant-root"
config.vm.provision :shell, :inline =>
"if [[ ! -f /apt-get-run ]]; then sudo apt-get update && sudo touch /apt-get-run; fi"
config.vm.provision :puppet do |puppet|
puppet.manifests_path = "vagrant/manifests"
puppet.module_path = "vagrant/modules"
puppet.options = ['--verbose']
end
end
Here's my default.pp file
group { 'puppet': ensure => present }
Exec { path => [ '/bin/', '/sbin/', '/usr/bin/', '/usr/sbin/' ] }
File { owner => 0, group => 0, mode => 0644 }
class {'apt':
always_apt_update => true,
}
Class['::apt::update'] -> Package <|
title != 'python-software-properties'
and title != 'software-properties-common'
|>
apt::key { '4F4EA0AAE5267A6C': }
apt::ppa { 'ppa:ondrej/php5-oldstable':
require => Apt::Key['4F4EA0AAE5267A6C']
}
class { 'puphpet::dotfiles': }
package { [
'build-essential',
'vim',
'curl',
'git-core'
]:
ensure => 'installed',
}
class { 'nginx': }
nginx::resource::vhost { 'mylaravel.com':
ensure => present,
server_name => [
'mylaravel.com' ],
listen_port => 80,
index_files => [
'index.html',
'index.htm',
'index.php'
],
www_root => '/var/www/public',
try_files => ['$uri', '$uri/', '/index.php?$args'],
}
$path_translated = 'PATH_TRANSLATED $document_root$fastcgi_path_info'
$script_filename = 'SCRIPT_FILENAME $document_root$fastcgi_script_name'
nginx::resource::location { 'mylaravel.com-php':
ensure => 'present',
vhost => 'mylaravel.com',
location => '~ \.php$',
proxy => undef,
try_files => ['$uri', '$uri/', '/index.php?$args'],
www_root => '/var/www/public',
location_cfg_append => {
'fastcgi_split_path_info' => '^(.+\.php)(/.+)$',
'fastcgi_param' => 'PATH_INFO $fastcgi_path_info',
'fastcgi_param ' => $path_translated,
'fastcgi_param ' => $script_filename,
'fastcgi_param ' => 'APP_ENV dev',
'fastcgi_param ' => 'APP_DBG true',
'fastcgi_pass' => 'unix:/var/run/php5-fpm.sock',
'fastcgi_index' => 'index.php',
'include' => 'fastcgi_params'
},
notify => Class['nginx::service'],
}
class { 'php':
package => 'php5-fpm',
service => 'php5-fpm',
service_autorestart => false,
config_file => '/etc/php5/fpm/php.ini',
module_prefix => ''
}
php::module {
[
'php5-pgsql',
'php5-cli',
'php5-curl',
'php5-intl',
'php5-mcrypt',
'php-apc',
]:
service => 'php5-fpm',
}
service { 'php5-fpm':
ensure => running,
enable => true,
hasrestart => true,
hasstatus => true,
require => Package['php5-fpm'],
}
class { 'php::devel':
require => Class['php'],
}
class { 'xdebug':
service => 'nginx',
}
class { 'composer':
require => Package['php5-fpm', 'curl'],
}
puphpet::ini { 'xdebug':
value => [
'xdebug.default_enable = 1',
'xdebug.remote_autostart = 0',
'xdebug.remote_connect_back = 1',
'xdebug.remote_enable = 1',
'xdebug.remote_handler = "dbgp"',
'xdebug.remote_port = 9000'
],
ini => '/etc/php5/conf.d/zzz_xdebug.ini',
notify => Service['php5-fpm'],
require => Class['php'],
}
puphpet::ini { 'php':
value => [
'date.timezone = "America/Chicago"'
],
ini => '/etc/php5/conf.d/zzz_php.ini',
notify => Service['php5-fpm'],
require => Class['php'],
}
puphpet::ini { 'custom':
value => [
'display_errors = On',
'error_reporting = -1'
],
ini => '/etc/php5/conf.d/zzz_custom.ini',
notify => Service['php5-fpm'],
require => Class['php'],
}
class { 'postgresql':
charset => 'UTF8',
locale => 'en_US.UTF-8',
}->
class { 'postgresql::server':
config_hash => {
postgres_password => 'root',
},
}
postgresql::db { 'appDB':
user => 'dadams',
password => 'mypassword',
grant => 'ALL',
}
and here's my pg_hba.conf file inside the VM
# This file is managed by Puppet. DO NOT EDIT.
# Rule Name: local access as postgres user
# Description: none
# Order: 001
local all postgres ident
# Rule Name: local access to database with same name
# Description: none
# Order: 002
local all all ident
# Rule Name: deny access to postgresql user
# Description: none
# Order: 003
host all postgres 0.0.0.0/0 reject
# Rule Name: allow access to all users
# Description: none
# Order: 100
host all all 127.0.0.1/32 md5
# Rule Name: allow access to ipv6 localhost
# Description: none
# Order: 101
host all all ::1/128 md5
Most GUIs will allow you to connect via an SSH tunnel. This is the best way to do what you want.
Add the following port forwarding rule in Vagrantfile and do a vagrant reload, see if you can connect to the postgresql.
config.vm.network :forwarded_port, guest: 5432, host:5432
NOTE: you may still need to change postgresql.conf listen_addresses (bind) to * (all) interfaces and allow client connections from certain networks by modifying host records in the pg_hba.conf file.
Sample allow connection from network 10.1.1.0/24 unconditionally
host all all 10.1.1.0/24 trust
I think in your use case, enabling a 2nd network interface (public network) will make life easier, avoid lots of port forwarding and network issue.

Sinatra on Thin: How to hide or change HTTP 'Server' response header

What is the cleanest way to do this? Some Rack middleware? I tried to modify env['SERVER_SOFTWARE'] but I still get in response:
Server: thin 1.3.1 codename Triple Espresso
How to change the value of that header, or remove it completetly from response?
EDIT
Another try:
before do
headers 'Server' => 'ipm'
end
after do
headers 'Server' => 'ipm'
end
But still no changes.
This works here:
require 'sinatra'
get '/' do
[200, {'Server' => 'My Server'}, 'contents']
end
If you want to do it for all requests:
class ChangeServer
def initialize(app)
#app = app
end
def call(env)
res = #app.call(env)
res[1]['Server'] = 'My server'
return res
end
end
And then you use ChangeServer in your app.

Openid - User details after authentication

I am trying to use Catalyst::Authentication::Credential::OpenID to authenticate users from Google.
Once authentication is successful, I get a Catalyst::Plugin::Authentication::User::Hash object as my user.
If users are logging in for the first time in my application, I want to get details of user from OpenID provider and store them in my DB.
This is to ease the process of registration, I want as much details from OpenID as possible.
But at least first name, last name, email etc..
But I am not able to achieve it. As an example, if I call, I get exception saying method *url,display * are not defined.
$c->user->url
$c->user->display
Any help in sorting it out is helpful.
After reading the Catalyst manual a number of times and getting some clue from Catalyst mailing lists, I came to know that we have to use extensions.
Because we will be using a number of different realms, I used progressive class.
Here is sample configuration used in my app, currently supporting only openID.
This uses Simple Registration Schema for OpenID Attribute Exchange defined at
http://www.axschema.org/types/
'Plugin::Authentication' => {
default_realm => 'progressive',
realms => {
progressive => {
class => 'Progressive',
realms => [ 'openid' ],
},
openid => {
credential => {
class => "OpenID",
store => {
class => "OpenID",
},
consumer_secret => "Don't bother setting",
ua_class => "LWP::UserAgent",
# whitelist is only relevant for LWPx::ParanoidAgent
ua_args => {
whitelisted_hosts => [qw/ 127.0.0.1 localhost /],
},
extensions => [
'http://openid.net/srv/ax/1.0' => {
mode => 'fetch_request',
'type.nickname' => 'http://axschema.org/namePerson/friendly',
'type.email' => 'http://axschema.org/contact/email',
'type.fullname' => 'http://axschema.org/namePerson',
'type.firstname' => 'http://axschema.org/namePerson/first',
'type.lastname' => 'http://axschema.org/namePerson/last',
'type.dob' => 'http://axschema.org/birthDate',
'type.gender' => 'http://axschema.org/person/gender',
'type.country' => 'http://axschema.org/contact/country/home',
'type.language' => 'http://axschema.org/pref/language',
'type.timezone' => 'http://axschema.org/pref/timezone',
required => 'nickname,fullname,email,firstname,lastname,dob,gender,country',
if_available => 'dob,gender,language,timezone',
}
],
},
}
}
},