Skip to content

Commit

Permalink
Feature/stackdriver credentials object (#89)
Browse files Browse the repository at this point in the history
* added support passing credentials object parameter to Stackdriver

* updated API doc with object credentials

* added credentials object into cli

* updated cli and api docs

* fix
  • Loading branch information
jhonny111s committed Nov 26, 2020
1 parent 1c7fa01 commit ae3b6a6
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 9 deletions.
6 changes: 4 additions & 2 deletions docs/API.md
Expand Up @@ -35,9 +35,11 @@ const writeStream = stackdriver.createWriteStream({

#### credentials

Type: `String` *(optional)*
Type: `String` or `{ client_email: String, private_key: String }` *(optional)*

Full path to the JSON file containing the Google Service Credentials,
you can also use an object parameter with the client_email and the private_key instead of the path. Defaults to the GOOGLE_APPLICATION_CREDENTIALS environment variable. At least one has to be available.

Full path to the JSON file containing the Google Service Credentials. Defaults to the GOOGLE_APPLICATION_CREDENTIALS environment variable. At least one has to be available.

#### projectId

Expand Down
4 changes: 3 additions & 1 deletion docs/CLI.md
Expand Up @@ -22,7 +22,9 @@ You can pass the following options via cli arguments:
| ------------- | ------------ |-------------|
| -V | --version | Output the version number |
| -p | --project <project> | Your Google Cloud Platform project ID (or use env var PROJECT_ID) |
| -c | --credentials <credentials> | The file path of the JSON file that contains your service account key (or use env var GOOGLE_APPLICATION_CREDENTIALS) |
| -c | --credentials <credentials> | The file path of the JSON file that contains your service account key (or use env var GOOGLE_APPLICATION_CREDENTIALS). you can also use clientEmail and privateKey instead of the path. |
| -e | --clientEmail <email> | Client email, part of credentials object provided by Google |
| -g | --privateKey <googleKey> | Private key, part of credentials object provided by Google. |
| -k | --key <key:customKey> | Repeatable `key:customKey` pairs for custom keys (see [API docs](./API.md#keys))
| -n | --logName | The resource to send logs to. Defaults to `{"type": "global"}`.
| -r | --resource <resourcejson> | Resource to send the logs to, input in JSON (see [API docs](./API.md#resource))
Expand Down
2 changes: 1 addition & 1 deletion pino-stackdriver.d.ts
Expand Up @@ -5,7 +5,7 @@ declare namespace PinoStackdriver {
* Full path to the JSON file containing the Google Service Credentials.
* Defaults to GOOGLE_APPLICATION_CREDENTIALS environment variable.
*/
credentials?: string;
credentials?: string | {client_email: string, private_key:string};

/**
* The name of the project.
Expand Down
12 changes: 10 additions & 2 deletions src/cli.js
Expand Up @@ -13,14 +13,22 @@ function collect (value, previous) {
function main () {
program
.version(pkg.version)
.option('-e, --clientEmail <email>', 'Client email, part of credentials object provided by Google')
.option('-g, --privateKey <googleKey>', 'Private key, part of credentials object provided by Google')
.option('-c, --credentials <credentials>', 'The file path of the JSON file that contains your service account key')
.option('-p, --project <project>', 'Your Google Cloud Platform project ID')
.option('-k, --key <key:customKey>', 'Customize additional data to include in log metadata', collect, [])
.option('-n, --logName <name>', 'The name of the log. Defaults to "pino_log".')
.option('-r, --resource <resource>', 'The resource to send logs to. Defaults to { "type": "global" }', JSON.parse, { type: 'global' })
.action(({ credentials, project, key, logName, resource }) => {
.action(({ credentials, project, key, logName, resource, clientEmail, privateKey }) => {
try {
const _credentials = credentials || process.env.GOOGLE_APPLICATION_CREDENTIALS
if (!clientEmail ^ !privateKey) throw Error('Client email or google private key is missing.')
if (credentials && (clientEmail || privateKey)) {
throw Error('Use client email and google private key or credentials, no both!')
}
const _credentials = (clientEmail && privateKey)
? { client_email: clientEmail, private_key: privateKey }
: (credentials || process.env.GOOGLE_APPLICATION_CREDENTIALS)
if (!process.env.PROJECT_ID && !project) { throw Error('Project is missing.') }
const _project = project || process.env.PROJECT_ID

Expand Down
12 changes: 9 additions & 3 deletions src/stackdriver.js
Expand Up @@ -86,16 +86,22 @@ module.exports.toLogEntryStream = function (options = {}) {

module.exports.toStackdriverStream = function (options = {}) {
const { logName, projectId, credentials, fallback } = options
if (process.env.GOOGLE_APPLICATION_CREDENTIALS || credentials) {
process.env.GOOGLE_APPLICATION_CREDENTIALS = process.env.GOOGLE_APPLICATION_CREDENTIALS || credentials
}
if (!projectId) { throw Error('The "projectId" argument is missing') }
const opt = {
logName: logName || 'pino_log',
projectId,
scopes: ['https://www.googleapis.com/auth/logging.write'],
fallback
}

if (typeof credentials === 'object' && credentials !== null) {
opt.credentials = credentials
} else {
if (process.env.GOOGLE_APPLICATION_CREDENTIALS || credentials) {
process.env.GOOGLE_APPLICATION_CREDENTIALS = process.env.GOOGLE_APPLICATION_CREDENTIALS || credentials
}
}

const log = new Logging(opt).log(opt.logName)
const writableStream = new stream.Writable({
objectMode: true,
Expand Down
36 changes: 36 additions & 0 deletions test/cli.test.js
Expand Up @@ -108,3 +108,39 @@ test('pipes data to output', (t) => {
app.kill()
})
})

test('throws on Client email or google private key is missing', t => {
t.plan(1)
delete process.env.GOOGLE_APPLICATION_CREDENTIALS
const app = spawn('node', [appPath, '-e', 'email@example.com', '-p', 'project-id'])
app.stdout.on('data', (data) => {
const msg = data.toString()
const res = (msg.indexOf('Client email or google private key is missing.') >= 0)
t.ok(res)
app.kill()
})
})

test('throws on use client email and google private key or credentials', t => {
t.plan(1)
delete process.env.GOOGLE_APPLICATION_CREDENTIALS
const app = spawn('node', [appPath, '-c', './credentials.json', '-e', 'email@example.com', '-g', 'private-key', '-p', 'project-id'])
app.stdout.on('data', (data) => {
const msg = data.toString()
const res = (msg.indexOf('Use client email and google private key or credentials, no both!') >= 0)
t.ok(res)
app.kill()
})
})

test('works passing object credentials', t => {
t.plan(1)
delete process.env.GOOGLE_APPLICATION_CREDENTIALS
const app = spawn('node', [appPath, '-e', 'email@example.com', '-g', 'private-key', '-p', 'project-id'])
app.stdout.on('data', (data) => {
const msg = data.toString()
const res = (msg.indexOf('logging') >= 0)
t.ok(res)
app.kill()
})
})
14 changes: 14 additions & 0 deletions test/stackdriver.test.js
Expand Up @@ -233,3 +233,17 @@ test('transforms log entry message field', t => {
const entry = tested.toLogEntry(log)
t.ok(entry.data.message === 'Message')
})

test('logs to stackdriver with an object credentials', t => {
t.plan(1)
const credentials = { client_email: 'fakeEmail', private_key: 'fakeKey' }
const projectId = 'test-project'

const writeStream = tested.toStackdriverStream({ credentials, projectId })
writeStream.on('finish', () => {
t.ok(true)
})
const entry = { meta: { resource: { type: 'global' }, severity: 'info' }, data: { message: 'Info message' } }
const readStream = helpers.readStreamTest([entry])
readStream.pipe(writeStream)
})

0 comments on commit ae3b6a6

Please sign in to comment.