From aa469e53e99f7fd988bca867b58fbead0dddfd71 Mon Sep 17 00:00:00 2001 From: Denis Arh Date: Tue, 15 Oct 2019 13:22:57 +0200 Subject: [PATCH] Add MinIO support --- .env.example | 35 ++++++++++ Makefile | 7 ++ go.mod | 1 + go.sum | 22 +++++++ messaging/service/service.go | 28 +++++++- pkg/cli/options/storage.go | 14 ++++ pkg/store/minio/store.go | 121 +++++++++++++++++++++++++++++++++++ 7 files changed, 226 insertions(+), 2 deletions(-) create mode 100644 pkg/store/minio/store.go diff --git a/.env.example b/.env.example index 3f5995e81..ba255c4e2 100644 --- a/.env.example +++ b/.env.example @@ -49,3 +49,38 @@ DB_DSN=corteza:corteza@tcp(localhost:3306)/corteza?collation=utf8mb4_general_ci # Enable debug logger (more verbose, #LOG_DEBUG=false + +######################################################################################################################## +# Storage configuration + +# Local, plain storage path: + +# General all-in-one +#STORAGE_PATH=var/store (defaults to "/var/store") + +# or separate path by service (Docker default values): +#MESSAGING_STORAGE_PATH=/data/messaging +#COMPOSE_STORAGE_PATH=/data/compose +#SYSTEM_STORAGE_PATH=/data/system + +# Min.io: +# Storage to minio backend is activated when MINIO_ENDPOINT is set +# +# If you are in development environemnt and using `make minio.up` for testing, +# your min.io instance is most likely listening on localhost +# (inspect `Makefile` for details) +#MINIO_ENDPOINT=localhost:9000 + +# Access & secret key +# These keys are used by Min.io Docker container as well +#MINIO_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE +#MINIO_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY + +# Per-service buckets (default values) +#MESSAGING_MINIO_BUCKET=messaging +#COMPOSE_MINIO_BUCKET=compose +#SYSTEM_MINIO_BUCKET=system + +# Strict mode: +# When true, it does not create un-existing buckets +#MINIO_STRICT=false diff --git a/Makefile b/Makefile index 0946a0bac..cf3bdf6f9 100644 --- a/Makefile +++ b/Makefile @@ -35,6 +35,8 @@ TEST_SUITE_unit = $(TEST_SUITE_pkg) $(TEST_SUITE_services) TEST_SUITE_integration = ./tests/... TEST_SUITE_all = $(TEST_SUITE_unit) $(TEST_SUITE_integration) +DEV_MINIO_PORT ?= 9000 + ######################################################################################################################## # Tool bins @@ -89,6 +91,11 @@ codegen: $(PROTOGEN) mailhog.up: docker run --rm --publish 8025:8025 --publish 1025:1025 mailhog/mailhog +minio.up: + # Runs temp minio server + # No volume because we do not want the data to persist + docker run --rm --publish 9000:$(DEV_MINIO_PORT) --env-file .env minio/minio server /data + watch.test.%: $(NODEMON) # Development helper - watches for file # changes & reruns tests diff --git a/go.mod b/go.mod index 1aca84bf7..3f96caa22 100644 --- a/go.mod +++ b/go.mod @@ -36,6 +36,7 @@ require ( github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 github.com/lib/pq v1.1.0 // indirect github.com/markbates/goth v1.50.0 + github.com/minio/minio-go/v6 v6.0.39 github.com/pkg/errors v0.8.1 github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect github.com/prometheus/client_golang v0.9.2 diff --git a/go.sum b/go.sum index 91b9ce2a2..bc5692e6f 100644 --- a/go.sum +++ b/go.sum @@ -43,6 +43,7 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/disintegration/imaging v1.6.0 h1:nVPXRUUQ36Z7MNf0O77UzgnOb1mkMMor7lmJMJXc/mA= github.com/disintegration/imaging v1.6.0/go.mod h1:xuIt+sRxDFrHS0drzXUlCJthkJ8k7lkkUojDSR247MQ= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/edwvee/exiffix v0.0.0-20180602190213-b57537c92a6b h1:6CBzNasH8+bKeFwr5Bt5JtALHLFN4iQp7sf4ShlP/ik= github.com/edwvee/exiffix v0.0.0-20180602190213-b57537c92a6b/go.mod h1:KoE3Ti1qbQXCb3s/XGj0yApHnbnNnn1bXTtB5Auq/Vc= github.com/gabriel-vasile/mimetype v0.3.17 h1:NGWgggJJqTofUcTV1E7hkk2zVjZ54EfJa1z5O3z6By4= @@ -85,6 +86,8 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OI github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +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/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 h1:f0n1xnMSmBLzVfsMMvriDyA75NB/oBgILX2GcHXIQzY= github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= @@ -112,6 +115,8 @@ github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhB github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -129,6 +134,12 @@ github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/ github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/minio/minio-go/v6 v6.0.39 h1:9qmKCTBpQpMdGlDAbs3mbb4mmL45/lwRUvHL1VLhYUk= +github.com/minio/minio-go/v6 v6.0.39/go.mod h1:qD0lajrGW49lKZLtXKtCB4X/qkMf0a5tBvN2PaZg7Gg= +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/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM= @@ -158,6 +169,11 @@ github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7z github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +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/sony/sonyflake v0.0.0-20181109022403-6d5bd6181009 h1:3wBL/e/qjpSYaXacpbIV+Bsj/nwQ4UO1llG/av54zzw= github.com/sony/sonyflake v0.0.0-20181109022403-6d5bd6181009/go.mod h1:dVvZuWJd174umvm5g8CmZD6S2GWwHKtpK/0ZPHswuNo= github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= @@ -191,6 +207,7 @@ go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 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-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -215,6 +232,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -234,6 +252,7 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -251,6 +270,7 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -282,6 +302,8 @@ gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gG gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk= +gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk= gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw= gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= diff --git a/messaging/service/service.go b/messaging/service/service.go index dca096e70..119c9eac8 100644 --- a/messaging/service/service.go +++ b/messaging/service/service.go @@ -11,6 +11,7 @@ import ( "github.com/cortezaproject/corteza-server/pkg/http" "github.com/cortezaproject/corteza-server/pkg/permissions" "github.com/cortezaproject/corteza-server/pkg/store" + "github.com/cortezaproject/corteza-server/pkg/store/minio" "github.com/cortezaproject/corteza-server/pkg/store/plain" ) @@ -49,8 +50,31 @@ func Init(ctx context.Context, log *zap.Logger, c Config) (err error) { DefaultLogger = log.Named("service") if DefaultStore == nil { - DefaultStore, err = plain.New(c.Storage.Path) - log.Info("initializing store", zap.String("path", c.Storage.Path), zap.Error(err)) + if c.Storage.MinioEndpoint != "" { + if c.Storage.MinioBucket == "" { + c.Storage.MinioBucket = "messaging" + } + + DefaultStore, err = minio.New(c.Storage.MinioBucket, minio.Options{ + Endpoint: c.Storage.MinioEndpoint, + Secure: c.Storage.MinioSecure, + Strict: c.Storage.MinioStrict, + AccessKeyID: c.Storage.MinioAccessKey, + SecretAccessKey: c.Storage.MinioSecretKey, + }) + + log.Info("initializing minio", + zap.String("bucket", c.Storage.MinioBucket), + zap.String("endpoint", c.Storage.MinioEndpoint), + zap.Error(err)) + } else { + DefaultStore, err = plain.New(c.Storage.Path) + + log.Info("initializing store", + zap.String("path", c.Storage.Path), + zap.Error(err)) + } + if err != nil { return err } diff --git a/pkg/cli/options/storage.go b/pkg/cli/options/storage.go index e1ab81e51..5159f6698 100644 --- a/pkg/cli/options/storage.go +++ b/pkg/cli/options/storage.go @@ -3,12 +3,26 @@ package options type ( StorageOpt struct { Path string `env:"STORAGE_PATH"` + + MinioEndpoint string `env:"MINIO_ENDPOINT"` + MinioSecure bool `env:"MINIO_SECURE"` + MinioAccessKey string `env:"MINIO_ACCESS_KEY"` + MinioSecretKey string `env:"MINIO_SECRET_KEY"` + MinioBucket string `env:"MINIO_BUCKET"` + MinioStrict bool `env:"MINIO_STRICT"` } ) func Storage(pfix string) (o *StorageOpt) { o = &StorageOpt{ Path: "var/store", + + // Make minio secure by default + MinioSecure: true, + + // Run in struct mode: + // - do not create unexisting buckets + MinioStrict: false, } fill(o, pfix) diff --git a/pkg/store/minio/store.go b/pkg/store/minio/store.go new file mode 100644 index 000000000..9f6a76653 --- /dev/null +++ b/pkg/store/minio/store.go @@ -0,0 +1,121 @@ +package minio + +import ( + "fmt" + "io" + + minio "github.com/minio/minio-go/v6" + "github.com/minio/minio-go/v6/pkg/encrypt" + "github.com/minio/minio-go/v6/pkg/s3utils" + "github.com/pkg/errors" +) + +type ( + Options struct { + Endpoint string + Secure bool + Strict bool + + AccessKeyID string + SecretAccessKey string + + ServerSideEncryptKey []byte + } + + store struct { + bucket string + + mc *minio.Client + sse encrypt.ServerSide + + originalFn func(id uint64, ext string) string + previewFn func(id uint64, ext string) string + } +) + +var ( + defPreviewFn = func(id uint64, ext string) string { + return fmt.Sprintf("%d_preview.%s", id, ext) + } + + defOriginalFn = func(id uint64, ext string) string { + return fmt.Sprintf("%d.%s", id, ext) + } +) + +func New(bucket string, opt Options) (s *store, err error) { + s = &store{ + bucket: bucket, + mc: nil, + + originalFn: defOriginalFn, + previewFn: defPreviewFn, + } + + if err = s3utils.CheckValidBucketName(s.bucket); err != nil { + return nil, err + } + + if s.mc, err = minio.New(opt.Endpoint, opt.AccessKeyID, opt.SecretAccessKey, opt.Secure); err != nil { + return nil, err + } + + if e, err := s.mc.BucketExists(s.bucket); err != nil { + return nil, err + } else if !e { + if opt.Strict { + return nil, errors.Errorf("bucket %q does not exist", s.bucket) + } + + err = s.mc.MakeBucket(s.bucket, "us-east-1") + if err != nil { + return nil, err + } + } + + if len(opt.ServerSideEncryptKey) > 0 { + s.sse, err = encrypt.NewSSEC(opt.ServerSideEncryptKey) + } + + return +} + +func (s *store) check(name string) error { + if len(name) == 0 { + return errors.Errorf("Invalid name when trying to store object: '%s' (for %s)", name, s.bucket) + } + + return nil +} + +func (s store) Original(id uint64, ext string) string { + // @todo presigned URL + return s.originalFn(id, ext) +} + +func (s store) Preview(id uint64, ext string) string { + // @todo presigned URL + return s.previewFn(id, ext) + +} + +func (s store) Save(name string, f io.Reader) (err error) { + _, err = s.mc.PutObject(s.bucket, name, f, -1, minio.PutObjectOptions{ + ServerSideEncryption: s.sse, + UserMetadata: map[string]string{ + "some": "User", + }, + }) + + return err +} + +func (s store) Remove(name string) error { + return s.mc.RemoveObject(s.bucket, name) +} + +func (s store) Open(name string) (io.ReadSeeker, error) { + return s.mc.GetObject(s.bucket, name, minio.GetObjectOptions{ + ServerSideEncryption: s.sse, + }) +}