Skip to content

Commit

Permalink
Initial implementation of diagnose CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
Adam Yeats committed Sep 2, 2020
1 parent 453745f commit c376bb0
Show file tree
Hide file tree
Showing 9 changed files with 227 additions and 34 deletions.
63 changes: 50 additions & 13 deletions packages/nodejs-ext/scripts/extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ const {
hasSupportedOs
} = require("./extension/helpers")

const { createReport } = require("./report")
const {
createReport,
createBuildReport,
createDownloadReport
} = require("./report")

const EXT_PATH = path.join(__dirname, "/../ext/")

Expand Down Expand Up @@ -60,9 +64,23 @@ function verify(filepath, checksum) {
})
}

function getMetadataForTarget(report) {
const { architecture, target, muslOverride } = report.build
function dumpReport(report) {
return new Promise(resolve => {
fs.writeFile(
"/tmp/appsignal-install-report.json",
JSON.stringify(report, null, 2),
() => {
return resolve()
}
)
})
}

function getMetadataForTarget({
architecture,
target,
musl_override: muslOverride
}) {
const triple = [
architecture === "x64" ? "x86_64" : "i686",
`-${target}`,
Expand All @@ -74,15 +92,13 @@ function getMetadataForTarget(report) {

// Script logic begins here
;(function () {
const report = createReport()

if (hasLocalBuild()) {
// check for a local build (dev only)
console.warn(`Using local build for agent. Skipping download.`)
return process.exit(0)
}

if (!hasSupportedArchitecture(report)) {
if (!hasSupportedArchitecture(process.arch)) {
console.error(
`AppSignal currently does not support your system architecture
(${process.platform} ${process.arch}). Please let us know at
Expand All @@ -92,7 +108,7 @@ function getMetadataForTarget(report) {
return process.exit(1)
}

if (!hasSupportedOs(report)) {
if (!hasSupportedOs(process.platform)) {
console.error(
`AppSignal currently does not support your operating system (${process.platform}).
Please let us know at support@appsignal.com, we aim to support everything
Expand All @@ -102,23 +118,44 @@ function getMetadataForTarget(report) {
return process.exit(1)
}

const report = createReport()
report.build = createBuildReport({})

// try and get one from the CDN
const metadata = getMetadataForTarget(report)
const metadata = getMetadataForTarget(report.build)
const filename = metadata.downloadUrl.split("/")[4]
const outputPath = path.join(EXT_PATH, filename)

return download(metadata.downloadUrl, outputPath)
.then(filepath => {
return verify(filepath, metadata.checksum).then(() => extract(filepath))
})
.then(filepath =>
verify(filepath, metadata.checksum).then(() => extract(filepath))
)
.then(() => {
// once extracted, we can then hand off to node-gyp for building
// @TODO: add cleanup step
console.log("The agent has installed successfully!")
process.exit(0)

report.download = createDownloadReport({
verified: true,
downloadUrl: metadata.downloadUrl
})

report.result.status = "success"

return dumpReport(report).then(() => {
process.exit(0)
})
})
.catch(error => {
console.error(error)
process.exit(1)

report.download = createDownloadReport({
verified: false,
downloadUrl: metadata.downloadUrl
})

return dumpReport(report).then(() => {
process.exit(1)
})
})
})()
14 changes: 4 additions & 10 deletions packages/nodejs-ext/scripts/extension/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,9 @@ function hasLocalBuild() {
*
* @return {boolean}
*/
function hasSupportedArchitecture(report) {
function hasSupportedArchitecture(arch) {
// 'x32' and 'x64' supported
return (
report.build.architecture === "x32" || report.build.architecture === "x64"
)
return arch === "x32" || arch === "x64"
}

/**
Expand All @@ -36,12 +34,8 @@ function hasSupportedArchitecture(report) {
*
* @return {boolean}
*/
function hasSupportedOs(report) {
return (
report.build.target === "darwin" ||
report.build.target === "freebsd" ||
report.build.target === "linux"
)
function hasSupportedOs(os) {
return os === "darwin" || os === "freebsd" || os === "linux"
}

/**
Expand Down
46 changes: 36 additions & 10 deletions packages/nodejs-ext/scripts/report.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,51 @@
const path = require("path")

const { AGENT_VERSION } = require("./extension/constants")
const { hasMusl } = require("./extension/helpers")

function createReport() {
return {
result: { status: "incomplete" },
language: {
name: "nodejs",
version: process.versions["node"]
},
build: {
time: new Date().toISOString(),
packagePath: path.join(__dirname, "/../ext/"),
architecture: process.arch,
target: process.platform,
muslOverride: (process.env["APPSIGNAL_BUILD_FOR_MUSL"] === "true") || hasMusl(),
libraryType: "static"
version: process.versions["node"],
implementation: "nodejs"
},
download: {},
build: {},
host: {
rootUser: process.getuid && process.getuid() === 0,
dependencies: {}
}
}
}

exports.createReport = createReport
function createBuildReport({ isLocalBuild = false }) {
return {
time: new Date().toISOString(),
package_path: path.join(__dirname, "/../ext/"),
architecture: process.arch,
target: process.platform,
musl_override:
process.env["APPSIGNAL_BUILD_FOR_MUSL"] === "true" || hasMusl(),
library_type: "static",
dependencies: {},
flags: {},
source: isLocalBuild ? "local" : "remote",
agent_version: AGENT_VERSION
}
}

function createDownloadReport({ verified = false, downloadUrl: download_url }) {
return {
checksum: verified ? "verified" : "unverified",
http_proxy: null,
download_url
}
}

module.exports = {
createReport,
createBuildReport,
createDownloadReport
}
52 changes: 52 additions & 0 deletions packages/nodejs/bin/diagnose
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/usr/bin/env node

const https = require("https")
const { DiagnoseTool } = require("../dist/diagnose")

// enable diagnose mode
process.env["_APPSIGNAL_DIAGNOSE"] = "true"

const tool = new DiagnoseTool()

console.log(`
🔧 AppSignal Diagnose Tool
Use this information to debug your configuration.
More information is available on the documentation site.
https://docs.appsignal.com/
Send this output to support@appsignal.com if you need help.
`)

if (!process.env["APPSIGNAL_PUSH_API_KEY"]) {
throw new Error(`No Push API key found. Set the APPSIGNAL_PUSH_API_KEY environment variable to your Push API key and try again.`)
}

const data = tool.generate()
const json = JSON.stringify(data)

const opts = {
port: 443,
method: "POST",
host: "appsignal.com",
path: "/diag",
headers: {
"Content-Type": "application/json",
"Content-Length": json.length
}
}

const req = https.request(opts, res => {
res.setEncoding("utf8")
})

req.on("error", e => {
console.error(`Problem with diagnose request: ${e.message}`)
})

// Write data to request body
req.write(json)
req.end()

console.log(data, "\n")
console.log("✅ Done!")
3 changes: 3 additions & 0 deletions packages/nodejs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"main": "dist/index",
"types": "dist/index",
"license": "MIT",
"bin": {
"appsignal-diagnose": "./bin/diagnose"
},
"dependencies": {
"@appsignal/core": "^1.0.9",
"@appsignal/types": "^1.0.0",
Expand Down
3 changes: 2 additions & 1 deletion packages/nodejs/scripts/create-versionfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ const fs = require("fs")
const pkg = require("../package.json")

const data = `// Do not touch this file, auto-generated by scripts/create-versionfile
export const VERSION = "${pkg.version}"\n`
export const VERSION = "${pkg.version}"
export const AGENT_VERSION = "6251929"\n`

fs.writeFile(`${process.cwd()}/src/version.ts`, data, "utf8", err => {
if (err) throw err
Expand Down
4 changes: 4 additions & 0 deletions packages/nodejs/src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,8 @@ export class Agent {

return this.isLoaded
}

public diagnose(): string {
return JSON.parse(extension.diagnoseRaw())
}
}
75 changes: 75 additions & 0 deletions packages/nodejs/src/diagnose.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import fs from "fs"

import { Agent } from "./agent"
import { Configuration } from "./config"
import { AGENT_VERSION, VERSION } from "./version"

export class DiagnoseTool {
#config: Configuration
#agent: Agent

constructor() {
this.#config = new Configuration({})
this.#agent = new Agent({ active: true })
}

/**
* Reports are serialized to JSON and send to an endpoint that expects
* snake_case keys, thus the keys in the report on this side must be snake cased also.
*/
public generate() {
return {
library: this.getLibraryData(),
installation: this.getInstallationReport(),
host: this.getHostData(),
app: {},
agent: this.#agent.diagnose(),
config: {
options: {},
sources: {}
},
validation: { push_api_key: "valid" },
process: {
uid: process.getuid()
},
paths: this.getPathsData()
}
}

private getLibraryData() {
return {
language: "nodejs",
package_version: VERSION,
agent_version: AGENT_VERSION,
extension_loaded: this.#agent.isLoaded
}
}

private getHostData() {
return {
architecture: process.arch,
os: process.platform,
language_version: process.versions.node,
heroku: !!process.env["DYNO"],
root_user: process.getuid() === 0,
running_in_container: false
}
}

private getInstallationReport() {
try {
const report = fs.readFileSync(
"/tmp/appsignal-install-report.json",
"utf8"
)

return report ? JSON.parse(report) : {}
} catch (e) {
return {}
}
}

private getPathsData() {
return {}
}
}
1 change: 1 addition & 0 deletions packages/nodejs/src/version.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
// Do not touch this file, auto-generated by scripts/create-versionfile
export const VERSION = "0.6.1"
export const AGENT_VERSION = "6251929"

0 comments on commit c376bb0

Please sign in to comment.