{"action":"created","comment":{"url":"https://api.github.com/repos/heroku/heroku-spaces/pulls/comments/65734229","id":65734229,"diff_hunk":"@@ -0,0 +1,86 @@\n+'use strict'\n+\n+let cli = require('heroku-cli-util')\n+\n+module.exports = function (heroku) {\n+  function getOutboundRules (space) {\n+    return heroku.request({\n+      path: `/spaces/${space}/outbound-ruleset`,\n+      headers: {Accept: 'application/vnd.heroku+json; version=3.dogwood'}\n+    })\n+  }\n+\n+  function putOutboundRules (space, ruleset) {\n+    return heroku.request({\n+      method: 'PUT',\n+      path: `/spaces/${space}/outbound-ruleset`,\n+      body: ruleset,\n+      headers: {Accept: 'application/vnd.heroku+json; version=3.dogwood'}\n+    })\n+  }\n+\n+  function displayRules (space, ruleset) {\n+    if (ruleset.rules.length > 0) {\n+      cli.styledHeader('Outbound Rules')\n+      display(ruleset.rules)\n+    } else {\n+      cli.styledHeader(`${space} has no Outbound Rules. Your Dynos cannot communicate with hosts outside of ${space}.`)\n+    }\n+  }\n+\n+  function lined (rules) {\n+    var lined = []\n+    for (var i = 0, len = rules.length; i < len; i++) {\n+      lined.push({\n+        line: i + 1,\n+        target: rules[i].target,\n+        from_port: rules[i].from_port,\n+        to_port: rules[i].to_port,\n+        protocol: rules[i].protocol\n+      })\n+    }\n+\n+    return lined\n+  }\n+\n+  function display (rules) {\n+    var f = function (p) {\n+      var n = p\n+      return n.toString()\n+    }\n+\n+    cli.table(lined(rules), {\n+      columns: [\n+        {key: 'line', label: 'Rule Number'},\n+        {key: 'target', label: 'Destination'},\n+        {key: 'from_port', label: 'From Port', format: fromPort => f(fromPort)},\n+        {key: 'to_port', label: 'To Port', format: toPort => f(toPort)},\n+        {key: 'protocol', label: 'Protocol'}\n+      ]\n+    })\n+  }\n+\n+  function parsePorts (p) {\n+    if (p === '-1' || p === 'any') {\n+      return [0, 65535]","path":"lib/outbound-rules.js","position":65,"original_position":65,"commit_id":"ce709b5527c8e40777ec28a3d360f072422a451c","original_commit_id":"3af6e6b0c91568561981de6397c6a345b4c932f2","user":{"login":"amerine","id":1016,"avatar_url":"https://avatars.githubusercontent.com/u/1016?v=3","gravatar_id":"","url":"https://api.github.com/users/amerine","html_url":"https://github.com/amerine","followers_url":"https://api.github.com/users/amerine/followers","following_url":"https://api.github.com/users/amerine/following{/other_user}","gists_url":"https://api.github.com/users/amerine/gists{/gist_id}","starred_url":"https://api.github.com/users/amerine/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/amerine/subscriptions","organizations_url":"https://api.github.com/users/amerine/orgs","repos_url":"https://api.github.com/users/amerine/repos","events_url":"https://api.github.com/users/amerine/events{/privacy}","received_events_url":"https://api.github.com/users/amerine/received_events","type":"User","site_admin":false},"body":"Fixed","created_at":"2016-06-03T16:13:24Z","updated_at":"2016-06-03T16:13:24Z","html_url":"https://github.com/heroku/heroku-spaces/pull/23#discussion_r65734229","pull_request_url":"https://api.github.com/repos/heroku/heroku-spaces/pulls/23","_links":{"self":{"href":"https://api.github.com/repos/heroku/heroku-spaces/pulls/comments/65734229"},"html":{"href":"https://github.com/heroku/heroku-spaces/pull/23#discussion_r65734229"},"pull_request":{"href":"https://api.github.com/repos/heroku/heroku-spaces/pulls/23"}}},"pull_request":{"url":"https://api.github.com/repos/heroku/heroku-spaces/pulls/23","id":72315520,"html_url":"https://github.com/heroku/heroku-spaces/pull/23","diff_url":"https://github.com/heroku/heroku-spaces/pull/23.diff","patch_url":"https://github.com/heroku/heroku-spaces/pull/23.patch","issue_url":"https://api.github.com/repos/heroku/heroku-spaces/issues/23","number":23,"state":"open","locked":false,"title":"Outbound Rules","user":{"login":"amerine","id":1016,"avatar_url":"https://avatars.githubusercontent.com/u/1016?v=3","gravatar_id":"","url":"https://api.github.com/users/amerine","html_url":"https://github.com/amerine","followers_url":"https://api.github.com/users/amerine/followers","following_url":"https://api.github.com/users/amerine/following{/other_user}","gists_url":"https://api.github.com/users/amerine/gists{/gist_id}","starred_url":"https://api.github.com/users/amerine/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/amerine/subscriptions","organizations_url":"https://api.github.com/users/amerine/orgs","repos_url":"https://api.github.com/users/amerine/repos","events_url":"https://api.github.com/users/amerine/events{/privacy}","received_events_url":"https://api.github.com/users/amerine/received_events","type":"User","site_admin":false},"body":"cc @jsullivan \r\n\r\nThis adds support to the heroku-spaces plugin to handle management of the Outbound Rules feature.\r\n\r\nThis supports the following commands:\r\n\r\n`heroku outbound-rules --space <<space>>`\r\n`heroku outbound-rules:add --space <<space>> --dest 0.0.0.0/0 --protocol tcp --port any`\r\n`heroku outbound-rules:remove --space <<space>>  <<rule number>>`\r\n\r\n# Adding Rule:\r\n\r\nYou can pass a range of port options\r\n\r\n```shell\r\n% heroku outbound-rules:add --space example-space --dest 0.0.0.0/0 --protocol tcp --port 80\r\nParsing heroku-spaces... done\r\nAdded rule to the Outbound Rules of example-space\r\n ▸    It may take a few moments for the changes to take effect.\r\n```\r\n\r\nWith a range\r\n```shell\r\n% heroku outbound-rules:add --space example-space --dest 0.0.0.0/0 --protocol tcp --port 80-100\r\nParsing heroku-spaces... done\r\nAdded rule to the Outbound Rules of example-space\r\n ▸    It may take a few moments for the changes to take effect.\r\n```\r\n\r\nAny Port\r\n```shell\r\n% heroku outbound-rules:add --space example-space --dest 0.0.0.0/0 --protocol tcp --port any\r\nParsing heroku-spaces... done\r\nAdded rule to the Outbound Rules of example-space\r\n ▸    It may take a few moments for the changes to take effect.\r\n```\r\n\r\n\r\n# Listing Rules:\r\n\r\n```shell\r\n% heroku outbound-rules --space example-space\r\n=== Outbound Rules\r\nRule Number  Destination  From Port  To Port  Protocol\r\n───────────  ───────────  ─────────  ───────  ────────\r\n1            0.0.0.0/0    0                   any\r\n2            0.0.0.0/0    0                   tcp\r\n```\r\n\r\n# Removing Rules\r\n```shell\r\n% heroku outbound-rules:remove --space example-space 2\r\n ▸    Destructive Action\r\n ▸    This will remove:\r\n ▸    Destination: 0.0.0.0/0, From Port: 0, To Port: 0, Protocol any\r\n ▸    from the Outbound Rules on example-space\r\n ▸\r\n ▸    To proceed, type example-space or re-run this command with --confirm example-space\r\n\r\n> example-space\r\nRemoved Rule 2 from Outbound Rules on example-space\r\n ▸    It may take a few moments for the changes to take effect.\r\n```\r\n","created_at":"2016-06-02T09:34:18Z","updated_at":"2016-06-03T16:13:24Z","closed_at":null,"merged_at":null,"merge_commit_sha":"807389e10a136abf4268b4340ee4216411492fb9","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/heroku/heroku-spaces/pulls/23/commits","review_comments_url":"https://api.github.com/repos/heroku/heroku-spaces/pulls/23/comments","review_comment_url":"https://api.github.com/repos/heroku/heroku-spaces/pulls/comments{/number}","comments_url":"https://api.github.com/repos/heroku/heroku-spaces/issues/23/comments","statuses_url":"https://api.github.com/repos/heroku/heroku-spaces/statuses/ce709b5527c8e40777ec28a3d360f072422a451c","head":{"label":"heroku:outbound-restrictions","ref":"outbound-restrictions","sha":"ce709b5527c8e40777ec28a3d360f072422a451c","user":{"login":"heroku","id":23211,"avatar_url":"https://avatars.githubusercontent.com/u/23211?v=3","gravatar_id":"","url":"https://api.github.com/users/heroku","html_url":"https://github.com/heroku","followers_url":"https://api.github.com/users/heroku/followers","following_url":"https://api.github.com/users/heroku/following{/other_user}","gists_url":"https://api.github.com/users/heroku/gists{/gist_id}","starred_url":"https://api.github.com/users/heroku/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/heroku/subscriptions","organizations_url":"https://api.github.com/users/heroku/orgs","repos_url":"https://api.github.com/users/heroku/repos","events_url":"https://api.github.com/users/heroku/events{/privacy}","received_events_url":"https://api.github.com/users/heroku/received_events","type":"Organization","site_admin":false},"repo":{"id":29360304,"name":"heroku-spaces","full_name":"heroku/heroku-spaces","owner":{"login":"heroku","id":23211,"avatar_url":"https://avatars.githubusercontent.com/u/23211?v=3","gravatar_id":"","url":"https://api.github.com/users/heroku","html_url":"https://github.com/heroku","followers_url":"https://api.github.com/users/heroku/followers","following_url":"https://api.github.com/users/heroku/following{/other_user}","gists_url":"https://api.github.com/users/heroku/gists{/gist_id}","starred_url":"https://api.github.com/users/heroku/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/heroku/subscriptions","organizations_url":"https://api.github.com/users/heroku/orgs","repos_url":"https://api.github.com/users/heroku/repos","events_url":"https://api.github.com/users/heroku/events{/privacy}","received_events_url":"https://api.github.com/users/heroku/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/heroku/heroku-spaces","description":"Heroku CLI plugin for Spaces","fork":false,"url":"https://api.github.com/repos/heroku/heroku-spaces","forks_url":"https://api.github.com/repos/heroku/heroku-spaces/forks","keys_url":"https://api.github.com/repos/heroku/heroku-spaces/keys{/key_id}","collaborators_url":"https://api.github.com/repos/heroku/heroku-spaces/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/heroku/heroku-spaces/teams","hooks_url":"https://api.github.com/repos/heroku/heroku-spaces/hooks","issue_events_url":"https://api.github.com/repos/heroku/heroku-spaces/issues/events{/number}","events_url":"https://api.github.com/repos/heroku/heroku-spaces/events","assignees_url":"https://api.github.com/repos/heroku/heroku-spaces/assignees{/user}","branches_url":"https://api.github.com/repos/heroku/heroku-spaces/branches{/branch}","tags_url":"https://api.github.com/repos/heroku/heroku-spaces/tags","blobs_url":"https://api.github.com/repos/heroku/heroku-spaces/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/heroku/heroku-spaces/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/heroku/heroku-spaces/git/refs{/sha}","trees_url":"https://api.github.com/repos/heroku/heroku-spaces/git/trees{/sha}","statuses_url":"https://api.github.com/repos/heroku/heroku-spaces/statuses/{sha}","languages_url":"https://api.github.com/repos/heroku/heroku-spaces/languages","stargazers_url":"https://api.github.com/repos/heroku/heroku-spaces/stargazers","contributors_url":"https://api.github.com/repos/heroku/heroku-spaces/contributors","subscribers_url":"https://api.github.com/repos/heroku/heroku-spaces/subscribers","subscription_url":"https://api.github.com/repos/heroku/heroku-spaces/subscription","commits_url":"https://api.github.com/repos/heroku/heroku-spaces/commits{/sha}","git_commits_url":"https://api.github.com/repos/heroku/heroku-spaces/git/commits{/sha}","comments_url":"https://api.github.com/repos/heroku/heroku-spaces/comments{/number}","issue_comment_url":"https://api.github.com/repos/heroku/heroku-spaces/issues/comments{/number}","contents_url":"https://api.github.com/repos/heroku/heroku-spaces/contents/{+path}","compare_url":"https://api.github.com/repos/heroku/heroku-spaces/compare/{base}...{head}","merges_url":"https://api.github.com/repos/heroku/heroku-spaces/merges","archive_url":"https://api.github.com/repos/heroku/heroku-spaces/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/heroku/heroku-spaces/downloads","issues_url":"https://api.github.com/repos/heroku/heroku-spaces/issues{/number}","pulls_url":"https://api.github.com/repos/heroku/heroku-spaces/pulls{/number}","milestones_url":"https://api.github.com/repos/heroku/heroku-spaces/milestones{/number}","notifications_url":"https://api.github.com/repos/heroku/heroku-spaces/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/heroku/heroku-spaces/labels{/name}","releases_url":"https://api.github.com/repos/heroku/heroku-spaces/releases{/id}","deployments_url":"https://api.github.com/repos/heroku/heroku-spaces/deployments","created_at":"2015-01-16T17:50:37Z","updated_at":"2016-05-24T14:24:24Z","pushed_at":"2016-06-03T07:55:00Z","git_url":"git://github.com/heroku/heroku-spaces.git","ssh_url":"git@github.com:heroku/heroku-spaces.git","clone_url":"https://github.com/heroku/heroku-spaces.git","svn_url":"https://github.com/heroku/heroku-spaces","homepage":"","size":122,"stargazers_count":1,"watchers_count":1,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":2,"forks":0,"open_issues":2,"watchers":1,"default_branch":"master"}},"base":{"label":"heroku:master","ref":"master","sha":"91864d779deef6cebb28db5619c49ebb804918c2","user":{"login":"heroku","id":23211,"avatar_url":"https://avatars.githubusercontent.com/u/23211?v=3","gravatar_id":"","url":"https://api.github.com/users/heroku","html_url":"https://github.com/heroku","followers_url":"https://api.github.com/users/heroku/followers","following_url":"https://api.github.com/users/heroku/following{/other_user}","gists_url":"https://api.github.com/users/heroku/gists{/gist_id}","starred_url":"https://api.github.com/users/heroku/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/heroku/subscriptions","organizations_url":"https://api.github.com/users/heroku/orgs","repos_url":"https://api.github.com/users/heroku/repos","events_url":"https://api.github.com/users/heroku/events{/privacy}","received_events_url":"https://api.github.com/users/heroku/received_events","type":"Organization","site_admin":false},"repo":{"id":29360304,"name":"heroku-spaces","full_name":"heroku/heroku-spaces","owner":{"login":"heroku","id":23211,"avatar_url":"https://avatars.githubusercontent.com/u/23211?v=3","gravatar_id":"","url":"https://api.github.com/users/heroku","html_url":"https://github.com/heroku","followers_url":"https://api.github.com/users/heroku/followers","following_url":"https://api.github.com/users/heroku/following{/other_user}","gists_url":"https://api.github.com/users/heroku/gists{/gist_id}","starred_url":"https://api.github.com/users/heroku/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/heroku/subscriptions","organizations_url":"https://api.github.com/users/heroku/orgs","repos_url":"https://api.github.com/users/heroku/repos","events_url":"https://api.github.com/users/heroku/events{/privacy}","received_events_url":"https://api.github.com/users/heroku/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/heroku/heroku-spaces","description":"Heroku CLI plugin for Spaces","fork":false,"url":"https://api.github.com/repos/heroku/heroku-spaces","forks_url":"https://api.github.com/repos/heroku/heroku-spaces/forks","keys_url":"https://api.github.com/repos/heroku/heroku-spaces/keys{/key_id}","collaborators_url":"https://api.github.com/repos/heroku/heroku-spaces/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/heroku/heroku-spaces/teams","hooks_url":"https://api.github.com/repos/heroku/heroku-spaces/hooks","issue_events_url":"https://api.github.com/repos/heroku/heroku-spaces/issues/events{/number}","events_url":"https://api.github.com/repos/heroku/heroku-spaces/events","assignees_url":"https://api.github.com/repos/heroku/heroku-spaces/assignees{/user}","branches_url":"https://api.github.com/repos/heroku/heroku-spaces/branches{/branch}","tags_url":"https://api.github.com/repos/heroku/heroku-spaces/tags","blobs_url":"https://api.github.com/repos/heroku/heroku-spaces/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/heroku/heroku-spaces/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/heroku/heroku-spaces/git/refs{/sha}","trees_url":"https://api.github.com/repos/heroku/heroku-spaces/git/trees{/sha}","statuses_url":"https://api.github.com/repos/heroku/heroku-spaces/statuses/{sha}","languages_url":"https://api.github.com/repos/heroku/heroku-spaces/languages","stargazers_url":"https://api.github.com/repos/heroku/heroku-spaces/stargazers","contributors_url":"https://api.github.com/repos/heroku/heroku-spaces/contributors","subscribers_url":"https://api.github.com/repos/heroku/heroku-spaces/subscribers","subscription_url":"https://api.github.com/repos/heroku/heroku-spaces/subscription","commits_url":"https://api.github.com/repos/heroku/heroku-spaces/commits{/sha}","git_commits_url":"https://api.github.com/repos/heroku/heroku-spaces/git/commits{/sha}","comments_url":"https://api.github.com/repos/heroku/heroku-spaces/comments{/number}","issue_comment_url":"https://api.github.com/repos/heroku/heroku-spaces/issues/comments{/number}","contents_url":"https://api.github.com/repos/heroku/heroku-spaces/contents/{+path}","compare_url":"https://api.github.com/repos/heroku/heroku-spaces/compare/{base}...{head}","merges_url":"https://api.github.com/repos/heroku/heroku-spaces/merges","archive_url":"https://api.github.com/repos/heroku/heroku-spaces/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/heroku/heroku-spaces/downloads","issues_url":"https://api.github.com/repos/heroku/heroku-spaces/issues{/number}","pulls_url":"https://api.github.com/repos/heroku/heroku-spaces/pulls{/number}","milestones_url":"https://api.github.com/repos/heroku/heroku-spaces/milestones{/number}","notifications_url":"https://api.github.com/repos/heroku/heroku-spaces/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/heroku/heroku-spaces/labels{/name}","releases_url":"https://api.github.com/repos/heroku/heroku-spaces/releases{/id}","deployments_url":"https://api.github.com/repos/heroku/heroku-spaces/deployments","created_at":"2015-01-16T17:50:37Z","updated_at":"2016-05-24T14:24:24Z","pushed_at":"2016-06-03T07:55:00Z","git_url":"git://github.com/heroku/heroku-spaces.git","ssh_url":"git@github.com:heroku/heroku-spaces.git","clone_url":"https://github.com/heroku/heroku-spaces.git","svn_url":"https://github.com/heroku/heroku-spaces","homepage":"","size":122,"stargazers_count":1,"watchers_count":1,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":2,"forks":0,"open_issues":2,"watchers":1,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/heroku/heroku-spaces/pulls/23"},"html":{"href":"https://github.com/heroku/heroku-spaces/pull/23"},"issue":{"href":"https://api.github.com/repos/heroku/heroku-spaces/issues/23"},"comments":{"href":"https://api.github.com/repos/heroku/heroku-spaces/issues/23/comments"},"review_comments":{"href":"https://api.github.com/repos/heroku/heroku-spaces/pulls/23/comments"},"review_comment":{"href":"https://api.github.com/repos/heroku/heroku-spaces/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/heroku/heroku-spaces/pulls/23/commits"},"statuses":{"href":"https://api.github.com/repos/heroku/heroku-spaces/statuses/ce709b5527c8e40777ec28a3d360f072422a451c"}}}}
{"actor":{"display_login":"amerine"}}