We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
sam 0.19.0 crashes on trying to create .aws-sam directory when not permitted
### Description

While running `sam validate` from a freshly built docker image, it crashes while trying to create the `.aws-sam` config directory.

### Steps to reproduce

In our CI pipeline, we run `sam` from within a docker container built according to this `Dockerfile`:

```
FROM alpine:3.9

RUN apk add --no-cache \
    build-base \
    python \
    python-dev \
    py-pip \
    groff \
    docker && \
    pip install awscli aws-sam-cli
```

AWS credentials are passed in as environment variables, volumes mounted from the Jenkins workspace into the docker image, and then we simply run:

`sam validate -t ./.../template.yaml`

### Observed result

Logs from the Jenkins build:

```
$ docker run -t -d -u 3000:100 --network=host -w /data/hudson/workspace/loper-portal_feature_jenkinstest -v /data/hudson/workspace/loper-portal_feature_jenkinstest:/data/hudson/workspace/loper-portal_feature_jenkinstest:rw,z -v /data/hudson/workspace/loper-portal_feature_jenkinstest@tmp:/data/hudson/workspace/loper-portal_feature_jenkinstest@tmp:rw,z -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** xxxx-aws-sam-cli cat
$ docker top 04fa30b17ceb0ae6d45b66190f32f4bad8dedd57386352a31e61f0da6ac18aa2 -eo pid,comm
[Pipeline] {
[Pipeline] withEnv
[Pipeline] {
[Pipeline] withCredentials
Masking supported pattern matches of $AWS_ACCESS_KEY_ID or $AWS_SECRET_ACCESS_KEY
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Validate CloudFormation template)
[Pipeline] sh
+ sam validate --debug -t cloudformation/template.yaml
Traceback (most recent call last):
  File "/usr/bin/sam", line 11, in <module>
    sys.exit(cli())
  File "/usr/lib/python2.7/site-packages/click/core.py", line 722, in __call__
    return self.main(*args, **kwargs)
  File "/usr/lib/python2.7/site-packages/click/core.py", line 697, in main
    rv = self.invoke(ctx)
  File "/usr/lib/python2.7/site-packages/click/core.py", line 1063, in invoke
    Command.invoke(self, ctx)
  File "/usr/lib/python2.7/site-packages/click/core.py", line 895, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/lib/python2.7/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/usr/lib/python2.7/site-packages/click/decorators.py", line 64, in new_func
    return ctx.invoke(f, obj, *args[1:], **kwargs)
  File "/usr/lib/python2.7/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/usr/lib/python2.7/site-packages/samcli/cli/main.py", line 83, in cli
    if global_cfg.telemetry_enabled is None:
  File "/usr/lib/python2.7/site-packages/samcli/cli/global_config.py", line 113, in telemetry_enabled
    self._telemetry_enabled = self._get_value(TELEMETRY_ENABLED_KEY)
  File "/usr/lib/python2.7/site-packages/samcli/cli/global_config.py", line 145, in _get_value
    cfg_path = self._get_config_file_path(CONFIG_FILENAME)
  File "/usr/lib/python2.7/site-packages/samcli/cli/global_config.py", line 170, in _get_config_file_path
    self._create_dir()
  File "/usr/lib/python2.7/site-packages/samcli/cli/global_config.py", line 167, in _create_dir
    self.config_dir.mkdir(mode=0o700, parents=True, exist_ok=True)
  File "/usr/lib/python2.7/site-packages/pathlib2/__init__.py", line 1540, in mkdir
    _try_except_filenotfounderror(_try_func, _exc_func)
  File "/usr/lib/python2.7/site-packages/pathlib2/__init__.py", line 113, in _try_except_filenotfounderror
    try_func()
  File "/usr/lib/python2.7/site-packages/pathlib2/__init__.py", line 1531, in _try_func
    self._accessor.mkdir(self, mode)
  File "/usr/lib/python2.7/site-packages/pathlib2/__init__.py", line 585, in wrapped
    return strfunc(str(pathobj), *args)
OSError: [Errno 13] Permission denied: '/.aws-sam'
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // withCredentials
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] }
```
### Expected result

A non-crashing sam :)

### Additional environment details (Ex: Windows, Mac, Amazon Linux etc)

1. OS: alpine linux, docker container
2. `sam --version`: 0.19.0

### Others

This was added in the 0.19.0 Telemetry release, where in commit dbd534a `samcli/cli/global_config.py` was added, that tries to write the config dir if it doesn't exist. 

Actually we should've pinned the versions used in our `Dockerfile`. Not pinning it had us miss the addition of the Telemetry feature in 0.19.0. Although I'd love to contribute, enabling Telemetry would undoubtedly go against some company policy. There _is_ the message in the first run of the new version (if it was able to write the config dir), but in our setup that's quite easy to miss. Basically I'm advocating an opt-in instead


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `samcli/cli/global_config.py`
Content:
```
1 """
2 Provides global configuration helpers.
3 """
4 
5 import json
6 import logging
7 import uuid
8 import os
9 
10 import click
11 
12 try:
13     from pathlib import Path
14 except ImportError:  # pragma: no cover
15     from pathlib2 import Path  # pragma: no cover
16 
17 LOG = logging.getLogger(__name__)
18 
19 CONFIG_FILENAME = "metadata.json"
20 INSTALLATION_ID_KEY = "installationId"
21 TELEMETRY_ENABLED_KEY = "telemetryEnabled"
22 
23 
24 class GlobalConfig(object):
25     """
26     Contains helper methods for global configuration files and values. Handles
27     configuration file creation, updates, and fetching in a platform-neutral way.
28 
29     Generally uses '~/.aws-sam/' or 'C:\\Users\\<user>\\AppData\\Roaming\\AWS SAM' as
30     the base directory, depending on platform.
31     """
32 
33     def __init__(self, config_dir=None, installation_id=None, telemetry_enabled=None):
34         """
35         Initializes the class, with options provided to assist with testing.
36 
37         :param config_dir: Optional, overrides the default config directory path.
38         :param installation_id: Optional, will use this installation id rather than checking config values.
39         """
40         self._config_dir = config_dir
41         self._installation_id = installation_id
42         self._telemetry_enabled = telemetry_enabled
43 
44     @property
45     def config_dir(self):
46         if not self._config_dir:
47             # Internal Environment variable to customize SAM CLI App Dir. Currently used only by integ tests.
48             app_dir = os.getenv("__SAM_CLI_APP_DIR")
49             self._config_dir = Path(app_dir) if app_dir else Path(click.get_app_dir('AWS SAM', force_posix=True))
50 
51         return Path(self._config_dir)
52 
53     @property
54     def installation_id(self):
55         """
56         Returns the installation UUID for this AWS SAM CLI installation. If the
57         installation id has not yet been set, it will be set before returning.
58 
59         Examples
60         --------
61 
62         >>> gc = GlobalConfig()
63         >>> gc.installation_id
64         "7b7d4db7-2f54-45ba-bf2f-a2cbc9e74a34"
65 
66         >>> gc = GlobalConfig()
67         >>> gc.installation_id
68         None
69 
70         Returns
71         -------
72         A string containing the installation UUID, or None in case of an error.
73         """
74         if self._installation_id:
75             return self._installation_id
76         try:
77             self._installation_id = self._get_or_set_uuid(INSTALLATION_ID_KEY)
78             return self._installation_id
79         except (ValueError, IOError):
80             return None
81 
82     @property
83     def telemetry_enabled(self):
84         """
85         Check if telemetry is enabled for this installation. Default value of
86         False. It first tries to get value from SAM_CLI_TELEMETRY environment variable. If its not set,
87         then it fetches the value from config file.
88 
89         To enable telemetry, set SAM_CLI_TELEMETRY environment variable equal to integer 1 or string '1'.
90         All other values including words like 'True', 'true', 'false', 'False', 'abcd' etc will disable Telemetry
91 
92         Examples
93         --------
94 
95         >>> gc = GlobalConfig()
96         >>> gc.telemetry_enabled
97         True
98 
99         Returns
100         -------
101         Boolean flag value. True if telemetry is enabled for this installation,
102         False otherwise.
103         """
104         if self._telemetry_enabled is not None:
105             return self._telemetry_enabled
106 
107         # If environment variable is set, its value takes precedence over the value from config file.
108         env_name = "SAM_CLI_TELEMETRY"
109         if env_name in os.environ:
110             return os.getenv(env_name) in ('1', 1)
111 
112         try:
113             self._telemetry_enabled = self._get_value(TELEMETRY_ENABLED_KEY)
114             return self._telemetry_enabled
115         except (ValueError, IOError) as ex:
116             LOG.debug("Error when retrieving telemetry_enabled flag", exc_info=ex)
117             return False
118 
119     @telemetry_enabled.setter
120     def telemetry_enabled(self, value):
121         """
122         Sets the telemetry_enabled flag to the provided boolean value.
123 
124         Examples
125         --------
126         >>> gc = GlobalConfig()
127         >>> gc.telemetry_enabled
128         False
129         >>> gc.telemetry_enabled = True
130         >>> gc.telemetry_enabled
131         True
132 
133         Raises
134         ------
135         IOError
136             If there are errors opening or writing to the global config file.
137 
138         JSONDecodeError
139             If the config file exists, and is not valid JSON.
140         """
141         self._set_value("telemetryEnabled", value)
142         self._telemetry_enabled = value
143 
144     def _get_value(self, key):
145         cfg_path = self._get_config_file_path(CONFIG_FILENAME)
146         if not cfg_path.exists():
147             return None
148         with open(str(cfg_path)) as fp:
149             body = fp.read()
150             json_body = json.loads(body)
151             return json_body.get(key)
152 
153     def _set_value(self, key, value):
154         cfg_path = self._get_config_file_path(CONFIG_FILENAME)
155         if not cfg_path.exists():
156             return self._set_json_cfg(cfg_path, key, value)
157         with open(str(cfg_path)) as fp:
158             body = fp.read()
159             try:
160                 json_body = json.loads(body)
161             except ValueError as ex:
162                 LOG.debug("Failed to decode JSON in {cfg_path}", exc_info=ex)
163                 raise ex
164             return self._set_json_cfg(cfg_path, key, value, json_body)
165 
166     def _create_dir(self):
167         self.config_dir.mkdir(mode=0o700, parents=True, exist_ok=True)
168 
169     def _get_config_file_path(self, filename):
170         self._create_dir()
171         filepath = self.config_dir.joinpath(filename)
172         return filepath
173 
174     def _get_or_set_uuid(self, key):
175         """
176         Special logic method for when we want a UUID to always be present, this
177         method behaves as a getter with side effects. Essentially, if the value
178         is not present, we will set it with a generated UUID.
179 
180         If we have multiple such values in the future, a possible refactor is
181         to just be _get_or_set_value, where we also take a default value as a
182         parameter.
183         """
184         cfg_value = self._get_value(key)
185         if cfg_value is not None:
186             return cfg_value
187         return self._set_value(key, str(uuid.uuid4()))
188 
189     def _set_json_cfg(self, filepath, key, value, json_body=None):
190         """
191         Special logic method to add a value to a JSON configuration file. This
192         method will write a new version of the file in question, so it will
193         either write a new file with only the first config value, or if a JSON
194         body is provided, it will upsert starting from that JSON body.
195         """
196         json_body = json_body or {}
197         json_body[key] = value
198         file_body = json.dumps(json_body, indent=4) + "\n"
199         try:
200             with open(str(filepath), 'w') as f:
201                 f.write(file_body)
202         except IOError as ex:
203             LOG.debug("Error writing to {filepath}", exc_info=ex)
204             raise ex
205         return value
206
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/samcli/cli/global_config.py b/samcli/cli/global_config.py
--- a/samcli/cli/global_config.py
+++ b/samcli/cli/global_config.py
@@ -47,7 +47,6 @@
             # Internal Environment variable to customize SAM CLI App Dir. Currently used only by integ tests.
             app_dir = os.getenv("__SAM_CLI_APP_DIR")
             self._config_dir = Path(app_dir) if app_dir else Path(click.get_app_dir('AWS SAM', force_posix=True))
-
         return Path(self._config_dir)
 
     @property
@@ -76,7 +75,7 @@
         try:
             self._installation_id = self._get_or_set_uuid(INSTALLATION_ID_KEY)
             return self._installation_id
-        except (ValueError, IOError):
+        except (ValueError, IOError, OSError):
             return None
 
     @property
@@ -112,7 +111,7 @@
         try:
             self._telemetry_enabled = self._get_value(TELEMETRY_ENABLED_KEY)
             return self._telemetry_enabled
-        except (ValueError, IOError) as ex:
+        except (ValueError, IOError, OSError) as ex:
             LOG.debug("Error when retrieving telemetry_enabled flag", exc_info=ex)
             return False
 
@@ -164,6 +163,10 @@
             return self._set_json_cfg(cfg_path, key, value, json_body)
 
     def _create_dir(self):
+        """
+        Creates configuration directory if it does not already exist, otherwise does nothing.
+        May raise an OSError if we do not have permissions to create the directory.
+        """
         self.config_dir.mkdir(mode=0o700, parents=True, exist_ok=True)
 
     def _get_config_file_path(self, filename):

{"golden_diff": "diff --git a/samcli/cli/global_config.py b/samcli/cli/global_config.py\n--- a/samcli/cli/global_config.py\n+++ b/samcli/cli/global_config.py\n@@ -47,7 +47,6 @@\n             # Internal Environment variable to customize SAM CLI App Dir. Currently used only by integ tests.\n             app_dir = os.getenv(\"__SAM_CLI_APP_DIR\")\n             self._config_dir = Path(app_dir) if app_dir else Path(click.get_app_dir('AWS SAM', force_posix=True))\n-\n         return Path(self._config_dir)\n \n     @property\n@@ -76,7 +75,7 @@\n         try:\n             self._installation_id = self._get_or_set_uuid(INSTALLATION_ID_KEY)\n             return self._installation_id\n-        except (ValueError, IOError):\n+        except (ValueError, IOError, OSError):\n             return None\n \n     @property\n@@ -112,7 +111,7 @@\n         try:\n             self._telemetry_enabled = self._get_value(TELEMETRY_ENABLED_KEY)\n             return self._telemetry_enabled\n-        except (ValueError, IOError) as ex:\n+        except (ValueError, IOError, OSError) as ex:\n             LOG.debug(\"Error when retrieving telemetry_enabled flag\", exc_info=ex)\n             return False\n \n@@ -164,6 +163,10 @@\n             return self._set_json_cfg(cfg_path, key, value, json_body)\n \n     def _create_dir(self):\n+        \"\"\"\n+        Creates configuration directory if it does not already exist, otherwise does nothing.\n+        May raise an OSError if we do not have permissions to create the directory.\n+        \"\"\"\n         self.config_dir.mkdir(mode=0o700, parents=True, exist_ok=True)\n \n     def _get_config_file_path(self, filename):\n", "issue": "sam 0.19.0 crashes on trying to create .aws-sam directory when not permitted\n### Description\r\n\r\nWhile running `sam validate` from a freshly built docker image, it crashes while trying to create the `.aws-sam` config directory.\r\n\r\n### Steps to reproduce\r\n\r\nIn our CI pipeline, we run `sam` from within a docker container built according to this `Dockerfile`:\r\n\r\n```\r\nFROM alpine:3.9\r\n\r\nRUN apk add --no-cache \\\r\n    build-base \\\r\n    python \\\r\n    python-dev \\\r\n    py-pip \\\r\n    groff \\\r\n    docker && \\\r\n    pip install awscli aws-sam-cli\r\n```\r\n\r\nAWS credentials are passed in as environment variables, volumes mounted from the Jenkins workspace into the docker image, and then we simply run:\r\n\r\n`sam validate -t ./.../template.yaml`\r\n\r\n### Observed result\r\n\r\nLogs from the Jenkins build:\r\n\r\n```\r\n$ docker run -t -d -u 3000:100 --network=host -w /data/hudson/workspace/loper-portal_feature_jenkinstest -v /data/hudson/workspace/loper-portal_feature_jenkinstest:/data/hudson/workspace/loper-portal_feature_jenkinstest:rw,z -v /data/hudson/workspace/loper-portal_feature_jenkinstest@tmp:/data/hudson/workspace/loper-portal_feature_jenkinstest@tmp:rw,z -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** xxxx-aws-sam-cli cat\r\n$ docker top 04fa30b17ceb0ae6d45b66190f32f4bad8dedd57386352a31e61f0da6ac18aa2 -eo pid,comm\r\n[Pipeline] {\r\n[Pipeline] withEnv\r\n[Pipeline] {\r\n[Pipeline] withCredentials\r\nMasking supported pattern matches of $AWS_ACCESS_KEY_ID or $AWS_SECRET_ACCESS_KEY\r\n[Pipeline] {\r\n[Pipeline] stage\r\n[Pipeline] { (Validate CloudFormation template)\r\n[Pipeline] sh\r\n+ sam validate --debug -t cloudformation/template.yaml\r\nTraceback (most recent call last):\r\n  File \"/usr/bin/sam\", line 11, in <module>\r\n    sys.exit(cli())\r\n  File \"/usr/lib/python2.7/site-packages/click/core.py\", line 722, in __call__\r\n    return self.main(*args, **kwargs)\r\n  File \"/usr/lib/python2.7/site-packages/click/core.py\", line 697, in main\r\n    rv = self.invoke(ctx)\r\n  File \"/usr/lib/python2.7/site-packages/click/core.py\", line 1063, in invoke\r\n    Command.invoke(self, ctx)\r\n  File \"/usr/lib/python2.7/site-packages/click/core.py\", line 895, in invoke\r\n    return ctx.invoke(self.callback, **ctx.params)\r\n  File \"/usr/lib/python2.7/site-packages/click/core.py\", line 535, in invoke\r\n    return callback(*args, **kwargs)\r\n  File \"/usr/lib/python2.7/site-packages/click/decorators.py\", line 64, in new_func\r\n    return ctx.invoke(f, obj, *args[1:], **kwargs)\r\n  File \"/usr/lib/python2.7/site-packages/click/core.py\", line 535, in invoke\r\n    return callback(*args, **kwargs)\r\n  File \"/usr/lib/python2.7/site-packages/samcli/cli/main.py\", line 83, in cli\r\n    if global_cfg.telemetry_enabled is None:\r\n  File \"/usr/lib/python2.7/site-packages/samcli/cli/global_config.py\", line 113, in telemetry_enabled\r\n    self._telemetry_enabled = self._get_value(TELEMETRY_ENABLED_KEY)\r\n  File \"/usr/lib/python2.7/site-packages/samcli/cli/global_config.py\", line 145, in _get_value\r\n    cfg_path = self._get_config_file_path(CONFIG_FILENAME)\r\n  File \"/usr/lib/python2.7/site-packages/samcli/cli/global_config.py\", line 170, in _get_config_file_path\r\n    self._create_dir()\r\n  File \"/usr/lib/python2.7/site-packages/samcli/cli/global_config.py\", line 167, in _create_dir\r\n    self.config_dir.mkdir(mode=0o700, parents=True, exist_ok=True)\r\n  File \"/usr/lib/python2.7/site-packages/pathlib2/__init__.py\", line 1540, in mkdir\r\n    _try_except_filenotfounderror(_try_func, _exc_func)\r\n  File \"/usr/lib/python2.7/site-packages/pathlib2/__init__.py\", line 113, in _try_except_filenotfounderror\r\n    try_func()\r\n  File \"/usr/lib/python2.7/site-packages/pathlib2/__init__.py\", line 1531, in _try_func\r\n    self._accessor.mkdir(self, mode)\r\n  File \"/usr/lib/python2.7/site-packages/pathlib2/__init__.py\", line 585, in wrapped\r\n    return strfunc(str(pathobj), *args)\r\nOSError: [Errno 13] Permission denied: '/.aws-sam'\r\n[Pipeline] }\r\n[Pipeline] // stage\r\n[Pipeline] }\r\n[Pipeline] // withCredentials\r\n[Pipeline] }\r\n[Pipeline] // withEnv\r\n[Pipeline] }\r\n```\r\n### Expected result\r\n\r\nA non-crashing sam :)\r\n\r\n### Additional environment details (Ex: Windows, Mac, Amazon Linux etc)\r\n\r\n1. OS: alpine linux, docker container\r\n2. `sam --version`: 0.19.0\r\n\r\n### Others\r\n\r\nThis was added in the 0.19.0 Telemetry release, where in commit dbd534a `samcli/cli/global_config.py` was added, that tries to write the config dir if it doesn't exist. \r\n\r\nActually we should've pinned the versions used in our `Dockerfile`. Not pinning it had us miss the addition of the Telemetry feature in 0.19.0. Although I'd love to contribute, enabling Telemetry would undoubtedly go against some company policy. There _is_ the message in the first run of the new version (if it was able to write the config dir), but in our setup that's quite easy to miss. Basically I'm advocating an opt-in instead\r\n\n", "before_files": [{"content": "\"\"\"\nProvides global configuration helpers.\n\"\"\"\n\nimport json\nimport logging\nimport uuid\nimport os\n\nimport click\n\ntry:\n    from pathlib import Path\nexcept ImportError:  # pragma: no cover\n    from pathlib2 import Path  # pragma: no cover\n\nLOG = logging.getLogger(__name__)\n\nCONFIG_FILENAME = \"metadata.json\"\nINSTALLATION_ID_KEY = \"installationId\"\nTELEMETRY_ENABLED_KEY = \"telemetryEnabled\"\n\n\nclass GlobalConfig(object):\n    \"\"\"\n    Contains helper methods for global configuration files and values. Handles\n    configuration file creation, updates, and fetching in a platform-neutral way.\n\n    Generally uses '~/.aws-sam/' or 'C:\\\\Users\\\\<user>\\\\AppData\\\\Roaming\\\\AWS SAM' as\n    the base directory, depending on platform.\n    \"\"\"\n\n    def __init__(self, config_dir=None, installation_id=None, telemetry_enabled=None):\n        \"\"\"\n        Initializes the class, with options provided to assist with testing.\n\n        :param config_dir: Optional, overrides the default config directory path.\n        :param installation_id: Optional, will use this installation id rather than checking config values.\n        \"\"\"\n        self._config_dir = config_dir\n        self._installation_id = installation_id\n        self._telemetry_enabled = telemetry_enabled\n\n    @property\n    def config_dir(self):\n        if not self._config_dir:\n            # Internal Environment variable to customize SAM CLI App Dir. Currently used only by integ tests.\n            app_dir = os.getenv(\"__SAM_CLI_APP_DIR\")\n            self._config_dir = Path(app_dir) if app_dir else Path(click.get_app_dir('AWS SAM', force_posix=True))\n\n        return Path(self._config_dir)\n\n    @property\n    def installation_id(self):\n        \"\"\"\n        Returns the installation UUID for this AWS SAM CLI installation. If the\n        installation id has not yet been set, it will be set before returning.\n\n        Examples\n        --------\n\n        >>> gc = GlobalConfig()\n        >>> gc.installation_id\n        \"7b7d4db7-2f54-45ba-bf2f-a2cbc9e74a34\"\n\n        >>> gc = GlobalConfig()\n        >>> gc.installation_id\n        None\n\n        Returns\n        -------\n        A string containing the installation UUID, or None in case of an error.\n        \"\"\"\n        if self._installation_id:\n            return self._installation_id\n        try:\n            self._installation_id = self._get_or_set_uuid(INSTALLATION_ID_KEY)\n            return self._installation_id\n        except (ValueError, IOError):\n            return None\n\n    @property\n    def telemetry_enabled(self):\n        \"\"\"\n        Check if telemetry is enabled for this installation. Default value of\n        False. It first tries to get value from SAM_CLI_TELEMETRY environment variable. If its not set,\n        then it fetches the value from config file.\n\n        To enable telemetry, set SAM_CLI_TELEMETRY environment variable equal to integer 1 or string '1'.\n        All other values including words like 'True', 'true', 'false', 'False', 'abcd' etc will disable Telemetry\n\n        Examples\n        --------\n\n        >>> gc = GlobalConfig()\n        >>> gc.telemetry_enabled\n        True\n\n        Returns\n        -------\n        Boolean flag value. True if telemetry is enabled for this installation,\n        False otherwise.\n        \"\"\"\n        if self._telemetry_enabled is not None:\n            return self._telemetry_enabled\n\n        # If environment variable is set, its value takes precedence over the value from config file.\n        env_name = \"SAM_CLI_TELEMETRY\"\n        if env_name in os.environ:\n            return os.getenv(env_name) in ('1', 1)\n\n        try:\n            self._telemetry_enabled = self._get_value(TELEMETRY_ENABLED_KEY)\n            return self._telemetry_enabled\n        except (ValueError, IOError) as ex:\n            LOG.debug(\"Error when retrieving telemetry_enabled flag\", exc_info=ex)\n            return False\n\n    @telemetry_enabled.setter\n    def telemetry_enabled(self, value):\n        \"\"\"\n        Sets the telemetry_enabled flag to the provided boolean value.\n\n        Examples\n        --------\n        >>> gc = GlobalConfig()\n        >>> gc.telemetry_enabled\n        False\n        >>> gc.telemetry_enabled = True\n        >>> gc.telemetry_enabled\n        True\n\n        Raises\n        ------\n        IOError\n            If there are errors opening or writing to the global config file.\n\n        JSONDecodeError\n            If the config file exists, and is not valid JSON.\n        \"\"\"\n        self._set_value(\"telemetryEnabled\", value)\n        self._telemetry_enabled = value\n\n    def _get_value(self, key):\n        cfg_path = self._get_config_file_path(CONFIG_FILENAME)\n        if not cfg_path.exists():\n            return None\n        with open(str(cfg_path)) as fp:\n            body = fp.read()\n            json_body = json.loads(body)\n            return json_body.get(key)\n\n    def _set_value(self, key, value):\n        cfg_path = self._get_config_file_path(CONFIG_FILENAME)\n        if not cfg_path.exists():\n            return self._set_json_cfg(cfg_path, key, value)\n        with open(str(cfg_path)) as fp:\n            body = fp.read()\n            try:\n                json_body = json.loads(body)\n            except ValueError as ex:\n                LOG.debug(\"Failed to decode JSON in {cfg_path}\", exc_info=ex)\n                raise ex\n            return self._set_json_cfg(cfg_path, key, value, json_body)\n\n    def _create_dir(self):\n        self.config_dir.mkdir(mode=0o700, parents=True, exist_ok=True)\n\n    def _get_config_file_path(self, filename):\n        self._create_dir()\n        filepath = self.config_dir.joinpath(filename)\n        return filepath\n\n    def _get_or_set_uuid(self, key):\n        \"\"\"\n        Special logic method for when we want a UUID to always be present, this\n        method behaves as a getter with side effects. Essentially, if the value\n        is not present, we will set it with a generated UUID.\n\n        If we have multiple such values in the future, a possible refactor is\n        to just be _get_or_set_value, where we also take a default value as a\n        parameter.\n        \"\"\"\n        cfg_value = self._get_value(key)\n        if cfg_value is not None:\n            return cfg_value\n        return self._set_value(key, str(uuid.uuid4()))\n\n    def _set_json_cfg(self, filepath, key, value, json_body=None):\n        \"\"\"\n        Special logic method to add a value to a JSON configuration file. This\n        method will write a new version of the file in question, so it will\n        either write a new file with only the first config value, or if a JSON\n        body is provided, it will upsert starting from that JSON body.\n        \"\"\"\n        json_body = json_body or {}\n        json_body[key] = value\n        file_body = json.dumps(json_body, indent=4) + \"\\n\"\n        try:\n            with open(str(filepath), 'w') as f:\n                f.write(file_body)\n        except IOError as ex:\n            LOG.debug(\"Error writing to {filepath}\", exc_info=ex)\n            raise ex\n        return value\n", "path": "samcli/cli/global_config.py"}], "after_files": [{"content": "\"\"\"\nProvides global configuration helpers.\n\"\"\"\n\nimport json\nimport logging\nimport uuid\nimport os\n\nimport click\n\ntry:\n    from pathlib import Path\nexcept ImportError:  # pragma: no cover\n    from pathlib2 import Path  # pragma: no cover\n\nLOG = logging.getLogger(__name__)\n\nCONFIG_FILENAME = \"metadata.json\"\nINSTALLATION_ID_KEY = \"installationId\"\nTELEMETRY_ENABLED_KEY = \"telemetryEnabled\"\n\n\nclass GlobalConfig(object):\n    \"\"\"\n    Contains helper methods for global configuration files and values. Handles\n    configuration file creation, updates, and fetching in a platform-neutral way.\n\n    Generally uses '~/.aws-sam/' or 'C:\\\\Users\\\\<user>\\\\AppData\\\\Roaming\\\\AWS SAM' as\n    the base directory, depending on platform.\n    \"\"\"\n\n    def __init__(self, config_dir=None, installation_id=None, telemetry_enabled=None):\n        \"\"\"\n        Initializes the class, with options provided to assist with testing.\n\n        :param config_dir: Optional, overrides the default config directory path.\n        :param installation_id: Optional, will use this installation id rather than checking config values.\n        \"\"\"\n        self._config_dir = config_dir\n        self._installation_id = installation_id\n        self._telemetry_enabled = telemetry_enabled\n\n    @property\n    def config_dir(self):\n        if not self._config_dir:\n            # Internal Environment variable to customize SAM CLI App Dir. Currently used only by integ tests.\n            app_dir = os.getenv(\"__SAM_CLI_APP_DIR\")\n            self._config_dir = Path(app_dir) if app_dir else Path(click.get_app_dir('AWS SAM', force_posix=True))\n        return Path(self._config_dir)\n\n    @property\n    def installation_id(self):\n        \"\"\"\n        Returns the installation UUID for this AWS SAM CLI installation. If the\n        installation id has not yet been set, it will be set before returning.\n\n        Examples\n        --------\n\n        >>> gc = GlobalConfig()\n        >>> gc.installation_id\n        \"7b7d4db7-2f54-45ba-bf2f-a2cbc9e74a34\"\n\n        >>> gc = GlobalConfig()\n        >>> gc.installation_id\n        None\n\n        Returns\n        -------\n        A string containing the installation UUID, or None in case of an error.\n        \"\"\"\n        if self._installation_id:\n            return self._installation_id\n        try:\n            self._installation_id = self._get_or_set_uuid(INSTALLATION_ID_KEY)\n            return self._installation_id\n        except (ValueError, IOError, OSError):\n            return None\n\n    @property\n    def telemetry_enabled(self):\n        \"\"\"\n        Check if telemetry is enabled for this installation. Default value of\n        False. It first tries to get value from SAM_CLI_TELEMETRY environment variable. If its not set,\n        then it fetches the value from config file.\n\n        To enable telemetry, set SAM_CLI_TELEMETRY environment variable equal to integer 1 or string '1'.\n        All other values including words like 'True', 'true', 'false', 'False', 'abcd' etc will disable Telemetry\n\n        Examples\n        --------\n\n        >>> gc = GlobalConfig()\n        >>> gc.telemetry_enabled\n        True\n\n        Returns\n        -------\n        Boolean flag value. True if telemetry is enabled for this installation,\n        False otherwise.\n        \"\"\"\n        if self._telemetry_enabled is not None:\n            return self._telemetry_enabled\n\n        # If environment variable is set, its value takes precedence over the value from config file.\n        env_name = \"SAM_CLI_TELEMETRY\"\n        if env_name in os.environ:\n            return os.getenv(env_name) in ('1', 1)\n\n        try:\n            self._telemetry_enabled = self._get_value(TELEMETRY_ENABLED_KEY)\n            return self._telemetry_enabled\n        except (ValueError, IOError, OSError) as ex:\n            LOG.debug(\"Error when retrieving telemetry_enabled flag\", exc_info=ex)\n            return False\n\n    @telemetry_enabled.setter\n    def telemetry_enabled(self, value):\n        \"\"\"\n        Sets the telemetry_enabled flag to the provided boolean value.\n\n        Examples\n        --------\n        >>> gc = GlobalConfig()\n        >>> gc.telemetry_enabled\n        False\n        >>> gc.telemetry_enabled = True\n        >>> gc.telemetry_enabled\n        True\n\n        Raises\n        ------\n        IOError\n            If there are errors opening or writing to the global config file.\n\n        JSONDecodeError\n            If the config file exists, and is not valid JSON.\n        \"\"\"\n        self._set_value(\"telemetryEnabled\", value)\n        self._telemetry_enabled = value\n\n    def _get_value(self, key):\n        cfg_path = self._get_config_file_path(CONFIG_FILENAME)\n        if not cfg_path.exists():\n            return None\n        with open(str(cfg_path)) as fp:\n            body = fp.read()\n            json_body = json.loads(body)\n            return json_body.get(key)\n\n    def _set_value(self, key, value):\n        cfg_path = self._get_config_file_path(CONFIG_FILENAME)\n        if not cfg_path.exists():\n            return self._set_json_cfg(cfg_path, key, value)\n        with open(str(cfg_path)) as fp:\n            body = fp.read()\n            try:\n                json_body = json.loads(body)\n            except ValueError as ex:\n                LOG.debug(\"Failed to decode JSON in {cfg_path}\", exc_info=ex)\n                raise ex\n            return self._set_json_cfg(cfg_path, key, value, json_body)\n\n    def _create_dir(self):\n        \"\"\"\n        Creates configuration directory if it does not already exist, otherwise does nothing.\n        May raise an OSError if we do not have permissions to create the directory.\n        \"\"\"\n        self.config_dir.mkdir(mode=0o700, parents=True, exist_ok=True)\n\n    def _get_config_file_path(self, filename):\n        self._create_dir()\n        filepath = self.config_dir.joinpath(filename)\n        return filepath\n\n    def _get_or_set_uuid(self, key):\n        \"\"\"\n        Special logic method for when we want a UUID to always be present, this\n        method behaves as a getter with side effects. Essentially, if the value\n        is not present, we will set it with a generated UUID.\n\n        If we have multiple such values in the future, a possible refactor is\n        to just be _get_or_set_value, where we also take a default value as a\n        parameter.\n        \"\"\"\n        cfg_value = self._get_value(key)\n        if cfg_value is not None:\n            return cfg_value\n        return self._set_value(key, str(uuid.uuid4()))\n\n    def _set_json_cfg(self, filepath, key, value, json_body=None):\n        \"\"\"\n        Special logic method to add a value to a JSON configuration file. This\n        method will write a new version of the file in question, so it will\n        either write a new file with only the first config value, or if a JSON\n        body is provided, it will upsert starting from that JSON body.\n        \"\"\"\n        json_body = json_body or {}\n        json_body[key] = value\n        file_body = json.dumps(json_body, indent=4) + \"\\n\"\n        try:\n            with open(str(filepath), 'w') as f:\n                f.write(file_body)\n        except IOError as ex:\n            LOG.debug(\"Error writing to {filepath}\", exc_info=ex)\n            raise ex\n        return value\n", "path": "samcli/cli/global_config.py"}]}