I wonder what is the easiest way to invoke an OpenWhisk action from a Python app?
Perhaps something equivalent to https://github.com/apache/incubator-openwhisk-client-js/ but in Python. I know that there used to be a Python-based CLI (https://github.com/apache/incubator-openwhisk-client-python), but I haven't found any documentation on how to reuse it from my Python script.
Invoking actions from a Python application will need you to send a HTTP request to the platform API. There is no official OpenWhisk SDK for Python.
The example code shows how to invoke the platform API using the requests library.
import subprocess
import requests
APIHOST = 'https://openwhisk.ng.bluemix.net'
AUTH_KEY = subprocess.check_output("wsk property get --auth", shell=True).split()[2]
NAMESPACE = 'whisk.system'
ACTION = 'utils/echo'
PARAMS = {'myKey':'myValue'};
BLOCKING = 'true'
RESULT = 'true'
url = APIHOST + '/api/v1/namespaces/' + NAMESPACE + '/actions/' + ACTION
user_pass = AUTH_KEY.split(':')
response = requests.post(url, json=PARAMS, params={'blocking': BLOCKING, 'result': RESULT}, auth=(user_pass[0], user_pass[1]))
print(response.text)
Swagger documentation for full API is available here.
There is an open issue to create a Python client library to make this easier.
Related
Laravel's Http:fake() method allows you to instruct the HTTP client to return stubbed / dummy responses when requests are made. How can I achieve the same using Django Rest Framework APIClient in tests?
I tried requests_mock but it didn't yield the result I was expecting. It only mocks requests made within test function and not anywhere else within the application or project you're testing.
When you use pytest-django you can use import the fixture admin_client and then do requests like this:
def test_get_project_list(admin_client):
resp = admin_client.get("/projects/")
assert resp.status_code == 200
resp_json = resp.json()
assert resp_json == {"some": "thing"}
The equivalent of Laravel's Http::fake() in Django is requests_mock.
You must user python requests module to call external apis
Then user requests_mock to fake external APIs
The mocker is a loading mechanism to ensure the adapter is correctly in place to intercept calls from requests. Its goal is to provide an interface that is as close to the real requests library interface as possible.
Let me know if you need me to post an example.
You can read more from the requests mock module official website
I've written a Python action on Bluemix OpenWhisk, and I need to call another action (actually a binding to a public package) from within this action. A sequence won't do it, because I need to call it a varying number of times with different parameters depending on the input.
How to invoke openwhisk action within openwhisk platform on bluemix? mentions how to do it from JavaScript, but the OpenWhisk package doesn't seem to be available for Python.
Actions can be invoked using a HTTP request to the platform API. The Python runtime in OpenWhisk includes the requests library for making HTTP calls.
Here's an example of an action that calls another (child) in the same namespace.
import os
import requests
APIHOST = os.environ.get('__OW_API_HOST')
NAMESPACE = os.environ.get('__OW_NAMESPACE')
USER_PASS = os.environ.get('__OW_API_KEY').split(':')
def main(params):
action = 'child'
url = APIHOST + '/api/v1/namespaces/' + NAMESPACE + '/actions/' + action
response = requests.post(url, data=params, params={'blocking': 'true'}, auth=(USER_PASS[0], USER_PASS[1]))
print(response.json())
return {"text": "invoked!"}
Swagger documentation for full API is available here.
There is an open issue to create a Python client library to make this easier.
I created 2 applications in my Azure directory, 1 for my API Server and one for my API client. I am using the Python ADAL Library and can successfully obtain a token using the following code:
tenant_id = "abc123-abc123-abc123"
context = adal.AuthenticationContext('https://login.microsoftonline.com/' + tenant_id)
token = context.acquire_token_with_username_password(
'https://myapiserver.azurewebsites.net/',
'myuser',
'mypassword',
'my_apiclient_client_id'
)
I then try to send a request to my API app using the following method but keep getting 'unauthorized':
at = token['accessToken']
id_token = "Bearer {0}".format(at)
response = requests.get('https://myapiserver.azurewebsites.net/', headers={"Authorization": id_token})
I am able to successfully login using myuser/mypass from the loginurl. I have also given the client app access to the server app in Azure AD.
Although the question was posted a long time ago, I'll try to provide an answer. I stumbled across the question because we had the exact same problem here. We could successfully obtain a token with the adal library but then we were not able to access the resource I obtained the token for.
To make things worse, we sat up a simple console app in .Net, used the exact same parameters, and it was working. We could also copy the token obtained through the .Net app and use it in our Python request and it worked (this one is kind of obvious, but made us confident that the problem was not related to how I assemble the request).
The source of the problem was in the end in the oauth2_client of the adal python package. When I compared the actual HTTP requests sent by the .Net and the python app, a subtle difference was that the python app sent a POST request explicitly asking for api-version=1.0.
POST https://login.microsoftonline.com/common//oauth2/token?api-version=1.0
Once I changed the following line in oauth2_client.py in the adal library, I could access my resource.
Changed
return urlparse('{}?{}'.format(self._token_endpoint, urlencode(parameters)))
in the method _create_token_url, to
return urlparse(self._token_endpoint)
We are working on a pull request to patch the library in github.
For the current release of Azure Python SDK, it support authentication with a service principal. It does not support authentication using an ADAL library yet. Maybe it will in future releases.
See https://azure-sdk-for-python.readthedocs.io/en/latest/resourcemanagement.html#authentication for details.
See also Azure Active Directory Authentication Libraries for the platforms ADAL is available on.
#Derek,
Could you set your Issue URL on Azure Portal? If I set the wrong Issue URL, I could get the same error with you. It seems that your code is right.
Base on my experience, you need add your application into Azure AD and get a client ID.(I am sure you have done this.) And then you can get the tenant ID and input into Issue URL textbox on Azure portal.
NOTE:
On old portal(manage.windowsazure.com),in the bottom command bar, click View Endpoints, and then copy the Federation Metadata Document URL and download that document or navigate to it in a browser.
Within the root EntityDescriptor element, there should be an entityID attribute of the form https://sts.windows.net/ followed by a GUID specific to your tenant (called a "tenant ID"). Copy this value - it will serve as your Issuer URL. You will configure your application to use this later.
My demo is as following:
import adal
import requests
TenantURL='https://login.microsoftonline.com/*******'
context = adal.AuthenticationContext(TenantURL)
RESOURCE = 'http://wi****.azurewebsites.net'
ClientID='****'
ClientSect='7****'
token_response = context.acquire_token_with_client_credentials(
RESOURCE,
ClientID,
ClientSect
)
access_token = token_response.get('accessToken')
print(access_token)
id_token = "Bearer {0}".format(access_token)
response = requests.get(RESOURCE, headers={"Authorization": id_token})
print(response)
Please try to modified it. Any updates, please let me know.
I built a Python 3.4 Web Application calling Google Analytics API.
class GA:
def __init__(self):
self.scope = ['https://www.googleapis.com/auth/analytics.readonly']
self.service_account_email = 'my_account_email'
self.key_file_location = 'my_key_location'
self.ga_id = 'my_ga_id'
def get_service(self, api_name = 'analytics', api_version = 'v3'):
f = open(self.key_file_location, 'rb')
key = f.read()
f.close()
credentials = SignedJwtAssertionCredentials(self.service_account_email, key,scope=self.scope)
http = credentials.authorize(httplib2.Http())
service = build(api_name, api_version, http=http)
self.service = service
return (service)
ga = GA()
ga.get_service()
It works perfectly without proxy
but I need to set it up on a windows server running behind a corporate proxy. So I tried to replace the http object by :
p = httplib2.proxy_info_from_url("http://username:pwd#myproxyname:80")
http = credentials.authorize(httplib2.Http(proxy_info=p))
But it doesn't work. So I also tried with :
os.environ['HTTP_PROXY']="http://username:pwd#myproxyname:80"
p = httplib2.proxy_info_from_environment(method='http')
http = credentials.authorize(httplib2.Http(proxy_info=p))
But it is not working either. I checked all the related questions without success. I always get a TimeoutError: [WinError 10060]
For those who are interesting, I decided to recode my web app in Python 2 ; still using httplib2 package;
The same code works (proxy info are taken into account contrary to Python 3)
I've just come across this question and it's relevant to a solution I've implemented in Python 3.6.
I tried a number of different methods but the only one that httplib2 seems to recognize the proxy is through modifying the http_proxy environment variable
I've also had to remove SSL validation since that was the only way I could get this working. I wouldn't recommend this approach, unless you trust the domain you're requesting from won't be compromised.
os.environ["https_proxy"] = "https://PROXY_URL:PROXY_PORT"
http = credentials.authorize(http=httplib2.Http(disable_ssl_certificate_validation=True))
This approach also works for the initial request to the authorization/discovery URL's, but fails later on if you need to request information from the API. I was above to dig into the google analytics api source code before I found a much simpler solution (above)
# http = credentials.authorize(http=httplib2.Http(proxy_info=httplib2.ProxyInfo(httplib2.socks.PROXY_TYPE_HTTP, "PROXY_URL", PROXY_PORT), disable_ssl_certificate_validation=True))
huberu, your code worked fine, you helped me a lot!
Blessings for you and your family!
If someone needs to test, you can use the code bellow to simulate a proxy connection:
import httplib2
import os
os.environ['https_proxy'] = '127.0.0.1:80'
http = httplib2.Http(disable_ssl_certificate_validation=True)
resp, content = http.request('http://google.com', 'GET')
print(resp)
print(content)
I am trying to create a very simple Python script to download the contents of an internal service at my company that sits within our firewall and authenticates using kerberos.
When I installed the requests_kerberos module I first edited the import kerberos in it to use import kerberos_sspi as kerberos instead after having installed the kerberos_sspi module.
Thus I have the following Python script
import requests
from requests_kerberos import HTTPKerberosAuth
response = requests.get('http://service.internaldomain',auth=HTTPKerberosAuth())
print response
While trying to process the 401 it crashes out with the error.
error: (-2146893053, 'InitializeSecurityContext', 'The specified target is unknown or unreachable')
While looking into seeing if I could do this with curl instead I ran kinit and noticed that it asked me for the password to authorisation with the following prompt:
Password for username#additionalInternalDomain.internaldomain
Thus I wondered if this might be what is causing the issue.
I have tried multiple libraries on python and failed when trying to authenticate from a windows machine.There is no easy way. The Kerberos libraries mainly work on Linux. The workarounds for Windows do not work. So what can be the solution to this.
Well... be a Roman while in Rome. Try the windows native libraries from Python.
import sys
import clr
from System.Net.Http import *
myClienthandler = HttpClientHandler()
myClienthandler.UseDefaultCredentials = True
myClient = HttpClient(myClienthandler)
x = myClient.GetStringAsync("putyourURLwithinthequoteshere")
myresult = x.Result
print(myresult)
Note that the this python script will have to run by the user who has access to the URL you are trying to access. By setting UseDefaultCredentials property as True, you are passing the Kerberos tickets for the logged in user.
The server is giving you a 401 challenge - and the client (usually a browser or even curl) provides the credentials in a subsequent call. If you are already logged in at your domain - try forcing a pre-emptive hop, i.e. you’d carry your Kerberos ticket with your call and the server will not give you a 401 challenge:
kerberos_auth = HTTPKerberosAuth(force_preemptive=True)
r = requests.get("http://myhost/DXAPIGraphQL/api/graphql", auth=kerberos_auth)
If the above doesn't help look into the:
principal and hostname_override arguments of the HTTPKerberosAuth class.
I had to connecto to a REST API who's in a keberized environment just now.
After some reading, i came to this (and it worked):
tk = 'long_kerberos_token'
headers = {'Authorization': 'Negotiate' + tk}
r = requests.get(url=PING_URL, headers=headers)