feat: working version
1. implemented filesystem storage, NATS object storage and saving to Vault. 2. Test coverage is fine for filesystem and Vault (and NATS object does not really require extensive tests)
This commit is contained in:
22
LICENSE
Normal file
22
LICENSE
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Copyright 2025 Dmitry Fedotov
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the “Software”), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
sell copies of the Software, and to permit persons to whom
|
||||||
|
the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall
|
||||||
|
be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
||||||
|
OR OTHER DEALINGS IN THE SOFTWARE.
|
6
README.md
Normal file
6
README.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# storage
|
||||||
|
|
||||||
|
go get code.uint32.ru/tiny/storage
|
||||||
|
|
||||||
|
This is a work in progress package.
|
||||||
|
|
18
go.mod
18
go.mod
@@ -2,21 +2,23 @@ module code.uint32.ru/tiny/storage
|
|||||||
|
|
||||||
go 1.24
|
go 1.24
|
||||||
|
|
||||||
require github.com/nats-io/nats.go v1.43.0
|
require (
|
||||||
|
github.com/hashicorp/vault/api v1.20.0
|
||||||
|
github.com/nats-io/nats.go v1.43.0
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
github.com/go-jose/go-jose/v4 v4.1.1 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
|
||||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
||||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect
|
github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 // indirect
|
||||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
|
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
|
||||||
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
|
github.com/hashicorp/go-sockaddr v1.0.7 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.1-vault-7 // indirect
|
github.com/hashicorp/hcl v1.0.1-vault-7 // indirect
|
||||||
github.com/hashicorp/vault/api v1.20.0 // indirect
|
|
||||||
github.com/klauspost/compress v1.18.0 // indirect
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
@@ -24,8 +26,8 @@ require (
|
|||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/ryanuber/go-glob v1.0.0 // indirect
|
github.com/ryanuber/go-glob v1.0.0 // indirect
|
||||||
golang.org/x/crypto v0.40.0 // indirect
|
golang.org/x/crypto v0.40.0 // indirect
|
||||||
golang.org/x/net v0.41.0 // indirect
|
golang.org/x/net v0.42.0 // indirect
|
||||||
golang.org/x/sys v0.34.0 // indirect
|
golang.org/x/sys v0.34.0 // indirect
|
||||||
golang.org/x/text v0.27.0 // indirect
|
golang.org/x/text v0.27.0 // indirect
|
||||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect
|
golang.org/x/time v0.12.0 // indirect
|
||||||
)
|
)
|
||||||
|
74
go.sum
74
go.sum
@@ -1,85 +1,67 @@
|
|||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||||
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||||
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
github.com/go-jose/go-jose/v4 v4.1.1 h1:JYhSgy4mXXzAdF3nUx3ygx347LRXJRrpgyU3adRmkAI=
|
||||||
|
github.com/go-jose/go-jose/v4 v4.1.1/go.mod h1:BdsZGqgdO3b6tTc6LSE56wcDbMMLuPsw5d4ZD5f94kA=
|
||||||
|
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
|
||||||
|
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||||
|
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
|
||||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ=
|
github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 h1:U+kC2dOhMFQctRfhK0gRctKAPTloZdMU5ZJxaesJ/VM=
|
||||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
|
github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0=
|
||||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
|
|
||||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
|
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
|
||||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
|
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
|
||||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw=
|
||||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw=
|
||||||
github.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I=
|
github.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I=
|
||||||
github.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=
|
github.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=
|
||||||
github.com/hashicorp/vault/api v1.20.0 h1:KQMHElgudOsr+IbJgmbjHnCTxEpKs9LnozA1D3nozU4=
|
github.com/hashicorp/vault/api v1.20.0 h1:KQMHElgudOsr+IbJgmbjHnCTxEpKs9LnozA1D3nozU4=
|
||||||
github.com/hashicorp/vault/api v1.20.0/go.mod h1:GZ4pcjfzoOWpkJ3ijHNpEoAxKEsBJnVljyTe3jM2Sms=
|
github.com/hashicorp/vault/api v1.20.0/go.mod h1:GZ4pcjfzoOWpkJ3ijHNpEoAxKEsBJnVljyTe3jM2Sms=
|
||||||
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
|
|
||||||
github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
|
||||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
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/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
|
||||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/nats-io/nats.go v1.36.0 h1:suEUPuWzTSse/XhESwqLxXGuj8vGRuPRoG7MoRN/qyU=
|
|
||||||
github.com/nats-io/nats.go v1.36.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
|
||||||
github.com/nats-io/nats.go v1.41.2 h1:5UkfLAtu/036s99AhFRlyNDI1Ieylb36qbGjJzHixos=
|
|
||||||
github.com/nats-io/nats.go v1.41.2/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
|
||||||
github.com/nats-io/nats.go v1.43.0 h1:uRFZ2FEoRvP64+UUhaTokyS18XBCR/xM2vQZKO4i8ug=
|
github.com/nats-io/nats.go v1.43.0 h1:uRFZ2FEoRvP64+UUhaTokyS18XBCR/xM2vQZKO4i8ug=
|
||||||
github.com/nats-io/nats.go v1.43.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.43.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
||||||
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
|
|
||||||
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
|
|
||||||
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
|
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
|
||||||
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
|
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
|
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
|
||||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
|
||||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
|
||||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
|
||||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
|
||||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
|
||||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
|
||||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
|
||||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
|
||||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
|
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
8
internal/errinternal/errors.go
Normal file
8
internal/errinternal/errors.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package errinternal
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidKey = errors.New("storage: invalid key")
|
||||||
|
ErrNotFound = errors.New("storage: not found")
|
||||||
|
)
|
@@ -1,12 +1,26 @@
|
|||||||
package filesystem
|
package filesystem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.uint32.ru/tiny/storage/internal/errinternal"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Open(path string) (*Storage, error) {
|
const (
|
||||||
|
fileModeDir os.FileMode = 0755
|
||||||
|
fileModeFile os.FileMode = 0644
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidKey = errinternal.ErrInvalidKey
|
||||||
|
ErrNotFound = errinternal.ErrNotFound
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(path string) (*Storage, error) {
|
||||||
info, err := os.Stat(path)
|
info, err := os.Stat(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -21,17 +35,24 @@ func Open(path string) (*Storage, error) {
|
|||||||
return nil, fmt.Errorf("could not tarnslate %s to absolute path", path)
|
return nil, fmt.Errorf("could not tarnslate %s to absolute path", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Storage{prefix: abs}, nil
|
return &Storage{Dir: abs}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Storage struct {
|
type Storage struct {
|
||||||
prefix string
|
Dir string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Storage) Save(key string, data []byte) error {
|
func (s *Storage) Save(key string, data []byte) error {
|
||||||
path := s.toAbs(key)
|
if err := validateKey(key); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := os.WriteFile(path, data, 0664); err != nil {
|
path := s.getKeyPath(key)
|
||||||
|
if err := os.MkdirAll(path, fileModeDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile(filepath.Join(path, key), data, fileModeFile); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,10 +60,16 @@ func (s *Storage) Save(key string, data []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Storage) Load(key string) ([]byte, error) {
|
func (s *Storage) Load(key string) ([]byte, error) {
|
||||||
path := s.toAbs(key)
|
if err := validateKey(key); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
b, err := os.ReadFile(path)
|
path := s.getKeyPath(key)
|
||||||
if err != nil {
|
|
||||||
|
b, err := os.ReadFile(filepath.Join(path, key))
|
||||||
|
if err != nil && errors.Is(err, os.ErrNotExist) {
|
||||||
|
return nil, errors.Join(errinternal.ErrNotFound, err)
|
||||||
|
} else if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,22 +77,47 @@ func (s *Storage) Load(key string) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Storage) Delete(key string) error {
|
func (s *Storage) Delete(key string) error {
|
||||||
path := s.toAbs(key)
|
if err := validateKey(key); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
err := os.Remove(path)
|
path := s.getKeyPath(key)
|
||||||
if err != nil && os.IsNotExist(err) {
|
|
||||||
|
err := os.Remove(filepath.Join(path, key))
|
||||||
|
if err != nil && errors.Is(err, os.ErrNotExist) {
|
||||||
return nil
|
return nil
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: think of cleaning up path when no files left
|
||||||
|
// in /basedir/a/b/ after deleting key abc
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Storage) Close() error {
|
func (s *Storage) getKeyPath(key string) string {
|
||||||
|
return filepath.Join(s.Dir, getPrefixPath(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateKey(key string) error {
|
||||||
|
if len([]rune(key)) < 3 {
|
||||||
|
return errors.Join(ErrInvalidKey, fmt.Errorf("key must be at least 3 characters long"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of course windoze guys are missing the whole point, but
|
||||||
|
// let us use os-specific path separator and not ruin the whole fun
|
||||||
|
// for them :)
|
||||||
|
if strings.Contains(key, string(os.PathSeparator)) {
|
||||||
|
return errors.Join(ErrInvalidKey, fmt.Errorf("key must not contain path separator character: %s", string(os.PathSeparator)))
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Storage) toAbs(path string) string {
|
func getPrefixPath(key string) string {
|
||||||
return filepath.Join(s.prefix, path)
|
r := []rune(key)
|
||||||
|
out := []rune{r[0], '/', r[1]}
|
||||||
|
|
||||||
|
return string(out)
|
||||||
}
|
}
|
||||||
|
@@ -7,15 +7,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestStorageMethods(t *testing.T) {
|
func TestStorageMethods(t *testing.T) {
|
||||||
st, err := Open("./testdata")
|
st, err := New("./testdata")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
name := "mytestfile"
|
name := "mytestfile"
|
||||||
data := []byte("contents of my test file")
|
|
||||||
|
|
||||||
defer os.Remove(name) // just in case
|
defer os.RemoveAll("./testdata/m")
|
||||||
|
|
||||||
|
data := []byte("contents of my test file")
|
||||||
|
|
||||||
if err := st.Save(name, data); err != nil {
|
if err := st.Save(name, data); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -41,5 +42,4 @@ func TestStorageMethods(t *testing.T) {
|
|||||||
if err := st.Delete(name); err != nil {
|
if err := st.Delete(name); err != nil {
|
||||||
t.Errorf("delete of non-existent failed: %v", err)
|
t.Errorf("delete of non-existent failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,41 +1,22 @@
|
|||||||
package natsobj
|
package natsobj
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"code.uint32.ru/tiny/storage/internal/errinternal"
|
||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNotFound = errinternal.ErrNotFound
|
||||||
|
)
|
||||||
|
|
||||||
type Storage struct {
|
type Storage struct {
|
||||||
store nats.ObjectStore
|
store nats.ObjectStore
|
||||||
conn *nats.Conn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Open(bucket, url string) (*Storage, error) {
|
func New(store nats.ObjectStore) *Storage {
|
||||||
nc, err := nats.Connect(url)
|
return &Storage{store: store}
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
js, err := nc.JetStream()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := &nats.ObjectStoreConfig{
|
|
||||||
Bucket: bucket,
|
|
||||||
Description: "tiny storage bucket",
|
|
||||||
MaxBytes: -1,
|
|
||||||
Storage: nats.FileStorage,
|
|
||||||
Compression: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
store, err := js.CreateObjectStore(cfg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
st := &Storage{store: store, conn: nc}
|
|
||||||
|
|
||||||
return st, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Storage) Save(key string, data []byte) error {
|
func (n *Storage) Save(key string, data []byte) error {
|
||||||
@@ -47,7 +28,9 @@ func (n *Storage) Save(key string, data []byte) error {
|
|||||||
|
|
||||||
func (n *Storage) Load(key string) ([]byte, error) {
|
func (n *Storage) Load(key string) ([]byte, error) {
|
||||||
b, err := n.store.GetBytes(key)
|
b, err := n.store.GetBytes(key)
|
||||||
if err != nil {
|
if err != nil && errors.Is(err, nats.ErrObjectNotFound) {
|
||||||
|
return nil, errors.Join(ErrNotFound, err)
|
||||||
|
} else if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,15 +38,12 @@ func (n *Storage) Load(key string) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *Storage) Delete(key string) error {
|
func (n *Storage) Delete(key string) error {
|
||||||
if err := n.store.Delete(key); err != nil {
|
err := n.store.Delete(key)
|
||||||
|
if err != nil && errors.Is(err, nats.ErrObjectNotFound) {
|
||||||
|
return nil
|
||||||
|
} else if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Storage) Close() error {
|
|
||||||
n.conn.Close()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@@ -5,32 +5,26 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"code.uint32.ru/tiny/storage/internal/errinternal"
|
||||||
"github.com/hashicorp/vault/api"
|
"github.com/hashicorp/vault/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNotFound = errinternal.ErrNotFound
|
||||||
|
)
|
||||||
|
|
||||||
type Storage struct {
|
type Storage struct {
|
||||||
client *api.Client
|
kv *api.KVv1
|
||||||
path string
|
// TODO: kv2: *api.KVv2
|
||||||
}
|
}
|
||||||
|
|
||||||
func Open(token string, path string, addr string) (*Storage, error) {
|
// New returns Storage writing to the specified vault path.
|
||||||
conf := &api.Config{
|
// Object will be base64 encoded and written to path/key.
|
||||||
Address: addr,
|
func New(c *api.Client, path string) *Storage {
|
||||||
}
|
return &Storage{kv: c.KVv1(path)}
|
||||||
|
|
||||||
c, err := api.NewClient(conf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.SetToken(token)
|
|
||||||
|
|
||||||
return &Storage{client: c, path: path}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Storage) Save(key string, data []byte) error {
|
func (s *Storage) Save(key string, data []byte) error {
|
||||||
kv := s.client.KVv1(s.path)
|
|
||||||
|
|
||||||
str := base64.StdEncoding.EncodeToString(data)
|
str := base64.StdEncoding.EncodeToString(data)
|
||||||
m := map[string]any{
|
m := map[string]any{
|
||||||
"data": map[string]string{
|
"data": map[string]string{
|
||||||
@@ -38,7 +32,7 @@ func (s *Storage) Save(key string, data []byte) error {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := kv.Put(context.Background(), "testkey", m); err != nil {
|
if err := s.kv.Put(context.Background(), "testkey", m); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,10 +40,10 @@ func (s *Storage) Save(key string, data []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Storage) Load(key string) ([]byte, error) {
|
func (s *Storage) Load(key string) ([]byte, error) {
|
||||||
kv := s.client.KVv1(s.path)
|
m, err := s.kv.Get(context.Background(), key)
|
||||||
|
if err != nil && errors.Is(err, api.ErrSecretNotFound) {
|
||||||
m, err := kv.Get(context.Background(), key)
|
return nil, errors.Join(ErrNotFound, err)
|
||||||
if err != nil {
|
} else if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,16 +78,9 @@ func (s *Storage) Load(key string) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Storage) Delete(key string) error {
|
func (s *Storage) Delete(key string) error {
|
||||||
kv := s.client.KVv1(s.path)
|
if err := s.kv.Delete(context.Background(), key); err != nil {
|
||||||
|
|
||||||
if err := kv.Delete(context.Background(), key); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Storage) Close() error {
|
|
||||||
s.client.ClearToken()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@@ -4,6 +4,8 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"code.uint32.ru/tiny/storage/storageutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestVaultStorage(t *testing.T) {
|
func TestVaultStorage(t *testing.T) {
|
||||||
@@ -25,7 +27,9 @@ func TestVaultStorage(t *testing.T) {
|
|||||||
t.Log(addr)
|
t.Log(addr)
|
||||||
t.Log(path)
|
t.Log(path)
|
||||||
|
|
||||||
st, err := Open(token, path, addr)
|
client, err := storageutil.NewVaultApiClient(token, addr)
|
||||||
|
|
||||||
|
st := New(client, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
43
storage.go
43
storage.go
@@ -1,11 +1,24 @@
|
|||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/hashicorp/vault/api"
|
||||||
|
"github.com/nats-io/nats.go"
|
||||||
|
|
||||||
|
"code.uint32.ru/tiny/storage/internal/errinternal"
|
||||||
"code.uint32.ru/tiny/storage/internal/filesystem"
|
"code.uint32.ru/tiny/storage/internal/filesystem"
|
||||||
"code.uint32.ru/tiny/storage/internal/natsobj"
|
"code.uint32.ru/tiny/storage/internal/natsobj"
|
||||||
"code.uint32.ru/tiny/storage/internal/vault"
|
"code.uint32.ru/tiny/storage/internal/vault"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrInvalidKey is returned when key validation
|
||||||
|
// for particular implementation of Storage fails.
|
||||||
|
ErrInvalidKey = errinternal.ErrInvalidKey
|
||||||
|
// ErrNotFound is returned when object is not found
|
||||||
|
// in Storage.
|
||||||
|
ErrNotFound = errinternal.ErrNotFound
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ Storage = (*natsobj.Storage)(nil)
|
_ Storage = (*natsobj.Storage)(nil)
|
||||||
_ Storage = (*filesystem.Storage)(nil)
|
_ Storage = (*filesystem.Storage)(nil)
|
||||||
@@ -17,32 +30,34 @@ type Storage interface {
|
|||||||
// Save puts object with name 'key' into the store.
|
// Save puts object with name 'key' into the store.
|
||||||
// If a key already exists it gets overwritten.
|
// If a key already exists it gets overwritten.
|
||||||
Save(key string, data []byte) error
|
Save(key string, data []byte) error
|
||||||
// Load returns contents of object named 'key'.
|
// Load returns contents of object named 'key' or
|
||||||
|
// ErrNotFound.
|
||||||
Load(key string) ([]byte, error)
|
Load(key string) ([]byte, error)
|
||||||
// Delete removes object named 'key' from the store.
|
// Delete removes object named 'key' from the store.
|
||||||
// If key does not exist Delete returns nil.
|
// If key does not exist Delete returns nil.
|
||||||
Delete(key string) error
|
Delete(key string) error
|
||||||
// Close must be called when you're done working with Storage.
|
|
||||||
Close() error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNats connects to NATS messaging system and tries to create
|
// NewNats wraps the provided ObjectStore with Storage interface.
|
||||||
// a new object storage with name 'bucket'. The returned Storage
|
func NewNats(store nats.ObjectStore) Storage {
|
||||||
// uses the created bucket as underlying physical store.
|
return natsobj.New(store)
|
||||||
func NewNats(bucket string, url string) (Storage, error) {
|
|
||||||
return natsobj.Open(bucket, url)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFS established a key/value within the directory 'path'
|
// NewFS established a key/value within the directory 'path'
|
||||||
// and uses is as underlying physical store.
|
// and uses it as underlying physical store.
|
||||||
|
// Note that the implementation requires keys to be at least
|
||||||
|
// 3 characters long.
|
||||||
|
// Key "abcd" will be stored in /path/a/b/abcd.
|
||||||
func NewFS(path string) (Storage, error) {
|
func NewFS(path string) (Storage, error) {
|
||||||
return filesystem.Open(path)
|
return filesystem.New(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewVault connects to Vault at addr and uses path as base path for
|
// NewVault uses provided Vault client to store objects.
|
||||||
|
// The provided path is used as base path for
|
||||||
// keys. Objects saved to Storage will be put at
|
// keys. Objects saved to Storage will be put at
|
||||||
// /path/key as new secrets.
|
// /path/key as new secrets.
|
||||||
// Bytes passed to storage will be base64 encoded and saved as string.
|
// Bytes passed to storage will be base64 encoded and saved
|
||||||
func NewVault(token string, path string, addr string) (Storage, error) {
|
// in Vault as string.
|
||||||
return vault.Open(token, path, addr)
|
func NewVault(client *api.Client, path string) Storage {
|
||||||
|
return vault.New(client, path)
|
||||||
}
|
}
|
||||||
|
36
storageutil/nats.go
Normal file
36
storageutil/nats.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package storageutil
|
||||||
|
|
||||||
|
import "github.com/nats-io/nats.go"
|
||||||
|
|
||||||
|
// CreateNatsObjectStore is a convenience function that
|
||||||
|
// connects to NATS and using provided url and creates
|
||||||
|
// new object store using bucket as bucket name.
|
||||||
|
// The object store uses NATS file storage and compression.
|
||||||
|
// If fine-tuning is required - just create the store in your
|
||||||
|
// code and pass it to the storage package.
|
||||||
|
func CreateNatsObjectStore(url string, bucket string) (nats.ObjectStore, *nats.Conn, error) {
|
||||||
|
nc, err := nats.Connect(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
js, err := nc.JetStream()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &nats.ObjectStoreConfig{
|
||||||
|
Bucket: bucket,
|
||||||
|
Description: "tiny storage bucket",
|
||||||
|
MaxBytes: -1,
|
||||||
|
Storage: nats.FileStorage,
|
||||||
|
Compression: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
store, err := js.CreateObjectStore(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return store, nc, nil
|
||||||
|
}
|
18
storageutil/vault.go
Normal file
18
storageutil/vault.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package storageutil
|
||||||
|
|
||||||
|
import "github.com/hashicorp/vault/api"
|
||||||
|
|
||||||
|
func NewVaultApiClient(token string, addr string) (*api.Client, error) {
|
||||||
|
conf := &api.Config{
|
||||||
|
Address: addr,
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := api.NewClient(conf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SetToken(token)
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
Reference in New Issue
Block a user