Skip to content

Commit cbc3a03

Browse files
Use Terraform Registry download URL (claranet#3)
Use Terraform Registry download URL The version tags used on the Terraform Registry don't necessarily match those in the repo, e.g. `0.7.0` compared to `v0.7.0`. This change uses the Terraform Registry download URL to determine the GitHub download URL, and grabs the repo version tag from that. Another change is to make local filesystem modules only work if they start with certain characters, and to not check if they exist or not. If they don't exist, it will error. The paths must also be relative to the Terrafile (pterrafile can run from a different directory, so behaviour would have been inconsistent).
1 parent 3df54fc commit cbc3a03

File tree

2 files changed

+63
-35
lines changed

2 files changed

+63
-35
lines changed

README.md

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,7 @@ Manages external Terraform modules, controlled by a `Terrafile`.
44

55
This is basically a Python version of the tool described at [http://bensnape.com/2016/01/14/terraform-design-patterns-the-terrafile/](http://bensnape.com/2016/01/14/terraform-design-patterns-the-terrafile/)
66

7-
Additionally pterrafile supports modules from Terraform Registry as well as modules in local directories, identified by a relative path starting with either `./` or `../`.
8-
9-
10-
```shell
11-
# registry module
12-
tf-aws-lambda:
13-
source: "claranet/lambda/aws"
14-
version: "v0.1.0"
15-
16-
# local module
17-
tf-aws-test:
18-
source: "../../modules/tf-aws-test"
19-
```
7+
Additionally, python-terrafile supports modules from the Terraform Registry, as well as modules in local directories identified by a relative path starting with either `./` or `../` or an absolute path starting with `/`.
208

219
## Installation
2210

@@ -31,3 +19,26 @@ pterrafile [path]
3119
```
3220

3321
If `path` is provided, it must be the path to a `Terrafile` file, or a directory containing one. If not provided, it looks for the file in the current working directory.
22+
23+
## Examples
24+
25+
```yaml
26+
# Terraform Registry module
27+
terraform-aws-lambda:
28+
source: "claranet/lambda/aws"
29+
version: "0.7.0"
30+
31+
# Git module (HTTPS)
32+
terraform-aws-lambda:
33+
source: "https://github.com/claranet/terraform-aws-lambda.git"
34+
version: "v0.7.0"
35+
36+
# Git module (SSH)
37+
terraform-aws-lambda:
38+
source: "git@github.com:claranet/terraform-aws-lambda.git"
39+
version: "v0.7.0"
40+
41+
# Local directory module
42+
terraform-aws-lambda:
43+
source: "../../modules/terraform-aws-lambda"
44+
```

terrafile/__init__.py

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,36 @@
1-
import errno
21
import os
2+
import re
3+
import requests
34
import shutil
45
import subprocess
56
import sys
67
import yaml
7-
import requests
8-
import re
98

109

11-
REGISTRY_BASE_URL = 'https://registry.terraform.io/v1/modules/'
10+
REGISTRY_BASE_URL = 'https://registry.terraform.io/v1/modules'
11+
GITHUB_DOWNLOAD_URL_RE = re.compile('https://[^/]+/repos/([^/]+)/([^/]+)/tarball/([^/]+)/.*')
12+
13+
14+
def get_source_from_registry(source, version):
15+
namespace, name, provider = source.split('/')
16+
registry_download_url = '{base_url}/{namespace}/{name}/{provider}/{version}/download'.format(
17+
base_url=REGISTRY_BASE_URL,
18+
namespace=namespace,
19+
name=name,
20+
provider=provider,
21+
version=version,
22+
)
23+
response = requests.get(registry_download_url)
24+
if response.status_code == 204:
25+
github_download_url = response.headers.get('X-Terraform-Get') or ''
26+
match = GITHUB_DOWNLOAD_URL_RE.match(github_download_url)
27+
if match:
28+
user, repo, version = match.groups()
29+
source = 'https://github.com/{}/{}.git'.format(user, repo)
30+
return source, version
31+
sys.stderr.write('Error looking up module in Terraform Registry: {}\n'.format(response.content))
32+
sys.exit(1)
1233

13-
def get_source_from_registry(module_name):
14-
response = requests.get('{}{}'.format(REGISTRY_BASE_URL, module_name))
15-
data = response.json()
16-
if 'errors' not in data.keys():
17-
return data['source']
18-
else:
19-
sys.stderr.write('Error looking up module in Terraform Registry: {}\n'.format(data['errors']))
20-
sys.exit(1)
2134

2235
def run(*args, **kwargs):
2336
proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs)
@@ -76,27 +89,31 @@ def update_modules(path):
7689

7790
for name, repository_details in sorted(terrafile.items()):
7891
target = os.path.join(module_path, name)
79-
raw_source = repository_details['source']
80-
if is_valid_registry_source(raw_source):
81-
source = get_source_from_registry(raw_source)
82-
elif os.path.isdir(raw_source):
83-
source = os.path.abspath(raw_source)
92+
source = repository_details['source']
93+
94+
# Support modules on the local filesystem.
95+
if source.startswith('./') or source.startswith('../') or source.startswith('/'):
96+
print('Copying {}/{}'.format(module_path_name, name))
97+
# Paths must be relative to the Terrafile directory.
98+
source = os.path.join(module_path, source)
8499
shutil.rmtree(target, ignore_errors=True)
85-
print('Fetching {}/{}'.format(module_path_name, name))
86100
shutil.copytree(source, target)
87101
continue
88-
else:
89-
source = raw_source
102+
90103
version = repository_details['version']
91104

105+
# Support Terraform Registry sources.
106+
if is_valid_registry_source(source):
107+
print('Checking {}/{}'.format(module_path_name, name))
108+
source, version = get_source_from_registry(source, version)
109+
92110
# Skip this module if it has already been checked out.
93111
if has_git_tag(path=target, tag=version):
94112
print('Fetched {}/{}'.format(module_path_name, name))
95113
continue
96114

97-
print('Fetching {}/{}'.format(module_path_name, name))
98-
99115
# Delete the old directory and clone it from scratch.
116+
print('Fetching {}/{}'.format(module_path_name, name))
100117
shutil.rmtree(target, ignore_errors=True)
101118
output, returncode = run('git', 'clone', '--branch={}'.format(version), source, target)
102119
if returncode != 0:

0 commit comments

Comments
 (0)