I am pretty new to using gcloud functions.
I have a google cloud function which is supposed to start up a VM instance, but I want to invoke this function from a website without authentication (for now) when sending the HTTP request. How can I do this?
I also don't need to send any params, just need to invoke function
Here is my code for a simple website to invoke the cloud function, though this seems to be using auth which I don't want to do, I have disabled authentication when making the function in the console:
<!DOCTYPE html>
<html lang="en">
<body>
<div class="bordered-text-block">
<h1>VM Controls</h1>
<button id="id_start_button" onclick="startVM()">Start
</button>
<button id="id_stop_button" onclick="stopVM()">Stop
</button>
</div>
<script>
function startVM() {
const fetch = require('node-fetch');
// TODO(developer): set these values
const REGION = '<my region>';
const PROJECT_ID = '<project name>';
const RECEIVING_FUNCTION = '<function name>';
// Constants for setting up metadata server request
// See https://cloud.google.com/compute/docs/instances/verifying-instance-identity#request_signature
const functionURL = `https://${REGION}-${PROJECT_ID}.cloudfunctions.net/${RECEIVING_FUNCTION}`;
const metadataServerURL =
'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=';
const tokenUrl = metadataServerURL + functionURL;
exports.callingFunction = async (req, res) => {
// Fetch the token
const tokenResponse = await fetch(tokenUrl, {
headers: {
'Metadata-Flavor': 'Google',
},
});
const token = await tokenResponse.text();
// Provide the token in the request to the receiving function
try {
const functionResponse = await fetch(functionURL, {
headers: { Authorization: `bearer ${token}` },
});
res.status(200).send(await functionResponse.text());
} catch (err) {
console.error(err);
res.status(500).send('An error occurred! See logs for more details.');
}
};
}
</script>
</body>
</html>
Following up on my comment, here's the simplest answer that should work.
NOTE I think you must ensure your Cloud Functions returns appropriate CORS headers
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
I wrote a Cloud Function in Golang:
package p
import (
"encoding/json"
"net/http"
)
type Message struct {
Name string `json:"name"`
}
func WebPage(w http.ResponseWriter, r *http.Request) {
m := Message{
Name: "Freddie",
}
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET")
w.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(w).Encode(m)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
}
I deployed this Function:
PROJECT="<your-project>"
REGION="<your-region>"
FUNCTION="<your-function>"
gcloud projects create ${PROJECT}
BILLING=$(gcloud beta billing accounts list --format="value(name)")
gcloud beta billing projects link ${PROJECT} --billing-account=${BILLING}
for SERVICE in "cloudbuild" "cloudfunctions"
do
gcloud services enable ${SERVICE}.googleapis.com \
--project=${PROJECT}
done
gcloud functions deploy ${FUNCTION} \
--entry-point=WebPage \
--source=. \
--project=${PROJECT} \
--allow-unauthenticated \
--region=${REGION} \
--runtime=go113 \
--trigger-http
You can grab your function's endpoint in curl:
URL=$(\
gcloud functions describe ${FUNCTION} \
--project=${PROJECT} \
--format="value(httpsTrigger.url)")
curl \
--silent \
--include \
--header "Accept: application/json" \
--request GET \
${URL}
Yields:
HTTP/2 200
access-control-allow-methods: GET
access-control-allow-origin: *
content-type: application/json
...
{"name":"Freddie"}
Then you can GET
the value and put it into an HTML id
:
<!DOCTYPE html>
<html lang="en">
<body>
<div id="result">/div>
<script>
const PROJECT = "<your-project>";
const REGION = "<your-region>";
const FUNCTION = "<your-function>";
const ID = "result";
const URL = `https://${REGION}-${PROJECT}.cloudfunctions.net/${FUNCTION}`;
fetch(URL,{
method: "GET",
headers: {
"Accept": "application/json"
}
})
.then(resp => resp.json())
.then(json => JSON.stringify(json))
.then(html => document.getElementById(ID).innerHTML = html);
</script>
</body>
</html>
I tested this (locally) using Caddy:
docker run \
--rm \
--name=caddy \
--interactive --tty \
--publish=80:80 \
--volume=$PWD/index.html:/usr/share/caddy/index.html \
--volume=caddy_data:/data \
caddy
And then browsing http://localhost:80
, I get:
{"name":"Freddie"}