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 {