diff --git a/.travis.yml b/.travis.yml index 979a61bed..2052a4fd6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,9 @@ go_import_path: github.com/minio/minio sudo: required +services: + - docker + dist: trusty language: go @@ -13,12 +16,25 @@ env: - ARCH=i686 script: +## Run all the tests - make -- make test GOFLAGS="-timeout 20m -race -v" +- make test GOFLAGS="-timeout 15m -race -v" - make coverage +# Refer https://blog.hypriot.com/post/setup-simple-ci-pipeline-for-arm-images/ +# push image +- > + if [ "$TRAVIS_BRANCH" == "master" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$ARCH" == "x86_64" ]; then + docker run --rm --privileged multiarch/qemu-user-static:register --reset + docker build -t minio/minio:edge-armhf . -f Dockerfile.armhf + docker build -t minio/minio:edge-aarch64 . -f Dockerfile.aarch64 + docker login -u="$DOCKER_USER" -p="$DOCKER_PASS" + docker push minio/minio:edge-armhf + docker push minio/minio:edge-aarch64 + fi + after_success: - bash <(curl -s https://codecov.io/bash) go: -- 1.7.4 +- 1.7.5 diff --git a/Dockerfile b/Dockerfile index b37ca0d04..4dbfc540c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,18 @@ -FROM golang:1.7-alpine +FROM alpine:3.5 -WORKDIR /go/src/app +ENV GOPATH /go +ENV PATH $PATH:$GOPATH/bin +ENV CGO_ENABLED 0 -COPY . /go/src/app +WORKDIR /go/src/github.com/minio/ -RUN \ - apk add --no-cache git && \ - go-wrapper download && \ - go-wrapper install -ldflags "-X github.com/minio/minio/cmd.Version=2017-02-16T01:47:30Z -X github.com/minio/minio/cmd.ReleaseTag=RELEASE.2017-02-16T01-47-30Z -X github.com/minio/minio/cmd.CommitID=3d98311d9f4ceb78dba996dcdc0751253241e697" && \ - mkdir -p /export/docker && \ - rm -rf /go/pkg /go/src && \ - apk del git +RUN \ + apk add --no-cache ca-certificates && \ + apk add --no-cache --virtual .build-deps git go musl-dev && \ + go get -v -d github.com/minio/minio && \ + cd /go/src/github.com/minio/minio && \ + go install -v -ldflags "$(go run buildscripts/gen-ldflags.go)" && \ + rm -rf /go/pkg /go/src /usr/local/go && apk del .build-deps EXPOSE 9000 ENTRYPOINT ["minio"] diff --git a/Dockerfile.aarch64 b/Dockerfile.aarch64 new file mode 100644 index 000000000..e92905ecc --- /dev/null +++ b/Dockerfile.aarch64 @@ -0,0 +1,19 @@ +FROM resin/aarch64-alpine:3.5 + +ENV GOPATH /go +ENV PATH $PATH:$GOPATH/bin +ENV CGO_ENABLED 0 + +WORKDIR /go/src/github.com/minio/ + +RUN \ + apk add --no-cache ca-certificates && \ + apk add --no-cache --virtual .build-deps git go musl-dev && \ + go get -v -d github.com/minio/minio && \ + cd /go/src/github.com/minio/minio && \ + go install -v -ldflags "$(go run buildscripts/gen-ldflags.go)" && \ + rm -rf /go/pkg /go/src /usr/local/go && apk del .build-deps + +EXPOSE 9000 +ENTRYPOINT ["minio"] +VOLUME ["/export"] diff --git a/Dockerfile.armhf b/Dockerfile.armhf new file mode 100644 index 000000000..6fab9a5ce --- /dev/null +++ b/Dockerfile.armhf @@ -0,0 +1,19 @@ +FROM resin/armhf-alpine:3.5 + +ENV GOPATH /go +ENV PATH $PATH:$GOPATH/bin +ENV CGO_ENABLED 0 + +WORKDIR /go/src/github.com/minio/ + +RUN \ + apk add --no-cache ca-certificates && \ + apk add --no-cache --virtual .build-deps git go musl-dev && \ + go get -v -d github.com/minio/minio && \ + cd /go/src/github.com/minio/minio && \ + go install -v -ldflags "$(go run buildscripts/gen-ldflags.go)" && \ + rm -rf /go/pkg /go/src /usr/local/go && apk del .build-deps + +EXPOSE 9000 +ENTRYPOINT ["minio"] +VOLUME ["/export"] diff --git a/Makefile b/Makefile index f9eb341fa..9c5127ef7 100644 --- a/Makefile +++ b/Makefile @@ -71,10 +71,8 @@ verifiers: vet fmt lint cyclo spelling vet: @echo "Running $@:" - @go tool vet -all ./cmd - @go tool vet -all ./pkg - @go tool vet -shadow=true ./cmd - @go tool vet -shadow=true ./pkg + @go vet github.com/minio/minio/cmd/... + @go vet github.com/minio/minio/pkg/... fmt: @echo "Running $@:" diff --git a/appveyor.yml b/appveyor.yml index 68a87aa07..5a87b661a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,11 +11,12 @@ clone_folder: c:\gopath\src\github.com\minio\minio # Environment variables environment: + GOROOT: c:\go17 GOPATH: c:\gopath # scripts that run after cloning repository install: - - set PATH=%GOPATH%\bin;c:\go\bin;%PATH% + - set PATH=%GOPATH%\bin;c:\go17\bin;%PATH% - go version - go env - python --version @@ -35,9 +36,9 @@ test_script: # Unit tests - ps: Add-AppveyorTest "Unit Tests" -Outcome Running - mkdir build\coverage - - go test -timeout 20m -v -race github.com/minio/minio/cmd... - - go test -v -race github.com/minio/minio/pkg... - - go test -timeout 15m -coverprofile=build\coverage\coverage.txt -covermode=atomic github.com/minio/minio/cmd + - go test -timeout 17m -race github.com/minio/minio/cmd... + - go test -race github.com/minio/minio/pkg... + - go test -coverprofile=build\coverage\coverage.txt -covermode=atomic github.com/minio/minio/cmd - ps: Update-AppveyorTest "Unit Tests" -Outcome Passed after_test: diff --git a/browser/app/index.js b/browser/app/index.js index d750577bc..26a1cc1d3 100644 --- a/browser/app/index.js +++ b/browser/app/index.js @@ -1,5 +1,5 @@ /* - * Minio Browser (C) 2016 Minio, Inc. + * Minio Cloud Storage (C) 2016 Minio, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/browser/app/js/__tests__/jsonrpc-test.js b/browser/app/js/__tests__/jsonrpc-test.js index 341d0c286..7e5b52f76 100644 --- a/browser/app/js/__tests__/jsonrpc-test.js +++ b/browser/app/js/__tests__/jsonrpc-test.js @@ -1,5 +1,5 @@ /* - * Minio Browser (C) 2016 Minio, Inc. + * Minio Cloud Storage (C) 2016 Minio, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/browser/app/js/actions.js b/browser/app/js/actions.js index c9b2f72bf..a39113dc6 100644 --- a/browser/app/js/actions.js +++ b/browser/app/js/actions.js @@ -1,5 +1,5 @@ /* - * Minio Browser (C) 2016 Minio, Inc. + * Minio Cloud Storage (C) 2016 Minio, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,9 @@ * limitations under the License. */ -import url from 'url' import Moment from 'moment' import browserHistory from 'react-router/lib/browserHistory' -import web from './web' -import * as utils from './utils' import storage from 'local-storage-fallback' - import { minioBrowserPrefix } from './constants' export const SET_WEB = 'SET_WEB' @@ -28,7 +24,6 @@ export const SET_CURRENT_BUCKET = 'SET_CURRENT_BUCKET' export const SET_CURRENT_PATH = 'SET_CURRENT_PATH' export const SET_BUCKETS = 'SET_BUCKETS' export const ADD_BUCKET = 'ADD_BUCKET' -export const ADD_OBJECT = 'ADD_OBJECT' export const SET_VISIBLE_BUCKETS = 'SET_VISIBLE_BUCKETS' export const SET_OBJECTS = 'SET_OBJECTS' export const SET_STORAGE_INFO = 'SET_STORAGE_INFO' @@ -57,6 +52,9 @@ export const SET_SHARE_OBJECT = 'SET_SHARE_OBJECT' export const DELETE_CONFIRMATION = 'DELETE_CONFIRMATION' export const SET_PREFIX_WRITABLE = 'SET_PREFIX_WRITABLE' export const REMOVE_OBJECT = 'REMOVE_OBJECT' +export const CHECKED_OBJECTS_ADD = 'CHECKED_OBJECTS_ADD' +export const CHECKED_OBJECTS_REMOVE = 'CHECKED_OBJECTS_REMOVE' +export const CHECKED_OBJECTS_RESET = 'CHECKED_OBJECTS_RESET' export const showDeleteConfirmation = (object) => { return { @@ -78,11 +76,12 @@ export const hideDeleteConfirmation = () => { } } -export const showShareObject = url => { +export const showShareObject = (object, url) => { return { type: SET_SHARE_OBJECT, shareObject: { - url: url, + object, + url, show: true } } @@ -98,15 +97,17 @@ export const hideShareObject = () => { } } -export const shareObject = (object, expiry) => (dispatch, getState) => { +export const shareObject = (object, days, hours, minutes) => (dispatch, getState) => { const {currentBucket, web} = getState() let host = location.host let bucket = currentBucket if (!web.LoggedIn()) { - dispatch(showShareObject(`${host}/${bucket}/${object}`)) + dispatch(showShareObject(object, `${host}/${bucket}/${object}`)) return } + + let expiry = days * 24 * 60 * 60 + hours * 60 * 60 + minutes * 60 web.PresignedGet({ host, bucket, @@ -114,7 +115,11 @@ export const shareObject = (object, expiry) => (dispatch, getState) => { expiry }) .then(obj => { - dispatch(showShareObject(obj.url)) + dispatch(showShareObject(object, obj.url)) + dispatch(showAlert({ + type: 'success', + message: `Object shared. Expires in ${days} days ${hours} hours ${minutes} minutes.` + })) }) .catch(err => { dispatch(showAlert({ @@ -304,13 +309,13 @@ export const listObjects = () => { marker: marker }) .then(res => { - let objects = res.objects + let objects = res.objects if (!objects) objects = [] objects = objects.map(object => { - object.name = object.name.replace(`${currentPath}`, ''); - return object - }) + object.name = object.name.replace(`${currentPath}`, ''); + return object + }) dispatch(setObjects(objects, res.nextmarker, res.istruncated)) dispatch(setPrefixWritable(res.writable)) dispatch(setLoadBucket('')) @@ -344,9 +349,9 @@ export const selectPrefix = prefix => { if (!objects) objects = [] objects = objects.map(object => { - object.name = object.name.replace(`${prefix}`, ''); - return object - }) + object.name = object.name.replace(`${prefix}`, ''); + return object + }) dispatch(setObjects( objects, res.nextmarker, @@ -410,6 +415,25 @@ export const setLoginError = () => { } } +export const downloadSelected = (url, req, xhr) => { + return (dispatch) => { + xhr.open('POST', url, true) + xhr.responseType = 'blob' + + xhr.onload = function(e) { + if (this.status == 200) { + dispatch(checkedObjectsReset()) + var blob = new Blob([this.response], { + type: 'application/zip' + }) + var blobUrl = window.URL.createObjectURL(blob); + window.location = blobUrl + } + }; + xhr.send(JSON.stringify(req)); + } +} + export const uploadFile = (file, xhr) => { return (dispatch, getState) => { const {currentBucket, currentPath} = getState() @@ -563,3 +587,24 @@ export const setPolicies = (policies) => { policies } } + +export const checkedObjectsAdd = (objectName) => { + return { + type: CHECKED_OBJECTS_ADD, + objectName + } +} + +export const checkedObjectsRemove = (objectName) => { + return { + type: CHECKED_OBJECTS_REMOVE, + objectName + } +} + +export const checkedObjectsReset = (objectName) => { + return { + type: CHECKED_OBJECTS_RESET, + objectName + } +} diff --git a/browser/app/js/components/Browse.js b/browser/app/js/components/Browse.js index 6458ad22b..85550ccb6 100644 --- a/browser/app/js/components/Browse.js +++ b/browser/app/js/components/Browse.js @@ -1,5 +1,5 @@ /* - * Minio Browser (C) 2016 Minio, Inc. + * Minio Cloud Storage (C) 2016 Minio, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,6 @@ import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger' import Tooltip from 'react-bootstrap/lib/Tooltip' import Dropdown from 'react-bootstrap/lib/Dropdown' import MenuItem from 'react-bootstrap/lib/MenuItem' - import InputGroup from '../components/InputGroup' import Dropzone from '../components/Dropzone' import ObjectsList from '../components/ObjectsList' @@ -227,14 +226,24 @@ export default class Browse extends React.Component { } removeObject() { - const {web, dispatch, currentPath, currentBucket, deleteConfirmation} = this.props + const {web, dispatch, currentPath, currentBucket, deleteConfirmation, checkedObjects} = this.props + let objects = checkedObjects.length > 0 ? checkedObjects : [deleteConfirmation.object] + web.RemoveObject({ - bucketName: currentBucket, - objectName: deleteConfirmation.object + bucketname: currentBucket, + objects: objects }) .then(() => { this.hideDeleteConfirmation() - dispatch(actions.removeObject(deleteConfirmation.object)) + if (checkedObjects.length > 0) { + for (let i = 0; i < checkedObjects.length; i++) { + dispatch(actions.removeObject(checkedObjects[i].replace(currentPath, ''))) + } + dispatch(actions.checkedObjectsReset()) + } else { + let delObject = deleteConfirmation.object.replace(currentPath, '') + dispatch(actions.removeObject(delObject)) + } }) .catch(e => dispatch(actions.showAlert({ type: 'danger', @@ -262,7 +271,8 @@ export default class Browse extends React.Component { shareObject(e, object) { e.preventDefault() const {dispatch} = this.props - dispatch(actions.shareObject(object)) + // let expiry = 5 * 24 * 60 * 60 // 5 days expiry by default + dispatch(actions.shareObject(object, 5, 0, 0)) } hideShareObjectModal() { @@ -354,18 +364,46 @@ export default class Browse extends React.Component { this.refs.copyTextInput.select() } - handleExpireValue(targetInput, inc) { + handleExpireValue(targetInput, inc, object) { inc === -1 ? this.refs[targetInput].stepDown(1) : this.refs[targetInput].stepUp(1) if (this.refs.expireDays.value == 7) { this.refs.expireHours.value = 0 this.refs.expireMins.value = 0 } + if (this.refs.expireDays.value + this.refs.expireHours.value + this.refs.expireMins.value == 0) { + this.refs.expireDays.value = 7 + } + const {dispatch} = this.props + dispatch(actions.shareObject(object, this.refs.expireDays.value, this.refs.expireHours.value, this.refs.expireMins.value)) + } + + checkObject(e, objectName) { + const {dispatch} = this.props + e.target.checked ? dispatch(actions.checkedObjectsAdd(objectName)) : dispatch(actions.checkedObjectsRemove(objectName)) + } + + downloadSelected() { + const {dispatch} = this.props + let req = { + bucketName: this.props.currentBucket, + objects: this.props.checkedObjects, + prefix: this.props.currentPath + } + let requestUrl = location.origin + "/minio/zip?token=" + localStorage.token + + this.xhr = new XMLHttpRequest() + dispatch(actions.downloadSelected(requestUrl, req, this.xhr)) + } + + clearSelected() { + const {dispatch} = this.props + dispatch(actions.checkedObjectsReset()) } render() { const {total, free} = this.props.storageInfo - const {showMakeBucketModal, alert, sortNameOrder, sortSizeOrder, sortDateOrder, showAbout, showBucketPolicy} = this.props + const {showMakeBucketModal, alert, sortNameOrder, sortSizeOrder, sortDateOrder, showAbout, showBucketPolicy, checkedObjects} = this.props const {version, memory, platform, runtime} = this.props.serverInfo const {sidebarStatus} = this.props const {showSettings} = this.props @@ -435,7 +473,6 @@ export default class Browse extends React.Component { - } let createButton = '' @@ -490,6 +527,14 @@ export default class Browse extends React.Component { clickOutside={ this.hideSidebar.bind(this) } showPolicy={ this.showBucketPolicy.bind(this) } />