diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 8f08dea1a..62803c723 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -3,10 +3,10 @@ name: Go on: pull_request: branches: - - master + - master push: branches: - - master + - master jobs: build: @@ -14,7 +14,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - go-version: [1.13.x, 1.14.x] + go-version: [1.14.x] os: [ubuntu-latest] steps: - name: Set up Go ${{ matrix.go-version }} on ${{ matrix.os }} diff --git a/cmd/console/main.go b/cmd/console/main.go index 7e4a44bb7..b47f0c5ba 100644 --- a/cmd/console/main.go +++ b/cmd/console/main.go @@ -76,21 +76,19 @@ func newApp(name string) *cli.App { findClosestCommands := func(command string) []string { var closestCommands []string - for _, value := range commandsTree.PrefixMatch(command) { - closestCommands = append(closestCommands, value.(string)) - } + closestCommands = append(closestCommands, commandsTree.PrefixMatch(command)...) sort.Strings(closestCommands) // Suggest other close commands - allow missed, wrongly added and // even transposed characters for _, value := range commandsTree.Walk(commandsTree.Root()) { - if sort.SearchStrings(closestCommands, value.(string)) < len(closestCommands) { + if sort.SearchStrings(closestCommands, value) < len(closestCommands) { continue } // 2 is arbitrary and represents the max // allowed number of typed errors - if words.DamerauLevenshteinDistance(command, value.(string)) < 2 { - closestCommands = append(closestCommands, value.(string)) + if words.DamerauLevenshteinDistance(command, value) < 2 { + closestCommands = append(closestCommands, value) } } diff --git a/go.mod b/go.mod index e786d11df..d30ac91fb 100644 --- a/go.mod +++ b/go.mod @@ -16,16 +16,16 @@ require ( github.com/jessevdk/go-flags v1.4.0 github.com/minio/cli v1.22.0 github.com/minio/kes v0.11.0 - github.com/minio/mc v0.0.0-20200901021141-b55e3e2d2987 - github.com/minio/minio v0.0.0-20200901011052-18725679c4f5 - github.com/minio/minio-go/v7 v7.0.6-0.20200901014009-5f8d15bbc5fd + github.com/minio/mc v0.0.0-20201001165056-7f2df96e4821 + github.com/minio/minio v0.0.0-20200927172404-27d9bd04e544 + github.com/minio/minio-go/v7 v7.0.6-0.20200923173112-bc846cb9b089 github.com/minio/operator v0.0.0-20200923155125-e7077234373b github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect github.com/secure-io/sio-go v0.3.1 github.com/stretchr/testify v1.6.1 github.com/unrolled/secure v1.0.7 - golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de - golang.org/x/net v0.0.0-20200707034311-ab3426394381 + golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a + golang.org/x/net v0.0.0-20200904194848-62affa334b73 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d gopkg.in/yaml.v2 v2.3.0 k8s.io/api v0.18.6 diff --git a/go.sum b/go.sum index 9c527581d..77e391201 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,7 @@ cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0 h1:EpMNVUorLiZIELdMZbCYX/ByTFCdoYopYAGxaGVz9ms= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= @@ -30,6 +31,7 @@ cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjp cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0 h1:86K1Gel7BQ9/WmNWn7dTKMvTLFzwtBe5FNqYbi9X35g= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= code.gitea.io/sdk/gitea v0.12.0/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY= contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA= @@ -43,6 +45,7 @@ git.apache.org/thrift.git v0.13.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqbl github.com/Azure/azure-amqp-common-go/v2 v2.1.0/go.mod h1:R8rea+gJRuJR6QxTir/XuEd+YuKoUiazDC/N96FiDEU= github.com/Azure/azure-pipeline-go v0.2.1 h1:OLBdZJ3yvOn2MezlWvbrBMTEUQC72zAftRZOMdj5HYo= github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= +github.com/Azure/azure-pipeline-go v0.2.2 h1:6oiIS9yaG6XCCzhgAgKFfIWyo4LLCiDhZot6ltoThhY= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= github.com/Azure/azure-sdk-for-go v29.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v30.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= @@ -52,6 +55,7 @@ github.com/Azure/azure-sdk-for-go v42.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo github.com/Azure/azure-service-bus-go v0.9.1/go.mod h1:yzBx6/BUGfjfeqbRZny9AQIbIe3AcV9WZbAdpkoXOa0= github.com/Azure/azure-storage-blob-go v0.8.0 h1:53qhf0Oxa0nOjgbDeeYPUeyiNmafAFEY95rZLK0Tj6o= github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0= +github.com/Azure/azure-storage-blob-go v0.10.0 h1:evCwGreYo3XLeBV4vSxLbLiYb6e0SzsJiXQVRGsRXxs= github.com/Azure/azure-storage-blob-go v0.10.0/go.mod h1:ep1edmW+kNQx4UfWM9heESNmQdijykocJ0YOxmMX8SE= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v12.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= @@ -172,6 +176,7 @@ github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMS github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A= github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -523,9 +528,11 @@ github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s= github.com/google/wire v0.4.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= +github.com/googleapis/gax-go v2.0.2+incompatible h1:silFMLAnr330+NRuag/VjIGF7TLp/LBrV2CJKFLWEww= github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.4 h1:hU4mGcQI4DaAYW+IbTun+2qEZVFxK0ySjQLTbS0VQKc= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.1.0 h1:rVsPeBmXbYv4If/cumu1AzZPwV58q433hvONV1UEZoI= @@ -590,6 +597,7 @@ github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es github.com/hashicorp/go-retryablehttp v0.5.4 h1:1BZvpawXoJCWX6pNtow9+rpEj+3itIlutiqnntI6jOE= github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.1 h1:DMo4fmknnz0E0evoNYnV48RjWndOsmd6OW+09R3cEP8= @@ -716,6 +724,8 @@ github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg= +github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -774,21 +784,20 @@ 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/kes v0.11.0 h1:8ma6OCVSxKT50b1uYXLJro3m7PmZtCLxBaTddQexI5k= github.com/minio/kes v0.11.0/go.mod h1:mTF1Bv8YVEtQqF/B7Felp4tLee44Pp+dgI0rhCvgNg8= -github.com/minio/mc v0.0.0-20200901021141-b55e3e2d2987 h1:yjZPpkMCZtUOVvkBhFLVa4AiQLYt/OfqQRHMjoqXt78= -github.com/minio/mc v0.0.0-20200901021141-b55e3e2d2987/go.mod h1:6P0FmQWPEojL4MxIyhR5GYsvoS3cZlaOkRk0apZRr3E= +github.com/minio/mc v0.0.0-20201001165056-7f2df96e4821 h1:+09Ta2rY29df6U4bfSC85t7jxlNuYpUfl7lIVgTiDEA= +github.com/minio/mc v0.0.0-20201001165056-7f2df96e4821/go.mod h1:+OeGPfw7qpvO1x2Td65ejwAuFT0Cxzx0GFTJSDlPMZQ= github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4= github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= github.com/minio/minio v0.0.0-20200723003940-b9be841fd222 h1:+XFGpEsqmA033nDX8LtjyPZy01Shivf6E2OL67WoGiE= github.com/minio/minio v0.0.0-20200723003940-b9be841fd222/go.mod h1:Eu2KC2p+vW03rnYY/6R/D+QduPB7/j4kBaVA/EDLjWM= -github.com/minio/minio v0.0.0-20200828210429-cd380251b39d/go.mod h1:RzAkUqX8ePyRXOSJmNzZvN+ArXRlszOu3tDKsa51Qos= -github.com/minio/minio v0.0.0-20200901011052-18725679c4f5 h1:TIjV3HutcC/xeaJ7ToAwtp++E2XofA7Hs6h7Bzy3bVo= -github.com/minio/minio v0.0.0-20200901011052-18725679c4f5/go.mod h1:53zYS6513LiG8+Co+leghfWiCj+k7kIiax32U/QXCyU= +github.com/minio/minio v0.0.0-20200927172404-27d9bd04e544 h1:G6M9uXdFShowoLG3rMkdCtHsx37ZYB1vc+7bu22r85I= +github.com/minio/minio v0.0.0-20200927172404-27d9bd04e544/go.mod h1:5uolst3SWpgvDNF+KJwfViU+j9WH7dB5i2YCi0nMNVo= github.com/minio/minio-go/v7 v7.0.1/go.mod h1:dJ80Mv2HeGkYLH1sqS/ksz07ON6csH3S6JUMSQ2zAns= github.com/minio/minio-go/v7 v7.0.2 h1:P/7wFd4KrRBHVo7AKdcqO+9ReoS+XpMjfRFoE5quH0E= github.com/minio/minio-go/v7 v7.0.2/go.mod h1:dJ80Mv2HeGkYLH1sqS/ksz07ON6csH3S6JUMSQ2zAns= github.com/minio/minio-go/v7 v7.0.5-0.20200811211821-14ed05478889/go.mod h1:CSt2ETZNs+bIIhWTse0mcZKZWMGrFU7Er7RR0TmkDYk= -github.com/minio/minio-go/v7 v7.0.6-0.20200901014009-5f8d15bbc5fd h1:38hrGmWnv+C+yBcKgAWipiwRDu7Exr0kKAfy1QArW0g= -github.com/minio/minio-go/v7 v7.0.6-0.20200901014009-5f8d15bbc5fd/go.mod h1:CSt2ETZNs+bIIhWTse0mcZKZWMGrFU7Er7RR0TmkDYk= +github.com/minio/minio-go/v7 v7.0.6-0.20200923173112-bc846cb9b089 h1:9DDs/Gc3fNHOQxQmwIFWs7YDLMTBh59r2XQ6RqEUA1I= +github.com/minio/minio-go/v7 v7.0.6-0.20200923173112-bc846cb9b089/go.mod h1:CSt2ETZNs+bIIhWTse0mcZKZWMGrFU7Er7RR0TmkDYk= github.com/minio/operator v0.0.0-20200923155125-e7077234373b h1:k9u35u4vUi9Lxl/CDYyLU/OVN5M1CG8HQsOC8XTJHxc= github.com/minio/operator v0.0.0-20200923155125-e7077234373b/go.mod h1:6lavbNo2YuJWeQR5bZYsEWdbpRCO2KrTyfQ0PtC/AN4= github.com/minio/selfupdate v0.3.1 h1:BWEFSNnrZVMUWXbXIgLDNDjbejkmpAmZvy/nCz1HlEs= @@ -797,6 +806,7 @@ github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKU github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/simdjson-go v0.1.5-0.20200303142138-b17fe061ea37 h1:pDeao6M5AEd8hwTtGmE0pVKomlL56JFRa5SiXDZAuJE= github.com/minio/simdjson-go v0.1.5-0.20200303142138-b17fe061ea37/go.mod h1:oKURrZZEBtqObgJrSjN1Ln2n9MJj2icuBTkeJzZnvSI= +github.com/minio/simdjson-go v0.1.5 h1:6T5mHh7r3kUvgwhmFWQAjoPV5Yt5oD/VPjAI9ViH1kM= github.com/minio/simdjson-go v0.1.5/go.mod h1:oKURrZZEBtqObgJrSjN1Ln2n9MJj2icuBTkeJzZnvSI= github.com/minio/sio v0.2.0 h1:NCRCFLx0r5pRbXf65LVNjxbCGZgNQvNFQkgX3XF4BoA= github.com/minio/sio v0.2.0/go.mod h1:nKM5GIWSrqbOZp0uhyj6M1iA0X6xQzSGtYSaTKSCut0= @@ -1072,6 +1082,7 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tommy-muehle/go-mnd v1.1.1/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig= github.com/tommy-muehle/go-mnd v1.3.1-0.20200224220436-e6f9a994e8fa/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= @@ -1092,6 +1103,7 @@ github.com/vdemeester/k8s-pkg-credentialprovider v1.17.4/go.mod h1:inCTmtUdr5KJb github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc= github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/willf/bloom v2.0.3+incompatible h1:QDacWdqcAUI1MPOwIQZRy9kOR7yxfyEmxX8Wdm2/JPA= github.com/willf/bloom v2.0.3+incompatible/go.mod h1:MmAltL9pDMNTrvUkxdg0k0q5I0suxmuwp3KbyrZLOZ8= @@ -1109,6 +1121,7 @@ github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1: github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= @@ -1128,6 +1141,7 @@ go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -1172,8 +1186,8 @@ golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg= golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig= -golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1254,6 +1268,9 @@ golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1332,8 +1349,8 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200806125547-5acd03effb82 h1:6cBnXxYO+CiRVrChvCosSv7magqTPbyAgz1M8iOv5wM= -golang.org/x/sys v0.0.0-20200806125547-5acd03effb82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200915084602-288bc346aa39 h1:356XA7ITklAU2//sYkjFeco+dH1bCRD8XCJ9FIEsvo4= +golang.org/x/sys v0.0.0-20200915084602-288bc346aa39/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1427,10 +1444,14 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d h1:szSOL78iTCl0LF1AMjhSWJj golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200814172026-c4923e618c08 h1:sfBQLM20fzeXhOixVQirwEbuW4PGStP773EXQpsBB6E= golang.org/x/tools v0.0.0-20200814172026-c4923e618c08/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200929223013-bf155c11ec6f h1:7+Nz9MyPqt2qMCTvNiRy1G0zYfkB7UCa+ayT6uVvbyI= +golang.org/x/tools v0.0.0-20200929223013-bf155c11ec6f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= @@ -1451,6 +1472,7 @@ google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/ google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.25.0 h1:LodzhlzZEUfhXzNUMIfVlf9Gr6Ua5MMtoFWh7+f47qA= google.golang.org/api v0.25.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/restapi/client.go b/restapi/client.go index 176b152a4..af453a390 100644 --- a/restapi/client.go +++ b/restapi/client.go @@ -123,6 +123,8 @@ type MCClient interface { addNotificationConfig(ctx context.Context, arn string, events []string, prefix, suffix string, ignoreExisting bool) *probe.Error removeNotificationConfig(ctx context.Context, arn string, event string, prefix string, suffix string) *probe.Error watch(ctx context.Context, options mc.WatchOptions) (*mc.WatchObject, *probe.Error) + remove(ctx context.Context, isIncomplete, isRemoveBucket, isBypass bool, contentCh <-chan *mc.ClientContent) <-chan *probe.Error + list(ctx context.Context, opts mc.ListOptions) <-chan *mc.ClientContent } // Interface implementation @@ -151,6 +153,14 @@ func (c mcClient) setReplication(ctx context.Context, cfg *replication.Config, o return c.client.SetReplication(ctx, cfg, opts) } +func (c mcClient) remove(ctx context.Context, isIncomplete, isRemoveBucket, isBypass bool, contentCh <-chan *mc.ClientContent) <-chan *probe.Error { + return c.client.Remove(ctx, isIncomplete, isRemoveBucket, isBypass, contentCh) +} + +func (c mcClient) list(ctx context.Context, opts mc.ListOptions) <-chan *mc.ClientContent { + return c.client.List(ctx, opts) +} + // ConsoleCredentials interface with all functions to be implemented // by mock when testing, it should include all needed consoleCredentials.Login api calls // that are used within this project. @@ -274,7 +284,7 @@ func newMinioClient(claims *models.Principal) (*minio.Client, error) { } // newS3BucketClient creates a new mc S3Client to talk to the server based on a bucket -func newS3BucketClient(claims *models.Principal, bucketName string) (*mc.S3Client, error) { +func newS3BucketClient(claims *models.Principal, bucketName string, prefix string) (*mc.S3Client, error) { endpoint := getMinIOServer() useTLS := getMinIOEndpointIsSecure() @@ -282,6 +292,10 @@ func newS3BucketClient(claims *models.Principal, bucketName string) (*mc.S3Clien endpoint += fmt.Sprintf("/%s", bucketName) } + if strings.TrimSpace(prefix) != "" { + endpoint += fmt.Sprintf("/%s", prefix) + } + if claims == nil { return nil, fmt.Errorf("the provided credentials are invalid") } diff --git a/restapi/embedded_spec.go b/restapi/embedded_spec.go index 957ba14da..123f263f3 100644 --- a/restapi/embedded_spec.go +++ b/restapi/embedded_spec.go @@ -380,6 +380,48 @@ func init() { } } } + }, + "delete": { + "tags": [ + "UserAPI" + ], + "summary": "Delete Object", + "operationId": "DeleteObject", + "parameters": [ + { + "type": "string", + "name": "bucket_name", + "in": "path", + "required": true + }, + { + "type": "string", + "name": "path", + "in": "query", + "required": true + }, + { + "type": "string", + "name": "version_id", + "in": "query" + }, + { + "type": "boolean", + "name": "recursive", + "in": "query" + } + ], + "responses": { + "200": { + "description": "A successful response." + }, + "default": { + "description": "Generic error response.", + "schema": { + "$ref": "#/definitions/error" + } + } + } } }, "/buckets/{bucket_name}/replication": { @@ -4620,6 +4662,48 @@ func init() { } } } + }, + "delete": { + "tags": [ + "UserAPI" + ], + "summary": "Delete Object", + "operationId": "DeleteObject", + "parameters": [ + { + "type": "string", + "name": "bucket_name", + "in": "path", + "required": true + }, + { + "type": "string", + "name": "path", + "in": "query", + "required": true + }, + { + "type": "string", + "name": "version_id", + "in": "query" + }, + { + "type": "boolean", + "name": "recursive", + "in": "query" + } + ], + "responses": { + "200": { + "description": "A successful response." + }, + "default": { + "description": "Generic error response.", + "schema": { + "$ref": "#/definitions/error" + } + } + } } }, "/buckets/{bucket_name}/replication": { diff --git a/restapi/operations/console_api.go b/restapi/operations/console_api.go index 43967c93a..92a035807 100644 --- a/restapi/operations/console_api.go +++ b/restapi/operations/console_api.go @@ -114,6 +114,9 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI { UserAPIDeleteBucketEventHandler: user_api.DeleteBucketEventHandlerFunc(func(params user_api.DeleteBucketEventParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation user_api.DeleteBucketEvent has not yet been implemented") }), + UserAPIDeleteObjectHandler: user_api.DeleteObjectHandlerFunc(func(params user_api.DeleteObjectParams, principal *models.Principal) middleware.Responder { + return middleware.NotImplemented("operation user_api.DeleteObject has not yet been implemented") + }), UserAPIDeleteRemoteBucketHandler: user_api.DeleteRemoteBucketHandlerFunc(func(params user_api.DeleteRemoteBucketParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation user_api.DeleteRemoteBucket has not yet been implemented") }), @@ -347,6 +350,8 @@ type ConsoleAPI struct { UserAPIDeleteBucketHandler user_api.DeleteBucketHandler // UserAPIDeleteBucketEventHandler sets the operation handler for the delete bucket event operation UserAPIDeleteBucketEventHandler user_api.DeleteBucketEventHandler + // UserAPIDeleteObjectHandler sets the operation handler for the delete object operation + UserAPIDeleteObjectHandler user_api.DeleteObjectHandler // UserAPIDeleteRemoteBucketHandler sets the operation handler for the delete remote bucket operation UserAPIDeleteRemoteBucketHandler user_api.DeleteRemoteBucketHandler // UserAPIDeleteServiceAccountHandler sets the operation handler for the delete service account operation @@ -571,6 +576,9 @@ func (o *ConsoleAPI) Validate() error { if o.UserAPIDeleteBucketEventHandler == nil { unregistered = append(unregistered, "user_api.DeleteBucketEventHandler") } + if o.UserAPIDeleteObjectHandler == nil { + unregistered = append(unregistered, "user_api.DeleteObjectHandler") + } if o.UserAPIDeleteRemoteBucketHandler == nil { unregistered = append(unregistered, "user_api.DeleteRemoteBucketHandler") } @@ -892,6 +900,10 @@ func (o *ConsoleAPI) initHandlerCache() { if o.handlers["DELETE"] == nil { o.handlers["DELETE"] = make(map[string]http.Handler) } + o.handlers["DELETE"]["/buckets/{bucket_name}/objects"] = user_api.NewDeleteObject(o.context, o.UserAPIDeleteObjectHandler) + if o.handlers["DELETE"] == nil { + o.handlers["DELETE"] = make(map[string]http.Handler) + } o.handlers["DELETE"]["/remote-buckets/{source-bucket-name}/{arn}"] = user_api.NewDeleteRemoteBucket(o.context, o.UserAPIDeleteRemoteBucketHandler) if o.handlers["DELETE"] == nil { o.handlers["DELETE"] = make(map[string]http.Handler) diff --git a/restapi/operations/user_api/delete_object.go b/restapi/operations/user_api/delete_object.go new file mode 100644 index 000000000..0b0460a8f --- /dev/null +++ b/restapi/operations/user_api/delete_object.go @@ -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 . +// + +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/console/models" +) + +// DeleteObjectHandlerFunc turns a function with the right signature into a delete object handler +type DeleteObjectHandlerFunc func(DeleteObjectParams, *models.Principal) middleware.Responder + +// Handle executing the request and returning a response +func (fn DeleteObjectHandlerFunc) Handle(params DeleteObjectParams, principal *models.Principal) middleware.Responder { + return fn(params, principal) +} + +// DeleteObjectHandler interface for that can handle valid delete object params +type DeleteObjectHandler interface { + Handle(DeleteObjectParams, *models.Principal) middleware.Responder +} + +// NewDeleteObject creates a new http.Handler for the delete object operation +func NewDeleteObject(ctx *middleware.Context, handler DeleteObjectHandler) *DeleteObject { + return &DeleteObject{Context: ctx, Handler: handler} +} + +/*DeleteObject swagger:route DELETE /buckets/{bucket_name}/objects UserAPI deleteObject + +Delete Object + +*/ +type DeleteObject struct { + Context *middleware.Context + Handler DeleteObjectHandler +} + +func (o *DeleteObject) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + r = rCtx + } + var Params = NewDeleteObjectParams() + + 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) + +} diff --git a/restapi/operations/user_api/delete_object_parameters.go b/restapi/operations/user_api/delete_object_parameters.go new file mode 100644 index 000000000..85e86aee1 --- /dev/null +++ b/restapi/operations/user_api/delete_object_parameters.go @@ -0,0 +1,183 @@ +// 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 . +// + +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/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// NewDeleteObjectParams creates a new DeleteObjectParams object +// no default values defined in spec. +func NewDeleteObjectParams() DeleteObjectParams { + + return DeleteObjectParams{} +} + +// DeleteObjectParams contains all the bound params for the delete object operation +// typically these are obtained from a http.Request +// +// swagger:parameters DeleteObject +type DeleteObjectParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + Required: true + In: path + */ + BucketName string + /* + Required: true + In: query + */ + Path string + /* + In: query + */ + Recursive *bool + /* + In: query + */ + VersionID *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 NewDeleteObjectParams() beforehand. +func (o *DeleteObjectParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + qs := runtime.Values(r.URL.Query()) + + rBucketName, rhkBucketName, _ := route.Params.GetOK("bucket_name") + if err := o.bindBucketName(rBucketName, rhkBucketName, route.Formats); err != nil { + res = append(res, err) + } + + qPath, qhkPath, _ := qs.GetOK("path") + if err := o.bindPath(qPath, qhkPath, route.Formats); err != nil { + res = append(res, err) + } + + qRecursive, qhkRecursive, _ := qs.GetOK("recursive") + if err := o.bindRecursive(qRecursive, qhkRecursive, route.Formats); err != nil { + res = append(res, err) + } + + qVersionID, qhkVersionID, _ := qs.GetOK("version_id") + if err := o.bindVersionID(qVersionID, qhkVersionID, 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 *DeleteObjectParams) 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 +} + +// bindPath binds and validates parameter Path from query. +func (o *DeleteObjectParams) bindPath(rawData []string, hasKey bool, formats strfmt.Registry) error { + if !hasKey { + return errors.Required("path", "query", rawData) + } + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: true + // AllowEmptyValue: false + if err := validate.RequiredString("path", "query", raw); err != nil { + return err + } + + o.Path = raw + + return nil +} + +// bindRecursive binds and validates parameter Recursive from query. +func (o *DeleteObjectParams) bindRecursive(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + if raw == "" { // empty values pass all other validations + return nil + } + + value, err := swag.ConvertBool(raw) + if err != nil { + return errors.InvalidType("recursive", "query", "bool", raw) + } + o.Recursive = &value + + return nil +} + +// bindVersionID binds and validates parameter VersionID from query. +func (o *DeleteObjectParams) bindVersionID(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + if raw == "" { // empty values pass all other validations + return nil + } + + o.VersionID = &raw + + return nil +} diff --git a/restapi/operations/user_api/delete_object_responses.go b/restapi/operations/user_api/delete_object_responses.go new file mode 100644 index 000000000..8f94ea48c --- /dev/null +++ b/restapi/operations/user_api/delete_object_responses.go @@ -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 . +// + +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/console/models" +) + +// DeleteObjectOKCode is the HTTP code returned for type DeleteObjectOK +const DeleteObjectOKCode int = 200 + +/*DeleteObjectOK A successful response. + +swagger:response deleteObjectOK +*/ +type DeleteObjectOK struct { +} + +// NewDeleteObjectOK creates DeleteObjectOK with default headers values +func NewDeleteObjectOK() *DeleteObjectOK { + + return &DeleteObjectOK{} +} + +// WriteResponse to the client +func (o *DeleteObjectOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(200) +} + +/*DeleteObjectDefault Generic error response. + +swagger:response deleteObjectDefault +*/ +type DeleteObjectDefault struct { + _statusCode int + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewDeleteObjectDefault creates DeleteObjectDefault with default headers values +func NewDeleteObjectDefault(code int) *DeleteObjectDefault { + if code <= 0 { + code = 500 + } + + return &DeleteObjectDefault{ + _statusCode: code, + } +} + +// WithStatusCode adds the status to the delete object default response +func (o *DeleteObjectDefault) WithStatusCode(code int) *DeleteObjectDefault { + o._statusCode = code + return o +} + +// SetStatusCode sets the status to the delete object default response +func (o *DeleteObjectDefault) SetStatusCode(code int) { + o._statusCode = code +} + +// WithPayload adds the payload to the delete object default response +func (o *DeleteObjectDefault) WithPayload(payload *models.Error) *DeleteObjectDefault { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the delete object default response +func (o *DeleteObjectDefault) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *DeleteObjectDefault) 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 + } + } +} diff --git a/restapi/operations/user_api/delete_object_urlbuilder.go b/restapi/operations/user_api/delete_object_urlbuilder.go new file mode 100644 index 000000000..b45ed9ba8 --- /dev/null +++ b/restapi/operations/user_api/delete_object_urlbuilder.go @@ -0,0 +1,147 @@ +// 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 . +// + +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" + + "github.com/go-openapi/swag" +) + +// DeleteObjectURL generates an URL for the delete object operation +type DeleteObjectURL struct { + BucketName string + + Path string + Recursive *bool + VersionID *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 *DeleteObjectURL) WithBasePath(bp string) *DeleteObjectURL { + 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 *DeleteObjectURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *DeleteObjectURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/buckets/{bucket_name}/objects" + + bucketName := o.BucketName + if bucketName != "" { + _path = strings.Replace(_path, "{bucket_name}", bucketName, -1) + } else { + return nil, errors.New("bucketName is required on DeleteObjectURL") + } + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/api/v1" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + qs := make(url.Values) + + pathQ := o.Path + if pathQ != "" { + qs.Set("path", pathQ) + } + + var recursiveQ string + if o.Recursive != nil { + recursiveQ = swag.FormatBool(*o.Recursive) + } + if recursiveQ != "" { + qs.Set("recursive", recursiveQ) + } + + var versionIDQ string + if o.VersionID != nil { + versionIDQ = *o.VersionID + } + if versionIDQ != "" { + qs.Set("version_id", versionIDQ) + } + + _result.RawQuery = qs.Encode() + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *DeleteObjectURL) 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 *DeleteObjectURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *DeleteObjectURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on DeleteObjectURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on DeleteObjectURL") + } + + 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 *DeleteObjectURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/restapi/user_buckets.go b/restapi/user_buckets.go index 4127cbad7..80a7d8467 100644 --- a/restapi/user_buckets.go +++ b/restapi/user_buckets.go @@ -132,7 +132,7 @@ func getAddBucketReplicationdResponse(session *models.Principal, bucketName stri } maxPrio++ - s3Client, err := newS3BucketClient(session, bucketName) + s3Client, err := newS3BucketClient(session, bucketName, "") if err != nil { log.Println("error creating S3Client:", err) return err diff --git a/restapi/user_buckets_events.go b/restapi/user_buckets_events.go index cac6e6240..e6674d797 100644 --- a/restapi/user_buckets_events.go +++ b/restapi/user_buckets_events.go @@ -177,7 +177,7 @@ func createBucketEvent(ctx context.Context, client MCClient, arn string, notific // getCreateBucketEventsResponse calls createBucketEvent to add a bucket event notification func getCreateBucketEventsResponse(session *models.Principal, bucketName string, eventReq *models.BucketEventRequest) *models.Error { ctx := context.Background() - s3Client, err := newS3BucketClient(session, bucketName) + s3Client, err := newS3BucketClient(session, bucketName, "") if err != nil { return prepareError(err) } @@ -212,7 +212,7 @@ func joinNotificationEvents(events []models.NotificationEventType) string { // getDeleteBucketEventsResponse calls deleteBucketEventNotification() to delete a bucket event notification func getDeleteBucketEventsResponse(session *models.Principal, bucketName string, arn string, events []models.NotificationEventType, prefix, suffix *string) *models.Error { ctx := context.Background() - s3Client, err := newS3BucketClient(session, bucketName) + s3Client, err := newS3BucketClient(session, bucketName, "") if err != nil { return prepareError(err) } diff --git a/restapi/user_objects.go b/restapi/user_objects.go index 6bbb91144..a0a93ae37 100644 --- a/restapi/user_objects.go +++ b/restapi/user_objects.go @@ -18,15 +18,26 @@ package restapi import ( "context" + "fmt" + "path/filepath" + "regexp" + "strings" "time" "github.com/go-openapi/runtime/middleware" "github.com/minio/console/models" "github.com/minio/console/restapi/operations" "github.com/minio/console/restapi/operations/user_api" + mc "github.com/minio/mc/cmd" "github.com/minio/minio-go/v7" ) +// enum types +const ( + objectStorage = iota // MinIO and S3 compatible cloud storage + fileSystem // POSIX compatible file systems +) + func registerObjectsHandlers(api *operations.ConsoleAPI) { // list objects api.UserAPIListObjectsHandler = user_api.ListObjectsHandlerFunc(func(params user_api.ListObjectsParams, session *models.Principal) middleware.Responder { @@ -36,24 +47,13 @@ func registerObjectsHandlers(api *operations.ConsoleAPI) { } return user_api.NewListObjectsOK().WithPayload(resp) }) -} - -// listBucketObjects gets an array of objects in a bucket -func listBucketObjects(ctx context.Context, client MinioClient, bucketName string, prefix string, recursive bool) ([]*models.BucketObject, error) { - var objects []*models.BucketObject - for lsObj := range client.listObjects(ctx, bucketName, minio.ListObjectsOptions{Prefix: prefix, Recursive: recursive}) { - if lsObj.Err != nil { - return nil, lsObj.Err + // delete object + api.UserAPIDeleteObjectHandler = user_api.DeleteObjectHandlerFunc(func(params user_api.DeleteObjectParams, session *models.Principal) middleware.Responder { + if err := getDeleteObjectResponse(session, params); err != nil { + return user_api.NewDeleteObjectDefault(int(err.Code)).WithPayload(err) } - obj := &models.BucketObject{ - Name: lsObj.Key, - Size: lsObj.Size, - LastModified: lsObj.LastModified.String(), - ContentType: lsObj.ContentType, - } - objects = append(objects, obj) - } - return objects, nil + return user_api.NewDeleteObjectOK() + }) } // getListObjectsResponse returns a list of objects @@ -91,3 +91,214 @@ func getListObjectsResponse(session *models.Principal, params user_api.ListObjec } return resp, nil } + +// listBucketObjects gets an array of objects in a bucket +func listBucketObjects(ctx context.Context, client MinioClient, bucketName string, prefix string, recursive bool) ([]*models.BucketObject, error) { + var objects []*models.BucketObject + for lsObj := range client.listObjects(ctx, bucketName, minio.ListObjectsOptions{Prefix: prefix, Recursive: recursive}) { + if lsObj.Err != nil { + return nil, lsObj.Err + } + obj := &models.BucketObject{ + Name: lsObj.Key, + Size: lsObj.Size, + LastModified: lsObj.LastModified.String(), + ContentType: lsObj.ContentType, + } + objects = append(objects, obj) + } + return objects, nil +} + +// getDeleteObjectResponse returns whether there was an error on deletion of object +func getDeleteObjectResponse(session *models.Principal, params user_api.DeleteObjectParams) *models.Error { + ctx := context.Background() + s3Client, err := newS3BucketClient(session, params.BucketName, params.Path) + if err != nil { + return prepareError(err) + } + // create a mc S3Client interface implementation + // defining the client to be used + mcClient := mcClient{client: s3Client} + var rec bool + var version string + if params.Recursive != nil { + rec = *params.Recursive + } + if params.VersionID != nil { + version = *params.VersionID + } + err = deleteObjects(ctx, mcClient, params.BucketName, params.Path, version, rec) + if err != nil { + return prepareError(err) + } + return nil +} + +// deleteObjects deletes either a single object or multiple objects based on recursive flag +func deleteObjects(ctx context.Context, client MCClient, bucket, path string, versionID string, recursive bool) error { + if recursive { + if err := deleteMultipleObjects(ctx, client, recursive); err != nil { + return err + } + } else { + if err := deleteSingleObject(ctx, client, bucket, path, versionID); err != nil { + return err + } + } + return nil +} + +// deleteMultipleObjects uses listing before removal, it can list recursively or not, +// Use cases: +// * Remove objects recursively +func deleteMultipleObjects(ctx context.Context, client MCClient, recursive bool) error { + isRemoveBucket := false + isIncomplete := false + isBypass := false + listOpts := mc.ListOptions{IsRecursive: recursive, IsIncomplete: isIncomplete, ShowDir: mc.DirNone} + // TODO: support older Versions + contentCh := make(chan *mc.ClientContent, 1) + + errorCh := client.remove(ctx, isIncomplete, isRemoveBucket, isBypass, contentCh) + for content := range client.list(ctx, listOpts) { + if content.Err != nil { + switch content.Err.ToGoError().(type) { + // ignore same as mc + case mc.PathInsufficientPermission: + // Ignore Permission error. + continue + } + close(contentCh) + return content.Err.Cause + } + sent := false + for !sent { + select { + case contentCh <- content: + sent = true + case pErr := <-errorCh: + switch pErr.ToGoError().(type) { + // ignore same as mc + case mc.PathInsufficientPermission: + // Ignore Permission error. + continue + } + close(contentCh) + return pErr.Cause + } + } + } + close(contentCh) + for pErr := range errorCh { + if pErr != nil { + switch pErr.ToGoError().(type) { + // ignore same as mc + case mc.PathInsufficientPermission: + // Ignore Permission error. + continue + } + return pErr.Cause + } + } + return nil + +} + +func deleteSingleObject(ctx context.Context, client MCClient, bucket, object string, versionID string) error { + targetURL := fmt.Sprintf("%s/%s", bucket, object) + contentCh := make(chan *mc.ClientContent, 1) + contentCh <- &mc.ClientContent{URL: *newClientURL(targetURL), VersionID: versionID} + close(contentCh) + + isRemoveBucket := false + isIncomplete := false + isBypass := false + + errorCh := client.remove(ctx, isIncomplete, isRemoveBucket, isBypass, contentCh) + for pErr := range errorCh { + if pErr != nil { + switch pErr.ToGoError().(type) { + // ignore same as mc + case mc.PathInsufficientPermission: + // Ignore Permission error. + continue + } + return pErr.Cause + } + } + return nil +} + +// newClientURL returns an abstracted URL for filesystems and object storage. +func newClientURL(urlStr string) *mc.ClientURL { + scheme, rest := getScheme(urlStr) + if strings.HasPrefix(rest, "//") { + // if rest has '//' prefix, skip them + var authority string + authority, rest = splitSpecial(rest[2:], "/", false) + if rest == "" { + rest = "/" + } + host := getHost(authority) + if host != "" && (scheme == "http" || scheme == "https") { + return &mc.ClientURL{ + Scheme: scheme, + Type: objectStorage, + Host: host, + Path: rest, + SchemeSeparator: "://", + Separator: '/', + } + } + } + return &mc.ClientURL{ + Type: fileSystem, + Path: rest, + Separator: filepath.Separator, + } +} + +// Maybe rawurl is of the form scheme:path. (Scheme must be [a-zA-Z][a-zA-Z0-9+-.]*) +// If so, return scheme, path; else return "", rawurl. +func getScheme(rawurl string) (scheme, path string) { + urlSplits := strings.Split(rawurl, "://") + if len(urlSplits) == 2 { + scheme, uri := urlSplits[0], "//"+urlSplits[1] + // ignore numbers in scheme + validScheme := regexp.MustCompile("^[a-zA-Z]+$") + if uri != "" { + if validScheme.MatchString(scheme) { + return scheme, uri + } + } + } + return "", rawurl +} + +// Assuming s is of the form [s delimiter s]. +// If so, return s, [delimiter]s or return s, s if cutdelimiter == true +// If no delimiter found return s, "". +func splitSpecial(s string, delimiter string, cutdelimiter bool) (string, string) { + i := strings.Index(s, delimiter) + if i < 0 { + // if delimiter not found return as is. + return s, "" + } + // if delimiter should be removed, remove it. + if cutdelimiter { + return s[0:i], s[i+len(delimiter):] + } + // return split strings with delimiter + return s[0:i], s[i:] +} + +// getHost - extract host from authority string, we do not support ftp style username@ yet. +func getHost(authority string) (host string) { + i := strings.LastIndex(authority, "@") + if i >= 0 { + // TODO support, username@password style userinfo, useful for ftp support. + return + } + return authority +} diff --git a/restapi/user_objects_test.go b/restapi/user_objects_test.go index 3e3c84c08..5237ebabc 100644 --- a/restapi/user_objects_test.go +++ b/restapi/user_objects_test.go @@ -25,17 +25,30 @@ import ( "time" "github.com/minio/console/models" + mc "github.com/minio/mc/cmd" + "github.com/minio/mc/pkg/probe" "github.com/minio/minio-go/v7" ) var minioListObjectsMock func(ctx context.Context, bucket string, opts minio.ListObjectsOptions) <-chan minio.ObjectInfo +var mcListMock func(ctx context.Context, opts mc.ListOptions) <-chan *mc.ClientContent +var mcRemoveMock func(ctx context.Context, isIncomplete, isRemoveBucket, isBypass bool, contentCh <-chan *mc.ClientContent) <-chan *probe.Error // mock function of listObjects() needed for list objects - func (ac minioClientMock) listObjects(ctx context.Context, bucket string, opts minio.ListObjectsOptions) <-chan minio.ObjectInfo { return minioListObjectsMock(ctx, bucket, opts) } +// implements mc.S3Client.List() +func (c s3ClientMock) list(ctx context.Context, opts mc.ListOptions) <-chan *mc.ClientContent { + return mcListMock(ctx, opts) +} + +// implements mc.S3Client.Remove() +func (c s3ClientMock) remove(ctx context.Context, isIncomplete, isRemoveBucket, isBypass bool, contentCh <-chan *mc.ClientContent) <-chan *probe.Error { + return mcRemoveMock(ctx, isIncomplete, isRemoveBucket, isBypass, contentCh) +} + func Test_listObjects(t *testing.T) { ctx := context.Background() t1 := time.Now() @@ -160,3 +173,168 @@ func Test_listObjects(t *testing.T) { }) } } + +func Test_deleteObjects(t *testing.T) { + ctx := context.Background() + client := s3ClientMock{} + type args struct { + bucket string + path string + versionID string + recursive bool + listFunc func(ctx context.Context, opts mc.ListOptions) <-chan *mc.ClientContent + removeFunc func(ctx context.Context, isIncomplete, isRemoveBucket, isBypass bool, contentCh <-chan *mc.ClientContent) <-chan *probe.Error + } + tests := []struct { + test string + args args + wantError error + }{ + { + test: "Remove single object", + args: args{ + path: "obj.txt", + versionID: "", + recursive: false, + removeFunc: func(ctx context.Context, isIncomplete, isRemoveBucket, isBypass bool, contentCh <-chan *mc.ClientContent) <-chan *probe.Error { + errorCh := make(chan *probe.Error) + go func() { + defer close(errorCh) + for { + return + } + }() + return errorCh + }, + }, + wantError: nil, + }, + { + test: "Error on Remove single object", + args: args{ + path: "obj.txt", + versionID: "", + recursive: false, + removeFunc: func(ctx context.Context, isIncomplete, isRemoveBucket, isBypass bool, contentCh <-chan *mc.ClientContent) <-chan *probe.Error { + errorCh := make(chan *probe.Error) + go func() { + defer close(errorCh) + for { + errorCh <- probe.NewError(errors.New("probe error")) + return + } + }() + return errorCh + }, + }, + wantError: errors.New("probe error"), + }, + { + test: "Remove multiple objects", + args: args{ + path: "path/", + versionID: "", + recursive: true, + removeFunc: func(ctx context.Context, isIncomplete, isRemoveBucket, isBypass bool, contentCh <-chan *mc.ClientContent) <-chan *probe.Error { + errorCh := make(chan *probe.Error) + go func() { + defer close(errorCh) + for { + return + } + }() + return errorCh + }, + listFunc: func(ctx context.Context, opts mc.ListOptions) <-chan *mc.ClientContent { + ch := make(chan *mc.ClientContent) + go func() { + defer close(ch) + for { + ch <- &mc.ClientContent{} + return + } + }() + return ch + }, + }, + wantError: nil, + }, + { + // Description handle error when error happens on list function + // while deleting multiple objects + test: "Error on Remove multiple objects 1", + args: args{ + path: "path/", + versionID: "", + recursive: true, + removeFunc: func(ctx context.Context, isIncomplete, isRemoveBucket, isBypass bool, contentCh <-chan *mc.ClientContent) <-chan *probe.Error { + errorCh := make(chan *probe.Error) + go func() { + defer close(errorCh) + for { + return + } + }() + return errorCh + }, + listFunc: func(ctx context.Context, opts mc.ListOptions) <-chan *mc.ClientContent { + ch := make(chan *mc.ClientContent) + go func() { + defer close(ch) + for { + ch <- &mc.ClientContent{Err: probe.NewError(errors.New("probe error"))} + return + } + }() + return ch + }, + }, + wantError: errors.New("probe error"), + }, + { + // Description handle error when error happens on remove function + // while deleting multiple objects + test: "Error on Remove multiple objects 2", + args: args{ + path: "path/", + versionID: "", + recursive: true, + removeFunc: func(ctx context.Context, isIncomplete, isRemoveBucket, isBypass bool, contentCh <-chan *mc.ClientContent) <-chan *probe.Error { + errorCh := make(chan *probe.Error) + go func() { + defer close(errorCh) + for { + errorCh <- probe.NewError(errors.New("probe error")) + return + } + }() + return errorCh + }, + listFunc: func(ctx context.Context, opts mc.ListOptions) <-chan *mc.ClientContent { + ch := make(chan *mc.ClientContent) + go func() { + defer close(ch) + for { + ch <- &mc.ClientContent{} + return + } + }() + return ch + }, + }, + wantError: errors.New("probe error"), + }, + } + + for _, tt := range tests { + t.Run(tt.test, func(t *testing.T) { + mcListMock = tt.args.listFunc + mcRemoveMock = tt.args.removeFunc + err := deleteObjects(ctx, client, tt.args.bucket, tt.args.path, tt.args.versionID, tt.args.recursive) + if !reflect.DeepEqual(err, tt.wantError) { + t.Errorf("deleteObjects() error: %v, wantErr: %v", err, tt.wantError) + return + } + }) + } +} diff --git a/restapi/ws_handle.go b/restapi/ws_handle.go index d8160d740..59187496f 100644 --- a/restapi/ws_handle.go +++ b/restapi/ws_handle.go @@ -185,7 +185,7 @@ func newWebSocketAdminClient(conn *websocket.Conn, autClaims *models.Principal) func newWebSocketS3Client(conn *websocket.Conn, claims *models.Principal, bucketName string) (*wsS3Client, error) { // Only start Websocket Interaction after user has been // authenticated with MinIO - s3Client, err := newS3BucketClient(claims, bucketName) + s3Client, err := newS3BucketClient(claims, bucketName, "") if err != nil { log.Println("error creating S3Client:", err) conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) diff --git a/swagger.yml b/swagger.yml index 1766688a3..78744beb7 100644 --- a/swagger.yml +++ b/swagger.yml @@ -251,6 +251,35 @@ paths: $ref: "#/definitions/error" tags: - UserAPI + delete: + summary: Delete Object + operationId: DeleteObject + parameters: + - name: bucket_name + in: path + required: true + type: string + - name: path + in: query + required: true + type: string + - name: version_id + in: query + required: false + type: string + - name: recursive + in: query + required: false + type: boolean + responses: + 200: + description: A successful response. + default: + description: Generic error response. + schema: + $ref: "#/definitions/error" + tags: + - UserAPI /buckets/{name}/set-policy: put: