{"action":"created","comment":{"url":"https://api.github.com/repos/deis/deis/pulls/comments/39817235","id":39817235,"diff_hunk":"@@ -2,102 +2,106 @@ package main\n \n import (\n \t\"flag\"\n-\t\"fmt\"\n \t\"log\"\n \t\"os\"\n \t\"os/signal\"\n \t\"strconv\"\n-\t\"strings\"\n \t\"syscall\"\n \t\"time\"\n \n-\t\"github.com/coreos/go-etcd/etcd\"\n-\t\"github.com/deis/deis/logger/syslogd\"\n+\t\"github.com/deis/deis/logger/configurer\"\n+\t\"github.com/deis/deis/logger/publisher\"\n+\t\"github.com/deis/deis/logger/storage\"\n+\t\"github.com/deis/deis/logger/syslogish\"\n+\t\"github.com/deis/deis/logger/weblog\"\n )\n \n var (\n \tlogAddr         string\n+\tlogHost         string\n \tlogPort         int\n-\tdrainURI        string\n \tenablePublish   bool\n-\tpublishHost     string\n-\tpublishPath     string\n-\tpublishPort     string\n+\twebAddr         string\n+\twebPort         int\n+\tetcdHost        string\n+\tetcdPort        string\n+\tetcdPath        string\n+\tconfigInterval  int\n \tpublishInterval int\n \tpublishTTL      int\n )\n \n func init() {\n \tflag.StringVar(&logAddr, \"log-addr\", \"0.0.0.0\", \"bind address for the logger\")\n+\tflag.StringVar(&logHost, \"log-host\", getopt(\"HOST\", \"127.0.0.1\"),\n+\t\t\"address of the host running logger\")\n \tflag.IntVar(&logPort, \"log-port\", 514, \"bind port for the logger\")\n-\tflag.StringVar(&drainURI, \"drain-uri\", \"\", \"default drainURI, once set in etcd, this has no effect.\")\n-\tflag.StringVar(&syslogd.LogRoot, \"log-root\", \"/data/logs\", \"log path to store logs\")\n+\tflag.StringVar(&configurer.DefaultDrainURI, \"drain-uri\", \"\",\n+\t\t\"default drainURI, once set in etcd, this has no effect.\")\n+\tflag.StringVar(&storage.LogRoot, \"log-root\", \"/data/logs\", \"log path to store logs\")\n+\tflag.StringVar(&webAddr, \"web-addr\", \"0.0.0.0\", \"bind address for the web service\")\n+\tflag.IntVar(&webPort, \"web-port\", 8088, \"bind port for the web service\")\n \tflag.BoolVar(&enablePublish, \"enable-publish\", false, \"enable publishing to service discovery\")\n-\tflag.StringVar(&publishHost, \"publish-host\", getopt(\"HOST\", \"127.0.0.1\"), \"service discovery hostname\")\n+\t// Support legacy flag names even though the variable names have been refactored\n+\tflag.StringVar(&etcdHost, \"publish-host\", getopt(\"HOST\", \"127.0.0.1\"),\n+\t\t\"service discovery hostname\")\n+\tflag.StringVar(&etcdPort, \"publish-port\", getopt(\"ETCD_PORT\", \"4001\"), \"service discovery port\")\n+\tflag.StringVar(&etcdPath, \"publish-path\", getopt(\"ETCD_PATH\", \"/deis/logs\"),\n+\t\t\"path to publish host/port information\")\n+\tflag.IntVar(&configInterval, \"config-interval\", 10, \"config interval in seconds\")\n \tflag.IntVar(&publishInterval, \"publish-interval\", 10, \"publish interval in seconds\")\n-\tflag.StringVar(&publishPath, \"publish-path\", getopt(\"ETCD_PATH\", \"/deis/logs\"), \"path to publish host/port information\")\n-\tflag.StringVar(&publishPort, \"publish-port\", getopt(\"ETCD_PORT\", \"4001\"), \"service discovery port\")\n \tflag.IntVar(&publishTTL, \"publish-ttl\", publishInterval*2, \"publish TTL in seconds\")","path":"logger/main.go","position":65,"original_position":65,"commit_id":"17c878cca5f139bab77b4481bb5921a6664aa7c9","original_commit_id":"17c878cca5f139bab77b4481bb5921a6664aa7c9","user":{"login":"kalbasit","id":87115,"avatar_url":"https://avatars.githubusercontent.com/u/87115?v=3","gravatar_id":"","url":"https://api.github.com/users/kalbasit","html_url":"https://github.com/kalbasit","followers_url":"https://api.github.com/users/kalbasit/followers","following_url":"https://api.github.com/users/kalbasit/following{/other_user}","gists_url":"https://api.github.com/users/kalbasit/gists{/gist_id}","starred_url":"https://api.github.com/users/kalbasit/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kalbasit/subscriptions","organizations_url":"https://api.github.com/users/kalbasit/orgs","repos_url":"https://api.github.com/users/kalbasit/repos","events_url":"https://api.github.com/users/kalbasit/events{/privacy}","received_events_url":"https://api.github.com/users/kalbasit/received_events","type":"User","site_admin":false},"body":"I do know that you didn't change but since it's a rewrite you might as well. Basically when you define flags as (just one flag here but you get the idea)\r\n\r\n```\r\nvar (\r\n    publishPath = flag.StringVar(&publishPath, \"publish-path\", getopt(\"ETCD_PATH\", \"/deis/logs\"), \"path to publish host/port information\")\r\n)\r\n```\r\n\r\nyou can access the value using `*publishPath` which is less pretty but the compiler already does the work. When you do it the way it's done now (through `init`) the binary sets up the memory on boot. It's not much of a performance improvement but it's something.\r\n\r\nI leave it to you to decide.","created_at":"2015-09-18T01:22:15Z","updated_at":"2015-09-18T01:22:15Z","html_url":"https://github.com/deis/deis/pull/4502#discussion_r39817235","pull_request_url":"https://api.github.com/repos/deis/deis/pulls/4502","_links":{"self":{"href":"https://api.github.com/repos/deis/deis/pulls/comments/39817235"},"html":{"href":"https://github.com/deis/deis/pull/4502#discussion_r39817235"},"pull_request":{"href":"https://api.github.com/repos/deis/deis/pulls/4502"}}},"pull_request":{"url":"https://api.github.com/repos/deis/deis/pulls/4502","id":45253827,"html_url":"https://github.com/deis/deis/pull/4502","diff_url":"https://github.com/deis/deis/pull/4502.diff","patch_url":"https://github.com/deis/deis/pull/4502.patch","issue_url":"https://api.github.com/repos/deis/deis/issues/4502","number":4502,"state":"open","locked":false,"title":"chore(logger): refactor-- also includes new features","user":{"login":"krancour","id":1821014,"avatar_url":"https://avatars.githubusercontent.com/u/1821014?v=3","gravatar_id":"","url":"https://api.github.com/users/krancour","html_url":"https://github.com/krancour","followers_url":"https://api.github.com/users/krancour/followers","following_url":"https://api.github.com/users/krancour/following{/other_user}","gists_url":"https://api.github.com/users/krancour/gists{/gist_id}","starred_url":"https://api.github.com/users/krancour/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/krancour/subscriptions","organizations_url":"https://api.github.com/users/krancour/orgs","repos_url":"https://api.github.com/users/krancour/repos","events_url":"https://api.github.com/users/krancour/events{/privacy}","received_events_url":"https://api.github.com/users/krancour/received_events","type":"User","site_admin":false},"body":"Fixes #4000; replaces #4435 \r\n\r\n__TL;DR__: A complete rewrite of `deis-logger` aimed at easier extensibility and maintenance.  Some bug fixes and new features are included as well, as are many new unit tests.\r\n\r\nMotivations\r\n=========\r\n\r\n* #4000 called for the implementation of an in-memory ring buffer as an alternative to file-based log storage (which doesn't work out so well for those running stateless / without Ceph).\r\n* The possibility that in-memory storage is used as an alternative to file-based storage means `deis-controller` can no longer mount a volume in common with `deis-logger` to get logs.  `deis-logger` needed to evolve to serve logs to `deis-controller` over HTTP.\r\n* `deis-logger` was not originally written to _read_ logs; only to write them.\r\n* The implementation offered by the community in #4083 was welcome, but fell short in a few keys areas:\r\n  * Introduced the concept of a \"handler\" for storing and reading logs, but clashed with `deis-logger`'s existing notion of what a \"handler\" is.  (Overloaded term.)\r\n  * Introduced some undesired complexity-- e.g. `deis-controller` only read logs from `deis-logger` over HTTP if the in-memory storage option was in use.  (`deis-controller` shouldn't have to know _how_ logs are being stored and shouldn't have to adjust its behavior based on that.  Logs could/should _always_ be retrieved from the `deis-logger` over HTTP.)\r\n  * Only checked the \"handler\" type to use at startup; couldn't dynamically reconfigure itself.\r\n  * Didn't adequately address failure cases.\r\n* Despite those few problems, #4083 __actually worked great!__  Big shout out to @rvadim and @aj-may for their efforts on this.  Unfortunately, since the implementation was layered on top of existing code that was in rough shape to begin with, this didn't bode well for future maintenance and extension.  `deis-logger` as it stood prior to this PR:\r\n  * Was based on a library that hasn't been updated in three years.\r\n  * Had lots of dead, untested, and hard-to-follow code.\r\n  * Existing support for writing logs to files and draining logs was a hack to begin with.\r\n  * Only supported UDP-based drains.  (TCP needed for use with TLS-- future feature.)\r\n\r\nIssues such as #4280 are also considerations that informed the approach.\r\n\r\nWeighing the myriad concerns, a scorched earth re-design seemed the way to go.\r\n\r\nDesign\r\n=====\r\n\r\nHere's a brief overview of the design.  (I'll see about adding a use case diagram tomorrow to help make this easier for reviewers.)\r\n\r\nLogger\r\n---------\r\n\r\n`main.go` handles startup including flag parsing.  It supports all the same flags as the old implementation for complete backwards compatibility.  It initializes and starts four components:\r\n\r\n1. A `syslogish.Server`: reads and writes logs.  For writing, it implements a loop that receives logs (1 per packet) over UDP.  A non-blocking write immediately puts these into an internal queue.  This is _exactly_ the same as before.  Another loop ranges over the queue and processes log messages.  It delegates their storage to an underlying `storage.Adapter` (file-based or in-memory) and delegates draining to an underlying `drain.LogDrain` (UDP-based or TCP-based :new:; SSL not supported yet).  For reading, reads are also delegated to the underlying `storage.Adapter`.  A key improvement is that storage write failures (rare) and failures to dial the drain (could happen if Papertrail went down, for instance) are handled _silently_.  This sounds bad, but it helps avoid a logging death spiral that happens when the logger can't handle its own error messages.  (All other failures that won't start the death spiral _are_ logged, of course.)\r\n\r\n2. A `weblog.Server`: handles HTTP `GET` and `DELETE` requests.  Delegates all actual work to the `syslogish.Server`.\r\n\r\n3. A `Configurer`: uses a ticker and watches etcd for changes to `/deis/logs/storageAdapterType` and `/deis/logs/drain`.  When changes are detected, it uses factories that take the values of those keys to construct appropriate `storage.Adapter` and `drain.LogDrain` instances, respectively.  These are then injected into the `syslogish.Server`.\r\n\r\n4. A `Publisher`: uses a ticker and periodically writes the logger's host and port to etcd using the `/deis/logs/host` and `/deis/logs/port`, respectively.\r\n\r\nApart from these four primary components, there are also two implementations each of `storage.Adapter` and `drain.LogDrain`.  These are unit tested rather thoroughly.  (Which is the reason this PR is a net addition of lines to the code base.)\r\n\r\nOne other notable improvement is that complex cleanup logic has been removed.  It is not needed AFAICT.\r\n\r\nThe Fleet unit for `deis-logger` no longer `Requires` `deis-store-volume`, but merely `Wants` it.  This allows it to start up in the absence of Ceph.\r\n\r\nController\r\n-------------\r\n\r\nThe modifications to `deis-controller` are more modest.  confd is used to look up the `deis-logger` host now.  Combining that with a hard-coded port `8088` permits discovery of the `deis-logger` component's new `weblog.Server`.  The RESTful interface it exposes allows the controller to read and destroy logs.  Failure cases are all accounted for and tests have been added to simulate the failure scenarios and assert that they are handled properly.\r\n\r\nThe Fleet unit for `deis-controller` no longer expresses a dependency on `deis-store-volume`, nor does it mount the volumes from that container on the `deis-controller` container.  (Let's hear it for decoupling.)\r\n\r\ndeisctl\r\n---------\r\n\r\n`deisctl` has been modified to now install `deis-logger` with the stateless platform (previously omitted) and to also start that component during stateless platform start or restart.","created_at":"2015-09-17T05:42:15Z","updated_at":"2015-09-18T01:22:15Z","closed_at":null,"merged_at":null,"merge_commit_sha":"81e8b062469472446fa344b7440b859d053dc156","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/deis/deis/pulls/4502/commits","review_comments_url":"https://api.github.com/repos/deis/deis/pulls/4502/comments","review_comment_url":"https://api.github.com/repos/deis/deis/pulls/comments{/number}","comments_url":"https://api.github.com/repos/deis/deis/issues/4502/comments","statuses_url":"https://api.github.com/repos/deis/deis/statuses/17c878cca5f139bab77b4481bb5921a6664aa7c9","head":{"label":"krancour:logger-rewrite","ref":"logger-rewrite","sha":"17c878cca5f139bab77b4481bb5921a6664aa7c9","user":{"login":"krancour","id":1821014,"avatar_url":"https://avatars.githubusercontent.com/u/1821014?v=3","gravatar_id":"","url":"https://api.github.com/users/krancour","html_url":"https://github.com/krancour","followers_url":"https://api.github.com/users/krancour/followers","following_url":"https://api.github.com/users/krancour/following{/other_user}","gists_url":"https://api.github.com/users/krancour/gists{/gist_id}","starred_url":"https://api.github.com/users/krancour/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/krancour/subscriptions","organizations_url":"https://api.github.com/users/krancour/orgs","repos_url":"https://api.github.com/users/krancour/repos","events_url":"https://api.github.com/users/krancour/events{/privacy}","received_events_url":"https://api.github.com/users/krancour/received_events","type":"User","site_admin":false},"repo":{"id":33512149,"name":"deis","full_name":"krancour/deis","owner":{"login":"krancour","id":1821014,"avatar_url":"https://avatars.githubusercontent.com/u/1821014?v=3","gravatar_id":"","url":"https://api.github.com/users/krancour","html_url":"https://github.com/krancour","followers_url":"https://api.github.com/users/krancour/followers","following_url":"https://api.github.com/users/krancour/following{/other_user}","gists_url":"https://api.github.com/users/krancour/gists{/gist_id}","starred_url":"https://api.github.com/users/krancour/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/krancour/subscriptions","organizations_url":"https://api.github.com/users/krancour/orgs","repos_url":"https://api.github.com/users/krancour/repos","events_url":"https://api.github.com/users/krancour/events{/privacy}","received_events_url":"https://api.github.com/users/krancour/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/krancour/deis","description":"Your PaaS. Your Rules. ","fork":true,"url":"https://api.github.com/repos/krancour/deis","forks_url":"https://api.github.com/repos/krancour/deis/forks","keys_url":"https://api.github.com/repos/krancour/deis/keys{/key_id}","collaborators_url":"https://api.github.com/repos/krancour/deis/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/krancour/deis/teams","hooks_url":"https://api.github.com/repos/krancour/deis/hooks","issue_events_url":"https://api.github.com/repos/krancour/deis/issues/events{/number}","events_url":"https://api.github.com/repos/krancour/deis/events","assignees_url":"https://api.github.com/repos/krancour/deis/assignees{/user}","branches_url":"https://api.github.com/repos/krancour/deis/branches{/branch}","tags_url":"https://api.github.com/repos/krancour/deis/tags","blobs_url":"https://api.github.com/repos/krancour/deis/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/krancour/deis/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/krancour/deis/git/refs{/sha}","trees_url":"https://api.github.com/repos/krancour/deis/git/trees{/sha}","statuses_url":"https://api.github.com/repos/krancour/deis/statuses/{sha}","languages_url":"https://api.github.com/repos/krancour/deis/languages","stargazers_url":"https://api.github.com/repos/krancour/deis/stargazers","contributors_url":"https://api.github.com/repos/krancour/deis/contributors","subscribers_url":"https://api.github.com/repos/krancour/deis/subscribers","subscription_url":"https://api.github.com/repos/krancour/deis/subscription","commits_url":"https://api.github.com/repos/krancour/deis/commits{/sha}","git_commits_url":"https://api.github.com/repos/krancour/deis/git/commits{/sha}","comments_url":"https://api.github.com/repos/krancour/deis/comments{/number}","issue_comment_url":"https://api.github.com/repos/krancour/deis/issues/comments{/number}","contents_url":"https://api.github.com/repos/krancour/deis/contents/{+path}","compare_url":"https://api.github.com/repos/krancour/deis/compare/{base}...{head}","merges_url":"https://api.github.com/repos/krancour/deis/merges","archive_url":"https://api.github.com/repos/krancour/deis/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/krancour/deis/downloads","issues_url":"https://api.github.com/repos/krancour/deis/issues{/number}","pulls_url":"https://api.github.com/repos/krancour/deis/pulls{/number}","milestones_url":"https://api.github.com/repos/krancour/deis/milestones{/number}","notifications_url":"https://api.github.com/repos/krancour/deis/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/krancour/deis/labels{/name}","releases_url":"https://api.github.com/repos/krancour/deis/releases{/id}","created_at":"2015-04-06T23:58:08Z","updated_at":"2015-05-05T14:10:36Z","pushed_at":"2015-09-17T21:14:04Z","git_url":"git://github.com/krancour/deis.git","ssh_url":"git@github.com:krancour/deis.git","clone_url":"https://github.com/krancour/deis.git","svn_url":"https://github.com/krancour/deis","homepage":"http://deis.io","size":19603,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"deis:master","ref":"master","sha":"0abf095947ba80f09d4e43142562550a187c3a6c","user":{"login":"deis","id":6117407,"avatar_url":"https://avatars.githubusercontent.com/u/6117407?v=3","gravatar_id":"","url":"https://api.github.com/users/deis","html_url":"https://github.com/deis","followers_url":"https://api.github.com/users/deis/followers","following_url":"https://api.github.com/users/deis/following{/other_user}","gists_url":"https://api.github.com/users/deis/gists{/gist_id}","starred_url":"https://api.github.com/users/deis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/deis/subscriptions","organizations_url":"https://api.github.com/users/deis/orgs","repos_url":"https://api.github.com/users/deis/repos","events_url":"https://api.github.com/users/deis/events{/privacy}","received_events_url":"https://api.github.com/users/deis/received_events","type":"Organization","site_admin":false},"repo":{"id":11589713,"name":"deis","full_name":"deis/deis","owner":{"login":"deis","id":6117407,"avatar_url":"https://avatars.githubusercontent.com/u/6117407?v=3","gravatar_id":"","url":"https://api.github.com/users/deis","html_url":"https://github.com/deis","followers_url":"https://api.github.com/users/deis/followers","following_url":"https://api.github.com/users/deis/following{/other_user}","gists_url":"https://api.github.com/users/deis/gists{/gist_id}","starred_url":"https://api.github.com/users/deis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/deis/subscriptions","organizations_url":"https://api.github.com/users/deis/orgs","repos_url":"https://api.github.com/users/deis/repos","events_url":"https://api.github.com/users/deis/events{/privacy}","received_events_url":"https://api.github.com/users/deis/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/deis/deis","description":"Your PaaS. Your Rules. ","fork":false,"url":"https://api.github.com/repos/deis/deis","forks_url":"https://api.github.com/repos/deis/deis/forks","keys_url":"https://api.github.com/repos/deis/deis/keys{/key_id}","collaborators_url":"https://api.github.com/repos/deis/deis/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/deis/deis/teams","hooks_url":"https://api.github.com/repos/deis/deis/hooks","issue_events_url":"https://api.github.com/repos/deis/deis/issues/events{/number}","events_url":"https://api.github.com/repos/deis/deis/events","assignees_url":"https://api.github.com/repos/deis/deis/assignees{/user}","branches_url":"https://api.github.com/repos/deis/deis/branches{/branch}","tags_url":"https://api.github.com/repos/deis/deis/tags","blobs_url":"https://api.github.com/repos/deis/deis/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/deis/deis/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/deis/deis/git/refs{/sha}","trees_url":"https://api.github.com/repos/deis/deis/git/trees{/sha}","statuses_url":"https://api.github.com/repos/deis/deis/statuses/{sha}","languages_url":"https://api.github.com/repos/deis/deis/languages","stargazers_url":"https://api.github.com/repos/deis/deis/stargazers","contributors_url":"https://api.github.com/repos/deis/deis/contributors","subscribers_url":"https://api.github.com/repos/deis/deis/subscribers","subscription_url":"https://api.github.com/repos/deis/deis/subscription","commits_url":"https://api.github.com/repos/deis/deis/commits{/sha}","git_commits_url":"https://api.github.com/repos/deis/deis/git/commits{/sha}","comments_url":"https://api.github.com/repos/deis/deis/comments{/number}","issue_comment_url":"https://api.github.com/repos/deis/deis/issues/comments{/number}","contents_url":"https://api.github.com/repos/deis/deis/contents/{+path}","compare_url":"https://api.github.com/repos/deis/deis/compare/{base}...{head}","merges_url":"https://api.github.com/repos/deis/deis/merges","archive_url":"https://api.github.com/repos/deis/deis/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/deis/deis/downloads","issues_url":"https://api.github.com/repos/deis/deis/issues{/number}","pulls_url":"https://api.github.com/repos/deis/deis/pulls{/number}","milestones_url":"https://api.github.com/repos/deis/deis/milestones{/number}","notifications_url":"https://api.github.com/repos/deis/deis/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/deis/deis/labels{/name}","releases_url":"https://api.github.com/repos/deis/deis/releases{/id}","created_at":"2013-07-22T19:02:40Z","updated_at":"2015-09-17T18:20:00Z","pushed_at":"2015-09-18T00:08:40Z","git_url":"git://github.com/deis/deis.git","ssh_url":"git@github.com:deis/deis.git","clone_url":"https://github.com/deis/deis.git","svn_url":"https://github.com/deis/deis","homepage":"http://deis.io","size":42574,"stargazers_count":4731,"watchers_count":4731,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":744,"mirror_url":null,"open_issues_count":285,"forks":744,"open_issues":285,"watchers":4731,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/deis/deis/pulls/4502"},"html":{"href":"https://github.com/deis/deis/pull/4502"},"issue":{"href":"https://api.github.com/repos/deis/deis/issues/4502"},"comments":{"href":"https://api.github.com/repos/deis/deis/issues/4502/comments"},"review_comments":{"href":"https://api.github.com/repos/deis/deis/pulls/4502/comments"},"review_comment":{"href":"https://api.github.com/repos/deis/deis/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/deis/deis/pulls/4502/commits"},"statuses":{"href":"https://api.github.com/repos/deis/deis/statuses/17c878cca5f139bab77b4481bb5921a6664aa7c9"}}}}