azkaban-developers

Merge branch 'master' of https://github.com/logiclord/azkaban

8/30/2015 3:05:01 PM

Details

diff --git a/azkaban-common/src/main/java/azkaban/project/JdbcProjectLoader.java b/azkaban-common/src/main/java/azkaban/project/JdbcProjectLoader.java
index bde18ec..12b72dd 100644
--- a/azkaban-common/src/main/java/azkaban/project/JdbcProjectLoader.java
+++ b/azkaban-common/src/main/java/azkaban/project/JdbcProjectLoader.java
@@ -141,7 +141,7 @@ public class JdbcProjectLoader extends AbstractJdbcLoader implements
           runner.query(connection, ProjectResultHandler.SELECT_PROJECT_BY_ID,
               handler, id);
       if (projects.isEmpty()) {
-        throw new ProjectManagerException("No active project with id " + id
+        throw new ProjectManagerException("No project with id " + id
             + " exists in db.");
       }
 
@@ -169,6 +169,67 @@ public class JdbcProjectLoader extends AbstractJdbcLoader implements
     return project;
   }
 
+    /**
+     * Fetch first project with a given name {@inheritDoc}
+     *
+     * @see azkaban.project.ProjectLoader#fetchProjectByName(java.lang.String)
+     */
+    @Override
+    public Project fetchProjectByName(String name)
+        throws ProjectManagerException {
+        Connection connection = getConnection();
+
+        Project project = null;
+        try {
+            project = fetchProjectByName(connection, name);
+        } finally {
+            DbUtils.closeQuietly(connection);
+        }
+
+        return project;
+    }
+
+    private Project fetchProjectByName(Connection connection, String name)
+        throws ProjectManagerException {
+        QueryRunner runner = new QueryRunner();
+        // Fetch the project
+        Project project = null;
+        ProjectResultHandler handler = new ProjectResultHandler();
+        try {
+            List<Project> projects =
+                runner.query(connection,
+                    ProjectResultHandler.SELECT_PROJECT_BY_NAME, handler, name);
+            if (projects.isEmpty()) {
+                throw new ProjectManagerException(
+                    "No project with name " + name + " exists in db.");
+            }
+
+            project = projects.get(0);
+        } catch (SQLException e) {
+            logger.error(ProjectResultHandler.SELECT_PROJECT_BY_NAME
+                + " failed.");
+            throw new ProjectManagerException(
+                "Query for existing project failed. Project " + name, e);
+        }
+
+        // Fetch the user permissions
+        List<Triple<String, Boolean, Permission>> permissions =
+            fetchPermissionsForProject(connection, project);
+
+        for (Triple<String, Boolean, Permission> perm : permissions) {
+            if (perm.getThird().toFlags() != 0) {
+                if (perm.getSecond()) {
+                    project
+                        .setGroupPermission(perm.getFirst(), perm.getThird());
+                } else {
+                    project.setUserPermission(perm.getFirst(), perm.getThird());
+                }
+            }
+        }
+
+        return project;
+    }
+
   private List<Triple<String, Boolean, Permission>> fetchPermissionsForProject(
       Connection connection, Project project) throws ProjectManagerException {
     ProjectPermissionsResultHandler permHander =
@@ -1136,6 +1197,9 @@ public class JdbcProjectLoader extends AbstractJdbcLoader implements
 
   private static class ProjectResultHandler implements
       ResultSetHandler<List<Project>> {
+    private static String SELECT_PROJECT_BY_NAME =
+        "SELECT id, name, active, modified_time, create_time, version, last_modified_by, description, enc_type, settings_blob FROM projects WHERE name=?";
+
     private static String SELECT_PROJECT_BY_ID =
         "SELECT id, name, active, modified_time, create_time, version, last_modified_by, description, enc_type, settings_blob FROM projects WHERE id=?";
 
diff --git a/azkaban-common/src/main/java/azkaban/project/ProjectLoader.java b/azkaban-common/src/main/java/azkaban/project/ProjectLoader.java
index 371599f..aa886d6 100644
--- a/azkaban-common/src/main/java/azkaban/project/ProjectLoader.java
+++ b/azkaban-common/src/main/java/azkaban/project/ProjectLoader.java
@@ -48,6 +48,14 @@ public interface ProjectLoader {
   public Project fetchProjectById(int id) throws ProjectManagerException;
 
   /**
+   * Loads whole project, including permissions, by the project name.
+   * @param name
+   * @return
+   * @throws ProjectManagerException
+   */
+  public Project fetchProjectByName(String name) throws ProjectManagerException;
+
+  /**
    * Should create an empty project with the given name and user and adds it to
    * the data store. It will auto assign a unique id for this project if
    * successful.
@@ -269,5 +277,4 @@ public interface ProjectLoader {
       throws ProjectManagerException;
 
   void updateProjectSettings(Project project) throws ProjectManagerException;
-
 }
diff --git a/azkaban-common/src/main/java/azkaban/project/ProjectLogEvent.java b/azkaban-common/src/main/java/azkaban/project/ProjectLogEvent.java
index a19d013..5512291 100644
--- a/azkaban-common/src/main/java/azkaban/project/ProjectLogEvent.java
+++ b/azkaban-common/src/main/java/azkaban/project/ProjectLogEvent.java
@@ -32,7 +32,8 @@ public class ProjectLogEvent {
     UPLOADED(6),
     SCHEDULE(7),
     SLA(8),
-    PROXY_USER(9);
+    PROXY_USER(9),
+    PURGE(10);
 
     private int numVal;
 
@@ -64,6 +65,8 @@ public class ProjectLogEvent {
         return SLA;
       case 9:
         return PROXY_USER;
+      case 10:
+        return PURGE;
       case 128:
         return ERROR;
       default:
diff --git a/azkaban-common/src/main/java/azkaban/project/ProjectManager.java b/azkaban-common/src/main/java/azkaban/project/ProjectManager.java
index 89c6539..0f09b9c 100644
--- a/azkaban-common/src/main/java/azkaban/project/ProjectManager.java
+++ b/azkaban-common/src/main/java/azkaban/project/ProjectManager.java
@@ -196,13 +196,65 @@ public class ProjectManager {
     return allProjects;
   }
 
-  public Project getProject(String name) {
-    return projectsByName.get(name);
-  }
+    /**
+     * Checks if a project is active using project_name
+     *
+     * @param name
+     */
+    public Boolean isActiveProject(String name) {
+        return projectsByName.containsKey(name);
+    }
 
-  public Project getProject(int id) {
-    return projectsById.get(id);
-  }
+    /**
+     * Checks if a project is active using project_id
+     *
+     * @param name
+     */
+    public Boolean isActiveProject(int id) {
+        return projectsById.containsKey(id);
+    }
+
+    /**
+     * fetch active project from cache and inactive projects from db by
+     * project_name
+     *
+     * @param name
+     * @return
+     */
+    public Project getProject(String name) {
+        Project fetchedProject = null;
+        if (isActiveProject(name)) {
+            fetchedProject = projectsByName.get(name);
+        } else {
+            try {
+                fetchedProject = projectLoader.fetchProjectByName(name);
+            } catch (ProjectManagerException e) {
+                logger.error("Could not load project from store.", e);
+            }
+        }
+        return fetchedProject;
+    }
+
+    /**
+     * fetch active project from cache and inactive projects from db by
+     * project_id
+     *
+     * @param id
+     * @return
+     */
+    public Project getProject(int id) {
+        Project fetchedProject = null;
+        if (isActiveProject(id)) {
+            fetchedProject = projectsById.get(id);
+        } else {
+            try {
+                fetchedProject = projectLoader.fetchProjectById(id);
+            } catch (ProjectManagerException e) {
+                logger.error("Could not load project from store.", e);
+            }
+        }
+        return fetchedProject;
+    }
 
   public Project createProject(String projectName, String description,
       User creator) throws ProjectManagerException {
@@ -249,6 +301,25 @@ public class ProjectManager {
     return newProject;
   }
 
+    /**
+     * Permanently delete all project files and properties data for all versions
+     * of a project and log event in project_events table
+     *
+     * @param project
+     * @param deleter
+     * @return
+     * @throws ProjectManagerException
+     */
+    public synchronized Project purgeProject(Project project, User deleter)
+        throws ProjectManagerException {
+        projectLoader.cleanOlderProjectVersion(project.getId(),
+            project.getVersion() + 1);
+        projectLoader
+            .postEvent(project, EventType.PURGE, deleter.getUserId(), String
+                .format("Purged versions before %d", project.getVersion() + 1));
+        return project;
+    }
+
   public synchronized Project removeProject(Project project, User deleter)
       throws ProjectManagerException {
     projectLoader.removeProject(project, deleter.getUserId());
diff --git a/azkaban-common/src/test/java/azkaban/project/JdbcProjectLoaderTest.java b/azkaban-common/src/test/java/azkaban/project/JdbcProjectLoaderTest.java
index a7ef5f3..67efed9 100644
--- a/azkaban-common/src/test/java/azkaban/project/JdbcProjectLoaderTest.java
+++ b/azkaban-common/src/test/java/azkaban/project/JdbcProjectLoaderTest.java
@@ -28,7 +28,6 @@ import javax.sql.DataSource;
 import org.apache.commons.dbutils.DbUtils;
 import org.apache.commons.dbutils.QueryRunner;
 import org.apache.commons.dbutils.ResultSetHandler;
-
 import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Ignore;
@@ -214,6 +213,96 @@ public class JdbcProjectLoaderTest {
     DbUtils.closeQuietly(connection);
   }
 
+    /** Test case to validated permissions for fetchProjectByName **/
+    @Test
+    public void testPermissionRetrivalByFetchProjectByName()
+        throws ProjectManagerException {
+        if (!isTestSetup()) {
+            return;
+        }
+
+        ProjectLoader loader = createLoader();
+        String projectName = "mytestProject";
+        String projectDescription = "This is my new project";
+        User user = new User("testUser");
+
+        Project project =
+            loader.createNewProject(projectName, projectDescription, user);
+
+        Permission perm = new Permission(0x2);
+        loader.updatePermission(project, user.getUserId(), perm, false);
+        loader.updatePermission(project, "group", perm, true);
+
+        Permission permOverride = new Permission(0x6);
+        loader.updatePermission(project, user.getUserId(), permOverride, false);
+
+        Project fetchedProject = loader.fetchProjectByName(project.getName());
+        assertProjectMemberEquals(project, fetchedProject);
+        Assert.assertEquals(permOverride,
+            fetchedProject.getUserPermission(user.getUserId()));
+    }
+
+    /** Default Test case for fetchProjectByName **/
+    @Test
+    public void testProjectRetrievalByFetchProjectByName()
+        throws ProjectManagerException {
+        if (!isTestSetup()) {
+            return;
+        }
+
+        ProjectLoader loader = createLoader();
+        String projectName = "mytestProject";
+        String projectDescription = "This is my new project";
+        User user = new User("testUser");
+
+        Project project =
+            loader.createNewProject(projectName, projectDescription, user);
+
+        Project fetchedProject = loader.fetchProjectByName(project.getName());
+        assertProjectMemberEquals(project, fetchedProject);
+    }
+
+    /** Default Test case for fetchProjectByName **/
+    @Test
+    public void testDuplicateRetrivalByFetchProjectByName()
+        throws ProjectManagerException {
+        if (!isTestSetup()) {
+            return;
+        }
+
+        ProjectLoader loader = createLoader();
+        String projectName = "mytestProject";
+        String projectDescription = "This is my new project";
+        User user = new User("testUser");
+
+        Project project =
+            loader.createNewProject(projectName, projectDescription, user);
+
+        loader.removeProject(project, user.getUserId());
+
+        Project newProject =
+            loader.createNewProject(projectName, projectDescription, user);
+
+        Project fetchedProject = loader.fetchProjectByName(project.getName());
+        Assert.assertEquals(newProject.getId(), fetchedProject.getId());
+
+    }
+
+    /** Test case for NonExistantProject project fetch **/
+    @Test
+    public void testInvalidProjectByFetchProjectByName() {
+        if (!isTestSetup()) {
+            return;
+        }
+        ProjectLoader loader = createLoader();
+        try {
+            loader.fetchProjectByName("NonExistantProject");
+        } catch (ProjectManagerException ex) {
+            System.out.println("Test true");
+        }
+        Assert.fail("Expecting exception, but didn't get one");
+    }
+
   @Test
   public void testCreateProject() throws ProjectManagerException {
     if (!isTestSetup()) {
diff --git a/azkaban-common/src/test/java/azkaban/project/MockProjectLoader.java b/azkaban-common/src/test/java/azkaban/project/MockProjectLoader.java
index 909b82e..42d57b7 100644
--- a/azkaban-common/src/test/java/azkaban/project/MockProjectLoader.java
+++ b/azkaban-common/src/test/java/azkaban/project/MockProjectLoader.java
@@ -243,4 +243,10 @@ public class MockProjectLoader implements ProjectLoader {
     // TODO Auto-generated method stub
 
   }
+
+@Override
+public Project fetchProjectByName(String name) throws ProjectManagerException {
+    // TODO Auto-generated method stub
+    return null;
+}
 }
diff --git a/azkaban-webserver/src/main/java/azkaban/webapp/servlet/ProjectManagerServlet.java b/azkaban-webserver/src/main/java/azkaban/webapp/servlet/ProjectManagerServlet.java
index 435e1f9..dc0ef87 100644
--- a/azkaban-webserver/src/main/java/azkaban/webapp/servlet/ProjectManagerServlet.java
+++ b/azkaban-webserver/src/main/java/azkaban/webapp/servlet/ProjectManagerServlet.java
@@ -148,6 +148,8 @@ public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
         handleFlowPage(req, resp, session);
       } else if (hasParam(req, "delete")) {
         handleRemoveProject(req, resp, session);
+      } else if (hasParam(req, "purge")) {
+        handlePurgeProject(req, resp, session);
       } else if (hasParam(req, "download")) {
         handleDownloadProject(req, resp, session);
       } else {
@@ -518,6 +520,56 @@ public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
 
   }
 
+    /**
+     * validate readiness of a project and user permission and use
+     * projectManager to purge the project if things looks good
+     **/
+    private void handlePurgeProject(HttpServletRequest req,
+        HttpServletResponse resp, Session session) throws ServletException,
+        IOException {
+        User user = session.getUser();
+        String projectName = getParam(req, "project");
+        HashMap<String, Object> ret = new HashMap<String, Object>();
+        boolean isOperationSuccessful = true;
+
+        if (projectManager.isActiveProject(projectName)) {
+            ret.put("error", "Project " + projectName
+                + " should be deleted before purging");
+            isOperationSuccessful = false;
+        }
+
+        try {
+            Project project = null;
+
+            // project is already deleted
+            if (isOperationSuccessful) {
+                project = projectManager.getProject(projectName);
+                if (project == null) {
+                    ret.put("error", "no project with name : " + projectName);
+                    isOperationSuccessful = false;
+                }
+            }
+
+            // only eligible users can purge a project
+            if (isOperationSuccessful
+                && !hasPermission(project, user, Type.ADMIN)) {
+                ret.put("error", "Cannot purge. User '" + user.getUserId()
+                    + "' is not an ADMIN.");
+                isOperationSuccessful = false;
+            }
+
+            if (isOperationSuccessful) {
+                projectManager.purgeProject(project, user);
+            }
+        } catch (Exception e) {
+            ret.put("error", e.getMessage());
+            isOperationSuccessful = false;
+        }
+
+        ret.put("success", isOperationSuccessful);
+        this.writeJSON(resp, ret);
+    }
+
   private void handleRemoveProject(HttpServletRequest req,
       HttpServletResponse resp, Session session) throws ServletException,
       IOException {