Skip to content

Commit

Permalink
Delete action for Container Repository Images and Vault Secrets
Browse files Browse the repository at this point in the history
  • Loading branch information
petrovic-d committed Aug 9, 2024
1 parent 3b74c2f commit 9804941
Show file tree
Hide file tree
Showing 9 changed files with 294 additions and 8 deletions.
8 changes: 8 additions & 0 deletions enterprise/cloud.oracle/nbproject/project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,14 @@
<specification-version>2.29</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.openide.actions</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<specification-version>6.65</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.openide.awt</code-name-base>
<build-prerequisite/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.cloud.oracle;

import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;

/**
*
* @author Dusan Petrovic
*/
public class NotificationUtils {

public static boolean confirmAction(String message) {
NotifyDescriptor.Confirmation msg = new NotifyDescriptor.Confirmation(message, NotifyDescriptor.YES_NO_OPTION);
Object choice = DialogDisplayer.getDefault().notify(msg);
return choice == NotifyDescriptor.YES_OPTION || choice == NotifyDescriptor.OK_OPTION;
}

public static void showErrorMessage(String message) {
StringBuilder sb = new StringBuilder("Error\n");
sb.append(message);
showMessage(sb.toString());
}

public static void showMessage(String message) {
DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message(message));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ public void refresh() {
public void update(OCIItem item) {

}

public OCIItem getItem() {
return this.item;
}

@Override
public Node.Handle getHandle() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,41 @@
package org.netbeans.modules.cloud.oracle.developer;

import com.oracle.bmc.artifacts.ArtifactsClient;
import com.oracle.bmc.artifacts.requests.DeleteContainerImageRequest;
import com.oracle.bmc.artifacts.requests.ListContainerImagesRequest;
import com.oracle.bmc.artifacts.responses.DeleteContainerImageResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import javax.swing.Action;
import org.netbeans.modules.cloud.oracle.ChildrenProvider;
import org.netbeans.modules.cloud.oracle.NodeProvider;
import static org.netbeans.modules.cloud.oracle.NotificationUtils.confirmAction;
import static org.netbeans.modules.cloud.oracle.NotificationUtils.showErrorMessage;
import static org.netbeans.modules.cloud.oracle.NotificationUtils.showMessage;
import org.netbeans.modules.cloud.oracle.OCIManager;
import org.netbeans.modules.cloud.oracle.OCINode;
import org.netbeans.modules.cloud.oracle.items.OCID;
import org.openide.actions.DeleteAction;
import org.openide.nodes.Children;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
import org.openide.util.actions.SystemAction;

/**
*
* @author Jan Horvath
*/
@NbBundle.Messages({
"ContainerTagDesc=Pull URL: {0}\nVersion: {1}\nDigest: {2}"
"ContainerTagDesc=Pull URL: {0}\nVersion: {1}\nDigest: {2}",
"# {0} - [OCIItem name]",
"MSG_ConfirmDeleteAction=Are you sure that you want to delete {0}.",
"# {0} - [OCIItem name]",
"MSG_DeleteActionFailed=Failed to delete {0}.",
"# {0} - [OCIItem name]",
"MSG_DeleteActionSuccess=Successfully deleted {0}."
})
public class ContainerTagNode extends OCINode {
private static final String CONTAINER_TAG_ICON = "org/netbeans/modules/cloud/oracle/resources/containertag.svg"; // NOI18N
Expand All @@ -49,6 +69,43 @@ public ContainerTagNode(ContainerTagItem tag) {
public static NodeProvider<ContainerTagItem> createNode() {
return ContainerTagNode::new;
}

@Override
public Action[] getActions(boolean context) {
Action[] actions = super.getActions(context);
List<Action> actionList = new ArrayList<>(Arrays.asList(actions));
actionList.add(SystemAction.get(DeleteAction.class));
return actionList.toArray(Action[]::new);
}

@Override
public boolean canDestroy() {
return true;
}

@Override
public void destroy() throws IOException {
RequestProcessor.getDefault().post(() -> {
if (!confirmAction(Bundle.MSG_ConfirmDeleteAction(this.getName()))) {
return;
}
ArtifactsClient client = OCIManager.getDefault().getActiveSession().newClient(ArtifactsClient.class);
DeleteContainerImageRequest request = DeleteContainerImageRequest.builder()
.imageId(this.getItem().getKey().getValue())
.build();
DeleteContainerImageResponse response = client.deleteContainerImage(request);
if (response.get__httpStatusCode__() != 204) {
showErrorMessage(Bundle.MSG_DeleteActionFailed(this.getName()));
return;
}

if (this.getParentNode() instanceof OCINode) {
((OCINode)this.getParentNode()).refresh();
}
showMessage(Bundle.MSG_DeleteActionSuccess(this.getName()));
});

}

/**
* Retrieves list of Vaults belonging to a given Compartment.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package org.netbeans.modules.cloud.oracle.vault;

import java.util.Date;
import org.netbeans.modules.cloud.oracle.items.OCID;
import org.netbeans.modules.cloud.oracle.items.OCIItem;

Expand All @@ -26,18 +27,40 @@
* @author Jan Horvath
*/
public class SecretItem extends OCIItem {

private String lifecycleState;
private Date deletionTime;

public SecretItem(OCID id, String compartmentId, String name) {
public SecretItem(OCID id, String compartmentId, String name, String lifecycleState, Date deletionTime) {
super(id, compartmentId, name);
this.lifecycleState = lifecycleState;
this.deletionTime = deletionTime;
}

public SecretItem() {
public SecretItem() {
super();
this.lifecycleState = null;
this.deletionTime = null;
}

@Override
public int maxInProject() {
return Integer.MAX_VALUE;
}

public Date getDeletionTime() {
return this.deletionTime;
}

void setDeletionTime(Date deletionTime) {
this.deletionTime = deletionTime;
}

public String getLifecycleState() {
return this.lifecycleState;
}

void setLifecycleState(String lifecycleState) {
this.lifecycleState = lifecycleState;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,162 @@
*/
package org.netbeans.modules.cloud.oracle.vault;

import com.oracle.bmc.keymanagement.KmsVaultClient;
import com.oracle.bmc.vault.VaultsClient;
import com.oracle.bmc.vault.model.ScheduleSecretDeletionDetails;
import com.oracle.bmc.vault.model.Secret;
import com.oracle.bmc.vault.model.SecretSummary.LifecycleState;
import com.oracle.bmc.vault.requests.GetSecretRequest;
import com.oracle.bmc.vault.requests.ListSecretsRequest;
import com.oracle.bmc.vault.requests.ScheduleSecretDeletionRequest;
import com.oracle.bmc.vault.responses.GetSecretResponse;
import com.oracle.bmc.vault.responses.ScheduleSecretDeletionResponse;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import javax.swing.Action;
import org.netbeans.modules.cloud.oracle.ChildrenProvider;
import org.netbeans.modules.cloud.oracle.NodeProvider;
import static org.netbeans.modules.cloud.oracle.OCIManager.getDefault;
import static org.netbeans.modules.cloud.oracle.NotificationUtils.confirmAction;
import static org.netbeans.modules.cloud.oracle.NotificationUtils.showErrorMessage;
import static org.netbeans.modules.cloud.oracle.NotificationUtils.showMessage;
import org.netbeans.modules.cloud.oracle.OCIManager;
import org.netbeans.modules.cloud.oracle.OCINode;
import org.netbeans.modules.cloud.oracle.items.OCID;
import org.netbeans.modules.cloud.oracle.items.OCIItem;
import org.openide.actions.DeleteAction;
import org.openide.nodes.Children;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
import org.openide.util.actions.SystemAction;

/**
*
* @author Jan Horvath
*/
@NbBundle.Messages({
"SecretNodeDesc=Valut Secret: {0}\nLifecycle State: {1}{2}",
"# {0} - [OCIItem name]",
"MSG_ConfirmDeleteAction=Are you sure that you want to schedule deletion of {0}",
"# {0} - [OCIItem name]",
"MSG_DeleteActionFailed=Failed to schedule deletion of {0}.",
"# {0} - [OCIItem name]", "# {1} - [Scheduled deletion time]",
"MSG_DeleteActionSuccess=Successfully scheduled deletion of {0} at {1}."
})
public class SecretNode extends OCINode {
private static final String SECRET_ICON = "org/netbeans/modules/cloud/oracle/resources/secret.svg"; // NOI18N
private static final SimpleDateFormat DELETION_TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

public SecretNode(SecretItem vault) {
super(vault, Children.LEAF);
setName(vault.getName());
setDisplayName(vault.getName());
setIconBaseWithExtension(SECRET_ICON);
setShortDescription(vault.getDescription());
setShortDescription(
createShortDescription(
vault.getLifecycleState(),
vault.getDeletionTime()));
}

private String createShortDescription(String state, Date deletionTime) {
if (deletionTime != null) {
return Bundle.SecretNodeDesc(this.getItem().getName(), state, getDeletionTimeInfo(deletionTime));
}
return Bundle.SecretNodeDesc(this.getItem().getName(), state, "");
}

private String getDeletionTimeInfo(Date deletionTime) {
return "\nDeletion time: " + DELETION_TIME_FORMAT.format(deletionTime);
}

public static NodeProvider<SecretItem> createNode() {
return SecretNode::new;
}

@Override
public void update(OCIItem item) {
SecretItem orig = (SecretItem) item;
VaultsClient client = OCIManager.getDefault().getActiveProfile().newClient(VaultsClient.class);
GetSecretRequest request = GetSecretRequest.builder()
.secretId(orig.getKey().getValue())
.build();

GetSecretResponse response = client.getSecret(request);
Secret secret = response.getSecret();
orig.setLifecycleState(secret.getLifecycleState().getValue());
orig.setDeletionTime(secret.getTimeOfDeletion());
setShortDescription(
createShortDescription(
orig.getLifecycleState(),
orig.getDeletionTime()));
}

@Override
public Action[] getActions(boolean context) {
Action[] actions = super.getActions(context);
List<Action> actionList = new ArrayList<>(Arrays.asList(actions));
actionList.add(SystemAction.get(DeleteAction.class));
return actionList.toArray(Action[]::new);
}

@Override
public boolean canDestroy() {
return ((SecretItem)this.getItem()).getLifecycleState().equals(LifecycleState.Active.getValue());
}

@Override
public void destroy() throws IOException {
RequestProcessor.getDefault().post(() -> {
if (!confirmAction(Bundle.MSG_ConfirmDeleteAction(this.getName()))) {
return;
}

VaultsClient client = OCIManager.getDefault().getActiveSession().newClient(VaultsClient.class);
Date deletionTime = getDeletionTime();
ScheduleSecretDeletionRequest request = buildScheduleDeletionRequest(deletionTime);
ScheduleSecretDeletionResponse response = client.scheduleSecretDeletion(request);

if (response.get__httpStatusCode__() != 200) {
showErrorMessage(Bundle.MSG_DeleteActionFailed(this.getName()));
return;
}

updateToPendingState(deletionTime);
showMessage(Bundle.MSG_DeleteActionSuccess(
this.getName(),
DELETION_TIME_FORMAT.format(deletionTime)));
});
}

private ScheduleSecretDeletionRequest buildScheduleDeletionRequest(Date deletionTime) {
ScheduleSecretDeletionDetails scheduleSecretDeletionDetails = ScheduleSecretDeletionDetails.builder()
.timeOfDeletion(deletionTime)
.build();

return ScheduleSecretDeletionRequest.builder()
.secretId(this.getItem().getKey().getValue())
.scheduleSecretDeletionDetails(scheduleSecretDeletionDetails)
.build();
}

private void updateToPendingState(Date deletionTime) {
((SecretItem) this.getItem()).setLifecycleState(LifecycleState.PendingDeletion.getValue());
setShortDescription(
createShortDescription(
LifecycleState.PendingDeletion.getValue(),
deletionTime));
}

private Date getDeletionTime() {
LocalDateTime tomorrow = LocalDateTime.now().plusDays(1).plusHours(1);
return Date.from(tomorrow.atZone(ZoneId.systemDefault()).toInstant());
}

/**
* Retrieves list of Secrets belonging to a given Vault.
*
Expand All @@ -69,7 +195,9 @@ public static ChildrenProvider.SessionAware<VaultItem, SecretItem> getSecrets()
.map(d -> new SecretItem(
OCID.of(d.getId(), "Vault/Secret"), //NOI18N
d.getCompartmentId(),
d.getSecretName())
d.getSecretName(),
d.getLifecycleState().getValue(),
d.getTimeOfDeletion())
)
.collect(Collectors.toList());
};
Expand Down
Loading

0 comments on commit 9804941

Please sign in to comment.