{"action":"created","comment":{"url":"https://api.github.com/repos/purpleidea/mgmt/pulls/comments/236037843","pull_request_review_id":178045941,"id":236037843,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIzNjAzNzg0Mw==","diff_hunk":"@@ -0,0 +1,441 @@\n+// Mgmt\n+// Copyright (C) 2013-2018+ James Shubin and the project contributors\n+// Written by James Shubin <james@shubin.ca> and the project contributors\n+//\n+// This program is free software: you can redistribute it and/or modify\n+// it under the terms of the GNU General Public License as published by\n+// 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 General Public License for more details.\n+//\n+// You should have received a copy of the GNU General Public License\n+// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n+\n+package resources\n+\n+import (\n+\t\"context\"\n+\t\"fmt\"\n+\t\"io/ioutil\"\n+\t\"regexp\"\n+\t\"strings\"\n+\t\"time\"\n+\n+\t\"github.com/purpleidea/mgmt/engine\"\n+\t\"github.com/purpleidea/mgmt/engine/traits\"\n+\t\"github.com/purpleidea/mgmt/util\"\n+\n+\t\"github.com/docker/docker/api/types\"\n+\t\"github.com/docker/docker/api/types/container\"\n+\t\"github.com/docker/docker/api/types/filters\"\n+\t\"github.com/docker/docker/client\"\n+\t\"github.com/docker/go-connections/nat\"\n+)\n+\n+const (\n+\t// ContainerRunning is the running container state.\n+\tContainerRunning = \"running\"\n+\t// ContainerStopped is the stopped container state.\n+\tContainerStopped = \"stopped\"\n+\t// ContainerRemoved is the removed container state.\n+\tContainerRemoved = \"removed\"\n+\n+\t// validateCtxTimeout is the length of time, in seconds, before requests\n+\t// are cancelled in Validate.\n+\tvalidateCtxTimeout = 20\n+\t// checkApplyCtxTimeout is the length of time, in seconds, before requests\n+\t// are cancelled in CheckApply.\n+\tcheckApplyCtxTimeout = 120\n+)\n+\n+func init() {\n+\tengine.RegisterResource(\"docker:container\", func() engine.Res { return &DockerContainerRes{} })\n+}\n+\n+// DockerContainerRes is a docker container resource.\n+type DockerContainerRes struct {\n+\ttraits.Base // add the base methods without re-implementation\n+\ttraits.Edgeable\n+\n+\tState      string                     `yaml:\"state\"`      // running, stopped, or removed\n+\tImage      string                     `yaml:\"image\"`      // docker image or image:tag\n+\tCmd        []string                   `yaml:\"cmd\"`        // command(s) to run on the container\n+\tEnv        []string                   `yaml:\"env\"`        // environment variables, e.g. [\"VAR\"=\"val\",],\n+\tPorts      map[string]map[int64]int64 `yaml:\"ports\"`      // port bindings, e.g. {\"tcp\" => {80 => 8080},},\n+\tAPIVersion string                     `yaml:\"apiversion\"` // optional api version, e.g. \"v1.39\"\n+\n+\tForce bool `yaml:\"force\"` // force destroy and redeploy if image is incorrect\n+\n+\tclient *client.Client // docker api client\n+\n+\tinit *engine.Init\n+}\n+\n+// Default returns some sensible defaults for this resource.\n+func (obj *DockerContainerRes) Default() engine.Res {\n+\treturn &DockerContainerRes{}\n+}\n+\n+// Validate if the params passed in are valid data.\n+func (obj *DockerContainerRes) Validate() error {\n+\tctx, cancel := context.WithTimeout(context.Background(), validateCtxTimeout*time.Second)\n+\tdefer cancel()\n+\n+\t// validate state\n+\tif obj.State != ContainerRunning && obj.State != ContainerStopped && obj.State != ContainerRemoved {\n+\t\treturn fmt.Errorf(\"state must be running, stopped or removed\")\n+\t}\n+\n+\t// TODO: should this stuff happen in validate or do we make sure there's no\n+\t// whitespace, and let it fail in CheckApply?\n+\t//\n+\t// set up temporary docker client to validate image\n+\tc, err := client.NewClient(client.DefaultDockerHost, obj.APIVersion, nil, nil)\n+\tif err != nil {\n+\t\treturn fmt.Errorf(\"error creating docker client: %s\", err)\n+\t}\n+\tdefer c.Close()\n+\n+\t// validate image\n+\tresp, err := c.ImageSearch(ctx, obj.Image, types.ImageSearchOptions{Limit: 1})\n+\tif err != nil {\n+\t\treturn fmt.Errorf(\"error searching for image: %s\", err)\n+\t}\n+\tif len(resp) == 0 {\n+\t\treturn fmt.Errorf(\"image: %s not found\", obj.Image)\n+\t}\n+\n+\t// validate env\n+\tfor _, env := range obj.Env {\n+\t\tif !strings.Contains(env, \"=\") || strings.Contains(env, \" \") {\n+\t\t\treturn fmt.Errorf(\"invalid environment variable: %s\", env)\n+\t\t}\n+\t}\n+\n+\t// validate ports\n+\tfor k, v := range obj.Ports {\n+\t\tif k != \"tcp\" && k != \"udp\" && k != \"sctp\" {\n+\t\t\treturn fmt.Errorf(\"ports primary key should be tcp, udp or sctp\")\n+\t\t}\n+\t\tfor p, q := range v {\n+\t\t\tif (p < 1 || p > 0xFFFF) || (q < 1 || q > 0xFFFF) {\n+\t\t\t\treturn fmt.Errorf(\"ports should be between 1 and 65535\")","path":"engine/resources/docker_container.go","position":126,"original_position":126,"commit_id":"7ba6bef6f5a890dcaadcdaa5b11626835d49f8b4","original_commit_id":"7ba6bef6f5a890dcaadcdaa5b11626835d49f8b4","user":{"login":"purpleidea","id":135091,"node_id":"MDQ6VXNlcjEzNTA5MQ==","avatar_url":"https://avatars0.githubusercontent.com/u/135091?v=4","gravatar_id":"","url":"https://api.github.com/users/purpleidea","html_url":"https://github.com/purpleidea","followers_url":"https://api.github.com/users/purpleidea/followers","following_url":"https://api.github.com/users/purpleidea/following{/other_user}","gists_url":"https://api.github.com/users/purpleidea/gists{/gist_id}","starred_url":"https://api.github.com/users/purpleidea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/purpleidea/subscriptions","organizations_url":"https://api.github.com/users/purpleidea/orgs","repos_url":"https://api.github.com/users/purpleidea/repos","events_url":"https://api.github.com/users/purpleidea/events{/privacy}","received_events_url":"https://api.github.com/users/purpleidea/received_events","type":"User","site_admin":false},"body":"s/should be/must be/ right?","created_at":"2018-11-24T10:08:25Z","updated_at":"2018-11-24T10:15:59Z","html_url":"https://github.com/purpleidea/mgmt/pull/400#discussion_r236037843","pull_request_url":"https://api.github.com/repos/purpleidea/mgmt/pulls/400","author_association":"OWNER","_links":{"self":{"href":"https://api.github.com/repos/purpleidea/mgmt/pulls/comments/236037843"},"html":{"href":"https://github.com/purpleidea/mgmt/pull/400#discussion_r236037843"},"pull_request":{"href":"https://api.github.com/repos/purpleidea/mgmt/pulls/400"}}},"pull_request":{"url":"https://api.github.com/repos/purpleidea/mgmt/pulls/400","id":232104145,"node_id":"MDExOlB1bGxSZXF1ZXN0MjMyMTA0MTQ1","html_url":"https://github.com/purpleidea/mgmt/pull/400","diff_url":"https://github.com/purpleidea/mgmt/pull/400.diff","patch_url":"https://github.com/purpleidea/mgmt/pull/400.patch","issue_url":"https://api.github.com/repos/purpleidea/mgmt/issues/400","number":400,"state":"open","locked":false,"title":"engine: resource: Add a Docker container resource","user":{"login":"jonathangold","id":30417125,"node_id":"MDQ6VXNlcjMwNDE3MTI1","avatar_url":"https://avatars3.githubusercontent.com/u/30417125?v=4","gravatar_id":"","url":"https://api.github.com/users/jonathangold","html_url":"https://github.com/jonathangold","followers_url":"https://api.github.com/users/jonathangold/followers","following_url":"https://api.github.com/users/jonathangold/following{/other_user}","gists_url":"https://api.github.com/users/jonathangold/gists{/gist_id}","starred_url":"https://api.github.com/users/jonathangold/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jonathangold/subscriptions","organizations_url":"https://api.github.com/users/jonathangold/orgs","repos_url":"https://api.github.com/users/jonathangold/repos","events_url":"https://api.github.com/users/jonathangold/events{/privacy}","received_events_url":"https://api.github.com/users/jonathangold/received_events","type":"User","site_admin":false},"body":"I've put together the resource with what I feel is the minimum viable api. I still have to validate the input, but I wanted to put this up so we can discuss a few things in context.\r\n\r\nDo we want to allow users to define stopped/terminated resources (like the aws resource?) \r\nIs there a better data structure than `map[string]string` for portmapping?\r\n\r\nWe need to talk about vendoring, because this involves vendoring a ~140mb repo, just to delete one of their vendored folders.\r\n\r\nFeel free to read through the resource, but note, there aren't many comments at the moment.\r\nCheers.","created_at":"2018-11-19T20:36:30Z","updated_at":"2018-11-24T10:15:59Z","closed_at":null,"merged_at":null,"merge_commit_sha":"6b66b9d76d89b3858036d351ab72a98fb277d9d4","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/purpleidea/mgmt/pulls/400/commits","review_comments_url":"https://api.github.com/repos/purpleidea/mgmt/pulls/400/comments","review_comment_url":"https://api.github.com/repos/purpleidea/mgmt/pulls/comments{/number}","comments_url":"https://api.github.com/repos/purpleidea/mgmt/issues/400/comments","statuses_url":"https://api.github.com/repos/purpleidea/mgmt/statuses/7ba6bef6f5a890dcaadcdaa5b11626835d49f8b4","head":{"label":"jonathangold:docker","ref":"docker","sha":"7ba6bef6f5a890dcaadcdaa5b11626835d49f8b4","user":{"login":"jonathangold","id":30417125,"node_id":"MDQ6VXNlcjMwNDE3MTI1","avatar_url":"https://avatars3.githubusercontent.com/u/30417125?v=4","gravatar_id":"","url":"https://api.github.com/users/jonathangold","html_url":"https://github.com/jonathangold","followers_url":"https://api.github.com/users/jonathangold/followers","following_url":"https://api.github.com/users/jonathangold/following{/other_user}","gists_url":"https://api.github.com/users/jonathangold/gists{/gist_id}","starred_url":"https://api.github.com/users/jonathangold/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jonathangold/subscriptions","organizations_url":"https://api.github.com/users/jonathangold/orgs","repos_url":"https://api.github.com/users/jonathangold/repos","events_url":"https://api.github.com/users/jonathangold/events{/privacy}","received_events_url":"https://api.github.com/users/jonathangold/received_events","type":"User","site_admin":false},"repo":{"id":102772903,"node_id":"MDEwOlJlcG9zaXRvcnkxMDI3NzI5MDM=","name":"mgmt","full_name":"jonathangold/mgmt","private":false,"owner":{"login":"jonathangold","id":30417125,"node_id":"MDQ6VXNlcjMwNDE3MTI1","avatar_url":"https://avatars3.githubusercontent.com/u/30417125?v=4","gravatar_id":"","url":"https://api.github.com/users/jonathangold","html_url":"https://github.com/jonathangold","followers_url":"https://api.github.com/users/jonathangold/followers","following_url":"https://api.github.com/users/jonathangold/following{/other_user}","gists_url":"https://api.github.com/users/jonathangold/gists{/gist_id}","starred_url":"https://api.github.com/users/jonathangold/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jonathangold/subscriptions","organizations_url":"https://api.github.com/users/jonathangold/orgs","repos_url":"https://api.github.com/users/jonathangold/repos","events_url":"https://api.github.com/users/jonathangold/events{/privacy}","received_events_url":"https://api.github.com/users/jonathangold/received_events","type":"User","site_admin":false},"html_url":"https://github.com/jonathangold/mgmt","description":"Next generation distributed, event-driven, parallel config management!","fork":true,"url":"https://api.github.com/repos/jonathangold/mgmt","forks_url":"https://api.github.com/repos/jonathangold/mgmt/forks","keys_url":"https://api.github.com/repos/jonathangold/mgmt/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jonathangold/mgmt/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jonathangold/mgmt/teams","hooks_url":"https://api.github.com/repos/jonathangold/mgmt/hooks","issue_events_url":"https://api.github.com/repos/jonathangold/mgmt/issues/events{/number}","events_url":"https://api.github.com/repos/jonathangold/mgmt/events","assignees_url":"https://api.github.com/repos/jonathangold/mgmt/assignees{/user}","branches_url":"https://api.github.com/repos/jonathangold/mgmt/branches{/branch}","tags_url":"https://api.github.com/repos/jonathangold/mgmt/tags","blobs_url":"https://api.github.com/repos/jonathangold/mgmt/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jonathangold/mgmt/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jonathangold/mgmt/git/refs{/sha}","trees_url":"https://api.github.com/repos/jonathangold/mgmt/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jonathangold/mgmt/statuses/{sha}","languages_url":"https://api.github.com/repos/jonathangold/mgmt/languages","stargazers_url":"https://api.github.com/repos/jonathangold/mgmt/stargazers","contributors_url":"https://api.github.com/repos/jonathangold/mgmt/contributors","subscribers_url":"https://api.github.com/repos/jonathangold/mgmt/subscribers","subscription_url":"https://api.github.com/repos/jonathangold/mgmt/subscription","commits_url":"https://api.github.com/repos/jonathangold/mgmt/commits{/sha}","git_commits_url":"https://api.github.com/repos/jonathangold/mgmt/git/commits{/sha}","comments_url":"https://api.github.com/repos/jonathangold/mgmt/comments{/number}","issue_comment_url":"https://api.github.com/repos/jonathangold/mgmt/issues/comments{/number}","contents_url":"https://api.github.com/repos/jonathangold/mgmt/contents/{+path}","compare_url":"https://api.github.com/repos/jonathangold/mgmt/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jonathangold/mgmt/merges","archive_url":"https://api.github.com/repos/jonathangold/mgmt/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jonathangold/mgmt/downloads","issues_url":"https://api.github.com/repos/jonathangold/mgmt/issues{/number}","pulls_url":"https://api.github.com/repos/jonathangold/mgmt/pulls{/number}","milestones_url":"https://api.github.com/repos/jonathangold/mgmt/milestones{/number}","notifications_url":"https://api.github.com/repos/jonathangold/mgmt/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jonathangold/mgmt/labels{/name}","releases_url":"https://api.github.com/repos/jonathangold/mgmt/releases{/id}","deployments_url":"https://api.github.com/repos/jonathangold/mgmt/deployments","created_at":"2017-09-07T18:39:11Z","updated_at":"2018-11-23T05:10:54Z","pushed_at":"2018-11-23T16:58:59Z","git_url":"git://github.com/jonathangold/mgmt.git","ssh_url":"git@github.com:jonathangold/mgmt.git","clone_url":"https://github.com/jonathangold/mgmt.git","svn_url":"https://github.com/jonathangold/mgmt","homepage":"","size":2842,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"other","name":"Other","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"purpleidea:master","ref":"master","sha":"43b5b4f5a409b203e4331fed962857e4ba034cd9","user":{"login":"purpleidea","id":135091,"node_id":"MDQ6VXNlcjEzNTA5MQ==","avatar_url":"https://avatars0.githubusercontent.com/u/135091?v=4","gravatar_id":"","url":"https://api.github.com/users/purpleidea","html_url":"https://github.com/purpleidea","followers_url":"https://api.github.com/users/purpleidea/followers","following_url":"https://api.github.com/users/purpleidea/following{/other_user}","gists_url":"https://api.github.com/users/purpleidea/gists{/gist_id}","starred_url":"https://api.github.com/users/purpleidea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/purpleidea/subscriptions","organizations_url":"https://api.github.com/users/purpleidea/orgs","repos_url":"https://api.github.com/users/purpleidea/repos","events_url":"https://api.github.com/users/purpleidea/events{/privacy}","received_events_url":"https://api.github.com/users/purpleidea/received_events","type":"User","site_admin":false},"repo":{"id":43111018,"node_id":"MDEwOlJlcG9zaXRvcnk0MzExMTAxOA==","name":"mgmt","full_name":"purpleidea/mgmt","private":false,"owner":{"login":"purpleidea","id":135091,"node_id":"MDQ6VXNlcjEzNTA5MQ==","avatar_url":"https://avatars0.githubusercontent.com/u/135091?v=4","gravatar_id":"","url":"https://api.github.com/users/purpleidea","html_url":"https://github.com/purpleidea","followers_url":"https://api.github.com/users/purpleidea/followers","following_url":"https://api.github.com/users/purpleidea/following{/other_user}","gists_url":"https://api.github.com/users/purpleidea/gists{/gist_id}","starred_url":"https://api.github.com/users/purpleidea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/purpleidea/subscriptions","organizations_url":"https://api.github.com/users/purpleidea/orgs","repos_url":"https://api.github.com/users/purpleidea/repos","events_url":"https://api.github.com/users/purpleidea/events{/privacy}","received_events_url":"https://api.github.com/users/purpleidea/received_events","type":"User","site_admin":false},"html_url":"https://github.com/purpleidea/mgmt","description":"Next generation distributed, event-driven, parallel config management!","fork":false,"url":"https://api.github.com/repos/purpleidea/mgmt","forks_url":"https://api.github.com/repos/purpleidea/mgmt/forks","keys_url":"https://api.github.com/repos/purpleidea/mgmt/keys{/key_id}","collaborators_url":"https://api.github.com/repos/purpleidea/mgmt/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/purpleidea/mgmt/teams","hooks_url":"https://api.github.com/repos/purpleidea/mgmt/hooks","issue_events_url":"https://api.github.com/repos/purpleidea/mgmt/issues/events{/number}","events_url":"https://api.github.com/repos/purpleidea/mgmt/events","assignees_url":"https://api.github.com/repos/purpleidea/mgmt/assignees{/user}","branches_url":"https://api.github.com/repos/purpleidea/mgmt/branches{/branch}","tags_url":"https://api.github.com/repos/purpleidea/mgmt/tags","blobs_url":"https://api.github.com/repos/purpleidea/mgmt/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/purpleidea/mgmt/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/purpleidea/mgmt/git/refs{/sha}","trees_url":"https://api.github.com/repos/purpleidea/mgmt/git/trees{/sha}","statuses_url":"https://api.github.com/repos/purpleidea/mgmt/statuses/{sha}","languages_url":"https://api.github.com/repos/purpleidea/mgmt/languages","stargazers_url":"https://api.github.com/repos/purpleidea/mgmt/stargazers","contributors_url":"https://api.github.com/repos/purpleidea/mgmt/contributors","subscribers_url":"https://api.github.com/repos/purpleidea/mgmt/subscribers","subscription_url":"https://api.github.com/repos/purpleidea/mgmt/subscription","commits_url":"https://api.github.com/repos/purpleidea/mgmt/commits{/sha}","git_commits_url":"https://api.github.com/repos/purpleidea/mgmt/git/commits{/sha}","comments_url":"https://api.github.com/repos/purpleidea/mgmt/comments{/number}","issue_comment_url":"https://api.github.com/repos/purpleidea/mgmt/issues/comments{/number}","contents_url":"https://api.github.com/repos/purpleidea/mgmt/contents/{+path}","compare_url":"https://api.github.com/repos/purpleidea/mgmt/compare/{base}...{head}","merges_url":"https://api.github.com/repos/purpleidea/mgmt/merges","archive_url":"https://api.github.com/repos/purpleidea/mgmt/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/purpleidea/mgmt/downloads","issues_url":"https://api.github.com/repos/purpleidea/mgmt/issues{/number}","pulls_url":"https://api.github.com/repos/purpleidea/mgmt/pulls{/number}","milestones_url":"https://api.github.com/repos/purpleidea/mgmt/milestones{/number}","notifications_url":"https://api.github.com/repos/purpleidea/mgmt/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/purpleidea/mgmt/labels{/name}","releases_url":"https://api.github.com/repos/purpleidea/mgmt/releases{/id}","deployments_url":"https://api.github.com/repos/purpleidea/mgmt/deployments","created_at":"2015-09-25T05:15:41Z","updated_at":"2018-11-24T09:43:22Z","pushed_at":"2018-11-24T09:49:43Z","git_url":"git://github.com/purpleidea/mgmt.git","ssh_url":"git@github.com:purpleidea/mgmt.git","clone_url":"https://github.com/purpleidea/mgmt.git","svn_url":"https://github.com/purpleidea/mgmt","homepage":"https://purpleidea.com/tags/mgmtconfig/","size":2815,"stargazers_count":1505,"watchers_count":1505,"language":"Go","has_issues":true,"has_projects":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":175,"mirror_url":null,"archived":false,"open_issues_count":72,"license":{"key":"other","name":"Other","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":175,"open_issues":72,"watchers":1505,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/purpleidea/mgmt/pulls/400"},"html":{"href":"https://github.com/purpleidea/mgmt/pull/400"},"issue":{"href":"https://api.github.com/repos/purpleidea/mgmt/issues/400"},"comments":{"href":"https://api.github.com/repos/purpleidea/mgmt/issues/400/comments"},"review_comments":{"href":"https://api.github.com/repos/purpleidea/mgmt/pulls/400/comments"},"review_comment":{"href":"https://api.github.com/repos/purpleidea/mgmt/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/purpleidea/mgmt/pulls/400/commits"},"statuses":{"href":"https://api.github.com/repos/purpleidea/mgmt/statuses/7ba6bef6f5a890dcaadcdaa5b11626835d49f8b4"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"purpleidea"}}