{"action":"created","comment":{"url":"https://api.github.com/repos/moby/moby/pulls/comments/231723752","pull_request_review_id":172756142,"id":231723752,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIzMTcyMzc1Mg==","diff_hunk":"@@ -0,0 +1,290 @@\n+package contextstore\n+\n+import (\n+\t\"archive/tar\"\n+\t\"encoding/json\"\n+\t\"io\"\n+\t\"io/ioutil\"\n+\t\"os\"\n+\t\"path/filepath\"\n+\t\"strings\"\n+)\n+\n+const configFileName = \"config.json\"\n+\n+// Store provides a context store for easily remembering endpoints configuration\n+type Store interface {\n+\tGetCurrentContext() string\n+\tSetCurrentContext(name string) error\n+\tListContexts() (map[string]ContextMetadata, error)\n+\tCreateOrUpdateContext(name string, meta ContextMetadata) error\n+\tGetContextMetadata(name string) (ContextMetadata, error)\n+\tResetContextTLSMaterial(name string, data *ContextTLSData) error\n+\tResetContextEndpointTLSMaterial(contextName string, endpointName string, data *EndpointTLSData) error\n+\tListContextTLSFiles(name string) (map[string]EndpointFiles, error)\n+\tGetContextTLSData(contextName, endpointName, fileName string) ([]byte, error)\n+\tExport(name string) io.ReadCloser\n+\tImport(name string, reader io.Reader) error\n+\tRemove(name string) error\n+}\n+type store struct {\n+\tconfigFile     string\n+\tcurrentContext string\n+\tmeta           *metadataStore\n+\ttls            *tlsStore\n+}\n+\n+// NewStore creates a store from a given directory.\n+// If the directory does not exist or is empty, initialize it\n+func NewStore(dir string) (Store, error) {\n+\tmetaRoot := filepath.Join(dir, metadataDir)\n+\ttlsRoot := filepath.Join(dir, tlsDir)\n+\tconfigFile := filepath.Join(dir, configFileName)\n+\terr := os.MkdirAll(metaRoot, 0755)\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\terr = os.MkdirAll(tlsRoot, 0700)\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\t_, err = os.Stat(configFile)\n+\n+\tswitch {\n+\tcase os.IsNotExist(err):\n+\t\t//create default file\n+\t\terr = ioutil.WriteFile(configFile, []byte(\"{}\"), 0644)\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n+\tcase err != nil:\n+\t\treturn nil, err\n+\t}\n+\n+\tconfigBytes, err := ioutil.ReadFile(configFile)\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\tvar cfg config\n+\terr = json.Unmarshal(configBytes, &cfg)\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\treturn &store{\n+\t\tconfigFile:     configFile,\n+\t\tcurrentContext: cfg.CurrentContext,\n+\t\tmeta: &metadataStore{\n+\t\t\troot: metaRoot,\n+\t\t},\n+\t\ttls: &tlsStore{\n+\t\t\troot: tlsRoot,\n+\t\t},\n+\t}, nil\n+}\n+\n+func (s *store) GetCurrentContext() string {\n+\treturn s.currentContext\n+}\n+\n+func (s *store) SetCurrentContext(name string) error {\n+\tcfg := config{CurrentContext: name}\n+\tconfigBytes, err := json.Marshal(&cfg)\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\terr = ioutil.WriteFile(s.configFile, configBytes, 0644)\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\ts.currentContext = name\n+\treturn nil\n+}\n+\n+func (s *store) ListContexts() (map[string]ContextMetadata, error) {\n+\treturn s.meta.list()\n+}\n+\n+func (s *store) CreateOrUpdateContext(name string, meta ContextMetadata) error {\n+\treturn s.meta.createOrUpdate(name, meta)\n+}\n+\n+func (s *store) GetContextMetadata(name string) (ContextMetadata, error) {\n+\treturn s.meta.get(name)\n+}\n+\n+func (s *store) ResetContextTLSMaterial(name string, data *ContextTLSData) error {\n+\terr := s.tls.removeAllContextData(name)\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\tif data != nil {\n+\t\tfor ep, files := range data.Endpoints {\n+\t\t\tfor fileName, data := range files.Files {\n+\t\t\t\terr = s.tls.createOrUpdate(name, ep, fileName, data)\n+\t\t\t\tif err != nil {\n+\t\t\t\t\treturn err\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn nil\n+}\n+\n+func (s *store) ResetContextEndpointTLSMaterial(contextName string, endpointName string, data *EndpointTLSData) error {\n+\terr := s.tls.removeAllEndpointData(contextName, endpointName)","path":"pkg/contextstore/store.go","position":134,"original_position":134,"commit_id":"4b476b65935544425ede76788ed3cdbd4ef12b46","original_commit_id":"4b476b65935544425ede76788ed3cdbd4ef12b46","user":{"login":"silvin-lubecki","id":31478878,"node_id":"MDQ6VXNlcjMxNDc4ODc4","avatar_url":"https://avatars0.githubusercontent.com/u/31478878?v=4","gravatar_id":"","url":"https://api.github.com/users/silvin-lubecki","html_url":"https://github.com/silvin-lubecki","followers_url":"https://api.github.com/users/silvin-lubecki/followers","following_url":"https://api.github.com/users/silvin-lubecki/following{/other_user}","gists_url":"https://api.github.com/users/silvin-lubecki/gists{/gist_id}","starred_url":"https://api.github.com/users/silvin-lubecki/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/silvin-lubecki/subscriptions","organizations_url":"https://api.github.com/users/silvin-lubecki/orgs","repos_url":"https://api.github.com/users/silvin-lubecki/repos","events_url":"https://api.github.com/users/silvin-lubecki/events{/privacy}","received_events_url":"https://api.github.com/users/silvin-lubecki/received_events","type":"User","site_admin":false},"body":"nit: if err :=...","created_at":"2018-11-07T23:59:23Z","updated_at":"2018-11-07T23:59:23Z","html_url":"https://github.com/moby/moby/pull/38148#discussion_r231723752","pull_request_url":"https://api.github.com/repos/moby/moby/pulls/38148","author_association":"NONE","_links":{"self":{"href":"https://api.github.com/repos/moby/moby/pulls/comments/231723752"},"html":{"href":"https://github.com/moby/moby/pull/38148#discussion_r231723752"},"pull_request":{"href":"https://api.github.com/repos/moby/moby/pulls/38148"}}},"pull_request":{"url":"https://api.github.com/repos/moby/moby/pulls/38148","id":228777723,"node_id":"MDExOlB1bGxSZXF1ZXN0MjI4Nzc3NzIz","html_url":"https://github.com/moby/moby/pull/38148","diff_url":"https://github.com/moby/moby/pull/38148.diff","patch_url":"https://github.com/moby/moby/pull/38148.patch","issue_url":"https://api.github.com/repos/moby/moby/issues/38148","number":38148,"state":"open","locked":false,"title":"[Context-Switch] Foundation of fast context switch","user":{"login":"simonferquel","id":1677333,"node_id":"MDQ6VXNlcjE2NzczMzM=","avatar_url":"https://avatars3.githubusercontent.com/u/1677333?v=4","gravatar_id":"","url":"https://api.github.com/users/simonferquel","html_url":"https://github.com/simonferquel","followers_url":"https://api.github.com/users/simonferquel/followers","following_url":"https://api.github.com/users/simonferquel/following{/other_user}","gists_url":"https://api.github.com/users/simonferquel/gists{/gist_id}","starred_url":"https://api.github.com/users/simonferquel/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/simonferquel/subscriptions","organizations_url":"https://api.github.com/users/simonferquel/orgs","repos_url":"https://api.github.com/users/simonferquel/repos","events_url":"https://api.github.com/users/simonferquel/events{/privacy}","received_events_url":"https://api.github.com/users/simonferquel/received_events","type":"User","site_admin":false},"body":"**- What I did**\r\nThis is the foundation of a feature we at Docker want to bring to the docker client ecosystem: A way to store credentials to connect to different clusters and quickly switch from one to the other. The UX related to this PR will mostly appear in PRs against docker CLI, but everything required to interact with the docker context-store will live in the moby/moby repo (mostly to avoid dependent projects to have to vendor Docker cli)\r\n\r\n**- How I did it**\r\nFirst, I introduced a pkg/contextstore package that does the actual context storage. A context is a named entity that contains arbitrary metadata, and references named endpoints (on the moby/moby side only one endpoint is actually created/consumed, but on the CLI side, we'll complement those contexts with endpoints targeting Kubernetes for our Compose on Kubernetes support). An endpoint also contains arbitrary metadata plus references to TLS materials. The contextstore actually stores metadata and TLS material in different directories to make it easy to put different access rights on them, or put sensitive data on an encrypted filesystem.\r\n\r\nThen the client/context package provides facilities to create and parse docker contexts and endpoints in order to configure a client\r\n\r\nClient package has been updated with a new WithContextStoreOrEnv that will interact with the context store to configure the client, or fallback to the \"legacy\" environment variables if there is no context data.\r\n\r\n**- How to verify it**\r\n\r\nThe PR comes with unit tests. User testing will be available soon with the docker/cli PRs coming\r\n\r\n**- Description for the changelog**\r\n\r\nIntroduce a context store to make it easy to store and use different sets of credentials to connect to different engines\r\n\r\n","created_at":"2018-11-06T17:41:47Z","updated_at":"2018-11-07T23:59:23Z","closed_at":null,"merged_at":null,"merge_commit_sha":"873710f2facbb6690d1bfe19e3e84b2460284169","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":175374554,"node_id":"MDU6TGFiZWwxNzUzNzQ1NTQ=","url":"https://api.github.com/repos/moby/moby/labels/status/0-triage","name":"status/0-triage","color":"EEEEEE","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/moby/moby/pulls/38148/commits","review_comments_url":"https://api.github.com/repos/moby/moby/pulls/38148/comments","review_comment_url":"https://api.github.com/repos/moby/moby/pulls/comments{/number}","comments_url":"https://api.github.com/repos/moby/moby/issues/38148/comments","statuses_url":"https://api.github.com/repos/moby/moby/statuses/4b476b65935544425ede76788ed3cdbd4ef12b46","head":{"label":"simonferquel:use-context-store","ref":"use-context-store","sha":"4b476b65935544425ede76788ed3cdbd4ef12b46","user":{"login":"simonferquel","id":1677333,"node_id":"MDQ6VXNlcjE2NzczMzM=","avatar_url":"https://avatars3.githubusercontent.com/u/1677333?v=4","gravatar_id":"","url":"https://api.github.com/users/simonferquel","html_url":"https://github.com/simonferquel","followers_url":"https://api.github.com/users/simonferquel/followers","following_url":"https://api.github.com/users/simonferquel/following{/other_user}","gists_url":"https://api.github.com/users/simonferquel/gists{/gist_id}","starred_url":"https://api.github.com/users/simonferquel/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/simonferquel/subscriptions","organizations_url":"https://api.github.com/users/simonferquel/orgs","repos_url":"https://api.github.com/users/simonferquel/repos","events_url":"https://api.github.com/users/simonferquel/events{/privacy}","received_events_url":"https://api.github.com/users/simonferquel/received_events","type":"User","site_admin":false},"repo":{"id":83673512,"node_id":"MDEwOlJlcG9zaXRvcnk4MzY3MzUxMg==","name":"docker","full_name":"simonferquel/docker","private":false,"owner":{"login":"simonferquel","id":1677333,"node_id":"MDQ6VXNlcjE2NzczMzM=","avatar_url":"https://avatars3.githubusercontent.com/u/1677333?v=4","gravatar_id":"","url":"https://api.github.com/users/simonferquel","html_url":"https://github.com/simonferquel","followers_url":"https://api.github.com/users/simonferquel/followers","following_url":"https://api.github.com/users/simonferquel/following{/other_user}","gists_url":"https://api.github.com/users/simonferquel/gists{/gist_id}","starred_url":"https://api.github.com/users/simonferquel/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/simonferquel/subscriptions","organizations_url":"https://api.github.com/users/simonferquel/orgs","repos_url":"https://api.github.com/users/simonferquel/repos","events_url":"https://api.github.com/users/simonferquel/events{/privacy}","received_events_url":"https://api.github.com/users/simonferquel/received_events","type":"User","site_admin":false},"html_url":"https://github.com/simonferquel/docker","description":"Docker - the open-source application container engine","fork":true,"url":"https://api.github.com/repos/simonferquel/docker","forks_url":"https://api.github.com/repos/simonferquel/docker/forks","keys_url":"https://api.github.com/repos/simonferquel/docker/keys{/key_id}","collaborators_url":"https://api.github.com/repos/simonferquel/docker/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/simonferquel/docker/teams","hooks_url":"https://api.github.com/repos/simonferquel/docker/hooks","issue_events_url":"https://api.github.com/repos/simonferquel/docker/issues/events{/number}","events_url":"https://api.github.com/repos/simonferquel/docker/events","assignees_url":"https://api.github.com/repos/simonferquel/docker/assignees{/user}","branches_url":"https://api.github.com/repos/simonferquel/docker/branches{/branch}","tags_url":"https://api.github.com/repos/simonferquel/docker/tags","blobs_url":"https://api.github.com/repos/simonferquel/docker/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/simonferquel/docker/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/simonferquel/docker/git/refs{/sha}","trees_url":"https://api.github.com/repos/simonferquel/docker/git/trees{/sha}","statuses_url":"https://api.github.com/repos/simonferquel/docker/statuses/{sha}","languages_url":"https://api.github.com/repos/simonferquel/docker/languages","stargazers_url":"https://api.github.com/repos/simonferquel/docker/stargazers","contributors_url":"https://api.github.com/repos/simonferquel/docker/contributors","subscribers_url":"https://api.github.com/repos/simonferquel/docker/subscribers","subscription_url":"https://api.github.com/repos/simonferquel/docker/subscription","commits_url":"https://api.github.com/repos/simonferquel/docker/commits{/sha}","git_commits_url":"https://api.github.com/repos/simonferquel/docker/git/commits{/sha}","comments_url":"https://api.github.com/repos/simonferquel/docker/comments{/number}","issue_comment_url":"https://api.github.com/repos/simonferquel/docker/issues/comments{/number}","contents_url":"https://api.github.com/repos/simonferquel/docker/contents/{+path}","compare_url":"https://api.github.com/repos/simonferquel/docker/compare/{base}...{head}","merges_url":"https://api.github.com/repos/simonferquel/docker/merges","archive_url":"https://api.github.com/repos/simonferquel/docker/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/simonferquel/docker/downloads","issues_url":"https://api.github.com/repos/simonferquel/docker/issues{/number}","pulls_url":"https://api.github.com/repos/simonferquel/docker/pulls{/number}","milestones_url":"https://api.github.com/repos/simonferquel/docker/milestones{/number}","notifications_url":"https://api.github.com/repos/simonferquel/docker/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/simonferquel/docker/labels{/name}","releases_url":"https://api.github.com/repos/simonferquel/docker/releases{/id}","deployments_url":"https://api.github.com/repos/simonferquel/docker/deployments","created_at":"2017-03-02T12:07:41Z","updated_at":"2017-03-02T12:07:59Z","pushed_at":"2018-11-07T17:56:18Z","git_url":"git://github.com/simonferquel/docker.git","ssh_url":"git@github.com:simonferquel/docker.git","clone_url":"https://github.com/simonferquel/docker.git","svn_url":"https://github.com/simonferquel/docker","homepage":"http://www.docker.com","size":139439,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"moby:master","ref":"master","sha":"bd224b5fe564f343e22fa76ae8569ec949f35802","user":{"login":"moby","id":27259197,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI3MjU5MTk3","avatar_url":"https://avatars1.githubusercontent.com/u/27259197?v=4","gravatar_id":"","url":"https://api.github.com/users/moby","html_url":"https://github.com/moby","followers_url":"https://api.github.com/users/moby/followers","following_url":"https://api.github.com/users/moby/following{/other_user}","gists_url":"https://api.github.com/users/moby/gists{/gist_id}","starred_url":"https://api.github.com/users/moby/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/moby/subscriptions","organizations_url":"https://api.github.com/users/moby/orgs","repos_url":"https://api.github.com/users/moby/repos","events_url":"https://api.github.com/users/moby/events{/privacy}","received_events_url":"https://api.github.com/users/moby/received_events","type":"Organization","site_admin":false},"repo":{"id":7691631,"node_id":"MDEwOlJlcG9zaXRvcnk3NjkxNjMx","name":"moby","full_name":"moby/moby","private":false,"owner":{"login":"moby","id":27259197,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI3MjU5MTk3","avatar_url":"https://avatars1.githubusercontent.com/u/27259197?v=4","gravatar_id":"","url":"https://api.github.com/users/moby","html_url":"https://github.com/moby","followers_url":"https://api.github.com/users/moby/followers","following_url":"https://api.github.com/users/moby/following{/other_user}","gists_url":"https://api.github.com/users/moby/gists{/gist_id}","starred_url":"https://api.github.com/users/moby/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/moby/subscriptions","organizations_url":"https://api.github.com/users/moby/orgs","repos_url":"https://api.github.com/users/moby/repos","events_url":"https://api.github.com/users/moby/events{/privacy}","received_events_url":"https://api.github.com/users/moby/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/moby/moby","description":"Moby Project - a collaborative project for the container ecosystem to assemble container-based systems","fork":false,"url":"https://api.github.com/repos/moby/moby","forks_url":"https://api.github.com/repos/moby/moby/forks","keys_url":"https://api.github.com/repos/moby/moby/keys{/key_id}","collaborators_url":"https://api.github.com/repos/moby/moby/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/moby/moby/teams","hooks_url":"https://api.github.com/repos/moby/moby/hooks","issue_events_url":"https://api.github.com/repos/moby/moby/issues/events{/number}","events_url":"https://api.github.com/repos/moby/moby/events","assignees_url":"https://api.github.com/repos/moby/moby/assignees{/user}","branches_url":"https://api.github.com/repos/moby/moby/branches{/branch}","tags_url":"https://api.github.com/repos/moby/moby/tags","blobs_url":"https://api.github.com/repos/moby/moby/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/moby/moby/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/moby/moby/git/refs{/sha}","trees_url":"https://api.github.com/repos/moby/moby/git/trees{/sha}","statuses_url":"https://api.github.com/repos/moby/moby/statuses/{sha}","languages_url":"https://api.github.com/repos/moby/moby/languages","stargazers_url":"https://api.github.com/repos/moby/moby/stargazers","contributors_url":"https://api.github.com/repos/moby/moby/contributors","subscribers_url":"https://api.github.com/repos/moby/moby/subscribers","subscription_url":"https://api.github.com/repos/moby/moby/subscription","commits_url":"https://api.github.com/repos/moby/moby/commits{/sha}","git_commits_url":"https://api.github.com/repos/moby/moby/git/commits{/sha}","comments_url":"https://api.github.com/repos/moby/moby/comments{/number}","issue_comment_url":"https://api.github.com/repos/moby/moby/issues/comments{/number}","contents_url":"https://api.github.com/repos/moby/moby/contents/{+path}","compare_url":"https://api.github.com/repos/moby/moby/compare/{base}...{head}","merges_url":"https://api.github.com/repos/moby/moby/merges","archive_url":"https://api.github.com/repos/moby/moby/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/moby/moby/downloads","issues_url":"https://api.github.com/repos/moby/moby/issues{/number}","pulls_url":"https://api.github.com/repos/moby/moby/pulls{/number}","milestones_url":"https://api.github.com/repos/moby/moby/milestones{/number}","notifications_url":"https://api.github.com/repos/moby/moby/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/moby/moby/labels{/name}","releases_url":"https://api.github.com/repos/moby/moby/releases{/id}","deployments_url":"https://api.github.com/repos/moby/moby/deployments","created_at":"2013-01-18T18:10:57Z","updated_at":"2018-11-07T16:28:12Z","pushed_at":"2018-11-07T17:56:25Z","git_url":"git://github.com/moby/moby.git","ssh_url":"git@github.com:moby/moby.git","clone_url":"https://github.com/moby/moby.git","svn_url":"https://github.com/moby/moby","homepage":"https://mobyproject.org/","size":139756,"stargazers_count":50983,"watchers_count":50983,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":14845,"mirror_url":null,"archived":false,"open_issues_count":3444,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":14845,"open_issues":3444,"watchers":50983,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/moby/moby/pulls/38148"},"html":{"href":"https://github.com/moby/moby/pull/38148"},"issue":{"href":"https://api.github.com/repos/moby/moby/issues/38148"},"comments":{"href":"https://api.github.com/repos/moby/moby/issues/38148/comments"},"review_comments":{"href":"https://api.github.com/repos/moby/moby/pulls/38148/comments"},"review_comment":{"href":"https://api.github.com/repos/moby/moby/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/moby/moby/pulls/38148/commits"},"statuses":{"href":"https://api.github.com/repos/moby/moby/statuses/4b476b65935544425ede76788ed3cdbd4ef12b46"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"silvin-lubecki"}}