├── .gitignore ├── Gopkg.lock ├── Gopkg.toml ├── LICENSE ├── README.md ├── build.sh ├── fdbarray ├── fdbarray.go ├── fdbarray_test.go └── readversioncache │ ├── atomicMax.go │ ├── readVersionCache.go │ └── readVersionCache_test.go ├── main.go └── vendor └── github.com ├── alecthomas └── units │ ├── COPYING │ ├── README.md │ ├── bytes.go │ ├── doc.go │ ├── si.go │ └── util.go ├── apple └── foundationdb │ ├── LICENSE │ ├── bindings │ └── go │ │ └── src │ │ └── fdb │ │ ├── cluster.go │ │ ├── database.go │ │ ├── directory │ │ ├── allocator.go │ │ ├── directory.go │ │ ├── directoryLayer.go │ │ ├── directoryPartition.go │ │ ├── directorySubspace.go │ │ └── node.go │ │ ├── doc.go │ │ ├── errors.go │ │ ├── fdb.go │ │ ├── futures.go │ │ ├── generated.go │ │ ├── keyselector.go │ │ ├── range.go │ │ ├── snapshot.go │ │ ├── subspace │ │ └── subspace.go │ │ ├── transaction.go │ │ └── tuple │ │ └── tuple.go │ ├── fdbclient │ └── rapidxml │ │ └── license.txt │ └── packaging │ └── msi │ └── LICENSE.rtf ├── dustin └── go-humanize │ ├── .travis.yml │ ├── LICENSE │ ├── README.markdown │ ├── big.go │ ├── bigbytes.go │ ├── bytes.go │ ├── comma.go │ ├── commaf.go │ ├── ftoa.go │ ├── humanize.go │ ├── number.go │ ├── ordinals.go │ ├── si.go │ └── times.go ├── meln1k └── buse-go │ ├── LICENSE │ └── buse │ ├── buse.go │ └── types.go └── urfave └── cli ├── .flake8 ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── app.go ├── appveyor.yml ├── category.go ├── cli.go ├── command.go ├── context.go ├── errors.go ├── flag-types.json ├── flag.go ├── flag_generated.go ├── funcs.go ├── generate-flag-types ├── help.go └── runtests /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | fdbbd 14 | -------------------------------------------------------------------------------- /Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | branch = "master" 6 | digest = "1:c198fdc381e898e8fb62b8eb62758195091c313ad18e52a3067366e1dda2fb3c" 7 | name = "github.com/alecthomas/units" 8 | packages = ["."] 9 | pruneopts = "UT" 10 | revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a" 11 | 12 | [[projects]] 13 | digest = "1:f9c4b3349dd882bb31842cf70a4b31f269a789cf95b5fef46a35a4d3423393ee" 14 | name = "github.com/apple/foundationdb" 15 | packages = [ 16 | "bindings/go/src/fdb", 17 | "bindings/go/src/fdb/directory", 18 | "bindings/go/src/fdb/subspace", 19 | "bindings/go/src/fdb/tuple", 20 | ] 21 | pruneopts = "UT" 22 | revision = "20566f2ff06a7e822b30e8cfd91090fbd863a393" 23 | version = "6.2.15" 24 | 25 | [[projects]] 26 | digest = "1:6f9339c912bbdda81302633ad7e99a28dfa5a639c864061f1929510a9a64aa74" 27 | name = "github.com/dustin/go-humanize" 28 | packages = ["."] 29 | pruneopts = "UT" 30 | revision = "9f541cc9db5d55bce703bd99987c9d5cb8eea45e" 31 | version = "v1.0.0" 32 | 33 | [[projects]] 34 | branch = "master" 35 | digest = "1:967be3bffc5c9539e5d3ad9ce4edb9ae15900c6f0544e10cda3b0fbacef0e57d" 36 | name = "github.com/meln1k/buse-go" 37 | packages = ["buse"] 38 | pruneopts = "UT" 39 | revision = "907e622df7a562faa7c49af64d5fe17ce8ba8456" 40 | 41 | [[projects]] 42 | digest = "1:b24d38b282bacf9791408a080f606370efa3d364e4b5fd9ba0f7b87786d3b679" 43 | name = "github.com/urfave/cli" 44 | packages = ["."] 45 | pruneopts = "UT" 46 | revision = "cfb38830724cc34fedffe9a2a29fb54fa9169cd1" 47 | version = "v1.20.0" 48 | 49 | [solve-meta] 50 | analyzer-name = "dep" 51 | analyzer-version = 1 52 | input-imports = [ 53 | "github.com/alecthomas/units", 54 | "github.com/apple/foundationdb/bindings/go/src/fdb", 55 | "github.com/apple/foundationdb/bindings/go/src/fdb/directory", 56 | "github.com/apple/foundationdb/bindings/go/src/fdb/tuple", 57 | "github.com/dustin/go-humanize", 58 | "github.com/meln1k/buse-go/buse", 59 | "github.com/urfave/cli", 60 | ] 61 | solver-name = "gps-cdcl" 62 | solver-version = 1 63 | -------------------------------------------------------------------------------- /Gopkg.toml: -------------------------------------------------------------------------------- 1 | # Gopkg.toml example 2 | # 3 | # Refer to https://golang.github.io/dep/docs/Gopkg.toml.html 4 | # for detailed Gopkg.toml documentation. 5 | # 6 | # required = ["github.com/user/thing/cmd/thing"] 7 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] 8 | # 9 | # [[constraint]] 10 | # name = "github.com/user/project" 11 | # version = "1.0.0" 12 | # 13 | # [[constraint]] 14 | # name = "github.com/user/project2" 15 | # branch = "dev" 16 | # source = "github.com/myfork/project2" 17 | # 18 | # [[override]] 19 | # name = "github.com/x/y" 20 | # version = "2.4.0" 21 | # 22 | # [prune] 23 | # non-go = false 24 | # go-tests = true 25 | # unused-packages = true 26 | 27 | 28 | [[constraint]] 29 | name = "github.com/apple/foundationdb" 30 | version = "6.0.18" 31 | 32 | [prune] 33 | go-tests = true 34 | unused-packages = true 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FoundationDB Block Device 2 | Replicated Block Device backed by FoundationDB 3 | 4 | ## What is this 5 | This is an implementation of a block device in userspace which uses FoundationDB as a backend. 6 | It provides a replicated block device for non-replicated workloads so they 7 | can benefit from transparent block-level replication and enhanced fault tolerance. 8 | 9 | Inspired by [spullara/nbd](https://github.com/spullara/nbd) 10 | 11 | ## Is it fast? 12 | I did a small benchmark using a FoundationDB cluster of 2 nodes (linux running on macbooks with SSDs, 13 | not tuned for FDB at all). 14 | FIO benchmark on 1GB file resulted in 10K random read/write IOPS in 4KB blocks and the latency was below 10ms (direct io was used). 15 | While doing sequential reads it was able to saturate 1Gbit network link. 16 | 17 | Postrgres running in virtualbox showed 900 TPS on TPC-B pgbench workload with a database of size 1g. 18 | 19 | ## How does it work with concurrent volume mounts? 20 | Currently there is a mechanism which relies on lease tokens and fdb transactions to transactionally 21 | transfer ownership to the new client and discard any in-flight write requests from the old one. 22 | 23 | ## Current status 24 | It's an early version. There are several important featues which are not implemented yet 25 | (such as IOPS limits and volume size estimation) but it works and it's relatively fast! 26 | 27 | ## How to use 28 | Commands are documented in the CLI: 29 | ``` 30 | $ ./fdbbd --help 31 | NAME: 32 | fdbbd - block device using FoundationDB as a backend. 33 | Our motto: still more performant and reliable than EBS 34 | 35 | USAGE: 36 | fdbbd [global options] command [command options] [arguments...] 37 | 38 | VERSION: 39 | 0.1.0 40 | 41 | COMMANDS: 42 | create Create a new volume 43 | list List all volumes 44 | attach Attach the volume 45 | delete Delete the volume 46 | help, h Shows a list of commands or help for one command 47 | 48 | GLOBAL OPTIONS: 49 | --help, -h show help 50 | --version, -v print the version 51 | ``` 52 | 53 | ## Getting started 54 | 55 | 1. Set up a FoundationDB cluster. 56 | 2. Build the driver: 57 | ```sh 58 | sh build.sh 59 | ``` 60 | 3. Create a new volume: 61 | ```sh 62 | $ ./fdbbd create --size 1GB myvolume 63 | ``` 64 | 4. If `nbd` kernel module is not loaded, load it: 65 | ```sh 66 | $ sudo modprobe nbd 67 | ``` 68 | 5. Attach the volume to the system: 69 | ```sh 70 | sudo ./fdbbd attach --bpt 4 myvolume /dev/nbd0 71 | ``` 72 | 6. Create a directory to mount the volume: 73 | ```sh 74 | mkdir nbdmount 75 | ``` 76 | 7. Create a file system on your block device. XFS is a good option: 77 | ```sh 78 | sudo mkfs.xfs /dev/nbd0 79 | ``` 80 | 8. Mount the attached volume: 81 | ```sh 82 | sudo mount /dev/nbd0 nbdmount/ 83 | ``` 84 | 9. Done! You have a replicated volume! 85 | 86 | ## What's inside 87 | This project uses Network Block Device kernel module underneath. A unix pipe is used to talk to a kernel, 88 | and then driver translates NBD protocol into FoundationDB calls. 89 | 90 | ## Roadmap 91 | There are a few features planned in future releases, ordered by importance: 92 | 93 | 1. Bulk insert support via batch transactions 94 | 2. IOPS isolation 95 | 3. CSI implementation 96 | 4. Snapshots 97 | 5. Volume size estimation (using roaring bitmaps or similar) 98 | 6. Client-side encryption 99 | 7. Control panel 100 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | go build -o fdbbd 2 | -------------------------------------------------------------------------------- /fdbarray/fdbarray_test.go: -------------------------------------------------------------------------------- 1 | package fdbarray 2 | 3 | import ( 4 | "bytes" 5 | "math/rand" 6 | "os" 7 | "testing" 8 | "time" 9 | 10 | "github.com/apple/foundationdb/bindings/go/src/fdb" 11 | "github.com/apple/foundationdb/bindings/go/src/fdb/directory" 12 | ) 13 | 14 | func TestNotAlignedReadWrite(t *testing.T) { 15 | 16 | fixture := newFixture("TestNotAlignedReadWrite") 17 | 18 | write := make([]byte, 12345) 19 | for i := 0; i < 12345; i++ { 20 | write[i] = byte(i) 21 | } 22 | 23 | fixture.fdbArray.Write(write, 10000) 24 | 25 | read := make([]byte, 12345) 26 | 27 | fixture.fdbArray.Read(read, 10000) 28 | 29 | if !bytes.Equal(write, read) { 30 | t.Errorf("Write is not equal to read") 31 | } 32 | } 33 | 34 | func TestAlignedReadWrite(t *testing.T) { 35 | 36 | fixture := newFixture("TestAlignedReadWrite") 37 | 38 | write := make([]byte, 131072) 39 | for i := 0; i < 131072; i++ { 40 | write[i] = byte(i) 41 | } 42 | 43 | fixture.fdbArray.Write(write, 0) 44 | 45 | read := make([]byte, 131072) 46 | 47 | fixture.fdbArray.Read(read, 0) 48 | 49 | if !bytes.Equal(write, read) { 50 | t.Errorf("Write is not equal to read") 51 | } 52 | } 53 | 54 | func TestRandomReadWrite(t *testing.T) { 55 | 56 | fixture := newFixture("TestRandomReadWrite") 57 | 58 | rand.Seed(42) 59 | 60 | for i := 0; i < 100; i++ { 61 | length := rand.Int31n(1000) 62 | write := make([]byte, length) 63 | rand.Read(write) 64 | offset := uint64(rand.Int63n(1000000)) 65 | fixture.fdbArray.Write(write, offset) 66 | read := make([]byte, length) 67 | fixture.fdbArray.Read(read, offset) 68 | if !bytes.Equal(write, read) { 69 | t.Errorf("Write is not equal to read!") 70 | } 71 | } 72 | 73 | } 74 | 75 | func TestDataCorruptionWriteProtection(t *testing.T) { 76 | 77 | fixture := newFixture("TestDataCorruptionWriteProtection") 78 | 79 | rand.Seed(42) 80 | 81 | write := make([]byte, 4096) 82 | rand.Read(write) 83 | fixture.fdbArray.Write(write, 0) 84 | 85 | // Open the array to invalidate ownership 86 | OpenBySubspace(fixture.db, fixture.arraySubspace, 0) 87 | 88 | // now any operation on fdbArray should fail 89 | 90 | err := fixture.fdbArray.Write(write, 0) 91 | 92 | if err == nil { 93 | t.Error("write after an ownership change is now allowed") 94 | } 95 | 96 | } 97 | 98 | func TestDataCorruptionReadProtection(t *testing.T) { 99 | 100 | fixture := newFixture("TestDataCorruptionReadProtection") 101 | 102 | rand.Seed(42) 103 | 104 | write := make([]byte, 4096) 105 | rand.Read(write) 106 | fixture.fdbArray.Write(write, 0) 107 | 108 | OpenBySubspace(fixture.db, fixture.arraySubspace, 0) 109 | 110 | // wait long enough to trigger version change 111 | time.Sleep(1000 * time.Millisecond) 112 | 113 | // now any operation on fdbArray should fail 114 | 115 | err := fixture.fdbArray.Read(write, 0) 116 | 117 | if err == nil { 118 | t.Error("read after an ownership change is now allowed") 119 | } 120 | 121 | } 122 | 123 | func BenchmarkWrite(b *testing.B) { 124 | 125 | fixture := newFixture("BenchmarkWrite") 126 | 127 | bs := int(fixture.fdbArray.BlockSize()) 128 | 129 | write := make([]byte, bs) 130 | for i := 0; i < bs; i++ { 131 | write[i] = byte(i) 132 | } 133 | 134 | b.ResetTimer() 135 | 136 | for n := 0; n < b.N; n++ { 137 | fixture.fdbArray.Write(write, uint64(n*bs)) 138 | } 139 | } 140 | 141 | func BenchmarkRead(b *testing.B) { 142 | 143 | fixture := newFixture("BenchmarkRead") 144 | 145 | bs := int(fixture.fdbArray.BlockSize()) 146 | 147 | writeSize := bs * b.N 148 | 149 | write := make([]byte, writeSize) 150 | for i := 0; i < writeSize; i++ { 151 | write[i] = byte(i) 152 | } 153 | 154 | read := make([]byte, fixture.fdbArray.BlockSize()) 155 | 156 | fixture.fdbArray.Write(write, 0) 157 | 158 | for n := 0; n < b.N; n++ { 159 | fixture.fdbArray.Read(read, uint64(n*bs)) 160 | } 161 | } 162 | 163 | func BenchmarkRead4k(b *testing.B) { 164 | 165 | fixture := newFixture("BenchmarkRead4k") 166 | 167 | bs := int(fixture.fdbArray.BlockSize()) 168 | 169 | writeSize := bs * b.N 170 | 171 | write := make([]byte, writeSize) 172 | for i := 0; i < writeSize; i++ { 173 | write[i] = byte(i) 174 | } 175 | 176 | read := make([]byte, fixture.fdbArray.BlockSize()) 177 | 178 | fixture.fdbArray.Write(write, 0) 179 | 180 | b.ResetTimer() 181 | 182 | for n := 0; n < b.N; n++ { 183 | fixture.fdbArray.Read(read, uint64(n*bs)) 184 | } 185 | } 186 | 187 | type fdbarrayFixture struct { 188 | db fdb.Database 189 | arraySubspace directory.DirectorySubspace 190 | fdbArray FDBArray 191 | } 192 | 193 | func newFixture(testName string) fdbarrayFixture { 194 | 195 | fdb.MustAPIVersion(600) 196 | // Open the default database from the system cluster 197 | db := fdb.MustOpenDefault() 198 | 199 | subspace, _ := directory.CreateOrOpen(db, []string{"com.github.meln1k.fdbbd.testing.fdbarray", testName}, nil) 200 | 201 | CreateBySubspace(db, subspace, 512, 10240) 202 | 203 | fdbArray, _ := OpenBySubspace(db, subspace, 0) 204 | 205 | return fdbarrayFixture{ 206 | db, 207 | subspace, 208 | fdbArray, 209 | } 210 | 211 | } 212 | 213 | func TestMain(m *testing.M) { 214 | 215 | fdb.MustAPIVersion(600) 216 | // Open the default database from the system cluster 217 | db := fdb.MustOpenDefault() 218 | 219 | dir, _ := directory.CreateOrOpen(db, []string{"com.github.meln1k.fdbbd.testing.fdbarray"}, nil) 220 | 221 | code := m.Run() 222 | 223 | defer dir.Remove(db, nil) 224 | 225 | os.Exit(code) 226 | } 227 | -------------------------------------------------------------------------------- /fdbarray/readversioncache/atomicMax.go: -------------------------------------------------------------------------------- 1 | package readversioncache 2 | 3 | import ( 4 | "sync/atomic" 5 | ) 6 | 7 | // AtomicMax is used to track the max value. 8 | type AtomicMax struct { 9 | value int64 10 | } 11 | 12 | // StoreMax is equivalent to storing max(value, tracker.value) in the tracker 13 | func (mt *AtomicMax) StoreMax(value int64) { 14 | for { 15 | stored := atomic.LoadInt64(&mt.value) 16 | if value <= stored { 17 | return 18 | } 19 | if atomic.CompareAndSwapInt64(&mt.value, stored, value) { 20 | return 21 | } 22 | } 23 | } 24 | 25 | // Get returns the current max value. 26 | func (mt *AtomicMax) Get() int64 { 27 | return atomic.LoadInt64(&mt.value) 28 | } 29 | -------------------------------------------------------------------------------- /fdbarray/readversioncache/readVersionCache.go: -------------------------------------------------------------------------------- 1 | package readversioncache 2 | 3 | import ( 4 | "encoding/binary" 5 | "sync/atomic" 6 | "time" 7 | 8 | "github.com/apple/foundationdb/bindings/go/src/fdb" 9 | ) 10 | 11 | // ReadVersionCache caches "safe" read versions, which are known to be 12 | type ReadVersionCache struct { 13 | database fdb.Database 14 | atomicMax AtomicMax 15 | leaseExpired int32 16 | currentToken uint64 17 | tokenKey fdb.KeyConvertible 18 | } 19 | 20 | // Init initializes the read version cache 21 | func Init(database fdb.Database, currentToken uint64, tokenKey fdb.KeyConvertible, updateInterval time.Duration) *ReadVersionCache { 22 | 23 | cache := &ReadVersionCache{ 24 | database, 25 | AtomicMax{}, 26 | 0, 27 | currentToken, 28 | tokenKey, 29 | } 30 | 31 | initReadVersion, err := cache.fetchReadVersion() 32 | if err == nil { 33 | cache.atomicMax.StoreMax(initReadVersion) 34 | 35 | ticker := time.NewTicker(updateInterval) 36 | go func() { 37 | for range ticker.C { 38 | readVersion, err := cache.fetchReadVersion() 39 | if err == nil { 40 | cache.atomicMax.StoreMax(readVersion) 41 | } else { 42 | ticker.Stop() 43 | atomic.StoreInt32(&cache.leaseExpired, 1) 44 | } 45 | } 46 | }() 47 | } else { 48 | atomic.StoreInt32(&cache.leaseExpired, 1) 49 | return cache 50 | } 51 | 52 | return cache 53 | 54 | } 55 | 56 | // GetReadVersion returns the latest safe cached read version 57 | func (cache *ReadVersionCache) GetReadVersion() (int64, error) { 58 | if atomic.LoadInt32(&cache.leaseExpired) != 0 { 59 | return -1, NewTokenInvalidError("lease expired") 60 | } 61 | 62 | return cache.atomicMax.Get(), nil 63 | } 64 | 65 | // SetReadVersion tries to set the user-proveded commit version as a new read version 66 | func (cache *ReadVersionCache) SetReadVersion(version int64) error { 67 | if atomic.LoadInt32(&cache.leaseExpired) != 0 { 68 | return NewTokenInvalidError("lease expired") 69 | } 70 | 71 | cache.atomicMax.StoreMax(version) 72 | 73 | return nil 74 | 75 | } 76 | 77 | func (cache *ReadVersionCache) fetchReadVersion() (int64, error) { 78 | 79 | readVersion, e := TransactConflicting(cache.database, func(tr fdb.Transaction) (ret interface{}, err error) { 80 | tokenBytes := tr.Get(cache.tokenKey).MustGet() 81 | 82 | token := binary.BigEndian.Uint64(tokenBytes) 83 | 84 | if token != cache.currentToken { 85 | tr.Cancel() 86 | err = NewTokenInvalidError("token was invalid during fetchReadVersion") 87 | } else { 88 | ret = tr.GetReadVersion().MustGet() 89 | } 90 | 91 | return 92 | }, nil) 93 | 94 | if e != nil { 95 | return 0, e 96 | } 97 | 98 | return readVersion.(int64), nil 99 | } 100 | 101 | // TransactConflicting is the same as db.Transact, but won't retry in case of conflicting transactions 102 | // and will throw NewTokenInvalidError instead 103 | // it will also execute onCommit callback if transaction commits sucessfully 104 | func TransactConflicting( 105 | database fdb.Database, 106 | f func(fdb.Transaction) (interface{}, error), 107 | onCommit func(fdb.Transaction)) (interface{}, error) { 108 | tr, e := database.CreateTransaction() 109 | /* Any error here is non-retryable */ 110 | if e != nil { 111 | return nil, e 112 | } 113 | 114 | wrapped := func() (ret interface{}, e error) { 115 | defer panicToError(&e) 116 | 117 | ret, e = f(tr) 118 | 119 | if e == nil { 120 | e = tr.Commit().Get() 121 | } 122 | 123 | if e == nil && onCommit != nil { 124 | onCommit(tr) 125 | } 126 | 127 | return 128 | } 129 | 130 | return retryable(wrapped, tr.OnError) 131 | } 132 | 133 | func panicToError(e *error) { 134 | if r := recover(); r != nil { 135 | fe, ok := r.(fdb.Error) 136 | if ok { 137 | *e = fe 138 | } else { 139 | panic(r) 140 | } 141 | } 142 | } 143 | 144 | func retryable(wrapped func() (interface{}, error), onError func(fdb.Error) fdb.FutureNil) (ret interface{}, e error) { 145 | for { 146 | ret, e = wrapped() 147 | 148 | /* No error means success! */ 149 | if e == nil { 150 | return 151 | } 152 | 153 | ep, ok := e.(fdb.Error) 154 | if ok { 155 | e = onError(ep).Get() 156 | } 157 | 158 | /* If OnError returns an error, then it's not 159 | /* retryable; otherwise take another pass at things */ 160 | if e != nil { 161 | return 162 | } 163 | 164 | // Transaction not committed due to conflict, aborting 165 | if ep.Code == 1020 { 166 | e = NewTokenInvalidError("Transaction conflict") 167 | return 168 | } 169 | } 170 | } 171 | 172 | // TokenInvalidError happens when the lease token is no longer valid. It means that array ownership was changed 173 | type TokenInvalidError struct { 174 | message string 175 | } 176 | 177 | func (e TokenInvalidError) Error() string { 178 | return e.message 179 | } 180 | 181 | // NewTokenInvalidError creates a TokenInvalidError using the provided message 182 | func NewTokenInvalidError(message string) TokenInvalidError { 183 | return TokenInvalidError{ 184 | message, 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /fdbarray/readversioncache/readVersionCache_test.go: -------------------------------------------------------------------------------- 1 | package readversioncache 2 | 3 | import ( 4 | "encoding/binary" 5 | "log" 6 | "os" 7 | "testing" 8 | "time" 9 | 10 | "github.com/apple/foundationdb/bindings/go/src/fdb" 11 | "github.com/apple/foundationdb/bindings/go/src/fdb/directory" 12 | "github.com/apple/foundationdb/bindings/go/src/fdb/tuple" 13 | ) 14 | 15 | func TestCustomTransact(t *testing.T) { 16 | 17 | fixture := newFixture("TestCustomTransact") 18 | 19 | fixture.db.Transact(func(tr fdb.Transaction) (ret interface{}, err error) { 20 | tr.Set(fixture.fooKey, []byte("bar")) 21 | tr.Set(fixture.tokenKey, []byte("1")) 22 | return 23 | }) 24 | 25 | tr, _ := fixture.db.CreateTransaction() 26 | 27 | tr.Get(fixture.tokenKey) 28 | 29 | tr.Commit().MustGet() 30 | 31 | readVersion := tr.GetReadVersion().MustGet() 32 | 33 | // Set the tokenKey in between 34 | fixture.db.Transact(func(tr fdb.Transaction) (ret interface{}, err error) { 35 | tr.Set(fixture.tokenKey, []byte("2")) 36 | return 37 | }) 38 | 39 | // This should conflict 40 | _, err := TransactConflicting(fixture.db, func(tr fdb.Transaction) (ret interface{}, err error) { 41 | tr.SetReadVersion(readVersion) 42 | tr.AddReadConflictKey(fixture.tokenKey) 43 | tr.Set(fixture.fooKey, []byte("baz")) 44 | return 45 | }, nil) 46 | 47 | if err != NewTokenInvalidError("Transaction conflict") { 48 | log.Fatal("Write transaction didn't conflict") 49 | } 50 | } 51 | 52 | func TestCustomTransactWithCallback(t *testing.T) { 53 | 54 | fixture := newFixture("TestCustomTransactWithCallback") 55 | 56 | tr, _ := fixture.db.CreateTransaction() 57 | 58 | tr.Set(fixture.fooKey, []byte("bar")) 59 | 60 | tr.Commit().MustGet() 61 | 62 | readVersion := tr.GetReadVersion().MustGet() 63 | 64 | // update the latest read version 65 | fixture.db.Transact(func(tr fdb.Transaction) (ret interface{}, err error) { 66 | tr.Set(fixture.fooKey, []byte("baz")) 67 | return 68 | }) 69 | 70 | var newReadVersion int64 71 | 72 | // store the read version on callback 73 | _, _ = TransactConflicting(fixture.db, func(tr fdb.Transaction) (ret interface{}, err error) { 74 | tr.Set(fixture.fooKey, []byte("foo")) 75 | return 76 | }, func(tr fdb.Transaction) { 77 | comittedVersion, _ := tr.GetCommittedVersion() 78 | newReadVersion = comittedVersion 79 | }) 80 | 81 | if readVersion >= newReadVersion { 82 | log.Fatal("GetCommittedVersion callback didn't work") 83 | } 84 | } 85 | 86 | func TestFetchReadVersion(t *testing.T) { 87 | 88 | fixture := newFixture("TestFetchReadVersion") 89 | 90 | tokenKey := fixture.subspace.Pack(tuple.Tuple{"token"}) 91 | 92 | currentToken := uint64(1) 93 | 94 | tokenBytes := make([]byte, 8) 95 | 96 | binary.BigEndian.PutUint64(tokenBytes, currentToken) 97 | 98 | cache := ReadVersionCache{ 99 | fixture.db, 100 | AtomicMax{}, 101 | 0, 102 | currentToken, 103 | tokenKey, 104 | } 105 | 106 | fixture.db.Transact(func(tr fdb.Transaction) (ret interface{}, err error) { 107 | tr.Set(tokenKey, tokenBytes) 108 | return 109 | }) 110 | 111 | readVersion, _ := cache.fetchReadVersion() 112 | 113 | if readVersion <= 0 { 114 | log.Fatal("read version invalid") 115 | } 116 | 117 | // trigger the read version update by making a write 118 | fixture.db.Transact(func(tr fdb.Transaction) (ret interface{}, err error) { 119 | tr.Set(fixture.fooKey, []byte("bar")) 120 | return 121 | }) 122 | 123 | readVersion2, _ := cache.fetchReadVersion() 124 | 125 | if readVersion2 <= readVersion { 126 | log.Fatal("read version 2 must be > read version 1") 127 | } 128 | 129 | // Let's check the case when the token was updated 130 | binary.BigEndian.PutUint64(tokenBytes, currentToken+1) 131 | 132 | fixture.db.Transact(func(tr fdb.Transaction) (ret interface{}, err error) { 133 | tr.Set(tokenKey, tokenBytes) 134 | return 135 | }) 136 | _, e := cache.fetchReadVersion() 137 | 138 | if e == nil { 139 | log.Fatal("expected fetchReadVersion to fail here") 140 | } 141 | } 142 | 143 | func TestVersionUpdates(t *testing.T) { 144 | 145 | fixture := newFixture("TestVersionUpdates") 146 | 147 | tokenKey := fixture.subspace.Pack(tuple.Tuple{"token"}) 148 | 149 | currentToken := uint64(1) 150 | 151 | tokenBytes := make([]byte, 8) 152 | 153 | binary.BigEndian.PutUint64(tokenBytes, currentToken) 154 | 155 | fixture.db.Transact(func(tr fdb.Transaction) (ret interface{}, err error) { 156 | tr.Set(tokenKey, tokenBytes) 157 | return 158 | }) 159 | 160 | cache := Init(fixture.db, currentToken, tokenKey, 10*time.Millisecond) 161 | 162 | rv1, _ := cache.GetReadVersion() 163 | 164 | // trigger the read version update by making a write 165 | fixture.db.Transact(func(tr fdb.Transaction) (ret interface{}, err error) { 166 | tr.Set(fixture.fooKey, []byte("bar")) 167 | return 168 | }) 169 | 170 | time.Sleep(100 * time.Millisecond) 171 | 172 | rv2, _ := cache.GetReadVersion() 173 | 174 | if rv2 <= rv1 { 175 | log.Fatalf("read version 2 (%d) <= read version 1 (%d) ", rv2, rv1) 176 | } 177 | 178 | } 179 | 180 | func BenchmarkGrvLatency(b *testing.B) { 181 | 182 | fixture := newFixture("BenchmarkGrvLatency") 183 | 184 | b.ResetTimer() 185 | 186 | key := fixture.fooKey 187 | 188 | for i := 0; i < b.N; i++ { 189 | fixture.db.Transact(func(tr fdb.Transaction) (ret interface{}, err error) { 190 | tr.Get(key).MustGet() 191 | 192 | return 193 | }) 194 | } 195 | 196 | } 197 | 198 | func BenchmarkCachedGrvLatency(b *testing.B) { 199 | 200 | fixture := newFixture("BenchmarkCachedGrvLatency") 201 | 202 | var transaction fdb.Transaction 203 | 204 | key := fixture.fooKey 205 | 206 | fixture.db.Transact(func(tr fdb.Transaction) (ret interface{}, err error) { 207 | tr.Get(key).MustGet() 208 | transaction = tr 209 | return 210 | }) 211 | 212 | committedVersion := transaction.GetReadVersion().MustGet() 213 | 214 | b.ResetTimer() 215 | 216 | for i := 0; i < b.N; i++ { 217 | fixture.db.Transact(func(tr fdb.Transaction) (ret interface{}, err error) { 218 | tr.SetReadVersion(committedVersion) 219 | tr.Get(key).MustGet() 220 | 221 | return 222 | }) 223 | } 224 | 225 | } 226 | 227 | type readVersionCacheFixture struct { 228 | db fdb.Database 229 | subspace directory.DirectorySubspace 230 | tokenKey fdb.Key 231 | fooKey fdb.Key 232 | } 233 | 234 | func newFixture(testName string) readVersionCacheFixture { 235 | 236 | fdb.MustAPIVersion(600) 237 | // Open the default database from the system cluster 238 | db := fdb.MustOpenDefault() 239 | 240 | subspace, _ := directory.CreateOrOpen(db, []string{"com.github.meln1k.fdbbd.testing", testName}, nil) 241 | 242 | tokenKey := subspace.Pack(tuple.Tuple{"token"}) 243 | 244 | fooKey := subspace.Pack(tuple.Tuple{"foo"}) 245 | 246 | return readVersionCacheFixture{ 247 | db, 248 | subspace, 249 | tokenKey, 250 | fooKey, 251 | } 252 | 253 | } 254 | 255 | func TestMain(m *testing.M) { 256 | code := m.Run() 257 | fdb.MustAPIVersion(600) 258 | // Open the default database from the system cluster 259 | db := fdb.MustOpenDefault() 260 | dir, _ := directory.CreateOrOpen(db, []string{"com.github.meln1k.fdbbd.testing.readversioncache"}, nil) 261 | 262 | dir.Remove(db, nil) 263 | 264 | os.Exit(code) 265 | } 266 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "os/signal" 8 | 9 | "github.com/alecthomas/units" 10 | 11 | "github.com/apple/foundationdb/bindings/go/src/fdb" 12 | 13 | "github.com/meln1k/buse-go/buse" 14 | "github.com/meln1k/foundationdb-block-device/fdbarray" 15 | "github.com/meln1k/foundationdb-block-device/fdbarray/readversioncache" 16 | 17 | humanize "github.com/dustin/go-humanize" 18 | 19 | "github.com/urfave/cli" 20 | ) 21 | 22 | type FdbStorage struct { 23 | array fdbarray.FDBArray 24 | } 25 | 26 | func CreateStorageVolume(database fdb.Database, name string, blockSize uint32, size uint64, blocksPerTransaction uint32) error { 27 | return fdbarray.Create(database, name, blockSize, size) 28 | } 29 | 30 | func OpenStorageVolume(database fdb.Database, name string, blocksPerTransaction uint32) FdbStorage { 31 | array, _ := fdbarray.OpenByName(database, name, blocksPerTransaction) // TODO handle the error 32 | return FdbStorage{array: array} 33 | } 34 | 35 | func (d FdbStorage) ReadAt(p []byte, off uint64) error { 36 | err := d.array.Read(p, off) 37 | if err != nil { 38 | switch err.(type) { 39 | case readversioncache.TokenInvalidError: 40 | return buse.NewNbdError(buse.ESHUTDOWN) 41 | default: 42 | return buse.NewNbdError(buse.EIO) 43 | } 44 | } 45 | return nil 46 | } 47 | 48 | func (d FdbStorage) WriteAt(p []byte, off uint64) error { 49 | err := d.array.Write(p, off) 50 | if err != nil { 51 | switch err.(type) { 52 | case readversioncache.TokenInvalidError: 53 | return buse.NewNbdError(buse.ESHUTDOWN) 54 | default: 55 | return buse.NewNbdError(buse.EIO) 56 | } 57 | } 58 | return nil 59 | } 60 | 61 | func (d FdbStorage) Disconnect() { 62 | log.Println("[fdbbd] DISCONNECT") 63 | } 64 | 65 | func (d FdbStorage) Flush() error { 66 | log.Println("[fdbbd] FLUSH is not needed") 67 | return nil 68 | } 69 | 70 | func (d FdbStorage) Trim(off uint64, length uint32) error { 71 | log.Printf("[fdbbd] TRIM is not implemented") 72 | return nil 73 | } 74 | 75 | func (d FdbStorage) Size() uint64 { 76 | return d.array.Size() 77 | } 78 | 79 | func (d FdbStorage) BlockSize() int32 { 80 | return int32(d.array.BlockSize()) 81 | } 82 | 83 | func main() { 84 | 85 | app := cli.NewApp() 86 | app.Name = "fdbbd" 87 | app.Version = "0.1.0" 88 | app.Usage = `block device using FoundationDB as a backend. 89 | Our motto: still more performant and reliable than EBS` 90 | 91 | app.Commands = []cli.Command{ 92 | { 93 | Name: "create", 94 | Usage: "Create a new volume", 95 | ArgsUsage: "[volume name]", 96 | Flags: []cli.Flag{ 97 | cli.IntFlag{ 98 | Name: "bs, blockSize", 99 | Usage: "size of a single block in bytes, must be a power of 2 and not more than 65536", 100 | Value: 4096, 101 | }, 102 | cli.StringFlag{ 103 | Name: "s, size", 104 | Usage: "size of the volume, e.g. 50GB. Valid units are KB, MB, GB, TB", 105 | Value: "512MB", 106 | }, 107 | }, 108 | Action: func(c *cli.Context) error { 109 | if !c.Args().Present() { 110 | return cli.NewExitError("volume name must me specified", 1) 111 | } 112 | 113 | blockSize := c.Int("blockSize") 114 | allowedBlockSizes := map[int]bool{ 115 | 512: true, 116 | 1024: true, 117 | 2048: true, 118 | 4096: true, 119 | 8192: true, 120 | 16384: true, 121 | 32768: true, 122 | 65536: true, 123 | } 124 | 125 | _, blockSizeValid := allowedBlockSizes[blockSize] 126 | 127 | if !blockSizeValid { 128 | return cli.NewExitError("blockSize must be a power of 2 but not more than 65536", 1) 129 | } 130 | 131 | fdb.MustAPIVersion(600) 132 | db := fdb.MustOpenDefault() 133 | 134 | size, sizeErr := units.ParseStrictBytes(c.String("size")) 135 | if sizeErr != nil { 136 | return cli.NewExitError("volume size is invalid", 1) 137 | } 138 | name := c.Args().Get(0) 139 | 140 | alreadyExists, _ := fdbarray.Exists(db, name) 141 | 142 | if alreadyExists { 143 | return cli.NewExitError("volume already exists", 1) 144 | } 145 | 146 | CreateStorageVolume(db, name, uint32(blockSize), uint64(size), 1) 147 | fmt.Printf("volume %s of size %s is created\n", name, humanize.Bytes(uint64(size))) 148 | return nil 149 | }, 150 | }, 151 | { 152 | Name: "list", 153 | Usage: "List all volumes", 154 | Action: func(c *cli.Context) error { 155 | 156 | fdb.MustAPIVersion(600) 157 | db := fdb.MustOpenDefault() 158 | 159 | description := fdbarray.List(db) 160 | 161 | fmt.Printf(" %-16s %-10s %s \n", "name", "blocksize", "size") 162 | for _, d := range description { 163 | fmt.Printf(" %-16s %-10d %s \n", d.VolumeName, d.BlockSize, humanize.Bytes(d.Size)) 164 | } 165 | 166 | return nil 167 | }, 168 | }, 169 | { 170 | Name: "attach", 171 | Usage: "Attach the volume", 172 | ArgsUsage: "[volume name] [device name]", 173 | Flags: []cli.Flag{ 174 | cli.UintFlag{ 175 | Name: "bpt", 176 | Usage: "Number of blocks being written per transaction in parallel. Smaller value will lead to lower latency and throughput," + 177 | " higher will increase throughput and latency. Not specifying the value will lead to writing all blocks of the NBD request in a single transaction", 178 | }, 179 | }, 180 | Action: func(c *cli.Context) error { 181 | if c.NArg() != 2 { 182 | return cli.NewExitError("volume name and device must me specified", 1) 183 | } 184 | volumeName := c.Args().Get(0) 185 | blockDeviceName := c.Args().Get(1) 186 | blocksPerTransaction := c.Uint("bpt") 187 | 188 | fdb.MustAPIVersion(600) 189 | 190 | // Open the default database from the system cluster 191 | db := fdb.MustOpenDefault() 192 | 193 | deviceExp := OpenStorageVolume(db, volumeName, uint32(blocksPerTransaction)) 194 | 195 | device, err := buse.CreateDevice(blockDeviceName, deviceExp.BlockSize(), deviceExp.Size(), deviceExp) 196 | if err != nil { 197 | fmt.Printf("Cannot create device: %s\n", err) 198 | os.Exit(1) 199 | } 200 | sig := make(chan os.Signal) 201 | signal.Notify(sig, os.Interrupt) 202 | fmt.Println("Waiting for SIGINT...") 203 | go func() { 204 | if err := device.Connect(); err != nil { 205 | log.Printf("Buse device stopped with error: %s", err) 206 | } else { 207 | log.Println("Buse device stopped gracefully.") 208 | } 209 | }() 210 | <-sig 211 | // Received SIGTERM, cleanup 212 | fmt.Println("SIGINT, disconnecting...") 213 | device.Disconnect() 214 | 215 | return nil 216 | }, 217 | }, 218 | { 219 | Name: "delete", 220 | Usage: "Delete the volume", 221 | ArgsUsage: "[volume name]", 222 | Action: func(c *cli.Context) error { 223 | if c.NArg() != 1 { 224 | return cli.NewExitError("volume name must me specified", 1) 225 | } 226 | volumeName := c.Args().Get(0) 227 | 228 | fdb.MustAPIVersion(600) 229 | 230 | // Open the default database from the system cluster 231 | db := fdb.MustOpenDefault() 232 | 233 | array, _ := fdbarray.OpenByName(db, volumeName, 1) 234 | 235 | array.Delete() 236 | 237 | fmt.Printf("volume %s deleted\n", volumeName) 238 | 239 | return nil 240 | }, 241 | }, 242 | } 243 | 244 | err := app.Run(os.Args) 245 | if err != nil { 246 | log.Fatal(err) 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/units/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014 Alec Thomas 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/units/README.md: -------------------------------------------------------------------------------- 1 | # Units - Helpful unit multipliers and functions for Go 2 | 3 | The goal of this package is to have functionality similar to the [time](http://golang.org/pkg/time/) package. 4 | 5 | It allows for code like this: 6 | 7 | ```go 8 | n, err := ParseBase2Bytes("1KB") 9 | // n == 1024 10 | n = units.Mebibyte * 512 11 | ``` 12 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/units/bytes.go: -------------------------------------------------------------------------------- 1 | package units 2 | 3 | // Base2Bytes is the old non-SI power-of-2 byte scale (1024 bytes in a kilobyte, 4 | // etc.). 5 | type Base2Bytes int64 6 | 7 | // Base-2 byte units. 8 | const ( 9 | Kibibyte Base2Bytes = 1024 10 | KiB = Kibibyte 11 | Mebibyte = Kibibyte * 1024 12 | MiB = Mebibyte 13 | Gibibyte = Mebibyte * 1024 14 | GiB = Gibibyte 15 | Tebibyte = Gibibyte * 1024 16 | TiB = Tebibyte 17 | Pebibyte = Tebibyte * 1024 18 | PiB = Pebibyte 19 | Exbibyte = Pebibyte * 1024 20 | EiB = Exbibyte 21 | ) 22 | 23 | var ( 24 | bytesUnitMap = MakeUnitMap("iB", "B", 1024) 25 | oldBytesUnitMap = MakeUnitMap("B", "B", 1024) 26 | ) 27 | 28 | // ParseBase2Bytes supports both iB and B in base-2 multipliers. That is, KB 29 | // and KiB are both 1024. 30 | func ParseBase2Bytes(s string) (Base2Bytes, error) { 31 | n, err := ParseUnit(s, bytesUnitMap) 32 | if err != nil { 33 | n, err = ParseUnit(s, oldBytesUnitMap) 34 | } 35 | return Base2Bytes(n), err 36 | } 37 | 38 | func (b Base2Bytes) String() string { 39 | return ToString(int64(b), 1024, "iB", "B") 40 | } 41 | 42 | var ( 43 | metricBytesUnitMap = MakeUnitMap("B", "B", 1000) 44 | ) 45 | 46 | // MetricBytes are SI byte units (1000 bytes in a kilobyte). 47 | type MetricBytes SI 48 | 49 | // SI base-10 byte units. 50 | const ( 51 | Kilobyte MetricBytes = 1000 52 | KB = Kilobyte 53 | Megabyte = Kilobyte * 1000 54 | MB = Megabyte 55 | Gigabyte = Megabyte * 1000 56 | GB = Gigabyte 57 | Terabyte = Gigabyte * 1000 58 | TB = Terabyte 59 | Petabyte = Terabyte * 1000 60 | PB = Petabyte 61 | Exabyte = Petabyte * 1000 62 | EB = Exabyte 63 | ) 64 | 65 | // ParseMetricBytes parses base-10 metric byte units. That is, KB is 1000 bytes. 66 | func ParseMetricBytes(s string) (MetricBytes, error) { 67 | n, err := ParseUnit(s, metricBytesUnitMap) 68 | return MetricBytes(n), err 69 | } 70 | 71 | func (m MetricBytes) String() string { 72 | return ToString(int64(m), 1000, "B", "B") 73 | } 74 | 75 | // ParseStrictBytes supports both iB and B suffixes for base 2 and metric, 76 | // respectively. That is, KiB represents 1024 and KB represents 1000. 77 | func ParseStrictBytes(s string) (int64, error) { 78 | n, err := ParseUnit(s, bytesUnitMap) 79 | if err != nil { 80 | n, err = ParseUnit(s, metricBytesUnitMap) 81 | } 82 | return int64(n), err 83 | } 84 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/units/doc.go: -------------------------------------------------------------------------------- 1 | // Package units provides helpful unit multipliers and functions for Go. 2 | // 3 | // The goal of this package is to have functionality similar to the time [1] package. 4 | // 5 | // 6 | // [1] http://golang.org/pkg/time/ 7 | // 8 | // It allows for code like this: 9 | // 10 | // n, err := ParseBase2Bytes("1KB") 11 | // // n == 1024 12 | // n = units.Mebibyte * 512 13 | package units 14 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/units/si.go: -------------------------------------------------------------------------------- 1 | package units 2 | 3 | // SI units. 4 | type SI int64 5 | 6 | // SI unit multiples. 7 | const ( 8 | Kilo SI = 1000 9 | Mega = Kilo * 1000 10 | Giga = Mega * 1000 11 | Tera = Giga * 1000 12 | Peta = Tera * 1000 13 | Exa = Peta * 1000 14 | ) 15 | 16 | func MakeUnitMap(suffix, shortSuffix string, scale int64) map[string]float64 { 17 | return map[string]float64{ 18 | shortSuffix: 1, 19 | "K" + suffix: float64(scale), 20 | "M" + suffix: float64(scale * scale), 21 | "G" + suffix: float64(scale * scale * scale), 22 | "T" + suffix: float64(scale * scale * scale * scale), 23 | "P" + suffix: float64(scale * scale * scale * scale * scale), 24 | "E" + suffix: float64(scale * scale * scale * scale * scale * scale), 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/units/util.go: -------------------------------------------------------------------------------- 1 | package units 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | var ( 10 | siUnits = []string{"", "K", "M", "G", "T", "P", "E"} 11 | ) 12 | 13 | func ToString(n int64, scale int64, suffix, baseSuffix string) string { 14 | mn := len(siUnits) 15 | out := make([]string, mn) 16 | for i, m := range siUnits { 17 | if n%scale != 0 || i == 0 && n == 0 { 18 | s := suffix 19 | if i == 0 { 20 | s = baseSuffix 21 | } 22 | out[mn-1-i] = fmt.Sprintf("%d%s%s", n%scale, m, s) 23 | } 24 | n /= scale 25 | if n == 0 { 26 | break 27 | } 28 | } 29 | return strings.Join(out, "") 30 | } 31 | 32 | // Below code ripped straight from http://golang.org/src/pkg/time/format.go?s=33392:33438#L1123 33 | var errLeadingInt = errors.New("units: bad [0-9]*") // never printed 34 | 35 | // leadingInt consumes the leading [0-9]* from s. 36 | func leadingInt(s string) (x int64, rem string, err error) { 37 | i := 0 38 | for ; i < len(s); i++ { 39 | c := s[i] 40 | if c < '0' || c > '9' { 41 | break 42 | } 43 | if x >= (1<<63-10)/10 { 44 | // overflow 45 | return 0, "", errLeadingInt 46 | } 47 | x = x*10 + int64(c) - '0' 48 | } 49 | return x, s[i:], nil 50 | } 51 | 52 | func ParseUnit(s string, unitMap map[string]float64) (int64, error) { 53 | // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+ 54 | orig := s 55 | f := float64(0) 56 | neg := false 57 | 58 | // Consume [-+]? 59 | if s != "" { 60 | c := s[0] 61 | if c == '-' || c == '+' { 62 | neg = c == '-' 63 | s = s[1:] 64 | } 65 | } 66 | // Special case: if all that is left is "0", this is zero. 67 | if s == "0" { 68 | return 0, nil 69 | } 70 | if s == "" { 71 | return 0, errors.New("units: invalid " + orig) 72 | } 73 | for s != "" { 74 | g := float64(0) // this element of the sequence 75 | 76 | var x int64 77 | var err error 78 | 79 | // The next character must be [0-9.] 80 | if !(s[0] == '.' || ('0' <= s[0] && s[0] <= '9')) { 81 | return 0, errors.New("units: invalid " + orig) 82 | } 83 | // Consume [0-9]* 84 | pl := len(s) 85 | x, s, err = leadingInt(s) 86 | if err != nil { 87 | return 0, errors.New("units: invalid " + orig) 88 | } 89 | g = float64(x) 90 | pre := pl != len(s) // whether we consumed anything before a period 91 | 92 | // Consume (\.[0-9]*)? 93 | post := false 94 | if s != "" && s[0] == '.' { 95 | s = s[1:] 96 | pl := len(s) 97 | x, s, err = leadingInt(s) 98 | if err != nil { 99 | return 0, errors.New("units: invalid " + orig) 100 | } 101 | scale := 1.0 102 | for n := pl - len(s); n > 0; n-- { 103 | scale *= 10 104 | } 105 | g += float64(x) / scale 106 | post = pl != len(s) 107 | } 108 | if !pre && !post { 109 | // no digits (e.g. ".s" or "-.s") 110 | return 0, errors.New("units: invalid " + orig) 111 | } 112 | 113 | // Consume unit. 114 | i := 0 115 | for ; i < len(s); i++ { 116 | c := s[i] 117 | if c == '.' || ('0' <= c && c <= '9') { 118 | break 119 | } 120 | } 121 | u := s[:i] 122 | s = s[i:] 123 | unit, ok := unitMap[u] 124 | if !ok { 125 | return 0, errors.New("units: unknown unit " + u + " in " + orig) 126 | } 127 | 128 | f += g * unit 129 | } 130 | 131 | if neg { 132 | f = -f 133 | } 134 | if f < float64(-1<<63) || f > float64(1<<63-1) { 135 | return 0, errors.New("units: overflow parsing unit") 136 | } 137 | return int64(f), nil 138 | } 139 | -------------------------------------------------------------------------------- /vendor/github.com/apple/foundationdb/bindings/go/src/fdb/cluster.go: -------------------------------------------------------------------------------- 1 | /* 2 | * cluster.go 3 | * 4 | * This source file is part of the FoundationDB open source project 5 | * 6 | * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | // FoundationDB Go API 22 | 23 | package fdb 24 | 25 | // #define FDB_API_VERSION 620 26 | // #include 27 | import "C" 28 | 29 | // Deprecated: Use OpenDatabase or OpenDefault to obtain a database handle directly. 30 | // Cluster is a handle to a FoundationDB cluster. Cluster is a lightweight 31 | // object that may be efficiently copied, and is safe for concurrent use by 32 | // multiple goroutines. 33 | type Cluster struct { 34 | clusterFileName string 35 | } 36 | 37 | // Deprecated: Use OpenDatabase or OpenDefault to obtain a database handle directly. 38 | // OpenDatabase returns a database handle from the FoundationDB cluster. 39 | // 40 | // The database name must be []byte("DB"). 41 | func (c Cluster) OpenDatabase(dbName []byte) (Database, error) { 42 | return Open(c.clusterFileName, dbName) 43 | } 44 | -------------------------------------------------------------------------------- /vendor/github.com/apple/foundationdb/bindings/go/src/fdb/database.go: -------------------------------------------------------------------------------- 1 | /* 2 | * database.go 3 | * 4 | * This source file is part of the FoundationDB open source project 5 | * 6 | * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | // FoundationDB Go API 22 | 23 | package fdb 24 | 25 | // #define FDB_API_VERSION 620 26 | // #include 27 | import "C" 28 | 29 | import ( 30 | "runtime" 31 | ) 32 | 33 | // Database is a handle to a FoundationDB database. Database is a lightweight 34 | // object that may be efficiently copied, and is safe for concurrent use by 35 | // multiple goroutines. 36 | // 37 | // Although Database provides convenience methods for reading and writing data, 38 | // modifications to a database are usually made via transactions, which are 39 | // usually created and committed automatically by the (Database).Transact 40 | // method. 41 | type Database struct { 42 | *database 43 | } 44 | 45 | type database struct { 46 | ptr *C.FDBDatabase 47 | } 48 | 49 | // DatabaseOptions is a handle with which to set options that affect a Database 50 | // object. A DatabaseOptions instance should be obtained with the 51 | // (Database).Options method. 52 | type DatabaseOptions struct { 53 | d *database 54 | } 55 | 56 | func (opt DatabaseOptions) setOpt(code int, param []byte) error { 57 | return setOpt(func(p *C.uint8_t, pl C.int) C.fdb_error_t { 58 | return C.fdb_database_set_option(opt.d.ptr, C.FDBDatabaseOption(code), p, pl) 59 | }, param) 60 | } 61 | 62 | func (d *database) destroy() { 63 | C.fdb_database_destroy(d.ptr) 64 | } 65 | 66 | // CreateTransaction returns a new FoundationDB transaction. It is generally 67 | // preferable to use the (Database).Transact method, which handles 68 | // automatically creating and committing a transaction with appropriate retry 69 | // behavior. 70 | func (d Database) CreateTransaction() (Transaction, error) { 71 | var outt *C.FDBTransaction 72 | 73 | if err := C.fdb_database_create_transaction(d.ptr, &outt); err != 0 { 74 | return Transaction{}, Error{int(err)} 75 | } 76 | 77 | t := &transaction{outt, d} 78 | runtime.SetFinalizer(t, (*transaction).destroy) 79 | 80 | return Transaction{t}, nil 81 | } 82 | 83 | func retryable(wrapped func() (interface{}, error), onError func(Error) FutureNil) (ret interface{}, e error) { 84 | for { 85 | ret, e = wrapped() 86 | 87 | // No error means success! 88 | if e == nil { 89 | return 90 | } 91 | 92 | ep, ok := e.(Error) 93 | if ok { 94 | e = onError(ep).Get() 95 | } 96 | 97 | // If OnError returns an error, then it's not 98 | // retryable; otherwise take another pass at things 99 | if e != nil { 100 | return 101 | } 102 | } 103 | } 104 | 105 | // Transact runs a caller-provided function inside a retry loop, providing it 106 | // with a newly created Transaction. After the function returns, the Transaction 107 | // will be committed automatically. Any error during execution of the function 108 | // (by panic or return) or the commit will cause the function and commit to be 109 | // retried or, if fatal, return the error to the caller. 110 | // 111 | // When working with Future objects in a transactional function, you may either 112 | // explicitly check and return error values using Get, or call MustGet. Transact 113 | // will recover a panicked Error and either retry the transaction or return the 114 | // error. 115 | // 116 | // Do not return Future objects from the function provided to Transact. The 117 | // Transaction created by Transact may be finalized at any point after Transact 118 | // returns, resulting in the cancellation of any outstanding 119 | // reads. Additionally, any errors returned or panicked by the Future will no 120 | // longer be able to trigger a retry of the caller-provided function. 121 | // 122 | // See the Transactor interface for an example of using Transact with 123 | // Transaction and Database objects. 124 | func (d Database) Transact(f func(Transaction) (interface{}, error)) (interface{}, error) { 125 | tr, e := d.CreateTransaction() 126 | // Any error here is non-retryable 127 | if e != nil { 128 | return nil, e 129 | } 130 | 131 | wrapped := func() (ret interface{}, e error) { 132 | defer panicToError(&e) 133 | 134 | ret, e = f(tr) 135 | 136 | if e == nil { 137 | e = tr.Commit().Get() 138 | } 139 | 140 | return 141 | } 142 | 143 | return retryable(wrapped, tr.OnError) 144 | } 145 | 146 | // ReadTransact runs a caller-provided function inside a retry loop, providing 147 | // it with a newly created Transaction (as a ReadTransaction). Any error during 148 | // execution of the function (by panic or return) will cause the function to be 149 | // retried or, if fatal, return the error to the caller. 150 | // 151 | // When working with Future objects in a read-only transactional function, you 152 | // may either explicitly check and return error values using Get, or call 153 | // MustGet. ReadTransact will recover a panicked Error and either retry the 154 | // transaction or return the error. 155 | // 156 | // Do not return Future objects from the function provided to ReadTransact. The 157 | // Transaction created by ReadTransact may be finalized at any point after 158 | // ReadTransact returns, resulting in the cancellation of any outstanding 159 | // reads. Additionally, any errors returned or panicked by the Future will no 160 | // longer be able to trigger a retry of the caller-provided function. 161 | // 162 | // See the ReadTransactor interface for an example of using ReadTransact with 163 | // Transaction, Snapshot and Database objects. 164 | func (d Database) ReadTransact(f func(ReadTransaction) (interface{}, error)) (interface{}, error) { 165 | tr, e := d.CreateTransaction() 166 | // Any error here is non-retryable 167 | if e != nil { 168 | return nil, e 169 | } 170 | 171 | wrapped := func() (ret interface{}, e error) { 172 | defer panicToError(&e) 173 | 174 | ret, e = f(tr) 175 | 176 | if e == nil { 177 | e = tr.Commit().Get() 178 | } 179 | 180 | return 181 | } 182 | 183 | return retryable(wrapped, tr.OnError) 184 | } 185 | 186 | // Options returns a DatabaseOptions instance suitable for setting options 187 | // specific to this database. 188 | func (d Database) Options() DatabaseOptions { 189 | return DatabaseOptions{d.database} 190 | } 191 | 192 | // LocalityGetBoundaryKeys returns a slice of keys that fall within the provided 193 | // range. Each key is located at the start of a contiguous range stored on a 194 | // single server. 195 | // 196 | // If limit is non-zero, only the first limit keys will be returned. In large 197 | // databases, the number of boundary keys may be large. In these cases, a 198 | // non-zero limit should be used, along with multiple calls to 199 | // LocalityGetBoundaryKeys. 200 | // 201 | // If readVersion is non-zero, the boundary keys as of readVersion will be 202 | // returned. 203 | func (d Database) LocalityGetBoundaryKeys(er ExactRange, limit int, readVersion int64) ([]Key, error) { 204 | tr, e := d.CreateTransaction() 205 | if e != nil { 206 | return nil, e 207 | } 208 | 209 | if readVersion != 0 { 210 | tr.SetReadVersion(readVersion) 211 | } 212 | 213 | tr.Options().SetReadSystemKeys() 214 | tr.Options().SetLockAware() 215 | 216 | bk, ek := er.FDBRangeKeys() 217 | ffer := KeyRange{ 218 | append(Key("\xFF/keyServers/"), bk.FDBKey()...), 219 | append(Key("\xFF/keyServers/"), ek.FDBKey()...), 220 | } 221 | 222 | kvs, e := tr.Snapshot().GetRange(ffer, RangeOptions{Limit: limit}).GetSliceWithError() 223 | if e != nil { 224 | return nil, e 225 | } 226 | 227 | size := len(kvs) 228 | if limit != 0 && limit < size { 229 | size = limit 230 | } 231 | 232 | boundaries := make([]Key, size) 233 | 234 | for i := 0; i < size; i++ { 235 | boundaries[i] = kvs[i].Key[13:] 236 | } 237 | 238 | return boundaries, nil 239 | } 240 | -------------------------------------------------------------------------------- /vendor/github.com/apple/foundationdb/bindings/go/src/fdb/directory/allocator.go: -------------------------------------------------------------------------------- 1 | /* 2 | * allocator.go 3 | * 4 | * This source file is part of the FoundationDB open source project 5 | * 6 | * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | // FoundationDB Go Directory Layer 22 | 23 | package directory 24 | 25 | import ( 26 | "bytes" 27 | "encoding/binary" 28 | "github.com/apple/foundationdb/bindings/go/src/fdb" 29 | "github.com/apple/foundationdb/bindings/go/src/fdb/subspace" 30 | "math/rand" 31 | "sync" 32 | ) 33 | 34 | var oneBytes = []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} 35 | var allocatorMutex = sync.Mutex{} 36 | 37 | type highContentionAllocator struct { 38 | counters, recent subspace.Subspace 39 | } 40 | 41 | func newHCA(s subspace.Subspace) highContentionAllocator { 42 | var hca highContentionAllocator 43 | 44 | hca.counters = s.Sub(0) 45 | hca.recent = s.Sub(1) 46 | 47 | return hca 48 | } 49 | 50 | func windowSize(start int64) int64 { 51 | // Larger window sizes are better for high contention, smaller sizes for 52 | // keeping the keys small. But if there are many allocations, the keys 53 | // can't be too small. So start small and scale up. We don't want this to 54 | // ever get *too* big because we have to store about window_size/2 recent 55 | // items. 56 | if start < 255 { 57 | return 64 58 | } 59 | if start < 65535 { 60 | return 1024 61 | } 62 | return 8192 63 | } 64 | 65 | func (hca highContentionAllocator) allocate(tr fdb.Transaction, s subspace.Subspace) (subspace.Subspace, error) { 66 | for { 67 | rr := tr.Snapshot().GetRange(hca.counters, fdb.RangeOptions{Limit: 1, Reverse: true}) 68 | kvs, e := rr.GetSliceWithError() 69 | if e != nil { 70 | return nil, e 71 | } 72 | 73 | var start int64 74 | var window int64 75 | 76 | if len(kvs) == 1 { 77 | t, e := hca.counters.Unpack(kvs[0].Key) 78 | if e != nil { 79 | return nil, e 80 | } 81 | start = t[0].(int64) 82 | } 83 | 84 | windowAdvanced := false 85 | for { 86 | allocatorMutex.Lock() 87 | 88 | if windowAdvanced { 89 | tr.ClearRange(fdb.KeyRange{hca.counters, hca.counters.Sub(start)}) 90 | tr.Options().SetNextWriteNoWriteConflictRange() 91 | tr.ClearRange(fdb.KeyRange{hca.recent, hca.recent.Sub(start)}) 92 | } 93 | 94 | // Increment the allocation count for the current window 95 | tr.Add(hca.counters.Sub(start), oneBytes) 96 | countFuture := tr.Snapshot().Get(hca.counters.Sub(start)) 97 | 98 | allocatorMutex.Unlock() 99 | 100 | countStr, e := countFuture.Get() 101 | if e != nil { 102 | return nil, e 103 | } 104 | 105 | var count int64 106 | if countStr == nil { 107 | count = 0 108 | } else { 109 | e = binary.Read(bytes.NewBuffer(countStr), binary.LittleEndian, &count) 110 | if e != nil { 111 | return nil, e 112 | } 113 | } 114 | 115 | window = windowSize(start) 116 | if count*2 < window { 117 | break 118 | } 119 | 120 | start += window 121 | windowAdvanced = true 122 | } 123 | 124 | for { 125 | // As of the snapshot being read from, the window is less than half 126 | // full, so this should be expected to take 2 tries. Under high 127 | // contention (and when the window advances), there is an additional 128 | // subsequent risk of conflict for this transaction. 129 | candidate := rand.Int63n(window) + start 130 | key := hca.recent.Sub(candidate) 131 | 132 | allocatorMutex.Lock() 133 | 134 | latestCounter := tr.Snapshot().GetRange(hca.counters, fdb.RangeOptions{Limit: 1, Reverse: true}) 135 | candidateValue := tr.Get(key) 136 | tr.Options().SetNextWriteNoWriteConflictRange() 137 | tr.Set(key, []byte("")) 138 | 139 | allocatorMutex.Unlock() 140 | 141 | kvs, e = latestCounter.GetSliceWithError() 142 | if e != nil { 143 | return nil, e 144 | } 145 | if len(kvs) > 0 { 146 | t, e := hca.counters.Unpack(kvs[0].Key) 147 | if e != nil { 148 | return nil, e 149 | } 150 | currentStart := t[0].(int64) 151 | if currentStart > start { 152 | break 153 | } 154 | } 155 | 156 | v, e := candidateValue.Get() 157 | if e != nil { 158 | return nil, e 159 | } 160 | if v == nil { 161 | tr.AddWriteConflictKey(key) 162 | return s.Sub(candidate), nil 163 | } 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /vendor/github.com/apple/foundationdb/bindings/go/src/fdb/directory/directory.go: -------------------------------------------------------------------------------- 1 | /* 2 | * directory.go 3 | * 4 | * This source file is part of the FoundationDB open source project 5 | * 6 | * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | // FoundationDB Go Directory Layer 22 | 23 | // Package directory provides a tool for managing related subspaces. Directories 24 | // are a recommended approach for administering applications. Each application 25 | // should create or open at least one directory to manage its subspaces. 26 | // 27 | // For general guidance on directory usage, see the Directories section of the 28 | // Developer Guide 29 | // (https://apple.github.io/foundationdb/developer-guide.html#directories). 30 | // 31 | // Directories are identified by hierarchical paths analogous to the paths in a 32 | // Unix-like file system. A path is represented as a slice of strings. Each 33 | // directory has an associated subspace used to store its content. The directory 34 | // layer maps each path to a short prefix used for the corresponding 35 | // subspace. In effect, directories provide a level of indirection for access to 36 | // subspaces. 37 | // 38 | // Directory operations are transactional. A byte slice layer option is used as 39 | // a metadata identifier when opening a directory. 40 | package directory 41 | 42 | import ( 43 | "errors" 44 | 45 | "github.com/apple/foundationdb/bindings/go/src/fdb" 46 | "github.com/apple/foundationdb/bindings/go/src/fdb/subspace" 47 | ) 48 | 49 | const ( 50 | _SUBDIRS int = 0 51 | 52 | // []int32{1,0,0} by any other name 53 | _MAJORVERSION int32 = 1 54 | _MINORVERSION int32 = 0 55 | _MICROVERSION int32 = 0 56 | ) 57 | 58 | var ( 59 | // ErrDirAlreadyExists is returned when trying to create a directory while it already exists. 60 | ErrDirAlreadyExists = errors.New("the directory already exists") 61 | 62 | // ErrDirNotExists is returned when opening or listing a directory that does not exist. 63 | ErrDirNotExists = errors.New("the directory does not exist") 64 | 65 | // ErrParentDirDoesNotExist is returned when opening a directory and one or more 66 | // parent directories in the path do not exist. 67 | ErrParentDirDoesNotExist = errors.New("the parent directory does not exist") 68 | ) 69 | 70 | // Directory represents a subspace of keys in a FoundationDB database, 71 | // identified by a hierarchical path. 72 | type Directory interface { 73 | // CreateOrOpen opens the directory specified by path (relative to this 74 | // Directory), and returns the directory and its contents as a 75 | // DirectorySubspace. If the directory does not exist, it is created 76 | // (creating parent directories if necessary). 77 | // 78 | // If the byte slice layer is specified and the directory is new, it is 79 | // recorded as the layer; if layer is specified and the directory already 80 | // exists, it is compared against the layer specified when the directory was 81 | // created, and an error is returned if they differ. 82 | CreateOrOpen(t fdb.Transactor, path []string, layer []byte) (DirectorySubspace, error) 83 | 84 | // Open opens the directory specified by path (relative to this Directory), 85 | // and returns the directory and its contents as a DirectorySubspace (or ErrDirNotExists 86 | // error if the directory does not exist, or ErrParentDirDoesNotExist if one of the parent 87 | // directories in the path does not exist). 88 | // 89 | // If the byte slice layer is specified, it is compared against the layer 90 | // specified when the directory was created, and an error is returned if 91 | // they differ. 92 | Open(rt fdb.ReadTransactor, path []string, layer []byte) (DirectorySubspace, error) 93 | 94 | // Create creates a directory specified by path (relative to this 95 | // Directory), and returns the directory and its contents as a 96 | // DirectorySubspace (or ErrDirAlreadyExists if the directory already exists). 97 | // 98 | // If the byte slice layer is specified, it is recorded as the layer and 99 | // will be checked when opening the directory in the future. 100 | Create(t fdb.Transactor, path []string, layer []byte) (DirectorySubspace, error) 101 | 102 | // CreatePrefix behaves like Create, but uses a manually specified byte 103 | // slice prefix to physically store the contents of this directory, rather 104 | // than an automatically allocated prefix. 105 | // 106 | // If this Directory was created in a root directory that does not allow 107 | // manual prefixes, CreatePrefix will return an error. The default root 108 | // directory does not allow manual prefixes. 109 | CreatePrefix(t fdb.Transactor, path []string, layer []byte, prefix []byte) (DirectorySubspace, error) 110 | 111 | // Move moves the directory at oldPath to newPath (both relative to this 112 | // Directory), and returns the directory (at its new location) and its 113 | // contents as a DirectorySubspace. Move will return an error if a directory 114 | // does not exist at oldPath, a directory already exists at newPath, or the 115 | // parent directory of newPath does not exist. 116 | // 117 | // There is no effect on the physical prefix of the given directory or on 118 | // clients that already have the directory open. 119 | Move(t fdb.Transactor, oldPath []string, newPath []string) (DirectorySubspace, error) 120 | 121 | // MoveTo moves this directory to newAbsolutePath (relative to the root 122 | // directory of this Directory), and returns the directory (at its new 123 | // location) and its contents as a DirectorySubspace. MoveTo will return an 124 | // error if a directory already exists at newAbsolutePath or the parent 125 | // directory of newAbsolutePath does not exist. 126 | // 127 | // There is no effect on the physical prefix of the given directory or on 128 | // clients that already have the directory open. 129 | MoveTo(t fdb.Transactor, newAbsolutePath []string) (DirectorySubspace, error) 130 | 131 | // Remove removes the directory at path (relative to this Directory), its 132 | // content, and all subdirectories. Remove returns true if a directory 133 | // existed at path and was removed, and false if no directory exists at 134 | // path. 135 | // 136 | // Note that clients that have already opened this directory might still 137 | // insert data into its contents after removal. 138 | Remove(t fdb.Transactor, path []string) (bool, error) 139 | 140 | // Exists returns true if the directory at path (relative to this Directory) 141 | // exists, and false otherwise. 142 | Exists(rt fdb.ReadTransactor, path []string) (bool, error) 143 | 144 | // List returns the names of the immediate subdirectories of the directory 145 | // at path (relative to this Directory) as a slice of strings. Each string 146 | // is the name of the last component of a subdirectory's path. 147 | List(rt fdb.ReadTransactor, path []string) ([]string, error) 148 | 149 | // GetLayer returns the layer specified when this Directory was created. 150 | GetLayer() []byte 151 | 152 | // GetPath returns the path with which this Directory was opened. 153 | GetPath() []string 154 | } 155 | 156 | func stringsEqual(a, b []string) bool { 157 | if len(a) != len(b) { 158 | return false 159 | } 160 | for i, v := range a { 161 | if v != b[i] { 162 | return false 163 | } 164 | } 165 | return true 166 | } 167 | 168 | func moveTo(t fdb.Transactor, dl directoryLayer, path, newAbsolutePath []string) (DirectorySubspace, error) { 169 | partition_len := len(dl.path) 170 | 171 | if !stringsEqual(newAbsolutePath[:partition_len], dl.path) { 172 | return nil, errors.New("cannot move between partitions") 173 | } 174 | 175 | return dl.Move(t, path[partition_len:], newAbsolutePath[partition_len:]) 176 | } 177 | 178 | var root = NewDirectoryLayer(subspace.FromBytes([]byte{0xFE}), subspace.AllKeys(), false) 179 | 180 | // CreateOrOpen opens the directory specified by path (resolved relative to the 181 | // default root directory), and returns the directory and its contents as a 182 | // DirectorySubspace. If the directory does not exist, it is created (creating 183 | // parent directories if necessary). 184 | // 185 | // If the byte slice layer is specified and the directory is new, it is recorded 186 | // as the layer; if layer is specified and the directory already exists, it is 187 | // compared against the layer specified when the directory was created, and an 188 | // error is returned if they differ. 189 | func CreateOrOpen(t fdb.Transactor, path []string, layer []byte) (DirectorySubspace, error) { 190 | return root.CreateOrOpen(t, path, layer) 191 | } 192 | 193 | // Open opens the directory specified by path (resolved relative to the default 194 | // root directory), and returns the directory and its contents as a 195 | // DirectorySubspace (or an error if the directory does not exist). 196 | // 197 | // If the byte slice layer is specified, it is compared against the layer 198 | // specified when the directory was created, and an error is returned if they 199 | // differ. 200 | func Open(rt fdb.ReadTransactor, path []string, layer []byte) (DirectorySubspace, error) { 201 | return root.Open(rt, path, layer) 202 | } 203 | 204 | // Create creates a directory specified by path (resolved relative to the 205 | // default root directory), and returns the directory and its contents as a 206 | // DirectorySubspace (or an error if the directory already exists). 207 | // 208 | // If the byte slice layer is specified, it is recorded as the layer and will be 209 | // checked when opening the directory in the future. 210 | func Create(t fdb.Transactor, path []string, layer []byte) (DirectorySubspace, error) { 211 | return root.Create(t, path, layer) 212 | } 213 | 214 | // Move moves the directory at oldPath to newPath (both resolved relative to the 215 | // default root directory), and returns the directory (at its new location) and 216 | // its contents as a DirectorySubspace. Move will return an error if a directory 217 | // does not exist at oldPath, a directory already exists at newPath, or the 218 | // parent directory of newPath does not exit. 219 | // 220 | // There is no effect on the physical prefix of the given directory or on 221 | // clients that already have the directory open. 222 | func Move(t fdb.Transactor, oldPath []string, newPath []string) (DirectorySubspace, error) { 223 | return root.Move(t, oldPath, newPath) 224 | } 225 | 226 | // Exists returns true if the directory at path (relative to the default root 227 | // directory) exists, and false otherwise. 228 | func Exists(rt fdb.ReadTransactor, path []string) (bool, error) { 229 | return root.Exists(rt, path) 230 | } 231 | 232 | // List returns the names of the immediate subdirectories of the default root 233 | // directory as a slice of strings. Each string is the name of the last 234 | // component of a subdirectory's path. 235 | func List(rt fdb.ReadTransactor, path []string) ([]string, error) { 236 | return root.List(rt, path) 237 | } 238 | 239 | // Root returns the default root directory. Any attempt to move or remove the 240 | // root directory will return an error. 241 | // 242 | // The default root directory stores directory layer metadata in keys beginning 243 | // with 0xFE, and allocates newly created directories in (unused) prefixes 244 | // starting with 0x00 through 0xFD. This is appropriate for otherwise empty 245 | // databases, but may conflict with other formal or informal partitionings of 246 | // keyspace. If you already have other content in your database, you may wish to 247 | // use NewDirectoryLayer to construct a non-standard root directory to control 248 | // where metadata and keys are stored. 249 | // 250 | // As an alternative to Root, you may use the package-level functions 251 | // CreateOrOpen, Open, Create, CreatePrefix, Move, Exists and List to operate 252 | // directly on the default DirectoryLayer. 253 | func Root() Directory { 254 | return root 255 | } 256 | -------------------------------------------------------------------------------- /vendor/github.com/apple/foundationdb/bindings/go/src/fdb/directory/directoryPartition.go: -------------------------------------------------------------------------------- 1 | /* 2 | * directoryPartition.go 3 | * 4 | * This source file is part of the FoundationDB open source project 5 | * 6 | * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | // FoundationDB Go Directory Layer 22 | 23 | package directory 24 | 25 | import ( 26 | "github.com/apple/foundationdb/bindings/go/src/fdb" 27 | "github.com/apple/foundationdb/bindings/go/src/fdb/subspace" 28 | "github.com/apple/foundationdb/bindings/go/src/fdb/tuple" 29 | ) 30 | 31 | type directoryPartition struct { 32 | directoryLayer 33 | parentDirectoryLayer directoryLayer 34 | } 35 | 36 | func (dp directoryPartition) Sub(el ...tuple.TupleElement) subspace.Subspace { 37 | panic("cannot open subspace in the root of a directory partition") 38 | } 39 | 40 | func (dp directoryPartition) Bytes() []byte { 41 | panic("cannot get key for the root of a directory partition") 42 | } 43 | 44 | func (dp directoryPartition) Pack(t tuple.Tuple) fdb.Key { 45 | panic("cannot pack keys using the root of a directory partition") 46 | } 47 | 48 | func (dp directoryPartition) Unpack(k fdb.KeyConvertible) (tuple.Tuple, error) { 49 | panic("cannot unpack keys using the root of a directory partition") 50 | } 51 | 52 | func (dp directoryPartition) Contains(k fdb.KeyConvertible) bool { 53 | panic("cannot check whether a key belongs to the root of a directory partition") 54 | } 55 | 56 | func (dp directoryPartition) FDBKey() fdb.Key { 57 | panic("cannot use the root of a directory partition as a key") 58 | } 59 | 60 | func (dp directoryPartition) FDBRangeKeys() (fdb.KeyConvertible, fdb.KeyConvertible) { 61 | panic("cannot get range for the root of a directory partition") 62 | } 63 | 64 | func (dp directoryPartition) FDBRangeKeySelectors() (fdb.Selectable, fdb.Selectable) { 65 | panic("cannot get range for the root of a directory partition") 66 | } 67 | 68 | func (dp directoryPartition) GetLayer() []byte { 69 | return []byte("partition") 70 | } 71 | 72 | func (dp directoryPartition) getLayerForPath(path []string) directoryLayer { 73 | if len(path) == 0 { 74 | return dp.parentDirectoryLayer 75 | } 76 | return dp.directoryLayer 77 | } 78 | 79 | func (dp directoryPartition) MoveTo(t fdb.Transactor, newAbsolutePath []string) (DirectorySubspace, error) { 80 | return moveTo(t, dp.parentDirectoryLayer, dp.path, newAbsolutePath) 81 | } 82 | 83 | func (dp directoryPartition) Remove(t fdb.Transactor, path []string) (bool, error) { 84 | dl := dp.getLayerForPath(path) 85 | return dl.Remove(t, dl.partitionSubpath(dp.path, path)) 86 | } 87 | 88 | func (dp directoryPartition) Exists(rt fdb.ReadTransactor, path []string) (bool, error) { 89 | dl := dp.getLayerForPath(path) 90 | return dl.Exists(rt, dl.partitionSubpath(dp.path, path)) 91 | } 92 | -------------------------------------------------------------------------------- /vendor/github.com/apple/foundationdb/bindings/go/src/fdb/directory/directorySubspace.go: -------------------------------------------------------------------------------- 1 | /* 2 | * directorySubspace.go 3 | * 4 | * This source file is part of the FoundationDB open source project 5 | * 6 | * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | // FoundationDB Go Directory Layer 22 | 23 | package directory 24 | 25 | import ( 26 | "github.com/apple/foundationdb/bindings/go/src/fdb" 27 | "github.com/apple/foundationdb/bindings/go/src/fdb/subspace" 28 | ) 29 | 30 | // DirectorySubspace represents a Directory that may also be used as a Subspace 31 | // to store key/value pairs. Subdirectories of a root directory (as returned by 32 | // Root or NewDirectoryLayer) are DirectorySubspaces, and provide all methods of 33 | // the Directory and subspace.Subspace interfaces. 34 | type DirectorySubspace interface { 35 | subspace.Subspace 36 | Directory 37 | } 38 | 39 | type directorySubspace struct { 40 | subspace.Subspace 41 | dl directoryLayer 42 | path []string 43 | layer []byte 44 | } 45 | 46 | func (d directorySubspace) CreateOrOpen(t fdb.Transactor, path []string, layer []byte) (DirectorySubspace, error) { 47 | return d.dl.CreateOrOpen(t, d.dl.partitionSubpath(d.path, path), layer) 48 | } 49 | 50 | func (d directorySubspace) Create(t fdb.Transactor, path []string, layer []byte) (DirectorySubspace, error) { 51 | return d.dl.Create(t, d.dl.partitionSubpath(d.path, path), layer) 52 | } 53 | 54 | func (d directorySubspace) CreatePrefix(t fdb.Transactor, path []string, layer []byte, prefix []byte) (DirectorySubspace, error) { 55 | return d.dl.CreatePrefix(t, d.dl.partitionSubpath(d.path, path), layer, prefix) 56 | } 57 | 58 | func (d directorySubspace) Open(rt fdb.ReadTransactor, path []string, layer []byte) (DirectorySubspace, error) { 59 | return d.dl.Open(rt, d.dl.partitionSubpath(d.path, path), layer) 60 | } 61 | 62 | func (d directorySubspace) MoveTo(t fdb.Transactor, newAbsolutePath []string) (DirectorySubspace, error) { 63 | return moveTo(t, d.dl, d.path, newAbsolutePath) 64 | } 65 | 66 | func (d directorySubspace) Move(t fdb.Transactor, oldPath []string, newPath []string) (DirectorySubspace, error) { 67 | return d.dl.Move(t, d.dl.partitionSubpath(d.path, oldPath), d.dl.partitionSubpath(d.path, newPath)) 68 | } 69 | 70 | func (d directorySubspace) Remove(t fdb.Transactor, path []string) (bool, error) { 71 | return d.dl.Remove(t, d.dl.partitionSubpath(d.path, path)) 72 | } 73 | 74 | func (d directorySubspace) Exists(rt fdb.ReadTransactor, path []string) (bool, error) { 75 | return d.dl.Exists(rt, d.dl.partitionSubpath(d.path, path)) 76 | } 77 | 78 | func (d directorySubspace) List(rt fdb.ReadTransactor, path []string) (subdirs []string, e error) { 79 | return d.dl.List(rt, d.dl.partitionSubpath(d.path, path)) 80 | } 81 | 82 | func (d directorySubspace) GetLayer() []byte { 83 | return d.layer 84 | } 85 | 86 | func (d directorySubspace) GetPath() []string { 87 | return d.path 88 | } 89 | -------------------------------------------------------------------------------- /vendor/github.com/apple/foundationdb/bindings/go/src/fdb/directory/node.go: -------------------------------------------------------------------------------- 1 | /* 2 | * node.go 3 | * 4 | * This source file is part of the FoundationDB open source project 5 | * 6 | * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | // FoundationDB Go Directory Layer 22 | 23 | package directory 24 | 25 | import ( 26 | "bytes" 27 | "github.com/apple/foundationdb/bindings/go/src/fdb" 28 | "github.com/apple/foundationdb/bindings/go/src/fdb/subspace" 29 | ) 30 | 31 | type node struct { 32 | subspace subspace.Subspace 33 | path []string 34 | targetPath []string 35 | _layer fdb.FutureByteSlice 36 | } 37 | 38 | func (n *node) exists() bool { 39 | if n.subspace == nil { 40 | return false 41 | } 42 | return true 43 | } 44 | 45 | func (n *node) prefetchMetadata(rtr fdb.ReadTransaction) *node { 46 | if n.exists() { 47 | n.layer(rtr) 48 | } 49 | return n 50 | } 51 | 52 | func (n *node) layer(rtr fdb.ReadTransaction) fdb.FutureByteSlice { 53 | if n._layer == nil { 54 | fv := rtr.Get(n.subspace.Sub([]byte("layer"))) 55 | n._layer = fv 56 | } 57 | 58 | return n._layer 59 | } 60 | 61 | func (n *node) isInPartition(tr *fdb.Transaction, includeEmptySubpath bool) bool { 62 | return n.exists() && bytes.Compare(n._layer.MustGet(), []byte("partition")) == 0 && (includeEmptySubpath || len(n.targetPath) > len(n.path)) 63 | } 64 | 65 | func (n *node) getPartitionSubpath() []string { 66 | return n.targetPath[len(n.path):] 67 | } 68 | 69 | func (n *node) getContents(dl directoryLayer, tr *fdb.Transaction) (DirectorySubspace, error) { 70 | l, err := n._layer.Get() 71 | if err != nil { 72 | return nil, err 73 | } 74 | return dl.contentsOfNode(n.subspace, n.path, l) 75 | } 76 | -------------------------------------------------------------------------------- /vendor/github.com/apple/foundationdb/bindings/go/src/fdb/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | * doc.go 3 | * 4 | * This source file is part of the FoundationDB open source project 5 | * 6 | * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | // FoundationDB Go API 22 | 23 | /* 24 | Package fdb provides an interface to FoundationDB databases (version 2.0 or higher). 25 | 26 | To build and run programs using this package, you must have an installed copy of 27 | the FoundationDB client libraries (version 2.0.0 or later), available for Linux, 28 | Windows and OS X at https://www.foundationdb.org/download/. 29 | 30 | This documentation specifically applies to the FoundationDB Go binding. For more 31 | extensive guidance to programming with FoundationDB, as well as API 32 | documentation for the other FoundationDB interfaces, please see 33 | https://apple.github.io/foundationdb/index.html. 34 | 35 | Basic Usage 36 | 37 | A basic interaction with the FoundationDB API is demonstrated below: 38 | 39 | package main 40 | 41 | import ( 42 | "github.com/apple/foundationdb/bindings/go/src/fdb" 43 | "log" 44 | "fmt" 45 | ) 46 | 47 | func main() { 48 | // Different API versions may expose different runtime behaviors. 49 | fdb.MustAPIVersion(620) 50 | 51 | // Open the default database from the system cluster 52 | db := fdb.MustOpenDefault() 53 | 54 | // Database reads and writes happen inside transactions 55 | ret, e := db.Transact(func(tr fdb.Transaction) (interface{}, error) { 56 | tr.Set(fdb.Key("hello"), []byte("world")) 57 | return tr.Get(fdb.Key("foo")).MustGet(), nil 58 | // db.Transact automatically commits (and if necessary, 59 | // retries) the transaction 60 | }) 61 | if e != nil { 62 | log.Fatalf("Unable to perform FDB transaction (%v)", e) 63 | } 64 | 65 | fmt.Printf("hello is now world, foo was: %s\n", string(ret.([]byte))) 66 | } 67 | 68 | Futures 69 | 70 | Many functions in this package are asynchronous and return Future objects. A 71 | Future represents a value (or error) to be available at some later 72 | time. Functions documented as blocking on a Future will block the calling 73 | goroutine until the Future is ready (although if the Future is already ready, 74 | the call will not block at all). While a goroutine is blocked on a Future, other 75 | goroutines are free to execute and interact with the FoundationDB API. 76 | 77 | It is possible (and often recommended) to call several asynchronous operations 78 | and have multiple Future objects outstanding inside a single goroutine. All 79 | operations will execute in parallel, and the calling goroutine will not block 80 | until a blocking method on any one of the Futures is called. 81 | 82 | On Panics 83 | 84 | Idiomatic Go code strongly frowns at panics that escape library/package 85 | boundaries, in favor of explicitly returned errors. Idiomatic FoundationDB 86 | client programs, however, are built around the idea of retryable 87 | programmer-provided transactional functions. Retryable transactions can be 88 | implemented using only error values: 89 | 90 | ret, e := db.Transact(func (tr Transaction) (interface{}, error) { 91 | // FoundationDB futures represent a value that will become available 92 | futureValueOne := tr.Get(fdb.Key("foo")) 93 | futureValueTwo := tr.Get(fdb.Key("bar")) 94 | 95 | // Both reads are being carried out in parallel 96 | 97 | // Get the first value (or any error) 98 | valueOne, e := futureValueOne.Get() 99 | if e != nil { 100 | return nil, e 101 | } 102 | 103 | // Get the second value (or any error) 104 | valueTwo, e := futureValueTwo.Get() 105 | if e != nil { 106 | return nil, e 107 | } 108 | 109 | // Return the two values 110 | return []string{valueOne, valueTwo}, nil 111 | }) 112 | 113 | If either read encounters an error, it will be returned to Transact, which will 114 | determine if the error is retryable or not (using (Transaction).OnError). If the 115 | error is an FDB Error and retryable (such as a conflict with with another 116 | transaction), then the programmer-provided function will be run again. If the 117 | error is fatal (or not an FDB Error), then the error will be returned to the 118 | caller of Transact. 119 | 120 | In practice, checking for an error from every asynchronous future type in the 121 | FoundationDB API quickly becomes frustrating. As a convenience, every Future 122 | type also has a MustGet method, which returns the same type and value as Get, 123 | but exposes FoundationDB Errors via a panic rather than an explicitly returned 124 | error. The above example may be rewritten as: 125 | 126 | ret, e := db.Transact(func (tr Transaction) (interface{}, error) { 127 | // FoundationDB futures represent a value that will become available 128 | futureValueOne := tr.Get(fdb.Key("foo")) 129 | futureValueTwo := tr.Get(fdb.Key("bar")) 130 | 131 | // Both reads are being carried out in parallel 132 | 133 | // Get the first value 134 | valueOne := futureValueOne.MustGet() 135 | // Get the second value 136 | valueTwo := futureValueTwo.MustGet() 137 | 138 | // Return the two values 139 | return []string{valueOne, valueTwo}, nil 140 | }) 141 | 142 | Any panic that occurs during execution of the caller-provided function will be 143 | recovered by the (Database).Transact method. If the error is an FDB Error, it 144 | will either result in a retry of the function or be returned by Transact. If the 145 | error is any other type (panics from code other than MustGet), Transact will 146 | re-panic the original value. 147 | 148 | Note that (Transaction).Transact also recovers panics, but does not itself 149 | retry. If the recovered value is an FDB Error, it will be returned to the caller 150 | of (Transaction).Transact; all other values will be re-panicked. 151 | 152 | Transactions and Goroutines 153 | 154 | When using a Transactor in the fdb package, particular care must be taken if 155 | goroutines are created inside of the function passed to the Transact method. Any 156 | panic from the goroutine will not be recovered by Transact, and (unless 157 | otherwise recovered) will result in the termination of that goroutine. 158 | 159 | Furthermore, any errors returned or panicked by fdb methods called in the 160 | goroutine must be safely returned to the function passed to Transact, and either 161 | returned or panicked, to allow Transact to appropriately retry or terminate the 162 | transactional function. 163 | 164 | Lastly, a transactional function may be retried indefinitely. It is advisable to 165 | make sure any goroutines created during the transactional function have 166 | completed before returning from the transactional function, or a potentially 167 | unbounded number of goroutines may be created. 168 | 169 | Given these complexities, it is generally best practice to use a single 170 | goroutine for each logical thread of interaction with FoundationDB, and allow 171 | each goroutine to block when necessary to wait for Futures to become ready. 172 | 173 | Streaming Modes 174 | 175 | When using GetRange methods in the FoundationDB API, clients can request large 176 | ranges of the database to iterate over. Making such a request doesn't 177 | necessarily mean that the client will consume all of the data in the range -- 178 | sometimes the client doesn't know how far it intends to iterate in 179 | advance. FoundationDB tries to balance latency and bandwidth by requesting data 180 | for iteration in batches. 181 | 182 | The Mode field of the RangeOptions struct allows a client to customize this 183 | performance tradeoff by providing extra information about how the iterator will 184 | be used. 185 | 186 | The default value of Mode is StreamingModeIterator, which tries to provide a 187 | reasonable default balance. Other streaming modes that prioritize throughput or 188 | latency are available -- see the documented StreamingMode values for specific 189 | options. 190 | 191 | Atomic Operations 192 | 193 | The FDB package provides a number of atomic operations on the Database and 194 | Transaction objects. An atomic operation is a single database command that 195 | carries out several logical steps: reading the value of a key, performing a 196 | transformation on that value, and writing the result. Different atomic 197 | operations perform different transformations. Like other database operations, an 198 | atomic operation is used within a transaction. 199 | 200 | For more information on atomic operations in FoundationDB, please see 201 | https://apple.github.io/foundationdb/developer-guide.html#atomic-operations. The 202 | operands to atomic operations in this API must be provided as appropriately 203 | encoded byte slices. To convert a Go type to a byte slice, see the binary 204 | package. 205 | 206 | The current atomic operations in this API are Add, BitAnd, BitOr, BitXor, 207 | CompareAndClear, Max, Min, SetVersionstampedKey, SetVersionstampedValue 208 | (all methods on Transaction). 209 | */ 210 | package fdb 211 | -------------------------------------------------------------------------------- /vendor/github.com/apple/foundationdb/bindings/go/src/fdb/errors.go: -------------------------------------------------------------------------------- 1 | /* 2 | * errors.go 3 | * 4 | * This source file is part of the FoundationDB open source project 5 | * 6 | * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | // FoundationDB Go API 22 | 23 | package fdb 24 | 25 | // #define FDB_API_VERSION 620 26 | // #include 27 | import "C" 28 | 29 | import ( 30 | "fmt" 31 | ) 32 | 33 | // Error represents a low-level error returned by the FoundationDB C library. An 34 | // Error may be returned by any FoundationDB API function that returns error, or 35 | // as a panic from any FoundationDB API function whose name ends with OrPanic. 36 | // 37 | // You may compare the Code field of an Error against the list of FoundationDB 38 | // error codes at https://apple.github.io/foundationdb/api-error-codes.html, 39 | // but generally an Error should be passed to (Transaction).OnError. When using 40 | // (Database).Transact, non-fatal errors will be retried automatically. 41 | type Error struct { 42 | Code int 43 | } 44 | 45 | func (e Error) Error() string { 46 | return fmt.Sprintf("FoundationDB error code %d (%s)", e.Code, C.GoString(C.fdb_get_error(C.fdb_error_t(e.Code)))) 47 | } 48 | 49 | // SOMEDAY: these (along with others) should be coming from fdb.options? 50 | 51 | var ( 52 | errNetworkNotSetup = Error{2008} 53 | 54 | errAPIVersionUnset = Error{2200} 55 | errAPIVersionAlreadySet = Error{2201} 56 | errAPIVersionNotSupported = Error{2203} 57 | ) 58 | -------------------------------------------------------------------------------- /vendor/github.com/apple/foundationdb/bindings/go/src/fdb/keyselector.go: -------------------------------------------------------------------------------- 1 | /* 2 | * keyselector.go 3 | * 4 | * This source file is part of the FoundationDB open source project 5 | * 6 | * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | // FoundationDB Go API 22 | 23 | package fdb 24 | 25 | // A Selectable can be converted to a FoundationDB KeySelector. All functions in 26 | // the FoundationDB API that resolve a key selector to a key accept Selectable. 27 | type Selectable interface { 28 | FDBKeySelector() KeySelector 29 | } 30 | 31 | // KeySelector represents a description of a key in a FoundationDB database. A 32 | // KeySelector may be resolved to a specific key with the GetKey method, or used 33 | // as the endpoints of a SelectorRange to be used with a GetRange function. 34 | // 35 | // The most common key selectors are constructed with the functions documented 36 | // below. For details of how KeySelectors are specified and resolved, see 37 | // https://apple.github.io/foundationdb/developer-guide.html#key-selectors. 38 | type KeySelector struct { 39 | Key KeyConvertible 40 | OrEqual bool 41 | Offset int 42 | } 43 | 44 | func (ks KeySelector) FDBKeySelector() KeySelector { 45 | return ks 46 | } 47 | 48 | // LastLessThan returns the KeySelector specifying the lexigraphically greatest 49 | // key present in the database which is lexigraphically strictly less than the 50 | // given key. 51 | func LastLessThan(key KeyConvertible) KeySelector { 52 | return KeySelector{key, false, 0} 53 | } 54 | 55 | // LastLessOrEqual returns the KeySelector specifying the lexigraphically 56 | // greatest key present in the database which is lexigraphically less than or 57 | // equal to the given key. 58 | func LastLessOrEqual(key KeyConvertible) KeySelector { 59 | return KeySelector{key, true, 0} 60 | } 61 | 62 | // FirstGreaterThan returns the KeySelector specifying the lexigraphically least 63 | // key present in the database which is lexigraphically strictly greater than 64 | // the given key. 65 | func FirstGreaterThan(key KeyConvertible) KeySelector { 66 | return KeySelector{key, true, 1} 67 | } 68 | 69 | // FirstGreaterOrEqual returns the KeySelector specifying the lexigraphically 70 | // least key present in the database which is lexigraphically greater than or 71 | // equal to the given key. 72 | func FirstGreaterOrEqual(key KeyConvertible) KeySelector { 73 | return KeySelector{key, false, 1} 74 | } 75 | -------------------------------------------------------------------------------- /vendor/github.com/apple/foundationdb/bindings/go/src/fdb/range.go: -------------------------------------------------------------------------------- 1 | /* 2 | * range.go 3 | * 4 | * This source file is part of the FoundationDB open source project 5 | * 6 | * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | // FoundationDB Go API 22 | 23 | package fdb 24 | 25 | // #define FDB_API_VERSION 620 26 | // #include 27 | import "C" 28 | 29 | import ( 30 | "fmt" 31 | ) 32 | 33 | // KeyValue represents a single key-value pair in the database. 34 | type KeyValue struct { 35 | Key Key 36 | Value []byte 37 | } 38 | 39 | // RangeOptions specify how a database range read operation is carried 40 | // out. RangeOptions objects are passed to GetRange methods of Database, 41 | // Transaction and Snapshot. 42 | // 43 | // The zero value of RangeOptions represents the default range read 44 | // configuration (no limit, lexicographic order, to be used as an iterator). 45 | type RangeOptions struct { 46 | // Limit restricts the number of key-value pairs returned as part of a range 47 | // read. A value of 0 indicates no limit. 48 | Limit int 49 | 50 | // Mode sets the streaming mode of the range read, allowing the database to 51 | // balance latency and bandwidth for this read. 52 | Mode StreamingMode 53 | 54 | // Reverse indicates that the read should be performed in lexicographic 55 | // (false) or reverse lexicographic (true) order. When Reverse is true and 56 | // Limit is non-zero, the last Limit key-value pairs in the range are 57 | // returned. 58 | Reverse bool 59 | } 60 | 61 | // A Range describes all keys between a begin (inclusive) and end (exclusive) 62 | // key selector. 63 | type Range interface { 64 | // FDBRangeKeySelectors returns a pair of key selectors that describe the 65 | // beginning and end of a range. 66 | FDBRangeKeySelectors() (begin, end Selectable) 67 | } 68 | 69 | // An ExactRange describes all keys between a begin (inclusive) and end 70 | // (exclusive) key. If you need to specify an ExactRange and you have only a 71 | // Range, you must resolve the selectors returned by 72 | // (Range).FDBRangeKeySelectors to keys using the (Transaction).GetKey method. 73 | // 74 | // Any object that implements ExactRange also implements Range, and may be used 75 | // accordingly. 76 | type ExactRange interface { 77 | // FDBRangeKeys returns a pair of keys that describe the beginning and end 78 | // of a range. 79 | FDBRangeKeys() (begin, end KeyConvertible) 80 | 81 | // An object that implements ExactRange must also implement Range 82 | // (logically, by returning FirstGreaterOrEqual of the keys returned by 83 | // FDBRangeKeys). 84 | Range 85 | } 86 | 87 | // KeyRange is an ExactRange constructed from a pair of KeyConvertibles. Note 88 | // that the default zero-value of KeyRange specifies an empty range before all 89 | // keys in the database. 90 | type KeyRange struct { 91 | // The (inclusive) beginning of the range 92 | Begin KeyConvertible 93 | 94 | // The (exclusive) end of the range 95 | End KeyConvertible 96 | } 97 | 98 | // FDBRangeKeys allows KeyRange to satisfy the ExactRange interface. 99 | func (kr KeyRange) FDBRangeKeys() (KeyConvertible, KeyConvertible) { 100 | return kr.Begin, kr.End 101 | } 102 | 103 | // FDBRangeKeySelectors allows KeyRange to satisfy the Range interface. 104 | func (kr KeyRange) FDBRangeKeySelectors() (Selectable, Selectable) { 105 | return FirstGreaterOrEqual(kr.Begin), FirstGreaterOrEqual(kr.End) 106 | } 107 | 108 | // SelectorRange is a Range constructed directly from a pair of Selectable 109 | // objects. Note that the default zero-value of SelectorRange specifies an empty 110 | // range before all keys in the database. 111 | type SelectorRange struct { 112 | Begin, End Selectable 113 | } 114 | 115 | // FDBRangeKeySelectors allows SelectorRange to satisfy the Range interface. 116 | func (sr SelectorRange) FDBRangeKeySelectors() (Selectable, Selectable) { 117 | return sr.Begin, sr.End 118 | } 119 | 120 | // RangeResult is a handle to the asynchronous result of a range 121 | // read. RangeResult is safe for concurrent use by multiple goroutines. 122 | // 123 | // A RangeResult should not be returned from a transactional function passed to 124 | // the Transact method of a Transactor. 125 | type RangeResult struct { 126 | t *transaction 127 | sr SelectorRange 128 | options RangeOptions 129 | snapshot bool 130 | f *futureKeyValueArray 131 | } 132 | 133 | // GetSliceWithError returns a slice of KeyValue objects satisfying the range 134 | // specified in the read that returned this RangeResult, or an error if any of 135 | // the asynchronous operations associated with this result did not successfully 136 | // complete. The current goroutine will be blocked until all reads have 137 | // completed. 138 | func (rr RangeResult) GetSliceWithError() ([]KeyValue, error) { 139 | var ret []KeyValue 140 | 141 | ri := rr.Iterator() 142 | 143 | if rr.options.Limit != 0 { 144 | ri.options.Mode = StreamingModeExact 145 | } else { 146 | ri.options.Mode = StreamingModeWantAll 147 | } 148 | 149 | for ri.Advance() { 150 | if ri.err != nil { 151 | return nil, ri.err 152 | } 153 | ret = append(ret, ri.kvs...) 154 | ri.index = len(ri.kvs) 155 | ri.fetchNextBatch() 156 | } 157 | 158 | return ret, nil 159 | } 160 | 161 | // GetSliceOrPanic returns a slice of KeyValue objects satisfying the range 162 | // specified in the read that returned this RangeResult, or panics if any of the 163 | // asynchronous operations associated with this result did not successfully 164 | // complete. The current goroutine will be blocked until all reads have 165 | // completed. 166 | func (rr RangeResult) GetSliceOrPanic() []KeyValue { 167 | kvs, e := rr.GetSliceWithError() 168 | if e != nil { 169 | panic(e) 170 | } 171 | return kvs 172 | } 173 | 174 | // Iterator returns a RangeIterator over the key-value pairs satisfying the 175 | // range specified in the read that returned this RangeResult. 176 | func (rr RangeResult) Iterator() *RangeIterator { 177 | return &RangeIterator{ 178 | t: rr.t, 179 | f: rr.f, 180 | sr: rr.sr, 181 | options: rr.options, 182 | iteration: 1, 183 | snapshot: rr.snapshot, 184 | } 185 | } 186 | 187 | // RangeIterator returns the key-value pairs in the database (as KeyValue 188 | // objects) satisfying the range specified in a range read. RangeIterator is 189 | // constructed with the (RangeResult).Iterator method. 190 | // 191 | // You must call Advance and get a true result prior to calling Get or MustGet. 192 | // 193 | // RangeIterator should not be copied or used concurrently from multiple 194 | // goroutines, but multiple RangeIterators may be constructed from a single 195 | // RangeResult and used concurrently. RangeIterator should not be returned from 196 | // a transactional function passed to the Transact method of a Transactor. 197 | type RangeIterator struct { 198 | t *transaction 199 | f *futureKeyValueArray 200 | sr SelectorRange 201 | options RangeOptions 202 | iteration int 203 | done bool 204 | more bool 205 | kvs []KeyValue 206 | index int 207 | err error 208 | snapshot bool 209 | } 210 | 211 | // Advance attempts to advance the iterator to the next key-value pair. Advance 212 | // returns true if there are more key-value pairs satisfying the range, or false 213 | // if the range has been exhausted. You must call this before every call to Get 214 | // or MustGet. 215 | func (ri *RangeIterator) Advance() bool { 216 | if ri.done { 217 | return false 218 | } 219 | 220 | if ri.f == nil { 221 | return true 222 | } 223 | 224 | ri.kvs, ri.more, ri.err = ri.f.Get() 225 | ri.index = 0 226 | ri.f = nil 227 | 228 | if ri.err != nil || len(ri.kvs) > 0 { 229 | return true 230 | } 231 | 232 | return false 233 | } 234 | 235 | func (ri *RangeIterator) fetchNextBatch() { 236 | if !ri.more || ri.index == ri.options.Limit { 237 | ri.done = true 238 | return 239 | } 240 | 241 | if ri.options.Limit > 0 { 242 | // Not worried about this being zero, checked equality above 243 | ri.options.Limit -= ri.index 244 | } 245 | 246 | if ri.options.Reverse { 247 | ri.sr.End = FirstGreaterOrEqual(ri.kvs[ri.index-1].Key) 248 | } else { 249 | ri.sr.Begin = FirstGreaterThan(ri.kvs[ri.index-1].Key) 250 | } 251 | 252 | ri.iteration++ 253 | 254 | f := ri.t.doGetRange(ri.sr, ri.options, ri.snapshot, ri.iteration) 255 | ri.f = &f 256 | } 257 | 258 | // Get returns the next KeyValue in a range read, or an error if one of the 259 | // asynchronous operations associated with this range did not successfully 260 | // complete. The Advance method of this RangeIterator must have returned true 261 | // prior to calling Get. 262 | func (ri *RangeIterator) Get() (kv KeyValue, e error) { 263 | if ri.err != nil { 264 | e = ri.err 265 | return 266 | } 267 | 268 | kv = ri.kvs[ri.index] 269 | 270 | ri.index++ 271 | 272 | if ri.index == len(ri.kvs) { 273 | ri.fetchNextBatch() 274 | } 275 | 276 | return 277 | } 278 | 279 | // MustGet returns the next KeyValue in a range read, or panics if one of the 280 | // asynchronous operations associated with this range did not successfully 281 | // complete. The Advance method of this RangeIterator must have returned true 282 | // prior to calling MustGet. 283 | func (ri *RangeIterator) MustGet() KeyValue { 284 | kv, e := ri.Get() 285 | if e != nil { 286 | panic(e) 287 | } 288 | return kv 289 | } 290 | 291 | // Strinc returns the first key that would sort outside the range prefixed by 292 | // prefix, or an error if prefix is empty or contains only 0xFF bytes. 293 | func Strinc(prefix []byte) ([]byte, error) { 294 | for i := len(prefix) - 1; i >= 0; i-- { 295 | if prefix[i] != 0xFF { 296 | ret := make([]byte, i+1) 297 | copy(ret, prefix[:i+1]) 298 | ret[i]++ 299 | return ret, nil 300 | } 301 | } 302 | 303 | return nil, fmt.Errorf("Key must contain at least one byte not equal to 0xFF") 304 | } 305 | 306 | // PrefixRange returns the KeyRange describing the range of keys k such that 307 | // bytes.HasPrefix(k, prefix) is true. PrefixRange returns an error if prefix is 308 | // empty or entirely 0xFF bytes. 309 | // 310 | // Do not use PrefixRange on objects that already implement the Range or 311 | // ExactRange interfaces. The prefix range of the byte representation of these 312 | // objects may not correspond to their logical range. 313 | func PrefixRange(prefix []byte) (KeyRange, error) { 314 | begin := make([]byte, len(prefix)) 315 | copy(begin, prefix) 316 | end, e := Strinc(begin) 317 | if e != nil { 318 | return KeyRange{}, e 319 | } 320 | return KeyRange{Key(begin), Key(end)}, nil 321 | } 322 | -------------------------------------------------------------------------------- /vendor/github.com/apple/foundationdb/bindings/go/src/fdb/snapshot.go: -------------------------------------------------------------------------------- 1 | /* 2 | * snapshot.go 3 | * 4 | * This source file is part of the FoundationDB open source project 5 | * 6 | * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | // FoundationDB Go API 22 | 23 | package fdb 24 | 25 | // Snapshot is a handle to a FoundationDB transaction snapshot, suitable for 26 | // performing snapshot reads. Snapshot reads offer a more relaxed isolation 27 | // level than FoundationDB's default serializable isolation, reducing 28 | // transaction conflicts but making it harder to reason about concurrency. 29 | // 30 | // For more information on snapshot reads, see 31 | // https://apple.github.io/foundationdb/developer-guide.html#snapshot-reads. 32 | type Snapshot struct { 33 | *transaction 34 | } 35 | 36 | // ReadTransact executes the caller-provided function, passing it the Snapshot 37 | // receiver object (as a ReadTransaction). 38 | // 39 | // A panic of type Error during execution of the function will be recovered and 40 | // returned to the caller as an error, but ReadTransact will not retry the 41 | // function. 42 | // 43 | // By satisfying the ReadTransactor interface, Snapshot may be passed to a 44 | // read-only transactional function from another (possibly read-only) 45 | // transactional function, allowing composition. 46 | // 47 | // See the ReadTransactor interface for an example of using ReadTransact with 48 | // Transaction, Snapshot and Database objects. 49 | func (s Snapshot) ReadTransact(f func(ReadTransaction) (interface{}, error)) (r interface{}, e error) { 50 | defer panicToError(&e) 51 | 52 | r, e = f(s) 53 | return 54 | } 55 | 56 | // Snapshot returns the receiver and allows Snapshot to satisfy the 57 | // ReadTransaction interface. 58 | func (s Snapshot) Snapshot() Snapshot { 59 | return s 60 | } 61 | 62 | // Get is equivalent to (Transaction).Get, performed as a snapshot read. 63 | func (s Snapshot) Get(key KeyConvertible) FutureByteSlice { 64 | return s.get(key.FDBKey(), 1) 65 | } 66 | 67 | // GetKey is equivalent to (Transaction).GetKey, performed as a snapshot read. 68 | func (s Snapshot) GetKey(sel Selectable) FutureKey { 69 | return s.getKey(sel.FDBKeySelector(), 1) 70 | } 71 | 72 | // GetRange is equivalent to (Transaction).GetRange, performed as a snapshot 73 | // read. 74 | func (s Snapshot) GetRange(r Range, options RangeOptions) RangeResult { 75 | return s.getRange(r, options, true) 76 | } 77 | 78 | // GetReadVersion is equivalent to (Transaction).GetReadVersion, performed as 79 | // a snapshot read. 80 | func (s Snapshot) GetReadVersion() FutureInt64 { 81 | return s.getReadVersion() 82 | } 83 | 84 | // GetDatabase returns a handle to the database with which this snapshot is 85 | // interacting. 86 | func (s Snapshot) GetDatabase() Database { 87 | return s.transaction.db 88 | } 89 | -------------------------------------------------------------------------------- /vendor/github.com/apple/foundationdb/bindings/go/src/fdb/subspace/subspace.go: -------------------------------------------------------------------------------- 1 | /* 2 | * subspace.go 3 | * 4 | * This source file is part of the FoundationDB open source project 5 | * 6 | * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | // FoundationDB Go Subspace Layer 22 | 23 | // Package subspace provides a convenient way to use FoundationDB tuples to 24 | // define namespaces for different categories of data. The namespace is 25 | // specified by a prefix tuple which is prepended to all tuples packed by the 26 | // subspace. When unpacking a key with the subspace, the prefix tuple will be 27 | // removed from the result. 28 | // 29 | // As a best practice, API clients should use at least one subspace for 30 | // application data. For general guidance on subspace usage, see the Subspaces 31 | // section of the Developer Guide 32 | // (https://apple.github.io/foundationdb/developer-guide.html#subspaces). 33 | package subspace 34 | 35 | import ( 36 | "bytes" 37 | "errors" 38 | "github.com/apple/foundationdb/bindings/go/src/fdb" 39 | "github.com/apple/foundationdb/bindings/go/src/fdb/tuple" 40 | ) 41 | 42 | // Subspace represents a well-defined region of keyspace in a FoundationDB 43 | // database. 44 | type Subspace interface { 45 | // Sub returns a new Subspace whose prefix extends this Subspace with the 46 | // encoding of the provided element(s). If any of the elements are not a 47 | // valid tuple.TupleElement, Sub will panic. 48 | Sub(el ...tuple.TupleElement) Subspace 49 | 50 | // Bytes returns the literal bytes of the prefix of this Subspace. 51 | Bytes() []byte 52 | 53 | // Pack returns the key encoding the specified Tuple with the prefix of this 54 | // Subspace prepended. 55 | Pack(t tuple.Tuple) fdb.Key 56 | 57 | // Unpack returns the Tuple encoded by the given key with the prefix of this 58 | // Subspace removed. Unpack will return an error if the key is not in this 59 | // Subspace or does not encode a well-formed Tuple. 60 | Unpack(k fdb.KeyConvertible) (tuple.Tuple, error) 61 | 62 | // Contains returns true if the provided key starts with the prefix of this 63 | // Subspace, indicating that the Subspace logically contains the key. 64 | Contains(k fdb.KeyConvertible) bool 65 | 66 | // All Subspaces implement fdb.KeyConvertible and may be used as 67 | // FoundationDB keys (corresponding to the prefix of this Subspace). 68 | fdb.KeyConvertible 69 | 70 | // All Subspaces implement fdb.ExactRange and fdb.Range, and describe all 71 | // keys logically in this Subspace. 72 | fdb.ExactRange 73 | } 74 | 75 | type subspace struct { 76 | b []byte 77 | } 78 | 79 | // AllKeys returns the Subspace corresponding to all keys in a FoundationDB 80 | // database. 81 | func AllKeys() Subspace { 82 | return subspace{} 83 | } 84 | 85 | // Sub returns a new Subspace whose prefix is the encoding of the provided 86 | // element(s). If any of the elements are not a valid tuple.TupleElement, a 87 | // runtime panic will occur. 88 | func Sub(el ...tuple.TupleElement) Subspace { 89 | return subspace{tuple.Tuple(el).Pack()} 90 | } 91 | 92 | // FromBytes returns a new Subspace from the provided bytes. 93 | func FromBytes(b []byte) Subspace { 94 | s := make([]byte, len(b)) 95 | copy(s, b) 96 | return subspace{s} 97 | } 98 | 99 | func (s subspace) Sub(el ...tuple.TupleElement) Subspace { 100 | return subspace{concat(s.Bytes(), tuple.Tuple(el).Pack()...)} 101 | } 102 | 103 | func (s subspace) Bytes() []byte { 104 | return s.b 105 | } 106 | 107 | func (s subspace) Pack(t tuple.Tuple) fdb.Key { 108 | return fdb.Key(concat(s.b, t.Pack()...)) 109 | } 110 | 111 | func (s subspace) Unpack(k fdb.KeyConvertible) (tuple.Tuple, error) { 112 | key := k.FDBKey() 113 | if !bytes.HasPrefix(key, s.b) { 114 | return nil, errors.New("key is not in subspace") 115 | } 116 | return tuple.Unpack(key[len(s.b):]) 117 | } 118 | 119 | func (s subspace) Contains(k fdb.KeyConvertible) bool { 120 | return bytes.HasPrefix(k.FDBKey(), s.b) 121 | } 122 | 123 | func (s subspace) FDBKey() fdb.Key { 124 | return fdb.Key(s.b) 125 | } 126 | 127 | func (s subspace) FDBRangeKeys() (fdb.KeyConvertible, fdb.KeyConvertible) { 128 | return fdb.Key(concat(s.b, 0x00)), fdb.Key(concat(s.b, 0xFF)) 129 | } 130 | 131 | func (s subspace) FDBRangeKeySelectors() (fdb.Selectable, fdb.Selectable) { 132 | begin, end := s.FDBRangeKeys() 133 | return fdb.FirstGreaterOrEqual(begin), fdb.FirstGreaterOrEqual(end) 134 | } 135 | 136 | func concat(a []byte, b ...byte) []byte { 137 | r := make([]byte, len(a)+len(b)) 138 | copy(r, a) 139 | copy(r[len(a):], b) 140 | return r 141 | } 142 | -------------------------------------------------------------------------------- /vendor/github.com/apple/foundationdb/fdbclient/rapidxml/license.txt: -------------------------------------------------------------------------------- 1 | Use of this software is granted under one of the following two licenses, 2 | to be chosen freely by the user. 3 | 4 | 1. Boost Software License - Version 1.0 - August 17th, 2003 5 | =============================================================================== 6 | 7 | Copyright (c) 2006, 2007 Marcin Kalicinski 8 | 9 | Permission is hereby granted, free of charge, to any person or organization 10 | obtaining a copy of the software and accompanying documentation covered by 11 | this license (the "Software") to use, reproduce, display, distribute, 12 | execute, and transmit the Software, and to prepare derivative works of the 13 | Software, and to permit third-parties to whom the Software is furnished to 14 | do so, all subject to the following: 15 | 16 | The copyright notices in the Software and this entire statement, including 17 | the above license grant, this restriction and the following disclaimer, 18 | must be included in all copies of the Software, in whole or in part, and 19 | all derivative works of the Software, unless such copies or derivative 20 | works are solely in the form of machine-executable object code generated by 21 | a source language processor. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 26 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 27 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 28 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 29 | DEALINGS IN THE SOFTWARE. 30 | 31 | 2. The MIT License 32 | =============================================================================== 33 | 34 | Copyright (c) 2006, 2007 Marcin Kalicinski 35 | 36 | Permission is hereby granted, free of charge, to any person obtaining a copy 37 | of this software and associated documentation files (the "Software"), to deal 38 | in the Software without restriction, including without limitation the rights 39 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 40 | of the Software, and to permit persons to whom the Software is furnished to do so, 41 | subject to the following conditions: 42 | 43 | The above copyright notice and this permission notice shall be included in all 44 | copies or substantial portions of the Software. 45 | 46 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 47 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 48 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 49 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 50 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 51 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 52 | IN THE SOFTWARE. 53 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: go 3 | go: 4 | - 1.3.x 5 | - 1.5.x 6 | - 1.6.x 7 | - 1.7.x 8 | - 1.8.x 9 | - 1.9.x 10 | - master 11 | matrix: 12 | allow_failures: 13 | - go: master 14 | fast_finish: true 15 | install: 16 | - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). 17 | script: 18 | - go get -t -v ./... 19 | - diff -u <(echo -n) <(gofmt -d -s .) 20 | - go tool vet . 21 | - go test -v -race ./... 22 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2005-2008 Dustin Sallings 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | 21 | 22 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/README.markdown: -------------------------------------------------------------------------------- 1 | # Humane Units [![Build Status](https://travis-ci.org/dustin/go-humanize.svg?branch=master)](https://travis-ci.org/dustin/go-humanize) [![GoDoc](https://godoc.org/github.com/dustin/go-humanize?status.svg)](https://godoc.org/github.com/dustin/go-humanize) 2 | 3 | Just a few functions for helping humanize times and sizes. 4 | 5 | `go get` it as `github.com/dustin/go-humanize`, import it as 6 | `"github.com/dustin/go-humanize"`, use it as `humanize`. 7 | 8 | See [godoc](https://godoc.org/github.com/dustin/go-humanize) for 9 | complete documentation. 10 | 11 | ## Sizes 12 | 13 | This lets you take numbers like `82854982` and convert them to useful 14 | strings like, `83 MB` or `79 MiB` (whichever you prefer). 15 | 16 | Example: 17 | 18 | ```go 19 | fmt.Printf("That file is %s.", humanize.Bytes(82854982)) // That file is 83 MB. 20 | ``` 21 | 22 | ## Times 23 | 24 | This lets you take a `time.Time` and spit it out in relative terms. 25 | For example, `12 seconds ago` or `3 days from now`. 26 | 27 | Example: 28 | 29 | ```go 30 | fmt.Printf("This was touched %s.", humanize.Time(someTimeInstance)) // This was touched 7 hours ago. 31 | ``` 32 | 33 | Thanks to Kyle Lemons for the time implementation from an IRC 34 | conversation one day. It's pretty neat. 35 | 36 | ## Ordinals 37 | 38 | From a [mailing list discussion][odisc] where a user wanted to be able 39 | to label ordinals. 40 | 41 | 0 -> 0th 42 | 1 -> 1st 43 | 2 -> 2nd 44 | 3 -> 3rd 45 | 4 -> 4th 46 | [...] 47 | 48 | Example: 49 | 50 | ```go 51 | fmt.Printf("You're my %s best friend.", humanize.Ordinal(193)) // You are my 193rd best friend. 52 | ``` 53 | 54 | ## Commas 55 | 56 | Want to shove commas into numbers? Be my guest. 57 | 58 | 0 -> 0 59 | 100 -> 100 60 | 1000 -> 1,000 61 | 1000000000 -> 1,000,000,000 62 | -100000 -> -100,000 63 | 64 | Example: 65 | 66 | ```go 67 | fmt.Printf("You owe $%s.\n", humanize.Comma(6582491)) // You owe $6,582,491. 68 | ``` 69 | 70 | ## Ftoa 71 | 72 | Nicer float64 formatter that removes trailing zeros. 73 | 74 | ```go 75 | fmt.Printf("%f", 2.24) // 2.240000 76 | fmt.Printf("%s", humanize.Ftoa(2.24)) // 2.24 77 | fmt.Printf("%f", 2.0) // 2.000000 78 | fmt.Printf("%s", humanize.Ftoa(2.0)) // 2 79 | ``` 80 | 81 | ## SI notation 82 | 83 | Format numbers with [SI notation][sinotation]. 84 | 85 | Example: 86 | 87 | ```go 88 | humanize.SI(0.00000000223, "M") // 2.23 nM 89 | ``` 90 | 91 | ## English-specific functions 92 | 93 | The following functions are in the `humanize/english` subpackage. 94 | 95 | ### Plurals 96 | 97 | Simple English pluralization 98 | 99 | ```go 100 | english.PluralWord(1, "object", "") // object 101 | english.PluralWord(42, "object", "") // objects 102 | english.PluralWord(2, "bus", "") // buses 103 | english.PluralWord(99, "locus", "loci") // loci 104 | 105 | english.Plural(1, "object", "") // 1 object 106 | english.Plural(42, "object", "") // 42 objects 107 | english.Plural(2, "bus", "") // 2 buses 108 | english.Plural(99, "locus", "loci") // 99 loci 109 | ``` 110 | 111 | ### Word series 112 | 113 | Format comma-separated words lists with conjuctions: 114 | 115 | ```go 116 | english.WordSeries([]string{"foo"}, "and") // foo 117 | english.WordSeries([]string{"foo", "bar"}, "and") // foo and bar 118 | english.WordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar and baz 119 | 120 | english.OxfordWordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar, and baz 121 | ``` 122 | 123 | [odisc]: https://groups.google.com/d/topic/golang-nuts/l8NhI74jl-4/discussion 124 | [sinotation]: http://en.wikipedia.org/wiki/Metric_prefix 125 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/big.go: -------------------------------------------------------------------------------- 1 | package humanize 2 | 3 | import ( 4 | "math/big" 5 | ) 6 | 7 | // order of magnitude (to a max order) 8 | func oomm(n, b *big.Int, maxmag int) (float64, int) { 9 | mag := 0 10 | m := &big.Int{} 11 | for n.Cmp(b) >= 0 { 12 | n.DivMod(n, b, m) 13 | mag++ 14 | if mag == maxmag && maxmag >= 0 { 15 | break 16 | } 17 | } 18 | return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag 19 | } 20 | 21 | // total order of magnitude 22 | // (same as above, but with no upper limit) 23 | func oom(n, b *big.Int) (float64, int) { 24 | mag := 0 25 | m := &big.Int{} 26 | for n.Cmp(b) >= 0 { 27 | n.DivMod(n, b, m) 28 | mag++ 29 | } 30 | return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag 31 | } 32 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/bigbytes.go: -------------------------------------------------------------------------------- 1 | package humanize 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "strings" 7 | "unicode" 8 | ) 9 | 10 | var ( 11 | bigIECExp = big.NewInt(1024) 12 | 13 | // BigByte is one byte in bit.Ints 14 | BigByte = big.NewInt(1) 15 | // BigKiByte is 1,024 bytes in bit.Ints 16 | BigKiByte = (&big.Int{}).Mul(BigByte, bigIECExp) 17 | // BigMiByte is 1,024 k bytes in bit.Ints 18 | BigMiByte = (&big.Int{}).Mul(BigKiByte, bigIECExp) 19 | // BigGiByte is 1,024 m bytes in bit.Ints 20 | BigGiByte = (&big.Int{}).Mul(BigMiByte, bigIECExp) 21 | // BigTiByte is 1,024 g bytes in bit.Ints 22 | BigTiByte = (&big.Int{}).Mul(BigGiByte, bigIECExp) 23 | // BigPiByte is 1,024 t bytes in bit.Ints 24 | BigPiByte = (&big.Int{}).Mul(BigTiByte, bigIECExp) 25 | // BigEiByte is 1,024 p bytes in bit.Ints 26 | BigEiByte = (&big.Int{}).Mul(BigPiByte, bigIECExp) 27 | // BigZiByte is 1,024 e bytes in bit.Ints 28 | BigZiByte = (&big.Int{}).Mul(BigEiByte, bigIECExp) 29 | // BigYiByte is 1,024 z bytes in bit.Ints 30 | BigYiByte = (&big.Int{}).Mul(BigZiByte, bigIECExp) 31 | ) 32 | 33 | var ( 34 | bigSIExp = big.NewInt(1000) 35 | 36 | // BigSIByte is one SI byte in big.Ints 37 | BigSIByte = big.NewInt(1) 38 | // BigKByte is 1,000 SI bytes in big.Ints 39 | BigKByte = (&big.Int{}).Mul(BigSIByte, bigSIExp) 40 | // BigMByte is 1,000 SI k bytes in big.Ints 41 | BigMByte = (&big.Int{}).Mul(BigKByte, bigSIExp) 42 | // BigGByte is 1,000 SI m bytes in big.Ints 43 | BigGByte = (&big.Int{}).Mul(BigMByte, bigSIExp) 44 | // BigTByte is 1,000 SI g bytes in big.Ints 45 | BigTByte = (&big.Int{}).Mul(BigGByte, bigSIExp) 46 | // BigPByte is 1,000 SI t bytes in big.Ints 47 | BigPByte = (&big.Int{}).Mul(BigTByte, bigSIExp) 48 | // BigEByte is 1,000 SI p bytes in big.Ints 49 | BigEByte = (&big.Int{}).Mul(BigPByte, bigSIExp) 50 | // BigZByte is 1,000 SI e bytes in big.Ints 51 | BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp) 52 | // BigYByte is 1,000 SI z bytes in big.Ints 53 | BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp) 54 | ) 55 | 56 | var bigBytesSizeTable = map[string]*big.Int{ 57 | "b": BigByte, 58 | "kib": BigKiByte, 59 | "kb": BigKByte, 60 | "mib": BigMiByte, 61 | "mb": BigMByte, 62 | "gib": BigGiByte, 63 | "gb": BigGByte, 64 | "tib": BigTiByte, 65 | "tb": BigTByte, 66 | "pib": BigPiByte, 67 | "pb": BigPByte, 68 | "eib": BigEiByte, 69 | "eb": BigEByte, 70 | "zib": BigZiByte, 71 | "zb": BigZByte, 72 | "yib": BigYiByte, 73 | "yb": BigYByte, 74 | // Without suffix 75 | "": BigByte, 76 | "ki": BigKiByte, 77 | "k": BigKByte, 78 | "mi": BigMiByte, 79 | "m": BigMByte, 80 | "gi": BigGiByte, 81 | "g": BigGByte, 82 | "ti": BigTiByte, 83 | "t": BigTByte, 84 | "pi": BigPiByte, 85 | "p": BigPByte, 86 | "ei": BigEiByte, 87 | "e": BigEByte, 88 | "z": BigZByte, 89 | "zi": BigZiByte, 90 | "y": BigYByte, 91 | "yi": BigYiByte, 92 | } 93 | 94 | var ten = big.NewInt(10) 95 | 96 | func humanateBigBytes(s, base *big.Int, sizes []string) string { 97 | if s.Cmp(ten) < 0 { 98 | return fmt.Sprintf("%d B", s) 99 | } 100 | c := (&big.Int{}).Set(s) 101 | val, mag := oomm(c, base, len(sizes)-1) 102 | suffix := sizes[mag] 103 | f := "%.0f %s" 104 | if val < 10 { 105 | f = "%.1f %s" 106 | } 107 | 108 | return fmt.Sprintf(f, val, suffix) 109 | 110 | } 111 | 112 | // BigBytes produces a human readable representation of an SI size. 113 | // 114 | // See also: ParseBigBytes. 115 | // 116 | // BigBytes(82854982) -> 83 MB 117 | func BigBytes(s *big.Int) string { 118 | sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} 119 | return humanateBigBytes(s, bigSIExp, sizes) 120 | } 121 | 122 | // BigIBytes produces a human readable representation of an IEC size. 123 | // 124 | // See also: ParseBigBytes. 125 | // 126 | // BigIBytes(82854982) -> 79 MiB 127 | func BigIBytes(s *big.Int) string { 128 | sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} 129 | return humanateBigBytes(s, bigIECExp, sizes) 130 | } 131 | 132 | // ParseBigBytes parses a string representation of bytes into the number 133 | // of bytes it represents. 134 | // 135 | // See also: BigBytes, BigIBytes. 136 | // 137 | // ParseBigBytes("42 MB") -> 42000000, nil 138 | // ParseBigBytes("42 mib") -> 44040192, nil 139 | func ParseBigBytes(s string) (*big.Int, error) { 140 | lastDigit := 0 141 | hasComma := false 142 | for _, r := range s { 143 | if !(unicode.IsDigit(r) || r == '.' || r == ',') { 144 | break 145 | } 146 | if r == ',' { 147 | hasComma = true 148 | } 149 | lastDigit++ 150 | } 151 | 152 | num := s[:lastDigit] 153 | if hasComma { 154 | num = strings.Replace(num, ",", "", -1) 155 | } 156 | 157 | val := &big.Rat{} 158 | _, err := fmt.Sscanf(num, "%f", val) 159 | if err != nil { 160 | return nil, err 161 | } 162 | 163 | extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) 164 | if m, ok := bigBytesSizeTable[extra]; ok { 165 | mv := (&big.Rat{}).SetInt(m) 166 | val.Mul(val, mv) 167 | rv := &big.Int{} 168 | rv.Div(val.Num(), val.Denom()) 169 | return rv, nil 170 | } 171 | 172 | return nil, fmt.Errorf("unhandled size name: %v", extra) 173 | } 174 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/bytes.go: -------------------------------------------------------------------------------- 1 | package humanize 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "strconv" 7 | "strings" 8 | "unicode" 9 | ) 10 | 11 | // IEC Sizes. 12 | // kibis of bits 13 | const ( 14 | Byte = 1 << (iota * 10) 15 | KiByte 16 | MiByte 17 | GiByte 18 | TiByte 19 | PiByte 20 | EiByte 21 | ) 22 | 23 | // SI Sizes. 24 | const ( 25 | IByte = 1 26 | KByte = IByte * 1000 27 | MByte = KByte * 1000 28 | GByte = MByte * 1000 29 | TByte = GByte * 1000 30 | PByte = TByte * 1000 31 | EByte = PByte * 1000 32 | ) 33 | 34 | var bytesSizeTable = map[string]uint64{ 35 | "b": Byte, 36 | "kib": KiByte, 37 | "kb": KByte, 38 | "mib": MiByte, 39 | "mb": MByte, 40 | "gib": GiByte, 41 | "gb": GByte, 42 | "tib": TiByte, 43 | "tb": TByte, 44 | "pib": PiByte, 45 | "pb": PByte, 46 | "eib": EiByte, 47 | "eb": EByte, 48 | // Without suffix 49 | "": Byte, 50 | "ki": KiByte, 51 | "k": KByte, 52 | "mi": MiByte, 53 | "m": MByte, 54 | "gi": GiByte, 55 | "g": GByte, 56 | "ti": TiByte, 57 | "t": TByte, 58 | "pi": PiByte, 59 | "p": PByte, 60 | "ei": EiByte, 61 | "e": EByte, 62 | } 63 | 64 | func logn(n, b float64) float64 { 65 | return math.Log(n) / math.Log(b) 66 | } 67 | 68 | func humanateBytes(s uint64, base float64, sizes []string) string { 69 | if s < 10 { 70 | return fmt.Sprintf("%d B", s) 71 | } 72 | e := math.Floor(logn(float64(s), base)) 73 | suffix := sizes[int(e)] 74 | val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10 75 | f := "%.0f %s" 76 | if val < 10 { 77 | f = "%.1f %s" 78 | } 79 | 80 | return fmt.Sprintf(f, val, suffix) 81 | } 82 | 83 | // Bytes produces a human readable representation of an SI size. 84 | // 85 | // See also: ParseBytes. 86 | // 87 | // Bytes(82854982) -> 83 MB 88 | func Bytes(s uint64) string { 89 | sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"} 90 | return humanateBytes(s, 1000, sizes) 91 | } 92 | 93 | // IBytes produces a human readable representation of an IEC size. 94 | // 95 | // See also: ParseBytes. 96 | // 97 | // IBytes(82854982) -> 79 MiB 98 | func IBytes(s uint64) string { 99 | sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"} 100 | return humanateBytes(s, 1024, sizes) 101 | } 102 | 103 | // ParseBytes parses a string representation of bytes into the number 104 | // of bytes it represents. 105 | // 106 | // See Also: Bytes, IBytes. 107 | // 108 | // ParseBytes("42 MB") -> 42000000, nil 109 | // ParseBytes("42 mib") -> 44040192, nil 110 | func ParseBytes(s string) (uint64, error) { 111 | lastDigit := 0 112 | hasComma := false 113 | for _, r := range s { 114 | if !(unicode.IsDigit(r) || r == '.' || r == ',') { 115 | break 116 | } 117 | if r == ',' { 118 | hasComma = true 119 | } 120 | lastDigit++ 121 | } 122 | 123 | num := s[:lastDigit] 124 | if hasComma { 125 | num = strings.Replace(num, ",", "", -1) 126 | } 127 | 128 | f, err := strconv.ParseFloat(num, 64) 129 | if err != nil { 130 | return 0, err 131 | } 132 | 133 | extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) 134 | if m, ok := bytesSizeTable[extra]; ok { 135 | f *= float64(m) 136 | if f >= math.MaxUint64 { 137 | return 0, fmt.Errorf("too large: %v", s) 138 | } 139 | return uint64(f), nil 140 | } 141 | 142 | return 0, fmt.Errorf("unhandled size name: %v", extra) 143 | } 144 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/comma.go: -------------------------------------------------------------------------------- 1 | package humanize 2 | 3 | import ( 4 | "bytes" 5 | "math" 6 | "math/big" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | // Comma produces a string form of the given number in base 10 with 12 | // commas after every three orders of magnitude. 13 | // 14 | // e.g. Comma(834142) -> 834,142 15 | func Comma(v int64) string { 16 | sign := "" 17 | 18 | // Min int64 can't be negated to a usable value, so it has to be special cased. 19 | if v == math.MinInt64 { 20 | return "-9,223,372,036,854,775,808" 21 | } 22 | 23 | if v < 0 { 24 | sign = "-" 25 | v = 0 - v 26 | } 27 | 28 | parts := []string{"", "", "", "", "", "", ""} 29 | j := len(parts) - 1 30 | 31 | for v > 999 { 32 | parts[j] = strconv.FormatInt(v%1000, 10) 33 | switch len(parts[j]) { 34 | case 2: 35 | parts[j] = "0" + parts[j] 36 | case 1: 37 | parts[j] = "00" + parts[j] 38 | } 39 | v = v / 1000 40 | j-- 41 | } 42 | parts[j] = strconv.Itoa(int(v)) 43 | return sign + strings.Join(parts[j:], ",") 44 | } 45 | 46 | // Commaf produces a string form of the given number in base 10 with 47 | // commas after every three orders of magnitude. 48 | // 49 | // e.g. Commaf(834142.32) -> 834,142.32 50 | func Commaf(v float64) string { 51 | buf := &bytes.Buffer{} 52 | if v < 0 { 53 | buf.Write([]byte{'-'}) 54 | v = 0 - v 55 | } 56 | 57 | comma := []byte{','} 58 | 59 | parts := strings.Split(strconv.FormatFloat(v, 'f', -1, 64), ".") 60 | pos := 0 61 | if len(parts[0])%3 != 0 { 62 | pos += len(parts[0]) % 3 63 | buf.WriteString(parts[0][:pos]) 64 | buf.Write(comma) 65 | } 66 | for ; pos < len(parts[0]); pos += 3 { 67 | buf.WriteString(parts[0][pos : pos+3]) 68 | buf.Write(comma) 69 | } 70 | buf.Truncate(buf.Len() - 1) 71 | 72 | if len(parts) > 1 { 73 | buf.Write([]byte{'.'}) 74 | buf.WriteString(parts[1]) 75 | } 76 | return buf.String() 77 | } 78 | 79 | // CommafWithDigits works like the Commaf but limits the resulting 80 | // string to the given number of decimal places. 81 | // 82 | // e.g. CommafWithDigits(834142.32, 1) -> 834,142.3 83 | func CommafWithDigits(f float64, decimals int) string { 84 | return stripTrailingDigits(Commaf(f), decimals) 85 | } 86 | 87 | // BigComma produces a string form of the given big.Int in base 10 88 | // with commas after every three orders of magnitude. 89 | func BigComma(b *big.Int) string { 90 | sign := "" 91 | if b.Sign() < 0 { 92 | sign = "-" 93 | b.Abs(b) 94 | } 95 | 96 | athousand := big.NewInt(1000) 97 | c := (&big.Int{}).Set(b) 98 | _, m := oom(c, athousand) 99 | parts := make([]string, m+1) 100 | j := len(parts) - 1 101 | 102 | mod := &big.Int{} 103 | for b.Cmp(athousand) >= 0 { 104 | b.DivMod(b, athousand, mod) 105 | parts[j] = strconv.FormatInt(mod.Int64(), 10) 106 | switch len(parts[j]) { 107 | case 2: 108 | parts[j] = "0" + parts[j] 109 | case 1: 110 | parts[j] = "00" + parts[j] 111 | } 112 | j-- 113 | } 114 | parts[j] = strconv.Itoa(int(b.Int64())) 115 | return sign + strings.Join(parts[j:], ",") 116 | } 117 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/commaf.go: -------------------------------------------------------------------------------- 1 | // +build go1.6 2 | 3 | package humanize 4 | 5 | import ( 6 | "bytes" 7 | "math/big" 8 | "strings" 9 | ) 10 | 11 | // BigCommaf produces a string form of the given big.Float in base 10 12 | // with commas after every three orders of magnitude. 13 | func BigCommaf(v *big.Float) string { 14 | buf := &bytes.Buffer{} 15 | if v.Sign() < 0 { 16 | buf.Write([]byte{'-'}) 17 | v.Abs(v) 18 | } 19 | 20 | comma := []byte{','} 21 | 22 | parts := strings.Split(v.Text('f', -1), ".") 23 | pos := 0 24 | if len(parts[0])%3 != 0 { 25 | pos += len(parts[0]) % 3 26 | buf.WriteString(parts[0][:pos]) 27 | buf.Write(comma) 28 | } 29 | for ; pos < len(parts[0]); pos += 3 { 30 | buf.WriteString(parts[0][pos : pos+3]) 31 | buf.Write(comma) 32 | } 33 | buf.Truncate(buf.Len() - 1) 34 | 35 | if len(parts) > 1 { 36 | buf.Write([]byte{'.'}) 37 | buf.WriteString(parts[1]) 38 | } 39 | return buf.String() 40 | } 41 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/ftoa.go: -------------------------------------------------------------------------------- 1 | package humanize 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | ) 7 | 8 | func stripTrailingZeros(s string) string { 9 | offset := len(s) - 1 10 | for offset > 0 { 11 | if s[offset] == '.' { 12 | offset-- 13 | break 14 | } 15 | if s[offset] != '0' { 16 | break 17 | } 18 | offset-- 19 | } 20 | return s[:offset+1] 21 | } 22 | 23 | func stripTrailingDigits(s string, digits int) string { 24 | if i := strings.Index(s, "."); i >= 0 { 25 | if digits <= 0 { 26 | return s[:i] 27 | } 28 | i++ 29 | if i+digits >= len(s) { 30 | return s 31 | } 32 | return s[:i+digits] 33 | } 34 | return s 35 | } 36 | 37 | // Ftoa converts a float to a string with no trailing zeros. 38 | func Ftoa(num float64) string { 39 | return stripTrailingZeros(strconv.FormatFloat(num, 'f', 6, 64)) 40 | } 41 | 42 | // FtoaWithDigits converts a float to a string but limits the resulting string 43 | // to the given number of decimal places, and no trailing zeros. 44 | func FtoaWithDigits(num float64, digits int) string { 45 | return stripTrailingZeros(stripTrailingDigits(strconv.FormatFloat(num, 'f', 6, 64), digits)) 46 | } 47 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/humanize.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package humanize converts boring ugly numbers to human-friendly strings and back. 3 | 4 | Durations can be turned into strings such as "3 days ago", numbers 5 | representing sizes like 82854982 into useful strings like, "83 MB" or 6 | "79 MiB" (whichever you prefer). 7 | */ 8 | package humanize 9 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/number.go: -------------------------------------------------------------------------------- 1 | package humanize 2 | 3 | /* 4 | Slightly adapted from the source to fit go-humanize. 5 | 6 | Author: https://github.com/gorhill 7 | Source: https://gist.github.com/gorhill/5285193 8 | 9 | */ 10 | 11 | import ( 12 | "math" 13 | "strconv" 14 | ) 15 | 16 | var ( 17 | renderFloatPrecisionMultipliers = [...]float64{ 18 | 1, 19 | 10, 20 | 100, 21 | 1000, 22 | 10000, 23 | 100000, 24 | 1000000, 25 | 10000000, 26 | 100000000, 27 | 1000000000, 28 | } 29 | 30 | renderFloatPrecisionRounders = [...]float64{ 31 | 0.5, 32 | 0.05, 33 | 0.005, 34 | 0.0005, 35 | 0.00005, 36 | 0.000005, 37 | 0.0000005, 38 | 0.00000005, 39 | 0.000000005, 40 | 0.0000000005, 41 | } 42 | ) 43 | 44 | // FormatFloat produces a formatted number as string based on the following user-specified criteria: 45 | // * thousands separator 46 | // * decimal separator 47 | // * decimal precision 48 | // 49 | // Usage: s := RenderFloat(format, n) 50 | // The format parameter tells how to render the number n. 51 | // 52 | // See examples: http://play.golang.org/p/LXc1Ddm1lJ 53 | // 54 | // Examples of format strings, given n = 12345.6789: 55 | // "#,###.##" => "12,345.67" 56 | // "#,###." => "12,345" 57 | // "#,###" => "12345,678" 58 | // "#\u202F###,##" => "12 345,68" 59 | // "#.###,###### => 12.345,678900 60 | // "" (aka default format) => 12,345.67 61 | // 62 | // The highest precision allowed is 9 digits after the decimal symbol. 63 | // There is also a version for integer number, FormatInteger(), 64 | // which is convenient for calls within template. 65 | func FormatFloat(format string, n float64) string { 66 | // Special cases: 67 | // NaN = "NaN" 68 | // +Inf = "+Infinity" 69 | // -Inf = "-Infinity" 70 | if math.IsNaN(n) { 71 | return "NaN" 72 | } 73 | if n > math.MaxFloat64 { 74 | return "Infinity" 75 | } 76 | if n < -math.MaxFloat64 { 77 | return "-Infinity" 78 | } 79 | 80 | // default format 81 | precision := 2 82 | decimalStr := "." 83 | thousandStr := "," 84 | positiveStr := "" 85 | negativeStr := "-" 86 | 87 | if len(format) > 0 { 88 | format := []rune(format) 89 | 90 | // If there is an explicit format directive, 91 | // then default values are these: 92 | precision = 9 93 | thousandStr = "" 94 | 95 | // collect indices of meaningful formatting directives 96 | formatIndx := []int{} 97 | for i, char := range format { 98 | if char != '#' && char != '0' { 99 | formatIndx = append(formatIndx, i) 100 | } 101 | } 102 | 103 | if len(formatIndx) > 0 { 104 | // Directive at index 0: 105 | // Must be a '+' 106 | // Raise an error if not the case 107 | // index: 0123456789 108 | // +0.000,000 109 | // +000,000.0 110 | // +0000.00 111 | // +0000 112 | if formatIndx[0] == 0 { 113 | if format[formatIndx[0]] != '+' { 114 | panic("RenderFloat(): invalid positive sign directive") 115 | } 116 | positiveStr = "+" 117 | formatIndx = formatIndx[1:] 118 | } 119 | 120 | // Two directives: 121 | // First is thousands separator 122 | // Raise an error if not followed by 3-digit 123 | // 0123456789 124 | // 0.000,000 125 | // 000,000.00 126 | if len(formatIndx) == 2 { 127 | if (formatIndx[1] - formatIndx[0]) != 4 { 128 | panic("RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers") 129 | } 130 | thousandStr = string(format[formatIndx[0]]) 131 | formatIndx = formatIndx[1:] 132 | } 133 | 134 | // One directive: 135 | // Directive is decimal separator 136 | // The number of digit-specifier following the separator indicates wanted precision 137 | // 0123456789 138 | // 0.00 139 | // 000,0000 140 | if len(formatIndx) == 1 { 141 | decimalStr = string(format[formatIndx[0]]) 142 | precision = len(format) - formatIndx[0] - 1 143 | } 144 | } 145 | } 146 | 147 | // generate sign part 148 | var signStr string 149 | if n >= 0.000000001 { 150 | signStr = positiveStr 151 | } else if n <= -0.000000001 { 152 | signStr = negativeStr 153 | n = -n 154 | } else { 155 | signStr = "" 156 | n = 0.0 157 | } 158 | 159 | // split number into integer and fractional parts 160 | intf, fracf := math.Modf(n + renderFloatPrecisionRounders[precision]) 161 | 162 | // generate integer part string 163 | intStr := strconv.FormatInt(int64(intf), 10) 164 | 165 | // add thousand separator if required 166 | if len(thousandStr) > 0 { 167 | for i := len(intStr); i > 3; { 168 | i -= 3 169 | intStr = intStr[:i] + thousandStr + intStr[i:] 170 | } 171 | } 172 | 173 | // no fractional part, we can leave now 174 | if precision == 0 { 175 | return signStr + intStr 176 | } 177 | 178 | // generate fractional part 179 | fracStr := strconv.Itoa(int(fracf * renderFloatPrecisionMultipliers[precision])) 180 | // may need padding 181 | if len(fracStr) < precision { 182 | fracStr = "000000000000000"[:precision-len(fracStr)] + fracStr 183 | } 184 | 185 | return signStr + intStr + decimalStr + fracStr 186 | } 187 | 188 | // FormatInteger produces a formatted number as string. 189 | // See FormatFloat. 190 | func FormatInteger(format string, n int) string { 191 | return FormatFloat(format, float64(n)) 192 | } 193 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/ordinals.go: -------------------------------------------------------------------------------- 1 | package humanize 2 | 3 | import "strconv" 4 | 5 | // Ordinal gives you the input number in a rank/ordinal format. 6 | // 7 | // Ordinal(3) -> 3rd 8 | func Ordinal(x int) string { 9 | suffix := "th" 10 | switch x % 10 { 11 | case 1: 12 | if x%100 != 11 { 13 | suffix = "st" 14 | } 15 | case 2: 16 | if x%100 != 12 { 17 | suffix = "nd" 18 | } 19 | case 3: 20 | if x%100 != 13 { 21 | suffix = "rd" 22 | } 23 | } 24 | return strconv.Itoa(x) + suffix 25 | } 26 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/si.go: -------------------------------------------------------------------------------- 1 | package humanize 2 | 3 | import ( 4 | "errors" 5 | "math" 6 | "regexp" 7 | "strconv" 8 | ) 9 | 10 | var siPrefixTable = map[float64]string{ 11 | -24: "y", // yocto 12 | -21: "z", // zepto 13 | -18: "a", // atto 14 | -15: "f", // femto 15 | -12: "p", // pico 16 | -9: "n", // nano 17 | -6: "µ", // micro 18 | -3: "m", // milli 19 | 0: "", 20 | 3: "k", // kilo 21 | 6: "M", // mega 22 | 9: "G", // giga 23 | 12: "T", // tera 24 | 15: "P", // peta 25 | 18: "E", // exa 26 | 21: "Z", // zetta 27 | 24: "Y", // yotta 28 | } 29 | 30 | var revSIPrefixTable = revfmap(siPrefixTable) 31 | 32 | // revfmap reverses the map and precomputes the power multiplier 33 | func revfmap(in map[float64]string) map[string]float64 { 34 | rv := map[string]float64{} 35 | for k, v := range in { 36 | rv[v] = math.Pow(10, k) 37 | } 38 | return rv 39 | } 40 | 41 | var riParseRegex *regexp.Regexp 42 | 43 | func init() { 44 | ri := `^([\-0-9.]+)\s?([` 45 | for _, v := range siPrefixTable { 46 | ri += v 47 | } 48 | ri += `]?)(.*)` 49 | 50 | riParseRegex = regexp.MustCompile(ri) 51 | } 52 | 53 | // ComputeSI finds the most appropriate SI prefix for the given number 54 | // and returns the prefix along with the value adjusted to be within 55 | // that prefix. 56 | // 57 | // See also: SI, ParseSI. 58 | // 59 | // e.g. ComputeSI(2.2345e-12) -> (2.2345, "p") 60 | func ComputeSI(input float64) (float64, string) { 61 | if input == 0 { 62 | return 0, "" 63 | } 64 | mag := math.Abs(input) 65 | exponent := math.Floor(logn(mag, 10)) 66 | exponent = math.Floor(exponent/3) * 3 67 | 68 | value := mag / math.Pow(10, exponent) 69 | 70 | // Handle special case where value is exactly 1000.0 71 | // Should return 1 M instead of 1000 k 72 | if value == 1000.0 { 73 | exponent += 3 74 | value = mag / math.Pow(10, exponent) 75 | } 76 | 77 | value = math.Copysign(value, input) 78 | 79 | prefix := siPrefixTable[exponent] 80 | return value, prefix 81 | } 82 | 83 | // SI returns a string with default formatting. 84 | // 85 | // SI uses Ftoa to format float value, removing trailing zeros. 86 | // 87 | // See also: ComputeSI, ParseSI. 88 | // 89 | // e.g. SI(1000000, "B") -> 1 MB 90 | // e.g. SI(2.2345e-12, "F") -> 2.2345 pF 91 | func SI(input float64, unit string) string { 92 | value, prefix := ComputeSI(input) 93 | return Ftoa(value) + " " + prefix + unit 94 | } 95 | 96 | // SIWithDigits works like SI but limits the resulting string to the 97 | // given number of decimal places. 98 | // 99 | // e.g. SIWithDigits(1000000, 0, "B") -> 1 MB 100 | // e.g. SIWithDigits(2.2345e-12, 2, "F") -> 2.23 pF 101 | func SIWithDigits(input float64, decimals int, unit string) string { 102 | value, prefix := ComputeSI(input) 103 | return FtoaWithDigits(value, decimals) + " " + prefix + unit 104 | } 105 | 106 | var errInvalid = errors.New("invalid input") 107 | 108 | // ParseSI parses an SI string back into the number and unit. 109 | // 110 | // See also: SI, ComputeSI. 111 | // 112 | // e.g. ParseSI("2.2345 pF") -> (2.2345e-12, "F", nil) 113 | func ParseSI(input string) (float64, string, error) { 114 | found := riParseRegex.FindStringSubmatch(input) 115 | if len(found) != 4 { 116 | return 0, "", errInvalid 117 | } 118 | mag := revSIPrefixTable[found[2]] 119 | unit := found[3] 120 | 121 | base, err := strconv.ParseFloat(found[1], 64) 122 | return base * mag, unit, err 123 | } 124 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/times.go: -------------------------------------------------------------------------------- 1 | package humanize 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "sort" 7 | "time" 8 | ) 9 | 10 | // Seconds-based time units 11 | const ( 12 | Day = 24 * time.Hour 13 | Week = 7 * Day 14 | Month = 30 * Day 15 | Year = 12 * Month 16 | LongTime = 37 * Year 17 | ) 18 | 19 | // Time formats a time into a relative string. 20 | // 21 | // Time(someT) -> "3 weeks ago" 22 | func Time(then time.Time) string { 23 | return RelTime(then, time.Now(), "ago", "from now") 24 | } 25 | 26 | // A RelTimeMagnitude struct contains a relative time point at which 27 | // the relative format of time will switch to a new format string. A 28 | // slice of these in ascending order by their "D" field is passed to 29 | // CustomRelTime to format durations. 30 | // 31 | // The Format field is a string that may contain a "%s" which will be 32 | // replaced with the appropriate signed label (e.g. "ago" or "from 33 | // now") and a "%d" that will be replaced by the quantity. 34 | // 35 | // The DivBy field is the amount of time the time difference must be 36 | // divided by in order to display correctly. 37 | // 38 | // e.g. if D is 2*time.Minute and you want to display "%d minutes %s" 39 | // DivBy should be time.Minute so whatever the duration is will be 40 | // expressed in minutes. 41 | type RelTimeMagnitude struct { 42 | D time.Duration 43 | Format string 44 | DivBy time.Duration 45 | } 46 | 47 | var defaultMagnitudes = []RelTimeMagnitude{ 48 | {time.Second, "now", time.Second}, 49 | {2 * time.Second, "1 second %s", 1}, 50 | {time.Minute, "%d seconds %s", time.Second}, 51 | {2 * time.Minute, "1 minute %s", 1}, 52 | {time.Hour, "%d minutes %s", time.Minute}, 53 | {2 * time.Hour, "1 hour %s", 1}, 54 | {Day, "%d hours %s", time.Hour}, 55 | {2 * Day, "1 day %s", 1}, 56 | {Week, "%d days %s", Day}, 57 | {2 * Week, "1 week %s", 1}, 58 | {Month, "%d weeks %s", Week}, 59 | {2 * Month, "1 month %s", 1}, 60 | {Year, "%d months %s", Month}, 61 | {18 * Month, "1 year %s", 1}, 62 | {2 * Year, "2 years %s", 1}, 63 | {LongTime, "%d years %s", Year}, 64 | {math.MaxInt64, "a long while %s", 1}, 65 | } 66 | 67 | // RelTime formats a time into a relative string. 68 | // 69 | // It takes two times and two labels. In addition to the generic time 70 | // delta string (e.g. 5 minutes), the labels are used applied so that 71 | // the label corresponding to the smaller time is applied. 72 | // 73 | // RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier" 74 | func RelTime(a, b time.Time, albl, blbl string) string { 75 | return CustomRelTime(a, b, albl, blbl, defaultMagnitudes) 76 | } 77 | 78 | // CustomRelTime formats a time into a relative string. 79 | // 80 | // It takes two times two labels and a table of relative time formats. 81 | // In addition to the generic time delta string (e.g. 5 minutes), the 82 | // labels are used applied so that the label corresponding to the 83 | // smaller time is applied. 84 | func CustomRelTime(a, b time.Time, albl, blbl string, magnitudes []RelTimeMagnitude) string { 85 | lbl := albl 86 | diff := b.Sub(a) 87 | 88 | if a.After(b) { 89 | lbl = blbl 90 | diff = a.Sub(b) 91 | } 92 | 93 | n := sort.Search(len(magnitudes), func(i int) bool { 94 | return magnitudes[i].D > diff 95 | }) 96 | 97 | if n >= len(magnitudes) { 98 | n = len(magnitudes) - 1 99 | } 100 | mag := magnitudes[n] 101 | args := []interface{}{} 102 | escaped := false 103 | for _, ch := range mag.Format { 104 | if escaped { 105 | switch ch { 106 | case 's': 107 | args = append(args, lbl) 108 | case 'd': 109 | args = append(args, diff/mag.DivBy) 110 | } 111 | escaped = false 112 | } else { 113 | escaped = ch == '%' 114 | } 115 | } 116 | return fmt.Sprintf(mag.Format, args...) 117 | } 118 | -------------------------------------------------------------------------------- /vendor/github.com/meln1k/buse-go/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Sam Alba 4 | Copyright (c) 2019 Nikita Melkozerov 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /vendor/github.com/meln1k/buse-go/buse/buse.go: -------------------------------------------------------------------------------- 1 | package buse 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "io" 7 | "log" 8 | "os" 9 | "sync" 10 | "syscall" 11 | ) 12 | 13 | func ioctl(fd, op, arg uintptr) { 14 | _, _, ep := syscall.Syscall(syscall.SYS_IOCTL, fd, op, arg) 15 | if ep != 0 { 16 | log.Fatalf("ioctl(%d, %d, %d) failed: %s", fd, op, arg, syscall.Errno(ep)) 17 | } 18 | } 19 | 20 | func opDeviceRead(driver BuseInterface, fp *os.File, mutex *sync.Mutex, chunk []byte, request *nbdRequest) error { 21 | var nbdErrorReplyCode uint32 22 | if err := driver.ReadAt(chunk, request.Offset); err != nil { 23 | log.Println("buseDriver.WriteAt returned an error:", err) 24 | nbdErr, ok := err.(NbdError) 25 | if ok { 26 | nbdErrorReplyCode = nbdErr.value 27 | } else { // not an NbdError, shouldn't happen in general 28 | nbdErrorReplyCode = EPERM 29 | } 30 | } 31 | header := nbdReplyHeader(request.Handle, nbdErrorReplyCode) 32 | mutex.Lock() 33 | defer mutex.Unlock() 34 | if _, err := fp.Write(header); err != nil { 35 | log.Println("Write error, when sending reply header:", err) 36 | } 37 | if _, err := fp.Write(chunk); err != nil { 38 | log.Println("Write error, when sending data chunk:", err) 39 | } 40 | return nil 41 | } 42 | 43 | func opDeviceWrite(driver BuseInterface, fp *os.File, mutex *sync.Mutex, chunk []byte, request *nbdRequest) error { 44 | var nbdErrorReplyCode uint32 45 | if err := driver.WriteAt(chunk, request.Offset); err != nil { 46 | log.Println("buseDriver.WriteAt returned an error:", err) 47 | nbdErr, ok := err.(NbdError) 48 | if ok { 49 | nbdErrorReplyCode = nbdErr.value 50 | } else { // not an NbdError, shouldn't happen in general 51 | nbdErrorReplyCode = EPERM 52 | } 53 | } 54 | header := nbdReplyHeader(request.Handle, nbdErrorReplyCode) 55 | mutex.Lock() 56 | defer mutex.Unlock() 57 | if _, err := fp.Write(header); err != nil { 58 | log.Println("Write error, when sending reply header:", err) 59 | } 60 | return nil 61 | } 62 | 63 | func opDeviceDisconnect(driver BuseInterface, fp *os.File, mutex *sync.Mutex, chunk []byte, request *nbdRequest) error { 64 | log.Println("Calling buseDriver.Disconnect()") 65 | driver.Disconnect() 66 | return fmt.Errorf("Received a disconnect") 67 | } 68 | 69 | func opDeviceFlush(driver BuseInterface, fp *os.File, mutex *sync.Mutex, chunk []byte, request *nbdRequest) error { 70 | var nbdErrorReplyCode uint32 71 | if err := driver.Flush(); err != nil { 72 | log.Println("buseDriver.WriteAt returned an error:", err) 73 | nbdErr, ok := err.(NbdError) 74 | if ok { 75 | nbdErrorReplyCode = nbdErr.value 76 | } else { // not an NbdError, shouldn't happen in general 77 | nbdErrorReplyCode = EPERM 78 | } 79 | } 80 | header := nbdReplyHeader(request.Handle, nbdErrorReplyCode) 81 | mutex.Lock() 82 | defer mutex.Unlock() 83 | if _, err := fp.Write(header); err != nil { 84 | log.Println("Write error, when sending reply header:", err) 85 | } 86 | return nil 87 | } 88 | 89 | func opDeviceTrim(driver BuseInterface, fp *os.File, mutex *sync.Mutex, chunk []byte, request *nbdRequest) error { 90 | var nbdErrorReplyCode uint32 91 | if err := driver.Trim(request.Offset, request.Length); err != nil { 92 | log.Println("buseDriver.WriteAt returned an error:", err) 93 | nbdErr, ok := err.(NbdError) 94 | if ok { 95 | nbdErrorReplyCode = nbdErr.value 96 | } else { // not an NbdError, shouldn't happen in general 97 | nbdErrorReplyCode = EPERM 98 | } 99 | } 100 | header := nbdReplyHeader(request.Handle, nbdErrorReplyCode) 101 | mutex.Lock() 102 | defer mutex.Unlock() 103 | if _, err := fp.Write(header); err != nil { 104 | log.Println("Write error, when sending reply header:", err) 105 | } 106 | return nil 107 | } 108 | 109 | func (bd *BuseDevice) startNBDClient() { 110 | ioctl(bd.deviceFp.Fd(), NBD_SET_SOCK, uintptr(bd.socketPair[1])) 111 | // The call below may fail on some systems (if flags unset), could be ignored 112 | ioctl(bd.deviceFp.Fd(), NBD_SET_FLAGS, NBD_FLAG_SEND_TRIM) 113 | // The following call will block until the client disconnects 114 | log.Println("Starting NBD client...") 115 | go ioctl(bd.deviceFp.Fd(), NBD_DO_IT, 0) 116 | // Block on the disconnect channel 117 | <-bd.disconnect 118 | } 119 | 120 | // Disconnect disconnects the BuseDevice 121 | func (bd *BuseDevice) Disconnect() { 122 | bd.disconnect <- 1 123 | // Ok to fail, ignore errors 124 | syscall.Syscall(syscall.SYS_IOCTL, bd.deviceFp.Fd(), NBD_CLEAR_QUE, 0) 125 | syscall.Syscall(syscall.SYS_IOCTL, bd.deviceFp.Fd(), NBD_DISCONNECT, 0) 126 | syscall.Syscall(syscall.SYS_IOCTL, bd.deviceFp.Fd(), NBD_CLEAR_SOCK, 0) 127 | // Cleanup fd 128 | syscall.Close(bd.socketPair[0]) 129 | syscall.Close(bd.socketPair[1]) 130 | bd.deviceFp.Close() 131 | log.Println("NBD client disconnected") 132 | } 133 | 134 | func readNbdRequest(buf []byte, request *nbdRequest) { 135 | request.Magic = binary.BigEndian.Uint32(buf) 136 | request.Type = binary.BigEndian.Uint32(buf[4:8]) 137 | request.Handle = binary.BigEndian.Uint64(buf[8:16]) 138 | request.Offset = binary.BigEndian.Uint64(buf[16:24]) 139 | request.Length = binary.BigEndian.Uint32(buf[24:28]) 140 | } 141 | 142 | func nbdReplyHeader(handle uint64, error uint32) []byte { 143 | buf := make([]byte, 16) 144 | binary.BigEndian.PutUint32(buf[0:4], NBD_REPLY_MAGIC) 145 | binary.BigEndian.PutUint32(buf[4:8], error) 146 | binary.BigEndian.PutUint64(buf[8:16], handle) 147 | // NOTE: a struct in go has 4 extra bytes, so we skip the last 148 | return buf 149 | } 150 | 151 | // Connect connects a BuseDevice to an actual device file 152 | // and starts handling requests. It does not return until it's done serving requests. 153 | func (bd *BuseDevice) Connect() error { 154 | go bd.startNBDClient() 155 | defer bd.Disconnect() 156 | //opens the device file at least once, to make sure the partition table is updated 157 | tmp, err := os.Open(bd.device) 158 | if err != nil { 159 | return fmt.Errorf("Cannot reach the device %s: %s", bd.device, err) 160 | } 161 | tmp.Close() 162 | fp := os.NewFile(uintptr(bd.socketPair[0]), "unix") 163 | mutex := &sync.Mutex{} 164 | 165 | // Start handling requests 166 | for true { 167 | request := nbdRequest{} 168 | // NOTE: a struct in go has 4 extra bytes... 169 | buf := make([]byte, 28) 170 | if _, err := fp.Read(buf[0:28]); err != nil { 171 | return fmt.Errorf("NBD client stopped: %s", err) 172 | } 173 | 174 | readNbdRequest(buf, &request) 175 | // fmt.Printf("DEBUG %#v\n", request) 176 | if request.Magic != NBD_REQUEST_MAGIC { 177 | return fmt.Errorf("Fatal error: received packet with wrong Magic number") 178 | } 179 | reply := nbdReply{Magic: NBD_REPLY_MAGIC} 180 | reply.Handle = request.Handle 181 | chunk := make([]byte, request.Length) 182 | reply.Error = 0 183 | // Dispatches READ, WRITE, DISC, FLUSH, TRIM to the corresponding implementation 184 | if request.Type < NBD_CMD_READ || request.Type > NBD_CMD_TRIM { 185 | log.Println("Received unknown request:", request.Type) 186 | continue 187 | } 188 | if request.Type == NBD_CMD_WRITE { 189 | if _, err := io.ReadFull(fp, chunk); err != nil { 190 | return fmt.Errorf("Fatal error, cannot read request packet: %s", err) 191 | } 192 | } 193 | 194 | // asynchronously handle the rest 195 | go bd.op[request.Type](bd.driver, fp, mutex, chunk, &request) 196 | } 197 | return nil 198 | } 199 | 200 | func CreateDevice(device string, blockSize int32, size uint64, buseDriver BuseInterface) (*BuseDevice, error) { 201 | buseDevice := &BuseDevice{size: size, device: device, driver: buseDriver} 202 | sockPair, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0) 203 | if err != nil { 204 | return nil, fmt.Errorf("Call to socketpair failed: %s", err) 205 | } 206 | fp, err := os.OpenFile(device, os.O_RDWR, 0600) 207 | if err != nil { 208 | return nil, fmt.Errorf("Cannot open \"%s\". Make sure the `nbd' kernel module is loaded: %s", device, err) 209 | } 210 | buseDevice.deviceFp = fp 211 | ioctl(buseDevice.deviceFp.Fd(), NBD_SET_SIZE, uintptr(size)) 212 | ioctl(buseDevice.deviceFp.Fd(), NBD_SET_BLKSIZE, uintptr(blockSize)) 213 | ioctl(buseDevice.deviceFp.Fd(), NBD_CLEAR_QUE, 0) 214 | ioctl(buseDevice.deviceFp.Fd(), NBD_CLEAR_SOCK, 0) 215 | buseDevice.socketPair = sockPair 216 | buseDevice.op[NBD_CMD_READ] = opDeviceRead 217 | buseDevice.op[NBD_CMD_WRITE] = opDeviceWrite 218 | buseDevice.op[NBD_CMD_DISC] = opDeviceDisconnect 219 | buseDevice.op[NBD_CMD_FLUSH] = opDeviceFlush 220 | buseDevice.op[NBD_CMD_TRIM] = opDeviceTrim 221 | buseDevice.disconnect = make(chan int, 5) 222 | return buseDevice, nil 223 | } 224 | -------------------------------------------------------------------------------- /vendor/github.com/meln1k/buse-go/buse/types.go: -------------------------------------------------------------------------------- 1 | package buse 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sync" 7 | ) 8 | 9 | // Rewrote type definitions for #defines and structs to workaround cgo 10 | // as defined in 11 | 12 | const ( 13 | NBD_SET_SOCK = (0xab<<8 | 0) 14 | NBD_SET_BLKSIZE = (0xab<<8 | 1) 15 | NBD_SET_SIZE = (0xab<<8 | 2) 16 | NBD_DO_IT = (0xab<<8 | 3) 17 | NBD_CLEAR_SOCK = (0xab<<8 | 4) 18 | NBD_CLEAR_QUE = (0xab<<8 | 5) 19 | NBD_PRINT_DEBUG = (0xab<<8 | 6) 20 | NBD_SET_SIZE_BLOCKS = (0xab<<8 | 7) 21 | NBD_DISCONNECT = (0xab<<8 | 8) 22 | NBD_SET_TIMEOUT = (0xab<<8 | 9) 23 | NBD_SET_FLAGS = (0xab<<8 | 10) 24 | ) 25 | 26 | const ( 27 | NBD_CMD_READ = 0 28 | NBD_CMD_WRITE = 1 29 | NBD_CMD_DISC = 2 30 | NBD_CMD_FLUSH = 3 31 | NBD_CMD_TRIM = 4 32 | ) 33 | 34 | const ( 35 | NBD_FLAG_HAS_FLAGS = (1 << 0) 36 | NBD_FLAG_READ_ONLY = (1 << 1) 37 | NBD_FLAG_SEND_FLUSH = (1 << 2) 38 | NBD_FLAG_SEND_TRIM = (1 << 5) 39 | ) 40 | 41 | const ( 42 | NBD_REQUEST_MAGIC = 0x25609513 43 | NBD_REPLY_MAGIC = 0x67446698 44 | ) 45 | 46 | const ( 47 | EPERM = 1 // Operation not permitted. 48 | EIO = 5 // Input/output error. 49 | ENOMEM = 12 // Cannot allocate memory. 50 | EINVAL = 22 // Invalid argument. 51 | ENOSPC = 28 // No space left on device. 52 | EOVERFLOW = 75 // Value too large. 53 | ESHUTDOWN = 108 // Server is in the process of being shut down. 54 | ) 55 | 56 | type NbdError struct { 57 | value uint32 58 | } 59 | 60 | func (e NbdError) Error() string { 61 | return fmt.Sprintf("NBD server error: %d", e.value) 62 | } 63 | 64 | func NewNbdError(value uint32) NbdError { 65 | return NbdError{value} 66 | } 67 | 68 | type nbdRequest struct { 69 | Magic uint32 70 | Type uint32 71 | Handle uint64 72 | Offset uint64 73 | Length uint32 74 | } 75 | 76 | type nbdReply struct { 77 | Magic uint32 78 | Error uint32 79 | Handle uint64 80 | } 81 | 82 | type BuseInterface interface { 83 | ReadAt(p []byte, off uint64) error 84 | WriteAt(p []byte, off uint64) error 85 | Disconnect() 86 | Flush() error 87 | Trim(off uint64, length uint32) error 88 | } 89 | 90 | type BuseDevice struct { 91 | size uint64 92 | device string 93 | driver BuseInterface 94 | deviceFp *os.File 95 | socketPair [2]int 96 | op [5]func(driver BuseInterface, fp *os.File, writeMutex *sync.Mutex, chunk []byte, request *nbdRequest) error 97 | disconnect chan int 98 | } 99 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/.gitignore: -------------------------------------------------------------------------------- 1 | *.coverprofile 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | dist: trusty 4 | osx_image: xcode8.3 5 | go: 1.8.x 6 | 7 | os: 8 | - linux 9 | - osx 10 | 11 | cache: 12 | directories: 13 | - node_modules 14 | 15 | before_script: 16 | - go get github.com/urfave/gfmrun/... || true 17 | - go get golang.org/x/tools/cmd/goimports 18 | - if [ ! -f node_modules/.bin/markdown-toc ] ; then 19 | npm install markdown-toc ; 20 | fi 21 | 22 | script: 23 | - ./runtests gen 24 | - ./runtests vet 25 | - ./runtests test 26 | - ./runtests gfmrun 27 | - ./runtests toc 28 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Jeremy Saenz & Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "{build}" 2 | 3 | os: Windows Server 2016 4 | 5 | image: Visual Studio 2017 6 | 7 | clone_folder: c:\gopath\src\github.com\urfave\cli 8 | 9 | environment: 10 | GOPATH: C:\gopath 11 | GOVERSION: 1.8.x 12 | PYTHON: C:\Python36-x64 13 | PYTHON_VERSION: 3.6.x 14 | PYTHON_ARCH: 64 15 | 16 | install: 17 | - set PATH=%GOPATH%\bin;C:\go\bin;%PATH% 18 | - go version 19 | - go env 20 | - go get github.com/urfave/gfmrun/... 21 | - go get -v -t ./... 22 | 23 | build_script: 24 | - python runtests vet 25 | - python runtests test 26 | - python runtests gfmrun 27 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/category.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | // CommandCategories is a slice of *CommandCategory. 4 | type CommandCategories []*CommandCategory 5 | 6 | // CommandCategory is a category containing commands. 7 | type CommandCategory struct { 8 | Name string 9 | Commands Commands 10 | } 11 | 12 | func (c CommandCategories) Less(i, j int) bool { 13 | return c[i].Name < c[j].Name 14 | } 15 | 16 | func (c CommandCategories) Len() int { 17 | return len(c) 18 | } 19 | 20 | func (c CommandCategories) Swap(i, j int) { 21 | c[i], c[j] = c[j], c[i] 22 | } 23 | 24 | // AddCommand adds a command to a category. 25 | func (c CommandCategories) AddCommand(category string, command Command) CommandCategories { 26 | for _, commandCategory := range c { 27 | if commandCategory.Name == category { 28 | commandCategory.Commands = append(commandCategory.Commands, command) 29 | return c 30 | } 31 | } 32 | return append(c, &CommandCategory{Name: category, Commands: []Command{command}}) 33 | } 34 | 35 | // VisibleCommands returns a slice of the Commands with Hidden=false 36 | func (c *CommandCategory) VisibleCommands() []Command { 37 | ret := []Command{} 38 | for _, command := range c.Commands { 39 | if !command.Hidden { 40 | ret = append(ret, command) 41 | } 42 | } 43 | return ret 44 | } 45 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/cli.go: -------------------------------------------------------------------------------- 1 | // Package cli provides a minimal framework for creating and organizing command line 2 | // Go applications. cli is designed to be easy to understand and write, the most simple 3 | // cli application can be written as follows: 4 | // func main() { 5 | // cli.NewApp().Run(os.Args) 6 | // } 7 | // 8 | // Of course this application does not do much, so let's make this an actual application: 9 | // func main() { 10 | // app := cli.NewApp() 11 | // app.Name = "greet" 12 | // app.Usage = "say a greeting" 13 | // app.Action = func(c *cli.Context) error { 14 | // println("Greetings") 15 | // return nil 16 | // } 17 | // 18 | // app.Run(os.Args) 19 | // } 20 | package cli 21 | 22 | //go:generate python ./generate-flag-types cli -i flag-types.json -o flag_generated.go 23 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/command.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "sort" 7 | "strings" 8 | ) 9 | 10 | // Command is a subcommand for a cli.App. 11 | type Command struct { 12 | // The name of the command 13 | Name string 14 | // short name of the command. Typically one character (deprecated, use `Aliases`) 15 | ShortName string 16 | // A list of aliases for the command 17 | Aliases []string 18 | // A short description of the usage of this command 19 | Usage string 20 | // Custom text to show on USAGE section of help 21 | UsageText string 22 | // A longer explanation of how the command works 23 | Description string 24 | // A short description of the arguments of this command 25 | ArgsUsage string 26 | // The category the command is part of 27 | Category string 28 | // The function to call when checking for bash command completions 29 | BashComplete BashCompleteFunc 30 | // An action to execute before any sub-subcommands are run, but after the context is ready 31 | // If a non-nil error is returned, no sub-subcommands are run 32 | Before BeforeFunc 33 | // An action to execute after any subcommands are run, but after the subcommand has finished 34 | // It is run even if Action() panics 35 | After AfterFunc 36 | // The function to call when this command is invoked 37 | Action interface{} 38 | // TODO: replace `Action: interface{}` with `Action: ActionFunc` once some kind 39 | // of deprecation period has passed, maybe? 40 | 41 | // Execute this function if a usage error occurs. 42 | OnUsageError OnUsageErrorFunc 43 | // List of child commands 44 | Subcommands Commands 45 | // List of flags to parse 46 | Flags []Flag 47 | // Treat all flags as normal arguments if true 48 | SkipFlagParsing bool 49 | // Skip argument reordering which attempts to move flags before arguments, 50 | // but only works if all flags appear after all arguments. This behavior was 51 | // removed n version 2 since it only works under specific conditions so we 52 | // backport here by exposing it as an option for compatibility. 53 | SkipArgReorder bool 54 | // Boolean to hide built-in help command 55 | HideHelp bool 56 | // Boolean to hide this command from help or completion 57 | Hidden bool 58 | 59 | // Full name of command for help, defaults to full command name, including parent commands. 60 | HelpName string 61 | commandNamePath []string 62 | 63 | // CustomHelpTemplate the text template for the command help topic. 64 | // cli.go uses text/template to render templates. You can 65 | // render custom help text by setting this variable. 66 | CustomHelpTemplate string 67 | } 68 | 69 | type CommandsByName []Command 70 | 71 | func (c CommandsByName) Len() int { 72 | return len(c) 73 | } 74 | 75 | func (c CommandsByName) Less(i, j int) bool { 76 | return c[i].Name < c[j].Name 77 | } 78 | 79 | func (c CommandsByName) Swap(i, j int) { 80 | c[i], c[j] = c[j], c[i] 81 | } 82 | 83 | // FullName returns the full name of the command. 84 | // For subcommands this ensures that parent commands are part of the command path 85 | func (c Command) FullName() string { 86 | if c.commandNamePath == nil { 87 | return c.Name 88 | } 89 | return strings.Join(c.commandNamePath, " ") 90 | } 91 | 92 | // Commands is a slice of Command 93 | type Commands []Command 94 | 95 | // Run invokes the command given the context, parses ctx.Args() to generate command-specific flags 96 | func (c Command) Run(ctx *Context) (err error) { 97 | if len(c.Subcommands) > 0 { 98 | return c.startApp(ctx) 99 | } 100 | 101 | if !c.HideHelp && (HelpFlag != BoolFlag{}) { 102 | // append help to flags 103 | c.Flags = append( 104 | c.Flags, 105 | HelpFlag, 106 | ) 107 | } 108 | 109 | set, err := flagSet(c.Name, c.Flags) 110 | if err != nil { 111 | return err 112 | } 113 | set.SetOutput(ioutil.Discard) 114 | 115 | if c.SkipFlagParsing { 116 | err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...)) 117 | } else if !c.SkipArgReorder { 118 | firstFlagIndex := -1 119 | terminatorIndex := -1 120 | for index, arg := range ctx.Args() { 121 | if arg == "--" { 122 | terminatorIndex = index 123 | break 124 | } else if arg == "-" { 125 | // Do nothing. A dash alone is not really a flag. 126 | continue 127 | } else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 { 128 | firstFlagIndex = index 129 | } 130 | } 131 | 132 | if firstFlagIndex > -1 { 133 | args := ctx.Args() 134 | regularArgs := make([]string, len(args[1:firstFlagIndex])) 135 | copy(regularArgs, args[1:firstFlagIndex]) 136 | 137 | var flagArgs []string 138 | if terminatorIndex > -1 { 139 | flagArgs = args[firstFlagIndex:terminatorIndex] 140 | regularArgs = append(regularArgs, args[terminatorIndex:]...) 141 | } else { 142 | flagArgs = args[firstFlagIndex:] 143 | } 144 | 145 | err = set.Parse(append(flagArgs, regularArgs...)) 146 | } else { 147 | err = set.Parse(ctx.Args().Tail()) 148 | } 149 | } else { 150 | err = set.Parse(ctx.Args().Tail()) 151 | } 152 | 153 | nerr := normalizeFlags(c.Flags, set) 154 | if nerr != nil { 155 | fmt.Fprintln(ctx.App.Writer, nerr) 156 | fmt.Fprintln(ctx.App.Writer) 157 | ShowCommandHelp(ctx, c.Name) 158 | return nerr 159 | } 160 | 161 | context := NewContext(ctx.App, set, ctx) 162 | context.Command = c 163 | if checkCommandCompletions(context, c.Name) { 164 | return nil 165 | } 166 | 167 | if err != nil { 168 | if c.OnUsageError != nil { 169 | err := c.OnUsageError(context, err, false) 170 | HandleExitCoder(err) 171 | return err 172 | } 173 | fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error()) 174 | fmt.Fprintln(context.App.Writer) 175 | ShowCommandHelp(context, c.Name) 176 | return err 177 | } 178 | 179 | if checkCommandHelp(context, c.Name) { 180 | return nil 181 | } 182 | 183 | if c.After != nil { 184 | defer func() { 185 | afterErr := c.After(context) 186 | if afterErr != nil { 187 | HandleExitCoder(err) 188 | if err != nil { 189 | err = NewMultiError(err, afterErr) 190 | } else { 191 | err = afterErr 192 | } 193 | } 194 | }() 195 | } 196 | 197 | if c.Before != nil { 198 | err = c.Before(context) 199 | if err != nil { 200 | ShowCommandHelp(context, c.Name) 201 | HandleExitCoder(err) 202 | return err 203 | } 204 | } 205 | 206 | if c.Action == nil { 207 | c.Action = helpSubcommand.Action 208 | } 209 | 210 | err = HandleAction(c.Action, context) 211 | 212 | if err != nil { 213 | HandleExitCoder(err) 214 | } 215 | return err 216 | } 217 | 218 | // Names returns the names including short names and aliases. 219 | func (c Command) Names() []string { 220 | names := []string{c.Name} 221 | 222 | if c.ShortName != "" { 223 | names = append(names, c.ShortName) 224 | } 225 | 226 | return append(names, c.Aliases...) 227 | } 228 | 229 | // HasName returns true if Command.Name or Command.ShortName matches given name 230 | func (c Command) HasName(name string) bool { 231 | for _, n := range c.Names() { 232 | if n == name { 233 | return true 234 | } 235 | } 236 | return false 237 | } 238 | 239 | func (c Command) startApp(ctx *Context) error { 240 | app := NewApp() 241 | app.Metadata = ctx.App.Metadata 242 | // set the name and usage 243 | app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) 244 | if c.HelpName == "" { 245 | app.HelpName = c.HelpName 246 | } else { 247 | app.HelpName = app.Name 248 | } 249 | 250 | app.Usage = c.Usage 251 | app.Description = c.Description 252 | app.ArgsUsage = c.ArgsUsage 253 | 254 | // set CommandNotFound 255 | app.CommandNotFound = ctx.App.CommandNotFound 256 | app.CustomAppHelpTemplate = c.CustomHelpTemplate 257 | 258 | // set the flags and commands 259 | app.Commands = c.Subcommands 260 | app.Flags = c.Flags 261 | app.HideHelp = c.HideHelp 262 | 263 | app.Version = ctx.App.Version 264 | app.HideVersion = ctx.App.HideVersion 265 | app.Compiled = ctx.App.Compiled 266 | app.Author = ctx.App.Author 267 | app.Email = ctx.App.Email 268 | app.Writer = ctx.App.Writer 269 | app.ErrWriter = ctx.App.ErrWriter 270 | 271 | app.categories = CommandCategories{} 272 | for _, command := range c.Subcommands { 273 | app.categories = app.categories.AddCommand(command.Category, command) 274 | } 275 | 276 | sort.Sort(app.categories) 277 | 278 | // bash completion 279 | app.EnableBashCompletion = ctx.App.EnableBashCompletion 280 | if c.BashComplete != nil { 281 | app.BashComplete = c.BashComplete 282 | } 283 | 284 | // set the actions 285 | app.Before = c.Before 286 | app.After = c.After 287 | if c.Action != nil { 288 | app.Action = c.Action 289 | } else { 290 | app.Action = helpSubcommand.Action 291 | } 292 | app.OnUsageError = c.OnUsageError 293 | 294 | for index, cc := range app.Commands { 295 | app.Commands[index].commandNamePath = []string{c.Name, cc.Name} 296 | } 297 | 298 | return app.RunAsSubcommand(ctx) 299 | } 300 | 301 | // VisibleFlags returns a slice of the Flags with Hidden=false 302 | func (c Command) VisibleFlags() []Flag { 303 | return visibleFlags(c.Flags) 304 | } 305 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/context.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "errors" 5 | "flag" 6 | "reflect" 7 | "strings" 8 | "syscall" 9 | ) 10 | 11 | // Context is a type that is passed through to 12 | // each Handler action in a cli application. Context 13 | // can be used to retrieve context-specific Args and 14 | // parsed command-line options. 15 | type Context struct { 16 | App *App 17 | Command Command 18 | shellComplete bool 19 | flagSet *flag.FlagSet 20 | setFlags map[string]bool 21 | parentContext *Context 22 | } 23 | 24 | // NewContext creates a new context. For use in when invoking an App or Command action. 25 | func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context { 26 | c := &Context{App: app, flagSet: set, parentContext: parentCtx} 27 | 28 | if parentCtx != nil { 29 | c.shellComplete = parentCtx.shellComplete 30 | } 31 | 32 | return c 33 | } 34 | 35 | // NumFlags returns the number of flags set 36 | func (c *Context) NumFlags() int { 37 | return c.flagSet.NFlag() 38 | } 39 | 40 | // Set sets a context flag to a value. 41 | func (c *Context) Set(name, value string) error { 42 | c.setFlags = nil 43 | return c.flagSet.Set(name, value) 44 | } 45 | 46 | // GlobalSet sets a context flag to a value on the global flagset 47 | func (c *Context) GlobalSet(name, value string) error { 48 | globalContext(c).setFlags = nil 49 | return globalContext(c).flagSet.Set(name, value) 50 | } 51 | 52 | // IsSet determines if the flag was actually set 53 | func (c *Context) IsSet(name string) bool { 54 | if c.setFlags == nil { 55 | c.setFlags = make(map[string]bool) 56 | 57 | c.flagSet.Visit(func(f *flag.Flag) { 58 | c.setFlags[f.Name] = true 59 | }) 60 | 61 | c.flagSet.VisitAll(func(f *flag.Flag) { 62 | if _, ok := c.setFlags[f.Name]; ok { 63 | return 64 | } 65 | c.setFlags[f.Name] = false 66 | }) 67 | 68 | // XXX hack to support IsSet for flags with EnvVar 69 | // 70 | // There isn't an easy way to do this with the current implementation since 71 | // whether a flag was set via an environment variable is very difficult to 72 | // determine here. Instead, we intend to introduce a backwards incompatible 73 | // change in version 2 to add `IsSet` to the Flag interface to push the 74 | // responsibility closer to where the information required to determine 75 | // whether a flag is set by non-standard means such as environment 76 | // variables is avaliable. 77 | // 78 | // See https://github.com/urfave/cli/issues/294 for additional discussion 79 | flags := c.Command.Flags 80 | if c.Command.Name == "" { // cannot == Command{} since it contains slice types 81 | if c.App != nil { 82 | flags = c.App.Flags 83 | } 84 | } 85 | for _, f := range flags { 86 | eachName(f.GetName(), func(name string) { 87 | if isSet, ok := c.setFlags[name]; isSet || !ok { 88 | return 89 | } 90 | 91 | val := reflect.ValueOf(f) 92 | if val.Kind() == reflect.Ptr { 93 | val = val.Elem() 94 | } 95 | 96 | envVarValue := val.FieldByName("EnvVar") 97 | if !envVarValue.IsValid() { 98 | return 99 | } 100 | 101 | eachName(envVarValue.String(), func(envVar string) { 102 | envVar = strings.TrimSpace(envVar) 103 | if _, ok := syscall.Getenv(envVar); ok { 104 | c.setFlags[name] = true 105 | return 106 | } 107 | }) 108 | }) 109 | } 110 | } 111 | 112 | return c.setFlags[name] 113 | } 114 | 115 | // GlobalIsSet determines if the global flag was actually set 116 | func (c *Context) GlobalIsSet(name string) bool { 117 | ctx := c 118 | if ctx.parentContext != nil { 119 | ctx = ctx.parentContext 120 | } 121 | 122 | for ; ctx != nil; ctx = ctx.parentContext { 123 | if ctx.IsSet(name) { 124 | return true 125 | } 126 | } 127 | return false 128 | } 129 | 130 | // FlagNames returns a slice of flag names used in this context. 131 | func (c *Context) FlagNames() (names []string) { 132 | for _, flag := range c.Command.Flags { 133 | name := strings.Split(flag.GetName(), ",")[0] 134 | if name == "help" { 135 | continue 136 | } 137 | names = append(names, name) 138 | } 139 | return 140 | } 141 | 142 | // GlobalFlagNames returns a slice of global flag names used by the app. 143 | func (c *Context) GlobalFlagNames() (names []string) { 144 | for _, flag := range c.App.Flags { 145 | name := strings.Split(flag.GetName(), ",")[0] 146 | if name == "help" || name == "version" { 147 | continue 148 | } 149 | names = append(names, name) 150 | } 151 | return 152 | } 153 | 154 | // Parent returns the parent context, if any 155 | func (c *Context) Parent() *Context { 156 | return c.parentContext 157 | } 158 | 159 | // value returns the value of the flag coressponding to `name` 160 | func (c *Context) value(name string) interface{} { 161 | return c.flagSet.Lookup(name).Value.(flag.Getter).Get() 162 | } 163 | 164 | // Args contains apps console arguments 165 | type Args []string 166 | 167 | // Args returns the command line arguments associated with the context. 168 | func (c *Context) Args() Args { 169 | args := Args(c.flagSet.Args()) 170 | return args 171 | } 172 | 173 | // NArg returns the number of the command line arguments. 174 | func (c *Context) NArg() int { 175 | return len(c.Args()) 176 | } 177 | 178 | // Get returns the nth argument, or else a blank string 179 | func (a Args) Get(n int) string { 180 | if len(a) > n { 181 | return a[n] 182 | } 183 | return "" 184 | } 185 | 186 | // First returns the first argument, or else a blank string 187 | func (a Args) First() string { 188 | return a.Get(0) 189 | } 190 | 191 | // Tail returns the rest of the arguments (not the first one) 192 | // or else an empty string slice 193 | func (a Args) Tail() []string { 194 | if len(a) >= 2 { 195 | return []string(a)[1:] 196 | } 197 | return []string{} 198 | } 199 | 200 | // Present checks if there are any arguments present 201 | func (a Args) Present() bool { 202 | return len(a) != 0 203 | } 204 | 205 | // Swap swaps arguments at the given indexes 206 | func (a Args) Swap(from, to int) error { 207 | if from >= len(a) || to >= len(a) { 208 | return errors.New("index out of range") 209 | } 210 | a[from], a[to] = a[to], a[from] 211 | return nil 212 | } 213 | 214 | func globalContext(ctx *Context) *Context { 215 | if ctx == nil { 216 | return nil 217 | } 218 | 219 | for { 220 | if ctx.parentContext == nil { 221 | return ctx 222 | } 223 | ctx = ctx.parentContext 224 | } 225 | } 226 | 227 | func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet { 228 | if ctx.parentContext != nil { 229 | ctx = ctx.parentContext 230 | } 231 | for ; ctx != nil; ctx = ctx.parentContext { 232 | if f := ctx.flagSet.Lookup(name); f != nil { 233 | return ctx.flagSet 234 | } 235 | } 236 | return nil 237 | } 238 | 239 | func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { 240 | switch ff.Value.(type) { 241 | case *StringSlice: 242 | default: 243 | set.Set(name, ff.Value.String()) 244 | } 245 | } 246 | 247 | func normalizeFlags(flags []Flag, set *flag.FlagSet) error { 248 | visited := make(map[string]bool) 249 | set.Visit(func(f *flag.Flag) { 250 | visited[f.Name] = true 251 | }) 252 | for _, f := range flags { 253 | parts := strings.Split(f.GetName(), ",") 254 | if len(parts) == 1 { 255 | continue 256 | } 257 | var ff *flag.Flag 258 | for _, name := range parts { 259 | name = strings.Trim(name, " ") 260 | if visited[name] { 261 | if ff != nil { 262 | return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name) 263 | } 264 | ff = set.Lookup(name) 265 | } 266 | } 267 | if ff == nil { 268 | continue 269 | } 270 | for _, name := range parts { 271 | name = strings.Trim(name, " ") 272 | if !visited[name] { 273 | copyFlag(name, ff, set) 274 | } 275 | } 276 | } 277 | return nil 278 | } 279 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/errors.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | // OsExiter is the function used when the app exits. If not set defaults to os.Exit. 11 | var OsExiter = os.Exit 12 | 13 | // ErrWriter is used to write errors to the user. This can be anything 14 | // implementing the io.Writer interface and defaults to os.Stderr. 15 | var ErrWriter io.Writer = os.Stderr 16 | 17 | // MultiError is an error that wraps multiple errors. 18 | type MultiError struct { 19 | Errors []error 20 | } 21 | 22 | // NewMultiError creates a new MultiError. Pass in one or more errors. 23 | func NewMultiError(err ...error) MultiError { 24 | return MultiError{Errors: err} 25 | } 26 | 27 | // Error implements the error interface. 28 | func (m MultiError) Error() string { 29 | errs := make([]string, len(m.Errors)) 30 | for i, err := range m.Errors { 31 | errs[i] = err.Error() 32 | } 33 | 34 | return strings.Join(errs, "\n") 35 | } 36 | 37 | type ErrorFormatter interface { 38 | Format(s fmt.State, verb rune) 39 | } 40 | 41 | // ExitCoder is the interface checked by `App` and `Command` for a custom exit 42 | // code 43 | type ExitCoder interface { 44 | error 45 | ExitCode() int 46 | } 47 | 48 | // ExitError fulfills both the builtin `error` interface and `ExitCoder` 49 | type ExitError struct { 50 | exitCode int 51 | message interface{} 52 | } 53 | 54 | // NewExitError makes a new *ExitError 55 | func NewExitError(message interface{}, exitCode int) *ExitError { 56 | return &ExitError{ 57 | exitCode: exitCode, 58 | message: message, 59 | } 60 | } 61 | 62 | // Error returns the string message, fulfilling the interface required by 63 | // `error` 64 | func (ee *ExitError) Error() string { 65 | return fmt.Sprintf("%v", ee.message) 66 | } 67 | 68 | // ExitCode returns the exit code, fulfilling the interface required by 69 | // `ExitCoder` 70 | func (ee *ExitError) ExitCode() int { 71 | return ee.exitCode 72 | } 73 | 74 | // HandleExitCoder checks if the error fulfills the ExitCoder interface, and if 75 | // so prints the error to stderr (if it is non-empty) and calls OsExiter with the 76 | // given exit code. If the given error is a MultiError, then this func is 77 | // called on all members of the Errors slice and calls OsExiter with the last exit code. 78 | func HandleExitCoder(err error) { 79 | if err == nil { 80 | return 81 | } 82 | 83 | if exitErr, ok := err.(ExitCoder); ok { 84 | if err.Error() != "" { 85 | if _, ok := exitErr.(ErrorFormatter); ok { 86 | fmt.Fprintf(ErrWriter, "%+v\n", err) 87 | } else { 88 | fmt.Fprintln(ErrWriter, err) 89 | } 90 | } 91 | OsExiter(exitErr.ExitCode()) 92 | return 93 | } 94 | 95 | if multiErr, ok := err.(MultiError); ok { 96 | code := handleMultiError(multiErr) 97 | OsExiter(code) 98 | return 99 | } 100 | } 101 | 102 | func handleMultiError(multiErr MultiError) int { 103 | code := 1 104 | for _, merr := range multiErr.Errors { 105 | if multiErr2, ok := merr.(MultiError); ok { 106 | code = handleMultiError(multiErr2) 107 | } else { 108 | fmt.Fprintln(ErrWriter, merr) 109 | if exitErr, ok := merr.(ExitCoder); ok { 110 | code = exitErr.ExitCode() 111 | } 112 | } 113 | } 114 | return code 115 | } 116 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/flag-types.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Bool", 4 | "type": "bool", 5 | "value": false, 6 | "context_default": "false", 7 | "parser": "strconv.ParseBool(f.Value.String())" 8 | }, 9 | { 10 | "name": "BoolT", 11 | "type": "bool", 12 | "value": false, 13 | "doctail": " that is true by default", 14 | "context_default": "false", 15 | "parser": "strconv.ParseBool(f.Value.String())" 16 | }, 17 | { 18 | "name": "Duration", 19 | "type": "time.Duration", 20 | "doctail": " (see https://golang.org/pkg/time/#ParseDuration)", 21 | "context_default": "0", 22 | "parser": "time.ParseDuration(f.Value.String())" 23 | }, 24 | { 25 | "name": "Float64", 26 | "type": "float64", 27 | "context_default": "0", 28 | "parser": "strconv.ParseFloat(f.Value.String(), 64)" 29 | }, 30 | { 31 | "name": "Generic", 32 | "type": "Generic", 33 | "dest": false, 34 | "context_default": "nil", 35 | "context_type": "interface{}" 36 | }, 37 | { 38 | "name": "Int64", 39 | "type": "int64", 40 | "context_default": "0", 41 | "parser": "strconv.ParseInt(f.Value.String(), 0, 64)" 42 | }, 43 | { 44 | "name": "Int", 45 | "type": "int", 46 | "context_default": "0", 47 | "parser": "strconv.ParseInt(f.Value.String(), 0, 64)", 48 | "parser_cast": "int(parsed)" 49 | }, 50 | { 51 | "name": "IntSlice", 52 | "type": "*IntSlice", 53 | "dest": false, 54 | "context_default": "nil", 55 | "context_type": "[]int", 56 | "parser": "(f.Value.(*IntSlice)).Value(), error(nil)" 57 | }, 58 | { 59 | "name": "Int64Slice", 60 | "type": "*Int64Slice", 61 | "dest": false, 62 | "context_default": "nil", 63 | "context_type": "[]int64", 64 | "parser": "(f.Value.(*Int64Slice)).Value(), error(nil)" 65 | }, 66 | { 67 | "name": "String", 68 | "type": "string", 69 | "context_default": "\"\"", 70 | "parser": "f.Value.String(), error(nil)" 71 | }, 72 | { 73 | "name": "StringSlice", 74 | "type": "*StringSlice", 75 | "dest": false, 76 | "context_default": "nil", 77 | "context_type": "[]string", 78 | "parser": "(f.Value.(*StringSlice)).Value(), error(nil)" 79 | }, 80 | { 81 | "name": "Uint64", 82 | "type": "uint64", 83 | "context_default": "0", 84 | "parser": "strconv.ParseUint(f.Value.String(), 0, 64)" 85 | }, 86 | { 87 | "name": "Uint", 88 | "type": "uint", 89 | "context_default": "0", 90 | "parser": "strconv.ParseUint(f.Value.String(), 0, 64)", 91 | "parser_cast": "uint(parsed)" 92 | } 93 | ] 94 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/funcs.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | // BashCompleteFunc is an action to execute when the bash-completion flag is set 4 | type BashCompleteFunc func(*Context) 5 | 6 | // BeforeFunc is an action to execute before any subcommands are run, but after 7 | // the context is ready if a non-nil error is returned, no subcommands are run 8 | type BeforeFunc func(*Context) error 9 | 10 | // AfterFunc is an action to execute after any subcommands are run, but after the 11 | // subcommand has finished it is run even if Action() panics 12 | type AfterFunc func(*Context) error 13 | 14 | // ActionFunc is the action to execute when no subcommands are specified 15 | type ActionFunc func(*Context) error 16 | 17 | // CommandNotFoundFunc is executed if the proper command cannot be found 18 | type CommandNotFoundFunc func(*Context, string) 19 | 20 | // OnUsageErrorFunc is executed if an usage error occurs. This is useful for displaying 21 | // customized usage error messages. This function is able to replace the 22 | // original error messages. If this function is not set, the "Incorrect usage" 23 | // is displayed and the execution is interrupted. 24 | type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error 25 | 26 | // FlagStringFunc is used by the help generation to display a flag, which is 27 | // expected to be a single line. 28 | type FlagStringFunc func(Flag) string 29 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/generate-flag-types: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | The flag types that ship with the cli library have many things in common, and 4 | so we can take advantage of the `go generate` command to create much of the 5 | source code from a list of definitions. These definitions attempt to cover 6 | the parts that vary between flag types, and should evolve as needed. 7 | 8 | An example of the minimum definition needed is: 9 | 10 | { 11 | "name": "SomeType", 12 | "type": "sometype", 13 | "context_default": "nil" 14 | } 15 | 16 | In this example, the code generated for the `cli` package will include a type 17 | named `SomeTypeFlag` that is expected to wrap a value of type `sometype`. 18 | Fetching values by name via `*cli.Context` will default to a value of `nil`. 19 | 20 | A more complete, albeit somewhat redundant, example showing all available 21 | definition keys is: 22 | 23 | { 24 | "name": "VeryMuchType", 25 | "type": "*VeryMuchType", 26 | "value": true, 27 | "dest": false, 28 | "doctail": " which really only wraps a []float64, oh well!", 29 | "context_type": "[]float64", 30 | "context_default": "nil", 31 | "parser": "parseVeryMuchType(f.Value.String())", 32 | "parser_cast": "[]float64(parsed)" 33 | } 34 | 35 | The meaning of each field is as follows: 36 | 37 | name (string) - The type "name", which will be suffixed with 38 | `Flag` when generating the type definition 39 | for `cli` and the wrapper type for `altsrc` 40 | type (string) - The type that the generated `Flag` type for `cli` 41 | is expected to "contain" as its `.Value` member 42 | value (bool) - Should the generated `cli` type have a `Value` 43 | member? 44 | dest (bool) - Should the generated `cli` type support a 45 | destination pointer? 46 | doctail (string) - Additional docs for the `cli` flag type comment 47 | context_type (string) - The literal type used in the `*cli.Context` 48 | reader func signature 49 | context_default (string) - The literal value used as the default by the 50 | `*cli.Context` reader funcs when no value is 51 | present 52 | parser (string) - Literal code used to parse the flag `f`, 53 | expected to have a return signature of 54 | (value, error) 55 | parser_cast (string) - Literal code used to cast the `parsed` value 56 | returned from the `parser` code 57 | """ 58 | 59 | from __future__ import print_function, unicode_literals 60 | 61 | import argparse 62 | import json 63 | import os 64 | import subprocess 65 | import sys 66 | import tempfile 67 | import textwrap 68 | 69 | 70 | class _FancyFormatter(argparse.ArgumentDefaultsHelpFormatter, 71 | argparse.RawDescriptionHelpFormatter): 72 | pass 73 | 74 | 75 | def main(sysargs=sys.argv[:]): 76 | parser = argparse.ArgumentParser( 77 | description='Generate flag type code!', 78 | formatter_class=_FancyFormatter) 79 | parser.add_argument( 80 | 'package', 81 | type=str, default='cli', choices=_WRITEFUNCS.keys(), 82 | help='Package for which flag types will be generated' 83 | ) 84 | parser.add_argument( 85 | '-i', '--in-json', 86 | type=argparse.FileType('r'), 87 | default=sys.stdin, 88 | help='Input JSON file which defines each type to be generated' 89 | ) 90 | parser.add_argument( 91 | '-o', '--out-go', 92 | type=argparse.FileType('w'), 93 | default=sys.stdout, 94 | help='Output file/stream to which generated source will be written' 95 | ) 96 | parser.epilog = __doc__ 97 | 98 | args = parser.parse_args(sysargs[1:]) 99 | _generate_flag_types(_WRITEFUNCS[args.package], args.out_go, args.in_json) 100 | return 0 101 | 102 | 103 | def _generate_flag_types(writefunc, output_go, input_json): 104 | types = json.load(input_json) 105 | 106 | tmp = tempfile.NamedTemporaryFile(suffix='.go', delete=False) 107 | writefunc(tmp, types) 108 | tmp.close() 109 | 110 | new_content = subprocess.check_output( 111 | ['goimports', tmp.name] 112 | ).decode('utf-8') 113 | 114 | print(new_content, file=output_go, end='') 115 | output_go.flush() 116 | os.remove(tmp.name) 117 | 118 | 119 | def _set_typedef_defaults(typedef): 120 | typedef.setdefault('doctail', '') 121 | typedef.setdefault('context_type', typedef['type']) 122 | typedef.setdefault('dest', True) 123 | typedef.setdefault('value', True) 124 | typedef.setdefault('parser', 'f.Value, error(nil)') 125 | typedef.setdefault('parser_cast', 'parsed') 126 | 127 | 128 | def _write_cli_flag_types(outfile, types): 129 | _fwrite(outfile, """\ 130 | package cli 131 | 132 | // WARNING: This file is generated! 133 | 134 | """) 135 | 136 | for typedef in types: 137 | _set_typedef_defaults(typedef) 138 | 139 | _fwrite(outfile, """\ 140 | // {name}Flag is a flag with type {type}{doctail} 141 | type {name}Flag struct {{ 142 | Name string 143 | Usage string 144 | EnvVar string 145 | Hidden bool 146 | """.format(**typedef)) 147 | 148 | if typedef['value']: 149 | _fwrite(outfile, """\ 150 | Value {type} 151 | """.format(**typedef)) 152 | 153 | if typedef['dest']: 154 | _fwrite(outfile, """\ 155 | Destination *{type} 156 | """.format(**typedef)) 157 | 158 | _fwrite(outfile, "\n}\n\n") 159 | 160 | _fwrite(outfile, """\ 161 | // String returns a readable representation of this value 162 | // (for usage defaults) 163 | func (f {name}Flag) String() string {{ 164 | return FlagStringer(f) 165 | }} 166 | 167 | // GetName returns the name of the flag 168 | func (f {name}Flag) GetName() string {{ 169 | return f.Name 170 | }} 171 | 172 | // {name} looks up the value of a local {name}Flag, returns 173 | // {context_default} if not found 174 | func (c *Context) {name}(name string) {context_type} {{ 175 | return lookup{name}(name, c.flagSet) 176 | }} 177 | 178 | // Global{name} looks up the value of a global {name}Flag, returns 179 | // {context_default} if not found 180 | func (c *Context) Global{name}(name string) {context_type} {{ 181 | if fs := lookupGlobalFlagSet(name, c); fs != nil {{ 182 | return lookup{name}(name, fs) 183 | }} 184 | return {context_default} 185 | }} 186 | 187 | func lookup{name}(name string, set *flag.FlagSet) {context_type} {{ 188 | f := set.Lookup(name) 189 | if f != nil {{ 190 | parsed, err := {parser} 191 | if err != nil {{ 192 | return {context_default} 193 | }} 194 | return {parser_cast} 195 | }} 196 | return {context_default} 197 | }} 198 | """.format(**typedef)) 199 | 200 | 201 | def _write_altsrc_flag_types(outfile, types): 202 | _fwrite(outfile, """\ 203 | package altsrc 204 | 205 | import ( 206 | "gopkg.in/urfave/cli.v1" 207 | ) 208 | 209 | // WARNING: This file is generated! 210 | 211 | """) 212 | 213 | for typedef in types: 214 | _set_typedef_defaults(typedef) 215 | 216 | _fwrite(outfile, """\ 217 | // {name}Flag is the flag type that wraps cli.{name}Flag to allow 218 | // for other values to be specified 219 | type {name}Flag struct {{ 220 | cli.{name}Flag 221 | set *flag.FlagSet 222 | }} 223 | 224 | // New{name}Flag creates a new {name}Flag 225 | func New{name}Flag(fl cli.{name}Flag) *{name}Flag {{ 226 | return &{name}Flag{{{name}Flag: fl, set: nil}} 227 | }} 228 | 229 | // Apply saves the flagSet for later usage calls, then calls the 230 | // wrapped {name}Flag.Apply 231 | func (f *{name}Flag) Apply(set *flag.FlagSet) {{ 232 | f.set = set 233 | f.{name}Flag.Apply(set) 234 | }} 235 | 236 | // ApplyWithError saves the flagSet for later usage calls, then calls the 237 | // wrapped {name}Flag.ApplyWithError 238 | func (f *{name}Flag) ApplyWithError(set *flag.FlagSet) error {{ 239 | f.set = set 240 | return f.{name}Flag.ApplyWithError(set) 241 | }} 242 | """.format(**typedef)) 243 | 244 | 245 | def _fwrite(outfile, text): 246 | print(textwrap.dedent(text), end='', file=outfile) 247 | 248 | 249 | _WRITEFUNCS = { 250 | 'cli': _write_cli_flag_types, 251 | 'altsrc': _write_altsrc_flag_types 252 | } 253 | 254 | if __name__ == '__main__': 255 | sys.exit(main()) 256 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/help.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "strings" 8 | "text/tabwriter" 9 | "text/template" 10 | ) 11 | 12 | // AppHelpTemplate is the text template for the Default help topic. 13 | // cli.go uses text/template to render templates. You can 14 | // render custom help text by setting this variable. 15 | var AppHelpTemplate = `NAME: 16 | {{.Name}}{{if .Usage}} - {{.Usage}}{{end}} 17 | 18 | USAGE: 19 | {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} 20 | 21 | VERSION: 22 | {{.Version}}{{end}}{{end}}{{if .Description}} 23 | 24 | DESCRIPTION: 25 | {{.Description}}{{end}}{{if len .Authors}} 26 | 27 | AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: 28 | {{range $index, $author := .Authors}}{{if $index}} 29 | {{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}} 30 | 31 | COMMANDS:{{range .VisibleCategories}}{{if .Name}} 32 | {{.Name}}:{{end}}{{range .VisibleCommands}} 33 | {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} 34 | 35 | GLOBAL OPTIONS: 36 | {{range $index, $option := .VisibleFlags}}{{if $index}} 37 | {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} 38 | 39 | COPYRIGHT: 40 | {{.Copyright}}{{end}} 41 | ` 42 | 43 | // CommandHelpTemplate is the text template for the command help topic. 44 | // cli.go uses text/template to render templates. You can 45 | // render custom help text by setting this variable. 46 | var CommandHelpTemplate = `NAME: 47 | {{.HelpName}} - {{.Usage}} 48 | 49 | USAGE: 50 | {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} 51 | 52 | CATEGORY: 53 | {{.Category}}{{end}}{{if .Description}} 54 | 55 | DESCRIPTION: 56 | {{.Description}}{{end}}{{if .VisibleFlags}} 57 | 58 | OPTIONS: 59 | {{range .VisibleFlags}}{{.}} 60 | {{end}}{{end}} 61 | ` 62 | 63 | // SubcommandHelpTemplate is the text template for the subcommand help topic. 64 | // cli.go uses text/template to render templates. You can 65 | // render custom help text by setting this variable. 66 | var SubcommandHelpTemplate = `NAME: 67 | {{.HelpName}} - {{if .Description}}{{.Description}}{{else}}{{.Usage}}{{end}} 68 | 69 | USAGE: 70 | {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}} 71 | 72 | COMMANDS:{{range .VisibleCategories}}{{if .Name}} 73 | {{.Name}}:{{end}}{{range .VisibleCommands}} 74 | {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}} 75 | {{end}}{{if .VisibleFlags}} 76 | OPTIONS: 77 | {{range .VisibleFlags}}{{.}} 78 | {{end}}{{end}} 79 | ` 80 | 81 | var helpCommand = Command{ 82 | Name: "help", 83 | Aliases: []string{"h"}, 84 | Usage: "Shows a list of commands or help for one command", 85 | ArgsUsage: "[command]", 86 | Action: func(c *Context) error { 87 | args := c.Args() 88 | if args.Present() { 89 | return ShowCommandHelp(c, args.First()) 90 | } 91 | 92 | ShowAppHelp(c) 93 | return nil 94 | }, 95 | } 96 | 97 | var helpSubcommand = Command{ 98 | Name: "help", 99 | Aliases: []string{"h"}, 100 | Usage: "Shows a list of commands or help for one command", 101 | ArgsUsage: "[command]", 102 | Action: func(c *Context) error { 103 | args := c.Args() 104 | if args.Present() { 105 | return ShowCommandHelp(c, args.First()) 106 | } 107 | 108 | return ShowSubcommandHelp(c) 109 | }, 110 | } 111 | 112 | // Prints help for the App or Command 113 | type helpPrinter func(w io.Writer, templ string, data interface{}) 114 | 115 | // Prints help for the App or Command with custom template function. 116 | type helpPrinterCustom func(w io.Writer, templ string, data interface{}, customFunc map[string]interface{}) 117 | 118 | // HelpPrinter is a function that writes the help output. If not set a default 119 | // is used. The function signature is: 120 | // func(w io.Writer, templ string, data interface{}) 121 | var HelpPrinter helpPrinter = printHelp 122 | 123 | // HelpPrinterCustom is same as HelpPrinter but 124 | // takes a custom function for template function map. 125 | var HelpPrinterCustom helpPrinterCustom = printHelpCustom 126 | 127 | // VersionPrinter prints the version for the App 128 | var VersionPrinter = printVersion 129 | 130 | // ShowAppHelpAndExit - Prints the list of subcommands for the app and exits with exit code. 131 | func ShowAppHelpAndExit(c *Context, exitCode int) { 132 | ShowAppHelp(c) 133 | os.Exit(exitCode) 134 | } 135 | 136 | // ShowAppHelp is an action that displays the help. 137 | func ShowAppHelp(c *Context) (err error) { 138 | if c.App.CustomAppHelpTemplate == "" { 139 | HelpPrinter(c.App.Writer, AppHelpTemplate, c.App) 140 | return 141 | } 142 | customAppData := func() map[string]interface{} { 143 | if c.App.ExtraInfo == nil { 144 | return nil 145 | } 146 | return map[string]interface{}{ 147 | "ExtraInfo": c.App.ExtraInfo, 148 | } 149 | } 150 | HelpPrinterCustom(c.App.Writer, c.App.CustomAppHelpTemplate, c.App, customAppData()) 151 | return nil 152 | } 153 | 154 | // DefaultAppComplete prints the list of subcommands as the default app completion method 155 | func DefaultAppComplete(c *Context) { 156 | for _, command := range c.App.Commands { 157 | if command.Hidden { 158 | continue 159 | } 160 | for _, name := range command.Names() { 161 | fmt.Fprintln(c.App.Writer, name) 162 | } 163 | } 164 | } 165 | 166 | // ShowCommandHelpAndExit - exits with code after showing help 167 | func ShowCommandHelpAndExit(c *Context, command string, code int) { 168 | ShowCommandHelp(c, command) 169 | os.Exit(code) 170 | } 171 | 172 | // ShowCommandHelp prints help for the given command 173 | func ShowCommandHelp(ctx *Context, command string) error { 174 | // show the subcommand help for a command with subcommands 175 | if command == "" { 176 | HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App) 177 | return nil 178 | } 179 | 180 | for _, c := range ctx.App.Commands { 181 | if c.HasName(command) { 182 | if c.CustomHelpTemplate != "" { 183 | HelpPrinterCustom(ctx.App.Writer, c.CustomHelpTemplate, c, nil) 184 | } else { 185 | HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c) 186 | } 187 | return nil 188 | } 189 | } 190 | 191 | if ctx.App.CommandNotFound == nil { 192 | return NewExitError(fmt.Sprintf("No help topic for '%v'", command), 3) 193 | } 194 | 195 | ctx.App.CommandNotFound(ctx, command) 196 | return nil 197 | } 198 | 199 | // ShowSubcommandHelp prints help for the given subcommand 200 | func ShowSubcommandHelp(c *Context) error { 201 | return ShowCommandHelp(c, c.Command.Name) 202 | } 203 | 204 | // ShowVersion prints the version number of the App 205 | func ShowVersion(c *Context) { 206 | VersionPrinter(c) 207 | } 208 | 209 | func printVersion(c *Context) { 210 | fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version) 211 | } 212 | 213 | // ShowCompletions prints the lists of commands within a given context 214 | func ShowCompletions(c *Context) { 215 | a := c.App 216 | if a != nil && a.BashComplete != nil { 217 | a.BashComplete(c) 218 | } 219 | } 220 | 221 | // ShowCommandCompletions prints the custom completions for a given command 222 | func ShowCommandCompletions(ctx *Context, command string) { 223 | c := ctx.App.Command(command) 224 | if c != nil && c.BashComplete != nil { 225 | c.BashComplete(ctx) 226 | } 227 | } 228 | 229 | func printHelpCustom(out io.Writer, templ string, data interface{}, customFunc map[string]interface{}) { 230 | funcMap := template.FuncMap{ 231 | "join": strings.Join, 232 | } 233 | if customFunc != nil { 234 | for key, value := range customFunc { 235 | funcMap[key] = value 236 | } 237 | } 238 | 239 | w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0) 240 | t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) 241 | err := t.Execute(w, data) 242 | if err != nil { 243 | // If the writer is closed, t.Execute will fail, and there's nothing 244 | // we can do to recover. 245 | if os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" { 246 | fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err) 247 | } 248 | return 249 | } 250 | w.Flush() 251 | } 252 | 253 | func printHelp(out io.Writer, templ string, data interface{}) { 254 | printHelpCustom(out, templ, data, nil) 255 | } 256 | 257 | func checkVersion(c *Context) bool { 258 | found := false 259 | if VersionFlag.GetName() != "" { 260 | eachName(VersionFlag.GetName(), func(name string) { 261 | if c.GlobalBool(name) || c.Bool(name) { 262 | found = true 263 | } 264 | }) 265 | } 266 | return found 267 | } 268 | 269 | func checkHelp(c *Context) bool { 270 | found := false 271 | if HelpFlag.GetName() != "" { 272 | eachName(HelpFlag.GetName(), func(name string) { 273 | if c.GlobalBool(name) || c.Bool(name) { 274 | found = true 275 | } 276 | }) 277 | } 278 | return found 279 | } 280 | 281 | func checkCommandHelp(c *Context, name string) bool { 282 | if c.Bool("h") || c.Bool("help") { 283 | ShowCommandHelp(c, name) 284 | return true 285 | } 286 | 287 | return false 288 | } 289 | 290 | func checkSubcommandHelp(c *Context) bool { 291 | if c.Bool("h") || c.Bool("help") { 292 | ShowSubcommandHelp(c) 293 | return true 294 | } 295 | 296 | return false 297 | } 298 | 299 | func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) { 300 | if !a.EnableBashCompletion { 301 | return false, arguments 302 | } 303 | 304 | pos := len(arguments) - 1 305 | lastArg := arguments[pos] 306 | 307 | if lastArg != "--"+BashCompletionFlag.GetName() { 308 | return false, arguments 309 | } 310 | 311 | return true, arguments[:pos] 312 | } 313 | 314 | func checkCompletions(c *Context) bool { 315 | if !c.shellComplete { 316 | return false 317 | } 318 | 319 | if args := c.Args(); args.Present() { 320 | name := args.First() 321 | if cmd := c.App.Command(name); cmd != nil { 322 | // let the command handle the completion 323 | return false 324 | } 325 | } 326 | 327 | ShowCompletions(c) 328 | return true 329 | } 330 | 331 | func checkCommandCompletions(c *Context, name string) bool { 332 | if !c.shellComplete { 333 | return false 334 | } 335 | 336 | ShowCommandCompletions(c, name) 337 | return true 338 | } 339 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/runtests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import print_function 3 | 4 | import argparse 5 | import os 6 | import sys 7 | import tempfile 8 | 9 | from subprocess import check_call, check_output 10 | 11 | 12 | PACKAGE_NAME = os.environ.get( 13 | 'CLI_PACKAGE_NAME', 'github.com/urfave/cli' 14 | ) 15 | 16 | 17 | def main(sysargs=sys.argv[:]): 18 | targets = { 19 | 'vet': _vet, 20 | 'test': _test, 21 | 'gfmrun': _gfmrun, 22 | 'toc': _toc, 23 | 'gen': _gen, 24 | } 25 | 26 | parser = argparse.ArgumentParser() 27 | parser.add_argument( 28 | 'target', nargs='?', choices=tuple(targets.keys()), default='test' 29 | ) 30 | args = parser.parse_args(sysargs[1:]) 31 | 32 | targets[args.target]() 33 | return 0 34 | 35 | 36 | def _test(): 37 | if check_output('go version'.split()).split()[2] < 'go1.2': 38 | _run('go test -v .') 39 | return 40 | 41 | coverprofiles = [] 42 | for subpackage in ['', 'altsrc']: 43 | coverprofile = 'cli.coverprofile' 44 | if subpackage != '': 45 | coverprofile = '{}.coverprofile'.format(subpackage) 46 | 47 | coverprofiles.append(coverprofile) 48 | 49 | _run('go test -v'.split() + [ 50 | '-coverprofile={}'.format(coverprofile), 51 | ('{}/{}'.format(PACKAGE_NAME, subpackage)).rstrip('/') 52 | ]) 53 | 54 | combined_name = _combine_coverprofiles(coverprofiles) 55 | _run('go tool cover -func={}'.format(combined_name)) 56 | os.remove(combined_name) 57 | 58 | 59 | def _gfmrun(): 60 | go_version = check_output('go version'.split()).split()[2] 61 | if go_version < 'go1.3': 62 | print('runtests: skip on {}'.format(go_version), file=sys.stderr) 63 | return 64 | _run(['gfmrun', '-c', str(_gfmrun_count()), '-s', 'README.md']) 65 | 66 | 67 | def _vet(): 68 | _run('go vet ./...') 69 | 70 | 71 | def _toc(): 72 | _run('node_modules/.bin/markdown-toc -i README.md') 73 | _run('git diff --exit-code') 74 | 75 | 76 | def _gen(): 77 | go_version = check_output('go version'.split()).split()[2] 78 | if go_version < 'go1.5': 79 | print('runtests: skip on {}'.format(go_version), file=sys.stderr) 80 | return 81 | 82 | _run('go generate ./...') 83 | _run('git diff --exit-code') 84 | 85 | 86 | def _run(command): 87 | if hasattr(command, 'split'): 88 | command = command.split() 89 | print('runtests: {}'.format(' '.join(command)), file=sys.stderr) 90 | check_call(command) 91 | 92 | 93 | def _gfmrun_count(): 94 | with open('README.md') as infile: 95 | lines = infile.read().splitlines() 96 | return len(filter(_is_go_runnable, lines)) 97 | 98 | 99 | def _is_go_runnable(line): 100 | return line.startswith('package main') 101 | 102 | 103 | def _combine_coverprofiles(coverprofiles): 104 | combined = tempfile.NamedTemporaryFile( 105 | suffix='.coverprofile', delete=False 106 | ) 107 | combined.write('mode: set\n') 108 | 109 | for coverprofile in coverprofiles: 110 | with open(coverprofile, 'r') as infile: 111 | for line in infile.readlines(): 112 | if not line.startswith('mode: '): 113 | combined.write(line) 114 | 115 | combined.flush() 116 | name = combined.name 117 | combined.close() 118 | return name 119 | 120 | 121 | if __name__ == '__main__': 122 | sys.exit(main()) 123 | --------------------------------------------------------------------------------