{"action":"created","comment":{"url":"https://api.github.com/repos/brkt/brkt-cli/pulls/comments/72857643","id":72857643,"diff_hunk":"@@ -0,0 +1,294 @@\n+# Copyright 2015 Bracket Computing, Inc. All Rights Reserved.\n+#\n+# Licensed under the Apache License, Version 2.0 (the \"License\").\n+# You may not use this file except in compliance with the License.\n+# A copy of the License is located at\n+#\n+# https://github.com/brkt/brkt-cli/blob/master/LICENSE\n+#\n+# or in the \"license\" file accompanying this file. This file is\n+# distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR\n+# CONDITIONS OF ANY KIND, either express or implied. See the\n+# License for the specific language governing permissions and\n+# limitations under the License.\n+import argparse\n+import collections\n+import errno\n+import logging\n+import os\n+import os.path\n+import sys\n+import tempfile\n+import yaml\n+\n+from brkt_cli.subcommand import Subcommand\n+from brkt_cli.util import render_table_rows\n+from brkt_cli.validation import ValidationError\n+\n+log = logging.getLogger(__name__)\n+\n+CONFIG_DIR = os.path.expanduser('~/.brkt')\n+CONFIG_PATH = os.path.join(CONFIG_DIR, 'config')\n+\n+VERSION = 1\n+\n+\n+class InvalidOptionError(Exception):\n+    def __init__(self, option):\n+        self.option = option\n+\n+\n+class CliConfig(object):\n+    \"\"\"CliConfig provides a stricter interface to a ConfigParser. All\n+    calls to set/get/delete options first verify that an option has been\n+    registered.\n+    \"\"\"\n+\n+    def __init__(self):\n+        self._config = {\n+            'version': VERSION,\n+            'options': {}\n+        }\n+        self._registered_options = collections.defaultdict(dict)\n+\n+    def _check_option(self, option):\n+        if option not in self._registered_options:\n+            raise InvalidOptionError(option)\n+\n+    def register_option(self, option, desc):\n+        self._registered_options[option] = desc\n+\n+    def registered_options(self):\n+        return self._registered_options\n+\n+    def set_option(self, option, value):\n+        \"\"\"Set the value for the supplied option.\n+\n+        :param option a dot-delimited option string\n+        :param value the option value\n+        \"\"\"\n+        self._check_option(option)\n+        levels = option.split('.')\n+        attr = levels.pop()\n+        cur = self._config['options']\n+        for level in levels:\n+            if level not in cur:\n+                cur[level] = {}\n+            cur = cur[level]\n+        cur[attr] = value\n+\n+    def get_option(self, option, default=None):\n+        \"\"\"Fetch the value for the supplied option.\n+\n+        :param option a dot-delimited option string\n+        :param default the value to be returned if option is not present\n+\n+        :return the option value\n+        \"\"\"\n+        self._check_option(option)\n+        levels = option.split('.')\n+        attr = levels.pop()\n+        cur = self._config['options']\n+        for level in levels:\n+            if level not in cur:\n+                return default\n+            cur = cur[level]\n+        return cur.get(attr, default)\n+\n+    def unset_option(self, option):\n+        \"\"\"Unset the value for the supplied option.\n+        :param option A dot-delimited option string\n+        \"\"\"\n+        self._check_option(option)\n+        levels = option.split('.')\n+        attr = levels.pop()\n+        cur = self._config['options']\n+        for level in levels:\n+            if level not in cur:\n+                return\n+            cur = cur[level]\n+        if attr in cur:\n+            del cur[attr]\n+        # Clean up any empty sub-sections\n+        cur = self._config['options']","path":"brkt_cli/config/__init__.py","position":113,"original_position":113,"commit_id":"ed70bd9e32491744675c339a999153de572aa69c","original_commit_id":"ed70bd9e32491744675c339a999153de572aa69c","user":{"login":"bburtin","id":5264133,"avatar_url":"https://avatars.githubusercontent.com/u/5264133?v=3","gravatar_id":"","url":"https://api.github.com/users/bburtin","html_url":"https://github.com/bburtin","followers_url":"https://api.github.com/users/bburtin/followers","following_url":"https://api.github.com/users/bburtin/following{/other_user}","gists_url":"https://api.github.com/users/bburtin/gists{/gist_id}","starred_url":"https://api.github.com/users/bburtin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bburtin/subscriptions","organizations_url":"https://api.github.com/users/bburtin/orgs","repos_url":"https://api.github.com/users/bburtin/repos","events_url":"https://api.github.com/users/bburtin/events{/privacy}","received_events_url":"https://api.github.com/users/bburtin/received_events","type":"User","site_admin":false},"body":"Doesn't this need to be done in decreasing order of depth?  Seems like this algorithm won't work with if you have a/b/c, where c is empty and b only contains c.","created_at":"2016-07-29T20:44:57Z","updated_at":"2016-07-29T20:44:57Z","html_url":"https://github.com/brkt/brkt-cli/pull/365#discussion_r72857643","pull_request_url":"https://api.github.com/repos/brkt/brkt-cli/pulls/365","_links":{"self":{"href":"https://api.github.com/repos/brkt/brkt-cli/pulls/comments/72857643"},"html":{"href":"https://github.com/brkt/brkt-cli/pull/365#discussion_r72857643"},"pull_request":{"href":"https://api.github.com/repos/brkt/brkt-cli/pulls/365"}}},"pull_request":{"url":"https://api.github.com/repos/brkt/brkt-cli/pulls/365","id":78362395,"html_url":"https://github.com/brkt/brkt-cli/pull/365","diff_url":"https://github.com/brkt/brkt-cli/pull/365.diff","patch_url":"https://github.com/brkt/brkt-cli/pull/365.patch","issue_url":"https://api.github.com/repos/brkt/brkt-cli/issues/365","number":365,"state":"open","locked":false,"title":"Add support for specifying options in a config file","user":{"login":"mpage","id":577841,"avatar_url":"https://avatars.githubusercontent.com/u/577841?v=3","gravatar_id":"","url":"https://api.github.com/users/mpage","html_url":"https://github.com/mpage","followers_url":"https://api.github.com/users/mpage/followers","following_url":"https://api.github.com/users/mpage/following{/other_user}","gists_url":"https://api.github.com/users/mpage/gists{/gist_id}","starred_url":"https://api.github.com/users/mpage/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mpage/subscriptions","organizations_url":"https://api.github.com/users/mpage/orgs","repos_url":"https://api.github.com/users/mpage/repos","events_url":"https://api.github.com/users/mpage/events{/privacy}","received_events_url":"https://api.github.com/users/mpage/received_events","type":"User","site_admin":false},"body":"This commit adds infrastructure to allow subcommands to read default\r\nvalues from a config file stored in ~/.brkt/config. In order to use\r\nthe new infrastructure, subcommands must implement the setup_config()\r\nmethod. In it, they should register any options they want to pull\r\nfrom the config.\r\n\r\nA new sub-command, \"config\", has been added to the cli. This lets\r\nusers list, set, get, and unset options in the config. It is\r\nmodeled after \"kubectl config\".\r\n\r\nFinally, the first consumer of the config infrastructure is the\r\n\"encrypt-gce-image\" sub-command. The \"project\", \"network\", and\r\n\"zone\" options may be specified via the config.\r\n\r\nSample usage:\r\n\r\n    # Set the default project to use for encrypting images:\r\n    brkt config set encrypt-gce-images.project myproject\r\n\r\n    # Retrieve the default project for encrypting images:\r\n    brkt config get encrypt-gce-images.project\r\n\r\n    # Unset the default project for encrypting images:\r\n    brkt config unset encrypt-gce-images.project\r\n\r\n    # Display all options set in the config file:\r\n    brkt config list","created_at":"2016-07-21T18:15:22Z","updated_at":"2016-07-29T20:44:57Z","closed_at":null,"merged_at":null,"merge_commit_sha":"fa44c4e567b8d0096cbb58c0033a5fedab06cd2d","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/brkt/brkt-cli/pulls/365/commits","review_comments_url":"https://api.github.com/repos/brkt/brkt-cli/pulls/365/comments","review_comment_url":"https://api.github.com/repos/brkt/brkt-cli/pulls/comments{/number}","comments_url":"https://api.github.com/repos/brkt/brkt-cli/issues/365/comments","statuses_url":"https://api.github.com/repos/brkt/brkt-cli/statuses/ed70bd9e32491744675c339a999153de572aa69c","head":{"label":"brkt:config-file","ref":"config-file","sha":"ed70bd9e32491744675c339a999153de572aa69c","user":{"login":"brkt","id":2963083,"avatar_url":"https://avatars.githubusercontent.com/u/2963083?v=3","gravatar_id":"","url":"https://api.github.com/users/brkt","html_url":"https://github.com/brkt","followers_url":"https://api.github.com/users/brkt/followers","following_url":"https://api.github.com/users/brkt/following{/other_user}","gists_url":"https://api.github.com/users/brkt/gists{/gist_id}","starred_url":"https://api.github.com/users/brkt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brkt/subscriptions","organizations_url":"https://api.github.com/users/brkt/orgs","repos_url":"https://api.github.com/users/brkt/repos","events_url":"https://api.github.com/users/brkt/events{/privacy}","received_events_url":"https://api.github.com/users/brkt/received_events","type":"Organization","site_admin":false},"repo":{"id":42469221,"name":"brkt-cli","full_name":"brkt/brkt-cli","owner":{"login":"brkt","id":2963083,"avatar_url":"https://avatars.githubusercontent.com/u/2963083?v=3","gravatar_id":"","url":"https://api.github.com/users/brkt","html_url":"https://github.com/brkt","followers_url":"https://api.github.com/users/brkt/followers","following_url":"https://api.github.com/users/brkt/following{/other_user}","gists_url":"https://api.github.com/users/brkt/gists{/gist_id}","starred_url":"https://api.github.com/users/brkt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brkt/subscriptions","organizations_url":"https://api.github.com/users/brkt/orgs","repos_url":"https://api.github.com/users/brkt/repos","events_url":"https://api.github.com/users/brkt/events{/privacy}","received_events_url":"https://api.github.com/users/brkt/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/brkt/brkt-cli","description":"","fork":false,"url":"https://api.github.com/repos/brkt/brkt-cli","forks_url":"https://api.github.com/repos/brkt/brkt-cli/forks","keys_url":"https://api.github.com/repos/brkt/brkt-cli/keys{/key_id}","collaborators_url":"https://api.github.com/repos/brkt/brkt-cli/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/brkt/brkt-cli/teams","hooks_url":"https://api.github.com/repos/brkt/brkt-cli/hooks","issue_events_url":"https://api.github.com/repos/brkt/brkt-cli/issues/events{/number}","events_url":"https://api.github.com/repos/brkt/brkt-cli/events","assignees_url":"https://api.github.com/repos/brkt/brkt-cli/assignees{/user}","branches_url":"https://api.github.com/repos/brkt/brkt-cli/branches{/branch}","tags_url":"https://api.github.com/repos/brkt/brkt-cli/tags","blobs_url":"https://api.github.com/repos/brkt/brkt-cli/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/brkt/brkt-cli/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/brkt/brkt-cli/git/refs{/sha}","trees_url":"https://api.github.com/repos/brkt/brkt-cli/git/trees{/sha}","statuses_url":"https://api.github.com/repos/brkt/brkt-cli/statuses/{sha}","languages_url":"https://api.github.com/repos/brkt/brkt-cli/languages","stargazers_url":"https://api.github.com/repos/brkt/brkt-cli/stargazers","contributors_url":"https://api.github.com/repos/brkt/brkt-cli/contributors","subscribers_url":"https://api.github.com/repos/brkt/brkt-cli/subscribers","subscription_url":"https://api.github.com/repos/brkt/brkt-cli/subscription","commits_url":"https://api.github.com/repos/brkt/brkt-cli/commits{/sha}","git_commits_url":"https://api.github.com/repos/brkt/brkt-cli/git/commits{/sha}","comments_url":"https://api.github.com/repos/brkt/brkt-cli/comments{/number}","issue_comment_url":"https://api.github.com/repos/brkt/brkt-cli/issues/comments{/number}","contents_url":"https://api.github.com/repos/brkt/brkt-cli/contents/{+path}","compare_url":"https://api.github.com/repos/brkt/brkt-cli/compare/{base}...{head}","merges_url":"https://api.github.com/repos/brkt/brkt-cli/merges","archive_url":"https://api.github.com/repos/brkt/brkt-cli/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/brkt/brkt-cli/downloads","issues_url":"https://api.github.com/repos/brkt/brkt-cli/issues{/number}","pulls_url":"https://api.github.com/repos/brkt/brkt-cli/pulls{/number}","milestones_url":"https://api.github.com/repos/brkt/brkt-cli/milestones{/number}","notifications_url":"https://api.github.com/repos/brkt/brkt-cli/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/brkt/brkt-cli/labels{/name}","releases_url":"https://api.github.com/repos/brkt/brkt-cli/releases{/id}","deployments_url":"https://api.github.com/repos/brkt/brkt-cli/deployments","created_at":"2015-09-14T18:39:20Z","updated_at":"2016-07-11T22:43:51Z","pushed_at":"2016-07-29T18:27:56Z","git_url":"git://github.com/brkt/brkt-cli.git","ssh_url":"git@github.com:brkt/brkt-cli.git","clone_url":"https://github.com/brkt/brkt-cli.git","svn_url":"https://github.com/brkt/brkt-cli","homepage":null,"size":989,"stargazers_count":3,"watchers_count":3,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":5,"mirror_url":null,"open_issues_count":2,"forks":5,"open_issues":2,"watchers":3,"default_branch":"master"}},"base":{"label":"brkt:master","ref":"master","sha":"3882c2c39f21505f1825c132965dbed2db4d6ebd","user":{"login":"brkt","id":2963083,"avatar_url":"https://avatars.githubusercontent.com/u/2963083?v=3","gravatar_id":"","url":"https://api.github.com/users/brkt","html_url":"https://github.com/brkt","followers_url":"https://api.github.com/users/brkt/followers","following_url":"https://api.github.com/users/brkt/following{/other_user}","gists_url":"https://api.github.com/users/brkt/gists{/gist_id}","starred_url":"https://api.github.com/users/brkt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brkt/subscriptions","organizations_url":"https://api.github.com/users/brkt/orgs","repos_url":"https://api.github.com/users/brkt/repos","events_url":"https://api.github.com/users/brkt/events{/privacy}","received_events_url":"https://api.github.com/users/brkt/received_events","type":"Organization","site_admin":false},"repo":{"id":42469221,"name":"brkt-cli","full_name":"brkt/brkt-cli","owner":{"login":"brkt","id":2963083,"avatar_url":"https://avatars.githubusercontent.com/u/2963083?v=3","gravatar_id":"","url":"https://api.github.com/users/brkt","html_url":"https://github.com/brkt","followers_url":"https://api.github.com/users/brkt/followers","following_url":"https://api.github.com/users/brkt/following{/other_user}","gists_url":"https://api.github.com/users/brkt/gists{/gist_id}","starred_url":"https://api.github.com/users/brkt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brkt/subscriptions","organizations_url":"https://api.github.com/users/brkt/orgs","repos_url":"https://api.github.com/users/brkt/repos","events_url":"https://api.github.com/users/brkt/events{/privacy}","received_events_url":"https://api.github.com/users/brkt/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/brkt/brkt-cli","description":"","fork":false,"url":"https://api.github.com/repos/brkt/brkt-cli","forks_url":"https://api.github.com/repos/brkt/brkt-cli/forks","keys_url":"https://api.github.com/repos/brkt/brkt-cli/keys{/key_id}","collaborators_url":"https://api.github.com/repos/brkt/brkt-cli/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/brkt/brkt-cli/teams","hooks_url":"https://api.github.com/repos/brkt/brkt-cli/hooks","issue_events_url":"https://api.github.com/repos/brkt/brkt-cli/issues/events{/number}","events_url":"https://api.github.com/repos/brkt/brkt-cli/events","assignees_url":"https://api.github.com/repos/brkt/brkt-cli/assignees{/user}","branches_url":"https://api.github.com/repos/brkt/brkt-cli/branches{/branch}","tags_url":"https://api.github.com/repos/brkt/brkt-cli/tags","blobs_url":"https://api.github.com/repos/brkt/brkt-cli/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/brkt/brkt-cli/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/brkt/brkt-cli/git/refs{/sha}","trees_url":"https://api.github.com/repos/brkt/brkt-cli/git/trees{/sha}","statuses_url":"https://api.github.com/repos/brkt/brkt-cli/statuses/{sha}","languages_url":"https://api.github.com/repos/brkt/brkt-cli/languages","stargazers_url":"https://api.github.com/repos/brkt/brkt-cli/stargazers","contributors_url":"https://api.github.com/repos/brkt/brkt-cli/contributors","subscribers_url":"https://api.github.com/repos/brkt/brkt-cli/subscribers","subscription_url":"https://api.github.com/repos/brkt/brkt-cli/subscription","commits_url":"https://api.github.com/repos/brkt/brkt-cli/commits{/sha}","git_commits_url":"https://api.github.com/repos/brkt/brkt-cli/git/commits{/sha}","comments_url":"https://api.github.com/repos/brkt/brkt-cli/comments{/number}","issue_comment_url":"https://api.github.com/repos/brkt/brkt-cli/issues/comments{/number}","contents_url":"https://api.github.com/repos/brkt/brkt-cli/contents/{+path}","compare_url":"https://api.github.com/repos/brkt/brkt-cli/compare/{base}...{head}","merges_url":"https://api.github.com/repos/brkt/brkt-cli/merges","archive_url":"https://api.github.com/repos/brkt/brkt-cli/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/brkt/brkt-cli/downloads","issues_url":"https://api.github.com/repos/brkt/brkt-cli/issues{/number}","pulls_url":"https://api.github.com/repos/brkt/brkt-cli/pulls{/number}","milestones_url":"https://api.github.com/repos/brkt/brkt-cli/milestones{/number}","notifications_url":"https://api.github.com/repos/brkt/brkt-cli/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/brkt/brkt-cli/labels{/name}","releases_url":"https://api.github.com/repos/brkt/brkt-cli/releases{/id}","deployments_url":"https://api.github.com/repos/brkt/brkt-cli/deployments","created_at":"2015-09-14T18:39:20Z","updated_at":"2016-07-11T22:43:51Z","pushed_at":"2016-07-29T18:27:56Z","git_url":"git://github.com/brkt/brkt-cli.git","ssh_url":"git@github.com:brkt/brkt-cli.git","clone_url":"https://github.com/brkt/brkt-cli.git","svn_url":"https://github.com/brkt/brkt-cli","homepage":null,"size":989,"stargazers_count":3,"watchers_count":3,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":5,"mirror_url":null,"open_issues_count":2,"forks":5,"open_issues":2,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/brkt/brkt-cli/pulls/365"},"html":{"href":"https://github.com/brkt/brkt-cli/pull/365"},"issue":{"href":"https://api.github.com/repos/brkt/brkt-cli/issues/365"},"comments":{"href":"https://api.github.com/repos/brkt/brkt-cli/issues/365/comments"},"review_comments":{"href":"https://api.github.com/repos/brkt/brkt-cli/pulls/365/comments"},"review_comment":{"href":"https://api.github.com/repos/brkt/brkt-cli/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/brkt/brkt-cli/pulls/365/commits"},"statuses":{"href":"https://api.github.com/repos/brkt/brkt-cli/statuses/ed70bd9e32491744675c339a999153de572aa69c"}}}}
{"actor":{"display_login":"bburtin"}}