From d5e7969521a0f61aa39ea8ce2e076a76f11afc1e Mon Sep 17 00:00:00 2001 From: Vasiliy Tolstov Date: Tue, 26 Jan 2021 02:11:14 +0300 Subject: [PATCH] initial import Signed-off-by: Vasiliy Tolstov --- .github/ISSUE_TEMPLATE/bug_report.md | 24 ++ .../feature-request---enhancement.md | 17 + .github/ISSUE_TEMPLATE/question.md | 14 + .github/PULL_REQUEST_TEMPLATE.md | 9 + .github/stale.sh | 13 + .github/workflows/build.yml | 46 +++ .github/workflows/pr.yml | 46 +++ go.mod | 9 + go.sum | 174 ++++++++++ options.go | 73 +++++ s3.go | 300 ++++++++++++++++++ s3_test.go | 71 +++++ 12 files changed, 796 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature-request---enhancement.md create mode 100644 .github/ISSUE_TEMPLATE/question.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100755 .github/stale.sh create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/pr.yml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 options.go create mode 100644 s3.go create mode 100644 s3_test.go diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..1899438 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,24 @@ +--- +name: Bug report +about: For reporting bugs in go-micro +title: "[BUG]" +labels: '' +assignees: '' + +--- + +**Describe the bug** + +1. What are you trying to do? +2. What did you expect to happen? +3. What happens instead? + +**How to reproduce the bug:** + +If possible, please include a minimal code snippet here. + +**Environment:** +Go Version: please paste `go version` output here +``` +please paste `go env` output here +``` diff --git a/.github/ISSUE_TEMPLATE/feature-request---enhancement.md b/.github/ISSUE_TEMPLATE/feature-request---enhancement.md new file mode 100644 index 0000000..459817f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request---enhancement.md @@ -0,0 +1,17 @@ +--- +name: Feature request / Enhancement +about: If you have a need not served by go-micro +title: "[FEATURE]" +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 0000000..1daf48b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,14 @@ +--- +name: Question +about: Ask a question about go-micro +title: '' +labels: '' +assignees: '' + +--- + +Before asking, please check if your question has already been answered: + +1. Check the documentation - https://micro.mu/docs/ +2. Check the examples and plugins - https://github.com/micro/examples & https://github.com/micro/go-plugins +3. Search existing issues diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..cba3cbc --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,9 @@ +## Pull Request template +Please, go through these steps before clicking submit on this PR. + +1. Give a descriptive title to your PR. +2. Provide a description of your changes. +3. Make sure you have some relevant tests. +4. Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if applicable). + +**PLEASE REMOVE THIS TEMPLATE BEFORE SUBMITTING** diff --git a/.github/stale.sh b/.github/stale.sh new file mode 100755 index 0000000..8a345c4 --- /dev/null +++ b/.github/stale.sh @@ -0,0 +1,13 @@ +#!/bin/bash -ex + +export PATH=$PATH:$(pwd)/bin +export GO111MODULE=on +export GOBIN=$(pwd)/bin + +#go get github.com/rvflash/goup@v0.4.1 + +#goup -v ./... +#go get github.com/psampaz/go-mod-outdated@v0.6.0 +go list -u -m -mod=mod -json all | go-mod-outdated -update -direct -ci || true + +#go list -u -m -json all | go-mod-outdated -update diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..cce94f0 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,46 @@ +name: build +on: + push: + branches: + - master +jobs: + test: + name: test + runs-on: ubuntu-latest + steps: + - name: setup + uses: actions/setup-go@v1 + with: + go-version: 1.15 + - name: checkout + uses: actions/checkout@v2 + - name: cache + uses: actions/cache@v2 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-go- + - name: deps + run: go get -v -t -d ./... + - name: test + env: + INTEGRATION_TESTS: yes + run: go test -mod readonly -v ./... + lint: + name: lint + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v2 + - name: lint + uses: golangci/golangci-lint-action@v1 + continue-on-error: true + with: + # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. + version: v1.30 + # Optional: working directory, useful for monorepos + # working-directory: somedir + # Optional: golangci-lint command line arguments. + # args: --issues-exit-code=0 + # Optional: show only new issues if it's a pull request. The default value is `false`. + # only-new-issues: true diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..fd84850 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,46 @@ +name: prbuild +on: + pull_request: + branches: + - master +jobs: + test: + name: test + runs-on: ubuntu-latest + steps: + - name: setup + uses: actions/setup-go@v1 + with: + go-version: 1.15 + - name: checkout + uses: actions/checkout@v2 + - name: cache + uses: actions/cache@v2 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-go- + - name: deps + run: go get -v -t -d ./... + - name: test + env: + INTEGRATION_TESTS: yes + run: go test -mod readonly -v ./... + lint: + name: lint + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v2 + - name: lint + uses: golangci/golangci-lint-action@v1 + continue-on-error: true + with: + # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. + version: v1.30 + # Optional: working directory, useful for monorepos + # working-directory: somedir + # Optional: golangci-lint command line arguments. + # args: --issues-exit-code=0 + # Optional: show only new issues if it's a pull request. The default value is `false`. + # only-new-issues: true diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..03d43fb --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module github.com/unistack-org/micro-store-s3/v3 + +go 1.15 + +require ( + github.com/gabriel-vasile/mimetype v1.1.2 + github.com/minio/minio-go/v7 v7.0.7 + github.com/unistack-org/micro/v3 v3.1.8 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..5ae7e15 --- /dev/null +++ b/go.sum @@ -0,0 +1,174 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/ef-ds/deque v1.0.4/go.mod h1:gXDnTC3yqvBcHbq2lcExjtAcVrOnJCbMcZXmuj8Z4tg= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/gabriel-vasile/mimetype v1.1.2 h1:gaPnPcNor5aZSVCJVSGipcpbgMWiAAj9z182ocSGbHU= +github.com/gabriel-vasile/mimetype v1.1.2/go.mod h1:6CDPel/o/3/s4+bp6kIbsWATq8pmgOisOPG40CJa6To= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I= +github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= +github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4= +github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= +github.com/minio/minio-go/v7 v7.0.7 h1:Qld/xb8C1Pwbu0jU46xAceyn9xXKCMW+3XfNbpmTB70= +github.com/minio/minio-go/v7 v7.0.7/go.mod h1:pEZBUa+L2m9oECoIA6IcSK8bv/qggtQVLovjeKK5jYc= +github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= +github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sio v0.2.1/go.mod h1:8b0yPp2avGThviy/+OCJBI6OMpvxoUuiLvE6F1lebhw= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/unistack-org/micro/v3 v3.1.8 h1:ma9tBX7h0QyByDZ+vrY3+kqJoy/Ro9vMx2iTLjrDaNI= +github.com/unistack-org/micro/v3 v3.1.8/go.mod h1:J8XxJj4Pqa3Ee0a4biRRtut7UwTlfBq8QRe+s4PKGS0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww= +gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/options.go b/options.go new file mode 100644 index 0000000..06f3e7c --- /dev/null +++ b/options.go @@ -0,0 +1,73 @@ +package s3 + +import ( + "github.com/unistack-org/micro/v3/store" +) + +type endpointKey struct{} + +// Endpoint sets the endpoint option +func Endpoint(ep string) store.Option { + return store.SetOption(endpointKey{}, ep) +} + +type regionKey struct{} + +// Region sets the region option +func Region(reg string) store.Option { + return store.SetOption(regionKey{}, reg) +} + +type accessKey struct{} + +// AccessKey sets the AccessKey option +func AccessKey(akey string) store.Option { + return store.SetOption(accessKey{}, akey) +} + +type secretKey struct{} + +// SecretKey sets the SecretKey option +func SecretKey(skey string) store.Option { + return store.SetOption(secretKey{}, skey) +} + +type bucketKey struct{} + +// ReadBucket sets the bucket name option +func ReadBucket(bucket string) store.ReadOption { + return store.SetReadOption(bucketKey{}, bucket) +} + +// WriteBucket sets the bucket name option +func WriteBucket(bucket string) store.WriteOption { + return store.SetWriteOption(bucketKey{}, bucket) +} + +// DeleteBucket sets the bucket name option +func DeleteBucket(bucket string) store.DeleteOption { + return store.SetDeleteOption(bucketKey{}, bucket) +} + +type contentTypeKey struct{} + +func ContentType(ct string) store.WriteOption { + return store.SetWriteOption(contentTypeKey{}, ct) +} + +// ListBucket sets the bucket name option +func ListBucket(bucket string) store.ListOption { + return store.SetListOption(bucketKey{}, bucket) +} + +type listRecursiveKey struct{} + +// ListRecursive sets the bucket name option +func ListRecursive(b bool) store.ListOption { + return store.SetListOption(listRecursiveKey{}, b) +} + +// ExistsBucket sets the bucket name option +func ExistsBucket(bucket string) store.ExistsOption { + return store.SetExistsOption(bucketKey{}, bucket) +} diff --git a/s3.go b/s3.go new file mode 100644 index 0000000..eb648d7 --- /dev/null +++ b/s3.go @@ -0,0 +1,300 @@ +package s3 + +import ( + "bytes" + "context" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "path/filepath" + "regexp" + "strings" + + "github.com/gabriel-vasile/mimetype" + "github.com/minio/minio-go/v7" + creds "github.com/minio/minio-go/v7/pkg/credentials" + "github.com/unistack-org/micro/v3/store" +) + +var keyRegex = regexp.MustCompile("[^a-zA-Z0-9]+") + +type s3Store struct { + client *minio.Client + opts store.Options + endpoint string + mopts *minio.Options +} + +func getBucket(ctx context.Context) string { + if ctx != nil { + if v, ok := ctx.Value(bucketKey{}).(string); ok && v != "" { + return v + } + } + return "" +} + +// NewStore create new s3 store +func NewStore(opts ...store.Option) store.Store { + options := store.NewOptions(opts...) + return &s3Store{opts: options, mopts: &minio.Options{}} +} + +func (s *s3Store) Connect(ctx context.Context) error { + if s.client == nil { + client, err := minio.New(s.endpoint, s.mopts) + if err != nil { + return fmt.Errorf("Error connecting to store: %w", err) + } + + s.client = client + } + + return nil +} + +func (s *s3Store) Disconnect(ctx context.Context) error { + return nil +} + +func (s *s3Store) Init(opts ...store.Option) error { + for _, o := range opts { + o(&s.opts) + } + + var akey, skey string + var endpoint string + + if s.opts.Context != nil { + if v, ok := s.opts.Context.Value(accessKey{}).(string); ok && v != "" { + akey = v + } + if v, ok := s.opts.Context.Value(secretKey{}).(string); ok && v != "" { + skey = v + } + if v, ok := s.opts.Context.Value(endpointKey{}).(string); ok && v != "" { + endpoint = v + } + } + + if len(akey) > 0 && len(skey) > 0 { + s.mopts.Creds = creds.NewStaticV2(akey, skey, "") + } + + if len(endpoint) == 0 { + return fmt.Errorf("missing Endpoint option") + } + + var secure bool + + if strings.HasPrefix(endpoint, "https://") || s.opts.TLSConfig != nil { + secure = true + } + + if u, err := url.Parse(endpoint); err == nil && u.Host != "" { + endpoint = u.Host + } + + ts, err := minio.DefaultTransport(secure) + if err != nil { + return fmt.Errorf("init error: %w", err) + } + + if s.opts.TLSConfig != nil { + ts.TLSClientConfig = s.opts.TLSConfig + } + s.mopts.Transport = ts + + s.endpoint = endpoint + + return nil +} + +func (s *s3Store) Read(ctx context.Context, key string, val interface{}, opts ...store.ReadOption) error { + options := store.NewReadOptions(opts...) + + if len(key) == 0 { + return store.ErrInvalidKey + } + + bucket := getBucket(options.Context) + if bucket == "" { + bucket = options.Namespace + } + + key = keyRegex.ReplaceAllString(key, "-") + + rsp, err := s.client.GetObject(ctx, bucket, key, minio.GetObjectOptions{}) + if verr, ok := err.(minio.ErrorResponse); ok && verr.StatusCode == http.StatusNotFound { + return store.ErrNotFound + } else if err != nil { + return err + } + + _, err = rsp.Stat() + if verr, ok := err.(minio.ErrorResponse); ok && verr.StatusCode == http.StatusNotFound { + return store.ErrNotFound + } else if err != nil { + return err + } + + switch val.(type) { + case io.ReadCloser: + val = rsp + } + + defer rsp.Close() + buf, err := ioutil.ReadAll(rsp) + if err != nil { + return err + } + + return s.opts.Codec.Unmarshal(buf, val) +} + +func (s *s3Store) Write(ctx context.Context, key string, val interface{}, opts ...store.WriteOption) error { + options := store.NewWriteOptions(opts...) + var r io.Reader + + if len(key) == 0 { + return store.ErrInvalidKey + } + + switch v := val.(type) { + case io.Reader: + r = v + case []byte: + r = bytes.NewReader(v) + default: + buf, err := s.opts.Codec.Marshal(v) + if err != nil { + return err + } + r = bytes.NewReader(buf) + } + + key = keyRegex.ReplaceAllString(key, "-") + + bucket := getBucket(options.Context) + if bucket == "" { + bucket = options.Namespace + } + + var region string + mputopts := minio.PutObjectOptions{} + + if v, ok := s.opts.Context.Value(regionKey{}).(string); ok && v != "" { + region = v + } + + if options.Context != nil { + if v, ok := options.Context.Value(contentTypeKey{}).(string); ok && v != "" { + mputopts.ContentType = v + } + } + + // slow path + if mputopts.ContentType == "" { + w := bytes.NewBuffer(nil) + tr := io.TeeReader(r, w) + mime, _ := mimetype.DetectReader(tr) + mputopts.ContentType = mime.String() + r = io.MultiReader(w, r) + } + + if ok, err := s.client.BucketExists(ctx, bucket); err != nil { + return err + } else if !ok { + opts := minio.MakeBucketOptions{Region: region} + if err := s.client.MakeBucket(ctx, bucket, opts); err != nil { + return err + } + } + + _, err := s.client.PutObject(ctx, bucket, key, r, int64(-1), mputopts) + return err +} + +func (s *s3Store) Delete(ctx context.Context, key string, opts ...store.DeleteOption) error { + // validate the key + if len(key) == 0 { + return store.ErrInvalidKey + } + + // make the key safe for use with s3 + key = keyRegex.ReplaceAllString(key, "-") + + options := store.NewDeleteOptions(opts...) + + bucket := getBucket(options.Context) + if bucket == "" { + bucket = options.Namespace + } + + if len(bucket) > 0 { + return s.client.RemoveObject( + ctx, // context + bucket, // bucket name + filepath.Join(options.Namespace, key), // object name + minio.RemoveObjectOptions{}, // options + ) + } + + return s.client.RemoveObject( + ctx, // context + options.Namespace, // bucket name + key, // object name + minio.RemoveObjectOptions{}, // options + ) +} + +func (s *s3Store) Options() store.Options { + return s.opts +} + +func (s *s3Store) Exists(ctx context.Context, key string, opts ...store.ExistsOption) error { + options := store.NewExistsOptions(opts...) + + bucket := getBucket(options.Context) + if bucket == "" { + bucket = options.Namespace + } + + _, err := s.client.StatObject(ctx, bucket, key, minio.StatObjectOptions{}) + if verr, ok := err.(minio.ErrorResponse); ok && verr.StatusCode == http.StatusNotFound { + return store.ErrNotFound + } else if err != nil { + return err + } + + return nil +} + +func (s *s3Store) List(ctx context.Context, opts ...store.ListOption) ([]string, error) { + options := store.NewListOptions(opts...) + + bucket := getBucket(options.Context) + if bucket == "" { + bucket = options.Namespace + } + + var recursive bool + if options.Context != nil { + if v, ok := options.Context.Value(listRecursiveKey{}).(bool); ok { + recursive = v + } + } + + var names []string + for oinfo := range s.client.ListObjects(ctx, bucket, minio.ListObjectsOptions{Prefix: options.Prefix, Recursive: recursive}) { + names = append(names, oinfo.Key) + } + + return names, nil +} + +func (s *s3Store) String() string { + return "s3" +} diff --git a/s3_test.go b/s3_test.go new file mode 100644 index 0000000..1a42901 --- /dev/null +++ b/s3_test.go @@ -0,0 +1,71 @@ +package s3 + +import ( + "bytes" + "context" + "os" + "testing" +) + +func TestStore(t *testing.T) { + if len(os.Getenv("INTEGRATION_TESTS")) > 0 { + t.Skip() + } + + ctx := context.Background() + + s := NewStore( + AccessKey("Q3AM3UQ867SPQQA43P2F"), + SecretKey("zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG"), + Endpoint("https://play.minio.io"), + ) + + if err := s.Init(); err != nil { + t.Fatal(err) + } + + if err := s.Connect(ctx); err != nil { + t.Fatal(err) + } + + defer func() { + if err := s.Disconnect(ctx); err != nil { + t.Fatal(err) + } + }() + + val := []byte("test") + key := "key" + + if err := s.Write(ctx, key, val, WriteBucket("micro-store-s3"), ContentType("text/plain")); err != nil { + t.Fatal(err) + } + val = nil + + if err := s.Exists(ctx, key, ExistsBucket("micro-store-s3")); err != nil { + t.Fatal(err) + } + + if err := s.Read(ctx, key, &val, ReadBucket("micro-store-s3")); err != nil { + t.Fatal(err) + } else if !bytes.Equal(val, []byte("test")) { + t.Fatalf("read bytes are not equal %s != %s", val, "test") + } + + names, err := s.List(ctx, ListBucket("micro-store-s3")) + if err != nil { + t.Fatal(err) + } + + var found bool + for _, n := range names { + if n == "key" { + found = true + } + } + + if !found { + t.Fatalf("key not found in %v", names) + } + +}