From 57ee56533f9a7854475d8e48ea307c99e15f8fd3 Mon Sep 17 00:00:00 2001
From: Brian Warner <warner@lothar.com>
Date: Thu, 13 Oct 2011 20:15:00 -0700
Subject: [PATCH] add --format= to 'tahoe put'/'mkdir', remove --mutable-type.
 Closes #1561

---
 docs/frontends/CLI.rst               |  14 ++--
 src/allmydata/scripts/cli.py         |  25 ++----
 src/allmydata/scripts/tahoe_mkdir.py |   8 +-
 src/allmydata/scripts/tahoe_put.py   |  15 ++--
 src/allmydata/test/check_grid.py     |   6 +-
 src/allmydata/test/test_cli.py       | 120 +++++++++++++++------------
 src/allmydata/test/test_system.py    |   2 +-
 7 files changed, 97 insertions(+), 93 deletions(-)

diff --git a/docs/frontends/CLI.rst b/docs/frontends/CLI.rst
index 7aa27979..ed8ddf65 100644
--- a/docs/frontends/CLI.rst
+++ b/docs/frontends/CLI.rst
@@ -261,9 +261,9 @@ Command Syntax Summary
 
 ``tahoe webopen [PATH]``
 
-``tahoe put [--mutable] [FROMLOCAL|-]``
+``tahoe put [--format=FORMAT] [FROMLOCAL|-]``
 
-``tahoe put [--mutable] FROMLOCAL|- TOPATH``
+``tahoe put [--format=FORMAT] FROMLOCAL|- TOPATH``
 
 ``tahoe put [FROMLOCAL|-] mutable-file-writecap``
 
@@ -394,15 +394,15 @@ Command Examples
  from the following path. When the source file is named "``-``", the contents
  are taken from stdin.
 
-``tahoe put file.txt --mutable``
+``tahoe put file.txt --format=SDMF``
 
- Create a new mutable file, fill it with the contents of ``file.txt``, and
- print the new write-cap to stdout.
+ Create a new (SDMF) mutable file, fill it with the contents of ``file.txt``,
+ and print the new write-cap to stdout.
 
 ``tahoe put file.txt MUTABLE-FILE-WRITECAP``
 
- Replace the contents of the given mutable file with the contents of ``file.txt``
- and prints the same write-cap to stdout.
+ Replace the contents of the given mutable file with the contents of
+ ``file.txt`` and prints the same write-cap to stdout.
 
 ``tahoe cp file.txt tahoe:uploaded.txt``
 
diff --git a/src/allmydata/scripts/cli.py b/src/allmydata/scripts/cli.py
index 970c8c40..950f6a79 100644
--- a/src/allmydata/scripts/cli.py
+++ b/src/allmydata/scripts/cli.py
@@ -51,17 +51,15 @@ class VDriveOptions(BaseOptions):
 
 class MakeDirectoryOptions(VDriveOptions):
     optParameters = [
-        ("mutable-type", None, None, "Create a mutable directory in the given format. "
-                                     "Valid formats are SDMF and MDMF, case-insensitive."),
+        ("format", None, None, "Create directory with the given format: SDMF and MDMF for mutable. (case-insensitive)"),
         ]
 
     def parseArgs(self, where=""):
         self.where = argv_to_unicode(where)
 
-        if self['mutable-type']:
-            if self['mutable-type'].lower() not in ("sdmf", "mdmf"):
-                raise usage.UsageError("%s is an invalid format" % self['mutable-type'])
-            self['mutable-type'] = self['mutable-type'].lower()
+        if self['format']:
+            if self['format'].upper() not in ("SDMF", "MDMF", "CHK"):
+                raise usage.UsageError("%s is an invalid format" % self['format'])
 
     def getSynopsis(self):
         return "Usage:  %s mkdir [options] [REMOTE_DIR]" % (self.command_name,)
@@ -172,11 +170,10 @@ Examples:
 
 class PutOptions(VDriveOptions):
     optFlags = [
-        ("mutable", "m", "Create a mutable file instead of an immutable one."),
+        ("mutable", "m", "Create a mutable file instead of an immutable one. (DEPRECATED, use --format=SDMF)"),
         ]
     optParameters = [
-        ("mutable-type", None, None, "Create a mutable file in the given format (implies --mutable). "
-                                     "Valid formats are SDMF and MDMF, case-insensitive."),
+        ("format", None, None, "Create file with the given format: SDMF and MDMF for mutable, CHK (default) for immutable. (case-insensitive)"),
         ]
 
     def parseArgs(self, arg1=None, arg2=None):
@@ -194,13 +191,9 @@ class PutOptions(VDriveOptions):
         if self.from_file == u"-":
             self.from_file = None
 
-        if self['mutable-type']:
-            if self['mutable-type'].lower() not in ("sdmf", "mdmf"):
-                raise usage.UsageError("%s is an invalid format" % self['mutable-type'])
-            self['mutable-type'] = self['mutable-type'].lower()
-
-        if self['mutable-type']:
-            self['mutable'] = True
+        if self['format']:
+            if self['format'].upper() not in ("SDMF", "MDMF", "CHK"):
+                raise usage.UsageError("%s is an invalid format" % self['format'])
 
     def getSynopsis(self):
         return "Usage:  %s put [options] LOCAL_FILE REMOTE_FILE" % (self.command_name,)
diff --git a/src/allmydata/scripts/tahoe_mkdir.py b/src/allmydata/scripts/tahoe_mkdir.py
index cc0374bf..9820ada7 100644
--- a/src/allmydata/scripts/tahoe_mkdir.py
+++ b/src/allmydata/scripts/tahoe_mkdir.py
@@ -22,8 +22,8 @@ def mkdir(options):
     if not where or not path:
         # create a new unlinked directory
         url = nodeurl + "uri?t=mkdir"
-        if options["mutable-type"]:
-            url += "&format=%s" % urllib.quote(options['mutable-type'])
+        if options["format"]:
+            url += "&format=%s" % urllib.quote(options['format'])
         resp = do_http("POST", url)
         rc = check_http_error(resp, stderr)
         if rc:
@@ -39,8 +39,8 @@ def mkdir(options):
     # path must be "/".join([s.encode("utf-8") for s in segments])
     url = nodeurl + "uri/%s/%s?t=mkdir" % (urllib.quote(rootcap),
                                            urllib.quote(path))
-    if options['mutable-type']:
-        url += "&format=%s" % urllib.quote(options['mutable-type'])
+    if options['format']:
+        url += "&format=%s" % urllib.quote(options['format'])
 
     resp = do_http("POST", url)
     check_http_error(resp, stderr)
diff --git a/src/allmydata/scripts/tahoe_put.py b/src/allmydata/scripts/tahoe_put.py
index 3a3b3945..a85539ef 100644
--- a/src/allmydata/scripts/tahoe_put.py
+++ b/src/allmydata/scripts/tahoe_put.py
@@ -18,7 +18,7 @@ def put(options):
     from_file = options.from_file
     to_file = options.to_file
     mutable = options['mutable']
-    mutable_type = options['mutable-type']
+    format = options['format']
     if options['quiet']:
         verbosity = 0
     else:
@@ -64,14 +64,13 @@ def put(options):
         # unlinked upload
         url = nodeurl + "uri"
 
-    file_format = None
+    queryargs = []
     if mutable:
-        file_format = "SDMF"
-    if mutable_type:
-        assert mutable
-        file_format = mutable_type.upper()
-    if file_format:
-        url += "?format=%s" % file_format
+        queryargs.append("mutable=true")
+    if format:
+        queryargs.append("format=%s" % format)
+    if queryargs:
+        url += "?" + "&".join(queryargs)
 
     if from_file:
         infileobj = open(os.path.expanduser(from_file), "rb")
diff --git a/src/allmydata/test/check_grid.py b/src/allmydata/test/check_grid.py
index fbe8f5f1..a9d362bd 100644
--- a/src/allmydata/test/check_grid.py
+++ b/src/allmydata/test/check_grid.py
@@ -30,8 +30,8 @@ To set up the client node, do the following:
   tahoe put -d DIR FILE testgrid:old.MD5SUM
   tahoe put -d DIR FILE testgrid:recent.MD5SUM
   tahoe put -d DIR FILE testgrid:recentdir/recent.MD5SUM
-  echo "" | tahoe put -d DIR --mutable testgrid:log
-  echo "" | tahoe put -d DIR --mutable testgrid:recentlog
+  echo "" | tahoe put -d DIR --format=SDMF testgrid:log
+  echo "" | tahoe put -d DIR --format=SDMF testgrid:recentlog
 
 This script will perform the following steps (the kind of compatibility that
 is being tested is in [brackets]):
@@ -189,7 +189,7 @@ class GridTester:
         self.cli("put", "-", "testgrid:"+fn, stdin=data, ignore_stderr=True)
 
     def put_mutable(self, fn, data):
-        self.cli("put", "--mutable", "-", "testgrid:"+fn,
+        self.cli("put", "--format=SDMF", "-", "testgrid:"+fn,
                  stdin=data, ignore_stderr=True)
 
     def update(self, fn):
diff --git a/src/allmydata/test/test_cli.py b/src/allmydata/test/test_cli.py
index f9acbc06..464e2b56 100644
--- a/src/allmydata/test/test_cli.py
+++ b/src/allmydata/test/test_cli.py
@@ -1160,67 +1160,79 @@ class Put(GridTestMixin, CLITestMixin, unittest.TestCase):
         self.failUnlessIn("URI:SSK-RO", json)
         self.failUnlessIn("URI:SSK-Verifier", json)
 
-    def test_mutable_type(self):
-        self.basedir = "cli/Put/mutable_type"
+    def _check_chk_json(self, (rc, json, err)):
+        self.failUnlessEqual(rc, 0)
+        self.failUnlessEqual(err, "")
+        self.failUnlessIn('"format": "CHK"', json)
+        # We also want to see the appropriate CHK caps.
+        self.failUnlessIn("URI:CHK", json)
+        self.failUnlessIn("URI:CHK-Verifier", json)
+
+    def test_format(self):
+        self.basedir = "cli/Put/format"
         self.set_up_grid()
-        data = "data" * 100000
+        data = "data" * 40000 # 160kB total, two segments
         fn1 = os.path.join(self.basedir, "data")
         fileutil.write(fn1, data)
         d = self.do_cli("create-alias", "tahoe")
 
-        def _put_and_ls(ign, mutable_type, filename):
-            d2 = self.do_cli("put", "--mutable", "--mutable-type="+mutable_type,
-                             fn1, filename)
-            def _dont_fail((rc, out, err)):
-                self.failUnlessEqual(rc, 0)
-            d2.addCallback(_dont_fail)
-            d2.addCallback(lambda ign: self.do_cli("ls", "--json", filename))
+        def _put_and_ls(ign, cmdargs, expected, filename=None):
+            if filename:
+                args = ["put"] + cmdargs + [fn1, filename]
+            else:
+                # unlinked
+                args = ["put"] + cmdargs + [fn1]
+            d2 = self.do_cli(*args)
+            def _list((rc, out, err)):
+                self.failUnlessEqual(rc, 0) # don't allow failure
+                if filename:
+                    return self.do_cli("ls", "--json", filename)
+                else:
+                    cap = out.strip()
+                    return self.do_cli("ls", "--json", cap)
+            d2.addCallback(_list)
             return d2
 
-        d.addCallback(_put_and_ls, "mdmf", "tahoe:uploaded.txt")
-        d.addCallback(self._check_mdmf_json)
-        d.addCallback(_put_and_ls, "MDMF", "tahoe:uploaded2.txt")
-        d.addCallback(self._check_mdmf_json)
-        d.addCallback(_put_and_ls, "sdmf", "tahoe:uploaded3.txt")
+        # 'tahoe put' to a directory
+        d.addCallback(_put_and_ls, ["--mutable"], "SDMF", "tahoe:s1.txt")
+        d.addCallback(self._check_sdmf_json) # backwards-compatibility
+        d.addCallback(_put_and_ls, ["--format=SDMF"], "SDMF", "tahoe:s2.txt")
         d.addCallback(self._check_sdmf_json)
-        d.addCallback(_put_and_ls, "SDMF", "tahoe:uploaded4.txt")
+        d.addCallback(_put_and_ls, ["--format=sdmf"], "SDMF", "tahoe:s3.txt")
+        d.addCallback(self._check_sdmf_json)
+        d.addCallback(_put_and_ls, ["--mutable", "--format=SDMF"], "SDMF", "tahoe:s4.txt")
         d.addCallback(self._check_sdmf_json)
-        return d
 
-    def test_mutable_type_unlinked(self):
-        self.basedir = "cli/Put/mutable_type_unlinked"
-        self.set_up_grid()
-        data = "data" * 100000
-        fn1 = os.path.join(self.basedir, "data")
-        fileutil.write(fn1, data)
-        d = self.do_cli("put", "--mutable", "--mutable-type=mdmf", fn1)
-        d.addCallback(lambda (rc, cap, err):
-            self.do_cli("ls", "--json", cap))
+        d.addCallback(_put_and_ls, ["--format=MDMF"], "MDMF", "tahoe:m1.txt")
         d.addCallback(self._check_mdmf_json)
-        d.addCallback(lambda ignored:
-            self.do_cli("put", "--mutable", "--mutable-type=sdmf", fn1))
-        d.addCallback(lambda (rc, cap, err):
-            self.do_cli("ls", "--json", cap))
+        d.addCallback(_put_and_ls, ["--mutable", "--format=MDMF"], "MDMF", "tahoe:m2.txt")
+        d.addCallback(self._check_mdmf_json)
+
+        d.addCallback(_put_and_ls, ["--format=CHK"], "CHK", "tahoe:c1.txt")
+        d.addCallback(self._check_chk_json)
+        d.addCallback(_put_and_ls, [], "CHK", "tahoe:c1.txt")
+        d.addCallback(self._check_chk_json)
+
+        # 'tahoe put' unlinked
+        d.addCallback(_put_and_ls, ["--mutable"], "SDMF")
+        d.addCallback(self._check_sdmf_json) # backwards-compatibility
+        d.addCallback(_put_and_ls, ["--format=SDMF"], "SDMF")
+        d.addCallback(self._check_sdmf_json)
+        d.addCallback(_put_and_ls, ["--format=sdmf"], "SDMF")
+        d.addCallback(self._check_sdmf_json)
+        d.addCallback(_put_and_ls, ["--mutable", "--format=SDMF"], "SDMF")
         d.addCallback(self._check_sdmf_json)
-        return d
 
-    def test_mutable_type_implies_mutable(self):
-        self.basedir = "cli/Put/mutable_type_implies_mutable"
-        self.set_up_grid()
-        data = "data" * 100000
-        fn1 = os.path.join(self.basedir, "data")
-        fileutil.write(fn1, data)
-        d = self.do_cli("put", "--mutable-type=mdmf", fn1)
-        d.addCallback(lambda (rc, cap, err):
-            self.do_cli("ls", "--json", cap))
-        # This will fail if an immutable file is created instead of a
-        # mutable file.
+        d.addCallback(_put_and_ls, ["--format=MDMF"], "MDMF")
         d.addCallback(self._check_mdmf_json)
-        d.addCallback(lambda ignored:
-            self.do_cli("put", "--mutable-type=sdmf", fn1))
-        d.addCallback(lambda (rc, cap, err):
-            self.do_cli("ls", "--json", cap))
-        d.addCallback(self._check_sdmf_json)
+        d.addCallback(_put_and_ls, ["--mutable", "--format=MDMF"], "MDMF")
+        d.addCallback(self._check_mdmf_json)
+
+        d.addCallback(_put_and_ls, ["--format=CHK"], "CHK")
+        d.addCallback(self._check_chk_json)
+        d.addCallback(_put_and_ls, [], "CHK")
+        d.addCallback(self._check_chk_json)
+
         return d
 
     def test_put_to_mdmf_cap(self):
@@ -1229,7 +1241,7 @@ class Put(GridTestMixin, CLITestMixin, unittest.TestCase):
         data = "data" * 100000
         fn1 = os.path.join(self.basedir, "data")
         fileutil.write(fn1, data)
-        d = self.do_cli("put", "--mutable", "--mutable-type=mdmf", fn1)
+        d = self.do_cli("put", "--format=MDMF", fn1)
         def _got_cap((rc, out, err)):
             self.failUnlessEqual(rc, 0)
             self.cap = out.strip()
@@ -1275,7 +1287,7 @@ class Put(GridTestMixin, CLITestMixin, unittest.TestCase):
         data = "data" * 100000
         fn1 = os.path.join(self.basedir, "data")
         fileutil.write(fn1, data)
-        d = self.do_cli("put", "--mutable", "--mutable-type=sdmf", fn1)
+        d = self.do_cli("put", "--format=SDMF", fn1)
         def _got_cap((rc, out, err)):
             self.failUnlessEqual(rc, 0)
             self.cap = out.strip()
@@ -1303,7 +1315,7 @@ class Put(GridTestMixin, CLITestMixin, unittest.TestCase):
         o = cli.PutOptions()
         self.failUnlessRaises(usage.UsageError,
                               o.parseOptions,
-                              ["--mutable", "--mutable-type=ldmf"])
+                              ["--format=LDMF"])
 
     def test_put_with_nonexistent_alias(self):
         # when invoked with an alias that doesn't exist, 'tahoe put'
@@ -3308,7 +3320,7 @@ class Mkdir(GridTestMixin, CLITestMixin, unittest.TestCase):
             self.failUnlessIn(st, out)
             return out
         def _mkdir(ign, mutable_type, uri_prefix, dirname):
-            d2 = self.do_cli("mkdir", "--mutable-type="+mutable_type, dirname)
+            d2 = self.do_cli("mkdir", "--format="+mutable_type, dirname)
             d2.addCallback(_check, uri_prefix)
             def _stash_filecap(cap):
                 u = uri.from_string(cap)
@@ -3330,7 +3342,7 @@ class Mkdir(GridTestMixin, CLITestMixin, unittest.TestCase):
     def test_mkdir_mutable_type_unlinked(self):
         self.basedir = os.path.dirname(self.mktemp())
         self.set_up_grid()
-        d = self.do_cli("mkdir", "--mutable-type=sdmf")
+        d = self.do_cli("mkdir", "--format=SDMF")
         def _check((rc, out, err), st):
             self.failUnlessReallyEqual(rc, 0)
             self.failUnlessReallyEqual(err, "")
@@ -3349,7 +3361,7 @@ class Mkdir(GridTestMixin, CLITestMixin, unittest.TestCase):
         d.addCallback(lambda res: self.do_cli("ls", "--json",
                                               self._filecap))
         d.addCallback(_check, '"format": "SDMF"')
-        d.addCallback(lambda res: self.do_cli("mkdir", "--mutable-type=mdmf"))
+        d.addCallback(lambda res: self.do_cli("mkdir", "--format=MDMF"))
         d.addCallback(_check, "URI:DIR2-MDMF")
         d.addCallback(_stash_dircap)
         d.addCallback(lambda res: self.do_cli("ls", "--json",
@@ -3361,7 +3373,7 @@ class Mkdir(GridTestMixin, CLITestMixin, unittest.TestCase):
         o = cli.MakeDirectoryOptions()
         self.failUnlessRaises(usage.UsageError,
                               o.parseOptions,
-                              ["--mutable", "--mutable-type=ldmf"])
+                              ["--format=LDMF"])
 
     def test_mkdir_unicode(self):
         self.basedir = os.path.dirname(self.mktemp())
diff --git a/src/allmydata/test/test_system.py b/src/allmydata/test/test_system.py
index 639968dd..ccf3cad4 100644
--- a/src/allmydata/test/test_system.py
+++ b/src/allmydata/test/test_system.py
@@ -1518,7 +1518,7 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
         d.addCallback(run, "put", files[1], "subdir/tahoe-file1")
         #  tahoe put bar tahoe:FOO
         d.addCallback(run, "put", files[2], "tahoe:file2")
-        d.addCallback(run, "put", "--mutable", files[3], "tahoe:file3")
+        d.addCallback(run, "put", "--format=SDMF", files[3], "tahoe:file3")
         def _check_put_mutable((out,err)):
             self._mutable_file3_uri = out.strip()
         d.addCallback(_check_put_mutable)
-- 
2.45.2