s3duct 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. s3duct-0.3.0/.github/workflows/ci.yml +29 -0
  2. s3duct-0.3.0/.github/workflows/integration.yml +373 -0
  3. s3duct-0.3.0/.github/workflows/minio.yml +66 -0
  4. s3duct-0.3.0/.gitignore +7 -0
  5. s3duct-0.3.0/CLA.md +64 -0
  6. s3duct-0.3.0/LICENSE +93 -0
  7. s3duct-0.3.0/PKG-INFO +411 -0
  8. s3duct-0.3.0/README.md +377 -0
  9. s3duct-0.3.0/pyproject.toml +55 -0
  10. s3duct-0.3.0/s3duct/__init__.py +3 -0
  11. s3duct-0.3.0/s3duct/backends/__init__.py +0 -0
  12. s3duct-0.3.0/s3duct/backends/base.py +62 -0
  13. s3duct-0.3.0/s3duct/backends/local.py +72 -0
  14. s3duct-0.3.0/s3duct/backends/s3.py +150 -0
  15. s3duct-0.3.0/s3duct/backpressure.py +86 -0
  16. s3duct-0.3.0/s3duct/chunker.py +114 -0
  17. s3duct-0.3.0/s3duct/cli.py +256 -0
  18. s3duct-0.3.0/s3duct/config.py +20 -0
  19. s3duct-0.3.0/s3duct/downloader.py +273 -0
  20. s3duct-0.3.0/s3duct/encryption.py +153 -0
  21. s3duct-0.3.0/s3duct/integrity.py +86 -0
  22. s3duct-0.3.0/s3duct/manifest.py +82 -0
  23. s3duct-0.3.0/s3duct/py.typed +0 -0
  24. s3duct-0.3.0/s3duct/resume.py +148 -0
  25. s3duct-0.3.0/s3duct/thaw.py +13 -0
  26. s3duct-0.3.0/s3duct/uploader.py +560 -0
  27. s3duct-0.3.0/s3duct.egg-info/PKG-INFO +411 -0
  28. s3duct-0.3.0/s3duct.egg-info/SOURCES.txt +54 -0
  29. s3duct-0.3.0/s3duct.egg-info/dependency_links.txt +1 -0
  30. s3duct-0.3.0/s3duct.egg-info/entry_points.txt +2 -0
  31. s3duct-0.3.0/s3duct.egg-info/requires.txt +8 -0
  32. s3duct-0.3.0/s3duct.egg-info/top_level.txt +1 -0
  33. s3duct-0.3.0/setup.cfg +4 -0
  34. s3duct-0.3.0/tests/__init__.py +0 -0
  35. s3duct-0.3.0/tests/conftest.py +48 -0
  36. s3duct-0.3.0/tests/fixtures/compat/v0.1.0/chunks/chunk-000000 +1 -0
  37. s3duct-0.3.0/tests/fixtures/compat/v0.1.0/chunks/chunk-000001 +1 -0
  38. s3duct-0.3.0/tests/fixtures/compat/v0.1.0/chunks/chunk-000002 +1 -0
  39. s3duct-0.3.0/tests/fixtures/compat/v0.1.0/expected_sha256 +1 -0
  40. s3duct-0.3.0/tests/fixtures/compat/v0.1.0/manifest.json +46 -0
  41. s3duct-0.3.0/tests/fixtures/compat/v0.1.0/metadata.json +8 -0
  42. s3duct-0.3.0/tests/integration/chaos.sh +396 -0
  43. s3duct-0.3.0/tests/integration/docker-compose.chaos.yml +37 -0
  44. s3duct-0.3.0/tests/integration/roundtrip.sh +153 -0
  45. s3duct-0.3.0/tests/test_backpressure.py +124 -0
  46. s3duct-0.3.0/tests/test_chunker.py +102 -0
  47. s3duct-0.3.0/tests/test_cli.py +218 -0
  48. s3duct-0.3.0/tests/test_downloader.py +568 -0
  49. s3duct-0.3.0/tests/test_encryption.py +215 -0
  50. s3duct-0.3.0/tests/test_integrity.py +111 -0
  51. s3duct-0.3.0/tests/test_manifest.py +117 -0
  52. s3duct-0.3.0/tests/test_resume.py +225 -0
  53. s3duct-0.3.0/tests/test_resume_abort.py +524 -0
  54. s3duct-0.3.0/tests/test_s3_backend.py +110 -0
  55. s3duct-0.3.0/tests/test_stress.py +286 -0
  56. s3duct-0.3.0/tests/test_uploader.py +522 -0
@@ -0,0 +1,29 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12"]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Set up Python ${{ matrix.python-version }}
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: ${{ matrix.python-version }}
22
+
23
+ - name: Install dependencies
24
+ run: |
25
+ python -m pip install --upgrade pip
26
+ pip install -e ".[dev]"
27
+
28
+ - name: Run tests
29
+ run: pytest tests/ -v --tb=short
@@ -0,0 +1,373 @@
1
+ name: Cloud Integration Tests
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ simple:
7
+ description: "Basic S3 roundtrip (upload, download, verify)"
8
+ type: boolean
9
+ default: false
10
+ backends:
11
+ description: "All S3-compatible backends (R2, B2, MinIO)"
12
+ type: boolean
13
+ default: false
14
+ huge_file:
15
+ description: "Large file stress test (multi-GB)"
16
+ type: boolean
17
+ default: false
18
+ glacier:
19
+ description: "Glacier lifecycle (upload, thaw, restore)"
20
+ type: boolean
21
+ default: false
22
+ compat:
23
+ description: "Backward compatibility (restore old manifests)"
24
+ type: boolean
25
+ default: false
26
+
27
+ env:
28
+ S3DUCT_TEST_PREFIX: "ci-${{ github.run_id }}"
29
+
30
+ jobs:
31
+ # --------------------------------------------------------------------------
32
+ # Simple: basic upload/download/verify on AWS S3
33
+ # --------------------------------------------------------------------------
34
+ simple:
35
+ if: ${{ inputs.simple }}
36
+ runs-on: ubuntu-latest
37
+ env:
38
+ AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
39
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
40
+ S3DUCT_TEST_BUCKET: ${{ secrets.S3DUCT_TEST_BUCKET }}
41
+ steps:
42
+ - uses: actions/checkout@v4
43
+ - uses: actions/setup-python@v5
44
+ with:
45
+ python-version: "3.12"
46
+ - name: Install
47
+ run: pip install -e ".[dev]"
48
+
49
+ - name: Run roundtrip
50
+ run: bash tests/integration/roundtrip.sh
51
+
52
+ - name: Cleanup
53
+ if: always()
54
+ run: |
55
+ aws s3 rm "s3://${S3DUCT_TEST_BUCKET}/${S3DUCT_TEST_PREFIX}-" --recursive 2>/dev/null || true
56
+
57
+ # --------------------------------------------------------------------------
58
+ # Backends: test all S3-compatible services
59
+ # --------------------------------------------------------------------------
60
+ backends-minio:
61
+ if: ${{ inputs.backends }}
62
+ runs-on: ubuntu-latest
63
+ services:
64
+ minio:
65
+ image: minio/minio
66
+ ports:
67
+ - 9000:9000
68
+ env:
69
+ MINIO_ROOT_USER: minioadmin
70
+ MINIO_ROOT_PASSWORD: minioadmin
71
+ options: >-
72
+ --health-cmd "mc ready local || exit 1"
73
+ --health-interval 5s
74
+ --health-timeout 5s
75
+ --health-retries 5
76
+ steps:
77
+ - uses: actions/checkout@v4
78
+ - uses: actions/setup-python@v5
79
+ with:
80
+ python-version: "3.12"
81
+ - name: Install
82
+ run: |
83
+ pip install -e ".[dev]" awscli
84
+ for i in $(seq 1 30); do
85
+ aws --endpoint-url http://localhost:9000 s3 ls 2>/dev/null && break
86
+ sleep 2
87
+ done
88
+ aws --endpoint-url http://localhost:9000 s3 mb s3://s3duct-test
89
+ env:
90
+ AWS_ACCESS_KEY_ID: minioadmin
91
+ AWS_SECRET_ACCESS_KEY: minioadmin
92
+ - name: Run roundtrip
93
+ env:
94
+ AWS_ACCESS_KEY_ID: minioadmin
95
+ AWS_SECRET_ACCESS_KEY: minioadmin
96
+ S3DUCT_TEST_BUCKET: s3duct-test
97
+ S3DUCT_ENDPOINT_URL: http://localhost:9000
98
+ run: bash tests/integration/roundtrip.sh
99
+
100
+ backends-r2:
101
+ if: ${{ inputs.backends }}
102
+ runs-on: ubuntu-latest
103
+ env:
104
+ AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
105
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
106
+ S3DUCT_TEST_BUCKET: ${{ secrets.R2_TEST_BUCKET }}
107
+ S3DUCT_ENDPOINT_URL: ${{ secrets.R2_ENDPOINT_URL }}
108
+ steps:
109
+ - uses: actions/checkout@v4
110
+ - uses: actions/setup-python@v5
111
+ with:
112
+ python-version: "3.12"
113
+ - name: Install
114
+ run: pip install -e ".[dev]"
115
+ - name: Run roundtrip
116
+ if: env.AWS_ACCESS_KEY_ID != ''
117
+ run: bash tests/integration/roundtrip.sh
118
+ - name: Skip (no secrets)
119
+ if: env.AWS_ACCESS_KEY_ID == ''
120
+ run: echo "R2 secrets not configured, skipping"
121
+ - name: Cleanup
122
+ if: always() && env.AWS_ACCESS_KEY_ID != ''
123
+ run: |
124
+ pip install awscli
125
+ aws --endpoint-url "${S3DUCT_ENDPOINT_URL}" s3 rm "s3://${S3DUCT_TEST_BUCKET}/${S3DUCT_TEST_PREFIX}-" --recursive 2>/dev/null || true
126
+
127
+ backends-b2:
128
+ if: ${{ inputs.backends }}
129
+ runs-on: ubuntu-latest
130
+ env:
131
+ AWS_ACCESS_KEY_ID: ${{ secrets.B2_ACCESS_KEY_ID }}
132
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.B2_SECRET_ACCESS_KEY }}
133
+ S3DUCT_TEST_BUCKET: ${{ secrets.B2_TEST_BUCKET }}
134
+ S3DUCT_ENDPOINT_URL: ${{ secrets.B2_ENDPOINT_URL }}
135
+ steps:
136
+ - uses: actions/checkout@v4
137
+ - uses: actions/setup-python@v5
138
+ with:
139
+ python-version: "3.12"
140
+ - name: Install
141
+ run: pip install -e ".[dev]"
142
+ - name: Run roundtrip
143
+ if: env.AWS_ACCESS_KEY_ID != ''
144
+ run: bash tests/integration/roundtrip.sh
145
+ - name: Skip (no secrets)
146
+ if: env.AWS_ACCESS_KEY_ID == ''
147
+ run: echo "B2 secrets not configured, skipping"
148
+ - name: Cleanup
149
+ if: always() && env.AWS_ACCESS_KEY_ID != ''
150
+ run: |
151
+ pip install awscli
152
+ aws --endpoint-url "${S3DUCT_ENDPOINT_URL}" s3 rm "s3://${S3DUCT_TEST_BUCKET}/${S3DUCT_TEST_PREFIX}-" --recursive 2>/dev/null || true
153
+
154
+ # --------------------------------------------------------------------------
155
+ # Huge file: multi-GB stress test
156
+ # --------------------------------------------------------------------------
157
+ huge-file:
158
+ if: ${{ inputs.huge_file }}
159
+ runs-on: ubuntu-latest
160
+ env:
161
+ AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
162
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
163
+ S3DUCT_TEST_BUCKET: ${{ secrets.S3DUCT_TEST_BUCKET }}
164
+ steps:
165
+ - uses: actions/checkout@v4
166
+ - uses: actions/setup-python@v5
167
+ with:
168
+ python-version: "3.12"
169
+ - name: Install
170
+ run: pip install -e ".[dev]"
171
+
172
+ - name: Generate 2GB test file
173
+ run: dd if=/dev/urandom of=/tmp/huge-test.bin bs=1M count=2048
174
+
175
+ - name: Compute expected hash
176
+ run: sha256sum /tmp/huge-test.bin | cut -d' ' -f1 > /tmp/expected.sha256
177
+
178
+ - name: Upload with backpressure
179
+ run: |
180
+ cat /tmp/huge-test.bin | s3duct put \
181
+ --bucket "${S3DUCT_TEST_BUCKET}" \
182
+ --name "${S3DUCT_TEST_PREFIX}-huge" \
183
+ --chunk-size 256M \
184
+ --diskspace-limit 1G \
185
+ --tag test=huge-file \
186
+ --no-encrypt
187
+
188
+ - name: Download and verify
189
+ run: |
190
+ s3duct get \
191
+ --bucket "${S3DUCT_TEST_BUCKET}" \
192
+ --name "${S3DUCT_TEST_PREFIX}-huge" \
193
+ > /tmp/restored.bin
194
+ ACTUAL=$(sha256sum /tmp/restored.bin | cut -d' ' -f1)
195
+ EXPECTED=$(cat /tmp/expected.sha256)
196
+ if [ "$ACTUAL" != "$EXPECTED" ]; then
197
+ echo "HASH MISMATCH: expected=$EXPECTED actual=$ACTUAL"
198
+ exit 1
199
+ fi
200
+ echo "Huge file roundtrip OK: $ACTUAL"
201
+
202
+ - name: Cleanup
203
+ if: always()
204
+ run: |
205
+ aws s3 rm "s3://${S3DUCT_TEST_BUCKET}/${S3DUCT_TEST_PREFIX}-huge/" --recursive 2>/dev/null || true
206
+ rm -f /tmp/huge-test.bin /tmp/restored.bin /tmp/expected.sha256
207
+
208
+ # --------------------------------------------------------------------------
209
+ # Glacier: upload to GLACIER, thaw, restore
210
+ # --------------------------------------------------------------------------
211
+ glacier:
212
+ if: ${{ inputs.glacier }}
213
+ runs-on: ubuntu-latest
214
+ timeout-minutes: 360 # thaw can take hours
215
+ env:
216
+ AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
217
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
218
+ S3DUCT_TEST_BUCKET: ${{ secrets.S3DUCT_TEST_BUCKET }}
219
+ steps:
220
+ - uses: actions/checkout@v4
221
+ - uses: actions/setup-python@v5
222
+ with:
223
+ python-version: "3.12"
224
+ - name: Install
225
+ run: pip install -e ".[dev]"
226
+
227
+ - name: Upload to GLACIER
228
+ run: |
229
+ dd if=/dev/urandom bs=1K count=64 | s3duct put \
230
+ --bucket "${S3DUCT_TEST_BUCKET}" \
231
+ --name "${S3DUCT_TEST_PREFIX}-glacier" \
232
+ --chunk-size 32K \
233
+ --storage-class GLACIER \
234
+ --tag test=glacier \
235
+ --no-encrypt
236
+
237
+ - name: Initiate restore (Expedited tier)
238
+ run: |
239
+ # Expedited tier: 1-5 minutes for GLACIER (not available for DEEP_ARCHIVE)
240
+ # This uses aws cli directly since s3duct thaw isn't implemented yet
241
+ MANIFEST_KEY="${S3DUCT_TEST_PREFIX}-glacier/.manifest.json"
242
+ aws s3 cp "s3://${S3DUCT_TEST_BUCKET}/${MANIFEST_KEY}" /tmp/manifest.json
243
+ # Extract chunk keys from manifest and request restore for each
244
+ python3 -c "
245
+ import json, subprocess, sys, os
246
+ m = json.load(open('/tmp/manifest.json'))
247
+ bucket = os.environ['S3DUCT_TEST_BUCKET']
248
+ for c in m['chunks']:
249
+ key = c['s3_key']
250
+ print(f'Restoring {key}...')
251
+ subprocess.run([
252
+ 'aws', 's3api', 'restore-object',
253
+ '--bucket', bucket,
254
+ '--key', key,
255
+ '--restore-request', '{\"Days\":1,\"GlacierJobParameters\":{\"Tier\":\"Expedited\"}}'
256
+ ], check=True)
257
+ "
258
+
259
+ - name: Wait for thaw (poll every 60s, up to 30 min)
260
+ run: |
261
+ python3 -c "
262
+ import json, subprocess, time, os
263
+ m = json.load(open('/tmp/manifest.json'))
264
+ bucket = os.environ['S3DUCT_TEST_BUCKET']
265
+ keys = [c['s3_key'] for c in m['chunks']]
266
+ for attempt in range(30):
267
+ all_ready = True
268
+ for key in keys:
269
+ result = subprocess.run(
270
+ ['aws', 's3api', 'head-object', '--bucket', bucket, '--key', key],
271
+ capture_output=True, text=True
272
+ )
273
+ output = json.loads(result.stdout) if result.stdout else {}
274
+ restore = output.get('Restore', '')
275
+ if 'ongoing-request=\"true\"' in restore:
276
+ all_ready = False
277
+ break
278
+ if all_ready:
279
+ print(f'All chunks thawed after {attempt + 1} poll(s)')
280
+ break
281
+ print(f'Poll {attempt + 1}/30: still thawing...')
282
+ time.sleep(60)
283
+ else:
284
+ print('Timed out waiting for thaw')
285
+ exit(1)
286
+ "
287
+
288
+ - name: Download and verify
289
+ run: |
290
+ s3duct verify \
291
+ --bucket "${S3DUCT_TEST_BUCKET}" \
292
+ --name "${S3DUCT_TEST_PREFIX}-glacier"
293
+ echo "Glacier roundtrip verified"
294
+
295
+ - name: Cleanup
296
+ if: always()
297
+ run: |
298
+ aws s3 rm "s3://${S3DUCT_TEST_BUCKET}/${S3DUCT_TEST_PREFIX}-glacier/" --recursive 2>/dev/null || true
299
+
300
+ # --------------------------------------------------------------------------
301
+ # Compat: backward compatibility with older manifest versions
302
+ # --------------------------------------------------------------------------
303
+ compat:
304
+ if: ${{ inputs.compat }}
305
+ runs-on: ubuntu-latest
306
+ env:
307
+ AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
308
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
309
+ S3DUCT_TEST_BUCKET: ${{ secrets.S3DUCT_TEST_BUCKET }}
310
+ steps:
311
+ - uses: actions/checkout@v4
312
+ - uses: actions/setup-python@v5
313
+ with:
314
+ python-version: "3.12"
315
+ - name: Install
316
+ run: pip install -e ".[dev]"
317
+
318
+ - name: Test backward compat fixtures
319
+ run: |
320
+ for VERSION_DIR in tests/fixtures/compat/v*/; do
321
+ VERSION=$(basename "$VERSION_DIR")
322
+ echo "Testing compat with $VERSION..."
323
+
324
+ STREAM_NAME="${S3DUCT_TEST_PREFIX}-compat-${VERSION}"
325
+ MANIFEST="${VERSION_DIR}/manifest.json"
326
+
327
+ if [ ! -f "$MANIFEST" ]; then
328
+ echo " No manifest.json in $VERSION_DIR, skipping"
329
+ continue
330
+ fi
331
+
332
+ # Rewrite manifest s3_keys to use our CI stream name, then upload
333
+ python3 -c "
334
+ import json, sys
335
+ stream = '${STREAM_NAME}'
336
+ m = json.load(open('${MANIFEST}'))
337
+ m['name'] = stream
338
+ for c in m['chunks']:
339
+ fname = c['s3_key'].rsplit('/', 1)[-1]
340
+ c['s3_key'] = f'{stream}/{fname}'
341
+ json.dump(m, sys.stdout)
342
+ " > /tmp/compat-manifest.json
343
+ aws s3 cp /tmp/compat-manifest.json \
344
+ "s3://${S3DUCT_TEST_BUCKET}/${STREAM_NAME}/.manifest.json"
345
+
346
+ # Upload chunks
347
+ for CHUNK in "${VERSION_DIR}"/chunks/*; do
348
+ [ -f "$CHUNK" ] || continue
349
+ CHUNK_NAME=$(basename "$CHUNK")
350
+ aws s3 cp "$CHUNK" \
351
+ "s3://${S3DUCT_TEST_BUCKET}/${STREAM_NAME}/${CHUNK_NAME}"
352
+ done
353
+
354
+ # Download with current tool and verify
355
+ EXPECTED_HASH=$(cat "${VERSION_DIR}/expected_sha256")
356
+ s3duct get \
357
+ --bucket "${S3DUCT_TEST_BUCKET}" \
358
+ --name "${STREAM_NAME}" \
359
+ > /tmp/compat-restored.bin
360
+
361
+ ACTUAL_HASH=$(sha256sum /tmp/compat-restored.bin | cut -d' ' -f1)
362
+ if [ "$ACTUAL_HASH" != "$EXPECTED_HASH" ]; then
363
+ echo " COMPAT FAIL ($VERSION): expected=$EXPECTED_HASH actual=$ACTUAL_HASH"
364
+ exit 1
365
+ fi
366
+ echo " $VERSION OK"
367
+ rm -f /tmp/compat-restored.bin /tmp/compat-manifest.json
368
+ done
369
+
370
+ - name: Cleanup
371
+ if: always()
372
+ run: |
373
+ aws s3 rm "s3://${S3DUCT_TEST_BUCKET}/${S3DUCT_TEST_PREFIX}-compat-" --recursive 2>/dev/null || true
@@ -0,0 +1,66 @@
1
+ name: MinIO Integration
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ minio-roundtrip:
11
+ runs-on: ubuntu-latest
12
+ services:
13
+ minio:
14
+ image: minio/minio
15
+ ports:
16
+ - 9000:9000
17
+ env:
18
+ MINIO_ROOT_USER: minioadmin
19
+ MINIO_ROOT_PASSWORD: minioadmin
20
+ options: >-
21
+ --health-cmd "mc ready local || exit 1"
22
+ --health-interval 5s
23
+ --health-timeout 5s
24
+ --health-retries 5
25
+ # minio server needs to be started via command
26
+ steps:
27
+ - uses: actions/checkout@v4
28
+
29
+ - name: Set up Python
30
+ uses: actions/setup-python@v5
31
+ with:
32
+ python-version: "3.12"
33
+
34
+ - name: Install s3duct
35
+ run: |
36
+ python -m pip install --upgrade pip
37
+ pip install -e ".[dev]"
38
+
39
+ - name: Wait for MinIO and create test bucket
40
+ run: |
41
+ pip install awscli
42
+ export AWS_ACCESS_KEY_ID=minioadmin
43
+ export AWS_SECRET_ACCESS_KEY=minioadmin
44
+ # Wait for MinIO to be ready
45
+ for i in $(seq 1 30); do
46
+ aws --endpoint-url http://localhost:9000 s3 ls 2>/dev/null && break
47
+ echo "Waiting for MinIO... ($i)"
48
+ sleep 2
49
+ done
50
+ aws --endpoint-url http://localhost:9000 s3 mb s3://s3duct-test
51
+
52
+ - name: Run roundtrip test
53
+ env:
54
+ AWS_ACCESS_KEY_ID: minioadmin
55
+ AWS_SECRET_ACCESS_KEY: minioadmin
56
+ S3DUCT_TEST_BUCKET: s3duct-test
57
+ S3DUCT_ENDPOINT_URL: http://localhost:9000
58
+ run: bash tests/integration/roundtrip.sh
59
+
60
+ - name: Cleanup
61
+ if: always()
62
+ env:
63
+ AWS_ACCESS_KEY_ID: minioadmin
64
+ AWS_SECRET_ACCESS_KEY: minioadmin
65
+ run: |
66
+ aws --endpoint-url http://localhost:9000 s3 rb s3://s3duct-test --force 2>/dev/null || true
@@ -0,0 +1,7 @@
1
+ .venv/
2
+ *.egg-info/
3
+ __pycache__/
4
+ *.pyc
5
+ .pytest_cache/
6
+ dist/
7
+ build/
s3duct-0.3.0/CLA.md ADDED
@@ -0,0 +1,64 @@
1
+ # Contributor License Agreement
2
+
3
+ By submitting a contribution (including but not limited to code, documentation,
4
+ or other materials) to this project, you agree to the following terms:
5
+
6
+ ## 1. Grant of Copyright License
7
+
8
+ You grant to the project maintainers and to recipients of software distributed
9
+ by the project maintainers a perpetual, worldwide, non-exclusive, no-charge,
10
+ royalty-free, irrevocable copyright license to reproduce, prepare derivative
11
+ works of, publicly display, publicly perform, sublicense, and distribute your
12
+ contributions and such derivative works.
13
+
14
+ ## 2. Grant of Patent License
15
+
16
+ You grant to the project maintainers and to recipients of software distributed
17
+ by the project maintainers a perpetual, worldwide, non-exclusive, no-charge,
18
+ royalty-free, irrevocable patent license to make, have made, use, offer to sell,
19
+ sell, import, and otherwise transfer the work, where such license applies only
20
+ to those patent claims licensable by you that are necessarily infringed by your
21
+ contribution alone or by combination of your contribution with the project to
22
+ which it was submitted.
23
+
24
+ ## 3. Right to Grant
25
+
26
+ You represent that you are legally entitled to grant the above licenses. If your
27
+ employer has rights to intellectual property that you create, you represent that
28
+ you have received permission to make contributions on behalf of that employer,
29
+ or that your employer has waived such rights for your contributions to this
30
+ project.
31
+
32
+ ## 4. Original Work
33
+
34
+ You represent that each of your contributions is your original creation. You
35
+ represent that your contribution submissions include complete details of any
36
+ third-party license or other restriction (including, but not limited to, related
37
+ patents and trademarks) of which you are aware and which are associated with any
38
+ part of your contributions.
39
+
40
+ ## 5. Re-licensing
41
+
42
+ You acknowledge and agree that the project maintainers may re-license the
43
+ project (including your contributions) under any license terms they see fit,
44
+ including proprietary licenses. This right is granted to ensure the long-term
45
+ viability and flexibility of the project.
46
+
47
+ ## 6. No Obligation
48
+
49
+ You understand that the decision to include your contribution in any project or
50
+ source repository is entirely at the discretion of the project maintainers, and
51
+ this agreement does not obligate the project maintainers to use or include your
52
+ contribution.
53
+
54
+ ## 7. Support
55
+
56
+ You are not expected to provide support for your contributions, except to the
57
+ extent you desire to provide support. You may provide support for free, for a
58
+ fee, or not at all.
59
+
60
+ ## How to Sign
61
+
62
+ By opening a pull request or otherwise submitting a contribution to this
63
+ project, you indicate that you have read this Contributor License Agreement and
64
+ agree to its terms.
s3duct-0.3.0/LICENSE ADDED
@@ -0,0 +1,93 @@
1
+ Elastic License 2.0
2
+
3
+ URL: https://www.elastic.co/licensing/elastic-license
4
+
5
+ ## Acceptance
6
+
7
+ By using the software, you agree to all of the terms and conditions below.
8
+
9
+ ## Copyright License
10
+
11
+ The licensor grants you a non-exclusive, royalty-free, worldwide,
12
+ non-sublicensable, non-transferable license to use, copy, distribute, make
13
+ available, and prepare derivative works of the software, in each case subject to
14
+ the limitations and conditions below.
15
+
16
+ ## Limitations
17
+
18
+ You may not provide the software to third parties as a hosted or managed
19
+ service, where the service provides users with access to any substantial set of
20
+ the features or functionality of the software.
21
+
22
+ You may not move, change, disable, or circumvent the license key functionality
23
+ in the software, and you may not remove or obscure any functionality in the
24
+ software that is protected by the license key.
25
+
26
+ You may not alter, remove, or obscure any licensing, copyright, or other notices
27
+ of the licensor in the software. Any use of the licensor's trademarks is subject
28
+ to applicable law.
29
+
30
+ ## Patents
31
+
32
+ The licensor grants you a license, under any patent claims the licensor can
33
+ license, or becomes able to license, to make, have made, use, sell, offer for
34
+ sale, import and have imported the software, in each case subject to the
35
+ limitations and conditions in this license. This license does not cover any
36
+ patent claims that you cause to be infringed by modifications or additions to
37
+ the software. If you or your company make any written claim that the software
38
+ infringes or contributes to infringement of any patent, your patent license for
39
+ the software granted under these terms ends immediately. If your company makes
40
+ such a claim, your patent license ends immediately for work on behalf of your
41
+ company.
42
+
43
+ ## Notices
44
+
45
+ You must ensure that anyone who gets a copy of any part of the software from you
46
+ also gets a copy of these terms.
47
+
48
+ If you modify the software, you must include in any modified copies of the
49
+ software prominent notices stating that you have modified the software.
50
+
51
+ ## No Other Rights
52
+
53
+ These terms do not imply any licenses other than those expressly granted in
54
+ these terms.
55
+
56
+ ## Termination
57
+
58
+ If you use the software in violation of these terms, such use is not licensed,
59
+ and your licenses will automatically terminate. If the licensor provides you
60
+ with a notice of your violation, and you cease all violation of this license no
61
+ later than 30 days after you receive that notice, your licenses will be
62
+ reinstated retroactively. However, if you violate these terms after such
63
+ reinstatement, any additional violation of these terms will cause your licenses
64
+ to terminate automatically and permanently.
65
+
66
+ ## No Liability
67
+
68
+ *As far as the law allows, the software comes as is, without any warranty or
69
+ condition, and the licensor will not be liable to you for any damages arising
70
+ out of these terms or the use or nature of the software, under any kind of
71
+ legal claim.*
72
+
73
+ ## Definitions
74
+
75
+ The **licensor** is the entity offering these terms, and the **software** is the
76
+ software the licensor makes available under these terms, including any portion
77
+ of it.
78
+
79
+ **you** refers to the individual or entity agreeing to these terms.
80
+
81
+ **your company** is any legal entity, sole proprietorship, or other kind of
82
+ organization that you work for, plus all organizations that have control over,
83
+ are under the control of, or are under common control with that
84
+ organization. **control** means ownership of substantially all the assets of an
85
+ entity, or the power to direct its management and policies by vote, contract, or
86
+ otherwise. Control can be direct or indirect.
87
+
88
+ **your licenses** are all the licenses granted to you for the software under
89
+ these terms.
90
+
91
+ **use** means anything you do with the software requiring one of your licenses.
92
+
93
+ **trademark** means trademarks, service marks, and similar rights.