Cloud
banshee/cloud-collection/cloud/google/https/
cloud-sql
The state of each Cloud SQL instance of a Google Cloud project, stopped told apart from running, by the Cloud SQL Admin API.
by banshee0 installations1 version
Published on October 27, 2026. First version.
// The Cloud SQL instances of a Google Cloud project, by the Cloud SQL Admin API with a service account: the state
// of each instance, stopped told apart from running.
// Reads POST https://oauth2.googleapis.com/token by google.token, then GET
// https://sqladmin.googleapis.com/v1/projects/<project>/instances, page after page.
need('protocol:http');
need('protocol:google');
$project = variable('google:project', 'text', array(
'label' => 'Project',
'help' => 'The ID of the Google Cloud project, as the console writes it under Project ID: my-project-123456.',
));
per('database', 'Databases');
output('state', 'text', array('label' => 'State', 'per' => 'database'));
watch.expect('not-running', 'Database not running', 'state', array('critical' => array('runnable'), 'for' => '2 runs'));
watch.missing('missing', 'Database missing', 'state', array('enabled' => false));
// The access token of the service account for this scope, asked by google.token and kept in the cache,
// encrypted on the poller, fifty minutes, under the hour Google gives it.
function access_token($scope) {
if (cache.has('token')) {
return cache.get('token');
}
$token = google.token($scope);
cache.set('token', $token, 3000);
return $token;
}
// One GET on a Google Cloud API with the token kept in the cache; a token Google no longer takes answers 401,
// and a new one is asked for, once.
function google_get($url, $scope, $what) {
$answer = http.get($url, headers=array('Authorization' => 'Bearer ' . access_token($scope)));
if ($answer['status'] == 401) {
cache.delete('token');
$answer = http.get($url, headers=array('Authorization' => 'Bearer ' . access_token($scope)));
}
if ($answer['status'] == 403) {
fail('misconfigured', 'The service account may not read ' . $what . ', code 403',
'Check its role on the project, and that the API is enabled on the project.');
}
if ($answer['status'] == 400 || $answer['status'] == 404) {
fail('misconfigured', 'Google Cloud refuses to read ' . $what . ', code ' . $answer['status'],
'Check the project and the names set on the probe.');
}
if ($answer['status'] != 200) {
fail('unreachable', 'Google Cloud answers the reading of ' . $what . ' with code ' . $answer['status']);
}
return json.read($answer['body']);
}
// {"items": [{"name": "orders-db", "state": "RUNNABLE", "databaseVersion": "POSTGRES_16", "region":
// "europe-west1", "settings": {"activationPolicy": "ALWAYS", …}, …}], "nextPageToken": "…"}. An instance the owner
// stopped stays RUNNABLE, its activation policy set to NEVER: it is written stopped.
$base = 'https://sqladmin.googleapis.com/v1/projects/' . url.encode($project) . '/instances';
$token = '';
while (true) {
$url = $base;
if ($token != '') {
$url = $base . '?' . url.query(array('pageToken' => $token));
}
$page = google_get($url, 'https://www.googleapis.com/auth/sqlservice.admin', 'the Cloud SQL instances');
foreach (list.get($page, 'items', array()) as $instance) {
$state = text.lower($instance['state']);
$policy = list.get(list.get($instance, 'settings', array()), 'activationPolicy', '');
if ($state == 'runnable' && $policy == 'NEVER') {
$state = 'stopped';
}
value('state', $state, $instance['name']);
}
$token = list.get($page, 'nextPageToken', '');
if ($token == '') {
break;
}
}