Skip to content
This repository has been archived by the owner on Jul 11, 2022. It is now read-only.

Commit

Permalink
[BZ 1076104] Agent plugins don't require explicit purge to disappear
Browse files Browse the repository at this point in the history
from DB.
  • Loading branch information
metlos committed Mar 25, 2014
1 parent 9a4897c commit 742bf8d
Show file tree
Hide file tree
Showing 17 changed files with 149 additions and 176 deletions.
2 changes: 1 addition & 1 deletion modules/core/dbutils/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<description>Database schema setup, upgrade and other utilities</description>

<properties>
<db.schema.version>2.143</db.schema.version>
<db.schema.version>2.144</db.schema.version>
<rhq.ds.type-mapping>${rhq.test.ds.type-mapping}</rhq.ds.type-mapping>
<rhq.ds.server-name>${rhq.test.ds.server-name}</rhq.ds.server-name>
<rhq.ds.db-name>${rhq.test.ds.db-name}</rhq.ds.db-name>
Expand Down
11 changes: 11 additions & 0 deletions modules/core/dbutils/src/main/scripts/dbsetup/amps-schema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,15 @@
</index>
</table>

<table name="RHQ_PLUGIN_SERVER_ACK_DELETE">
<column name="PLUGIN_ID" type="INTEGER" required="true" references="RHQ_PLUGIN"/>
<column name="SERVER_ID" type="INTEGER" required="true" references="RHQ_SERVER"/>

<constraint name="RHQ_PLUGIN_SERVER_ACK_DELETE_PK">
<primaryKey>
<field ref="PLUGIN_ID"/>
<field ref="SERVER_ID"/>
</primaryKey>
</constraint>
</table>
</dbsetup>
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@
<xs:enumeration value="DOUBLE"/>
<xs:enumeration value="TIMESTAMP"/>
<xs:enumeration value="LONGVARCHAR"/>
<xs:enumeration value="BLOB"/>
<xs:enumeration value="CLOB"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
Expand Down Expand Up @@ -129,4 +131,4 @@
</xs:complexType>
</xs:element>

</xs:schema>
</xs:schema>
12 changes: 12 additions & 0 deletions modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2289,6 +2289,18 @@
<schemaSpec version="2.143">
<schema-alterColumn table="RHQ_RAW_CONFIG" column="config_ID" nullable="true"/>
</schemaSpec>

<schemaSpec version="2.144">
<schema-directSQL>
<statement desc="Create new RHQ_PLUGIN_SERVER_ACK_DELETE table">
CREATE TABLE RHQ_PLUGIN_SERVER_ACK_DELETE (
PLUGIN_ID INTEGER NOT NULL REFERENCES RHQ_PLUGIN,
SERVER_ID INTEGER NOT NULL REFERENCES RHQ_SERVER,
CONSTRAINT RHQ_PLUGIN_SERVER_ACK_DELETE_PK PRIMARY KEY (PLUGIN_ID, SERVER_ID)
)
</statement>
</schema-directSQL>
</schemaSpec>
</dbupgrade>
</target>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,30 @@
package org.rhq.core.domain.plugin;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.PrePersist;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.rhq.core.domain.cloud.Server;

/**
* Base plugin implementation that agent and server plugin implementations extend.
*
Expand Down Expand Up @@ -107,6 +115,11 @@ public class AbstractPlugin implements Serializable {
@Column(name = "CONTENT", nullable = true)
private byte[] content;

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "RHQ_PLUGIN_SERVER_ACK_DELETE", joinColumns = @JoinColumn(name = "PLUGIN_ID"),
inverseJoinColumns = @JoinColumn(name = "SERVER_ID"))
private Set<Server> serversAcknowledgedDelete;

public AbstractPlugin() {
}

Expand Down Expand Up @@ -357,6 +370,17 @@ public void setDeployment(PluginDeploymentType deployment) {
this.deployment = deployment;
}

/**
* The list of the servers that acknowledged that this plugin is deleted.
* Used to determine whether it is safe to purge the plugin from the database.
*/
public Set<Server> getServersAcknowledgedDelete() {
if (serversAcknowledgedDelete == null) {
serversAcknowledgedDelete = new HashSet<Server>();
}
return serversAcknowledgedDelete;
}

/**
* Returns the actual content of the plugin file. Be careful calling this
* in an entity context - the entire plugin file content will be loaded in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,11 @@
+ " WHERE p.ctime = -1 AND p.status = 'DELETED'"
),

@NamedQuery(name = Plugin.PURGE_PLUGINS, query = ""
+ " DELETE FROM Plugin p WHERE p IN (:plugins)")
@NamedQuery(
name = Plugin.QUERY_UNACKED_DELETED_PLUGINS,
query = "SELECT p FROM Plugin p WHERE p.status = 'DELETED' AND :serverId NOT MEMBER OF p.serversAcknowledgedDelete"
)

})
@Entity
public class Plugin extends AbstractPlugin {
Expand All @@ -259,7 +262,7 @@ public class Plugin extends AbstractPlugin {
public static final String UPDATE_PLUGINS_ENABLED_BY_IDS = "Plugin.updatePluginsEnabledByIds";
public static final String QUERY_FIND_BY_RESOURCE_TYPE_AND_CATEGORY = "Plugin.findByResourceType";
public static final String UPDATE_PLUGIN_ENABLED_BY_ID = "Plugin.updatePluginEnabledById";
public static final String PURGE_PLUGINS = "Plugin.purgePlugins";
public static final String QUERY_UNACKED_DELETED_PLUGINS = "Plugin.unackedDeletedPlugins";

public static final long PURGED = -1;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,6 @@ public class AgentPluginTableView extends TableSection<AgentPluginDataSource> {
private static final String FIELD_DEPLOYED = "deployed";
private static final String FIELD_VERSION = "version";

private boolean showDeleted = false;

public AgentPluginTableView() {
super(null);
setHeight100();
Expand Down Expand Up @@ -191,38 +189,6 @@ public void onFailure(Throwable caught) {
}
});

addTableAction(MSG.common_button_purge(), MSG.common_msg_areYouSure(), new AuthorizedTableAction(this,
TableActionEnablement.ANY, Permission.MANAGE_SETTINGS) {
public boolean isEnabled(ListGridRecord[] selection) {
if (showDeleted) {
return super.isEnabled(selection);
} else {
return false; // we aren't showing deleted plugins, so there is no plugin shown that can be purged anyway
}
}

public void executeAction(ListGridRecord[] selections, Object actionValue) {
int[] selectedIds = getSelectedIds(selections);
GWTServiceLookup.getPluginService().purgeAgentPlugins(selectedIds,
new AsyncCallback<ArrayList<String>>() {
@Override
public void onSuccess(ArrayList<String> result) {
Message msg = new Message(MSG.view_admin_plugins_purgedAgentPlugins(result.toString()),
Severity.Info);
CoreGUI.getMessageCenter().notify(msg);
refresh();
}

@Override
public void onFailure(Throwable caught) {
CoreGUI.getErrorHandler().handleError(
MSG.view_admin_plugins_purgedAgentPluginsFailure() + " " + caught.getMessage(), caught);
refreshTableInfo();
}
});
}
});

IButton scanForUpdatesButton = new EnhancedIButton(MSG.view_admin_plugins_scan());
scanForUpdatesButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
Expand All @@ -244,25 +210,9 @@ public void onFailure(Throwable caught) {
}
});

final IButton showDeletedButton = new EnhancedIButton(MSG.view_admin_plugins_showDeleted());
showDeletedButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
showDeleted = !showDeleted;
if (showDeleted) {
showDeletedButton.setTitle(MSG.view_admin_plugins_hideDeleted());
getListGrid().showField(FIELD_DEPLOYED);
} else {
showDeletedButton.setTitle(MSG.view_admin_plugins_showDeleted());
getListGrid().hideField(FIELD_DEPLOYED);
}
refresh();
}
});

PluginFileUploadForm pluginUploadForm = new PluginFileUploadForm(MSG.view_admin_plugins_upload(), true);

addExtraWidget(scanForUpdatesButton, true);
addExtraWidget(showDeletedButton, true);
addExtraWidget(pluginUploadForm, true);

super.configureTable();
Expand Down Expand Up @@ -336,12 +286,6 @@ public List<ListGridField> getListGridFields() {
enabledField.setAlign(Alignment.CENTER);
fields.add(enabledField);

ListGridField deployedField = new ListGridField(FIELD_DEPLOYED, MSG.view_admin_plugins_deployed());
deployedField.setType(ListGridFieldType.IMAGE);
deployedField.setAlign(Alignment.CENTER);
deployedField.setHidden(true);
fields.add(deployedField);

ListGridField versionField = new ListGridField(FIELD_VERSION, MSG.common_title_version());
versionField.setHidden(true);
fields.add(versionField);
Expand All @@ -351,15 +295,14 @@ public List<ListGridField> getListGridFields() {
descriptionField.setWidth("*");
lastUpdateField.setWidth("20%");
enabledField.setWidth(65);
deployedField.setWidth(75);
versionField.setWidth(100);

return fields;
}

@Override
protected void executeFetch(final DSRequest request, final DSResponse response, Criteria criteria) {
GWTServiceLookup.getPluginService().getAgentPlugins(showDeleted, new AsyncCallback<ArrayList<Plugin>>() {
GWTServiceLookup.getPluginService().getAgentPlugins(false, new AsyncCallback<ArrayList<Plugin>>() {
public void onSuccess(ArrayList<Plugin> result) {
response.setData(buildRecords(result));
response.setTotalRows(result.size());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,6 @@ public interface PluginGWTService extends RemoteService {
*/
ArrayList<String> deleteAgentPlugins(int[] selectedPluginIds) throws RuntimeException;

/**
* Schedules an agent plugin to be purged. Purging a plugin permanently deletes it from the database. Purging is done
* asynchronously and will not happen until all resource types defined by the plugin have first been purged. Plugins
* must first be deleted before they can be purged.
*
* @param selectedPluginIds the IDs of the plugins that are to be purged
* @return list of names of those plugins that were purged
*/
ArrayList<String> purgeAgentPlugins(int[] selectedPluginIds) throws RuntimeException;

/**
* Enables the server plugins with the given IDs.
*
Expand Down Expand Up @@ -213,4 +203,4 @@ ServerPluginControlResults invokeServerPluginControl(PluginKey serverPluginKey,
* @param jobsConfig the new config containing the scheduled jobs configuration
*/
void updateServerPluginScheduledJobs(PluginKey serverPluginKey, Configuration jobsConfig) throws RuntimeException;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -215,55 +215,6 @@ public ArrayList<String> deleteAgentPlugins(int[] selectedPluginIds) throws Runt
}
}

@Override
public ArrayList<String> purgeAgentPlugins(int[] selectedPluginIds) throws RuntimeException {
List<Plugin> allSelectedPlugins;
List<String> pluginsToDelete;

try {
allSelectedPlugins = getSelectedAgentPlugins(selectedPluginIds);

if (allSelectedPlugins.isEmpty()) {
log.debug("No agent plugins were selected. Nothing to purge.");
return new ArrayList<String>(0);
}

pluginsToDelete = new ArrayList<String>();
for (Plugin plugin : allSelectedPlugins) {
if (plugin.getStatus() != PluginStatusType.DELETED) {
pluginsToDelete.add(plugin.getDisplayName());
}
}
} catch (Throwable t) {
throw getExceptionToThrowToClient(t);
}

if (!pluginsToDelete.isEmpty()) {
throw new RuntimeException(
"Agent plugins must be deleted before they can be purged. The following plugins must first be deleted: "
+ pluginsToDelete + ". No plugins were purged.");
}

try {
ArrayList<String> pluginNames = new ArrayList<String>();
for (Plugin plugin : allSelectedPlugins) {
pluginNames.add(plugin.getDisplayName());
}

pluginManager.markPluginsForPurge(getSessionSubject(), getIds(allSelectedPlugins));

log.info("Preparing to purge agent plugins: " + pluginNames
+ ". This may take a few minutes since all type definitions from the plugins must "
+ "first be purged from the system. The plugins will still be visible until they have "
+ "been purged. Please note that you must not re-install the plugin while the purge is running, "
+ "as this is going to fail. Wait for re-add until the purge is done.");

return pluginNames;
} catch (Throwable t) {
throw getExceptionToThrowToClient(t);
}
}

@Override
public ArrayList<String> enableServerPlugins(int[] selectedPluginIds) throws RuntimeException {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -389,8 +389,10 @@ public void purgeAgentPlugins() {
pluginNames.add(plugin.getName());
}

Subject subject = EnterpriseFacesContextUtility.getSubject();
pluginMgr.markPluginsForPurge(subject, getIds(getSelectedAgentPlugins()));
//Plugin deletion has been reimplemented to not require an explicit purge operation
//This UI bean is now not used, so I just comment out the conflicting pieces of impl.
//Subject subject = EnterpriseFacesContextUtility.getSubject();
//pluginMgr.markPluginsForPurge(subject, getIds(getSelectedAgentPlugins()));
FacesContextUtility.addMessage(FacesMessage.SEVERITY_INFO, "Preparing to purge agent plugins: " +
pluginNames + ". This may take a few minutes since all type definitions from the plugins must " +
"first be purged from the system. The plugins will still be visible on this page until they have " +
Expand Down Expand Up @@ -456,4 +458,4 @@ private <T extends AbstractPlugin> List<T> sort(List<T> plugins) {
}
return new ArrayList<T>(map.values());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ protected void afterClassWork() throws Exception {
Subject overlord = LookupUtil.getSubjectManager().getOverlord();
List<Integer> doomedPlugins = new ArrayList<Integer>(pluginIds);
pluginMgr.deletePlugins(overlord, doomedPlugins);
pluginMgr.markPluginsForPurge(overlord, new ArrayList(doomedPlugins));
new PurgeResourceTypesJob().executeJobCode(null);
new PurgePluginsJob().executeJobCode(null);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ public void registerPlugins() throws Exception {
System.out.println("Purging plugins " + plugins + "...");
for (Plugin plugin : plugins) {
pluginMgr.deletePlugins(subjectMgr.getOverlord(), asList(plugin.getId()));
pluginMgr.markPluginsForPurge(subjectMgr.getOverlord(), asList(plugin.getId()));
}
new PurgeResourceTypesJob().execute(null);
new PurgePluginsJob().execute(null);
Expand Down Expand Up @@ -271,20 +270,21 @@ public void pluginPurgeCheckShouldUseExactMatchesInQuery() throws Exception {
pluginMgr.deletePlugins(subjectMgr.getOverlord(), asList(plugin3.getId()));
inventoryManager.purgeDeletedResourceType(resourceType);
inventoryManager.purgeDeletedResourceType(resourceTypeIgnored);
pluginMgr.markPluginsForPurge(subjectMgr.getOverlord(), asList(plugin3.getId()));

assertTrue("Expected " + plugin3 + " to be ready for purge since all its resource types have been purged "
+ "and the plugin has been marked for purge", pluginMgr.isReadyForPurge(plugin3));
}

//TODO make this work again
@Test(enabled = false, dependsOnMethods = { "deletePlugins" })
public void purgePlugins() throws Exception {
Plugin plugin1 = getPlugin(PLUGIN_1,
"Deleting a plugin should not remove it from the database");
Plugin plugin2 = getPlugin(PLUGIN_2,
"Deleting a plugin should not remove it from the database");

pluginMgr.markPluginsForPurge(subjectMgr.getOverlord(), asList(plugin1.getId(), plugin2.getId()));
//this method has been removed
//pluginMgr.markPluginsForPurge(subjectMgr.getOverlord(), asList(plugin1.getId(), plugin2.getId()));

assertEquals("Failed to purge plugins from the database", 1, pluginMgr.getPlugins().size());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -627,9 +627,9 @@ private void scheduleJobs() throws RuntimeException {
}

try {
// Do not check until we are up at least 1 min, and every 5 minutes thereafter.
// Do not check until we are up at least 1 min, and every 3 minutes thereafter.
final long initialDelay = 1000L * 60;
final long interval = 1000L * 60 * 5;
final long interval = 1000L * 60 * 3;
schedulerBean.scheduleSimpleRepeatingJob(PurgePluginsJob.class, true, false, initialDelay, interval);
} catch (Exception e) {
log.error("Cannot schedule purge plugins job.", e);
Expand Down

0 comments on commit 742bf8d

Please sign in to comment.