-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Dan Ellis
committed
Jun 17, 2015
1 parent
2b19d5e
commit 3d5a601
Showing
9 changed files
with
298 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
*.sw? | ||
*.egg-info* | ||
*.pyc | ||
/dist/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
import difflib | ||
from itertools import islice | ||
import re | ||
import subprocess | ||
|
||
from ansible.utils.vault import VaultLib | ||
|
||
from utils import get_vault_password, green, red, cyan, intense | ||
|
||
|
||
def get_parts(git_diff_output): | ||
r = re.compile(r"^diff --git", re.MULTILINE) | ||
parts = [] | ||
locations = [i.start() for i in r.finditer(git_diff_output)] | ||
for i, location in enumerate(locations): | ||
is_last_item = i + 1 == len(locations) | ||
next_location = None if is_last_item else locations[i + 1] | ||
parts.append(git_diff_output[location:next_location]) | ||
return parts | ||
|
||
|
||
def get_old_sha(diff_part): | ||
""" | ||
Returns the SHA for the original file that was changed in a diff part. | ||
""" | ||
r = re.compile(r'index ([a-fA-F\d]*)') | ||
return r.search(diff_part).groups()[0] | ||
|
||
|
||
def get_old_filename(diff_part): | ||
""" | ||
Returns the filename for the original file that was changed in a diff part. | ||
""" | ||
r = re.compile(r'^--- a/(.*)', re.MULTILINE) | ||
return r.search(diff_part).groups()[0] | ||
|
||
|
||
def get_old_contents(sha, filename): | ||
return subprocess.check_output(['git', 'show', sha, '--', filename]) | ||
|
||
|
||
def get_new_filename(diff_part): | ||
""" | ||
Returns the filename for the updated file in a diff part. | ||
""" | ||
r = re.compile(r'^\+\+\+ b/(.*)', re.MULTILINE) | ||
return r.search(diff_part).groups()[0] | ||
|
||
|
||
def get_new_contents(filename): | ||
with open(filename, 'rb') as f: | ||
return f.read() | ||
|
||
|
||
def get_head(diff_part): | ||
""" | ||
Returns the pre-content, non-chunk headers of a diff part. | ||
E.g. | ||
diff --git a/group_vars/foo b/group_vars/foo | ||
index 6b9eef7..eb9fb09 100644 | ||
--- a/group_vars/foo | ||
+++ b/group_vars/foo | ||
""" | ||
return '\n'.join(diff_part.split('\n')[:4]) + '\n' | ||
|
||
|
||
def get_contents(diff_part): | ||
""" | ||
Returns a tuple of old content and new content. | ||
""" | ||
old_sha = get_old_sha(diff_part) | ||
old_filename = get_old_filename(diff_part) | ||
old_contents = get_old_contents(old_sha, old_filename) | ||
new_filename = get_new_filename(diff_part) | ||
new_contents = get_new_contents(new_filename) | ||
return old_contents, new_contents | ||
|
||
|
||
def decrypt_diff(diff_part, password_file=None): | ||
""" | ||
Diff part is a string in the format: | ||
diff --git a/group_vars/foo b/group_vars/foo | ||
index c09080b..0d803bb 100644 | ||
--- a/group_vars/foo | ||
+++ b/group_vars/foo | ||
@@ -1,32 +1,33 @@ | ||
$ANSIBLE_VAULT;1.1;AES256 | ||
-61316662363730313230626432303662316330323064373866616436623565613033396539366263 | ||
-383632656663356364656531653039333965 | ||
+30393563383639396563623339383936613866326332383162306532653239636166633162323236 | ||
+62376161626137626133 | ||
Returns a tuple of decrypted old contents and decrypted new contents. | ||
""" | ||
vault = VaultLib(get_vault_password(password_file)) | ||
old_contents, new_contents = get_contents(diff_part) | ||
if vault.is_encrypted(old_contents): | ||
old_contents = vault.decrypt(old_contents) | ||
if vault.is_encrypted(new_contents): | ||
new_contents = vault.decrypt(new_contents) | ||
return old_contents, new_contents | ||
|
||
|
||
def show_unencrypted_diff(diff_part, password_file=None): | ||
intense(get_head(diff_part).strip()) | ||
old, new = decrypt_diff(diff_part, password_file) | ||
diff = difflib.unified_diff(old.split('\n'), new.split('\n'), lineterm='') | ||
# ... we'll take the git filenames from git's diff output rather than | ||
# ... difflib | ||
for line in islice(diff, 2, None): | ||
if line.startswith('-'): | ||
red(line) | ||
elif line.startswith('+'): | ||
green(line) | ||
elif line.startswith('@@'): | ||
cyan(line) | ||
else: | ||
print line | ||
|
||
|
||
def show_unencrypted_diffs(git_diff_output, password_file=None): | ||
parts = get_parts(git_diff_output) | ||
for part in parts: | ||
show_unencrypted_diff(part, password_file) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import tempfile | ||
import unittest | ||
|
||
from ansible_toolkit import git_diff | ||
|
||
|
||
VAULT_PASSWORD = "foo" | ||
|
||
|
||
# Vaulted Files | ||
|
||
OLD_FILE_1 = """$ANSIBLE_VAULT;1.1;AES256 | ||
35626233663138316665643331653539633239313534376130633265353137313531656664613539 | ||
6466346666333332636231356362323834616462323161610a613533363462663730366462633466 | ||
36303466376432323137383661653036663338343830666264343562643833313931616165653463 | ||
6637633063376664360a643964336134383034343665383730623064353338366436326135366265 | ||
3038""" | ||
|
||
OLD_FILE_1_DECRYPTED = """foo | ||
""" | ||
|
||
NEW_FILE_1 = """$ANSIBLE_VAULT;1.1;AES256 | ||
39373663313137383662393466386133396438366235323234336365623535363133353631366566 | ||
3565616539353938343863633635626334636532393936390a353430663164393034616161356237 | ||
34386335363662623266646637653135613337666636323834313764303766306131663334336436 | ||
6633623031353931640a626366353232613963303939303130313132666437346163363535663265 | ||
6535""" | ||
|
||
NEW_FILE_1_DECRYPTED = """bar | ||
""" | ||
|
||
|
||
# Git Diff Output | ||
|
||
DIFF_HEAD_1 = """diff --git a/group_vars/foo b/group_vars/foo | ||
index c09080b..0d803bb 100644 | ||
--- a/group_vars/foo | ||
+++ b/group_vars/foo | ||
""" | ||
|
||
SAMPLE_DIFF = DIFF_HEAD_1 | ||
|
||
SAMPLE_DIFF += """@@ -1,32 +1,33 @@ | ||
ANSIBLE_VAULT;1.1;AES256""" | ||
|
||
SAMPLE_DIFF += ''.join('\n-' + i for i in OLD_FILE_1.split('\n')[1:]) | ||
SAMPLE_DIFF += ''.join('\n+' + i for i in NEW_FILE_1.split('\n')[1:]) | ||
|
||
# ... another section to make sure we can handle multiple parts | ||
|
||
SAMPLE_DIFF += """ | ||
diff --git a/group_vars/bar b/group_vars/bar | ||
index 6b9eef7..eb9fb09 100644 | ||
--- a/group_vars/bar | ||
+++ b/group_vars/bar | ||
@@ -1,22 +1,23 @@ | ||
$ANSIBLE_VAULT;1.1;AES256 | ||
-32346330646639326335373939383634656365376531353531306238616239626265313963613561 | ||
-61393637373834646566353739393762306436393234636438323434626666366136 | ||
+65393432336536653066303736336632356364306533643131656461316332353138316239336137 | ||
+3038396139303439356236343161396331353332326232626566""" | ||
|
||
|
||
class TestGitDiff(unittest.TestCase): | ||
|
||
def test_get_parts(self): | ||
parts = git_diff.get_parts(SAMPLE_DIFF) | ||
self.assertEqual(len(parts), 2) | ||
|
||
def test_get_old_sha(self): | ||
parts = git_diff.get_parts(SAMPLE_DIFF) | ||
old_sha = git_diff.get_old_sha(parts[0]) | ||
self.assertEqual(old_sha, 'c09080b') | ||
|
||
def test_get_old_filename(self): | ||
parts = git_diff.get_parts(SAMPLE_DIFF) | ||
old_filename = git_diff.get_old_filename(parts[0]) | ||
self.assertEqual(old_filename, 'group_vars/foo') | ||
|
||
def test_decrypt_diff(self): | ||
|
||
# Monkey-patch | ||
_get_old_contents = git_diff.get_old_contents | ||
def get_old_contents(*args): # noqa | ||
return OLD_FILE_1 | ||
git_diff.get_old_contents = get_old_contents | ||
_get_new_contents = git_diff.get_new_contents | ||
def get_new_contents(*args): # noqa | ||
return NEW_FILE_1 | ||
git_diff.get_new_contents = get_new_contents | ||
|
||
# Test decryption | ||
try: | ||
|
||
# ... create temporary vault file | ||
f = tempfile.NamedTemporaryFile() | ||
f.write(VAULT_PASSWORD) | ||
f.seek(0) | ||
|
||
# ... decrypt the diff | ||
parts = git_diff.get_parts(SAMPLE_DIFF) | ||
old, new = git_diff.decrypt_diff( | ||
parts[0], password_file=f.name) | ||
self.assertEqual(old, OLD_FILE_1_DECRYPTED) | ||
self.assertEqual(new, NEW_FILE_1_DECRYPTED) | ||
|
||
# Restore monkey-patched functions | ||
finally: | ||
git_diff.get_old_contents = _get_old_contents | ||
git_diff.get_new_contents = _get_new_contents | ||
|
||
def test_get_head(self): | ||
parts = git_diff.get_parts(SAMPLE_DIFF) | ||
head = git_diff.get_head(parts[0]) | ||
self.assertEqual(head, DIFF_HEAD_1) | ||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
#!/usr/bin/env python | ||
|
||
import argparse | ||
import subprocess | ||
import sys | ||
|
||
from ansible_toolkit.git_diff import show_unencrypted_diffs | ||
|
||
|
||
if __name__ == '__main__': | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument('-p', '--vault-password-file', type=str, | ||
help="Path to vault password file") | ||
args = parser.parse_args() | ||
output = subprocess.check_output(['git', 'diff']) | ||
show_unencrypted_diffs(output, args.vault_password_file) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters