Trying to create an API for the first time in GCP and getting the following error
ERROR: (gcloud.api-gateway.api-configs.create) API Config's backend has no rules. If using gRPC, be sure to specify the 'rules[]' under the 'Backend' field. See https://cloud.google.com/endpoints/docs/grpc-service-config/reference/rpc/google.api#backendrule for more details.
However, if I try to add a backend to the yaml file, I am getting the following error.
ERROR: (gcloud.api-gateway.api-configs.create) INVALID_ARGUMENT: Cannot convert to service config.
'location: "unknown location"
kind: ERROR
message: "Invalid OpenAPI file. Please fix the schema errors:\nerror: object instance has properties which are not allowed by the schema: ["backend"]\n level: "error"\n schema: {"loadingURI":"http://swagger.io/v2/schema.json#","pointer":""}\n instance: {"pointer":""}\n domain: "validation"\n keyword: "additionalProperties"\n unwanted: ["backend"]"
This is the bare bones yaml file I am using.
swagger: '2.0'
info:
title: sayHello
version: '1.0'
description: sayHello
schemes:
- https
produces:
- application/json
consumes:
- application/json
backend:
rules:
- selector: "*"
address: https://abc.cloudfunctions.net/sayHello
paths:
'/getPoints/{clientId}':
parameters:
- type: string
name: clientId
in: path
required: true
definitions: {}
responses:
'200':
description: Success
schema:
type: string
examples: {}
headers: {}
What am I missing? Somehow, not able to find much resources around adding backend rules to the service config file. Not even able to get the spec to validate against.
Any help would be appreciated.
Related
I'm creating an AWS API Gateway that exposes a GET method on its root.
My GW:
`
MyApiGateway:
Type: "AWS::Serverless::Api"
DependsOn: GetRequest
Properties:
Name: !Sub my-api-${EnvironmentName}
The DependsOn is according to the docs: "If you create an AWS::ApiGateway::RestApi resource and its methods (using AWS::ApiGateway::Method) in the same template as your deployment, the deployment must depend on the RestApi's methods"
My method:
GetRequest:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: AWS_IAM
HttpMethod: GET
RestApiId: !Sub my-api-${EnvironmentName}
ResourceId: !GetAtt MyApiGateway.RootResourceId
`
And it depends on the GW again according to the docs: "The ID of an API Gateway resource. For root resource methods, specify the RestApi root resource ID, such as { "Fn::GetAtt": ["MyRestApi", "RootResourceId"] }."
This CloudFormation template cannot be built due to the circular dependency.
How can I expose a GET method on the API GW's root path without causing this circular dependency?
I tried removing the DependsOn section, and got the error "No integration defined for method"
I have a project which has a cloudfront distribution to serve some data out of a bucket. I am using Serverless framework, but I think this is mainly a CloudFormation question.
I would like to create the A record in a Route53 hosted domain (third level domain if that matters, ie: dashboard.domain.com is pointed to Route53, and I'm trying to add .dashboard.domain.com).
I just cannot figure out how to reference the output from the CloudFront resource?
This is what I have right now, and it works because it's all static. However, I need to automatically put in the correct cloud front domain which will be created by another resource. I figure these is some type of GetAttr I can do, but I just cannot get it to work.
DNSRecords:
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneId: Z09193931V4YGJEPVMLG1
RecordSets:
- Name: prod.dashboard.domain.com
Type: A
AliasTarget:
HostedZoneId: Z2FDTNDATAQYW2
DNSName: someid.cloudfront.net
WebAppCloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
- DomainName:
Fn::Join: [
"", [
{ "Ref": "WebAppS3Bucket" },
".s3.amazonaws.com"
]
]
## An identifier for the origin which must be unique within the distribution
Id: WebApp
CustomOriginConfig:
HTTPPort: 80
HTTPSPort: 443
OriginProtocolPolicy: https-only
Enabled: 'true'
## Uncomment the following section in case you are using a custom domain
Aliases:
- ${self:provider.stage}.dashboard.domain.com
DefaultRootObject: index.html
## Since the Single Page App is taking care of the routing we need to make sure ever path is served with index.html
## The only exception are files that actually exist e.h. app.js, reset.css
CustomErrorResponses:
- ErrorCode: 404
ResponseCode: 200
ResponsePagePath: /index.html
DefaultCacheBehavior:
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
## The origin id defined above
TargetOriginId: WebApp
## Defining if and how the QueryString and Cookies are forwarded to the origin which in this case is S3
ForwardedValues:
QueryString: 'false'
Cookies:
Forward: none
## The protocol that users can use to access the files in the origin. To allow HTTP use `allow-all`
ViewerProtocolPolicy: redirect-to-https
## The certificate to use when viewers use HTTPS to request objects.
ViewerCertificate:
AcmCertificateArn:
Ref: SSLCertificate
SslSupportMethod: sni-only
MinimumProtocolVersion: TLSv1
EDIT: Updated to include the WebAppCloudFrontDistribution
You haven't provided your AWS::CloudFront::Distribution resource definition, so I only can based it on an example.
MyCloudFrontDistro:
Type: AWS::CloudFront::Distribution
Properties:
# some properties
Then you can modify your DNSRecords
DNSRecords:
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneId: Z09193931V4YGJEPVMLG1
RecordSets:
- Name: prod.dashboard.domain.com
Type: A
AliasTarget:
HostedZoneId: !Ref MyCloudFrontDistro
DNSName: !GetAtt MyCloudFrontDistro.DomainName
WebAppCloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
- DomainName:
Fn::Join: [
"", [
{ "Ref": "WebAppS3Bucket" },
".s3.amazonaws.com"
]
]
## An identifier for the origin which must be unique within the distribution
Id: WebApp
CustomOriginConfig:
HTTPPort: 80
HTTPSPort: 443
OriginProtocolPolicy: https-only
Enabled: 'true'
Aliases:
- ${self:provider.stage}.dashboard.domain.com
DefaultRootObject: index.html
CustomErrorResponses:
- ErrorCode: 404
ResponseCode: 200
ResponsePagePath: /index.html
DefaultCacheBehavior:
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
TargetOriginId: WebApp
ForwardedValues:
QueryString: 'false'
Cookies:
Forward: none
## The protocol that users can use to access the files in the origin. To allow HTTP use `allow-all`
ViewerProtocolPolicy: redirect-to-https
## The certificate to use when viewers use HTTPS to request objects.
ViewerCertificate:
AcmCertificateArn:
Ref: SSLCertificate
SslSupportMethod: sni-only
MinimumProtocolVersion: TLSv1
## Uncomment the following section in case you want to enable logging for CloudFront requests
# Logging:
# IncludeCookies: 'false'
# Bucket: mylogs.s3.amazonaws.com
# Prefix: myprefix
Resources:
DNSRecords:
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneName: dashboard.domain.com.
RecordSets:
- Name: ${self:provider.stage}.dashboard.domain.com
Type: A
AliasTarget:
HostedZoneId: Z2FDTNDATAQYW2
DNSName: !GetAtt WebAppCloudFrontDistribution.DomainName
Here is the working solution for me, take note of some points.
The HostedZoneId of Z2FDTNDATAQYW2 is special for the cloudfront domain. It needs to be used when referencing a cloud front resource.
The trailing space needs to be included on the HostedZoneName (if you use that compared to the HostedZoneId). In my case, I have the domain setup prior to the Cloud Formation.
I've followed the google cloud endpoints with Google Kubernetes engine tutorial here: https://cloud.google.com/endpoints/docs/openapi/get-started-kubernetes-engine
using my own docker image. The Kubernetes part works fine and I can access my services via the loadbalancer IP.
However, when I try to put my service behind cloud endpoints in order to secure it, the endpoint remains public and can be accessed without an API key. Here is my openapi.yaml, deployed with gcloud endpoints services deploy openapi.yaml:
swagger: "2.0"
info:
description: "A test."
title: "API test"
version: "1.0.0"
host: "<test-api.endpoints.MY-PROJECT-ID.cloud.goog>"
x-google-endpoints:
- name: "<test-api.endpoints.MY-PROJECT-ID.cloud.goog>"
target: "<MY LOADBALANCER IP>"
#require an API key to access project
security:
- api_key: []
paths:
/:
get:
summary: Return django REST default page
description: test
operationId: test
responses:
"200":
description: OK
securityDefinitions:
# This section configures basic authentication with an API key.
api_key:
type: "apiKey"
name: "key"
in: "query"
When I try to access my service via the value for host (obscured because it is still open), it is still open and does not require an API key . Nothing is shown in the cloud endpoints logs either. As far as I understand, the configuration of openapi.yaml alone should be enough to restrict access?
Following this page (https://swagger.io/docs/specification/2-0/authentication/api-keys/), your security is misplaced. It should looks like this:
swagger: "2.0"
info:
description: "A test."
title: "API test"
version: "1.0.0"
host: "<test-api.endpoints.MY-PROJECT-ID.cloud.goog>"
x-google-endpoints:
- name: "<test-api.endpoints.MY-PROJECT-ID.cloud.goog>"
target: "<MY LOADBALANCER IP>"
#require an API key to access project
paths:
/:
get:
security:
- api_key: []
summary: Return django REST default page
description: test
operationId: test
responses:
"200":
description: OK
securityDefinitions:
# This section configures basic authentication with an API key.
api_key:
type: "apiKey"
name: "key"
in: "query"
Edit:
In case you want the api-key in the header, you have to change the definition but you can leave security in the place you declared:
swagger: "2.0"
info:
description: "A test."
title: "API test"
version: "1.0.0"
host: "<test-api.endpoints.MY-PROJECT-ID.cloud.goog>"
x-google-endpoints:
- name: "<test-api.endpoints.MY-PROJECT-ID.cloud.goog>"
target: "<MY LOADBALANCER IP>"
#require an API key to access project
security:
- api_key: []
paths:
/:
get:
summary: Return django REST default page
description: test
operationId: test
responses:
"200":
description: OK
securityDefinitions:
# This section configures basic authentication with an API key.
api_key:
type: "apiKey"
name: "key"
in: "header"
I didn't understand which version you preferred, so I adjusted both.
I am trying to use serverless-offline to develop / simulate my API Gateway locally. My API gateway makes liberal use of the HTTP proxy integrations. The production Resource looks like this:
I have created a serverless-offline configuration based on a few documents and discussion which say that it is possible to define an HTTP Proxy integration using Cloud Formation configuration:
httpProxyWithApiGateway.md - Setting an HTTP Proxy on API Gateway by using Serverless framework.
Setting an HTTP Proxy on API Gateway (official Serverless docs: API Gateway)
I have adapted the above two configuration examples for my purposes, see below.
Have any tips, for what I might be doing wrong here?
plugins:
- serverless-offline
service: company-apig
provider:
name: aws
stage: dev
runtime: python2.7
resources:
Resources:
# Parent APIG RestApi
ApiGatewayRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: company-apig
Description: 'The main entry point of the APIG'
# Resource /endpoint
EndpointResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId:
Fn::GetAtt:
- ApiGatewayRestApi
- RootResourceId
PathPart: 'endpoint'
RestApiId:
Ref: ApiGatewayRestApi
# Resource /endpoint/{proxy+}
EndpointProxyPath:
Type: AWS::ApiGateway::Resource
Properties:
ParentId:
Ref: EndpointResource
PathPart: '{proxy+}'
RestApiId:
Ref: ApiGatewayRestApi
# Method ANY /endpoint/{proxy+}
EndpointProxyAnyMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: NONE
HttpMethod: ANY
Integration:
IntegrationHttpMethod: ANY
Type: HTTP_PROXY
Uri: http://endpoint.company.cool/{proxy}
PassthroughBehavior: WHEN_NO_MATCH
MethodResponses:
- StatusCode: 200
ResourceId:
Ref: EndpointProxyPath
RestApiId:
Ref: ApiGatewayRestApi
For the above configuration, I get this output. Apparently, the configuration registers no routes at all.
{
"statusCode":404,
"error":"Serverless-offline: route not found.",
"currentRoute":"get - /endpoint/ping",
"existingRoutes":[]
}
Related: I am also attempting to solve the same problem using aws-sam, at the following post - API Gateway HTTP Proxy integration with aws-sam (NOT Lambda Proxy)
By default serverless-offline doesn't parse your resources for endpoints, enable it via custom config.
custom:
serverless-offline:
resourceRoutes: true
Ends up serving:
Serverless: Routes defined in resources:
Serverless: ANY /endpoint/{proxy*} -> http://endpoint.company.cool/{proxy}
Serverless: Offline listening on http://localhost:3000
Documentation
To implement binary support in api-gateway for file upload, i have used serverless-apigw-binary plugin and added necessary content types which should be converted by api-gateway.
This is my serverless.ymlfile
service: aws-java-gradle
provider:
name: aws
runtime: java8
stage: dev
region: us-east-1
custom:
apigwBinary:
types:
- 'application/pdf'
plugins:
- serverless-apigw-binary
package:
artifact: build/distributions/hello.zip
functions:
uploadLoadFiles:
handler: com.serverless.UploadFileHandler
role: UploadFileRole
timeout: 180
events:
- http:
integration: lambda
path: upload
method: put
cors:
origin: '*'
headers:
- Content-Type
- X-Amz-Date
- Authorization
- X-Api-Key
- X-Amz-Security-Token
- X-Amz-User-Agent
- X-Requested-With
allowCredentials: false
request:
passThrough: WHEN_NO_TEMPLATES
template:
application/pdf: '{ "operation":"dev-aws-java-gradle", "base64Image": "$input.body", "query": "$input.params("fileName")" }'
response:
template: $input.body
statusCodes:
400:
pattern: '.*"httpStatus":400,.*'
template: ${file(response-template.txt)}
401:
pattern: '.*"httpStatus":401,.*'
template: ${file(response-template.txt)}
403:
pattern: '.*"httpStatus":403,.*'
template: ${file(response-template.txt)}
500:
pattern: '.*"httpStatus":500,.*'
template: ${file(response-template.txt)}
501:
pattern: '.*[a-zA-Z].*'
template: ${file(unknown-error-response-template.txt)}
environment:
S3BUCKET: ${env:S3_BUCKET}
APS_ENV: ${env:APS_ENV}
# you can add CloudFormation resource templates here
resources:
Resources:
UploadFileRole:
Type: AWS::IAM::Role
Properties:
RoleName: "UploadFileRole"
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: loggingPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource:
- 'Fn::Join':
- ':'
-
- 'arn:aws:logs'
- Ref: 'AWS::Region'
- Ref: 'AWS::AccountId'
- 'log-group:/aws/lambda/*:*:*'
all the necessary settings got implemented in api-gateway after doing sls deploy
(checked based on this article https://aws.amazon.com/blogs/compute/binary-support-for-api-integrations-with-amazon-api-gateway/)
but when i hit my end point api gateway is giving out an error like this
Verifying Usage Plan for request: 45bac722-f039-11e7-bcc6-f9a1aa509052. API Key: API Stage: fd4ue8lpia/int
API Key authorized because method 'PUT /upload' does not require API Key. Request will not contribute to throttle or quota limits
Usage Plan check succeeded for API Key and API Stage fd4ue8lpia/int
Starting execution for request: 45bac722-f039-11e7-bcc6-f9a1aa509052
HTTP Method: PUT, Resource Path: /upload
Method request path:
{}
Method request query string:
{}
Method request headers: {Accept=*/*, CloudFront-Viewer-Country=IN, postman-token=6f1a23ba-36c2-104d-2e12-dc2c76a985ee, CloudFront-Forwarded-Proto=https, CloudFront-Is-Tablet-Viewer=false, origin=chrome-extension://aicmkgpgakddgnaphhhpliifpcfhicfo, CloudFront-Is-Mobile-Viewer=false, User-Agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36, X-Forwarded-Proto=https, CloudFront-Is-SmartTV-Viewer=false, Host=fd4ue8lpia.execute-api.us-east-1.amazonaws.com, Accept-Encoding=gzip, deflate, br, X-Forwarded-Port=443, X-Amzn-Trace-Id=Root=1-5a4c530e-2c1927f85440b71b66cf0c15, Via=2.0 fc18daf68173838631b562fe2efaf8f8.cloudfront.net (CloudFront), x-postman-interceptor-id=8fa8440f-0541-fdac-c60c-6019c2269a66, X-Amz-Cf-Id=P4PYoi5Wwb-NCYRSdTQ_5TdtpbQLBaEATXoyJGC7cS8g6LsoCRPbkg==, X-Forwarded-For=157.50.20.12, 52.46.37.156, content-type=application/pdf, Accept-Language=en-US,en;q=0.9, cache-control=no-cache, CloudFront-Is-Desktop-Viewer=true}
Method request body before transformations: [Binary Data]
Execution failed due to configuration error: Unable to transform request
Method completed with status: 500
and the request is not crossing api-gateway to lambda function.
But i followed a solution mentioned here
to edit lambda function name in integration request section of resource in api-gateway to the same name and click ok. Then error's were gone and working fine.
I checked for changes in roles after doing that but none were found.
Can any one suggest what could have happened there, and any better solutions for the above mentioned problem.
Thanks in advance.
But i followed a solution mentioned here to edit lambda function name
in integration request section of resource in api-gateway to the same
name and click ok. Then error's were gone and working fine.
When you edit lambda function on the console, it sets up the permissions to call the lambda function automatically. If you want to do that manually, you can do that using the CLI