Repository: weaveworks/kubediff

Issue:
TypeError on SealedSecret
Reported in PR #78 

error below for e.g. `SealedSecret.v1alpha1.bitnami.com`

```
Traceback (most recent call last):
  File "/Users/po/src/kubediff/kubediff", line 48, in <module>
    main()
  File "/Users/po/src/kubediff/kubediff", line 42, in main
    failed = check_files(args, printer, config)
  File "/Users/po/src/kubediff/kubedifflib/_diff.py", line 246, in check_files
    differences += check_file(printer, path, config)
  File "/Users/po/src/kubediff/kubedifflib/_diff.py", line 174, in check_file
    printer.diff(path, difference)
  File "/Users/po/src/kubediff/kubedifflib/_diff.py", line 214, in diff
    self._write('%s', difference.to_text(self._current.kind))
  File "/Users/po/src/kubediff/kubedifflib/_diff.py", line 35, in to_text
    message = self.message % ((len(self.args[0]) * '*'), (len(self.args[1]) * '*'))
TypeError: object of type 'NoneType' has no len()
```

Discussion:

Patch:
diff --git a/kubedifflib/_diff.py b/kubedifflib/_diff.py
index 03a6337..e51b8f6 100644
--- a/kubedifflib/_diff.py
+++ b/kubedifflib/_diff.py
@@ -21,6 +21,12 @@ from ._kube import (
   iter_files,
 )
 
+def mask(x):
+  """Turn a string into an equal-length string of asterisks"""
+  try:
+    return len(x) * '*'
+  except TypeError: # not a string - perhaps None - just return it as-is
+    return x
 
 class Difference(object):
   """An observed difference."""
@@ -32,7 +38,7 @@ class Difference(object):
 
   def to_text(self, kind=''):
     if 'secret' in kind.lower() and len(self.args) == 2:
-      message = self.message % ((len(self.args[0]) * '*'), (len(self.args[1]) * '*'))
+      message = self.message % (mask(self.args[0]), mask(self.args[1]))
 
     else:
       message = self.message % self.args


Test patch:
diff --git a/kubedifflib/tests/test_diff.py b/kubedifflib/tests/test_diff.py
index fa5a683..575fffb 100644
--- a/kubedifflib/tests/test_diff.py
+++ b/kubedifflib/tests/test_diff.py
@@ -2,10 +2,10 @@ import copy
 
 import random
 
-from hypothesis import given
+from hypothesis import given, example
 from hypothesis.strategies import integers, lists, text, fixed_dictionaries, sampled_from, none, one_of
 
-from kubedifflib._diff import diff_lists, list_subtract
+from kubedifflib._diff import diff_lists, list_subtract, Difference
 from kubedifflib._kube import KubeObject
 
 
@@ -137,3 +137,16 @@ def test_from_dict_kubernetes_list_type(data):
 def test_from_dict_kubernetes_obj_type(data):
     """KubeObject.from_dict parses regular kubernetes objects."""
     assert [kube_obj.data for kube_obj in KubeObject.from_dict(data)] == [data]
+
+@given(path=text(), kind=text())
+def test_difference_no_args(path, kind):
+    """Difference.to_text works as expected when no args passed."""
+    d = Difference("Message", path)
+    assert d.to_text(kind) == path + ": Message"
+
+@given(path=text(), kind=text(), arg1=text(), arg2=one_of(text(), none()))
+@example("somepath","Secret", "foo", None)
+def test_difference_two_args(path, kind, arg1, arg2):
+    """Difference.to_text works when two args passed, that may be 'none'."""
+    d = Difference("Message %s %s", path, arg1, arg2)
+    assert d.to_text(kind) != ""