Cloud Foundry more then 10 ports for an app by curl - cloud-foundry

I have running app in Cloud Foundry. I would like to proceed the procedure of adding more then 10 ports to the app described here: https://docs.cloudfoundry.org/devguide/custom-ports.html#procedure
When I am trying to add more then 10 ports, I am getting an error:
cf curl /v2/apps/c5476e63-d1d2-4eb7-a757-18cff957bc5a -X PUT -d '{"ports": [2030,2031,2032,2033,2034,2035,2036,2037,2038,2039,2040,2041,2042]}'
{
"description": "The app is invalid: Process must have at most 10 exposed ports.",
"error_code": "CF-AppInvalid",
"code": 100001
}
I tried to adjust some quota definitions like total_routes (-1) or total_reserved_route_ports (-1) but without any result. Why do I get this error and how can I assign more then 10 ports by cf curl?

As of writing this, it would appear to be an arbitrary and hardcoded limit that cannot be changed.
See https://github.com/cloudfoundry/cloud_controller_ng/blob/master/app/models/runtime/constraints/ports_policy.rb#L9:
def validate
return if #process.ports.nil? || #process.ports.empty?
return #errors.add('Process', 'must have at most 10 exposed ports.') if ports_limit_exceeded?
return #errors.add('Ports', 'must be integers.') unless all_ports_are_integers?
...
and https://github.com/cloudfoundry/cloud_controller_ng/blob/master/app/models/runtime/constraints/ports_policy.rb#L44, which defines the limit.
def ports_limit_exceeded?
#process.ports.length > 10
end

Related

Cloud Run Error 504 (Upstream Request Timeout) after successful deploy

I was following this tutorial from Google to deploy a servise to Cloud Run (https://codelabs.developers.google.com/codelabs/cloud-run-hello-python3#5). In Cloud Shell my project is deployed successfully (screenshot below). However, once I click on the link I get timeout. If I test it locally from Cloud Shell it works fine.
Why could this be happening? Where could I get more data about the issue?
As mentioned in the Documentation :
For Cloud Run services, the request timeout setting specifies the time
within which a response must be returned by services deployed to Cloud
Run. If a response isn't returned within the time specified, the
request ends and error 504 is returned.
The timeout is set by default to 5 minutes and can be extended up to
60 minutes. You can change this setting when you deploy a container
image or by updating the service configuration. In addition to
changing the Cloud Run request timeout, you should also check your
language framework to see whether it has its own request timeout
setting that you must also update.
You can refer to this Public group issue which will be helpful in resolving the current error.
You can increase timeout by clicking EDIT & DEPLOY NEW REVISION and then adjust new Request timeout value

"Host header is specified and is not an IP address or localhost" message when using chromedp headless-shell

I'm trying to deploy chromedp/headless-shell to Cloud Run.
Here is my Dockerfile:
FROM chromedp/headless-shell
ENTRYPOINT [ "/headless-shell/headless-shell", "--remote-debugging-address=0.0.0.0", "--remote-debugging-port=9222", "--disable-gpu", "--headless", "--no-sandbox" ]
The command I used to deploy to Cloud Run is
gcloud run deploy chromedp-headless-shell --source . --port 9222
Problem
When I go to this path /json/list, I expect to see something like this
[{
"description": "",
"devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:9222/devtools/page/B06F36A73E5F33A515E87C6AE4E2284E",
"id": "B06F36A73E5F33A515E87C6AE4E2284E",
"title": "about:blank",
"type": "page",
"url": "about:blank",
"webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/B06F36A73E5F33A515E87C6AE4E2284E"
}]
but instead, I get this error:
Host header is specified and is not an IP address or localhost.
Is there something wrong with my configuration or is Cloud Run not the ideal choice for deploying this?
This specific issue is not unique to Cloud Run. It originates from an existing change in the Chrome DevTools Protocol which generates this error when accessing it remotely. It could be attributed to security measures against some types of attacks. You can see the related Chromium pull request here.
I deployed a chromedp/headless-shell container to Cloud Run using your configuration and also received the same error. Now, there is this useful comment in a GitHub issue showing a workaround for this problem, by passing a HOST:localhost header. While this does work when I tested it locally, it does not work on Cloud Run (returns a 404 error). This 404 error could be due to how Cloud Run also utilizes the HOST header to route requests to the correct service.
Unfortunately this answer is not a solution, but it sheds some light on what you are seeing and why. I would go for using a different service from GCP, such a GCE that are pure virtual machines and less managed.

How can I avoid "IN_USED_ADDRESSES" error when starting multiple Dataflow jobs from the same template?

I have created a Dataflow template which allows me to import data from CSV file in Cloud Storage into BigQuery. I use Cloud Function for Firebase to create jobs from this template at certain time everyday. This is the code in the Function (with some irrelevant parts removed).
const filePath = object.name?.replace(".csv", "");
// Exit function if file changes are in temporary or staging folder
if (
filePath?.includes("staging") ||
filePath?.includes("temp") ||
filePath?.includes("templates")
)
return;
const dataflow = google.dataflow("v1b3");
const auth = await google.auth.getClient({
scopes: ["https://www.googleapis.com/auth/cloud-platform"],
});
let request = {
auth,
projectId: process.env.GCLOUD_PROJECT,
location: "asia-east1",
gcsPath: "gs://my_project_bucket/templates/csv_to_bq",
requestBody: {
jobName: `csv-to-bq-${filePath?.replace(/\//g, "-")}`,
environment: {
tempLocation: "gs://my_project_bucket/temp",
},
parameters: {
input: `gs://my_project_bucket/${object.name}`,
output: biqQueryOutput,
},
},
};
return dataflow.projects.locations.templates.launch(request);
This function is triggered every time any file is written in Cloud Storage. I am working with sensors so at least I have to import 89 different data i.e. different CSV files within 15 minutes.
The whole process works fine if there are only 4 jobs working at the same time. However, when the function tried to create the fifth job, the API returned many different types of errors.
Error 1 (not exact since somehow I cannot find the error anymore):
Error Response: [400] The following quotas were exceeded: IN_USE_ADDRESSES
Error 2:
Dataflow quota error for jobs-per-project quota. Project *** is running 25 jobs.
Please check the quota usage via GCP Console.
If it exceeds the limit, please wait for a workflow to finish or contact Google Cloud Support to request an increase in quota.
If it does not, contact Google Cloud Support.
Error 3:
Quota exceeded for quota metric 'Job template requests' and limit 'Job template requests per minute per user' of service 'dataflow.googleapis.com' for consumer 'project_number:****'.
I know I can space out starting jobs to avoid Error 2 and 3. However, I don't know how to start jobs in a way that won't fill up the addresses. So, how do I avoid that? If I cannot, then what approach should I use?
I had answered this in another post here - Which Compute Engine quotas need to be updated to run Dataflow with 50 workers (IN_USE_ADDRESSES, CPUS, CPUS_ALL_REGIONS ..)?.
Let me know if that helps.
This is a GCP external IP quota issue and the best solution is not to use any public IPs for dataflow jobs as long as your pipeline resources stay within GCP networks.
To enable public IP in dataflow jobs:
Create or update your subnetwork to allow Private google access. this is fairly simple to do using the console - VPC > networks > subnetworks > tick enable private google access
In the parameters of your Cloud Dataflow job, specify --usePublicIps=false and --network=[NETWORK] or --subnetwork=[SUBNETWORK].
Note: - For internal IP IN_USED errors just change your subnet CIDR range to accommodate more addresses like 20.0.0.0/16 will give you close to 60k internal IP address.
By this, you will never be exceeding your internal IP ranges

How to access application default credentials from a GCE container without any Google API binding?

I am writing an application that runs in a container, on Google Container Engine, in a language that doesn't have any binding to the Google API.
I need to access the application default credentials. Unfortunately the official documentation doesn't explain how to do such a thing in production environment without using one of the existing bindings to the Google API.
In development environment (i.e. on my local dev machine) I export the GOOGLE_APPLICATION_CREDENTIALS variable, but it isn't available in the production container. Does it mean I have to use some endpoint from the REST API?
Ruby's implementation is open source and can be accessed here.
Different locations to check by priority
The get_application_default method clearly shows that:
the GOOGLE_APPLICATION_CREDENTIALS environment variable is checked,
then the PATH is checked,
then the default path /etc/google/auth is checked,
finally, if still nothing and on a compute instance, a new access token is fetched.
def get_application_default(scope = nil, options = {})
creds = DefaultCredentials.from_env(scope) ||
DefaultCredentials.from_well_known_path(scope) ||
DefaultCredentials.from_system_default_path(scope)
return creds unless creds.nil?
raise NOT_FOUND_ERROR unless GCECredentials.on_gce?(options)
GCECredentials.new
end
It is consistent with what says the official documentation:
The environment variable GOOGLE_APPLICATION_CREDENTIALS is checked. If
this variable is specified it should point to a file that defines the
credentials. [...]
If you have installed the Google Cloud SDK on your machine and have run
the command gcloud auth application-default login, your identity can
be used as a proxy to test code calling APIs from that machine.
If you
are running in Google App Engine production, the built-in service
account associated with the application will be used.
If you are running in Google Compute Engine production, the built-in service
account associated with the virtual machine instance will be used.
If none of these conditions is true, an error will occur.
Detecting GCE environment
The on_gce? method shows how to check whether we are on GCE by sending a GET/HEAD HTTP request to http://169.254.169.254. If there is a Metadata-Flavor: Google header in the response, then it's probably GCE.
def on_gce?(options = {})
c = options[:connection] || Faraday.default_connection
resp = c.get(COMPUTE_CHECK_URI) do |req|
# Comment from: oauth2client/client.py
#
# Note: the explicit `timeout` below is a workaround. The underlying
# issue is that resolving an unknown host on some networks will take
# 20-30 seconds; making this timeout short fixes the issue, but
# could lead to false negatives in the event that we are on GCE, but
# the metadata resolution was particularly slow. The latter case is
# "unlikely".
req.options.timeout = 0.1
end
return false unless resp.status == 200
return false unless resp.headers.key?('Metadata-Flavor')
return resp.headers['Metadata-Flavor'] == 'Google'
rescue Faraday::TimeoutError, Faraday::ConnectionFailed
return false
end
Fetching an access token directly from Google
If the default credentials could not be found on the filesystem and the application is running on GCE, we can ask a new access token without any prior authentication. This is possible because of the default service account, that is created automatically when GCE is enabled in a project.
The fetch_access_token method shows how, from a GCE instance, we can get a new access token by simply issuing a GET request to http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token.
def fetch_access_token(options = {})
c = options[:connection] || Faraday.default_connection
c.headers = { 'Metadata-Flavor' => 'Google' }
resp = c.get(COMPUTE_AUTH_TOKEN_URI)
case resp.status
when 200
Signet::OAuth2.parse_credentials(resp.body,
resp.headers['content-type'])
when 404
raise(Signet::AuthorizationError, NO_METADATA_SERVER_ERROR)
else
msg = "Unexpected error code #{resp.status}" + UNEXPECTED_ERROR_SUFFIX
raise(Signet::AuthorizationError, msg)
end
end
Here is a curl command to illustrate:
curl \
http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token \
-H 'accept: application/json' \
-H 'Metadata-Flavor: Google'

ELB command line tools not working

I have downloaded the elb packarge from the AWS site and set the required environment variables but I am unable to use the commands.
Can anyone tell me why I am getting this error:
elb-describe-lbs --aws-credential-file /home/prateek/credential-file-path.template
elb-describe-lbs: Refused: Signature not yet current: 20131118T182313Z is still later than
20131118T125753Z (20131118T125253Z + 5 min.)
AWSRequestId:56c9101b-5050-11e3-9f02-ff424c27b77f
It appears that your system time is off. Most api tools require your system time to be within 5 minutes of the current time. Its one of the elements used to verify the validity of the request.