{"action":"created","comment":{"url":"https://api.github.com/repos/percona/pmm-managed/pulls/comments/225404446","pull_request_review_id":164993281,"id":225404446,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIyNTQwNDQ0Ng==","diff_hunk":"@@ -0,0 +1,507 @@\n+// pmm-managed\n+// Copyright (C) 2017 Percona LLC\n+//\n+// This program is free software: you can redistribute it and/or modify\n+// it under the terms of the GNU Affero General Public License as published\n+// by the Free Software Foundation, either version 3 of the License, or\n+// (at your option) any later version.\n+//\n+// This program is distributed in the hope that it will be useful,\n+// but WITHOUT ANY WARRANTY; without even the implied warranty of\n+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n+// GNU Affero General Public License for more details.\n+//\n+// You should have received a copy of the GNU Affero General Public License\n+// along with this program. If not, see <http://www.gnu.org/licenses/>.\n+\n+// Package postgresql contains business logic of working with Remote PostgreSQL instances.\n+package postgresql\n+\n+import (\n+\t\"context\"\n+\t\"database/sql\"\n+\t\"fmt\"\n+\t\"github.com/AlekSi/pointer\"\n+\t\"github.com/go-sql-driver/mysql\"\n+\t\"github.com/lib/pq\"\n+\tservicelib \"github.com/percona/kardianos-service\"\n+\t\"github.com/percona/pmm-managed/models\"\n+\t\"github.com/percona/pmm-managed/services\"\n+\t\"github.com/percona/pmm-managed/services/prometheus\"\n+\t\"github.com/percona/pmm-managed/services/qan\"\n+\t\"github.com/percona/pmm-managed/utils/logger\"\n+\t\"github.com/percona/pmm-managed/utils/ports\"\n+\t\"github.com/pkg/errors\"\n+\t\"google.golang.org/grpc/codes\"\n+\t\"google.golang.org/grpc/status\"\n+\t\"gopkg.in/reform.v1\"\n+\t\"net\"\n+\t\"net/http\"\n+\t\"os/exec\"\n+\t\"regexp\"\n+\t\"sort\"\n+\t\"strconv\"\n+)\n+\n+type ServiceConfig struct {\n+\tPostgreSQLExporterPath string\n+\n+\tDB            *reform.DB\n+\tPrometheus    *prometheus.Service\n+\tQAN           *qan.Service\n+\tSupervisor    services.Supervisor\n+\tPortsRegistry *ports.Registry\n+}\n+\n+// Service is responsible for interactions with PostgreSQL.\n+type Service struct {\n+\t*ServiceConfig\n+\thttpClient    *http.Client\n+\tpmmServerNode *models.Node\n+}\n+\n+// NewService creates a new service.\n+func NewService(config *ServiceConfig) (*Service, error) {\n+\tvar node models.Node\n+\terr := config.DB.FindOneTo(&node, \"type\", models.PMMServerNodeType)\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\n+\tfor _, path := range []*string{\n+\t\t&config.PostgreSQLExporterPath,\n+\t} {\n+\t\tif *path == \"\" {\n+\t\t\tcontinue\n+\t\t}\n+\t\tp, err := exec.LookPath(*path)\n+\t\tif err != nil {\n+\t\t\treturn nil, errors.WithStack(err)\n+\t\t}\n+\t\t*path = p\n+\t}\n+\n+\tsvc := &Service{\n+\t\tServiceConfig: config,\n+\t\thttpClient:    new(http.Client),\n+\t\tpmmServerNode: &node,\n+\t}\n+\treturn svc, nil\n+}\n+\n+// ApplyPrometheusConfiguration Adds postgres to prometheus configuration and applies it\n+func (svc *Service) ApplyPrometheusConfiguration(ctx context.Context, q *reform.Querier) error {\n+\tpostgreSQLConfig := &prometheus.ScrapeConfig{\n+\t\tJobName:        \"postgres\",\n+\t\tScrapeInterval: \"1s\",\n+\t\tScrapeTimeout:  \"1s\",\n+\t\tMetricsPath:    \"/metrics\",\n+\t\tHonorLabels:    true,\n+\t\tRelabelConfigs: []prometheus.RelabelConfig{{\n+\t\t\tTargetLabel: \"job\",\n+\t\t\tReplacement: \"postgres\",\n+\t\t}},\n+\t}\n+\n+\tnodes, err := q.FindAllFrom(models.PostgreSQLNodeTable, \"type\", models.PostgreSQLNodeType)\n+\tif err != nil {\n+\t\treturn errors.WithStack(err)\n+\t}\n+\tfor _, n := range nodes {\n+\t\tnode := n.(*models.PostgreSQLNode)\n+\n+\t\tvar service models.PostgreSQLService\n+\t\tif e := q.SelectOneTo(&service, \"WHERE node_id = ?\", node.ID); e != nil {\n+\t\t\treturn errors.WithStack(e)\n+\t\t}\n+\n+\t\tagents, err := models.AgentsForServiceID(q, service.ID)\n+\t\tif err != nil {\n+\t\t\treturn err\n+\t\t}\n+\t\tfor _, agent := range agents {\n+\t\t\tswitch agent.Type {\n+\t\t\tcase models.PostgreSQLExporterAgentType:\n+\t\t\t\ta := models.PostgreSQLExporter{ID: agent.ID}\n+\t\t\t\tif e := q.Reload(&a); e != nil {\n+\t\t\t\t\treturn errors.WithStack(e)\n+\t\t\t\t}\n+\t\t\t\tlogger.Get(ctx).WithField(\"component\", \"postgresql\").Infof(\"%s %s %d\", a.Type, node.Name, *a.ListenPort)\n+\n+\t\t\t\tsc := prometheus.StaticConfig{\n+\t\t\t\t\tTargets: []string{fmt.Sprintf(\"127.0.0.1:%d\", *a.ListenPort)},\n+\t\t\t\t\tLabels: []prometheus.LabelPair{\n+\t\t\t\t\t\t{Name: \"instance\", Value: node.Name},\n+\t\t\t\t\t},\n+\t\t\t\t}\n+\t\t\t\tpostgreSQLConfig.StaticConfigs = append(postgreSQLConfig.StaticConfigs, sc)\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t// sort by region and name\n+\tsorterFor := func(sc []prometheus.StaticConfig) func(int, int) bool {\n+\t\treturn func(i, j int) bool {\n+\t\t\tif sc[i].Labels[0].Value != sc[j].Labels[0].Value {\n+\t\t\t\treturn sc[i].Labels[0].Value < sc[j].Labels[0].Value\n+\t\t\t}\n+\t\t\treturn sc[i].Labels[1].Value < sc[j].Labels[1].Value\n+\t\t}\n+\t}\n+\tsort.Slice(postgreSQLConfig.StaticConfigs, sorterFor(postgreSQLConfig.StaticConfigs))\n+\n+\treturn svc.Prometheus.SetScrapeConfigs(ctx, false, postgreSQLConfig)\n+}\n+\n+type Instance struct {\n+\tNode    models.PostgreSQLNode\n+\tService models.PostgreSQLService\n+}\n+\n+func (svc *Service) List(ctx context.Context) ([]Instance, error) {\n+\tres := []Instance{}\n+\terr := svc.DB.InTransaction(func(tx *reform.TX) error {\n+\t\tstructs, e := tx.SelectAllFrom(models.PostgreSQLNodeTable, \"WHERE type = ? ORDER BY id\", models.PostgreSQLNodeType)\n+\t\tif e != nil {\n+\t\t\treturn e\n+\t\t}\n+\t\tnodes := make([]models.PostgreSQLNode, len(structs))\n+\t\tfor i, str := range structs {\n+\t\t\tnodes[i] = *str.(*models.PostgreSQLNode)\n+\t\t}\n+\n+\t\tstructs, e = tx.SelectAllFrom(models.PostgreSQLServiceTable, \"WHERE type = ? ORDER BY id\", models.PostgreSQLServiceType)\n+\t\tif e != nil {\n+\t\t\treturn e\n+\t\t}\n+\t\tservices := make([]models.PostgreSQLService, len(structs))\n+\t\tfor i, str := range structs {\n+\t\t\tservices[i] = *str.(*models.PostgreSQLService)\n+\t\t}\n+\n+\t\tfor _, node := range nodes {\n+\t\t\tfor _, service := range services {\n+\t\t\t\tif node.ID == service.NodeID {\n+\t\t\t\t\tres = append(res, Instance{\n+\t\t\t\t\t\tNode:    node,\n+\t\t\t\t\t\tService: service,\n+\t\t\t\t\t})\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\treturn nil\n+\t})\n+\treturn res, err\n+}\n+\n+// Add new postgreSQL service and start postgres_exporter\n+func (svc *Service) Add(ctx context.Context, name, address string, port uint32, username, password string) (int32, error) {\n+\tif address == \"\" {\n+\t\treturn 0, status.Error(codes.InvalidArgument, \"PostgreSQL instance host is not given.\")\n+\t}\n+\tif port == 0 {\n+\t\treturn 0, status.Error(codes.InvalidArgument, \"PostgreSQL instance port is not given.\")\n+\t}\n+\tif username == \"\" {\n+\t\treturn 0, status.Error(codes.InvalidArgument, \"Username is not given.\")\n+\t}\n+\tif name == \"\" {\n+\t\tname = fmt.Sprintf(\"%s:%d\", address, port)\n+\t}\n+\n+\tvar id int32\n+\terr := svc.DB.InTransaction(func(tx *reform.TX) error {\n+\t\t// insert node\n+\t\tnode := &models.PostgreSQLNode{\n+\t\t\tType: models.PostgreSQLNodeType,\n+\t\t\tName: name,\n+\t\t}\n+\t\tif err := tx.Insert(node); err != nil {\n+\t\t\tif err, ok := err.(*mysql.MySQLError); ok && err.Number == 0x426 {\n+\t\t\t\treturn status.Errorf(codes.AlreadyExists, \"PostgreSQL instance %q already exists.\",\n+\t\t\t\t\tnode.Name)\n+\t\t\t}\n+\t\t\treturn errors.WithStack(err)\n+\t\t}\n+\t\tid = node.ID\n+\n+\t\tengine, engineVersion, err := svc.engineAndEngineVersion(ctx, address, port, username, password)\n+\t\tif err != nil {\n+\t\t\treturn errors.WithStack(err)\n+\t\t}\n+\n+\t\t// insert service\n+\t\tservice := &models.PostgreSQLService{\n+\t\t\tType:   models.PostgreSQLServiceType,\n+\t\t\tNodeID: node.ID,\n+\n+\t\t\tAddress:       &address,\n+\t\t\tPort:          pointer.ToUint16(uint16(port)),\n+\t\t\tEngine:        &engine,\n+\t\t\tEngineVersion: &engineVersion,\n+\t\t}\n+\t\tif err := tx.Insert(service); err != nil {\n+\t\t\treturn errors.WithStack(err)\n+\t\t}\n+\n+\t\tif err := svc.addPostgreSQLExporter(ctx, tx, service, username, password); err != nil {\n+\t\t\treturn err\n+\t\t}\n+\n+\t\treturn svc.ApplyPrometheusConfiguration(ctx, tx.Querier)\n+\t})\n+\n+\treturn id, err\n+}\n+\n+func (svc *Service) engineAndEngineVersion(ctx context.Context, host string, port uint32, username string, password string) (string, string, error) {","path":"services/postgresql/postgresql.go","position":257,"original_position":257,"commit_id":"42a95c98d71abb5ec872af6664d7dd953eb67855","original_commit_id":"42a95c98d71abb5ec872af6664d7dd953eb67855","user":{"login":"golangcibot","id":42910462,"node_id":"MDQ6VXNlcjQyOTEwNDYy","avatar_url":"https://avatars1.githubusercontent.com/u/42910462?v=4","gravatar_id":"","url":"https://api.github.com/users/golangcibot","html_url":"https://github.com/golangcibot","followers_url":"https://api.github.com/users/golangcibot/followers","following_url":"https://api.github.com/users/golangcibot/following{/other_user}","gists_url":"https://api.github.com/users/golangcibot/gists{/gist_id}","starred_url":"https://api.github.com/users/golangcibot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/golangcibot/subscriptions","organizations_url":"https://api.github.com/users/golangcibot/orgs","repos_url":"https://api.github.com/users/golangcibot/repos","events_url":"https://api.github.com/users/golangcibot/events{/privacy}","received_events_url":"https://api.github.com/users/golangcibot/received_events","type":"User","site_admin":false},"body":"line is 149 characters","created_at":"2018-10-16T05:48:57Z","updated_at":"2018-10-16T05:48:58Z","html_url":"https://github.com/percona/pmm-managed/pull/62#discussion_r225404446","pull_request_url":"https://api.github.com/repos/percona/pmm-managed/pulls/62","author_association":"NONE","_links":{"self":{"href":"https://api.github.com/repos/percona/pmm-managed/pulls/comments/225404446"},"html":{"href":"https://github.com/percona/pmm-managed/pull/62#discussion_r225404446"},"pull_request":{"href":"https://api.github.com/repos/percona/pmm-managed/pulls/62"}}},"pull_request":{"url":"https://api.github.com/repos/percona/pmm-managed/pulls/62","id":222374019,"node_id":"MDExOlB1bGxSZXF1ZXN0MjIyMzc0MDE5","html_url":"https://github.com/percona/pmm-managed/pull/62","diff_url":"https://github.com/percona/pmm-managed/pull/62.diff","patch_url":"https://github.com/percona/pmm-managed/pull/62.patch","issue_url":"https://api.github.com/repos/percona/pmm-managed/issues/62","number":62,"state":"open","locked":false,"title":"[WIP] PMM-3075 PostgreSQL API","user":{"login":"BupycHuk","id":2506513,"node_id":"MDQ6VXNlcjI1MDY1MTM=","avatar_url":"https://avatars3.githubusercontent.com/u/2506513?v=4","gravatar_id":"","url":"https://api.github.com/users/BupycHuk","html_url":"https://github.com/BupycHuk","followers_url":"https://api.github.com/users/BupycHuk/followers","following_url":"https://api.github.com/users/BupycHuk/following{/other_user}","gists_url":"https://api.github.com/users/BupycHuk/gists{/gist_id}","starred_url":"https://api.github.com/users/BupycHuk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/BupycHuk/subscriptions","organizations_url":"https://api.github.com/users/BupycHuk/orgs","repos_url":"https://api.github.com/users/BupycHuk/repos","events_url":"https://api.github.com/users/BupycHuk/events{/privacy}","received_events_url":"https://api.github.com/users/BupycHuk/received_events","type":"User","site_admin":false},"body":"some of the logic like Remove isn't implemented yet.","created_at":"2018-10-12T07:29:33Z","updated_at":"2018-10-16T05:48:58Z","closed_at":null,"merged_at":null,"merge_commit_sha":"bc765d2e63ea1d4cde01bd56fe7c83a5b6cb4f10","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/percona/pmm-managed/pulls/62/commits","review_comments_url":"https://api.github.com/repos/percona/pmm-managed/pulls/62/comments","review_comment_url":"https://api.github.com/repos/percona/pmm-managed/pulls/comments{/number}","comments_url":"https://api.github.com/repos/percona/pmm-managed/issues/62/comments","statuses_url":"https://api.github.com/repos/percona/pmm-managed/statuses/42a95c98d71abb5ec872af6664d7dd953eb67855","head":{"label":"percona:PMM-3075-postgresql-api","ref":"PMM-3075-postgresql-api","sha":"42a95c98d71abb5ec872af6664d7dd953eb67855","user":{"login":"percona","id":1683025,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE2ODMwMjU=","avatar_url":"https://avatars0.githubusercontent.com/u/1683025?v=4","gravatar_id":"","url":"https://api.github.com/users/percona","html_url":"https://github.com/percona","followers_url":"https://api.github.com/users/percona/followers","following_url":"https://api.github.com/users/percona/following{/other_user}","gists_url":"https://api.github.com/users/percona/gists{/gist_id}","starred_url":"https://api.github.com/users/percona/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/percona/subscriptions","organizations_url":"https://api.github.com/users/percona/orgs","repos_url":"https://api.github.com/users/percona/repos","events_url":"https://api.github.com/users/percona/events{/privacy}","received_events_url":"https://api.github.com/users/percona/received_events","type":"Organization","site_admin":false},"repo":{"id":97122795,"node_id":"MDEwOlJlcG9zaXRvcnk5NzEyMjc5NQ==","name":"pmm-managed","full_name":"percona/pmm-managed","private":false,"owner":{"login":"percona","id":1683025,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE2ODMwMjU=","avatar_url":"https://avatars0.githubusercontent.com/u/1683025?v=4","gravatar_id":"","url":"https://api.github.com/users/percona","html_url":"https://github.com/percona","followers_url":"https://api.github.com/users/percona/followers","following_url":"https://api.github.com/users/percona/following{/other_user}","gists_url":"https://api.github.com/users/percona/gists{/gist_id}","starred_url":"https://api.github.com/users/percona/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/percona/subscriptions","organizations_url":"https://api.github.com/users/percona/orgs","repos_url":"https://api.github.com/users/percona/repos","events_url":"https://api.github.com/users/percona/events{/privacy}","received_events_url":"https://api.github.com/users/percona/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/percona/pmm-managed","description":"pmm-managed manages configuration of PMM server components and exposes API for that.","fork":false,"url":"https://api.github.com/repos/percona/pmm-managed","forks_url":"https://api.github.com/repos/percona/pmm-managed/forks","keys_url":"https://api.github.com/repos/percona/pmm-managed/keys{/key_id}","collaborators_url":"https://api.github.com/repos/percona/pmm-managed/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/percona/pmm-managed/teams","hooks_url":"https://api.github.com/repos/percona/pmm-managed/hooks","issue_events_url":"https://api.github.com/repos/percona/pmm-managed/issues/events{/number}","events_url":"https://api.github.com/repos/percona/pmm-managed/events","assignees_url":"https://api.github.com/repos/percona/pmm-managed/assignees{/user}","branches_url":"https://api.github.com/repos/percona/pmm-managed/branches{/branch}","tags_url":"https://api.github.com/repos/percona/pmm-managed/tags","blobs_url":"https://api.github.com/repos/percona/pmm-managed/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/percona/pmm-managed/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/percona/pmm-managed/git/refs{/sha}","trees_url":"https://api.github.com/repos/percona/pmm-managed/git/trees{/sha}","statuses_url":"https://api.github.com/repos/percona/pmm-managed/statuses/{sha}","languages_url":"https://api.github.com/repos/percona/pmm-managed/languages","stargazers_url":"https://api.github.com/repos/percona/pmm-managed/stargazers","contributors_url":"https://api.github.com/repos/percona/pmm-managed/contributors","subscribers_url":"https://api.github.com/repos/percona/pmm-managed/subscribers","subscription_url":"https://api.github.com/repos/percona/pmm-managed/subscription","commits_url":"https://api.github.com/repos/percona/pmm-managed/commits{/sha}","git_commits_url":"https://api.github.com/repos/percona/pmm-managed/git/commits{/sha}","comments_url":"https://api.github.com/repos/percona/pmm-managed/comments{/number}","issue_comment_url":"https://api.github.com/repos/percona/pmm-managed/issues/comments{/number}","contents_url":"https://api.github.com/repos/percona/pmm-managed/contents/{+path}","compare_url":"https://api.github.com/repos/percona/pmm-managed/compare/{base}...{head}","merges_url":"https://api.github.com/repos/percona/pmm-managed/merges","archive_url":"https://api.github.com/repos/percona/pmm-managed/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/percona/pmm-managed/downloads","issues_url":"https://api.github.com/repos/percona/pmm-managed/issues{/number}","pulls_url":"https://api.github.com/repos/percona/pmm-managed/pulls{/number}","milestones_url":"https://api.github.com/repos/percona/pmm-managed/milestones{/number}","notifications_url":"https://api.github.com/repos/percona/pmm-managed/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/percona/pmm-managed/labels{/name}","releases_url":"https://api.github.com/repos/percona/pmm-managed/releases{/id}","deployments_url":"https://api.github.com/repos/percona/pmm-managed/deployments","created_at":"2017-07-13T12:58:40Z","updated_at":"2018-10-14T11:53:49Z","pushed_at":"2018-10-16T05:48:14Z","git_url":"git://github.com/percona/pmm-managed.git","ssh_url":"git@github.com:percona/pmm-managed.git","clone_url":"https://github.com/percona/pmm-managed.git","svn_url":"https://github.com/percona/pmm-managed","homepage":"","size":69278,"stargazers_count":10,"watchers_count":10,"language":"Go","has_issues":false,"has_projects":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":2,"mirror_url":null,"archived":false,"open_issues_count":1,"license":{"key":"agpl-3.0","name":"GNU Affero General Public License v3.0","spdx_id":"AGPL-3.0","url":"https://api.github.com/licenses/agpl-3.0","node_id":"MDc6TGljZW5zZTE="},"forks":2,"open_issues":1,"watchers":10,"default_branch":"master"}},"base":{"label":"percona:PMM-1.16","ref":"PMM-1.16","sha":"8c0960ecfd136f2d26e668b2193a8b3049d450f1","user":{"login":"percona","id":1683025,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE2ODMwMjU=","avatar_url":"https://avatars0.githubusercontent.com/u/1683025?v=4","gravatar_id":"","url":"https://api.github.com/users/percona","html_url":"https://github.com/percona","followers_url":"https://api.github.com/users/percona/followers","following_url":"https://api.github.com/users/percona/following{/other_user}","gists_url":"https://api.github.com/users/percona/gists{/gist_id}","starred_url":"https://api.github.com/users/percona/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/percona/subscriptions","organizations_url":"https://api.github.com/users/percona/orgs","repos_url":"https://api.github.com/users/percona/repos","events_url":"https://api.github.com/users/percona/events{/privacy}","received_events_url":"https://api.github.com/users/percona/received_events","type":"Organization","site_admin":false},"repo":{"id":97122795,"node_id":"MDEwOlJlcG9zaXRvcnk5NzEyMjc5NQ==","name":"pmm-managed","full_name":"percona/pmm-managed","private":false,"owner":{"login":"percona","id":1683025,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE2ODMwMjU=","avatar_url":"https://avatars0.githubusercontent.com/u/1683025?v=4","gravatar_id":"","url":"https://api.github.com/users/percona","html_url":"https://github.com/percona","followers_url":"https://api.github.com/users/percona/followers","following_url":"https://api.github.com/users/percona/following{/other_user}","gists_url":"https://api.github.com/users/percona/gists{/gist_id}","starred_url":"https://api.github.com/users/percona/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/percona/subscriptions","organizations_url":"https://api.github.com/users/percona/orgs","repos_url":"https://api.github.com/users/percona/repos","events_url":"https://api.github.com/users/percona/events{/privacy}","received_events_url":"https://api.github.com/users/percona/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/percona/pmm-managed","description":"pmm-managed manages configuration of PMM server components and exposes API for that.","fork":false,"url":"https://api.github.com/repos/percona/pmm-managed","forks_url":"https://api.github.com/repos/percona/pmm-managed/forks","keys_url":"https://api.github.com/repos/percona/pmm-managed/keys{/key_id}","collaborators_url":"https://api.github.com/repos/percona/pmm-managed/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/percona/pmm-managed/teams","hooks_url":"https://api.github.com/repos/percona/pmm-managed/hooks","issue_events_url":"https://api.github.com/repos/percona/pmm-managed/issues/events{/number}","events_url":"https://api.github.com/repos/percona/pmm-managed/events","assignees_url":"https://api.github.com/repos/percona/pmm-managed/assignees{/user}","branches_url":"https://api.github.com/repos/percona/pmm-managed/branches{/branch}","tags_url":"https://api.github.com/repos/percona/pmm-managed/tags","blobs_url":"https://api.github.com/repos/percona/pmm-managed/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/percona/pmm-managed/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/percona/pmm-managed/git/refs{/sha}","trees_url":"https://api.github.com/repos/percona/pmm-managed/git/trees{/sha}","statuses_url":"https://api.github.com/repos/percona/pmm-managed/statuses/{sha}","languages_url":"https://api.github.com/repos/percona/pmm-managed/languages","stargazers_url":"https://api.github.com/repos/percona/pmm-managed/stargazers","contributors_url":"https://api.github.com/repos/percona/pmm-managed/contributors","subscribers_url":"https://api.github.com/repos/percona/pmm-managed/subscribers","subscription_url":"https://api.github.com/repos/percona/pmm-managed/subscription","commits_url":"https://api.github.com/repos/percona/pmm-managed/commits{/sha}","git_commits_url":"https://api.github.com/repos/percona/pmm-managed/git/commits{/sha}","comments_url":"https://api.github.com/repos/percona/pmm-managed/comments{/number}","issue_comment_url":"https://api.github.com/repos/percona/pmm-managed/issues/comments{/number}","contents_url":"https://api.github.com/repos/percona/pmm-managed/contents/{+path}","compare_url":"https://api.github.com/repos/percona/pmm-managed/compare/{base}...{head}","merges_url":"https://api.github.com/repos/percona/pmm-managed/merges","archive_url":"https://api.github.com/repos/percona/pmm-managed/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/percona/pmm-managed/downloads","issues_url":"https://api.github.com/repos/percona/pmm-managed/issues{/number}","pulls_url":"https://api.github.com/repos/percona/pmm-managed/pulls{/number}","milestones_url":"https://api.github.com/repos/percona/pmm-managed/milestones{/number}","notifications_url":"https://api.github.com/repos/percona/pmm-managed/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/percona/pmm-managed/labels{/name}","releases_url":"https://api.github.com/repos/percona/pmm-managed/releases{/id}","deployments_url":"https://api.github.com/repos/percona/pmm-managed/deployments","created_at":"2017-07-13T12:58:40Z","updated_at":"2018-10-14T11:53:49Z","pushed_at":"2018-10-16T05:48:14Z","git_url":"git://github.com/percona/pmm-managed.git","ssh_url":"git@github.com:percona/pmm-managed.git","clone_url":"https://github.com/percona/pmm-managed.git","svn_url":"https://github.com/percona/pmm-managed","homepage":"","size":69278,"stargazers_count":10,"watchers_count":10,"language":"Go","has_issues":false,"has_projects":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":2,"mirror_url":null,"archived":false,"open_issues_count":1,"license":{"key":"agpl-3.0","name":"GNU Affero General Public License v3.0","spdx_id":"AGPL-3.0","url":"https://api.github.com/licenses/agpl-3.0","node_id":"MDc6TGljZW5zZTE="},"forks":2,"open_issues":1,"watchers":10,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/percona/pmm-managed/pulls/62"},"html":{"href":"https://github.com/percona/pmm-managed/pull/62"},"issue":{"href":"https://api.github.com/repos/percona/pmm-managed/issues/62"},"comments":{"href":"https://api.github.com/repos/percona/pmm-managed/issues/62/comments"},"review_comments":{"href":"https://api.github.com/repos/percona/pmm-managed/pulls/62/comments"},"review_comment":{"href":"https://api.github.com/repos/percona/pmm-managed/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/percona/pmm-managed/pulls/62/commits"},"statuses":{"href":"https://api.github.com/repos/percona/pmm-managed/statuses/42a95c98d71abb5ec872af6664d7dd953eb67855"}},"author_association":"NONE"}}
{"actor":{"display_login":"golangcibot"}}