GCP Stackdriver Agent: “write_gcm: can not take infinite value” Error

PROBLEM

When running the stackdriver-agent (v6.x), the log file contains the following errors:-

collectd[1496]: write_gcm: can not take infinite value
collectd[1496]: write_gcm: wg_typed_value_create_from_value_t_inline failed for swap/percent/value! Continuing.
collectd[1496]: write_gcm: can not take infinite value
collectd[1496]: write_gcm: wg_typed_value_create_from_value_t_inline failed for swap/percent/value! Continuing.
collectd[1496]: write_gcm: can not take infinite value
collectd[1496]: write_gcm: wg_typed_value_create_from_value_t_inline failed for swap/percent/value! Continuing.

SOLUTION

By default, the swap metric is enabled.

To verify, go to /etc/stackdriver/collectd.conf and locate the following configuration:-

LoadPlugin swap
<Plugin "swap">
  ValuesPercentage true
</Plugin>

This error occurs because the VM instance does not have swap memory, hence this metric plugin tries to divide by 0.

To verify the VM’s swap memory:-

$ free -m
              total        used        free      shared  buff/cache   available
Mem:           3536         191        3197           8         147        3151
Swap:             0           0           0

To fix this, remove this configuration and restart stackdriver-agent.

Terraform: Handling Errors with try(…)

PROBLEM

Given the following output block:-

output "subnet_uc1" {
  description = "Subnets in `us-central1` region for all 3 products"
  value = {
    artifactory = module.subnet_uc1_artifactory.subnets.name
    xray        = module.subnet_uc1_xray.subnets.name
    mc          = module.subnet_uc1_mc.subnets.name
  }
}

Sometimes, during an apply or destroy, we may get this error:-

Error: Attempt to get attribute from null value

  on outputs.tf line 40, in output "subnet_uc1":
  40:     artifactory = module.subnet_uc1_artifactory.subnets.name
    |----------------
    | module.subnet_uc1_artifactory.subnets is null

This value is null, so it does not have any attributes.


Error: Attempt to get attribute from null value

  on outputs.tf line 41, in output "subnet_uc1":
  41:     xray        = module.subnet_uc1_xray.subnets.name
    |----------------
    | module.subnet_uc1_xray.subnets is null

This value is null, so it does not have any attributes.


Error: Attempt to get attribute from null value

  on outputs.tf line 42, in output "subnet_uc1":
  42:     mc          = module.subnet_uc1_mc.subnets.name
    |----------------
    | module.subnet_uc1_mc.subnets is null

This value is null, so it does not have any attributes.

One way to fix this is to do conditional expressions like this, but it’s not pretty:-

output "subnet_uc1" {
  description = "Subnets in `us-central1` region for all 3 products"
  value = {
    artifactory = module.subnet_uc1_artifactory.subnets != null ? module.subnet_uc1_artifactory.subnets.name: ""
    xray        = module.subnet_uc1_xray.subnets != null ?module.subnet_uc1_xray.subnets.name: ""
    mc          = module.subnet_uc1_mc.subnets != null ? module.subnet_uc1_mc.subnets.name: ""
  }
}

SOLUTION

Since Terraform v0.12.20, we can solve this with try and achieve the same outcome:-

output "subnet_uc1" {
  description = "Subnets in `us-central1` region for all 3 products"
  value = {
    artifactory = try(module.subnet_uc1_artifactory.subnets.name, "")
    xray        = try(module.subnet_uc1_xray.subnets.name, "")
    mc          = try(module.subnet_uc1_mc.subnets.name, "")
  }
}

GCP: Deleting Project with Lien… Quickly

PROBLEM

The whole idea of placing a lien on a project is to prevent accidental deletion.

But, sometimes it’s a little pain in the ass to attempt a project deletion in GCP Console only to find out a lien was set, especially during the development phase.

Then, we grumpily open up Cloud Shell and run a series of commands to delete the project.

SOLUTION

To play fast and loose in the name of Shitty Agile, create a script called delete-project.sh with the following content:-

#!/bin/bash

set -e

project_id="$1"

gcloud config set project "${project_id}"

lien_id=$(gcloud alpha resource-manager liens list --format=json | jq -r '.[0] .name' | sed -e 's/liens\///g')

[[ "${lien_id}" != "null" ]] && gcloud alpha resource-manager liens delete "${lien_id}"

gcloud projects delete "${project_id}" --quiet

This script will delete a project regardless the existence of lien.

From Cloud Shell, run this bad boy:-

./delete-project.sh [PROJECT_ID]

Terraform: Skipping Buggy Provider Version

PROBLEM

Given the following required_providers block…

terraform {
  required_providers {
    google = "~> 3.8"
  }
}

… it will allow the following Google provider version: >= 3.8, < 4.0.

As of today (May 10), the latest Google provider is 3.20.0. A quick terraform init confirms that.

Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "google" (hashicorp/google) 3.20.0...

However, sometimes, there’s a need to skip a buggy version. For example, 3.20.0 breaks google_compute_firewall.

SOLUTION

To achieve that, we can do the following…

terraform {
  required_providers {
    google = "~> 3.8, != 3.20.0"
  }
}

To confirm this works, after deleting .terraform/ dir, terraform init now shows the following result…

Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "google" (hashicorp/google) 3.19.0...

GCP + Terraform: Running Terraform Commands with a Service Account

PROBLEM

When running these commands…

gcloud auth login
gcloud auth application-default login

… it allows terraform apply to provision the infrastructure using your credential.

However, sometimes there’s a need to run Terraform using a service account.

SOLUTION

First, identify the service account you want to use… for example: my-service-account@my-project.iam.gserviceaccount.com.

Then, create and download the private key for the service account.

Command:

gcloud iam service-accounts keys create --iam-account my-service-account@my-project.iam.gserviceaccount.com  key.json              

Output:

created key [xxxxxxxx] of type [json] as [key.json] for [my-service-account@my-project.iam.gserviceaccount.com]

With this service account’s private key, we can now authorize its access to GCP.

Command:

gcloud auth activate-service-account --key-file key.json  

Output:

Activated service account credentials for: [my-service-account@my-project.iam.gserviceaccount.com]

You can verify whether the right account is being used or not.

Command:

gcloud auth list

Output:

                      Credentialed Accounts
ACTIVE  ACCOUNT
*       my-service-account@my-project.iam.gserviceaccount.com
        user@myshittycode.com

To set the active account, run:
    $ gcloud config set account `ACCOUNT`

In this case, the * marks the active account being used.

Now, you can run terraform apply to provision the infrastructure using the selected service account.