You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by al...@apache.org on 2014/11/26 17:40:34 UTC

[1/2] ambari git commit: AMBARI-8403. Views: Files download of ZIP fails on folder with no permissions (alexantonenko)

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 <hi...@gmail.com>
Authored: Wed Nov 26 17:43:36 2014 +0200
Committer: Alex Antonenko <hi...@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));
   }
 


[2/2] ambari git commit: AMBARI-8428. "Service actions" drop down menu is continuously re-created. Added UT (alexantonenko)

Posted by al...@apache.org.
AMBARI-8428. "Service actions" drop down menu is continuously re-created. Added UT (alexantonenko)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/9218a8eb
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/9218a8eb
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/9218a8eb

Branch: refs/heads/trunk
Commit: 9218a8ebf7011df6f4259c43aa88f14f11673851
Parents: a612715
Author: Alex Antonenko <hi...@gmail.com>
Authored: Wed Nov 26 18:05:14 2014 +0200
Committer: Alex Antonenko <hi...@gmail.com>
Committed: Wed Nov 26 18:40:09 2014 +0200

----------------------------------------------------------------------
 ambari-web/test/views/main/service/item_test.js | 50 +++++++++++---------
 1 file changed, 27 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/9218a8eb/ambari-web/test/views/main/service/item_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/service/item_test.js b/ambari-web/test/views/main/service/item_test.js
index b35aa6c..c4e847c 100644
--- a/ambari-web/test/views/main/service/item_test.js
+++ b/ambari-web/test/views/main/service/item_test.js
@@ -19,10 +19,13 @@
 var App = require('app');
 require('views/main/service/item');
 
+var view;
+
 describe('App.MainServiceItemView', function () {
 
   describe('#mastersExcludedCommands', function () {
-    var view = App.MainServiceItemView.create({
+
+    view = App.MainServiceItemView.create({
       controller: Em.Object.create({
         content: Em.Object.create({
           hostComponents: []
@@ -61,7 +64,7 @@ describe('App.MainServiceItemView', function () {
     });
   });
 
-  describe.skip('#observeMaintenance', function () {
+  describe('#observeMaintenance', function () {
 
     var mastersExcludedCommands = {
         NAMENODE: ["DECOMMISSION", "REBALANCEHDFS"],
@@ -323,8 +326,14 @@ describe('App.MainServiceItemView', function () {
 
     beforeEach(function () {
 
+      view = App.MainServiceItemView.create({});
+
       sinon.stub(App, 'get', function (k) {
         switch (k) {
+          case 'supports.autoRollbackHA':
+          case 'isRMHaEnabled':
+          case 'isHaEnabled':
+            return false;
           case 'components.rollinRestartAllowed':
             return ["DATANODE", "JOURNALNODE", "ZKFC", "NODEMANAGER", "GANGLIA_MONITOR", "HBASE_REGIONSERVER", "SUPERVISOR", "FLUME_HANDLER"];
           case 'components.reassignable':
@@ -375,20 +384,22 @@ describe('App.MainServiceItemView', function () {
 
     testCases.forEach(function (testCase) {
 
-      var view = App.MainServiceItemView.create({
-        controller: Em.Object.create({
-          content: Em.Object.create({})
-        })
-      });
-
       it('Maintenance for ' + testCase.serviceName + ' service', function () {
-        view.set('controller.content', Em.Object.create({
-          hostComponents: testCase.hostComponents,
-          serviceName: testCase.serviceName,
-          displayName: testCase.displayName,
-          serviceTypes: testCase.serviceTypes,
-          passiveState: 'OFF'
-        }));
+        view.reopen({
+          controller: Em.Object.create({
+            content: Em.Object.create({
+              hostComponents: testCase.hostComponents,
+              serviceName: testCase.serviceName,
+              displayName: testCase.displayName,
+              serviceTypes: testCase.serviceTypes,
+              passiveState: 'OFF'
+            }),
+            isSeveralClients: false,
+            clientComponents: []
+          }),
+          mastersExcludedCommands: mastersExcludedCommands,
+          hasConfigTab: hasConfigTab
+        });
         if (testCase.controller) {
           testCase.controller.forEach(function (item) {
             Object.keys(item).forEach(function (key) {
@@ -396,16 +407,9 @@ describe('App.MainServiceItemView', function () {
             });
           });
         }
-        view.set('controller.isSeveralClients', false);
-        view.set('controller.clientComponents', []);
-        view.set('mastersExcludedCommands', mastersExcludedCommands);
-        view.set('hasConfigTab', hasConfigTab);
         view.observeMaintenanceOnce();
         expect(view.get('maintenance')).to.eql(testCase.result);
-      });
-
-      it('Change isPassive option in maintenance for ' + testCase.serviceName + ' service', function () {
-        var oldMaintenance = JSON.parse(JSON.stringify(view.maintenance));
+        var oldMaintenance = JSON.parse(JSON.stringify(view.get('maintenance')));
         view.set('controller.content.passiveState', 'ON');
         view.observeMaintenanceOnce();
         expect(view.get('maintenance')).to.not.eql(oldMaintenance);