{"action":"created","comment":{"url":"https://api.github.com/repos/joyent/containerbuddy/pulls/comments/48615798","id":48615798,"diff_hunk":"@@ -0,0 +1,253 @@\n+package main\n+\n+import (\n+\t\"bytes\"\n+\t\"fmt\"\n+\t\"log\"\n+\t\"net\"\n+\t\"regexp\"\n+\t\"sort\"\n+\t\"strconv\"\n+\t\"strings\"\n+)\n+\n+// GetIP determines the IP address of the container\n+func GetIP(specList []string) (string, error) {\n+\n+\tif specList == nil || len(specList) == 0 {\n+\t\t// Use a sane default\n+\t\tspecList = []string{\"eth0:inet\"}\n+\t}\n+\n+\tspecs, err := parseInterfaceSpecs(specList)\n+\tif err != nil {\n+\t\treturn \"\", err\n+\t}\n+\n+\tinterfaces, interfacesErr := net.Interfaces()\n+\n+\tif interfacesErr != nil {\n+\t\treturn \"\", interfacesErr\n+\t}\n+\n+\tinterfaceIPs, interfaceIPsErr := getinterfaceIPs(interfaces)\n+\n+\t/* We had an error and there were no interfaces returned, this is clearly\n+\t * an error state. */\n+\tif interfaceIPsErr != nil && len(interfaceIPs) < 1 {\n+\t\treturn \"\", interfaceIPsErr\n+\t}\n+\t/* We had error(s) and there were interfaces returned, this is potentially\n+\t * recoverable. Let's pass on the parsed interfaces and log the error\n+\t * state. */\n+\tif interfaceIPsErr != nil && len(interfaceIPs) > 0 {\n+\t\tlog.Printf(\"We had a problem reading information about some network \"+\n+\t\t\t\"interfaces. If everything works, it is safe to ignore this\"+\n+\t\t\t\"message. Details:\\n%s\\n\", interfaceIPsErr)\n+\t}\n+\n+\treturn findIPWithSpecs(specs, interfaceIPs)\n+}\n+\n+// findIPWithSpecs will use the given interface specification list and will\n+// find the first IP in the interfaceIPs that matches a spec\n+func findIPWithSpecs(specs []interfaceSpec, interfaceIPs []interfaceIP) (string, error) {\n+\t// Find the interface matching the name given\n+\tfor _, spec := range specs {\n+\t\tindex := 1\n+\t\tiface := \"\"\n+\t\tfor _, iip := range interfaceIPs {\n+\t\t\t// Since the interfaces are ordered by name\n+\t\t\t// a change in interface name can safely reset the index\n+\t\t\tif iface != iip.Name {\n+\t\t\t\tindex = 1\n+\t\t\t\tiface = iip.Name\n+\t\t\t} else {\n+\t\t\t\tindex++\n+\t\t\t}\n+\t\t\tif spec.Match(index, iip) {\n+\t\t\t\treturn iip.IPString(), nil\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t// Interface not found, return error\n+\treturn \"\", fmt.Errorf(\"None of the interface specifications were able to match\\nSpecifications: %s\\nInterfaces IPs: %s\",\n+\t\tspecs, interfaceIPs)\n+}\n+\n+type interfaceSpec struct {\n+\tSpec    string\n+\tIPv6    bool\n+\tName    string\n+\tNetwork *net.IPNet\n+\tIndex   int\n+}\n+\n+func (spec interfaceSpec) String() string {\n+\treturn spec.Spec\n+}\n+\n+func (spec interfaceSpec) Match(index int, iip interfaceIP) bool {\n+\t// Specific Interface eth1, eth0[1], eth0:inet6, inet, inet6\n+\tif spec.Name == iip.Name || spec.Name == \"*\" {\n+\t\t// Has index and matches\n+\t\tif spec.Index > 0 {\n+\t\t\treturn (spec.Index == index)\n+\t\t}\n+\t\t// Don't match loopback address for wildcard spec\n+\t\tif spec.Name == \"*\" && iip.IP.IsLoopback() {\n+\t\t\treturn false\n+\t\t}\n+\t\treturn spec.IPv6 != iip.IsIPv4()\n+\t}\n+\t// CIDR\n+\tif spec.Network != nil && spec.Network.Contains(iip.IP) {\n+\t\treturn true\n+\t}\n+\n+\treturn false\n+}\n+\n+func parseInterfaceSpecs(interfaces []string) ([]interfaceSpec, error) {\n+\tvar errors []string\n+\tvar specs []interfaceSpec\n+\tfor _, iface := range interfaces {\n+\t\tspec, err := parseInterfaceSpec(iface)\n+\t\tif err != nil {\n+\t\t\terrors = append(errors, err.Error())\n+\t\t\tcontinue\n+\t\t}\n+\t\tspecs = append(specs, spec)\n+\t}\n+\tif len(errors) > 0 {\n+\t\terr := fmt.Errorf(strings.Join(errors, \"\\n\"))\n+\t\tprintln(err.Error())","path":"src/containerbuddy/ips.go","position":125,"original_position":125,"commit_id":"0e613d44943be15f327495e6ac0d0ab048f5f226","original_commit_id":"0e613d44943be15f327495e6ac0d0ab048f5f226","user":{"login":"tgross","id":1409219,"avatar_url":"https://avatars.githubusercontent.com/u/1409219?v=3","gravatar_id":"","url":"https://api.github.com/users/tgross","html_url":"https://github.com/tgross","followers_url":"https://api.github.com/users/tgross/followers","following_url":"https://api.github.com/users/tgross/following{/other_user}","gists_url":"https://api.github.com/users/tgross/gists{/gist_id}","starred_url":"https://api.github.com/users/tgross/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tgross/subscriptions","organizations_url":"https://api.github.com/users/tgross/orgs","repos_url":"https://api.github.com/users/tgross/repos","events_url":"https://api.github.com/users/tgross/events{/privacy}","received_events_url":"https://api.github.com/users/tgross/received_events","type":"User","site_admin":false},"body":"This should be `log.Println(err)` I think?","created_at":"2015-12-30T16:55:54Z","updated_at":"2015-12-30T16:55:54Z","html_url":"https://github.com/joyent/containerbuddy/pull/58#discussion_r48615798","pull_request_url":"https://api.github.com/repos/joyent/containerbuddy/pulls/58","_links":{"self":{"href":"https://api.github.com/repos/joyent/containerbuddy/pulls/comments/48615798"},"html":{"href":"https://github.com/joyent/containerbuddy/pull/58#discussion_r48615798"},"pull_request":{"href":"https://api.github.com/repos/joyent/containerbuddy/pulls/58"}}},"pull_request":{"url":"https://api.github.com/repos/joyent/containerbuddy/pulls/58","id":54765878,"html_url":"https://github.com/joyent/containerbuddy/pull/58","diff_url":"https://github.com/joyent/containerbuddy/pull/58.diff","patch_url":"https://github.com/joyent/containerbuddy/pull/58.patch","issue_url":"https://api.github.com/repos/joyent/containerbuddy/issues/58","number":58,"state":"open","locked":false,"title":"Implement interface specification syntax","user":{"login":"justenwalker","id":222027,"avatar_url":"https://avatars.githubusercontent.com/u/222027?v=3","gravatar_id":"","url":"https://api.github.com/users/justenwalker","html_url":"https://github.com/justenwalker","followers_url":"https://api.github.com/users/justenwalker/followers","following_url":"https://api.github.com/users/justenwalker/following{/other_user}","gists_url":"https://api.github.com/users/justenwalker/gists{/gist_id}","starred_url":"https://api.github.com/users/justenwalker/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/justenwalker/subscriptions","organizations_url":"https://api.github.com/users/justenwalker/orgs","repos_url":"https://api.github.com/users/justenwalker/repos","events_url":"https://api.github.com/users/justenwalker/events{/privacy}","received_events_url":"https://api.github.com/users/justenwalker/received_events","type":"User","site_admin":false},"body":"For #53\r\n\r\n## Changes\r\n\r\n- Factor out IP/Interface logic into its own go file\r\n- Implement stable sorting of interfaces and their IP\r\n- Implement Interface Specification syntax for the `interfaces` option\r\n\r\n## Interface Specification \r\n\r\n- `eth0` : Match the first IPv4 address on `eth0` (alias for `eth0:inet`)\r\n- `eth0:inet6` : Match the first IPv6 address on `eth0`\r\n- `eth0[2]` : Match the 2nd IP address on `eth0`\r\n- `10.0.0.0/16` : Match the first IP that is contained within the IP Network\r\n- `fdc6:238c:c4bc::/48` : Match the first IP that is contained within the IPv6 Network\r\n- `inet` : Match the first IPv4 Address (excluding `127.0.0.0/8`)\r\n- `inet6` : Match the first IPv6 Address (excluding `::1/128`)\r\n\r\n## Stable Sorting\r\n\r\nInterfaces and their IP addresses are ordered alphabetically by interface name, then by IP address (lexicographically by bytes).\r\n\r\n- eth0 10.2.0.1 192.168.1.100\r\n- eth1 10.0.0.100 10.0.0.200\r\n- eth2 10.1.0.200 fdc6:238c:c4bc::1\r\n- lo ::1 127.0.0.1","created_at":"2015-12-29T23:07:36Z","updated_at":"2015-12-30T16:55:54Z","closed_at":null,"merged_at":null,"merge_commit_sha":"222add016176c1c31e79460a89a9f031047433b0","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/joyent/containerbuddy/pulls/58/commits","review_comments_url":"https://api.github.com/repos/joyent/containerbuddy/pulls/58/comments","review_comment_url":"https://api.github.com/repos/joyent/containerbuddy/pulls/comments{/number}","comments_url":"https://api.github.com/repos/joyent/containerbuddy/issues/58/comments","statuses_url":"https://api.github.com/repos/joyent/containerbuddy/statuses/0e613d44943be15f327495e6ac0d0ab048f5f226","head":{"label":"justenwalker:interface_spec","ref":"interface_spec","sha":"0e613d44943be15f327495e6ac0d0ab048f5f226","user":{"login":"justenwalker","id":222027,"avatar_url":"https://avatars.githubusercontent.com/u/222027?v=3","gravatar_id":"","url":"https://api.github.com/users/justenwalker","html_url":"https://github.com/justenwalker","followers_url":"https://api.github.com/users/justenwalker/followers","following_url":"https://api.github.com/users/justenwalker/following{/other_user}","gists_url":"https://api.github.com/users/justenwalker/gists{/gist_id}","starred_url":"https://api.github.com/users/justenwalker/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/justenwalker/subscriptions","organizations_url":"https://api.github.com/users/justenwalker/orgs","repos_url":"https://api.github.com/users/justenwalker/repos","events_url":"https://api.github.com/users/justenwalker/events{/privacy}","received_events_url":"https://api.github.com/users/justenwalker/received_events","type":"User","site_admin":false},"repo":{"id":47218030,"name":"containerbuddy","full_name":"justenwalker/containerbuddy","owner":{"login":"justenwalker","id":222027,"avatar_url":"https://avatars.githubusercontent.com/u/222027?v=3","gravatar_id":"","url":"https://api.github.com/users/justenwalker","html_url":"https://github.com/justenwalker","followers_url":"https://api.github.com/users/justenwalker/followers","following_url":"https://api.github.com/users/justenwalker/following{/other_user}","gists_url":"https://api.github.com/users/justenwalker/gists{/gist_id}","starred_url":"https://api.github.com/users/justenwalker/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/justenwalker/subscriptions","organizations_url":"https://api.github.com/users/justenwalker/orgs","repos_url":"https://api.github.com/users/justenwalker/repos","events_url":"https://api.github.com/users/justenwalker/events{/privacy}","received_events_url":"https://api.github.com/users/justenwalker/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/justenwalker/containerbuddy","description":"A service for autodiscovery and configuration of applications running in containers","fork":true,"url":"https://api.github.com/repos/justenwalker/containerbuddy","forks_url":"https://api.github.com/repos/justenwalker/containerbuddy/forks","keys_url":"https://api.github.com/repos/justenwalker/containerbuddy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/justenwalker/containerbuddy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/justenwalker/containerbuddy/teams","hooks_url":"https://api.github.com/repos/justenwalker/containerbuddy/hooks","issue_events_url":"https://api.github.com/repos/justenwalker/containerbuddy/issues/events{/number}","events_url":"https://api.github.com/repos/justenwalker/containerbuddy/events","assignees_url":"https://api.github.com/repos/justenwalker/containerbuddy/assignees{/user}","branches_url":"https://api.github.com/repos/justenwalker/containerbuddy/branches{/branch}","tags_url":"https://api.github.com/repos/justenwalker/containerbuddy/tags","blobs_url":"https://api.github.com/repos/justenwalker/containerbuddy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/justenwalker/containerbuddy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/justenwalker/containerbuddy/git/refs{/sha}","trees_url":"https://api.github.com/repos/justenwalker/containerbuddy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/justenwalker/containerbuddy/statuses/{sha}","languages_url":"https://api.github.com/repos/justenwalker/containerbuddy/languages","stargazers_url":"https://api.github.com/repos/justenwalker/containerbuddy/stargazers","contributors_url":"https://api.github.com/repos/justenwalker/containerbuddy/contributors","subscribers_url":"https://api.github.com/repos/justenwalker/containerbuddy/subscribers","subscription_url":"https://api.github.com/repos/justenwalker/containerbuddy/subscription","commits_url":"https://api.github.com/repos/justenwalker/containerbuddy/commits{/sha}","git_commits_url":"https://api.github.com/repos/justenwalker/containerbuddy/git/commits{/sha}","comments_url":"https://api.github.com/repos/justenwalker/containerbuddy/comments{/number}","issue_comment_url":"https://api.github.com/repos/justenwalker/containerbuddy/issues/comments{/number}","contents_url":"https://api.github.com/repos/justenwalker/containerbuddy/contents/{+path}","compare_url":"https://api.github.com/repos/justenwalker/containerbuddy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/justenwalker/containerbuddy/merges","archive_url":"https://api.github.com/repos/justenwalker/containerbuddy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/justenwalker/containerbuddy/downloads","issues_url":"https://api.github.com/repos/justenwalker/containerbuddy/issues{/number}","pulls_url":"https://api.github.com/repos/justenwalker/containerbuddy/pulls{/number}","milestones_url":"https://api.github.com/repos/justenwalker/containerbuddy/milestones{/number}","notifications_url":"https://api.github.com/repos/justenwalker/containerbuddy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/justenwalker/containerbuddy/labels{/name}","releases_url":"https://api.github.com/repos/justenwalker/containerbuddy/releases{/id}","created_at":"2015-12-01T21:18:18Z","updated_at":"2015-12-01T21:18:19Z","pushed_at":"2015-12-30T00:20:59Z","git_url":"git://github.com/justenwalker/containerbuddy.git","ssh_url":"git@github.com:justenwalker/containerbuddy.git","clone_url":"https://github.com/justenwalker/containerbuddy.git","svn_url":"https://github.com/justenwalker/containerbuddy","homepage":null,"size":139,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_downloads":true,"has_wiki":true,"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":"joyent:master","ref":"master","sha":"7249deb6e69fd66469a3f4ed366186d28937769b","user":{"login":"joyent","id":10161,"avatar_url":"https://avatars.githubusercontent.com/u/10161?v=3","gravatar_id":"","url":"https://api.github.com/users/joyent","html_url":"https://github.com/joyent","followers_url":"https://api.github.com/users/joyent/followers","following_url":"https://api.github.com/users/joyent/following{/other_user}","gists_url":"https://api.github.com/users/joyent/gists{/gist_id}","starred_url":"https://api.github.com/users/joyent/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/joyent/subscriptions","organizations_url":"https://api.github.com/users/joyent/orgs","repos_url":"https://api.github.com/users/joyent/repos","events_url":"https://api.github.com/users/joyent/events{/privacy}","received_events_url":"https://api.github.com/users/joyent/received_events","type":"Organization","site_admin":false},"repo":{"id":44767194,"name":"containerbuddy","full_name":"joyent/containerbuddy","owner":{"login":"joyent","id":10161,"avatar_url":"https://avatars.githubusercontent.com/u/10161?v=3","gravatar_id":"","url":"https://api.github.com/users/joyent","html_url":"https://github.com/joyent","followers_url":"https://api.github.com/users/joyent/followers","following_url":"https://api.github.com/users/joyent/following{/other_user}","gists_url":"https://api.github.com/users/joyent/gists{/gist_id}","starred_url":"https://api.github.com/users/joyent/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/joyent/subscriptions","organizations_url":"https://api.github.com/users/joyent/orgs","repos_url":"https://api.github.com/users/joyent/repos","events_url":"https://api.github.com/users/joyent/events{/privacy}","received_events_url":"https://api.github.com/users/joyent/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/joyent/containerbuddy","description":"A service for autodiscovery and configuration of applications running in containers","fork":false,"url":"https://api.github.com/repos/joyent/containerbuddy","forks_url":"https://api.github.com/repos/joyent/containerbuddy/forks","keys_url":"https://api.github.com/repos/joyent/containerbuddy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/joyent/containerbuddy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/joyent/containerbuddy/teams","hooks_url":"https://api.github.com/repos/joyent/containerbuddy/hooks","issue_events_url":"https://api.github.com/repos/joyent/containerbuddy/issues/events{/number}","events_url":"https://api.github.com/repos/joyent/containerbuddy/events","assignees_url":"https://api.github.com/repos/joyent/containerbuddy/assignees{/user}","branches_url":"https://api.github.com/repos/joyent/containerbuddy/branches{/branch}","tags_url":"https://api.github.com/repos/joyent/containerbuddy/tags","blobs_url":"https://api.github.com/repos/joyent/containerbuddy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/joyent/containerbuddy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/joyent/containerbuddy/git/refs{/sha}","trees_url":"https://api.github.com/repos/joyent/containerbuddy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/joyent/containerbuddy/statuses/{sha}","languages_url":"https://api.github.com/repos/joyent/containerbuddy/languages","stargazers_url":"https://api.github.com/repos/joyent/containerbuddy/stargazers","contributors_url":"https://api.github.com/repos/joyent/containerbuddy/contributors","subscribers_url":"https://api.github.com/repos/joyent/containerbuddy/subscribers","subscription_url":"https://api.github.com/repos/joyent/containerbuddy/subscription","commits_url":"https://api.github.com/repos/joyent/containerbuddy/commits{/sha}","git_commits_url":"https://api.github.com/repos/joyent/containerbuddy/git/commits{/sha}","comments_url":"https://api.github.com/repos/joyent/containerbuddy/comments{/number}","issue_comment_url":"https://api.github.com/repos/joyent/containerbuddy/issues/comments{/number}","contents_url":"https://api.github.com/repos/joyent/containerbuddy/contents/{+path}","compare_url":"https://api.github.com/repos/joyent/containerbuddy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/joyent/containerbuddy/merges","archive_url":"https://api.github.com/repos/joyent/containerbuddy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/joyent/containerbuddy/downloads","issues_url":"https://api.github.com/repos/joyent/containerbuddy/issues{/number}","pulls_url":"https://api.github.com/repos/joyent/containerbuddy/pulls{/number}","milestones_url":"https://api.github.com/repos/joyent/containerbuddy/milestones{/number}","notifications_url":"https://api.github.com/repos/joyent/containerbuddy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/joyent/containerbuddy/labels{/name}","releases_url":"https://api.github.com/repos/joyent/containerbuddy/releases{/id}","created_at":"2015-10-22T19:06:39Z","updated_at":"2015-12-28T19:46:07Z","pushed_at":"2015-12-30T00:20:59Z","git_url":"git://github.com/joyent/containerbuddy.git","ssh_url":"git@github.com:joyent/containerbuddy.git","clone_url":"https://github.com/joyent/containerbuddy.git","svn_url":"https://github.com/joyent/containerbuddy","homepage":null,"size":133,"stargazers_count":192,"watchers_count":192,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":13,"mirror_url":null,"open_issues_count":8,"forks":13,"open_issues":8,"watchers":192,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/joyent/containerbuddy/pulls/58"},"html":{"href":"https://github.com/joyent/containerbuddy/pull/58"},"issue":{"href":"https://api.github.com/repos/joyent/containerbuddy/issues/58"},"comments":{"href":"https://api.github.com/repos/joyent/containerbuddy/issues/58/comments"},"review_comments":{"href":"https://api.github.com/repos/joyent/containerbuddy/pulls/58/comments"},"review_comment":{"href":"https://api.github.com/repos/joyent/containerbuddy/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/joyent/containerbuddy/pulls/58/commits"},"statuses":{"href":"https://api.github.com/repos/joyent/containerbuddy/statuses/0e613d44943be15f327495e6ac0d0ab048f5f226"}}}}