Merge branch 'master' into doc/model
1
Cargo.lock
generated
@ -461,6 +461,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"md-5",
|
"md-5",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
|
"rand",
|
||||||
"roxmltree",
|
"roxmltree",
|
||||||
"sha2",
|
"sha2",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
23
README.md
@ -20,17 +20,24 @@ Our main use case is to provide a distributed storage layer for small-scale self
|
|||||||
|
|
||||||
We propose the following quickstart to setup a full dev. environment as quickly as possible:
|
We propose the following quickstart to setup a full dev. environment as quickly as possible:
|
||||||
|
|
||||||
1. Setup a rust/cargo environment and install s3cmd. eg. `dnf install rust cargo s3cmd`
|
1. Setup a rust/cargo environment. eg. `dnf install rust cargo`
|
||||||
2. Run `cargo build` to build the project
|
2. Install awscli v2 by following the guide [here](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html).
|
||||||
3. Run `./script/dev-cluster.sh` to launch a test cluster (feel free to read the script)
|
3. Run `cargo build` to build the project
|
||||||
4. Run `./script/dev-configure.sh` to configure your test cluster with default values (same datacenter, 100 tokens)
|
4. Run `./script/dev-cluster.sh` to launch a test cluster (feel free to read the script)
|
||||||
5. Run `./script/dev-bucket.sh` to create a bucket named `éprouvette` and an API key that will be stored in `/tmp/garage.s3`
|
5. Run `./script/dev-configure.sh` to configure your test cluster with default values (same datacenter, 100 tokens)
|
||||||
6. Run `source ./script/dev-env.sh` to configure your CLI environment
|
6. Run `./script/dev-bucket.sh` to create a bucket named `eprouvette` and an API key that will be stored in `/tmp/garage.s3`
|
||||||
7. You can use `garage` to manage the cluster. Try `garage --help`.
|
7. Run `source ./script/dev-env-aws.sh` to configure your CLI environment
|
||||||
8. You can use `s3grg` to add, remove, and delete files. Try `s3grg --help`, `s3grg put /proc/cpuinfo s3://éprouvette/cpuinfo.txt`, `s3grg ls s3://éprouvette`. `s3grg` is a wrapper on `s3cmd` configured with the previously generated API key (the one in `/tmp/garage.s3`).
|
8. You can use `garage` to manage the cluster. Try `garage --help`.
|
||||||
|
9. You can use the `awsgrg` alias to add, remove, and delete files. Try `awsgrg help`, `awsgrg cp /proc/cpuinfo s3://eprouvette/cpuinfo.txt`, or `awsgrg ls s3://eprouvette`. `awsgrg` is a wrapper on the `aws s3` command pre-configured with the previously generated API key (the one in `/tmp/garage.s3`) and localhost as the endpoint.
|
||||||
|
|
||||||
Now you should be ready to start hacking on garage!
|
Now you should be ready to start hacking on garage!
|
||||||
|
|
||||||
|
## S3 compatibility
|
||||||
|
|
||||||
|
Only a subset of S3 is supported: adding, listing, getting and deleting files in a bucket.
|
||||||
|
Bucket management, ACL and other advanced features are not (yet?) handled through the S3 API but through the `garage` CLI.
|
||||||
|
We primarily test `garage` against the `awscli` tool and `nextcloud`.
|
||||||
|
|
||||||
## Setting up Garage
|
## Setting up Garage
|
||||||
|
|
||||||
Use the `genkeys.sh` script to generate TLS keys for encrypting communications between Garage nodes.
|
Use the `genkeys.sh` script to generate TLS keys for encrypting communications between Garage nodes.
|
||||||
|
12
doc/20201202_talk/.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
*
|
||||||
|
|
||||||
|
!img
|
||||||
|
|
||||||
|
!.gitignore
|
||||||
|
!*.svg
|
||||||
|
!*.png
|
||||||
|
!*.jpg
|
||||||
|
!*.tex
|
||||||
|
!Makefile
|
||||||
|
!.gitignore
|
||||||
|
!talk.pdf
|
6
doc/20201202_talk/Makefile
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
talk.pdf: talk.tex img/garage_distributed.pdf img/consistent_hashing_1.pdf img/consistent_hashing_2.pdf img/consistent_hashing_3.pdf img/consistent_hashing_4.pdf img/garage_tables.pdf
|
||||||
|
pdflatex talk.tex
|
||||||
|
|
||||||
|
img/%.pdf: img/%.svg
|
||||||
|
inkscape -D -z --file=$^ --export-pdf=$@
|
||||||
|
|
BIN
doc/20201202_talk/img/Amazon-S3.jpg
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
doc/20201202_talk/img/cloud.png
Normal file
After Width: | Height: | Size: 19 KiB |
301
doc/20201202_talk/img/consistent_hashing_1.svg
Normal file
After Width: | Height: | Size: 53 KiB |
334
doc/20201202_talk/img/consistent_hashing_2.svg
Normal file
After Width: | Height: | Size: 54 KiB |
358
doc/20201202_talk/img/consistent_hashing_3.svg
Normal file
After Width: | Height: | Size: 56 KiB |
377
doc/20201202_talk/img/consistent_hashing_4.svg
Normal file
After Width: | Height: | Size: 57 KiB |
BIN
doc/20201202_talk/img/dc.jpg
Normal file
After Width: | Height: | Size: 360 KiB |
BIN
doc/20201202_talk/img/death.jpg
Normal file
After Width: | Height: | Size: 39 KiB |
404
doc/20201202_talk/img/garage_distributed.svg
Normal file
@ -0,0 +1,404 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="480"
|
||||||
|
height="480"
|
||||||
|
viewBox="0 0 127 127"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
|
||||||
|
sodipodi:docname="garage_distributed.svg">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1.979899"
|
||||||
|
inkscape:cx="171.34852"
|
||||||
|
inkscape:cy="170.69443"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:current-layer="layer1-3"
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
inkscape:window-width="1404"
|
||||||
|
inkscape:window-height="1016"
|
||||||
|
inkscape:window-x="103"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="0" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
id="layer1-3"
|
||||||
|
transform="matrix(0.42851498,0,0,0.42851498,24.079728,-24.925134)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.065;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 66.78016,80.71889 99.921832,61.598165 132.84481,80.509232 V 127.38418 H 66.701651 Z"
|
||||||
|
id="path124"
|
||||||
|
sodipodi:nodetypes="cccccc" />
|
||||||
|
<g
|
||||||
|
id="g1106-5"
|
||||||
|
transform="matrix(0,0.95201267,-0.95201267,0,194.01664,-57.627274)"
|
||||||
|
style="stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none">
|
||||||
|
<g
|
||||||
|
id="g1061-3"
|
||||||
|
style="stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none">
|
||||||
|
<circle
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path956-5"
|
||||||
|
cx="168.8569"
|
||||||
|
cy="92.889587"
|
||||||
|
r="13.125794" />
|
||||||
|
<circle
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path958-6"
|
||||||
|
cx="168.77444"
|
||||||
|
cy="92.702293"
|
||||||
|
r="3.0778286" />
|
||||||
|
<path
|
||||||
|
id="path960-2"
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 169.46072,82.84435 c 4.95795,0.336608 8.87296,4.341959 9.09638,9.306301"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 154.67824,112.84018 11.89881,-13.038071 c 1.46407,-1.552664 3.79541,0.878511 2.81832,2.089181 l -10.57965,14.481 c -1.8851,2.02632 -6.10786,-1.06119 -4.13748,-3.53211 z"
|
||||||
|
id="path964-9"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
<g
|
||||||
|
id="g1071-1"
|
||||||
|
style="stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||||
|
<g
|
||||||
|
id="g1065-3"
|
||||||
|
style="stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none">
|
||||||
|
<rect
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect949-6"
|
||||||
|
width="35.576611"
|
||||||
|
height="48.507355"
|
||||||
|
x="150.9623"
|
||||||
|
y="74.698929"
|
||||||
|
ry="2.7302756" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 150.76919,106.16944 6.36181,-0.0223 c 2.53845,3.46232 6.29787,4.20243 10.1055,4.40362 l 0.0176,13.09251"
|
||||||
|
id="path1033-0"
|
||||||
|
sodipodi:nodetypes="cccc" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
id="layer1-3-5"
|
||||||
|
transform="matrix(0.42851499,0,0,0.42851499,68.181495,12.180995)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.065;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 66.78016,73.340623 99.921832,54.219898 132.84481,73.130965 V 120.00591 H 66.701651 Z"
|
||||||
|
id="path124-6"
|
||||||
|
sodipodi:nodetypes="cccccc" />
|
||||||
|
<g
|
||||||
|
id="g1106-5-2"
|
||||||
|
transform="matrix(0,0.95201267,-0.95201267,0,194.01664,-65.058377)"
|
||||||
|
style="stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none">
|
||||||
|
<g
|
||||||
|
id="g1061-3-9"
|
||||||
|
style="stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none">
|
||||||
|
<circle
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path956-5-1"
|
||||||
|
cx="168.8569"
|
||||||
|
cy="92.889587"
|
||||||
|
r="13.125794" />
|
||||||
|
<circle
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path958-6-2"
|
||||||
|
cx="168.77444"
|
||||||
|
cy="92.702293"
|
||||||
|
r="3.0778286" />
|
||||||
|
<path
|
||||||
|
id="path960-2-7"
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 169.46072,82.84435 c 4.95795,0.336608 8.87296,4.341959 9.09638,9.306301"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 154.67824,112.84018 11.89881,-13.038071 c 1.46407,-1.552664 3.79541,0.878511 2.81832,2.089181 l -10.57965,14.481 c -1.8851,2.02632 -6.10786,-1.06119 -4.13748,-3.53211 z"
|
||||||
|
id="path964-9-0"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
<g
|
||||||
|
id="g1071-1-9"
|
||||||
|
style="stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||||
|
<g
|
||||||
|
id="g1065-3-3"
|
||||||
|
style="stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none">
|
||||||
|
<rect
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect949-6-6"
|
||||||
|
width="35.576611"
|
||||||
|
height="48.507355"
|
||||||
|
x="150.9623"
|
||||||
|
y="74.698929"
|
||||||
|
ry="2.7302756" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 150.76919,106.16944 6.36181,-0.0223 c 2.53845,3.46232 6.29787,4.20243 10.1055,4.40362 l 0.0176,13.09251"
|
||||||
|
id="path1033-0-0"
|
||||||
|
sodipodi:nodetypes="cccc" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
id="layer1-3-6"
|
||||||
|
transform="matrix(0.42851499,0,0,0.42851499,-20.953301,19.351613)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.065;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 66.78016,73.340623 99.921832,54.219898 132.84481,73.130965 V 120.00591 H 66.701651 Z"
|
||||||
|
id="path124-2"
|
||||||
|
sodipodi:nodetypes="cccccc" />
|
||||||
|
<g
|
||||||
|
id="g1106-5-6"
|
||||||
|
transform="matrix(0,0.95201267,-0.95201267,0,194.01664,-65.058377)"
|
||||||
|
style="stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none">
|
||||||
|
<g
|
||||||
|
id="g1061-3-1"
|
||||||
|
style="stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none">
|
||||||
|
<circle
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path956-5-8"
|
||||||
|
cx="168.8569"
|
||||||
|
cy="92.889587"
|
||||||
|
r="13.125794" />
|
||||||
|
<circle
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path958-6-7"
|
||||||
|
cx="168.77444"
|
||||||
|
cy="92.702293"
|
||||||
|
r="3.0778286" />
|
||||||
|
<path
|
||||||
|
id="path960-2-9"
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 169.46072,82.84435 c 4.95795,0.336608 8.87296,4.341959 9.09638,9.306301"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 154.67824,112.84018 11.89881,-13.038071 c 1.46407,-1.552664 3.79541,0.878511 2.81832,2.089181 l -10.57965,14.481 c -1.8851,2.02632 -6.10786,-1.06119 -4.13748,-3.53211 z"
|
||||||
|
id="path964-9-2"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
<g
|
||||||
|
id="g1071-1-0"
|
||||||
|
style="stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||||
|
<g
|
||||||
|
id="g1065-3-2"
|
||||||
|
style="stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none">
|
||||||
|
<rect
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect949-6-3"
|
||||||
|
width="35.576611"
|
||||||
|
height="48.507355"
|
||||||
|
x="150.9623"
|
||||||
|
y="74.698929"
|
||||||
|
ry="2.7302756" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 150.76919,106.16944 6.36181,-0.0223 c 2.53845,3.46232 6.29787,4.20243 10.1055,4.40362 l 0.0176,13.09251"
|
||||||
|
id="path1033-0-7"
|
||||||
|
sodipodi:nodetypes="cccc" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
id="layer1-3-59"
|
||||||
|
transform="matrix(0.42851499,0,0,0.42851499,51.949789,75.218277)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.065;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 66.78016,73.340623 99.921832,54.219898 132.84481,73.130965 V 120.00591 H 66.701651 Z"
|
||||||
|
id="path124-22"
|
||||||
|
sodipodi:nodetypes="cccccc" />
|
||||||
|
<g
|
||||||
|
id="g1106-5-8"
|
||||||
|
transform="matrix(0,0.95201267,-0.95201267,0,194.01664,-65.058377)"
|
||||||
|
style="stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none">
|
||||||
|
<g
|
||||||
|
id="g1061-3-97"
|
||||||
|
style="stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none">
|
||||||
|
<circle
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path956-5-3"
|
||||||
|
cx="168.8569"
|
||||||
|
cy="92.889587"
|
||||||
|
r="13.125794" />
|
||||||
|
<circle
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path958-6-6"
|
||||||
|
cx="168.77444"
|
||||||
|
cy="92.702293"
|
||||||
|
r="3.0778286" />
|
||||||
|
<path
|
||||||
|
id="path960-2-1"
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 169.46072,82.84435 c 4.95795,0.336608 8.87296,4.341959 9.09638,9.306301"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 154.67824,112.84018 11.89881,-13.038071 c 1.46407,-1.552664 3.79541,0.878511 2.81832,2.089181 l -10.57965,14.481 c -1.8851,2.02632 -6.10786,-1.06119 -4.13748,-3.53211 z"
|
||||||
|
id="path964-9-29"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
<g
|
||||||
|
id="g1071-1-3"
|
||||||
|
style="stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||||
|
<g
|
||||||
|
id="g1065-3-1"
|
||||||
|
style="stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none">
|
||||||
|
<rect
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect949-6-9"
|
||||||
|
width="35.576611"
|
||||||
|
height="48.507355"
|
||||||
|
x="150.9623"
|
||||||
|
y="74.698929"
|
||||||
|
ry="2.7302756" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 150.76919,106.16944 6.36181,-0.0223 c 2.53845,3.46232 6.29787,4.20243 10.1055,4.40362 l 0.0176,13.09251"
|
||||||
|
id="path1033-0-4"
|
||||||
|
sodipodi:nodetypes="cccc" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
id="layer1-3-7"
|
||||||
|
transform="matrix(0.42851499,0,0,0.42851499,-1.173447,75.150288)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.065;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 66.78016,73.340623 99.921832,54.219898 132.84481,73.130965 V 120.00591 H 66.701651 Z"
|
||||||
|
id="path124-8"
|
||||||
|
sodipodi:nodetypes="cccccc" />
|
||||||
|
<g
|
||||||
|
id="g1106-5-4"
|
||||||
|
transform="matrix(0,0.95201267,-0.95201267,0,194.01664,-65.058377)"
|
||||||
|
style="stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none">
|
||||||
|
<g
|
||||||
|
id="g1061-3-5"
|
||||||
|
style="stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none">
|
||||||
|
<circle
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path956-5-0"
|
||||||
|
cx="168.8569"
|
||||||
|
cy="92.889587"
|
||||||
|
r="13.125794" />
|
||||||
|
<circle
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path958-6-3"
|
||||||
|
cx="168.77444"
|
||||||
|
cy="92.702293"
|
||||||
|
r="3.0778286" />
|
||||||
|
<path
|
||||||
|
id="path960-2-6"
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 169.46072,82.84435 c 4.95795,0.336608 8.87296,4.341959 9.09638,9.306301"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 154.67824,112.84018 11.89881,-13.038071 c 1.46407,-1.552664 3.79541,0.878511 2.81832,2.089181 l -10.57965,14.481 c -1.8851,2.02632 -6.10786,-1.06119 -4.13748,-3.53211 z"
|
||||||
|
id="path964-9-1"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
<g
|
||||||
|
id="g1071-1-06"
|
||||||
|
style="stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||||
|
<g
|
||||||
|
id="g1065-3-32"
|
||||||
|
style="stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none">
|
||||||
|
<rect
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect949-6-0"
|
||||||
|
width="35.576611"
|
||||||
|
height="48.507355"
|
||||||
|
x="150.9623"
|
||||||
|
y="74.698929"
|
||||||
|
ry="2.7302756" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.17959;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 150.76919,106.16944 6.36181,-0.0223 c 2.53845,3.46232 6.29787,4.20243 10.1055,4.40362 l 0.0176,13.09251"
|
||||||
|
id="path1033-0-6"
|
||||||
|
sodipodi:nodetypes="cccc" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 35.21897,43.254452 46.803736,32.872178"
|
||||||
|
id="path1045" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 85.798392,29.613721 10.944185,7.688225"
|
||||||
|
id="path1047" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 107.59813,71.879386 -6.2564,22.552649"
|
||||||
|
id="path1049" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 75.866769,119.14997 61.529058,118.74136"
|
||||||
|
id="path1051"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 29.414211,98.256475 C 29.681482,96.462435 21.07721,77.446418 21.07721,77.446418"
|
||||||
|
id="path1053" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 39.447822,61.341585 90.641428,57.562618"
|
||||||
|
id="path1055" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 90.440176,64.423751 54.180736,100.02908"
|
||||||
|
id="path1057"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 47.163557,96.532205 61.535331,33.078667"
|
||||||
|
id="path1059"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 71.396211,33.058731 15.77285,60.595014"
|
||||||
|
id="path1061" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 79.384641,100.96895 41.150775,67.902625"
|
||||||
|
id="path1063" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 19 KiB |
502
doc/20201202_talk/img/garage_tables.svg
Normal file
@ -0,0 +1,502 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="850"
|
||||||
|
height="480"
|
||||||
|
viewBox="0 0 224.89584 127"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
|
||||||
|
sodipodi:docname="garage_tables.svg">
|
||||||
|
<defs
|
||||||
|
id="defs2">
|
||||||
|
<marker
|
||||||
|
style="overflow:visible"
|
||||||
|
id="marker1262"
|
||||||
|
refX="0"
|
||||||
|
refY="0"
|
||||||
|
orient="auto"
|
||||||
|
inkscape:stockid="Arrow1Mend"
|
||||||
|
inkscape:isstock="true">
|
||||||
|
<path
|
||||||
|
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||||
|
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||||
|
d="M 0,0 5,-5 -12.5,0 5,5 Z"
|
||||||
|
id="path1260" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
style="overflow:visible"
|
||||||
|
id="Arrow1Mend"
|
||||||
|
refX="0"
|
||||||
|
refY="0"
|
||||||
|
orient="auto"
|
||||||
|
inkscape:stockid="Arrow1Mend"
|
||||||
|
inkscape:isstock="true"
|
||||||
|
inkscape:collect="always">
|
||||||
|
<path
|
||||||
|
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||||
|
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||||
|
d="M 0,0 5,-5 -12.5,0 5,5 Z"
|
||||||
|
id="path965" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
style="overflow:visible"
|
||||||
|
id="Arrow1Lend"
|
||||||
|
refX="0"
|
||||||
|
refY="0"
|
||||||
|
orient="auto"
|
||||||
|
inkscape:stockid="Arrow1Lend"
|
||||||
|
inkscape:isstock="true">
|
||||||
|
<path
|
||||||
|
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||||
|
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||||
|
d="M 0,0 5,-5 -12.5,0 5,5 Z"
|
||||||
|
id="path959" />
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="0.98994949"
|
||||||
|
inkscape:cx="381.09221"
|
||||||
|
inkscape:cy="219.5592"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
inkscape:window-width="1867"
|
||||||
|
inkscape:window-height="1016"
|
||||||
|
inkscape:window-x="53"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="39.570904"
|
||||||
|
y="38.452755"
|
||||||
|
id="text2025"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan2023"
|
||||||
|
x="39.570904"
|
||||||
|
y="38.452755"
|
||||||
|
style="font-size:5.64444px;stroke-width:0.264583" /></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="101.95796"
|
||||||
|
y="92.835831"
|
||||||
|
id="text2139"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan2137"
|
||||||
|
x="101.95796"
|
||||||
|
y="92.835831"
|
||||||
|
style="stroke-width:0.264583"> </tspan></text>
|
||||||
|
<g
|
||||||
|
id="g2316"
|
||||||
|
transform="translate(-11.455511,1.5722486)">
|
||||||
|
<g
|
||||||
|
id="g2277">
|
||||||
|
<rect
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.8;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect833"
|
||||||
|
width="47.419891"
|
||||||
|
height="95.353409"
|
||||||
|
x="18.534418"
|
||||||
|
y="24.42766" />
|
||||||
|
<rect
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.799999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect833-3"
|
||||||
|
width="47.419891"
|
||||||
|
height="86.973076"
|
||||||
|
x="18.534418"
|
||||||
|
y="32.807987" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="32.250839"
|
||||||
|
y="29.894743"
|
||||||
|
id="text852"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan850"
|
||||||
|
x="32.250839"
|
||||||
|
y="29.894743"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">Object</tspan></text>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g2066"
|
||||||
|
transform="translate(-2.1807817,-3.0621439)">
|
||||||
|
<g
|
||||||
|
id="g1969"
|
||||||
|
transform="matrix(0.12763631,0,0,0.12763631,0.7215051,24.717273)"
|
||||||
|
style="fill:#ff6600;fill-opacity:1;stroke:none;stroke-opacity:1">
|
||||||
|
<path
|
||||||
|
style="fill:#ff6600;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-opacity:1"
|
||||||
|
d="m 203.71837,154.80038 c -1.11451,3.75057 -2.45288,5.84095 -5.11132,7.98327 -2.2735,1.83211 -4.66721,2.65982 -8.09339,2.79857 -2.59227,0.10498 -2.92868,0.0577 -5.02863,-0.70611 -3.99215,-1.45212 -7.1627,-4.65496 -8.48408,-8.57046 -1.28374,-3.80398 -0.61478,-8.68216 1.64793,-12.01698 0.87317,-1.28689 3.15089,-3.48326 4.18771,-4.03815 l 0.53332,-28.51234 5.78454,-5.09197 6.95158,6.16704 -3.21112,3.49026 3.17616,3.45499 -3.17616,3.40822 2.98973,3.28645 -3.24843,3.3829 4.49203,4.58395 0.0516,5.69106 c 1.06874,0.64848 3.81974,3.24046 4.69548,4.56257 0.452,0.68241 1.06834,2.0197 1.36962,2.97176 0.62932,1.98864 0.88051,5.785 0.47342,7.15497 z m -10.0406,2.32604 c -0.88184,-3.17515 -4.92402,-3.78864 -6.75297,-1.02492 -0.58328,0.8814 -0.6898,1.28852 -0.58362,2.23056 0.26492,2.35041 2.45434,3.95262 4.60856,3.37255 1.19644,-0.32217 2.39435,-1.44872 2.72875,-2.56621 0.30682,-1.02529 0.30686,-0.9045 -7.9e-4,-2.01198 z"
|
||||||
|
id="path1971"
|
||||||
|
sodipodi:nodetypes="ssscsscccccccccccssscsssscc" />
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="28.809687"
|
||||||
|
y="44.070885"
|
||||||
|
id="text852-9"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan850-4"
|
||||||
|
x="28.809687"
|
||||||
|
y="44.070885"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">bucket </tspan></text>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g2066-7"
|
||||||
|
transform="translate(-2.1807817,6.2627616)">
|
||||||
|
<g
|
||||||
|
id="g1969-8"
|
||||||
|
transform="matrix(0.12763631,0,0,0.12763631,0.7215051,24.717273)"
|
||||||
|
style="fill:#ff6600;fill-opacity:1;stroke:none;stroke-opacity:1">
|
||||||
|
<path
|
||||||
|
style="fill:#ff6600;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-opacity:1"
|
||||||
|
d="m 203.71837,154.80038 c -1.11451,3.75057 -2.45288,5.84095 -5.11132,7.98327 -2.2735,1.83211 -4.66721,2.65982 -8.09339,2.79857 -2.59227,0.10498 -2.92868,0.0577 -5.02863,-0.70611 -3.99215,-1.45212 -7.1627,-4.65496 -8.48408,-8.57046 -1.28374,-3.80398 -0.61478,-8.68216 1.64793,-12.01698 0.87317,-1.28689 3.15089,-3.48326 4.18771,-4.03815 l 0.53332,-28.51234 5.78454,-5.09197 6.95158,6.16704 -3.21112,3.49026 3.17616,3.45499 -3.17616,3.40822 2.98973,3.28645 -3.24843,3.3829 4.49203,4.58395 0.0516,5.69106 c 1.06874,0.64848 3.81974,3.24046 4.69548,4.56257 0.452,0.68241 1.06834,2.0197 1.36962,2.97176 0.62932,1.98864 0.88051,5.785 0.47342,7.15497 z m -10.0406,2.32604 c -0.88184,-3.17515 -4.92402,-3.78864 -6.75297,-1.02492 -0.58328,0.8814 -0.6898,1.28852 -0.58362,2.23056 0.26492,2.35041 2.45434,3.95262 4.60856,3.37255 1.19644,-0.32217 2.39435,-1.44872 2.72875,-2.56621 0.30682,-1.02529 0.30686,-0.9045 -7.9e-4,-2.01198 z"
|
||||||
|
id="path1971-4"
|
||||||
|
sodipodi:nodetypes="ssscsscccccccccccssscsssscc" />
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="28.809687"
|
||||||
|
y="44.070885"
|
||||||
|
id="text852-9-5"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan850-4-0"
|
||||||
|
x="28.809687"
|
||||||
|
y="44.070885"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">file path </tspan></text>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g2161"
|
||||||
|
transform="translate(-62.264403,-59.333115)">
|
||||||
|
<g
|
||||||
|
id="g2271"
|
||||||
|
transform="translate(0,67.042823)">
|
||||||
|
<rect
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.799999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect833-6"
|
||||||
|
width="39.008453"
|
||||||
|
height="16.775949"
|
||||||
|
x="84.896881"
|
||||||
|
y="90.266838" />
|
||||||
|
<rect
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.799999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect833-3-1"
|
||||||
|
width="39.008453"
|
||||||
|
height="8.673645"
|
||||||
|
x="84.896881"
|
||||||
|
y="98.369141" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="89.826942"
|
||||||
|
y="96.212921"
|
||||||
|
id="text852-0"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan850-6"
|
||||||
|
x="89.826942"
|
||||||
|
y="96.212921"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">Version 1</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="89.826942"
|
||||||
|
y="104.71013"
|
||||||
|
id="text852-0-3"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan850-6-2"
|
||||||
|
x="89.826942"
|
||||||
|
y="104.71013"
|
||||||
|
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';fill:#4d4d4d;stroke-width:0.264583">deleted</tspan></text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g2263"
|
||||||
|
transform="translate(0,-22.791204)">
|
||||||
|
<g
|
||||||
|
id="g2161-1"
|
||||||
|
transform="translate(-62.264403,-10.910843)">
|
||||||
|
<rect
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.799999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect833-6-5"
|
||||||
|
width="39.008453"
|
||||||
|
height="36.749603"
|
||||||
|
x="84.896881"
|
||||||
|
y="90.266838" />
|
||||||
|
<rect
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.799999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect833-3-1-5"
|
||||||
|
width="39.008453"
|
||||||
|
height="28.647301"
|
||||||
|
x="84.896881"
|
||||||
|
y="98.369141" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="89.826942"
|
||||||
|
y="96.212921"
|
||||||
|
id="text852-0-4"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan850-6-7"
|
||||||
|
x="89.826942"
|
||||||
|
y="96.212921"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">Version 2</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="89.826942"
|
||||||
|
y="104.71013"
|
||||||
|
id="text852-0-3-6"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan850-6-2-5"
|
||||||
|
x="89.826942"
|
||||||
|
y="104.71013"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';fill:#000000;stroke-width:0.264583">id</tspan></text>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="27.56254"
|
||||||
|
y="100.34132"
|
||||||
|
id="text852-0-3-6-6"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan850-6-2-5-9"
|
||||||
|
x="27.56254"
|
||||||
|
y="100.34132"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';fill:#000000;stroke-width:0.264583">size</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="27.56254"
|
||||||
|
y="106.90263"
|
||||||
|
id="text852-0-3-6-6-3"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan850-6-2-5-9-7"
|
||||||
|
x="27.56254"
|
||||||
|
y="106.90263"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';fill:#000000;stroke-width:0.264583">MIME type</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="27.56254"
|
||||||
|
y="111.92816"
|
||||||
|
id="text852-0-3-6-6-3-4"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan850-6-2-5-9-7-5"
|
||||||
|
x="27.56254"
|
||||||
|
y="111.92816"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';fill:#000000;stroke-width:0.264583">...</tspan></text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g898"
|
||||||
|
transform="translate(-6.2484318,29.95006)">
|
||||||
|
<rect
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.799999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect833-7"
|
||||||
|
width="47.419891"
|
||||||
|
height="44.007515"
|
||||||
|
x="95.443573"
|
||||||
|
y="24.42766" />
|
||||||
|
<rect
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.799999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect833-3-4"
|
||||||
|
width="47.419891"
|
||||||
|
height="35.627186"
|
||||||
|
x="95.443573"
|
||||||
|
y="32.807987" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="107.46638"
|
||||||
|
y="29.894743"
|
||||||
|
id="text852-4"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan850-3"
|
||||||
|
x="107.46638"
|
||||||
|
y="29.894743"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">Version</tspan></text>
|
||||||
|
<path
|
||||||
|
style="fill:#ff6600;fill-opacity:1;stroke:none;stroke-width:0.0337704;stroke-opacity:1"
|
||||||
|
d="m 102.90563,41.413279 c -0.14226,0.478709 -0.31308,0.745518 -0.65239,1.018956 -0.29019,0.233843 -0.59571,0.339489 -1.03301,0.357199 -0.33087,0.0134 -0.37381,0.0074 -0.64184,-0.09013 -0.50954,-0.185343 -0.914221,-0.594142 -1.082877,-1.093901 -0.163852,-0.485526 -0.07847,-1.108159 0.210335,-1.533803 0.111448,-0.164254 0.402172,-0.444591 0.534502,-0.515415 l 0.0681,-3.63921 0.73832,-0.64992 0.88727,0.787138 -0.40985,0.445484 0.40539,0.440982 -0.40539,0.435013 0.3816,0.41947 -0.41462,0.431781 0.57335,0.585078 0.007,0.726386 c 0.13641,0.08277 0.48753,0.413601 0.59931,0.58235 0.0577,0.0871 0.13636,0.257787 0.17481,0.379304 0.0803,0.253823 0.11239,0.738377 0.0604,0.913234 z m -1.28155,0.296888 c -0.11255,-0.405265 -0.62848,-0.483569 -0.86192,-0.130817 -0.0744,0.112498 -0.088,0.164461 -0.0745,0.2847 0.0338,0.299998 0.31326,0.504498 0.58822,0.43046 0.15271,-0.04112 0.3056,-0.184909 0.34828,-0.327542 0.0392,-0.130864 0.0392,-0.115447 -1e-4,-0.256801 z"
|
||||||
|
id="path1971-0"
|
||||||
|
sodipodi:nodetypes="ssscsscccccccccccssscsssscc" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="104.99195"
|
||||||
|
y="41.008743"
|
||||||
|
id="text852-9-7"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan850-4-8"
|
||||||
|
x="104.99195"
|
||||||
|
y="41.008743"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">id </tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="104.99195"
|
||||||
|
y="49.168018"
|
||||||
|
id="text852-9-7-6"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan850-4-8-8"
|
||||||
|
x="104.99195"
|
||||||
|
y="49.168018"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">h(block 1)</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="104.99195"
|
||||||
|
y="56.583336"
|
||||||
|
id="text852-9-7-6-8"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan850-4-8-8-4"
|
||||||
|
x="104.99195"
|
||||||
|
y="56.583336"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">h(block 2)</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="104.99195"
|
||||||
|
y="64.265732"
|
||||||
|
id="text852-9-7-6-3"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan850-4-8-8-1"
|
||||||
|
x="104.99195"
|
||||||
|
y="64.265732"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">...</tspan></text>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g898-3"
|
||||||
|
transform="translate(75.777779,38.888663)">
|
||||||
|
<rect
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.799999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect833-7-6"
|
||||||
|
width="47.419891"
|
||||||
|
height="29.989157"
|
||||||
|
x="95.443573"
|
||||||
|
y="24.42766" />
|
||||||
|
<rect
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.799999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect833-3-4-7"
|
||||||
|
width="47.419891"
|
||||||
|
height="21.608831"
|
||||||
|
x="95.443573"
|
||||||
|
y="32.807987" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="102.11134"
|
||||||
|
y="29.894743"
|
||||||
|
id="text852-4-5"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan850-3-3"
|
||||||
|
x="102.11134"
|
||||||
|
y="29.894743"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">Data block</tspan></text>
|
||||||
|
<path
|
||||||
|
style="fill:#ff6600;fill-opacity:1;stroke:none;stroke-width:0.0337704;stroke-opacity:1"
|
||||||
|
d="m 102.90563,41.413279 c -0.14226,0.478709 -0.31308,0.745518 -0.65239,1.018956 -0.29019,0.233843 -0.59571,0.339489 -1.03301,0.357199 -0.33087,0.0134 -0.37381,0.0074 -0.64184,-0.09013 -0.50954,-0.185343 -0.914221,-0.594142 -1.082877,-1.093901 -0.163852,-0.485526 -0.07847,-1.108159 0.210335,-1.533803 0.111448,-0.164254 0.402172,-0.444591 0.534502,-0.515415 l 0.0681,-3.63921 0.73832,-0.64992 0.88727,0.787138 -0.40985,0.445484 0.40539,0.440982 -0.40539,0.435013 0.3816,0.41947 -0.41462,0.431781 0.57335,0.585078 0.007,0.726386 c 0.13641,0.08277 0.48753,0.413601 0.59931,0.58235 0.0577,0.0871 0.13636,0.257787 0.17481,0.379304 0.0803,0.253823 0.11239,0.738377 0.0604,0.913234 z m -1.28155,0.296888 c -0.11255,-0.405265 -0.62848,-0.483569 -0.86192,-0.130817 -0.0744,0.112498 -0.088,0.164461 -0.0745,0.2847 0.0338,0.299998 0.31326,0.504498 0.58822,0.43046 0.15271,-0.04112 0.3056,-0.184909 0.34828,-0.327542 0.0392,-0.130864 0.0392,-0.115447 -1e-4,-0.256801 z"
|
||||||
|
id="path1971-0-5"
|
||||||
|
sodipodi:nodetypes="ssscsscccccccccccssscsssscc" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="104.99195"
|
||||||
|
y="41.008743"
|
||||||
|
id="text852-9-7-62"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan850-4-8-9"
|
||||||
|
x="104.99195"
|
||||||
|
y="41.008743"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">hash </tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="104.99195"
|
||||||
|
y="49.168018"
|
||||||
|
id="text852-9-7-6-1"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan850-4-8-8-2"
|
||||||
|
x="104.99195"
|
||||||
|
y="49.168018"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">data</tspan></text>
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Mend)"
|
||||||
|
d="M 42.105292,69.455903 89.563703,69.317144"
|
||||||
|
id="path954"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker1262)"
|
||||||
|
d="m 134.32612,77.363197 38.12618,0.260865"
|
||||||
|
id="path1258"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="8.6727352"
|
||||||
|
y="16.687063"
|
||||||
|
id="text852-3"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan850-67"
|
||||||
|
x="8.6727352"
|
||||||
|
y="16.687063"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">Objects table </tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="89.190445"
|
||||||
|
y="16.687063"
|
||||||
|
id="text852-3-5"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan850-67-3"
|
||||||
|
x="89.190445"
|
||||||
|
y="16.687063"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">Versions table </tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="174.55702"
|
||||||
|
y="16.687063"
|
||||||
|
id="text852-3-56"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan850-67-2"
|
||||||
|
x="174.55702"
|
||||||
|
y="16.687063"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">Blocks table</tspan></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 27 KiB |
BIN
doc/20201202_talk/img/rustacean-flat-happy.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
doc/20201202_talk/img/shh.jpg
Normal file
After Width: | Height: | Size: 86 KiB |
BIN
doc/20201202_talk/img/sync.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
doc/20201202_talk/talk.pdf
Normal file
247
doc/20201202_talk/talk.tex
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
%\nonstopmode
|
||||||
|
\documentclass[aspectratio=169]{beamer}
|
||||||
|
\usepackage[utf8]{inputenc}
|
||||||
|
% \usepackage[frenchb]{babel}
|
||||||
|
\usepackage{amsmath}
|
||||||
|
\usepackage{mathtools}
|
||||||
|
\usepackage{breqn}
|
||||||
|
\usepackage{multirow}
|
||||||
|
\usetheme{Luebeck}
|
||||||
|
\usepackage{graphicx}
|
||||||
|
%\useoutertheme[footline=authortitle,subsection=false]{miniframes}
|
||||||
|
|
||||||
|
\beamertemplatenavigationsymbolsempty
|
||||||
|
\setbeamertemplate{footline}
|
||||||
|
{%
|
||||||
|
\leavevmode%
|
||||||
|
\hbox{\begin{beamercolorbox}[wd=.15\paperwidth,ht=2.5ex,dp=1.125ex,leftskip=.3cm,rightskip=.3cm plus1fill]{author in head/foot}%
|
||||||
|
\usebeamerfont{author in head/foot} \insertframenumber{} / \inserttotalframenumber
|
||||||
|
\end{beamercolorbox}%
|
||||||
|
\begin{beamercolorbox}[wd=.2\paperwidth,ht=2.5ex,dp=1.125ex,leftskip=.3cm plus1fill,rightskip=.3cm]{author in head/foot}%
|
||||||
|
\usebeamerfont{author in head/foot}\insertshortauthor
|
||||||
|
\end{beamercolorbox}%
|
||||||
|
\begin{beamercolorbox}[wd=.65\paperwidth,ht=2.5ex,dp=1.125ex,leftskip=.3cm,rightskip=.3cm plus1fil]{title in head/foot}%
|
||||||
|
\usebeamerfont{title in head/foot}\insertshorttitle~--~\insertshortdate
|
||||||
|
\end{beamercolorbox}}%
|
||||||
|
\vskip0pt%
|
||||||
|
}
|
||||||
|
|
||||||
|
\usepackage{tabu}
|
||||||
|
\usepackage{multicol}
|
||||||
|
\usepackage{vwcol}
|
||||||
|
\usepackage{stmaryrd}
|
||||||
|
\usepackage{graphicx}
|
||||||
|
|
||||||
|
\usepackage[normalem]{ulem}
|
||||||
|
|
||||||
|
\title[Garage : jouer dans la cour des grands quand on est un hébergeur associatif]{Garage : jouer dans la cour des grands \\quand on est un hébergeur associatif}
|
||||||
|
\subtitle{(ou pourquoi on a décidé de réinventer la roue)}
|
||||||
|
\author[Q. Dufour \& A. Auvolat]{Quentin Dufour \& Alex Auvolat}
|
||||||
|
\date[02/12/2020]{Mercredi 2 décembre 2020}
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
|
||||||
|
\begin{frame}
|
||||||
|
\titlepage
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}
|
||||||
|
\frametitle{La question qui tue}
|
||||||
|
|
||||||
|
\begin{center}
|
||||||
|
\includegraphics[scale=3]{img/sync.png} \\
|
||||||
|
\Huge Pourquoi vous n'hébergez pas vos fichiers chez vous ? \\
|
||||||
|
\end{center}
|
||||||
|
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[t]
|
||||||
|
\frametitle{La cour des grands}
|
||||||
|
|
||||||
|
\begin{columns}[t]
|
||||||
|
\begin{column}{0.5\textwidth}
|
||||||
|
{\huge Le modèle du cloud...}
|
||||||
|
|
||||||
|
\begin{center}
|
||||||
|
\includegraphics[scale=0.08]{img/cloud.png}
|
||||||
|
\end{center}
|
||||||
|
|
||||||
|
+ \underline{intégrité} : plus de perte de données
|
||||||
|
|
||||||
|
+ \underline{disponibilité} : tout le temps accessible
|
||||||
|
|
||||||
|
+ \underline{service} : rien à gérer
|
||||||
|
|
||||||
|
\vspace{0.15cm}
|
||||||
|
\textbf{changement des comportements}
|
||||||
|
\end{column}
|
||||||
|
\pause
|
||||||
|
\begin{column}{0.5\textwidth}
|
||||||
|
{\huge ...et son prix}
|
||||||
|
|
||||||
|
\begin{center}
|
||||||
|
\includegraphics[scale=0.07]{img/dc.jpg}
|
||||||
|
\end{center}
|
||||||
|
|
||||||
|
- matériel couteux et polluant
|
||||||
|
|
||||||
|
- logiciels secrets
|
||||||
|
|
||||||
|
- gestion opaque
|
||||||
|
|
||||||
|
\vspace{0.2cm}
|
||||||
|
\textbf{prisonnier de l'écosystème}
|
||||||
|
\end{column}
|
||||||
|
\end{columns}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[t]
|
||||||
|
\frametitle{Garage l'imposteur}
|
||||||
|
|
||||||
|
\begin{columns}[t]
|
||||||
|
\begin{column}{0.5\textwidth}
|
||||||
|
{\huge Ressemble à du cloud...}
|
||||||
|
|
||||||
|
\begin{center}
|
||||||
|
\includegraphics[scale=0.5]{img/shh.jpg}
|
||||||
|
\end{center}
|
||||||
|
|
||||||
|
+ \underline{compatible} avec les apps existantes
|
||||||
|
|
||||||
|
+ \underline{fonctionne} avec le mobile
|
||||||
|
|
||||||
|
+ \underline{s'adapte} aux habitudes prises
|
||||||
|
|
||||||
|
|
||||||
|
\end{column}
|
||||||
|
|
||||||
|
\pause
|
||||||
|
\begin{column}{0.5\textwidth}
|
||||||
|
{\huge ...fait du P2P}
|
||||||
|
|
||||||
|
\begin{center}
|
||||||
|
\includegraphics[scale=1]{img/death.jpg}
|
||||||
|
\end{center}
|
||||||
|
|
||||||
|
\vspace{0.4cm}
|
||||||
|
|
||||||
|
+ \underline{contrôle} de l'infrastructure
|
||||||
|
|
||||||
|
+ \underline{transparent} code libre
|
||||||
|
|
||||||
|
+ \underline{sobre} fonctionne avec de vieilles machines à la maison
|
||||||
|
\end{column}
|
||||||
|
\end{columns}
|
||||||
|
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
|
||||||
|
\graphicspath{{img/}}
|
||||||
|
|
||||||
|
\begin{frame}
|
||||||
|
\frametitle{Mais donc, c'est quoi Garage ?}
|
||||||
|
|
||||||
|
\begin{columns}[t]
|
||||||
|
\begin{column}{0.5\textwidth}
|
||||||
|
\centering
|
||||||
|
\textbf{Un système de stockage distribué}
|
||||||
|
\vspace{1em}
|
||||||
|
|
||||||
|
\includegraphics[width=.7\columnwidth]{img/garage_distributed.pdf}
|
||||||
|
\end{column}
|
||||||
|
\pause
|
||||||
|
|
||||||
|
\begin{column}{0.5\textwidth}
|
||||||
|
\centering
|
||||||
|
\textbf{qui implémente l'API S3}
|
||||||
|
\vspace{2em}
|
||||||
|
|
||||||
|
\includegraphics[width=.7\columnwidth]{img/Amazon-S3.jpg}
|
||||||
|
\end{column}
|
||||||
|
\end{columns}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}
|
||||||
|
\frametitle{Consistent Hashing (DynamoDB)}
|
||||||
|
\textbf{Comment répartir les fichiers sur les différentes machines ?}
|
||||||
|
\vspace{1em}
|
||||||
|
|
||||||
|
\centering
|
||||||
|
|
||||||
|
\only<1>{\includegraphics[width=.55\columnwidth]{img/consistent_hashing_1.pdf}}%
|
||||||
|
\only<2>{\includegraphics[width=.55\columnwidth]{img/consistent_hashing_2.pdf}}%
|
||||||
|
\only<3>{\includegraphics[width=.55\columnwidth]{img/consistent_hashing_3.pdf}}%
|
||||||
|
\only<4>{\includegraphics[width=.55\columnwidth]{img/consistent_hashing_4.pdf}}%
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}
|
||||||
|
\frametitle{Garage Internals : 3 niveaux de consistent hashing}
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=.85\columnwidth]{img/garage_tables.pdf}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}
|
||||||
|
\frametitle{Modèles de cohérence}
|
||||||
|
Garage utilise un modèle de cohérence relativement faible :
|
||||||
|
\vspace{1em}
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item Objets répliqués 3 fois, quorum de 2 pour les lectures et les écritures\\
|
||||||
|
$\to$ cohérence \textbf{``read your writes''}
|
||||||
|
\vspace{1em}
|
||||||
|
\item<2-> Types de donnée CRDT + mécanisme d'anti-entropie\\
|
||||||
|
$\to$ cohérence \textbf{à terme} (eventual consistency)
|
||||||
|
\vspace{1em}
|
||||||
|
\item<3-> Cela s'applique pour chaque fichier individuellement :\\
|
||||||
|
pas de linéarisabilté ou de cohérence causale entre les opérations\\
|
||||||
|
sur des fichiers différents
|
||||||
|
\vspace{1em}
|
||||||
|
\item<4-> \textbf{Avantage :} convient bien à un déploiement géodistribué (multi-datacenter)
|
||||||
|
\end{itemize}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}
|
||||||
|
\frametitle{Rust : retour d'expérience}
|
||||||
|
|
||||||
|
\begin{columns}
|
||||||
|
\begin{column}{0.55\textwidth}
|
||||||
|
Garage est entièrement écrit en Rust !
|
||||||
|
\vspace{2em}
|
||||||
|
|
||||||
|
\textbf{Points forts :}
|
||||||
|
\vspace{.5em}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Langage compilé, très rapide
|
||||||
|
\vspace{.5em}
|
||||||
|
\item Typage fort, beaucoup de sécurités
|
||||||
|
\vspace{.5em}
|
||||||
|
\item Le meilleur de plusieurs paradigmes:
|
||||||
|
fonctionnel, orienté objet, impératif
|
||||||
|
\vspace{.5em}
|
||||||
|
\item Un écosytème de librairies très complet:
|
||||||
|
serialisation, async/await, http, ...
|
||||||
|
\end{itemize}
|
||||||
|
\end{column}
|
||||||
|
|
||||||
|
\begin{column}{0.45\textwidth}
|
||||||
|
\begin{centering}
|
||||||
|
\hspace{2em}\includegraphics[width=0.55\columnwidth]{img/rustacean-flat-happy.png}
|
||||||
|
\end{centering}
|
||||||
|
|
||||||
|
\vspace{2em}
|
||||||
|
\textbf{Points faibles :}
|
||||||
|
\vspace{.5em}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Les temps de compilation...
|
||||||
|
\vspace{.5em}
|
||||||
|
\item Compliqué à apprendre
|
||||||
|
\end{itemize}
|
||||||
|
\vspace{2em}
|
||||||
|
\end{column}
|
||||||
|
\end{columns}
|
||||||
|
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\end{document}
|
||||||
|
|
||||||
|
%% vim: set ts=4 sw=4 tw=0 noet spelllang=fr :
|
@ -6,11 +6,11 @@ GARAGE_DEBUG="${REPO_FOLDER}/target/debug/"
|
|||||||
GARAGE_RELEASE="${REPO_FOLDER}/target/release/"
|
GARAGE_RELEASE="${REPO_FOLDER}/target/release/"
|
||||||
PATH="${GARAGE_DEBUG}:${GARAGE_RELEASE}:$PATH"
|
PATH="${GARAGE_DEBUG}:${GARAGE_RELEASE}:$PATH"
|
||||||
|
|
||||||
garage bucket create éprouvette
|
garage bucket create eprouvette
|
||||||
KEY_INFO=`garage key new --name opérateur`
|
KEY_INFO=`garage key new --name opérateur`
|
||||||
ACCESS_KEY=`echo $KEY_INFO|grep -Po 'GK[a-f0-9]+'`
|
ACCESS_KEY=`echo $KEY_INFO|grep -Po 'GK[a-f0-9]+'`
|
||||||
SECRET_KEY=`echo $KEY_INFO|grep -Po 'secret_key: "[a-f0-9]+'|grep -Po '[a-f0-9]+$'`
|
SECRET_KEY=`echo $KEY_INFO|grep -Po 'secret_key: "[a-f0-9]+'|grep -Po '[a-f0-9]+$'`
|
||||||
garage bucket allow éprouvette --read --write --key $ACCESS_KEY
|
garage bucket allow eprouvette --read --write --key $ACCESS_KEY
|
||||||
echo "$ACCESS_KEY $SECRET_KEY" > /tmp/garage.s3
|
echo "$ACCESS_KEY $SECRET_KEY" > /tmp/garage.s3
|
||||||
|
|
||||||
echo "Bucket s3://éprouvette created. Credentials stored in /tmp/garage.s3."
|
echo "Bucket s3://eprouvette created. Credentials stored in /tmp/garage.s3."
|
||||||
|
7
script/dev-clean.sh
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
killall -9 garage || echo "garage is not running"
|
||||||
|
rm -rf /tmp/garage*
|
||||||
|
rm -rf /tmp/config.*.toml
|
@ -24,11 +24,11 @@ cat > $CONF_PATH <<EOF
|
|||||||
block_size = 1048576 # objects are split in blocks of maximum this number of bytes
|
block_size = 1048576 # objects are split in blocks of maximum this number of bytes
|
||||||
metadata_dir = "/tmp/garage-meta-$count"
|
metadata_dir = "/tmp/garage-meta-$count"
|
||||||
data_dir = "/tmp/garage-data-$count"
|
data_dir = "/tmp/garage-data-$count"
|
||||||
rpc_bind_addr = "127.0.0.$count:3901" # the port other Garage nodes will use to talk to this node
|
rpc_bind_addr = "[::]:$((3900+$count))" # the port other Garage nodes will use to talk to this node
|
||||||
bootstrap_peers = [
|
bootstrap_peers = [
|
||||||
"127.0.0.1:3901",
|
"[::1]:3901",
|
||||||
"127.0.0.2:3901",
|
"[::1]:3902",
|
||||||
"127.0.0.3:3901"
|
"[::1]:3903"
|
||||||
]
|
]
|
||||||
max_concurrent_rpc_requests = 12
|
max_concurrent_rpc_requests = 12
|
||||||
data_replication_factor = 3
|
data_replication_factor = 3
|
||||||
@ -36,11 +36,11 @@ meta_replication_factor = 3
|
|||||||
meta_epidemic_fanout = 3
|
meta_epidemic_fanout = 3
|
||||||
|
|
||||||
[s3_api]
|
[s3_api]
|
||||||
api_bind_addr = "127.0.0.$count:3900" # the S3 API port, HTTP without TLS. Add a reverse proxy for the TLS part.
|
api_bind_addr = "[::]:$((3910+$count))" # the S3 API port, HTTP without TLS. Add a reverse proxy for the TLS part.
|
||||||
s3_region = "garage" # set this to anything. S3 API calls will fail if they are not made against the region set here.
|
s3_region = "garage" # set this to anything. S3 API calls will fail if they are not made against the region set here.
|
||||||
|
|
||||||
[s3_web]
|
[s3_web]
|
||||||
bind_addr = "127.0.0.$count:3902"
|
bind_addr = "[::]:$((3920+$count))"
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo -en "$LABEL configuration written to $CONF_PATH\n"
|
echo -en "$LABEL configuration written to $CONF_PATH\n"
|
||||||
|
@ -6,6 +6,11 @@ GARAGE_DEBUG="${REPO_FOLDER}/target/debug/"
|
|||||||
GARAGE_RELEASE="${REPO_FOLDER}/target/release/"
|
GARAGE_RELEASE="${REPO_FOLDER}/target/release/"
|
||||||
PATH="${GARAGE_DEBUG}:${GARAGE_RELEASE}:$PATH"
|
PATH="${GARAGE_DEBUG}:${GARAGE_RELEASE}:$PATH"
|
||||||
|
|
||||||
|
until garage status 2>&1|grep -q Healthy ; do
|
||||||
|
echo "cluster starting..."
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
garage status \
|
garage status \
|
||||||
| grep UNCONFIGURED \
|
| grep UNCONFIGURED \
|
||||||
| grep -Po '^[0-9a-f]+' \
|
| grep -Po '^[0-9a-f]+' \
|
||||||
|
14
script/dev-env-aws.sh
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SCRIPT_FOLDER="`dirname \"${BASH_SOURCE[0]}\"`"
|
||||||
|
REPO_FOLDER="${SCRIPT_FOLDER}/../"
|
||||||
|
GARAGE_DEBUG="${REPO_FOLDER}/target/debug/"
|
||||||
|
GARAGE_RELEASE="${REPO_FOLDER}/target/release/"
|
||||||
|
PATH="${GARAGE_DEBUG}:${GARAGE_RELEASE}:$PATH"
|
||||||
|
|
||||||
|
export AWS_ACCESS_KEY_ID=`cat /tmp/garage.s3 |cut -d' ' -f1`
|
||||||
|
export AWS_SECRET_ACCESS_KEY=`cat /tmp/garage.s3 |cut -d' ' -f2`
|
||||||
|
export AWS_DEFAULT_REGION='garage'
|
||||||
|
|
||||||
|
alias awsgrg="aws s3 \
|
||||||
|
--endpoint-url http://127.0.0.1:3911"
|
3
script/dev-env.sh → script/dev-env-s3cmd.sh
Executable file → Normal file
@ -10,7 +10,8 @@ ACCESS_KEY=`cat /tmp/garage.s3 |cut -d' ' -f1`
|
|||||||
SECRET_KEY=`cat /tmp/garage.s3 |cut -d' ' -f2`
|
SECRET_KEY=`cat /tmp/garage.s3 |cut -d' ' -f2`
|
||||||
|
|
||||||
alias s3grg="s3cmd \
|
alias s3grg="s3cmd \
|
||||||
--host 127.0.0.1:3900 \
|
--host 127.0.0.1:3911 \
|
||||||
|
--host-bucket 127.0.0.1:3911 \
|
||||||
--access_key=$ACCESS_KEY \
|
--access_key=$ACCESS_KEY \
|
||||||
--secret_key=$SECRET_KEY \
|
--secret_key=$SECRET_KEY \
|
||||||
--region=garage \
|
--region=garage \
|
62
script/test-smoke.sh
Executable file
@ -0,0 +1,62 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
shopt -s expand_aliases
|
||||||
|
|
||||||
|
SCRIPT_FOLDER="`dirname \"$0\"`"
|
||||||
|
REPO_FOLDER="${SCRIPT_FOLDER}/../"
|
||||||
|
|
||||||
|
cargo build
|
||||||
|
${SCRIPT_FOLDER}/dev-clean.sh
|
||||||
|
${SCRIPT_FOLDER}/dev-cluster.sh > /tmp/garage.log 2>&1 &
|
||||||
|
${SCRIPT_FOLDER}/dev-configure.sh
|
||||||
|
${SCRIPT_FOLDER}/dev-bucket.sh
|
||||||
|
source ${SCRIPT_FOLDER}/dev-env-aws.sh
|
||||||
|
source ${SCRIPT_FOLDER}/dev-env-s3cmd.sh
|
||||||
|
|
||||||
|
garage status
|
||||||
|
garage key list
|
||||||
|
garage bucket list
|
||||||
|
|
||||||
|
dd if=/dev/urandom of=/tmp/garage.1.rnd bs=1k count=2 # < INLINE_THRESHOLD = 3072 bytes
|
||||||
|
dd if=/dev/urandom of=/tmp/garage.2.rnd bs=1M count=5
|
||||||
|
dd if=/dev/urandom of=/tmp/garage.3.rnd bs=1M count=10
|
||||||
|
|
||||||
|
for idx in $(seq 1 3); do
|
||||||
|
# AWS sends
|
||||||
|
awsgrg cp /tmp/garage.$idx.rnd s3://eprouvette/garage.$idx.aws
|
||||||
|
|
||||||
|
awsgrg ls s3://eprouvette
|
||||||
|
|
||||||
|
awsgrg cp s3://eprouvette/garage.$idx.aws /tmp/garage.$idx.dl
|
||||||
|
diff /tmp/garage.$idx.rnd /tmp/garage.$idx.dl
|
||||||
|
rm /tmp/garage.$idx.dl
|
||||||
|
|
||||||
|
s3grg get s3://eprouvette/garage.$idx.aws /tmp/garage.$idx.dl
|
||||||
|
diff /tmp/garage.$idx.rnd /tmp/garage.$idx.dl
|
||||||
|
rm /tmp/garage.$idx.dl
|
||||||
|
|
||||||
|
awsgrg rm s3://eprouvette/garage.$idx.aws
|
||||||
|
|
||||||
|
# S3CMD sends
|
||||||
|
s3grg put /tmp/garage.$idx.rnd s3://eprouvette/garage.$idx.s3cmd
|
||||||
|
|
||||||
|
s3grg ls s3://eprouvette
|
||||||
|
|
||||||
|
s3grg get s3://eprouvette/garage.$idx.s3cmd /tmp/garage.$idx.dl
|
||||||
|
diff /tmp/garage.$idx.rnd /tmp/garage.$idx.dl
|
||||||
|
rm /tmp/garage.$idx.dl
|
||||||
|
|
||||||
|
awsgrg cp s3://eprouvette/garage.$idx.s3cmd /tmp/garage.$idx.dl
|
||||||
|
diff /tmp/garage.$idx.rnd /tmp/garage.$idx.dl
|
||||||
|
rm /tmp/garage.$idx.dl
|
||||||
|
|
||||||
|
s3grg rm s3://eprouvette/garage.$idx.s3cmd
|
||||||
|
done
|
||||||
|
rm /tmp/garage.{1,2,3}.rnd
|
||||||
|
|
||||||
|
garage bucket deny --read --write eprouvette --key $AWS_ACCESS_KEY_ID
|
||||||
|
garage bucket delete --yes eprouvette
|
||||||
|
garage key delete --yes $AWS_ACCESS_KEY_ID
|
||||||
|
|
||||||
|
echo "success"
|
@ -27,6 +27,7 @@ md-5 = "0.9.1"
|
|||||||
sha2 = "0.8"
|
sha2 = "0.8"
|
||||||
hmac = "0.7"
|
hmac = "0.7"
|
||||||
crypto-mac = "0.7"
|
crypto-mac = "0.7"
|
||||||
|
rand = "0.7"
|
||||||
|
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
futures-util = "0.3"
|
futures-util = "0.3"
|
||||||
|
@ -24,11 +24,13 @@ fn object_headers(
|
|||||||
"Content-Type",
|
"Content-Type",
|
||||||
version_meta.headers.content_type.to_string(),
|
version_meta.headers.content_type.to_string(),
|
||||||
)
|
)
|
||||||
.header("Content-Length", format!("{}", version_meta.size))
|
|
||||||
.header("ETag", version_meta.etag.to_string())
|
|
||||||
.header("Last-Modified", date_str)
|
.header("Last-Modified", date_str)
|
||||||
.header("Accept-Ranges", format!("bytes"));
|
.header("Accept-Ranges", format!("bytes"));
|
||||||
|
|
||||||
|
if !version_meta.etag.is_empty() {
|
||||||
|
resp = resp.header("ETag", format!("\"{}\"", version_meta.etag));
|
||||||
|
}
|
||||||
|
|
||||||
for (k, v) in version_meta.headers.other.iter() {
|
for (k, v) in version_meta.headers.other.iter() {
|
||||||
resp = resp.header(k, v.to_string());
|
resp = resp.header(k, v.to_string());
|
||||||
}
|
}
|
||||||
@ -63,6 +65,7 @@ pub async fn handle_head(
|
|||||||
|
|
||||||
let body: Body = Body::from(vec![]);
|
let body: Body = Body::from(vec![]);
|
||||||
let response = object_headers(&version, version_meta)
|
let response = object_headers(&version, version_meta)
|
||||||
|
.header("Content-Length", format!("{}", version_meta.size))
|
||||||
.status(StatusCode::OK)
|
.status(StatusCode::OK)
|
||||||
.body(body)
|
.body(body)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -123,7 +126,9 @@ pub async fn handle_get(
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
let resp_builder = object_headers(&last_v, last_v_meta).status(StatusCode::OK);
|
let resp_builder = object_headers(&last_v, last_v_meta)
|
||||||
|
.header("Content-Length", format!("{}", last_v_meta.size))
|
||||||
|
.status(StatusCode::OK);
|
||||||
|
|
||||||
match &last_v_data {
|
match &last_v_data {
|
||||||
ObjectVersionData::DeleteMarker => unreachable!(),
|
ObjectVersionData::DeleteMarker => unreachable!(),
|
||||||
@ -161,7 +166,7 @@ pub async fn handle_get(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.buffered(2);
|
.buffered(2);
|
||||||
//let body: Body = Box::new(StreamBody::new(Box::pin(body_stream)));
|
|
||||||
let body = hyper::body::Body::wrap_stream(body_stream);
|
let body = hyper::body::Body::wrap_stream(body_stream);
|
||||||
Ok(resp_builder.body(body)?)
|
Ok(resp_builder.body(body)?)
|
||||||
}
|
}
|
||||||
@ -181,9 +186,10 @@ pub async fn handle_get_range(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let resp_builder = object_headers(version, version_meta)
|
let resp_builder = object_headers(version, version_meta)
|
||||||
|
.header("Content-Length", format!("{}", end - begin))
|
||||||
.header(
|
.header(
|
||||||
"Content-Range",
|
"Content-Range",
|
||||||
format!("bytes {}-{}/{}", begin, end, version_meta.size),
|
format!("bytes {}-{}/{}", begin, end - 1, version_meta.size),
|
||||||
)
|
)
|
||||||
.status(StatusCode::PARTIAL_CONTENT);
|
.status(StatusCode::PARTIAL_CONTENT);
|
||||||
|
|
||||||
@ -206,35 +212,49 @@ pub async fn handle_get_range(
|
|||||||
None => return Err(Error::NotFound),
|
None => return Err(Error::NotFound),
|
||||||
};
|
};
|
||||||
|
|
||||||
let blocks = version
|
// We will store here the list of blocks that have an intersection with the requested
|
||||||
.blocks()
|
// range, as well as their "true offset", which is their actual offset in the complete
|
||||||
.iter()
|
// file (whereas block.offset designates the offset of the block WITHIN THE PART
|
||||||
.cloned()
|
// block.part_number, which is not the same in the case of a multipart upload)
|
||||||
.filter(|block| block.offset + block.size > begin && block.offset < end)
|
let mut blocks = Vec::with_capacity(std::cmp::min(
|
||||||
.collect::<Vec<_>>();
|
version.blocks().len(),
|
||||||
|
4 + ((end - begin) / std::cmp::max(version.blocks()[0].size as u64, 1024)) as usize,
|
||||||
|
));
|
||||||
|
let mut true_offset = 0;
|
||||||
|
for b in version.blocks().iter() {
|
||||||
|
if true_offset >= end {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Keep only blocks that have an intersection with the requested range
|
||||||
|
if true_offset < end && true_offset + b.size > begin {
|
||||||
|
blocks.push((b.clone(), true_offset));
|
||||||
|
}
|
||||||
|
true_offset += b.size;
|
||||||
|
}
|
||||||
|
|
||||||
let body_stream = futures::stream::iter(blocks)
|
let body_stream = futures::stream::iter(blocks)
|
||||||
.map(move |block| {
|
.map(move |(block, true_offset)| {
|
||||||
let garage = garage.clone();
|
let garage = garage.clone();
|
||||||
async move {
|
async move {
|
||||||
let data = garage.block_manager.rpc_get_block(&block.hash).await?;
|
let data = garage.block_manager.rpc_get_block(&block.hash).await?;
|
||||||
let start_in_block = if block.offset > begin {
|
let data = Bytes::from(data);
|
||||||
|
let start_in_block = if true_offset > begin {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
begin - block.offset
|
begin - true_offset
|
||||||
};
|
};
|
||||||
let end_in_block = if block.offset + block.size < end {
|
let end_in_block = if true_offset + block.size < end {
|
||||||
block.size
|
block.size
|
||||||
} else {
|
} else {
|
||||||
end - block.offset
|
end - true_offset
|
||||||
};
|
};
|
||||||
Result::<Bytes, Error>::Ok(Bytes::from(
|
Result::<Bytes, Error>::Ok(Bytes::from(
|
||||||
data[start_in_block as usize..end_in_block as usize].to_vec(),
|
data.slice(start_in_block as usize..end_in_block as usize),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.buffered(2);
|
.buffered(2);
|
||||||
//let body: Body = Box::new(StreamBody::new(Box::pin(body_stream)));
|
|
||||||
let body = hyper::body::Body::wrap_stream(body_stream);
|
let body = hyper::body::Body::wrap_stream(body_stream);
|
||||||
Ok(resp_builder.body(body)?)
|
Ok(resp_builder.body(body)?)
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ use crate::encoding::*;
|
|||||||
struct ListResultInfo {
|
struct ListResultInfo {
|
||||||
last_modified: u64,
|
last_modified: u64,
|
||||||
size: u64,
|
size: u64,
|
||||||
|
etag: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_list(
|
pub async fn handle_list(
|
||||||
@ -56,12 +57,12 @@ pub async fn handle_list(
|
|||||||
|
|
||||||
for object in objects.iter() {
|
for object in objects.iter() {
|
||||||
if !object.key.starts_with(prefix) {
|
if !object.key.starts_with(prefix) {
|
||||||
truncated = false;
|
truncated = None;
|
||||||
break 'query_loop;
|
break 'query_loop;
|
||||||
}
|
}
|
||||||
if let Some(version) = object.versions().iter().find(|x| x.is_data()) {
|
if let Some(version) = object.versions().iter().find(|x| x.is_data()) {
|
||||||
if result_keys.len() + result_common_prefixes.len() >= max_keys {
|
if result_keys.len() + result_common_prefixes.len() >= max_keys {
|
||||||
truncated = true;
|
truncated = Some(object.key.to_string());
|
||||||
break 'query_loop;
|
break 'query_loop;
|
||||||
}
|
}
|
||||||
let common_prefix = if delimiter.len() > 0 {
|
let common_prefix = if delimiter.len() > 0 {
|
||||||
@ -75,19 +76,18 @@ pub async fn handle_list(
|
|||||||
if let Some(pfx) = common_prefix {
|
if let Some(pfx) = common_prefix {
|
||||||
result_common_prefixes.insert(pfx.to_string());
|
result_common_prefixes.insert(pfx.to_string());
|
||||||
} else {
|
} else {
|
||||||
let size = match &version.state {
|
let meta = match &version.state {
|
||||||
ObjectVersionState::Complete(ObjectVersionData::Inline(meta, _)) => {
|
ObjectVersionState::Complete(ObjectVersionData::Inline(meta, _)) => meta,
|
||||||
meta.size
|
|
||||||
}
|
|
||||||
ObjectVersionState::Complete(ObjectVersionData::FirstBlock(meta, _)) => {
|
ObjectVersionState::Complete(ObjectVersionData::FirstBlock(meta, _)) => {
|
||||||
meta.size
|
meta
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
let info = match result_keys.get(&object.key) {
|
let info = match result_keys.get(&object.key) {
|
||||||
None => ListResultInfo {
|
None => ListResultInfo {
|
||||||
last_modified: version.timestamp,
|
last_modified: version.timestamp,
|
||||||
size,
|
size: meta.size,
|
||||||
|
etag: meta.etag.to_string(),
|
||||||
},
|
},
|
||||||
Some(_lri) => {
|
Some(_lri) => {
|
||||||
return Err(Error::Message(format!("Duplicate key?? {}", object.key)))
|
return Err(Error::Message(format!("Duplicate key?? {}", object.key)))
|
||||||
@ -98,7 +98,7 @@ pub async fn handle_list(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if objects.len() < max_keys + 1 {
|
if objects.len() < max_keys + 1 {
|
||||||
truncated = false;
|
truncated = None;
|
||||||
break 'query_loop;
|
break 'query_loop;
|
||||||
}
|
}
|
||||||
if objects.len() > 0 {
|
if objects.len() > 0 {
|
||||||
@ -113,11 +113,22 @@ pub async fn handle_list(
|
|||||||
r#"<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">"#
|
r#"<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">"#
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
writeln!(&mut xml, "\t<Bucket>{}</Bucket>", bucket).unwrap();
|
writeln!(&mut xml, "\t<Name>{}</Name>", bucket).unwrap();
|
||||||
writeln!(&mut xml, "\t<Prefix>{}</Prefix>", prefix).unwrap();
|
writeln!(&mut xml, "\t<Prefix>{}</Prefix>", prefix).unwrap();
|
||||||
|
if let Some(mkr) = marker {
|
||||||
|
writeln!(&mut xml, "\t<Marker>{}</Marker>", mkr).unwrap();
|
||||||
|
}
|
||||||
writeln!(&mut xml, "\t<KeyCount>{}</KeyCount>", result_keys.len()).unwrap();
|
writeln!(&mut xml, "\t<KeyCount>{}</KeyCount>", result_keys.len()).unwrap();
|
||||||
writeln!(&mut xml, "\t<MaxKeys>{}</MaxKeys>", max_keys).unwrap();
|
writeln!(&mut xml, "\t<MaxKeys>{}</MaxKeys>", max_keys).unwrap();
|
||||||
writeln!(&mut xml, "\t<IsTruncated>{}</IsTruncated>", truncated).unwrap();
|
writeln!(
|
||||||
|
&mut xml,
|
||||||
|
"\t<IsTruncated>{}</IsTruncated>",
|
||||||
|
truncated.is_some()
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
if let Some(next_marker) = truncated {
|
||||||
|
writeln!(&mut xml, "\t<NextMarker>{}</NextMarker>", next_marker).unwrap();
|
||||||
|
}
|
||||||
for (key, info) in result_keys.iter() {
|
for (key, info) in result_keys.iter() {
|
||||||
let last_modif = NaiveDateTime::from_timestamp(info.last_modified as i64 / 1000, 0);
|
let last_modif = NaiveDateTime::from_timestamp(info.last_modified as i64 / 1000, 0);
|
||||||
let last_modif = DateTime::<Utc>::from_utc(last_modif, Utc);
|
let last_modif = DateTime::<Utc>::from_utc(last_modif, Utc);
|
||||||
@ -132,6 +143,9 @@ pub async fn handle_list(
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
writeln!(&mut xml, "\t\t<LastModified>{}</LastModified>", last_modif).unwrap();
|
writeln!(&mut xml, "\t\t<LastModified>{}</LastModified>", last_modif).unwrap();
|
||||||
writeln!(&mut xml, "\t\t<Size>{}</Size>", info.size).unwrap();
|
writeln!(&mut xml, "\t\t<Size>{}</Size>", info.size).unwrap();
|
||||||
|
if !info.etag.is_empty() {
|
||||||
|
writeln!(&mut xml, "\t\t<ETag>\"{}\"</ETag>", info.etag).unwrap();
|
||||||
|
}
|
||||||
writeln!(&mut xml, "\t\t<StorageClass>STANDARD</StorageClass>").unwrap();
|
writeln!(&mut xml, "\t\t<StorageClass>STANDARD</StorageClass>").unwrap();
|
||||||
writeln!(&mut xml, "\t</Contents>").unwrap();
|
writeln!(&mut xml, "\t</Contents>").unwrap();
|
||||||
}
|
}
|
||||||
|
@ -51,12 +51,7 @@ pub async fn handle_put(
|
|||||||
let md5sum_arr = md5sum.finalize();
|
let md5sum_arr = md5sum.finalize();
|
||||||
let md5sum_hex = hex::encode(md5sum_arr);
|
let md5sum_hex = hex::encode(md5sum_arr);
|
||||||
|
|
||||||
let mut sha256sum = Sha256::new();
|
let sha256sum_hash = hash(&first_block[..]);
|
||||||
sha256sum.input(&first_block[..]);
|
|
||||||
let sha256sum_arr = sha256sum.result();
|
|
||||||
let mut hash = [0u8; 32];
|
|
||||||
hash.copy_from_slice(&sha256sum_arr[..]);
|
|
||||||
let sha256sum_hash = Hash::from(hash);
|
|
||||||
|
|
||||||
ensure_checksum_matches(
|
ensure_checksum_matches(
|
||||||
md5sum_arr.as_slice(),
|
md5sum_arr.as_slice(),
|
||||||
@ -253,7 +248,7 @@ impl BodyChunker {
|
|||||||
body,
|
body,
|
||||||
read_all: false,
|
read_all: false,
|
||||||
block_size,
|
block_size,
|
||||||
buf: VecDeque::new(),
|
buf: VecDeque::with_capacity(2 * block_size),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async fn next(&mut self) -> Result<Option<Vec<u8>>, GarageError> {
|
async fn next(&mut self) -> Result<Option<Vec<u8>>, GarageError> {
|
||||||
@ -278,11 +273,10 @@ impl BodyChunker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put_response(version_uuid: UUID, etag: String) -> Response<Body> {
|
pub fn put_response(version_uuid: UUID, md5sum_hex: String) -> Response<Body> {
|
||||||
Response::builder()
|
Response::builder()
|
||||||
.header("x-amz-version-id", hex::encode(version_uuid))
|
.header("x-amz-version-id", hex::encode(version_uuid))
|
||||||
.header("ETag", etag)
|
.header("ETag", format!("\"{}\"", md5sum_hex))
|
||||||
// TODO ETag
|
|
||||||
.body(Body::from(vec![]))
|
.body(Body::from(vec![]))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
@ -369,7 +363,7 @@ pub async fn handle_put_part(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Copy block to store
|
// Copy block to store
|
||||||
let version = Version::new(version_uuid, bucket.into(), key.into(), false, vec![]);
|
let version = Version::new(version_uuid, bucket, key, false, vec![]);
|
||||||
let first_block_hash = hash(&first_block[..]);
|
let first_block_hash = hash(&first_block[..]);
|
||||||
let (_, md5sum_arr, sha256sum) = read_and_put_blocks(
|
let (_, md5sum_arr, sha256sum) = read_and_put_blocks(
|
||||||
&garage,
|
&garage,
|
||||||
@ -388,7 +382,11 @@ pub async fn handle_put_part(
|
|||||||
content_sha256,
|
content_sha256,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(Response::new(Body::from(vec![])))
|
let response = Response::builder()
|
||||||
|
.header("ETag", format!("\"{}\"", hex::encode(md5sum_arr)))
|
||||||
|
.body(Body::from(vec![]))
|
||||||
|
.unwrap();
|
||||||
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_complete_multipart_upload(
|
pub async fn handle_complete_multipart_upload(
|
||||||
@ -430,6 +428,21 @@ pub async fn handle_complete_multipart_upload(
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ETag calculation: we produce ETags that have the same form as
|
||||||
|
// those of S3 multipart uploads, but we don't use their actual
|
||||||
|
// calculation for the first part (we use random bytes). This
|
||||||
|
// shouldn't impact compatibility as the S3 docs specify that
|
||||||
|
// the ETag is an opaque value in case of a multipart upload.
|
||||||
|
// See also: https://teppen.io/2018/06/23/aws_s3_etags/
|
||||||
|
let num_parts = version.blocks().last().unwrap().part_number
|
||||||
|
- version.blocks().first().unwrap().part_number
|
||||||
|
+ 1;
|
||||||
|
let etag = format!(
|
||||||
|
"{}-{}",
|
||||||
|
hex::encode(&rand::random::<[u8; 16]>()[..]),
|
||||||
|
num_parts
|
||||||
|
);
|
||||||
|
|
||||||
// TODO: check that all the parts that they pretend they gave us are indeed there
|
// TODO: check that all the parts that they pretend they gave us are indeed there
|
||||||
// TODO: when we read the XML from _req, remember to check the sha256 sum of the payload
|
// TODO: when we read the XML from _req, remember to check the sha256 sum of the payload
|
||||||
// against the signed x-amz-content-sha256
|
// against the signed x-amz-content-sha256
|
||||||
@ -444,7 +457,7 @@ pub async fn handle_complete_multipart_upload(
|
|||||||
ObjectVersionMeta {
|
ObjectVersionMeta {
|
||||||
headers,
|
headers,
|
||||||
size: total_size,
|
size: total_size,
|
||||||
etag: "".to_string(), // TODO
|
etag: etag,
|
||||||
},
|
},
|
||||||
version.blocks()[0].hash,
|
version.blocks()[0].hash,
|
||||||
));
|
));
|
||||||
|
@ -391,7 +391,8 @@ where
|
|||||||
let (old_entry, new_entry) = self.store.transaction(|db| {
|
let (old_entry, new_entry) = self.store.transaction(|db| {
|
||||||
let (old_entry, new_entry) = match db.get(&tree_key)? {
|
let (old_entry, new_entry) = match db.get(&tree_key)? {
|
||||||
Some(prev_bytes) => {
|
Some(prev_bytes) => {
|
||||||
let old_entry = self.decode_entry(&prev_bytes)
|
let old_entry = self
|
||||||
|
.decode_entry(&prev_bytes)
|
||||||
.map_err(sled::ConflictableTransactionError::Abort)?;
|
.map_err(sled::ConflictableTransactionError::Abort)?;
|
||||||
let mut new_entry = old_entry.clone();
|
let mut new_entry = old_entry.clone();
|
||||||
new_entry.merge(&update);
|
new_entry.merge(&update);
|
||||||
|