Skip to content

Commit

Permalink
Remove DC collocation check on listSnapshots method
Browse files Browse the repository at this point in the history
  • Loading branch information
alessandro-verzicco authored and adejanovski committed Oct 15, 2021
1 parent 5b36f67 commit bee5dad
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 5 deletions.
Expand Up @@ -34,6 +34,7 @@
import io.cassandrareaper.resources.NodeStatsResource;
import io.cassandrareaper.resources.PingResource;
import io.cassandrareaper.resources.ReaperHealthCheck;
import io.cassandrareaper.resources.ReaperResource;
import io.cassandrareaper.resources.RepairRunResource;
import io.cassandrareaper.resources.RepairScheduleResource;
import io.cassandrareaper.resources.SnapshotResource;
Expand Down Expand Up @@ -214,6 +215,8 @@ public void run(ReaperApplicationConfiguration config, Environment environment)
environment.jersey().register(addRepairScheduleResource);
final SnapshotResource snapshotResource = new SnapshotResource(context, environment);
environment.jersey().register(snapshotResource);
final ReaperResource reaperResource = new ReaperResource(context);
environment.jersey().register(reaperResource);

final NodeStatsResource nodeStatsResource = new NodeStatsResource(context);
environment.jersey().register(nodeStatsResource);
Expand Down
Expand Up @@ -698,8 +698,7 @@ public Pair<Node, String> takeSnapshot(String snapshotName, Node host, String...
*/
public List<Snapshot> listSnapshots(Node host) throws ReaperException {
try {
if (context.config.getDatacenterAvailability().isInCollocatedMode()
&& context.jmxConnectionFactory.getHostConnectionCounters().getSuccessfulConnections(
if (context.jmxConnectionFactory.getHostConnectionCounters().getSuccessfulConnections(
host.getHostname()) >= 0) {
return SnapshotProxy.create(connect(host)).listSnapshots();
}
Expand Down
@@ -0,0 +1,59 @@
/**
* Copyright 2018-2018 The Last Pickle Ltd
*
*
* Licensed 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 io.cassandrareaper.resources;

import io.cassandrareaper.AppContext;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import com.google.common.collect.ImmutableMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Provides an endpoint to retrieve reaper configuration
*/
@Path("/reaper")
@Produces(MediaType.APPLICATION_JSON)
public final class ReaperResource {

private static final Logger LOG = LoggerFactory.getLogger(SnapshotResource.class);

private final AppContext context;

public ReaperResource(AppContext context) {
this.context = context;
}

/**
* Endpoint used to retrieve datacenterAvailability config parameter
*
* @return value of datacenterAvailability configuration parameter
*/
@GET
@Path("/datacenterAvailability")
public Response getDatacenterAvailability() {
return Response.ok().entity(
ImmutableMap.of("datacenterAvailability", context.config.getDatacenterAvailability())
).build();
}
}
@@ -0,0 +1,67 @@
/**
* Copyright 2018-2018 The Last Pickle Ltd
*
*
* Licensed 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 io.cassandrareaper.resources;

import io.cassandrareaper.AppContext;
import io.cassandrareaper.ReaperApplicationConfiguration.DatacenterAvailability;
import io.cassandrareaper.service.TestRepairConfiguration;
import io.cassandrareaper.storage.MemoryStorage;

import javax.ws.rs.core.Response;

import com.google.common.collect.BiMap;
import junit.framework.TestCase;
import org.eclipse.jetty.http.HttpStatus;
import org.junit.Test;


public class ReaperResourceTest extends TestCase {

@Test
public void testGetDatacenterAvailability() throws Exception {
final MockObjects mocks = initMocks();

ReaperResource resource = new ReaperResource(mocks.context);
Response response = resource.getDatacenterAvailability();
BiMap<String, DatacenterAvailability> config = (BiMap<String, DatacenterAvailability>) response.getEntity();

assertEquals(config.get("datacenterAvailability"), DatacenterAvailability.EACH);
assertEquals(HttpStatus.OK_200, response.getStatus());
}

private MockObjects initMocks() {
AppContext context = new AppContext();
context.storage = new MemoryStorage();
context.config = TestRepairConfiguration.defaultConfig();
context.config.setDatacenterAvailability(DatacenterAvailability.EACH);

return new MockObjects(context);
}

private static final class MockObjects {

final AppContext context;

MockObjects(
AppContext context) {
super();
this.context = context;
}

}
}
Expand Up @@ -23,6 +23,7 @@
import io.cassandrareaper.core.Node;
import io.cassandrareaper.core.Snapshot;
import io.cassandrareaper.jmx.ClusterFacade;
import io.cassandrareaper.jmx.HostConnectionCounters;
import io.cassandrareaper.jmx.JmxConnectionFactory;
import io.cassandrareaper.jmx.JmxProxy;
import io.cassandrareaper.jmx.JmxProxyTest;
Expand Down Expand Up @@ -110,14 +111,16 @@ public void testListSnapshot() throws InterruptedException, ReaperException, Cla
AppContext cxt = new AppContext();
cxt.config = TestRepairConfiguration.defaultConfig();
cxt.jmxConnectionFactory = mock(JmxConnectionFactory.class);
HostConnectionCounters connectionCounters = mock(HostConnectionCounters.class);
when(cxt.jmxConnectionFactory.connectAny(any(Collection.class))).thenReturn(proxy);
when(cxt.jmxConnectionFactory.getHostConnectionCounters()).thenReturn(connectionCounters);

List<Snapshot> result = SnapshotService
.create(cxt, SNAPSHOT_MANAGER_EXECUTOR)
.listSnapshots(Node.builder().withHostname("127.0.0.1").build());

Assertions.assertThat(result).isEmpty();
verify(storageMBean, times(0)).getSnapshotDetails();
verify(storageMBean, times(1)).getSnapshotDetails();
}

@Test
Expand Down
31 changes: 29 additions & 2 deletions src/ui/app/jsx/node-status.jsx
Expand Up @@ -22,7 +22,7 @@ import Streams from "jsx/streams";
import DroppedMessages from "jsx/dropped-messages";
import ClientRequestLatency from "jsx/client-request-latency";
import ActiveCompactions from "jsx/active-compactions";
import {DeleteStatusMessageMixin, humanFileSize, getUrlPrefix, toast} from "jsx/mixin";
import {DeleteStatusMessageMixin, humanFileSize, getUrlPrefix, toast, toastPermanent} from "jsx/mixin";
import Modal from 'react-bootstrap/lib/Modal';
import Button from 'react-bootstrap/lib/Button';
import Tooltip from 'react-bootstrap/lib/Tooltip';
Expand Down Expand Up @@ -57,7 +57,27 @@ const NodeStatus = CreateReactClass({
UNSAFE_componentWillMount: function() {
this._getNodeTokens();
},


componentDidMount: function() {
this.setState({'datacenterAvailability': 'ALL'});
this.setState({communicating: true});
$.ajax({
url: getUrlPrefix(window.top.location.pathname) + '/reaper/datacenterAvailability',
method: 'GET',
component: this,
success: function(data) {
let reaperConfig = data;
this.component.setState({'datacenterAvailability': reaperConfig.datacenterAvailability});
},
complete: function(data) {
this.component.setState({communicating: false});
},
error: function(data) {
toastPermanent(this.component._notificationSystem, "Error : " + data.responseText, "error", currentClusterValue);
}
});
},

_getNodeTokens: function() {
$.ajax({
url: this.state.urlPrefix + '/node/tokens/' + encodeURIComponent(this.props.clusterName) + '/' + encodeURIComponent(this.props.endpointStatus.endpoint),
Expand Down Expand Up @@ -315,6 +335,13 @@ const NodeStatus = CreateReactClass({
<div className="panel-body" id="snapshots">
<div className="row">
<div className="col-lg-12">
{this.state.datacenterAvailability != "ALL" &&
<div className="alert alert-warning" role="alert">
<p>
Reaper is configured with datacenterAvailability={this.state.datacenterAvailability}, only snapshots on reachable nodes are listed
</p>
</div>
}
<OverlayTrigger trigger="focus" placement="bottom" overlay={takeSnapshotClick}><button type="button" className="btn btn-md btn-success" style={takeSnapshotStyle}>Take a snapshot</button></OverlayTrigger>
<button type="button" className="btn btn-md btn-success" style={progressStyle} disabled>Taking a snapshot...</button>
</div>
Expand Down
24 changes: 24 additions & 0 deletions src/ui/app/jsx/snapshot-screen.jsx
Expand Up @@ -63,6 +63,23 @@ const SnapshotScreen = CreateReactClass({
this._listSnapshots(this.state.currentCluster);
}
this._notificationSystem = this.refs.notificationSystem;
this.setState({'datacenterAvailability': 'ALL'});
this.setState({communicating: true});
$.ajax({
url: getUrlPrefix(window.top.location.pathname) + '/reaper/datacenterAvailability',
method: 'GET',
component: this,
success: function(data) {
let reaperConfig = data;
this.component.setState({'datacenterAvailability': reaperConfig.datacenterAvailability});
},
complete: function(data) {
this.component.setState({communicating: false});
},
error: function(data) {
toastPermanent(this.component._notificationSystem, "Error : " + data.responseText, "error", currentClusterValue);
}
});
},

changeCurrentCluster : function(clusterName){
Expand Down Expand Up @@ -192,6 +209,13 @@ const SnapshotScreen = CreateReactClass({
<div className="row">
<div className="col-lg-12">
<h1 className="page-header">Snapshots</h1>
{this.state.datacenterAvailability != "ALL" &&
<div className="alert alert-warning" role="alert">
<p>
Reaper is configured with datacenterAvailability={this.state.datacenterAvailability}, only snapshots on reachable nodes are listed
</p>
</div>
}
</div>
</div>
<div className="col-lg-12">
Expand Down

0 comments on commit bee5dad

Please sign in to comment.