how to call sagemaker endpoint from another server - amazon-web-services

I try to call aws sagemaker endpoint. but I don't use lambda function. only, I want to acess endpoint ARN, URL.
if impossible method, I want to know lambda function
my endpoint based keras model. I don't know json.dumps
import json
import boto3
client = boto3.client('runtime.sagemaker')
import numpy as np
test = np.zeros((1, 1, 4325))
test[0][0][1] = 1
data = {"instances": test.tolist()}
response = client.invoke_endpoint(EndpointName=endpoint_name,
Body=json.dumps(data))
response_body = response['Body']
print(response_body.read())

Your question It's a little bit confusing. I understand you want to call a deployed endpoint outside Sagemaker and You can do this as shown here call-sagemaker-endpoint-using-lambda-function

You can also call it using awscurl like that:
awscurl --profile aws-profile -XPOST -H 'Content-Type: application/json' --data #input_file_name https://runtime.sagemaker.us-east-1.amazonaws.com/endpoints/your-endpoint-name/invocations --service sagemaker
You may want to also try with enviornment variable
export AWS_PROFILE=aws-profile; before the call to be sure you don't have something else set.

Related

Authenticating API Call to Google ML Engine with an API Key

I have a saved model in Google AI Platform that works when I test the predictions in the AI Platform UI.
However, when I try and access the API via REST, I keep getting a response with a 401 status. I would like to know how to do this successfully.
My api URL looks like the following:
'https://ml.googleapis.com/v1/projects/ml-project-name/models/my-model-names/versions/v2:predict
I would like to be able to access this endpoint in an external app that's residing on any platform to generate predictions with it.
Google Cloud recommends service account authorization, however, all of the directions for it require setting environment variables so the app can automatically authenticate you. I'd prefer to supply them directly in the request to make things more portable and congruent with how it's done elsewhere at work.
So instead I tried getting an API key.
According to this page: https://cloud.google.com/docs/authentication/api-keys you can authenticate a request in the following way:
POST https://language.googleapis.com/v1/documents:analyzeEntities?key=API_KEY
However, when I run the following code, my request status is 401:
import requests
api_key = my_sample_api_key
url = f'https://ml.googleapis.com/v1/projects/project-name/models/model-name/versions/v2:predict?key={api_key}'
json = {"instances": [ {"input_1": ["Please predict this text"]}]}
res = request.post(url, json=json)
Any help would be appreciated, thank you.
Auto ML does not support the use of API keys when sending requests. I suggest to use the auth token on your request or use the available client libraries to send predictions.
Here is a code snippet using its python client library for sending prediction request:
# Create the AI Platform service object.
# To authenticate set the environment variable
# GOOGLE_APPLICATION_CREDENTIALS=<path_to_service_account_file>
service = googleapiclient.discovery.build('ml', 'v1')
def predict_json(project, model, instances, version=None):
"""Send json data to a deployed model for prediction.
Args:
project (str): project where the AI Platform Model is deployed.
model (str): model name.
instances ([Mapping[str: Any]]): Keys should be the names of Tensors
your deployed model expects as inputs. Values should be datatypes
convertible to Tensors, or (potentially nested) lists of datatypes
convertible to tensors.
version: str, version of the model to target.
Returns:
Mapping[str: any]: dictionary of prediction results defined by the
model.
"""
name = 'projects/{}/models/{}'.format(project, model)
if version is not None:
name += '/versions/{}'.format(version)
response = service.projects().predict(
name=name,
body={'instances': instances}
).execute()
if 'error' in response:
raise RuntimeError(response['error'])
return response['predictions']
Here is a sample of sending a POST request using curl with the auth token:
curl -X POST \
-H "Authorization: Bearer "$(gcloud auth application-default print-access-token) \
-H "Content-Type: application/json; charset=utf-8" \
-d #request.json \
https://ml.googleapis.com/v1/projects/your-project/models/you-model-name/versions/your-version-name:predict

Invoking a Google Cloud Function from a Django View

I have created a Google Cloud function that can be invoked through HTTP. The access to the function is limited to the Service account only.
If I had a Django View which should invoke this function and expect a response?
Here is what I have tried
1) Before starting Django I set the environment variable
export GOOGLE_APPLICATION_CREDENTIALS
2) I tried invoking the function using a standalone code, but soon realised this was going nowhere, because I could not figure out the next step after this.
from google.oauth2 import service_account
from apiclient.http import call
SCOPES = ['https://www.googleapis.com/auth/cloud-platform']
SERVICE_ACCOUNT_FILE = 'credentials/credentials.json'
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
Google's documentation does give you documentation around the API, but there is no sample code on how to invoke the methods or what to import within your Python code and what are the ways to invoke those methods.
How do you send a POST request with JSON data to an Cloud Function, with authorization through a service account?
**Edit
A couple hours later I did some more digging and figured this out partially
from google.oauth2 import service_account
import googleapiclient.discovery
import json
SCOPES = ['https://www.googleapis.com/auth/cloud-platform']
SERVICE_ACCOUNT_FILE = 'credentials/credentials.json'
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
cloudfunction = googleapiclient.discovery.build('cloudfunctions', 'v1', credentials=credentials)
#projects/{project_id}/locations/{location_id}/functions/{function_id}.
path='some project path'
data='some data in json that works when invoked through the console'
data=json.dumps(data)
a=cloudfunction.projects().locations().functions().call(name=path, body=data).execute()
I get another error now.
Details: "[{'#type': 'type.googleapis.com/google.rpc.BadRequest', 'fieldViolations': [{'description': 'Invalid JSON payload received. Unknown name "": Root element must be a message.'}]}]">
I cant find any documentation on this. How should the JSON be formed?
making the json like {"message":{my actual payload}} doesn't work.
The requested documentation can be found here.
The request body argument should be an object with the following form:
{ # Request for the `CallFunction` method.
"data": "A String", # Input to be passed to the function.
}
The following modification on your code should work correctly:
from google.oauth2 import service_account
import googleapiclient.discovery
SCOPES = ['https://www.googleapis.com/auth/cloud-platform']
SERVICE_ACCOUNT_FILE = 'credentials/credentials.json'
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
cloudfunction = googleapiclient.discovery.build('cloudfunctions', 'v1', credentials=credentials)
path ="projects/your-project-name/locations/cloud-function-location/functions/name-of-cloud-function"
data = {"data": "A String"}
a=cloudfunction.projects().locations().functions().call(name=path, body=data).execute()
Notice that very limited traffic is allowed since there are limits to the API calls.

How to validate API Key in AWS Lambda function

I have read quite a few articles like this one and it looks like currently in AWS API Gateway you cannot send API Key in query string.
We also have few legacy clients that will need passing api-key in query string.
So i thought of two options
1>Create lambda function as Integration Type and validate API in key inside function handler. But i am not able to figure out how to validate it against keys in aws. Something like
public async Task<JObject> FunctionHandler(JObject request, ILambdaContext context)
{
// i know how to get apikey from queryStringParameters here
// but how do i validate it against api keys in aws
}
2> Create new custom authorizer, but again not sure how do i do it.
Which would be prefered option. I am using .NET core. Are there any nuget packages already available?
Please note querystring support is required and not debatable even if it is not recommended for security reason
You can pass security key in query param use header and pass key
curl -X PUT \
https://XXXX.XXXXX-api.ca-central-1.amazonaws.com/PROD/XXX-microservice \
-H 'Content-Type: application/json' \
-H 'x-api-key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' \
-d '{
"initData": "HI",
"name": "vaquar khan",
"likes": "Java"
}'
Security key validation taken care by API getaway so no lambda
authorizer required
If you are passing in query string
URL:
https://XXXX.XXXXX-api.ca-central-1.amazonaws.com/PROD/XXX-microservice ?x-api-key=XXXXXXXXXXXXXXXX &uid=5
Python 2.7
from __future__ import print_function
import boto3
import json
print('Loading function')
def lambda_handler(event, context):
print(event['params']['querystring']['x-api-key'])
print(event['params']['querystring']['uid'])

How can I print the Canonical String which aws-requests-auth sends?

I want to have a lambda calling a Sagemaker instance in another region. If both are in the same region, everything works fine. If they are not, I get the following error:
The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.
The Canonical String for this request should have been
'POST
/endpoints/foo-endpoint/invocations
host:runtime.sagemaker.us-east-1.amazonaws.com
x-amz-date:20180406T082536Z
host;x-amz-date
1234567890foobarfoobarfoobarboofoobarfoobarfoobarfoobarfoobarfoo'
The String-to-Sign should have been
'AWS4-HMAC-SHA256
20180406T082536Z
20180406/us-east-1/sagemaker/aws4_request
987654321abcdeffoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarf'
I use aws-requests-auth (0.4.1) with boto3 (1.5.15 - updating to 1.7.1 didn't change anything, changelog) like this:
import requests
from aws_requests_auth.aws_auth import AWSRequestsAuth
auth = AWSRequestsAuth(aws_access_key=config['AWS']['ACCESS_KEY'],
aws_secret_access_key=(
config['AWS']['SECRET_ACCESS_KEY']),
aws_host=config['AWS']['HOST'],
aws_region=config['AWS']['REGION'],
aws_service=config['AWS']['SERVICE'])
payload = {'foo': 'bar'}
response = requests.post(post_url,
data=json.dumps(payload),
headers={'content-type': 'application/json'},
auth=auth)
printing auth only gives <aws_requests_auth.aws_auth.AWSRequestsAuth object at 0x7f9d00c98390>.
Is there a way to print the "Canonical String" mentioned in the error message?
(Any other ideas how to fix this are appreciated as well)
A work-around for the asked question:
req = requests.request('POST', 'http://httpbin.org/get')
req.body = b''
req.method = ''
print(auth.get_aws_request_headers(req,
aws_access_key=auth.aws_access_key,
aws_secret_access_key=auth.aws_secret_access_key,
aws_token=auth.aws_token))
The problem is not solved, though. And now I wonder what the first argument of auth.get_aws_request_headers is.

Making a signed HTTP request to AWS Elasticsearch in Python

I'm trying to make a simple Python Lambda that makes snapshots of our Elasticsearch database. This is done through Elasticsearch's REST API using simple HTTP requests.
However, for AWS, I have to sign these requests. I have a feeling it can be achieved through boto3's low-level clients probably with generate_presigned_url, but I cannot for the life of me figure out how to invoke this function correctly. For example, what are the valid ClientMethods? I've tried ESHttpGet but to no avail.
Can anyone point me in the right direction?
Edit: Apparently this workaround has been broken by Elastic.
I struggled for a while to do a similar thing. Currently the boto3 library doesn't support making signed es requests, though since I raised an issue with them it's become a feature request.
Here's what I've done in the meantime using DavidMuller's library mentioned above and boto3 to get my STS session credentials:
import boto3
from aws_requests_auth.aws_auth import AWSRequestsAuth
from elasticsearch import Elasticsearch, RequestsHttpConnection
session = boto3.session.Session()
credentials = session.get_credentials().get_frozen_credentials()
es_host = 'search-my-es-domain.eu-west-1.es.amazonaws.com'
awsauth = AWSRequestsAuth(
aws_access_key=credentials.access_key,
aws_secret_access_key=credentials.secret_key,
aws_token=credentials.token,
aws_host=es_host,
aws_region=session.region_name,
aws_service='es'
)
# use the requests connection_class and pass in our custom auth class
es = Elasticsearch(
hosts=[{'host': es_host, 'port': 443}],
http_auth=awsauth,
use_ssl=True,
verify_certs=True,
connection_class=RequestsHttpConnection
)
print(es.info())
Hope this saves somebody some time.
There are several Python extensions to the requests library that will perform the SigV4 signing for you. I have used this one and it works well.
While other answers are perfectly fine, I wanted to eliminate the use of external packages. Obviously, botocore itself has all the required functionality to sign requests it was just a matter of looking at the source code. This is what I ended up with for sending AWS API requests directly (things are hardcoded for the demonstration purposes):
import boto3
import botocore.credentials
from botocore.awsrequest import AWSRequest
from botocore.endpoint import URLLib3Session
from botocore.auth import SigV4Auth
params = '{"name": "hello"}'
headers = {
'Host': 'ram.ap-southeast-2.amazonaws.com',
}
request = AWSRequest(method="POST", url="https://ram.ap-southeast-2.amazonaws.com/createresourceshare", data=params, headers=headers)
SigV4Auth(boto3.Session().get_credentials(), "ram", "ap-southeast-2").add_auth(request)
session = URLLib3Session()
r = session.send(request.prepare())
I recently published requests-aws-sign, which provides AWS V4 request signing for the Python requests library.
If you look at this code you will see how you can use Botocore to generate the V4 request signing.
why not just use requests?
import requests
headers = {'Content-Type': 'application/json',}
data = '{"director": "Burton, Tim", "genre": ["Comedy","Sci-Fi","R-rated"],"profit" : 98 , "year": 1996, "actor": ["Jack Nicholson","PierceBrosnan","Sarah Jessica Parker"], "title": "Mars Attacks!"}'
response = requests.post('https://search-your-awsendpoint.us-west-2.es.amazonaws.com/yourindex/_yourdoc/', headers=headers, data=data)
this worked for me