mcs add bucket event api using public mc S3Client struct (#15)

* mcs add bucket event api using public mc S3Client struct

* remove log

* remove replace repo on go.mod

* apply go mod tidy
This commit is contained in:
César Nieto
2020-04-02 20:09:36 -07:00
committed by GitHub
parent c710aa32fc
commit 768c7c70a3
14 changed files with 890 additions and 26 deletions

3
go.sum
View File

@@ -79,8 +79,6 @@ github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dvaldivia/mc v0.0.0-20200402232537-1048833f1701 h1:TaXA4hNW3k99MbCKOFnXsaLr9xsx/kh9YXcUNQ/Q8Wk=
github.com/dvaldivia/mc v0.0.0-20200402232537-1048833f1701/go.mod h1:IDy4dA4aFY6zFFNkYgdUztl0jcYuev/Ubg3NadoaMKc=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q=
github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
@@ -385,7 +383,6 @@ github.com/minio/highwayhash v1.0.0 h1:iMSDhgUILCr0TNm8LWlSjF8N0ZIj2qbO8WHp6Q/J2
github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5dxBpaMbdc=
github.com/minio/lsync v1.0.1 h1:AVvILxA976xc27hstd1oR+X9PQG0sPSom1MNb1ImfUs=
github.com/minio/lsync v1.0.1/go.mod h1:tCFzfo0dlvdGl70IT4IAK/5Wtgb0/BrTmo/jE8pArKA=
github.com/minio/mc v0.0.0-20200401220942-e05f02d9f459/go.mod h1:GWohdY5tXSiMnBCofmDRK5yRCihQH2FKNM0eh+UsY5Y=
github.com/minio/mc v0.0.0-20200403024131-4d36c1f8b856 h1:4uIc5fw4tVr5glh2Mc8GFuiY04pTGEhmihPxJPUvCoU=
github.com/minio/mc v0.0.0-20200403024131-4d36c1f8b856/go.mod h1:IDy4dA4aFY6zFFNkYgdUztl0jcYuev/Ubg3NadoaMKc=
github.com/minio/minio v0.0.0-20200327214830-6f992134a25f h1:RoOBi0vhXkZqe2b6RTROOsVJUwMqLMoet9r7eL01euo=

View File

@@ -0,0 +1,93 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// BucketEventRequest bucket event request
//
// swagger:model bucketEventRequest
type BucketEventRequest struct {
// configuration
// Required: true
Configuration *NotificationConfig `json:"configuration"`
// ignore existing
IgnoreExisting bool `json:"ignoreExisting,omitempty"`
}
// Validate validates this bucket event request
func (m *BucketEventRequest) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateConfiguration(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *BucketEventRequest) validateConfiguration(formats strfmt.Registry) error {
if err := validate.Required("configuration", "body", m.Configuration); err != nil {
return err
}
if m.Configuration != nil {
if err := m.Configuration.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("configuration")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *BucketEventRequest) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *BucketEventRequest) UnmarshalBinary(b []byte) error {
var res BucketEventRequest
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -28,6 +28,7 @@ import (
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// NotificationConfig notification config
@@ -36,7 +37,8 @@ import (
type NotificationConfig struct {
// arn
Arn string `json:"arn,omitempty"`
// Required: true
Arn *string `json:"arn"`
// filter specific type of event. Defaults to all event (default: '[put,delete,get]')
Events []NotificationEventType `json:"events"`
@@ -55,6 +57,10 @@ type NotificationConfig struct {
func (m *NotificationConfig) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateArn(formats); err != nil {
res = append(res, err)
}
if err := m.validateEvents(formats); err != nil {
res = append(res, err)
}
@@ -65,6 +71,15 @@ func (m *NotificationConfig) Validate(formats strfmt.Registry) error {
return nil
}
func (m *NotificationConfig) validateArn(formats strfmt.Registry) error {
if err := validate.Required("arn", "body", m.Arn); err != nil {
return err
}
return nil
}
func (m *NotificationConfig) validateEvents(formats strfmt.Registry) error {
if swag.IsZero(m.Events) { // not required

View File

@@ -18,7 +18,10 @@ package restapi
import (
"context"
"fmt"
mc "github.com/minio/mc/cmd"
"github.com/minio/mc/pkg/probe"
"github.com/minio/minio-go/v6"
)
@@ -50,33 +53,53 @@ type minioClient struct {
}
// implements minio.ListBucketsWithContext(ctx)
func (mc minioClient) listBucketsWithContext(ctx context.Context) ([]minio.BucketInfo, error) {
return mc.client.ListBucketsWithContext(ctx)
func (c minioClient) listBucketsWithContext(ctx context.Context) ([]minio.BucketInfo, error) {
return c.client.ListBucketsWithContext(ctx)
}
// implements minio.MakeBucketWithContext(ctx, bucketName, location)
func (mc minioClient) makeBucketWithContext(ctx context.Context, bucketName, location string) error {
return mc.client.MakeBucketWithContext(ctx, bucketName, location)
func (c minioClient) makeBucketWithContext(ctx context.Context, bucketName, location string) error {
return c.client.MakeBucketWithContext(ctx, bucketName, location)
}
// implements minio.SetBucketPolicyWithContext(ctx, bucketName, policy)
func (mc minioClient) setBucketPolicyWithContext(ctx context.Context, bucketName, policy string) error {
return mc.client.SetBucketPolicyWithContext(ctx, bucketName, policy)
func (c minioClient) setBucketPolicyWithContext(ctx context.Context, bucketName, policy string) error {
return c.client.SetBucketPolicyWithContext(ctx, bucketName, policy)
}
// implements minio.RemoveBucket(bucketName)
func (mc minioClient) removeBucket(bucketName string) error {
return mc.client.RemoveBucket(bucketName)
func (c minioClient) removeBucket(bucketName string) error {
return c.client.RemoveBucket(bucketName)
}
// implements minio.GetBucketNotification(bucketName)
func (mc minioClient) getBucketNotification(bucketName string) (bucketNotification minio.BucketNotification, err error) {
return mc.client.GetBucketNotification(bucketName)
func (c minioClient) getBucketNotification(bucketName string) (bucketNotification minio.BucketNotification, err error) {
return c.client.GetBucketNotification(bucketName)
}
// implements minio.GetBucketPolicy(bucketName)
func (mc minioClient) getBucketPolicy(bucketName string) (string, error) {
return mc.client.GetBucketPolicy(bucketName)
func (c minioClient) getBucketPolicy(bucketName string) (string, error) {
return c.client.GetBucketPolicy(bucketName)
}
// Define MCS3Client interface with all functions to be implemented
// by mock when testing, it should include all mc/S3Client respective api calls
// that are used within this project.
type MCS3Client interface {
addNotificationConfig(arn string, events []string, prefix, suffix string, ignoreExisting bool) *probe.Error
}
// Interface implementation
//
// Define the structure of a mc S3Client and define the functions that are actually used
// from mcS3client api.
type mcS3Client struct {
client *mc.S3Client
}
// implements minio.ListBucketsWithContext(ctx)
func (c mcS3Client) addNotificationConfig(arn string, events []string, prefix, suffix string, ignoreExisting bool) *probe.Error {
return c.client.AddNotificationConfig(arn, events, prefix, suffix, ignoreExisting)
}
// newMinioClient creates a new MinIO client to talk to the server
@@ -84,7 +107,7 @@ func newMinioClient() (*minio.Client, error) {
endpoint := getMinIOEndpoint()
accessKeyID := getAccessKey()
secretAccessKey := getSecretKey()
useSSL := getMinIOEndpointSSL()
useSSL := getMinIOEndpointIsSecure()
// Initialize minio client object.
minioClient, err := minio.NewV4(endpoint, accessKeyID, secretAccessKey, useSSL)
@@ -94,3 +117,46 @@ func newMinioClient() (*minio.Client, error) {
return minioClient, nil
}
// newS3BucketClient creates a new mc S3Client to talk to the server based on a bucket
func newS3BucketClient(bucketName *string) (*mc.S3Client, error) {
endpoint := getMinIOServer()
accessKeyID := getAccessKey()
secretAccessKey := getSecretKey()
useSSL := getMinIOEndpointIsSecure()
if bucketName != nil {
endpoint += fmt.Sprintf("/%s", *bucketName)
}
s3Config := newS3Config(endpoint, accessKeyID, secretAccessKey, !useSSL)
client, err := mc.S3New(s3Config)
if err != nil {
return nil, err.Cause
}
s3Client, ok := client.(*mc.S3Client)
if !ok {
return nil, fmt.Errorf("the provided url doesn't point to a S3 server")
}
return s3Client, nil
}
// newS3Config simply creates a new Config struct using the passed
// parameters.
func newS3Config(endpoint, accessKey, secretKey string, isSecure bool) *mc.Config {
// We have a valid alias and hostConfig. We populate the
// credentials from the match found in the config file.
s3Config := new(mc.Config)
s3Config.AppName = "mcs" // TODO: make this a constant
s3Config.AppVersion = "" // TODO: get this from constant or build
s3Config.AppComments = []string{}
s3Config.Debug = false
s3Config.Insecure = isSecure
s3Config.HostURL = endpoint
s3Config.AccessKey = accessKey
s3Config.SecretKey = secretKey
s3Config.Signature = "S3v4"
return s3Config
}

View File

@@ -45,7 +45,7 @@ func getMinIOEndpoint() string {
return server
}
func getMinIOEndpointSSL() bool {
func getMinIOEndpointIsSecure() bool {
server := getMinIOServer()
if strings.Contains(server, "://") {
parts := strings.Split(server, "://")

View File

@@ -161,6 +161,40 @@ func init() {
}
}
}
},
"post": {
"tags": [
"UserAPI"
],
"summary": "Create Bucket Event",
"operationId": "CreateBucketEvent",
"parameters": [
{
"type": "string",
"name": "bucket_name",
"in": "path",
"required": true
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/bucketEventRequest"
}
}
],
"responses": {
"201": {
"description": "A successful response."
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
},
"/api/v1/buckets/{name}": {
@@ -987,6 +1021,20 @@ func init() {
"CUSTOM"
]
},
"bucketEventRequest": {
"type": "object",
"required": [
"configuration"
],
"properties": {
"configuration": {
"$ref": "#/definitions/notificationConfig"
},
"ignoreExisting": {
"type": "boolean"
}
}
},
"configDescription": {
"type": "object",
"properties": {
@@ -1207,6 +1255,9 @@ func init() {
},
"notificationConfig": {
"type": "object",
"required": [
"arn"
],
"properties": {
"arn": {
"type": "string"
@@ -1576,6 +1627,40 @@ func init() {
}
}
}
},
"post": {
"tags": [
"UserAPI"
],
"summary": "Create Bucket Event",
"operationId": "CreateBucketEvent",
"parameters": [
{
"type": "string",
"name": "bucket_name",
"in": "path",
"required": true
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/bucketEventRequest"
}
}
],
"responses": {
"201": {
"description": "A successful response."
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
},
"/api/v1/buckets/{name}": {
@@ -2402,6 +2487,20 @@ func init() {
"CUSTOM"
]
},
"bucketEventRequest": {
"type": "object",
"required": [
"configuration"
],
"properties": {
"configuration": {
"$ref": "#/definitions/notificationConfig"
},
"ignoreExisting": {
"type": "boolean"
}
}
},
"configDescription": {
"type": "object",
"properties": {
@@ -2622,6 +2721,9 @@ func init() {
},
"notificationConfig": {
"type": "object",
"required": [
"arn"
],
"properties": {
"arn": {
"type": "string"

View File

@@ -81,6 +81,9 @@ func NewMcsAPI(spec *loads.Document) *McsAPI {
AdminAPIConfigInfoHandler: admin_api.ConfigInfoHandlerFunc(func(params admin_api.ConfigInfoParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation admin_api.ConfigInfo has not yet been implemented")
}),
UserAPICreateBucketEventHandler: user_api.CreateBucketEventHandlerFunc(func(params user_api.CreateBucketEventParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation user_api.CreateBucketEvent has not yet been implemented")
}),
UserAPIDeleteBucketHandler: user_api.DeleteBucketHandlerFunc(func(params user_api.DeleteBucketParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation user_api.DeleteBucket has not yet been implemented")
}),
@@ -205,6 +208,8 @@ type McsAPI struct {
UserAPIBucketSetPolicyHandler user_api.BucketSetPolicyHandler
// AdminAPIConfigInfoHandler sets the operation handler for the config info operation
AdminAPIConfigInfoHandler admin_api.ConfigInfoHandler
// UserAPICreateBucketEventHandler sets the operation handler for the create bucket event operation
UserAPICreateBucketEventHandler user_api.CreateBucketEventHandler
// UserAPIDeleteBucketHandler sets the operation handler for the delete bucket operation
UserAPIDeleteBucketHandler user_api.DeleteBucketHandler
// AdminAPIGroupInfoHandler sets the operation handler for the group info operation
@@ -338,6 +343,9 @@ func (o *McsAPI) Validate() error {
if o.AdminAPIConfigInfoHandler == nil {
unregistered = append(unregistered, "admin_api.ConfigInfoHandler")
}
if o.UserAPICreateBucketEventHandler == nil {
unregistered = append(unregistered, "user_api.CreateBucketEventHandler")
}
if o.UserAPIDeleteBucketHandler == nil {
unregistered = append(unregistered, "user_api.DeleteBucketHandler")
}
@@ -525,6 +533,10 @@ func (o *McsAPI) initHandlerCache() {
o.handlers["GET"] = make(map[string]http.Handler)
}
o.handlers["GET"]["/api/v1/configs/{name}"] = admin_api.NewConfigInfo(o.context, o.AdminAPIConfigInfoHandler)
if o.handlers["POST"] == nil {
o.handlers["POST"] = make(map[string]http.Handler)
}
o.handlers["POST"]["/api/v1/buckets/{bucket_name}/events"] = user_api.NewCreateBucketEvent(o.context, o.UserAPICreateBucketEventHandler)
if o.handlers["DELETE"] == nil {
o.handlers["DELETE"] = make(map[string]http.Handler)
}

View File

@@ -0,0 +1,90 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package user_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
"github.com/go-openapi/runtime/middleware"
"github.com/minio/m3/mcs/models"
)
// CreateBucketEventHandlerFunc turns a function with the right signature into a create bucket event handler
type CreateBucketEventHandlerFunc func(CreateBucketEventParams, *models.Principal) middleware.Responder
// Handle executing the request and returning a response
func (fn CreateBucketEventHandlerFunc) Handle(params CreateBucketEventParams, principal *models.Principal) middleware.Responder {
return fn(params, principal)
}
// CreateBucketEventHandler interface for that can handle valid create bucket event params
type CreateBucketEventHandler interface {
Handle(CreateBucketEventParams, *models.Principal) middleware.Responder
}
// NewCreateBucketEvent creates a new http.Handler for the create bucket event operation
func NewCreateBucketEvent(ctx *middleware.Context, handler CreateBucketEventHandler) *CreateBucketEvent {
return &CreateBucketEvent{Context: ctx, Handler: handler}
}
/*CreateBucketEvent swagger:route POST /api/v1/buckets/{bucket_name}/events UserAPI createBucketEvent
Create Bucket Event
*/
type CreateBucketEvent struct {
Context *middleware.Context
Handler CreateBucketEventHandler
}
func (o *CreateBucketEvent) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
r = rCtx
}
var Params = NewCreateBucketEventParams()
uprinc, aCtx, err := o.Context.Authorize(r, route)
if err != nil {
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
if aCtx != nil {
r = aCtx
}
var principal *models.Principal
if uprinc != nil {
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
}
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params, principal) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View File

@@ -0,0 +1,120 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package user_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"io"
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/strfmt"
"github.com/minio/m3/mcs/models"
)
// NewCreateBucketEventParams creates a new CreateBucketEventParams object
// no default values defined in spec.
func NewCreateBucketEventParams() CreateBucketEventParams {
return CreateBucketEventParams{}
}
// CreateBucketEventParams contains all the bound params for the create bucket event operation
// typically these are obtained from a http.Request
//
// swagger:parameters CreateBucketEvent
type CreateBucketEventParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
/*
Required: true
In: body
*/
Body *models.BucketEventRequest
/*
Required: true
In: path
*/
BucketName string
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewCreateBucketEventParams() beforehand.
func (o *CreateBucketEventParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
if runtime.HasBody(r) {
defer r.Body.Close()
var body models.BucketEventRequest
if err := route.Consumer.Consume(r.Body, &body); err != nil {
if err == io.EOF {
res = append(res, errors.Required("body", "body"))
} else {
res = append(res, errors.NewParseError("body", "body", "", err))
}
} else {
// validate body object
if err := body.Validate(route.Formats); err != nil {
res = append(res, err)
}
if len(res) == 0 {
o.Body = &body
}
}
} else {
res = append(res, errors.Required("body", "body"))
}
rBucketName, rhkBucketName, _ := route.Params.GetOK("bucket_name")
if err := o.bindBucketName(rBucketName, rhkBucketName, route.Formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// bindBucketName binds and validates parameter BucketName from path.
func (o *CreateBucketEventParams) bindBucketName(rawData []string, hasKey bool, formats strfmt.Registry) error {
var raw string
if len(rawData) > 0 {
raw = rawData[len(rawData)-1]
}
// Required: true
// Parameter is provided by construction from the route
o.BucketName = raw
return nil
}

View File

@@ -0,0 +1,113 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package user_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
"github.com/minio/m3/mcs/models"
)
// CreateBucketEventCreatedCode is the HTTP code returned for type CreateBucketEventCreated
const CreateBucketEventCreatedCode int = 201
/*CreateBucketEventCreated A successful response.
swagger:response createBucketEventCreated
*/
type CreateBucketEventCreated struct {
}
// NewCreateBucketEventCreated creates CreateBucketEventCreated with default headers values
func NewCreateBucketEventCreated() *CreateBucketEventCreated {
return &CreateBucketEventCreated{}
}
// WriteResponse to the client
func (o *CreateBucketEventCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
rw.WriteHeader(201)
}
/*CreateBucketEventDefault Generic error response.
swagger:response createBucketEventDefault
*/
type CreateBucketEventDefault struct {
_statusCode int
/*
In: Body
*/
Payload *models.Error `json:"body,omitempty"`
}
// NewCreateBucketEventDefault creates CreateBucketEventDefault with default headers values
func NewCreateBucketEventDefault(code int) *CreateBucketEventDefault {
if code <= 0 {
code = 500
}
return &CreateBucketEventDefault{
_statusCode: code,
}
}
// WithStatusCode adds the status to the create bucket event default response
func (o *CreateBucketEventDefault) WithStatusCode(code int) *CreateBucketEventDefault {
o._statusCode = code
return o
}
// SetStatusCode sets the status to the create bucket event default response
func (o *CreateBucketEventDefault) SetStatusCode(code int) {
o._statusCode = code
}
// WithPayload adds the payload to the create bucket event default response
func (o *CreateBucketEventDefault) WithPayload(payload *models.Error) *CreateBucketEventDefault {
o.Payload = payload
return o
}
// SetPayload sets the payload to the create bucket event default response
func (o *CreateBucketEventDefault) SetPayload(payload *models.Error) {
o.Payload = payload
}
// WriteResponse to the client
func (o *CreateBucketEventDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(o._statusCode)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}

View File

@@ -0,0 +1,113 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package user_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"errors"
"net/url"
golangswaggerpaths "path"
"strings"
)
// CreateBucketEventURL generates an URL for the create bucket event operation
type CreateBucketEventURL struct {
BucketName string
_basePath string
// avoid unkeyed usage
_ struct{}
}
// WithBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *CreateBucketEventURL) WithBasePath(bp string) *CreateBucketEventURL {
o.SetBasePath(bp)
return o
}
// SetBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *CreateBucketEventURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *CreateBucketEventURL) Build() (*url.URL, error) {
var _result url.URL
var _path = "/api/v1/buckets/{bucket_name}/events"
bucketName := o.BucketName
if bucketName != "" {
_path = strings.Replace(_path, "{bucket_name}", bucketName, -1)
} else {
return nil, errors.New("bucketName is required on CreateBucketEventURL")
}
_basePath := o._basePath
_result.Path = golangswaggerpaths.Join(_basePath, _path)
return &_result, nil
}
// Must is a helper function to panic when the url builder returns an error
func (o *CreateBucketEventURL) Must(u *url.URL, err error) *url.URL {
if err != nil {
panic(err)
}
if u == nil {
panic("url can't be nil")
}
return u
}
// String returns the string representation of the path with query string
func (o *CreateBucketEventURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *CreateBucketEventURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on CreateBucketEventURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on CreateBucketEventURL")
}
base, err := o.Build()
if err != nil {
return nil, err
}
base.Scheme = scheme
base.Host = host
return base, nil
}
// StringFull returns the string representation of a complete url
func (o *CreateBucketEventURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}

View File

@@ -36,6 +36,12 @@ func registerBucketEventsHandlers(api *operations.McsAPI) {
}
return user_api.NewListBucketEventsOK().WithPayload(listBucketEventsResponse)
})
api.UserAPICreateBucketEventHandler = user_api.CreateBucketEventHandlerFunc(func(params user_api.CreateBucketEventParams, principal *models.Principal) middleware.Responder {
if err := getCreateBucketEventsResponse(params.BucketName, params.Body); err != nil {
return user_api.NewCreateBucketEventDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
}
return user_api.NewCreateBucketEventCreated()
})
}
// listBucketEvents fetches a list of all events set for a bucket and serializes them for a proper output
@@ -83,7 +89,7 @@ func listBucketEvents(client MinioClient, bucketName string) ([]*models.Notifica
for _, config := range bn.TopicConfigs {
prefix, suffix := getFilters(config.NotificationConfig)
configs = append(configs, &models.NotificationConfig{ID: config.ID,
Arn: config.Topic,
Arn: swag.String(config.Topic),
Events: prettyEventNames(config.Events),
Prefix: prefix,
Suffix: suffix})
@@ -91,7 +97,7 @@ func listBucketEvents(client MinioClient, bucketName string) ([]*models.Notifica
for _, config := range bn.QueueConfigs {
prefix, suffix := getFilters(config.NotificationConfig)
configs = append(configs, &models.NotificationConfig{ID: config.ID,
Arn: config.Queue,
Arn: swag.String(config.Queue),
Events: prettyEventNames(config.Events),
Prefix: prefix,
Suffix: suffix})
@@ -99,7 +105,7 @@ func listBucketEvents(client MinioClient, bucketName string) ([]*models.Notifica
for _, config := range bn.LambdaConfigs {
prefix, suffix := getFilters(config.NotificationConfig)
configs = append(configs, &models.NotificationConfig{ID: config.ID,
Arn: config.Lambda,
Arn: swag.String(config.Lambda),
Events: prettyEventNames(config.Events),
Prefix: prefix,
Suffix: suffix})
@@ -130,3 +136,50 @@ func getListBucketEventsResponse(params user_api.ListBucketEventsParams) (*model
}
return listBucketsResponse, nil
}
// createBucketEvent calls mc AddNotificationConfig() to create a bucket nofication
//
// If notificationEvents is empty, by default will set [get, put, delete], else the provided
// ones will be set.
// this function follows same behavior as minio/mc for adding a bucket event
func createBucketEvent(client MCS3Client, arn string, notificationEvents []models.NotificationEventType, prefix, suffix string, ignoreExisting bool) error {
var events []string
if len(notificationEvents) == 0 {
// default event values are [get, put, delete]
events = []string{
string(models.NotificationEventTypeGet),
string(models.NotificationEventTypePut),
string(models.NotificationEventTypeDelete),
}
} else {
// else use defined events in request
// cast type models.NotificationEventType to string
for _, e := range notificationEvents {
events = append(events, string(e))
}
}
perr := client.addNotificationConfig(arn, events, prefix, suffix, ignoreExisting)
if perr != nil {
return perr.Cause
}
return nil
}
// getCreateBucketEventsResponse calls createBucketEvent to add a bucket event notification
func getCreateBucketEventsResponse(bucketName string, eventReq *models.BucketEventRequest) error {
s3Client, err := newS3BucketClient(swag.String(bucketName))
if err != nil {
log.Println("error creating MinIO Client:", err)
return err
}
// create a minioClient interface implementation
// defining the client to be used
mcS3Client := mcS3Client{client: s3Client}
err = createBucketEvent(mcS3Client, *eventReq.Configuration.Arn, eventReq.Configuration.Events, eventReq.Configuration.Prefix, eventReq.Configuration.Suffix, eventReq.IgnoreExisting)
if err != nil {
log.Println("error creating bucket event:", err)
return err
}
return nil
}

View File

@@ -22,7 +22,9 @@ import (
"errors"
"github.com/go-openapi/swag"
"github.com/minio/m3/mcs/models"
"github.com/minio/mc/pkg/probe"
"github.com/minio/minio-go/v6"
"github.com/stretchr/testify/assert"
)
@@ -35,6 +37,61 @@ func (mc minioClientMock) getBucketNotification(bucketName string) (bucketNotifi
return minioGetBucketNotificationMock(bucketName)
}
//// Mock mc S3Client functions ////
var mcAddNotificationConfigMock func(arn string, events []string, prefix, suffix string, ignoreExisting bool) *probe.Error
// Define a mock struct of mc S3Client interface implementation
type s3ClientMock struct {
}
// implements mc.S3Client.AddNotificationConfigMock(ctx)
func (c s3ClientMock) addNotificationConfig(arn string, events []string, prefix, suffix string, ignoreExisting bool) *probe.Error {
return mcAddNotificationConfigMock(arn, events, prefix, suffix, ignoreExisting)
}
func TestAddBucketNotification(t *testing.T) {
assert := assert.New(t)
// mock minIO client
client := s3ClientMock{}
function := "createBucketEvent()"
// Test-1: createBucketEvent() set an event with empty parameters and events, should set default values with no error
testArn := "arn:minio:sqs::test:postgresql"
testNotificationEvents := []models.NotificationEventType{}
testPrefix := ""
testSuffix := ""
testIgnoreExisting := false
mcAddNotificationConfigMock = func(arn string, events []string, prefix, suffix string, ignoreExisting bool) *probe.Error {
return nil
}
if err := createBucketEvent(client, testArn, testNotificationEvents, testPrefix, testSuffix, testIgnoreExisting); err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
// Test-2: createBucketEvent() with different even types in list shouls create event with no errors
testArn = "arn:minio:sqs::test:postgresql"
testNotificationEvents = []models.NotificationEventType{
models.NotificationEventTypePut,
models.NotificationEventTypeGet,
}
testPrefix = "photos/"
testSuffix = ".jpg"
testIgnoreExisting = true
mcAddNotificationConfigMock = func(arn string, events []string, prefix, suffix string, ignoreExisting bool) *probe.Error {
return nil
}
if err := createBucketEvent(client, testArn, testNotificationEvents, testPrefix, testSuffix, testIgnoreExisting); err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
// Test-3 createBucketEvent() S3Client.AddNotificationConfig returns an error and is handled correctly
mcAddNotificationConfigMock = func(arn string, events []string, prefix, suffix string, ignoreExisting bool) *probe.Error {
return probe.NewError(errors.New("error"))
}
if err := createBucketEvent(client, testArn, testNotificationEvents, testPrefix, testSuffix, testIgnoreExisting); assert.Error(err) {
assert.Equal("error", err.Error())
}
}
func TestListBucketEvents(t *testing.T) {
assert := assert.New(t)
// mock minIO client
@@ -76,7 +133,7 @@ func TestListBucketEvents(t *testing.T) {
}
expectedOutput := []*models.NotificationConfig{
&models.NotificationConfig{
Arn: "arn:minio:sqs::test:postgresql",
Arn: swag.String("arn:minio:sqs::test:postgresql"),
ID: "",
Prefix: "file/",
Suffix: ".jpg",
@@ -125,7 +182,7 @@ func TestListBucketEvents(t *testing.T) {
}
expectedOutput = []*models.NotificationConfig{
&models.NotificationConfig{
Arn: "arn:minio:sqs::test:postgresql",
Arn: swag.String("arn:minio:sqs::test:postgresql"),
ID: "",
Prefix: "",
Suffix: "",
@@ -226,7 +283,7 @@ func TestListBucketEvents(t *testing.T) {
// order matters in output: topic,queue then lambda are given respectively
expectedOutput = []*models.NotificationConfig{
&models.NotificationConfig{
Arn: "topic",
Arn: swag.String("topic"),
ID: "",
Prefix: "topic/",
Suffix: ".gif",
@@ -235,7 +292,7 @@ func TestListBucketEvents(t *testing.T) {
},
},
&models.NotificationConfig{
Arn: "arn:minio:sqs::test:postgresql",
Arn: swag.String("arn:minio:sqs::test:postgresql"),
ID: "",
Prefix: "",
Suffix: "",
@@ -244,7 +301,7 @@ func TestListBucketEvents(t *testing.T) {
},
},
&models.NotificationConfig{
Arn: "lambda",
Arn: swag.String("lambda"),
ID: "",
Prefix: "lambda/",
Suffix: ".png",

View File

@@ -159,6 +159,28 @@ paths:
$ref: "#/definitions/error"
tags:
- UserAPI
post:
summary: Create Bucket Event
operationId: CreateBucketEvent
parameters:
- name: bucket_name
in: path
required: true
type: string
- name: body
in: body
required: true
schema:
$ref: '#/definitions/bucketEventRequest'
responses:
201:
description: A successful response.
default:
description: Generic error response.
schema:
$ref: "#/definitions/error"
tags:
- UserAPI
/api/v1/users:
get:
summary: List Users
@@ -839,6 +861,8 @@ definitions:
- get
notificationConfig:
type: object
required:
- arn
properties:
id:
type: string
@@ -855,6 +879,15 @@ definitions:
suffix:
type: string
title: "filter event associated to the specified suffix"
bucketEventRequest:
type: object
required:
- configuration
properties:
configuration:
$ref: "#/definitions/notificationConfig"
ignoreExisting:
type: boolean
listBucketEventsResponse:
type: object
properties: