Code Monkey home page Code Monkey logo

ibm / newrelic-cli Goto Github PK

View Code? Open in Web Editor NEW
62.0 22.0 30.0 194 KB

New Relic CLI is a command line tool which is used to operate New Relic objects(Synthetic monitors, alert policies, conditions, account users etc). You can use it easily to list/get/create/delete these objects. It can be used to backup your New Relic configuration data and restore in the future. It is easy to be used other than calling different REST API endpoints.

License: Apache License 2.0

Makefile 0.31% Go 97.34% Shell 2.35%

newrelic-cli's Introduction

newrelic-cli

Build Status

New Relic CLI is a command line tool which is used to operate New Relic objects (Synthetic monitors, alert policies, conditions, account users etc). You can use it easily to list/get/create/delete these objects. It can be used to backup your New Relic configuration data and restore in the future. It is easy to be used other than calling different REST API endpoints.

Wiki

Wiki

Command

Command Subcommand Resource arguments flag
nr get users -
nr get user <id>
nr get monitors -
nr get monitor <id>
nr get labels -
nr get labelsmonitors <category:label>
nr get alertspolicies -
nr get alertsconditions -
nr get alertschannels -
nr get dashboards -
nr get dashboard <id>
nr create monitor - -f <monitor_sample.json>
nr create alertspolicies - -f <alertspolicies_sample.json>
nr create alertsconditions - -f <alertsconditions_sample.json>
nr create alertschannels - -f <alertschannels_sample.json>
nr add alertschannels <id> <category:label>
nr update monitor - -f <monitor_sample.json>
nr update alertspolicies - -f <alertspolicies_sample.json>
nr update alertsconditions - -f <alertsconditions_sample.json>
nr update alertschannels - -f <alertschannels_sample.json>
nr patch monitor - -f <monitor_sample.json>
nr delete monitor <id>
nr delete alertspolicies <id>
nr delete alertsconditions <id>
nr delete alertschannels <id>
nr delete labelsmonitors <id> <category:label>
nr insert customevents - -f <custom_events.json>
-i <New Relic insert key>
-a <New Relic account ID>
nr backup monitors - -d <backup_folder>
-r <result_file.log>
nr backup alertsconditions - -d <backup_folder>
-r <result_file.log>
nr backup dashboards - -d <backup_folder>
-r <result_file.log>
nr restore monitors - -d <monitors_folder>
-f <monitor_filenames>
-F <file_contains_names>
-m [skip|override|clean]
-r <result_file.log>
nr restore alertsconditions - -d <alertsconditions_folder>
-f <alertscondition_filenames>
-F <file_contains_names>
-m [skip|override|clean]
-r <result_file.log>
nr restore dashboards - -d <dashboards_folder>
-f <dashboard_filenames>
-F <file_contains_names>
-m [skip|override|clean]
-r <result_file.log>
nr take template <template type name>

To start using nr CLI

Getting Started with the nr CLI (A quick sample to get all users in New Relic account)

  • Set environment variable NEW_RELIC_APIKEY

Define New Relic admin API key in environment by export cmd on Linux OS like this:
export NEW_RELIC_APIKEY=xxxx-xxxxxxx-xxxxx-xxxxxx

xxxx-xxxxxxx-xxxxx-xxxxxx is the New Relic admin API key. How to find the admin API key in your New Relic account, reference this doc, Activate Admin user's API key: REST API keys

  • Get all users info in current New Relic account

Use nr get users command like this:

$ nr get users
ID        FirstName   LastName    Email                Role
2071178   Tom       Smith       [email protected]    admin
2000900   Jack        Xi        [email protected]     admin


Define the output format as JSON using -o json argument

$ nr get users -o json
{
  "users": [
    {
      "id": 2071178,
      "first_name": "Tom",
      "last_name": "Smith",
      "email": "[email protected]",
      "role": "admin"
    },
......


Define the output format as JSON using -o YAML argument

$ nr get users -o yaml
users:
- email: [email protected]
  first_name: Tom
  id: 2071178
  last_name: Smith
  role: admin
......
  • Use proxy

Can configure proxy if the target machine can not directly connect to newrelic.com

export NEW_RELIC_PROXY=http://<user>:<password>@<ip>:<port>

Like:
export NEW_RELIC_PROXY=http://user1:[email protected]:3128

  • Configure retries

Can configure retries for calling NewRelic REST API while some network issues, all NewRelic REST API callings in CLI would follow the retries. The default retries value is 3 if RETRIES not configure

export RETRIES=<times>

Like:
export RETRIES=5

  • Return codes

The nr CLI uses exit codes, which help with scripting and confirming that a command has run successfully. For example, after you run a nr CLI command, you can retrieve its return code by running echo $? (on Windows, echo %ERRORLEVEL%). If the return code is 0, the command was successful.

A sample to use return code in shell scripts for CI/CD pipeline To restore monitors, we check if successful by the return code, if failed, it would output the monitor file names to the log file fail-restore-monitors.log(the file name you can customize by -r argument), we continue to retry to restore all monitors that failed, the monitor names were stored in fail-restore-monitors.log and we use -F argument to tell nr what monitors we want to retry, we also add a counter, if retry times exceed 3, it would end.

shell scripts to restore monitors with retry:

#!/bin/bash

noNRKey="No NEW_RELIC_APIKEY detected."
noNRKey=${noNRKey}"\n\n"
noNRKey=${noNRKey}"Please export New Relic API key."
noNRKey=${noNRKey}"\n\n"
noNRKey=${noNRKey}"Example:\n"
noNRKey=${noNRKey}"  export NEW_RELIC_APIKEY=xxx-xxxxx-xx"
noNRKey=${noNRKey}"\n"
if [ $NEW_RELIC_APIKEY"" == "" ];then
    echo -e "${noNRKey}"
    exit 1
fi


basepath=$(cd `dirname $0`; pwd)

${basepath}/nr restore monitors -d ${basepath}/backup_monitors_folder -r fail-restore-monitors.log
exitCode=$?""

if [ $exitCode == "0" ];then
    echo ""
    echo "Success, restore end."
    exit 0
else
    echo ""
    echo "Some monitors to restore failed, begin to retry..."
fi

counter=0
while [ $exitCode != "0" ]
do
    counter=`expr $counter + 1`

    ${basepath}/nr restore monitors -F ${basepath}/fail-restore-monitors.log -r fail-restore-monitors.log
    exitCode=$?""

    if [ $exitCode != "0" ];then
        echo ""
        echo "Some monitors to restore failed in this retry: "$counter"."
        if [ $counter -ge 3 ];then
            echo ""
            echo "Retry 3 times, restore end."
            exit 1
        fi
    else
        echo ""
        echo "After retry, no failed, restore end."
        exit 0
    fi    
done
  • nr help command

The nr help command lists the nr CLI commands and a brief description of each. Passing the -h flag to any command lists detailed help, including any aliases. For example, to see detailed help for nr get, run:

$ nr get -h
Display one or many NewRelic resources.

Usage:
  nr get [command]

Available Commands:
  alertschannels   Display all alerts_channels.
  alertsconditions Display alert conditions by alert id.
  alertspolicies   Display all alerts_policies.
  labels           Display all labels.
  labelsmonitors   Display monitors by label.
  monitor          Display a single monitor by id.
  monitors         Display all synthetics monitors.
  user             Display a single user by id.
  users            Display all users.

Flags:
  -h, --help                    help for get
  -o, --output string           Output format. table/json/yaml are supported (default "table")
  -t, --type-condition string   Alert condition type. Only used for 'alertsconditions' command. all|conditions|synthetics|ext|plugin|nrql are supported (default "all")


for nr get users, run:

$ nr get users -h
Display all users.

Usage:
  nr get users [flags]

Examples:
* nr get users
* nr get users -o json
* nr get users -o yaml
* nr get users -i 2102902
* nr get users -i 2102902,+801314

Flags:
  -e, --email string   email to filter returned result. can't specify emails
  -h, --help           help for users
  -i, --id string      user id(s) to filter returned result. use ',+' to separate ids

Global Flags:
  -o, --output string           Output format. table/json/yaml are supported (default "table")
  -t, --type-condition string   Alert condition type. Only used for 'alertsconditions' command. all|conditions|synthetics|ext|plugin|nrql are supported (default "all")

To start developing nr CLI

Prerequisite

  • Golang 1.9 or 1.9+

  • Golang dep
    Install dep:
    go get -u github.com/golang/dep/cmd/dep

Build newrelic-cli project

  • git clone newrelic-cli
  • Enter project root folder
  • Run make deps
  • Run make build

Cross compilation by using gox

  • Install gox
  • Enter project root folder
  • Run gox -os "windows linux darwin" -arch "amd64"

How to run unit test

  • export NEW_RELIC_APIKEY=<Your NewRelic API Key>
  • make test

Changelog

Changelog

Acknowledgement

Special thanks to Huang Wei who proposed this good idea and developed the initial version.

newrelic-cli's People

Contributors

gailtang avatar gaochan-1 avatar huang-wei avatar kant avatar liurui-1 avatar maohuang81 avatar mlvazqu2 avatar sstarcher avatar wentao-zh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

newrelic-cli's Issues

Monitor Restore Error Handling

Create Monitor 400 The monitor values is invalid, or the format of the request is invalid. was not logged as a failure. The results were recording as 100% success:

Monitors to restore, total: 37, success: 37, fail: 0

Windows version

Issues seen with windows version:
get Users:

C:\swati\IBM_CLI>nr-windows-amd64.exe get users
panic: reflect: call of reflect.Value.Interface on zero Value
goroutine 1 [running]:
reflect.valueInterface(0x0, 0x0, 0x0, 0x1, 0xc042228280, 0xc0422620c0)
/Users/gaochan/Documents/zwork/software/Golang/go/src/reflect/value.go:9
reflect.Value.Interface(0x0, 0x0, 0x0, 0x1, 0x2)
/Users/gaochan/Documents/zwork/software/Golang/go/src/reflect/value.go:9
github.com/IBM/newrelic-cli/utils.(*TablePrinter).Print(0xb982c8, 0x840420, 0xc0
/Users/gaochan/Documents/zwork/workspace-go/src/github.com/IBM/newrelic-
github.com/IBM/newrelic-cli/cmd/get.glob..func16(0xb71540, 0xb982c8, 0x0, 0x0)

Get Dashboards:
C:\swati\IBM_CLI>nr-windows-amd64.exe get dashboards
Response status code: 401. Get one page dashboards, pageCount '1'
Status code is not 2XX calling NewRelic REST

Get Alert Conditions
C:\swati\IBM_CLI>nr-windows-amd64 get alertsconditions -t all -o json
length of [flags] should be 1 instead of 0

C:\swati\IBM_CLI>nr-windows-amd64 get alertsconditions -t all
length of [flags] should be 1 instead of 0

Get AlertsChannels

C:\swati\IBM_CLI>nr-windows-amd64 get alertschannels
[WARNING] unsupported data format.

Update Alert Conditions Fails To Update

Testing the update alert conditions locally and was not able to update conditions

nr update alertsconditions -f condition.yaml

returns

Error validating for default type condition "condition.yaml".

I also tried using different types of conditions (nrql, default, synthetics), they all fail.

I will try to debug the code when I have some time too

[Feature Request] Upsert command for alertsConditions

What
I would like to have upsert command for alert condition. It creates an alerts conditions if it is not already there else it updates the existing alert based on the name.

Why
It helps to manage the alerts through gitops with minimal effort. (We don't need to check if the alert is already created or not before applying the changes. It will make the gitops jobs idempotent.

I intend to create the PR for this myself, if you all don't mind. I'll start on that if I get >=1 ๐Ÿ‘ react from a Contributor

New Feature: Outputing JSON template for creating target for user convenience

Outputing JSON template for creating target for user convenience.

Background:
In creating target cases, like Synthetics monitor creating in cli, end user need to provide the JSON file in specific data format, but user did not know the JSON file format, how to organize data for Synthetics monitor creating. It is hard to use cli to create monitor.

Feature:
cli provide sub cmd like nr get monitor template to output the JSON template for monitor creating

It starts to back up the monitors but then errors out

It starts to back up the monitors but errors out with

invalid character '<' looking for beginning of value
Call NewRelic REST error.

The backup command worked when I ran it on an account with only a few Synthetic checks. The account I'm trying to back up has 400 tests.

Hitting Max API requests and getting a 429 Error

I have about 400 monitors, is there a way to slow down the backup so that it doesn't hit the max number of API requests and fails.

Get Monitor Script 200 Success monitor id: xxxx monitor name: xxxx
Get Monitor Script 429 monitor id: xxx, monitor name: xxxx

invalid character '<' looking for beginning of value
Call NewRelic REST error

Failed to backup monitors, exit.
exit status 1

failed task logs

running nr cli backup cmd against new relic policy, sometimes goes fail without displaying error log.

SIGSEGV: segmentation violation code when running backup alertsconditions

SIGSEGV: segmentation violation code
$ ./nr backup alertsconditions -d /Users/rmalhotra
Start to backup all alertsconditions to '/Users/rmalhotra' folder
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x1378246]

goroutine 1 [running]:
github.com/IBM/newrelic-cli/cmd/get.GetMonitorByID(0xc00018d140, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
/Users/rmalhotra/go/src/github.com/IBM/newrelic-cli/cmd/get/get_monitor.go:89 +0x3c6
github.com/IBM/newrelic-cli/cmd/backup.glob..func1(0x18b2c60, 0xc00007a8c0, 0x0, 0x2)
/Users/rmalhotra/go/src/github.com/IBM/newrelic-cli/cmd/backup/backup_alertsconditions.go:145 +0x526
github.com/spf13/cobra.(*Command).execute(0x18b2c60, 0xc00007a880, 0x2, 0x2, 0x18b2c60, 0xc00007a880)
/Users/rmalhotra/go/src/github.com/spf13/cobra/command.go:766 +0x2cc
github.com/spf13/cobra.(*Command).ExecuteC(0x18b2080, 0x18b2540, 0x18b7ac0, 0x0)
/Users/rmalhotra/go/src/github.com/spf13/cobra/command.go:852 +0x2fd
github.com/spf13/cobra.(*Command).Execute(0x18b2080, 0x1005390, 0xc00007e058)
/Users/rmalhotra/go/src/github.com/spf13/cobra/command.go:800 +0x2b
github.com/IBM/newrelic-cli/cmd.Execute()
/Users/rmalhotra/go/src/github.com/IBM/newrelic-cli/cmd/root.go:56 +0x2d
main.main()
/Users/rmalhotra/IBM/newrelic-cli/nr.go:20 +0x20

get dashboard does not return expected widget content when dashboard NRQL query is in a different account

when running nr get dashboard <id> for a dashboard which contains widgets that are configured to query data in a different account context (a subaccount in this case), the general dashboard metadata is successfully returned, but all widgets are missing several keys; account_id, data and presentation. Also, the visualization key returns a value of "inaccessible"

the dashboards themselves render without issue in Insights and One.

Cannot backup monitors with cli

I can get users and backup dashboards without a problem. However, when i try to backup monitors (i'm specifically interested in my Synthetics monitors), i get a 401 with some extra details... see below:

Command issued:
user$ nr backup monitors -d ./backups/

Response:

Start to backup all monitors to './backups/' folder
Response status code: 401. Get one page monitors, pageSize '50', pageOffset '0'
OperationName   StatusCode   Description   Message
Get Monitors    401                        pageSize:50,pageOffset:0

Status code is not 2XX calling NewRelic REST
Status code is not 2XX calling NewRelic REST


Failed to backup monitors, exit.

invalid character '<'

Hi,

Great tool for backup, but when I run the backup-alert-conditions.sh I get an "invalid character" message.

Start to backup all alertsconditions to '.....backup_alert_conditions/20200917-184055' folder
Calling GetMonitorByID() func, monitor id:
Enter GetMonitorByID() func, monitor id:
invalid character '<' looking for beginning of value
invalid character '<' looking for beginning of value
Calling GetMonitorByID() func, monitor id:
Enter GetMonitorByID() func, monitor id:
invalid character '<' looking for beginning of value
invalid character '<' looking for beginning of value

I have 1000+ monitors which I'm trying to backup. Any tips on how to resolve this ?

Thanks!

Cannot get monitors with CLI

I have admin access to the NR account and I can see all the monitors in the dashboard, but I am getting errors for both commands below:

$ nr get monitors
Response status code: 401. Get one page monitors, pageSize '50', pageOffset '0'

$ nr get monitor 834b5a8d-367a-4311-8d71-9d5a2db906e6
Enter GetMonitorByID() func, monitor id: 834b5a8d-367a-4311-8d71-9d5a2db906e6
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x13b2924]

goroutine 1 [running]:
github.com/IBM/newrelic-cli/cmd/get.GetMonitorByID(0x7ffeefbffaf7, 0x24, 0x3, 0x2, 0xc000128060, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
/Users/yanli/Downloads/WHC/scripts/src/github.com/IBM/newrelic-cli/cmd/get/get_monitor.go:90 +0x394
github.com/IBM/newrelic-cli/cmd/get.glob..func12(0x197c0a0, 0xc00005d5e0, 0x1, 0x1)
/Users/yanli/Downloads/WHC/scripts/src/github.com/IBM/newrelic-cli/cmd/get/get_monitor.go:47 +0x5d
github.com/IBM/newrelic-cli/vendor/github.com/spf13/cobra.(*Command).execute(0x197c0a0, 0xc00005d5a0, 0x1, 0x1, 0x197c0a0, 0xc00005d5a0)
/Users/yanli/Downloads/WHC/scripts/src/github.com/IBM/newrelic-cli/vendor/github.com/spf13/cobra/command.go:766 +0x2ae
github.com/IBM/newrelic-cli/vendor/github.com/spf13/cobra.(*Command).ExecuteC(0x19782e0, 0xc00010ff68, 0x146708e, 0x19782e0)
/Users/yanli/Downloads/WHC/scripts/src/github.com/IBM/newrelic-cli/vendor/github.com/spf13/cobra/command.go:852 +0x2ec
github.com/IBM/newrelic-cli/vendor/github.com/spf13/cobra.(*Command).Execute(...)
/Users/yanli/Downloads/WHC/scripts/src/github.com/IBM/newrelic-cli/vendor/github.com/spf13/cobra/command.go:800
github.com/IBM/newrelic-cli/cmd.Execute()
/Users/yanli/Downloads/WHC/scripts/src/github.com/IBM/newrelic-cli/cmd/root.go:57 +0x32
main.main()
/Users/yanli/Downloads/WHC/scripts/src/github.com/IBM/newrelic-cli/nr.go:20 +0x20

get dashboards 401

Using a account level key get users, alertspolicies both work, but get dashboards fails with a 401

Response status code: 401. Get one page dashboards, pageCount '1'
Status code is not 2XX calling NewRelic REST

alertsconditions Display alert conditions by alert policy id.

Usage:
nr get [command]

Available Commands:
alertsconditions Display alert conditions by alert policy id.

$ ./nr get alertsconditions
$ length of [flags] should be 1 instead of 0

or

$ ./nr get alertsconditions 449096
$ infrastructure_condition.Response: 401 Unauthorized. Error: invalid character 'I' looking for beginning of value.

Does not work instructions say there should not be a required argument but -help says "by alert policy id". neither is working...

`nr get users` fails with `call of reflect.Value.Interface on zero Value`

Using:

$ nr version
v0.1.5

Got this when I run:

$ nr get users
panic: reflect: call of reflect.Value.Interface on zero Value

goroutine 1 [running]:
reflect.valueInterface(0x0, 0x0, 0x0, 0x1, 0xc42030a2a0, 0xc4204fc000)
        /Users/gaochan/Documents/zwork/software/Golang/go/src/reflect/value.go:936 +0x1bf
reflect.Value.Interface(0x0, 0x0, 0x0, 0x1, 0x2)
        /Users/gaochan/Documents/zwork/software/Golang/go/src/reflect/value.go:931 +0x44
github.com/IBM/newrelic-cli/utils.(*TablePrinter).Print(0xba9fc8, 0x840ee0, 0xc4200dca20, 0xb43880, 0xc42000c018)
        /Users/gaochan/Documents/zwork/workspace-go/src/github.com/IBM/newrelic-cli/utils/printer.go:130 +0x2f3
github.com/IBM/newrelic-cli/cmd/get.glob..func16(0xb80fa0, 0xba9fc8, 0x0, 0x0)
        /Users/gaochan/Documents/zwork/workspace-go/src/github.com/IBM/newrelic-cli/cmd/get/get_users.go:69 +0x384
github.com/IBM/newrelic-cli/vendor/github.com/spf13/cobra.(*Command).execute(0xb80fa0, 0xba9fc8, 0x0, 0x0, 0xb80fa0, 0xba9fc8)
        /Users/gaochan/Documents/zwork/workspace-go/src/github.com/IBM/newrelic-cli/vendor/github.com/spf13/cobra/command.go:766 +0x2c1
github.com/IBM/newrelic-cli/vendor/github.com/spf13/cobra.(*Command).ExecuteC(0xb7cac0, 0xb7cd20, 0xb81920, 0xb7cf80)
        /Users/gaochan/Documents/zwork/workspace-go/src/github.com/IBM/newrelic-cli/vendor/github.com/spf13/cobra/command.go:852 +0x334
github.com/IBM/newrelic-cli/vendor/github.com/spf13/cobra.(*Command).Execute(0xb7cac0, 0x0, 0xc42012bf48)
        /Users/gaochan/Documents/zwork/workspace-go/src/github.com/IBM/newrelic-cli/vendor/github.com/spf13/cobra/command.go:800 +0x2b
github.com/IBM/newrelic-cli/cmd.Execute()
        /Users/gaochan/Documents/zwork/workspace-go/src/github.com/IBM/newrelic-cli/cmd/root.go:56 +0x31
main.main()
        /Users/gaochan/Documents/zwork/workspace-go/src/github.com/IBM/newrelic-cli/nr.go:20 +0x20

having difficulty building...

So I am trying to build this for the first time. I do have go 1.10 on my mac. I had to install dep using brew (not sure why the go get... command didn't appear to do anything.

Anyway, I am on the make deps step and I get an error:

SECMacbookPro:newrelic-cli scottchapman$ make deps
git config --global url."[email protected]:".insteadOf "https://github.com/"
dep ensure
/Users/scottchapman/Workspaces/newrelic-cli is not within a known GOPATH/src
make: *** [deps] Error 1

(sorry, not a seasoned Go developer...)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.