Repository: godaddy/kubernetes-external-secrets (js)

Issue:
Support `isBinary: true` for GCP secrets manager
This is useful if you have a secret (for example a JSON file) that is already base64 encoded in GCP secrets manager.

With this I can then create a secret that looks like 

```data:
  credentials.json: |
  { ...}
```

which is needed e.g. when writing out GCP SA files.

Pull request description:
feat: support isBinary for GCP
Fixes #352

Patch:
diff --git a/README.md b/README.md
index 90838b7..6293f51 100644
--- a/README.md
+++ b/README.md
@@ -435,6 +435,7 @@ kubernetes-external-secrets exposes the following metrics over a prometheus endp
 | Metric                                    | Description                                                                     | Example                                                                       |
 | ----------------------------------------- | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
 | `sync_calls`                              | This metric counts the number of sync calls by backend, secret name and status  | `sync_calls{name="foo",namespace="example",backend="foo",status="success"} 1` |
+| `last_state`                              | A value of -1 or 1 where -1 means the last sync_call was an error and 1 means the last sync_call was a success  | `last_state{name="foo",namespace="example",backend="foo"} 1` |
 
 
 ## Development
diff --git a/lib/backends/gcp-secrets-manager-backend.js b/lib/backends/gcp-secrets-manager-backend.js
index 9bdf9e7..5f497f1 100644
--- a/lib/backends/gcp-secrets-manager-backend.js
+++ b/lib/backends/gcp-secrets-manager-backend.js
@@ -17,14 +17,19 @@ class GCPSecretsManagerBackend extends KVBackend {
   /**
    * Get secret property value from GCP Secrets Manager.
    * @param {string} key - Key used to store secret property value in GCP Secrets Manager.
+   * @param {boolean} keyOptions.isBinary - Is the secret base64 encoded? Set to true to handle as binary.
    * @returns {Promise} Promise object representing secret property value.
    */
-  async _get ({ key }) {
+  async _get ({ key, keyOptions }) {
     this._logger.info(`fetching secret ${key} from GCP Secret Manager`)
     const version = await this._client.accessSecretVersion({
       name: key
     })
     const secret = { value: version[0].payload.data.toString('utf8') }
+    // Handle binary files - this is useful when you've stored a base64 encoded string
+    if (keyOptions && keyOptions.isBinary) {
+      return Buffer.from(secret.value, 'base64')
+    }
     return JSON.stringify(secret)
   }
 }
diff --git a/lib/metrics.js b/lib/metrics.js
index 83095e0..efa4a88 100644
--- a/lib/metrics.js
+++ b/lib/metrics.js
@@ -15,6 +15,12 @@ class Metrics {
       labelNames: ['name', 'namespace', 'backend', 'status'],
       registers: [registry]
     })
+    this._lastState = new Prometheus.Gauge({
+      name: 'last_state',
+      help: 'Value -1 if the last sync was a failure, 1 otherwise.',
+      labelNames: ['name', 'namespace', 'backend'],
+      registers: [registry]
+    })
   }
 
   /**
@@ -31,6 +37,19 @@ class Metrics {
       backend,
       status
     })
+    if (status === 'success') {
+      this._lastState.set({
+        name,
+        namespace,
+        backend
+      }, 1)
+    } else {
+      this._lastState.set({
+        name,
+        namespace,
+        backend
+      }, -1)
+    }
   }
 }
 


Test patch:
diff --git a/lib/metrics-server.test.js b/lib/metrics-server.test.js
index f5c8b7e..b9ffe2b 100644
--- a/lib/metrics-server.test.js
+++ b/lib/metrics-server.test.js
@@ -57,5 +57,7 @@ describe('MetricsServer', () => {
 
     expect(res.text).to.have.string('sync_calls{name="foo",namespace="example",backend="foo",status="success"} 1')
     expect(res.text).to.have.string('sync_calls{name="bar",namespace="example",backend="foo",status="failed"} 1')
+    expect(res.text).to.have.string('last_state{name="foo",namespace="example",backend="foo"} 1')
+    expect(res.text).to.have.string('last_state{name="bar",namespace="example",backend="foo"} -1')
   })
 })