Repository: ambari Updated Branches: refs/heads/trunk 7b7f0c92b -> 9218a8ebf
AMBARI-8403. Views: Files download of ZIP fails on folder with no permissions (alexantonenko) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/a6127158 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/a6127158 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/a6127158 Branch: refs/heads/trunk Commit: a61271583c7bd85f5c1b3cf537cdcce2a461e0a1 Parents: 7b7f0c9 Author: Alex Antonenko <hiv...@gmail.com> Authored: Wed Nov 26 17:43:36 2014 +0200 Committer: Alex Antonenko <hiv...@gmail.com> Committed: Wed Nov 26 18:40:08 2014 +0200 ---------------------------------------------------------------------- contrib/views/files/pom.xml | 1 - .../view/filebrowser/DownloadService.java | 47 ++++++++++++++------ .../view/filebrowser/FileOperationService.java | 10 ++--- .../apache/ambari/view/filebrowser/HdfsApi.java | 32 +++++++++++-- .../ambari/view/filebrowser/HdfsService.java | 8 ++-- .../ambari/view/filebrowser/HelpService.java | 6 +-- .../ambari/view/filebrowser/UploadService.java | 4 +- .../files/src/main/resources/ui/app/adapter.js | 30 ++++++------- .../main/resources/ui/app/controllers/file.js | 26 +++++++---- .../main/resources/ui/app/controllers/files.js | 40 ++++++++++------- .../src/main/resources/ui/app/models/file.js | 23 +++++----- .../ui/app/templates/components/contextMenu.hbs | 4 +- .../main/resources/ui/app/templates/files.hbs | 6 +-- .../resources/ui/app/templates/util/fileRow.hbs | 12 ++--- .../files/src/main/resources/ui/bower.json | 8 +--- .../files/src/main/resources/ui/config.coffee | 15 ++++--- .../files/src/main/resources/ui/package.json | 4 +- contrib/views/files/src/main/resources/view.xml | 10 ++--- .../view/filebrowser/FilebrowserTest.java | 4 +- 19 files changed, 177 insertions(+), 113 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/a6127158/contrib/views/files/pom.xml ---------------------------------------------------------------------- diff --git a/contrib/views/files/pom.xml b/contrib/views/files/pom.xml index dd8325e..7813712 100644 --- a/contrib/views/files/pom.xml +++ b/contrib/views/files/pom.xml @@ -177,7 +177,6 @@ <arguments> <argument>node_modules/.bin/brunch</argument> <argument>build</argument> - <argument>--production</argument> </arguments> </configuration> </execution> http://git-wip-us.apache.org/repos/asf/ambari/blob/a6127158/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/DownloadService.java ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/DownloadService.java b/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/DownloadService.java index 72ba726..063d453 100644 --- a/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/DownloadService.java +++ b/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/DownloadService.java @@ -51,6 +51,7 @@ import org.apache.ambari.view.filebrowser.utils.ServiceFormattedException; import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FileStatus; import org.apache.ambari.view.ViewContext; +import org.apache.hadoop.security.AccessControlException; import org.json.simple.JSONObject; //import org.glassfish.jersey.server.ChunkedOutput; @@ -100,34 +101,44 @@ public class DownloadService extends HdfsService { } } - private void zipFile(ZipOutputStream zip, String path) throws InterruptedException, IOException { + private void zipFile(ZipOutputStream zip, String path) { try { - zip.putNextEntry(new ZipEntry(path.substring(1))); FSDataInputStream in = getApi(context).open(path); + zip.putNextEntry(new ZipEntry(path.substring(1))); byte[] chunk = new byte[1024]; while (in.read(chunk) != -1) { zip.write(chunk); } } catch (IOException ex) { - String msg = "Error zipping file " + path.substring(1) + ": " + logger.error("Error zipping file " + path.substring(1) + " (file ignored): " + + ex.getMessage()); + } catch (InterruptedException ex) { + String msg = "Error zipping file " + path.substring(1) + " (file ignored): " + ex.getMessage(); logger.error(msg); - zip.write(ex.getMessage().getBytes()); - throw new ServiceFormattedException(ex.getMessage(), ex); } finally { - zip.closeEntry(); + try { + zip.closeEntry(); + } catch (IOException ex) { + logger.error("Error closing entry " + path.substring(1) + " (file ignored): " + + ex.getMessage()); + } } } private void zipDirectory(ZipOutputStream zip, String path) { try { zip.putNextEntry(new ZipEntry(path.substring(1) + "/")); - zip.closeEntry(); } catch (IOException ex) { - String msg = "Error zipping directory " + path.substring(1) + "/" + ": " - + ex.getMessage(); - logger.error(msg); - throw new ServiceFormattedException(msg, ex); + logger.error("Error zipping directory " + path.substring(1) + "/ (directory ignored)" + ": " + + ex.getMessage()); + } finally { + try { + zip.closeEntry(); + } catch (IOException ex) { + logger.error("Error zipping directory " + path.substring(1) + "/ (directory ignored)" + ": " + + ex.getMessage()); + } } } @@ -156,7 +167,14 @@ public class DownloadService extends HdfsService { String path = files.poll(); FileStatus status = api.getFileStatus(path); if (status.isDirectory()) { - FileStatus[] subdir = api.listdir(path); + FileStatus[] subdir; + try { + subdir = api.listdir(path); + } catch (AccessControlException ex) { + logger.error("Error zipping directory " + path.substring(1) + "/ (directory ignored)" + ": " + + ex.getMessage()); + continue; + } for (FileStatus file : subdir) { files.add(org.apache.hadoop.fs.Path .getPathWithoutSchemeAndAuthority(file.getPath()) @@ -240,10 +258,13 @@ public class DownloadService extends HdfsService { @GET @Path("/zip") @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_OCTET_STREAM) + @Produces("application/zip") public Response zipByRequestId(@QueryParam("requestId") String requestId) { try { String json = context.getInstanceData(requestId); + if (json == null) { + throw new NotFoundFormattedException("Request is old", null); + } DownloadRequest request = gson.fromJson(json, DownloadRequest.class); context.removeInstanceData(requestId); return downloadGZip(request); http://git-wip-us.apache.org/repos/asf/ambari/blob/a6127158/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/FileOperationService.java ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/FileOperationService.java b/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/FileOperationService.java index d043917..444899f 100644 --- a/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/FileOperationService.java +++ b/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/FileOperationService.java @@ -58,7 +58,7 @@ public class FileOperationService extends HdfsService { public Response listdir(@QueryParam("path") String path) { try { return Response.ok( - HdfsApi.fileStatusToJSON(getApi(context).listdir(path))).build(); + getApi(context).fileStatusToJSON(getApi(context).listdir(path))).build(); } catch (WebApplicationException ex) { throw ex; } catch (FileNotFoundException ex) { @@ -82,7 +82,7 @@ public class FileOperationService extends HdfsService { HdfsApi api = getApi(context); ResponseBuilder result; if (api.rename(request.src, request.dst)) { - result = Response.ok(HdfsApi.fileStatusToJSON(api + result = Response.ok(getApi(context).fileStatusToJSON(api .getFileStatus(request.dst))); } else { result = Response.ok(new BoolResult(false)).status(422); @@ -109,7 +109,7 @@ public class FileOperationService extends HdfsService { HdfsApi api = getApi(context); ResponseBuilder result; if (api.chmod(request.path, request.mode)) { - result = Response.ok(HdfsApi.fileStatusToJSON(api + result = Response.ok(getApi(context).fileStatusToJSON(api .getFileStatus(request.path))); } else { result = Response.ok(new BoolResult(false)).status(422); @@ -137,7 +137,7 @@ public class FileOperationService extends HdfsService { HdfsApi api = getApi(context); ResponseBuilder result; if (api.copy(request.src, request.dst)) { - result = Response.ok(HdfsApi.fileStatusToJSON(api + result = Response.ok(getApi(context).fileStatusToJSON(api .getFileStatus(request.dst))); } else { result = Response.ok(new BoolResult(false)).status(422); @@ -163,7 +163,7 @@ public class FileOperationService extends HdfsService { HdfsApi api = getApi(context); ResponseBuilder result; if (api.mkdir(request.path)) { - result = Response.ok(HdfsApi.fileStatusToJSON(api.getFileStatus(request.path))); + result = Response.ok(getApi(context).fileStatusToJSON(api.getFileStatus(request.path))); } else { result = Response.ok(new BoolResult(false)).status(422); } http://git-wip-us.apache.org/repos/asf/ambari/blob/a6127158/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HdfsApi.java ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HdfsApi.java b/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HdfsApi.java index cd88c54..fcc4d16 100644 --- a/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HdfsApi.java +++ b/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HdfsApi.java @@ -20,12 +20,16 @@ package org.apache.ambari.view.filebrowser; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.*; +import org.apache.hadoop.fs.permission.AccessControlException; +import org.apache.hadoop.fs.permission.FsAction; import org.apache.hadoop.fs.permission.FsPermission; import java.io.FileNotFoundException; import java.io.IOException; import java.net.URI; import java.security.PrivilegedExceptionAction; +import java.util.Arrays; +import java.util.List; import java.util.Map; import org.apache.hadoop.security.UserGroupInformation; @@ -261,7 +265,7 @@ public class HdfsApi { return ugi.doAs(new PrivilegedExceptionAction<Boolean>() { public Boolean run() throws Exception { try { - fs.setPermission(new Path(path), new FsPermission(permissions)); + fs.setPermission(new Path(path), FsPermission.valueOf(permissions)); } catch (Exception ex) { return false; } @@ -313,7 +317,7 @@ public class HdfsApi { * @return The JSON representation of the file status. */ - public static Map<String, Object> fileStatusToJSON(FileStatus status) { + public Map<String, Object> fileStatusToJSON(FileStatus status) { Map<String, Object> json = new LinkedHashMap<String, Object>(); json.put("path", Path.getPathWithoutSchemeAndAuthority(status.getPath()) .toString()); @@ -327,6 +331,9 @@ public class HdfsApi { json.put("modificationTime", status.getModificationTime()); json.put("blockSize", status.getBlockSize()); json.put("replication", status.getReplication()); + json.put("readAccess", checkAccessPermissions(status, FsAction.READ, ugi)); + json.put("writeAccess", checkAccessPermissions(status, FsAction.WRITE, ugi)); + json.put("executeAccess", checkAccessPermissions(status, FsAction.EXECUTE, ugi)); return json; } @@ -341,7 +348,7 @@ public class HdfsApi { * @return The JSON representation of the file status array. */ @SuppressWarnings("unchecked") - public static JSONArray fileStatusToJSON(FileStatus[] status) { + public JSONArray fileStatusToJSON(FileStatus[] status) { JSONArray json = new JSONArray(); if (status != null) { for (FileStatus s : status) { @@ -351,4 +358,23 @@ public class HdfsApi { return json; } + public static boolean checkAccessPermissions(FileStatus stat, FsAction mode, UserGroupInformation ugi) { + FsPermission perm = stat.getPermission(); + String user = ugi.getShortUserName(); + List<String> groups = Arrays.asList(ugi.getGroupNames()); + if (user.equals(stat.getOwner())) { + if (perm.getUserAction().implies(mode)) { + return true; + } + } else if (groups.contains(stat.getGroup())) { + if (perm.getGroupAction().implies(mode)) { + return true; + } + } else { + if (perm.getOtherAction().implies(mode)) { + return true; + } + } + return false; + } } http://git-wip-us.apache.org/repos/asf/ambari/blob/a6127158/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HdfsService.java ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HdfsService.java b/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HdfsService.java index 06f27b0..1fd5719 100644 --- a/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HdfsService.java +++ b/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HdfsService.java @@ -64,13 +64,13 @@ public abstract class HdfsService { public HdfsApi getApi(ViewContext context) { if (_api == null) { Thread.currentThread().setContextClassLoader(null); - String defaultFs = context.getProperties().get("dataworker.defaultFs"); + String defaultFs = context.getProperties().get("webhdfs.url"); if (defaultFs == null) - throw new MisconfigurationFormattedException("dataworker.defaultFs"); + throw new MisconfigurationFormattedException("webhdfs.url"); try { _api = new HdfsApi(defaultFs, getUsername(context)); } catch (Exception ex) { - throw new ServiceFormattedException("HdfsApi connection failed. Check \"dataworker.defaultFs\" property", ex); + throw new ServiceFormattedException("HdfsApi connection failed. Check \"webhdfs.url\" property", ex); } } return _api; @@ -82,7 +82,7 @@ public abstract class HdfsService { * @return user name */ public String getUsername(ViewContext context) { - String username = context.getProperties().get("dataworker.username"); + String username = context.getProperties().get("webhdfs.username"); if (username == null || username.isEmpty()) username = context.getUsername(); return username; http://git-wip-us.apache.org/repos/asf/ambari/blob/a6127158/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HelpService.java ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HelpService.java b/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HelpService.java index d12c47c..695ca38 100644 --- a/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HelpService.java +++ b/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HelpService.java @@ -75,7 +75,7 @@ public class HelpService extends HdfsService { @Produces(MediaType.TEXT_PLAIN) public Response filesystem() { return Response.ok( - context.getProperties().get("dataworker.defaultFs")).build(); + context.getProperties().get("webhdfs.url")).build(); } /** @@ -89,7 +89,7 @@ public class HelpService extends HdfsService { try { HdfsApi api = getApi(context); return Response - .ok(HdfsApi.fileStatusToJSON(api.getFileStatus(api.getHomeDir() + .ok(getApi(context).fileStatusToJSON(api.getFileStatus(api.getHomeDir() .toString()))).build(); } catch (WebApplicationException ex) { throw ex; @@ -127,7 +127,7 @@ public class HelpService extends HdfsService { try { HdfsApi api = getApi(context); return Response.ok( - HdfsApi.fileStatusToJSON(api.getFileStatus(api.getTrashDir() + getApi(context).fileStatusToJSON(api.getFileStatus(api.getTrashDir() .toString()))).build(); } catch (WebApplicationException ex) { throw ex; http://git-wip-us.apache.org/repos/asf/ambari/blob/a6127158/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/UploadService.java ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/UploadService.java b/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/UploadService.java index 1ff5571..051bdfb 100644 --- a/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/UploadService.java +++ b/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/UploadService.java @@ -77,7 +77,7 @@ public class UploadService extends HdfsService { String filePath = path + contentDisposition.getFileName(); uploadFile(filePath, uploadedInputStream); return Response.ok( - HdfsApi.fileStatusToJSON(getApi(context).getFileStatus(filePath))) + getApi(context).fileStatusToJSON(getApi(context).getFileStatus(filePath))) .build(); } catch (WebApplicationException ex) { throw ex; @@ -118,7 +118,7 @@ public class UploadService extends HdfsService { } ze = zip.getNextEntry(); } - return Response.ok(HdfsApi.fileStatusToJSON(api.listdir(path))).build(); + return Response.ok(getApi(context).fileStatusToJSON(api.listdir(path))).build(); } catch (WebApplicationException ex) { throw ex; } catch (Exception ex) { http://git-wip-us.apache.org/repos/asf/ambari/blob/a6127158/contrib/views/files/src/main/resources/ui/app/adapter.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/adapter.js b/contrib/views/files/src/main/resources/ui/app/adapter.js index a6e471f..ff98a10 100644 --- a/contrib/views/files/src/main/resources/ui/app/adapter.js +++ b/contrib/views/files/src/main/resources/ui/app/adapter.js @@ -147,11 +147,10 @@ function getNamespaceUrl() { instance = parts[2]; version = ''; } - var namespaceUrl = 'api/v1/views' + view + version + '/instances' + instance + '/'; - return namespaceUrl; + return 'api/v1/views' + view + version + '/instances' + instance + '/'; } -App.Store = DS.Store.extend({ +App.ApplicationStore = DS.Store.extend({ adapter: DS.RESTAdapter.extend({ namespace: getNamespaceUrl() + 'resources/files', headers: { @@ -252,24 +251,25 @@ App.Store = DS.Store.extend({ }, /** * get dowload link - * @param {Array} file records for download + * @param {Array} files records for download * @param {String} option browse, zip or concat * @param {Boolean} download * @return {Promise} */ linkFor:function (files, option, download) { - var resolver = Ember.RSVP.defer('promiseLabel'); - var adapter = this.adapterFor(this.modelFor('file')), - download = download || true; - option = option || "browse"; + var resolver = Ember.RSVP.defer('promiseLabel'), + adapter = this.adapterFor(this.modelFor('file')), + query = {}, + download = download || true; + option = option || "browse"; if (option == 'browse') { - var query = { "path": files.get('firstObject.path'), "download": download }; - resolver.resolve(adapter.downloadUrl('browse',query)) + query = { "path": files.get('firstObject.path'), "download": download }; + resolver.resolve(adapter.downloadUrl('browse',query)); return resolver.promise; - }; + } - var query = { + query = { "entries": [], "download": download }; @@ -278,7 +278,7 @@ App.Store = DS.Store.extend({ query.entries.push(item.get('path')); }); - resolver.resolve(adapter.linkFor(option, query)) + resolver.resolve(adapter.linkFor(option, query)); return resolver.promise.then(function(response) { return adapter.downloadUrl(option,response); @@ -286,7 +286,7 @@ App.Store = DS.Store.extend({ throw reason; }); } -}) +}); App.FileSerializer = DS.RESTSerializer.extend({ primaryKey:'path', @@ -300,7 +300,7 @@ App.FileSerializer = DS.RESTSerializer.extend({ }, extractChmod:function(store, type, payload, id, requestType) { return this.extractSingle(store, type, payload, id, requestType); - }, + } }); App.Uploader = Ember.Uploader.create({ http://git-wip-us.apache.org/repos/asf/ambari/blob/a6127158/contrib/views/files/src/main/resources/ui/app/controllers/file.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/controllers/file.js b/contrib/views/files/src/main/resources/ui/app/controllers/file.js index f8b7046..7992814 100644 --- a/contrib/views/files/src/main/resources/ui/app/controllers/file.js +++ b/contrib/views/files/src/main/resources/ui/app/controllers/file.js @@ -25,9 +25,11 @@ App.FileController = Ember.ObjectController.extend({ }, actions:{ download:function (option) { - this.store.linkFor([this.get('content')],option).then(function (link) { - window.location.href = link; - },Em.run.bind(this,this.sendAlert)); + if (this.get('content.readAccess')) { + this.store.linkFor([this.get('content')],option).then(function (link) { + window.location.href = link; + },Em.run.bind(this,this.sendAlert)); + } }, showChmod:function () { this.toggleProperty('chmodVisible',true); @@ -56,17 +58,20 @@ App.FileController = Ember.ObjectController.extend({ .then(null,Em.run.bind(this,this.chmodErrorCallback,record)); }, open:function (file) { + if (!this.get('content.readAccess')) { + return false; + } if (this.get('content.isDirectory')) { return this.transitionToRoute('files',{queryParams: {path: this.get('content.id')}}); } else{ return this.send('download'); - }; + } }, deleteFile:function (deleteForever) { this.store .remove(this.get('content'),!deleteForever) - .then(null,Em.run.bind(this,this.sendAlert)); - }, + .then(null,Em.run.bind(this,this.deleteErrorCallback,this.get('content'))); + } }, selected:false, isRenaming:false, @@ -95,10 +100,15 @@ App.FileController = Ember.ObjectController.extend({ chmodErrorCallback:function (record,error) { record.rollback(); - this.sendAlert(error); + this.sendAlert({message:'Permissions change failed'}); }, - sendAlert:function (error) { + deleteErrorCallback:function (record,error) { + this.parentController.model.pushRecord(record); this.send('showAlert',error); }, + + sendAlert:function (error) { + this.send('showAlert',error); + } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/a6127158/contrib/views/files/src/main/resources/ui/app/controllers/files.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/controllers/files.js b/contrib/views/files/src/main/resources/ui/app/controllers/files.js index a9d98e8..8de6a4b 100644 --- a/contrib/views/files/src/main/resources/ui/app/controllers/files.js +++ b/contrib/views/files/src/main/resources/ui/app/controllers/files.js @@ -22,36 +22,35 @@ var bind = Ember.run.bind; App.FilesController = Ember.ArrayController.extend({ actions:{ moveFile:function (opt,file) { - var src, title, - file = file || this.get('selectedFiles.firstObject'), + var src, moving = this.get('movingFile'); - + file = file || this.get('selectedFiles.firstObject'); if (opt == 'cut') { src = file.toJSON({includeId: true}); - src = Em.merge(src,{name:file.get('name'),path:file.get('path')}) + src = Em.merge(src,{name:file.get('name'),path:file.get('path')}); this.set('movingFile',src); - }; + } if (opt == 'move') { this.store.move(moving.path,[this.get('path'),moving.name].join('/').replace('//','/')) .then(bind(this,this.set,'movingFile',null),bind(this,this.throwAlert)); - }; + } if (opt == 'cancel') { this.set('movingFile',null); - }; + } }, showRenameInput:function () { this.toggleProperty('isRenaming'); }, renameDir:function (path,newName) { var _this = this, - basedir = path.substring(0,path.lastIndexOf('/')+1); + basedir = path.substring(0,path.lastIndexOf('/')+1), newPath = basedir + newName; if (path === newPath) { return false; - }; + } this.store.listdir(basedir).then(function (listdir) { var recordExists = listdir.isAny('id',newPath); @@ -62,14 +61,14 @@ App.FilesController = Ember.ArrayController.extend({ if (recordExists) { return _this.throwAlert({message:newPath + ' already exists.'}); - }; + } return _this.store.move(path,newPath); }).then(function (newDir) { if (newDir) { _this.store.unloadRecord(newDir); _this.set('path',newPath); - }; + } }).catch(bind(this,this.throwAlert)); }, @@ -78,11 +77,11 @@ App.FilesController = Ember.ArrayController.extend({ var selected = this.get('selectedFiles'); var moveToTrash = !deleteForever; selected.forEach(function (file) { - self.store.remove(file,moveToTrash).then(null,bind(self,self.throwAlert)); + self.store.remove(file,moveToTrash).then(null,bind(self,self.deleteErrorCallback,file)); }); }, download:function (option) { - var files = this.get('selectedFiles'); + var files = this.get('selectedFiles').filterBy('readAccess',true); this.store.linkFor(files,option).then(function (link) { window.location.href = link; }); @@ -94,11 +93,11 @@ App.FilesController = Ember.ArrayController.extend({ upload:function (opt) { if (opt === 'open') { this.set('isUploading',true); - }; + } if (opt === 'close') { this.set('isUploading',false); - }; + } }, sort:function (pr) { var currentProperty = this.get('sortProperties'); @@ -107,7 +106,7 @@ App.FilesController = Ember.ArrayController.extend({ } else{ this.set('sortProperties',[pr]); this.set('sortAscending',true); - }; + } }, clearSearchField:function () { this.set('searchString',''); @@ -168,6 +167,15 @@ App.FilesController = Ember.ArrayController.extend({ } }, + clearSearch:function () { + this.set('searchString',''); + }.observes('path'), + + deleteErrorCallback:function (record,error) { + this.model.pushRecord(record); + this.send('showAlert',error); + }, + throwAlert:function (error) { this.send('showAlert',error); } http://git-wip-us.apache.org/repos/asf/ambari/blob/a6127158/contrib/views/files/src/main/resources/ui/app/models/file.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/models/file.js b/contrib/views/files/src/main/resources/ui/app/models/file.js index 6908f44..2a8c83c 100644 --- a/contrib/views/files/src/main/resources/ui/app/models/file.js +++ b/contrib/views/files/src/main/resources/ui/app/models/file.js @@ -18,7 +18,7 @@ var App = require('app'); -var a = DS.attr; +var dsa = DS.attr; App.File = DS.Model.extend({ path: function() { @@ -28,15 +28,18 @@ App.File = DS.Model.extend({ var path = this.get('id'); return path.substring(0,path.lastIndexOf('/'))||'/'; }.property('id'), - isDirectory: a('boolean'), - len: a('number'), - owner: a('string'), - group: a('string'), - permission: a('string'), - accessTime: a('isodate'), - modificationTime: a('isodate'), - blockSize: a('number'), - replication: a('number'), + isDirectory: dsa('boolean'), + readAccess: dsa('boolean'), + writeAccess: dsa('boolean'), + executeAccess: dsa('boolean'), + len: dsa('number'), + owner: dsa('string'), + group: dsa('string'), + permission: dsa('string'), + accessTime: dsa('isodate'), + modificationTime: dsa('isodate'), + blockSize: dsa('number'), + replication: dsa('number'), name:function () { var splitpath = this.get('path').split('/'); return splitpath.get(splitpath.length-1); http://git-wip-us.apache.org/repos/asf/ambari/blob/a6127158/contrib/views/files/src/main/resources/ui/app/templates/components/contextMenu.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/templates/components/contextMenu.hbs b/contrib/views/files/src/main/resources/ui/app/templates/components/contextMenu.hbs index 844363c..aa86ed1 100644 --- a/contrib/views/files/src/main/resources/ui/app/templates/components/contextMenu.hbs +++ b/contrib/views/files/src/main/resources/ui/app/templates/components/contextMenu.hbs @@ -20,9 +20,9 @@ <div id="context-menu"> <ul class="dropdown-menu dropdown-context compressed-context" role="menu"> {{#if view.target.content.isDirectory}} - <li><a tabindex="-1" href="#" {{action 'open'}}>Open folder</a></li> + <li {{bind-attr class="view.target.content.readAccess::disabled"}}><a tabindex="-1" href="#" {{action 'open'}}>Open folder</a></li> {{else}} - <li><a tabindex="-1" href="#" {{action 'download'}}>Download</a></li> + <li {{bind-attr class="view.target.content.readAccess::disabled"}}><a tabindex="-1" href="#" {{action 'download'}}>Download</a></li> {{/if}} <li><a tabindex="-1" href="#" {{action 'moveFile' 'cut' view.target.content}}>Move</a></li> <li><a tabindex="-1" href="#" {{action 'showChmod'}} >Permissions</a></li> http://git-wip-us.apache.org/repos/asf/ambari/blob/a6127158/contrib/views/files/src/main/resources/ui/app/templates/files.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/templates/files.hbs b/contrib/views/files/src/main/resources/ui/app/templates/files.hbs index 6f65e91..76c439d 100644 --- a/contrib/views/files/src/main/resources/ui/app/templates/files.hbs +++ b/contrib/views/files/src/main/resources/ui/app/templates/files.hbs @@ -43,8 +43,8 @@ <div class="panel-body"> <h4 class="i-am-in pull-left"> <i class="fa fa-folder fa-lg"></i> {{#rename-input file=path confirm='renameDir' isRenaming=isRenaming class='renameable stocked half'}} - <a href="#" class="dir-name" {{action refreshDir}}>{{currentDir}}</a> - <a href="#" {{bind-attr class="isRootDir:hide"}} {{action showRenameInput}}><i class="fa fa-edit"></i></a> + <a href="#" class="dir-name" {{action 'refreshDir'}}>{{currentDir}}</a> + <a href="#" {{bind-attr class="isRootDir:hide"}} {{action 'showRenameInput'}}><i class="fa fa-edit"></i></a> {{/rename-input}} </h4> @@ -68,7 +68,7 @@ <th class="perm" {{action 'sort' 'permission'}} >Permission {{sort-arrow sPs=sortProperties sA=sortAscending sP='permission'}}</th> <th class="download"> <div class="btn-group btn-sort pull-right" data-toggle="tooltip" data-placement="left" title="Sort by:"> - <button type="button" class="btn btn-xs btn-default" {{action sort 'toggle'}}> + <button type="button" class="btn btn-xs btn-default" {{action 'sort' 'toggle'}}> {{#if sortAscending}} Asc {{else}} Desc {{/if}} </button> http://git-wip-us.apache.org/repos/asf/ambari/blob/a6127158/contrib/views/files/src/main/resources/ui/app/templates/util/fileRow.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/templates/util/fileRow.hbs b/contrib/views/files/src/main/resources/ui/app/templates/util/fileRow.hbs index 052a14e..50693b0 100644 --- a/contrib/views/files/src/main/resources/ui/app/templates/util/fileRow.hbs +++ b/contrib/views/files/src/main/resources/ui/app/templates/util/fileRow.hbs @@ -55,10 +55,12 @@ {{#unless isMoving}} <ul class="list-inline file-actions text-right"> <li> - {{#if content.isDirectory}} - <a href="#" {{action 'download' 'zip'}} data-toggle="tooltip" data-placement="bottom" title="Download zip"><i class="fa fa-archive fa-fw fa-lg"></i></a> - {{else}} - <a href="#" {{action 'download' 'browse'}} data-toggle="tooltip" data-placement="bottom" title="Download"><i class="fa fa-download fa-fw fa-lg"></i></a> + {{#if content.readAccess}} + {{#if content.isDirectory}} + <a href="#" {{action 'download' 'zip'}} data-toggle="tooltip" data-placement="bottom" title="Download zip"><i class="fa fa-archive fa-fw fa-lg"></i></a> + {{else}} + <a href="#" {{action 'download' 'browse'}} data-toggle="tooltip" data-placement="bottom" title="Download"><i class="fa fa-download fa-fw fa-lg"></i></a> + {{/if}} {{/if}} </li> <li> @@ -81,4 +83,4 @@ </td> </tr> {{chmod-input chVisible=chmodVisible file=content confirm="chmod"}} - \ No newline at end of file + http://git-wip-us.apache.org/repos/asf/ambari/blob/a6127158/contrib/views/files/src/main/resources/ui/bower.json ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/bower.json b/contrib/views/files/src/main/resources/ui/bower.json index 9579a32..b7cb52a 100644 --- a/contrib/views/files/src/main/resources/ui/bower.json +++ b/contrib/views/files/src/main/resources/ui/bower.json @@ -5,7 +5,7 @@ "dependencies": { "ember": "1.7.0", "ember-data": "1.0.0-beta.9", - "jquery": "1.9.0", + "jquery": "2.x", "bootstrap": "3.1.x", "ember-uploader": "~0.2.7", "ladda-bootstrap": "git://github.com/msurguy/ladda-bootstrap.git#~0.1.0", @@ -15,9 +15,6 @@ "font-awesome": "~4.0.3" }, "overrides": { - "jquery": { - "main": "jquery.js" - }, "ember-uploader": { "main": "dist/ember-uploader.js" }, @@ -30,8 +27,5 @@ "font-awesome": { "main": "css/font-awesome.css" } - }, - "resolutions": { - "jquery": "1.9.0" } } http://git-wip-us.apache.org/repos/asf/ambari/blob/a6127158/contrib/views/files/src/main/resources/ui/config.coffee ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/config.coffee b/contrib/views/files/src/main/resources/ui/config.coffee index 36889ce..b7bc79f 100644 --- a/contrib/views/files/src/main/resources/ui/config.coffee +++ b/contrib/views/files/src/main/resources/ui/config.coffee @@ -17,8 +17,12 @@ exports.config = + watcher: + usePolling: true + + fileListInterval: 512 + files: - javascripts: defaultExtension: 'js' joinTo: @@ -35,17 +39,14 @@ exports.config = defaultExtension: 'hbs' joinTo: 'javascripts/app.js' : /^app/ paths: - jquery: 'bower_components/jquery/jquery.js' + jquery: 'bower_components/jquery/dist/jquery.js' handlebars: 'bower_components/handlebars/handlebars.js' ember: 'bower_components/ember/ember.js' modules: addSourceURLs: true - paths: - public: '/usr/lib/ambari-server/web/views-debug/FILES/0.1.0/MyFiles/' - overrides: - production: + development: paths: - public: 'public' + public: '/usr/lib/ambari-server/web/views-debug/FILES/0.1.0/MyFiles/' http://git-wip-us.apache.org/repos/asf/ambari/blob/a6127158/contrib/views/files/src/main/resources/ui/package.json ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/package.json b/contrib/views/files/src/main/resources/ui/package.json index 54d6788..8ff13e5 100644 --- a/contrib/views/files/src/main/resources/ui/package.json +++ b/contrib/views/files/src/main/resources/ui/package.json @@ -22,9 +22,9 @@ "uglify-js-brunch": "^1.7.7", "clean-css-brunch": "^1.7.1", "bower": "^1.2.8", - "brunch": "^1.7.13", + "brunch": "1.7.17", "scaffolt": "^0.4.3", - "ember-precompiler-brunch": "^1.5.1", + "ember-precompiler-brunch": ">= 1.5.0", "less-brunch": "^1.7.2" }, "devDependencies": {}, http://git-wip-us.apache.org/repos/asf/ambari/blob/a6127158/contrib/views/files/src/main/resources/view.xml ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/view.xml b/contrib/views/files/src/main/resources/view.xml index 69586d1..5bd72d5 100644 --- a/contrib/views/files/src/main/resources/view.xml +++ b/contrib/views/files/src/main/resources/view.xml @@ -20,16 +20,16 @@ <version>0.1.0</version> <parameter> - <name>dataworker.defaultFs</name> - <description>The FileSystem URI (for example, hdfs://c6401.ambari.apache.org:8020)</description> + <name>webhdfs.url</name> + <description>WebHDFS FileSystem URI (example: webhdfs://namenode:50070)</description> <required>true</required> </parameter> <parameter> - <name>dataworker.username</name> - <description>The username (defaults to ViewContext username)</description> + <name>webhdfs.username</name> + <description>User and doAs for proxy user for HDFS</description> <required>false</required> </parameter> - + <resource> <name>files</name> <service-class>org.apache.ambari.view.filebrowser.FileBrowserService</service-class> http://git-wip-us.apache.org/repos/asf/ambari/blob/a6127158/contrib/views/files/src/test/java/org/apache/ambari/view/filebrowser/FilebrowserTest.java ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/test/java/org/apache/ambari/view/filebrowser/FilebrowserTest.java b/contrib/views/files/src/test/java/org/apache/ambari/view/filebrowser/FilebrowserTest.java index 6eb10e3..b8dcb92 100644 --- a/contrib/views/files/src/test/java/org/apache/ambari/view/filebrowser/FilebrowserTest.java +++ b/contrib/views/files/src/test/java/org/apache/ambari/view/filebrowser/FilebrowserTest.java @@ -84,7 +84,7 @@ public class FilebrowserTest{ MiniDFSCluster.Builder builder = new MiniDFSCluster.Builder(conf); hdfsCluster = builder.build(); String hdfsURI = hdfsCluster.getURI() + "/"; - properties.put("dataworker.defaultFs", hdfsURI); + properties.put("webhdfs.url", hdfsURI); expect(context.getProperties()).andReturn(properties).anyTimes(); expect(context.getUsername()).andReturn(System.getProperty("user.name")).anyTimes(); replay(handler, context, httpHeaders, uriInfo); @@ -158,7 +158,7 @@ public class FilebrowserTest{ @Test public void testUsername() throws Exception { Assert.assertEquals(System.getProperty("user.name"), fileBrowserService.upload().getUsername(context)); - properties.put("dataworker.username", "test-user"); + properties.put("webhdfs.username", "test-user"); Assert.assertEquals("test-user", fileBrowserService.upload().getUsername(context)); }