frankgh commented on code in PR #256:
URL: https://github.com/apache/cassandra-sidecar/pull/256#discussion_r2340404928


##########
client-common/src/main/java/org/apache/cassandra/sidecar/common/response/LifecycleInfoResponse.java:
##########
@@ -0,0 +1,119 @@
+/*
+ * 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.apache.cassandra.sidecar.common.response;
+
+import java.util.Objects;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import org.apache.cassandra.sidecar.common.data.LifecycleCassandraState;
+import org.apache.cassandra.sidecar.common.data.LifecycleStatus;
+
+/**
+ * A class representing a response for the {@code LifecycleInfoRequest}.
+ */
+public class LifecycleInfoResponse
+{
+
+    private final LifecycleCassandraState currentState;
+    private final LifecycleCassandraState desiredState;
+    private final LifecycleStatus status;
+    private final String lastUpdate;
+
+    /**
+     * Constructs a {@link LifecycleInfoResponse} object with the {@code 
currentState}, {@code intendedState}, {@code result},
+     * and {@code message}
+     *
+     * @param currentState the current state of the Cassandra node
+     * @param desiredState the intended state of the Cassandra node
+     * @param status the result of the last lifecycle operation
+     * @param lastUpdate a message providing additional context about the last 
lifecycle operation
+     */
+    @JsonCreator
+    public LifecycleInfoResponse(@JsonProperty("current_state") 
LifecycleCassandraState currentState,
+                                 @JsonProperty("desired_state") 
LifecycleCassandraState desiredState,
+                                 @JsonProperty("status") LifecycleStatus 
status,
+                                 @JsonProperty("last_update") String 
lastUpdate)
+    {
+        this.currentState = Objects.requireNonNull(currentState, "State must 
be non-null");
+        this.desiredState = desiredState;
+        this.status = status;
+        this.lastUpdate = lastUpdate;
+    }
+
+    /**
+     * @return the current state of the Cassandra node
+     */
+    @JsonProperty("current_state")
+    public LifecycleCassandraState currentState()
+    {
+        return currentState;
+    }
+
+    /**
+     * @return the intended state of the Cassandra node
+     */
+    @JsonProperty("desired_state")
+    public LifecycleCassandraState desiredState()
+    {
+        return desiredState;
+    }
+
+    /**
+     * @return the status of the last lifecycle state
+     */
+    @JsonProperty("status")
+    public LifecycleStatus status()
+    {
+        return status;
+    }
+
+    /**
+     * @return message providing additional context about the last lifecycle 
operation
+     */
+    @JsonProperty("last_update")
+    public String lastUpdate()
+    {
+        return lastUpdate;
+    }
+
+    public boolean equals(Object o)
+    {
+        if (this == o) return true;
+        if (!(o instanceof LifecycleInfoResponse)) return false;
+        LifecycleInfoResponse that = (LifecycleInfoResponse) o;
+        return currentState == that.currentState && desiredState == 
that.desiredState && status == that.status && Objects.equals(lastUpdate, 
that.lastUpdate);
+    }
+
+    public int hashCode()
+    {
+        return Objects.hash(currentState, desiredState, status, lastUpdate);
+    }
+
+    public String toString()

Review Comment:
   NIT
   ```suggestion
       @Override
       public String toString()
   ```



##########
server/src/main/java/org/apache/cassandra/sidecar/modules/LifecycleModule.java:
##########
@@ -0,0 +1,142 @@
+/*
+ * 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.apache.cassandra.sidecar.modules;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
+import com.google.inject.Singleton;
+import com.google.inject.multibindings.ProvidesIntoMap;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadata;
+import org.apache.cassandra.sidecar.common.ApiEndpointsV1;
+import org.apache.cassandra.sidecar.common.response.LifecycleInfoResponse;
+import org.apache.cassandra.sidecar.config.LifecycleConfiguration;
+import org.apache.cassandra.sidecar.config.ParameterizedClassConfiguration;
+import org.apache.cassandra.sidecar.config.SidecarConfiguration;
+import org.apache.cassandra.sidecar.exceptions.ConfigurationException;
+import org.apache.cassandra.sidecar.handlers.LifecycleInfoHandler;
+import org.apache.cassandra.sidecar.handlers.LifecycleUpdateHandler;
+import org.apache.cassandra.sidecar.lifecycle.LifecycleProvider;
+import org.apache.cassandra.sidecar.lifecycle.ProcessLifecycleProvider;
+import org.apache.cassandra.sidecar.modules.multibindings.KeyClassMapKey;
+import org.apache.cassandra.sidecar.modules.multibindings.VertxRouteMapKeys;
+import org.apache.cassandra.sidecar.routes.RouteBuilder;
+import org.apache.cassandra.sidecar.routes.VertxRoute;
+import org.eclipse.microprofile.openapi.annotations.Operation;
+import org.eclipse.microprofile.openapi.annotations.media.Content;
+import org.eclipse.microprofile.openapi.annotations.media.Schema;
+import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Provides the telemetry capability
+ */
+public class LifecycleModule extends AbstractModule
+{
+    @Provides
+    @Singleton
+    LifecycleProvider lifecycleProvider(SidecarConfiguration 
sidecarConfiguration)
+    {
+        LifecycleConfiguration lifecycleConfiguration = 
sidecarConfiguration.lifecycleConfiguration();
+        if (!lifecycleConfiguration.enabled())
+        {
+            return getNoopProvider();
+        }
+
+        ParameterizedClassConfiguration providerClass = 
lifecycleConfiguration.lifecycleProvider();
+        if (providerClass == null)
+        {
+            throw new ConfigurationException("Lifecycle management is enabled, 
but provider not set.");
+        }
+
+        if 
(providerClass.className().equalsIgnoreCase(ProcessLifecycleProvider.class.getName()))
+        {
+            Map<String, String> params = new HashMap<>();
+            Map<String, String> namedParams = providerClass.namedParameters();
+            if (namedParams != null)
+            {
+                params.putAll(namedParams);
+            }
+            return new ProcessLifecycleProvider(params);
+        }
+
+        throw new ConfigurationException("Unrecognized authorization provider 
" + providerClass.className() + " set");
+    }
+
+    private static @NotNull LifecycleProvider getNoopProvider()
+    {
+        return new LifecycleProvider()
+        {
+            public void start(InstanceMetadata instance)

Review Comment:
   can we add missing override annotations here?



##########
client-common/src/main/java/org/apache/cassandra/sidecar/common/data/LifecycleStatus.java:
##########
@@ -0,0 +1,44 @@
+/*
+ * 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.apache.cassandra.sidecar.common.data;
+
+/**
+ * Represents the lifecycle status of this instance relative to the desired 
state.
+ */
+public enum LifecycleStatus
+{
+    UNDEFINED,

Review Comment:
   can we add javadocs for all the enum values here ? something concise but 
that gives us an idea of when we have each state



##########
server/src/main/java/org/apache/cassandra/sidecar/lifecycle/ProcessLifecycleProvider.java:
##########
@@ -0,0 +1,52 @@
+/*
+ * 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.apache.cassandra.sidecar.lifecycle;
+
+import java.util.Map;
+
+import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadata;
+
+/**
+ * A {@link LifecycleProvider} that manages Cassandra instances as OS 
processes.
+ * <p>
+ * This implementation is a placeholder and is not yet implemented.
+ */
+public class ProcessLifecycleProvider implements LifecycleProvider
+{
+    public ProcessLifecycleProvider(Map<String, String> params)
+    {
+        // Params unused for now
+    }
+
+    public void start(InstanceMetadata instance)

Review Comment:
   ```suggestion
       @Override
       public void start(InstanceMetadata instance)
   ```



##########
server/src/main/java/org/apache/cassandra/sidecar/handlers/LifecycleUpdateHandler.java:
##########
@@ -0,0 +1,121 @@
+/*
+ * 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.apache.cassandra.sidecar.handlers;
+
+import java.util.Collections;
+import java.util.Set;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.json.Json;
+import io.vertx.core.net.SocketAddress;
+import io.vertx.ext.auth.authorization.Authorization;
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.handler.HttpException;
+import org.apache.cassandra.sidecar.acl.authorization.BasicPermissions;
+import org.apache.cassandra.sidecar.common.data.LifecycleCassandraState;
+import 
org.apache.cassandra.sidecar.common.request.data.NodeCommandRequestPayload;
+import org.apache.cassandra.sidecar.concurrent.ExecutorPools;
+import org.apache.cassandra.sidecar.exceptions.LifecycleTaskConflictException;
+import org.apache.cassandra.sidecar.lifecycle.LifecycleManager;
+import org.apache.cassandra.sidecar.utils.HttpExceptions;
+
+import org.apache.cassandra.sidecar.utils.InstanceMetadataFetcher;
+import org.jetbrains.annotations.NotNull;
+
+import static 
org.apache.cassandra.sidecar.common.data.LifecycleCassandraState.fromNodeCommandState;
+
+/**
+ * Handles {@code PUT /api/v1/cassandra/lifecycle} requests to start or stop a 
Cassandra node.
+ *
+ * <p> Expects a JSON payload:
+ * { "state": "start" } or { "state": "stop" }
+ * and will record the desired state. </p>
+ */
+@Singleton
+public class LifecycleUpdateHandler extends NodeCommandHandler implements 
AccessProtected
+{
+    private final LifecycleManager lifecycleManager;
+
+    @Inject
+    public LifecycleUpdateHandler(InstanceMetadataFetcher metadataFetcher, 
ExecutorPools executorPools, LifecycleManager lifecycleManager)
+    {
+        super(metadataFetcher, executorPools, null);
+        this.lifecycleManager = lifecycleManager;
+    }
+
+    @Override
+    public Set<Authorization> requiredAuthorizations()
+    {
+        return 
Collections.singleton(BasicPermissions.MODIFY_LIFECYCLE.toAuthorization());
+    }
+
+    @Override
+    protected void handleInternal(RoutingContext context,
+                                  HttpServerRequest httpRequest,
+                                  @NotNull String host,
+                                  SocketAddress remoteAddress,
+                                  NodeCommandRequestPayload request)
+    {
+        LifecycleCassandraState desiredState = 
fromNodeCommandState(request.state());
+        executorPools.service()
+                     .executeBlocking(() -> 
lifecycleManager.updateDesiredState(host, desiredState))
+                     .onSuccess(info ->
+                                {
+                                    switch (info.status())
+                                    {
+                                        case CONVERGED:
+                                            
context.response().putHeader("Content-Type", "application/json")
+                                                   
.setStatusCode(HttpResponseStatus.OK.code())
+                                                   .end(Json.encode(info));
+                                            break;
+                                        case CONVERGING:
+                                            
context.response().putHeader("Content-Type", "application/json")
+                                                   
.setStatusCode(HttpResponseStatus.ACCEPTED.code())
+                                                   .end(Json.encode(info));
+                                            break;
+                                        default:
+                                            logger.warn("{} request failed 
with unexpected result. request={}, remoteAddress={}, instance={}",

Review Comment:
   can we log the value of `info.status()` here as well. It would help 
determine whether we are diverging or if the status is undefined



##########
server/src/main/java/org/apache/cassandra/sidecar/lifecycle/ProcessLifecycleProvider.java:
##########
@@ -0,0 +1,52 @@
+/*
+ * 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.apache.cassandra.sidecar.lifecycle;
+
+import java.util.Map;
+
+import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadata;
+
+/**
+ * A {@link LifecycleProvider} that manages Cassandra instances as OS 
processes.
+ * <p>
+ * This implementation is a placeholder and is not yet implemented.
+ */
+public class ProcessLifecycleProvider implements LifecycleProvider
+{
+    public ProcessLifecycleProvider(Map<String, String> params)
+    {
+        // Params unused for now
+    }
+
+    public void start(InstanceMetadata instance)
+    {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public void stop(InstanceMetadata instance)
+    {
+        throw new UnsupportedOperationException("Not implemented yet");
+
+    }
+
+    public boolean isRunning(InstanceMetadata instance)

Review Comment:
   ```suggestion
       @Override
       public boolean isRunning(InstanceMetadata instance)
   ```



##########
client-common/src/main/java/org/apache/cassandra/sidecar/common/data/LifecycleCassandraState.java:
##########
@@ -0,0 +1,44 @@
+/*
+ * 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.apache.cassandra.sidecar.common.data;
+
+import static 
org.apache.cassandra.sidecar.common.request.data.NodeCommandRequestPayload.State;
+
+/**
+ * Represents the lifecycle state of a Cassandra instance.
+ */
+public enum LifecycleCassandraState
+{
+    UNKNOWN,

Review Comment:
   can we add javadocs with a small description here?



##########
server/src/main/java/org/apache/cassandra/sidecar/config/LifecycleConfiguration.java:
##########
@@ -0,0 +1,37 @@
+/*
+ * 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.apache.cassandra.sidecar.config;
+
+/**
+ * Configuration for Cassandra lifecycle management service
+ */
+public interface LifecycleConfiguration
+{
+
+    /**
+     * @return {@code true} if lifecycle management is enabled, {@code false} 
otherwise
+     */
+    Boolean enabled();

Review Comment:
   use primitive here
   ```suggestion
       boolean enabled();
   ```



##########
integration-framework/src/main/java/org/apache/cassandra/sidecar/lifecycle/InJvmDTestLifecycleProvider.java:
##########
@@ -0,0 +1,73 @@
+/*
+ * 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.apache.cassandra.sidecar.lifecycle;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.cassandra.distributed.api.IInstance;
+import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadata;
+
+/**
+ * Manages the lifecycle of JVM Dtest Casasndra instances.
+ * This should used for integration tests where Cassandra instances are 
started and stopped
+ */
+public class InJvmDTestLifecycleProvider implements LifecycleProvider
+{
+    private final Iterable<? extends IInstance> instances;
+
+    public InJvmDTestLifecycleProvider(Iterable<? extends IInstance> instances)
+    {
+        this.instances = instances;
+    }
+
+    public void start(InstanceMetadata instanceMetadata)
+    {
+        getInstance(instanceMetadata).startup();
+    }
+
+    public void stop(InstanceMetadata instanceMetadata)

Review Comment:
   ```suggestion
       @Override
       public void stop(InstanceMetadata instanceMetadata)
   ```



##########
server/src/main/java/org/apache/cassandra/sidecar/config/yaml/LifecycleConfigurationImpl.java:
##########
@@ -0,0 +1,70 @@
+/*
+ * 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.apache.cassandra.sidecar.config.yaml;
+
+import java.util.Collections;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.apache.cassandra.sidecar.config.LifecycleConfiguration;
+import org.apache.cassandra.sidecar.config.ParameterizedClassConfiguration;
+import org.apache.cassandra.sidecar.lifecycle.ProcessLifecycleProvider;
+
+/**
+ * Configuration for Cassandra lifecycle management service
+ */
+public class LifecycleConfigurationImpl implements LifecycleConfiguration
+{
+    private static final boolean DEFAULT_ENABLED = false;
+    private static final ParameterizedClassConfiguration 
DEFAULT_LIFECYCLE_PROVIDER =
+            new 
ParameterizedClassConfigurationImpl(ProcessLifecycleProvider.class.getName(), 
Collections.emptyMap());
+
+    protected final Boolean enabled;
+
+    protected final String directory;

Review Comment:
   is this something we'll be using in the future? 



##########
integration-framework/src/main/java/org/apache/cassandra/sidecar/lifecycle/InJvmDTestLifecycleProvider.java:
##########
@@ -0,0 +1,73 @@
+/*
+ * 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.apache.cassandra.sidecar.lifecycle;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.cassandra.distributed.api.IInstance;
+import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadata;
+
+/**
+ * Manages the lifecycle of JVM Dtest Casasndra instances.

Review Comment:
   typo
   ```suggestion
    * Manages the lifecycle of JVM Dtest Cassandra instances.
   ```



##########
server/src/main/java/org/apache/cassandra/sidecar/config/LifecycleConfiguration.java:
##########
@@ -0,0 +1,37 @@
+/*
+ * 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.apache.cassandra.sidecar.config;
+
+/**
+ * Configuration for Cassandra lifecycle management service
+ */
+public interface LifecycleConfiguration

Review Comment:
   we should add the configuration in `sidecar.yaml` and have user-facing 
documentation for the feature



##########
server/src/main/java/org/apache/cassandra/sidecar/lifecycle/LifecycleProvider.java:
##########
@@ -0,0 +1,34 @@
+/*
+ * 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.apache.cassandra.sidecar.lifecycle;
+
+import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadata;
+
+/**
+ * Manages the lifecycle of Cassandra instances through Sidecar
+ */
+public interface LifecycleProvider
+{
+    void start(InstanceMetadata instance);

Review Comment:
   let's add javadocs for all methods in public facing interfaces



##########
integration-framework/src/main/java/org/apache/cassandra/sidecar/lifecycle/InJvmDTestLifecycleProvider.java:
##########
@@ -0,0 +1,73 @@
+/*
+ * 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.apache.cassandra.sidecar.lifecycle;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.cassandra.distributed.api.IInstance;
+import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadata;
+
+/**
+ * Manages the lifecycle of JVM Dtest Casasndra instances.
+ * This should used for integration tests where Cassandra instances are 
started and stopped
+ */
+public class InJvmDTestLifecycleProvider implements LifecycleProvider
+{
+    private final Iterable<? extends IInstance> instances;
+
+    public InJvmDTestLifecycleProvider(Iterable<? extends IInstance> instances)
+    {
+        this.instances = instances;
+    }
+
+    public void start(InstanceMetadata instanceMetadata)

Review Comment:
   let's add override annotations in this class
   ```suggestion
       @Override
       public void start(InstanceMetadata instanceMetadata)
   ```



##########
server/src/main/java/org/apache/cassandra/sidecar/lifecycle/LifecycleTaskInfo.java:
##########
@@ -0,0 +1,58 @@
+/*
+ * 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.apache.cassandra.sidecar.lifecycle;
+
+import org.apache.cassandra.sidecar.common.data.LifecycleCassandraState;
+import org.apache.cassandra.sidecar.common.data.LifecycleStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Represents a lifecycle task
+ */
+public class LifecycleTaskInfo

Review Comment:
   this seems unused, will this be used in the future? and if so does it make 
sense to add it later when needed?



##########
client-common/src/main/java/org/apache/cassandra/sidecar/common/response/LifecycleInfoResponse.java:
##########
@@ -0,0 +1,119 @@
+/*
+ * 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.apache.cassandra.sidecar.common.response;
+
+import java.util.Objects;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import org.apache.cassandra.sidecar.common.data.LifecycleCassandraState;
+import org.apache.cassandra.sidecar.common.data.LifecycleStatus;
+
+/**
+ * A class representing a response for the {@code LifecycleInfoRequest}.
+ */
+public class LifecycleInfoResponse
+{
+
+    private final LifecycleCassandraState currentState;
+    private final LifecycleCassandraState desiredState;
+    private final LifecycleStatus status;
+    private final String lastUpdate;
+
+    /**
+     * Constructs a {@link LifecycleInfoResponse} object with the {@code 
currentState}, {@code intendedState}, {@code result},
+     * and {@code message}
+     *
+     * @param currentState the current state of the Cassandra node
+     * @param desiredState the intended state of the Cassandra node
+     * @param status the result of the last lifecycle operation
+     * @param lastUpdate a message providing additional context about the last 
lifecycle operation
+     */
+    @JsonCreator
+    public LifecycleInfoResponse(@JsonProperty("current_state") 
LifecycleCassandraState currentState,
+                                 @JsonProperty("desired_state") 
LifecycleCassandraState desiredState,
+                                 @JsonProperty("status") LifecycleStatus 
status,
+                                 @JsonProperty("last_update") String 
lastUpdate)
+    {
+        this.currentState = Objects.requireNonNull(currentState, "State must 
be non-null");
+        this.desiredState = desiredState;
+        this.status = status;
+        this.lastUpdate = lastUpdate;
+    }
+
+    /**
+     * @return the current state of the Cassandra node
+     */
+    @JsonProperty("current_state")
+    public LifecycleCassandraState currentState()
+    {
+        return currentState;
+    }
+
+    /**
+     * @return the intended state of the Cassandra node
+     */
+    @JsonProperty("desired_state")
+    public LifecycleCassandraState desiredState()
+    {
+        return desiredState;
+    }
+
+    /**
+     * @return the status of the last lifecycle state
+     */
+    @JsonProperty("status")
+    public LifecycleStatus status()
+    {
+        return status;
+    }
+
+    /**
+     * @return message providing additional context about the last lifecycle 
operation
+     */
+    @JsonProperty("last_update")
+    public String lastUpdate()
+    {
+        return lastUpdate;
+    }
+
+    public boolean equals(Object o)

Review Comment:
   NIT
   ```suggestion
       @Override
       public boolean equals(Object o)
   ```



##########
integration-framework/src/main/java/org/apache/cassandra/sidecar/lifecycle/InJvmDTestLifecycleProvider.java:
##########
@@ -0,0 +1,73 @@
+/*
+ * 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.apache.cassandra.sidecar.lifecycle;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.cassandra.distributed.api.IInstance;
+import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadata;
+
+/**
+ * Manages the lifecycle of JVM Dtest Casasndra instances.
+ * This should used for integration tests where Cassandra instances are 
started and stopped
+ */
+public class InJvmDTestLifecycleProvider implements LifecycleProvider
+{
+    private final Iterable<? extends IInstance> instances;
+
+    public InJvmDTestLifecycleProvider(Iterable<? extends IInstance> instances)
+    {
+        this.instances = instances;
+    }
+
+    public void start(InstanceMetadata instanceMetadata)
+    {
+        getInstance(instanceMetadata).startup();
+    }
+
+    public void stop(InstanceMetadata instanceMetadata)
+    {
+        try
+        {
+            // Synchronous to ensure JMX port is released before start again
+            getInstance(instanceMetadata).shutdown().get(1, TimeUnit.MINUTES);
+        }
+        catch (Exception e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public boolean isRunning(InstanceMetadata host)

Review Comment:
   ```suggestion
       @Override
       public boolean isRunning(InstanceMetadata host)
   ```



##########
client-common/src/main/java/org/apache/cassandra/sidecar/common/response/LifecycleInfoResponse.java:
##########
@@ -0,0 +1,119 @@
+/*
+ * 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.apache.cassandra.sidecar.common.response;
+
+import java.util.Objects;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import org.apache.cassandra.sidecar.common.data.LifecycleCassandraState;
+import org.apache.cassandra.sidecar.common.data.LifecycleStatus;
+
+/**
+ * A class representing a response for the {@code LifecycleInfoRequest}.
+ */
+public class LifecycleInfoResponse
+{
+
+    private final LifecycleCassandraState currentState;
+    private final LifecycleCassandraState desiredState;
+    private final LifecycleStatus status;
+    private final String lastUpdate;
+
+    /**
+     * Constructs a {@link LifecycleInfoResponse} object with the {@code 
currentState}, {@code intendedState}, {@code result},
+     * and {@code message}
+     *
+     * @param currentState the current state of the Cassandra node
+     * @param desiredState the intended state of the Cassandra node
+     * @param status the result of the last lifecycle operation
+     * @param lastUpdate a message providing additional context about the last 
lifecycle operation
+     */
+    @JsonCreator
+    public LifecycleInfoResponse(@JsonProperty("current_state") 
LifecycleCassandraState currentState,
+                                 @JsonProperty("desired_state") 
LifecycleCassandraState desiredState,
+                                 @JsonProperty("status") LifecycleStatus 
status,
+                                 @JsonProperty("last_update") String 
lastUpdate)
+    {
+        this.currentState = Objects.requireNonNull(currentState, "State must 
be non-null");
+        this.desiredState = desiredState;
+        this.status = status;
+        this.lastUpdate = lastUpdate;
+    }
+
+    /**
+     * @return the current state of the Cassandra node
+     */
+    @JsonProperty("current_state")
+    public LifecycleCassandraState currentState()
+    {
+        return currentState;
+    }
+
+    /**
+     * @return the intended state of the Cassandra node
+     */
+    @JsonProperty("desired_state")
+    public LifecycleCassandraState desiredState()
+    {
+        return desiredState;
+    }
+
+    /**
+     * @return the status of the last lifecycle state
+     */
+    @JsonProperty("status")
+    public LifecycleStatus status()
+    {
+        return status;
+    }
+
+    /**
+     * @return message providing additional context about the last lifecycle 
operation
+     */
+    @JsonProperty("last_update")
+    public String lastUpdate()
+    {
+        return lastUpdate;
+    }
+
+    public boolean equals(Object o)
+    {
+        if (this == o) return true;
+        if (!(o instanceof LifecycleInfoResponse)) return false;
+        LifecycleInfoResponse that = (LifecycleInfoResponse) o;
+        return currentState == that.currentState && desiredState == 
that.desiredState && status == that.status && Objects.equals(lastUpdate, 
that.lastUpdate);

Review Comment:
   NIT for better readability 
   ```suggestion
           return currentState == that.currentState
                  && desiredState == that.desiredState
                  && status == that.status
                  && Objects.equals(lastUpdate, that.lastUpdate);
   ```



##########
client-common/src/main/java/org/apache/cassandra/sidecar/common/response/LifecycleInfoResponse.java:
##########
@@ -0,0 +1,119 @@
+/*
+ * 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.apache.cassandra.sidecar.common.response;
+
+import java.util.Objects;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import org.apache.cassandra.sidecar.common.data.LifecycleCassandraState;
+import org.apache.cassandra.sidecar.common.data.LifecycleStatus;
+
+/**
+ * A class representing a response for the {@code LifecycleInfoRequest}.
+ */
+public class LifecycleInfoResponse
+{
+
+    private final LifecycleCassandraState currentState;
+    private final LifecycleCassandraState desiredState;
+    private final LifecycleStatus status;
+    private final String lastUpdate;
+
+    /**
+     * Constructs a {@link LifecycleInfoResponse} object with the {@code 
currentState}, {@code intendedState}, {@code result},
+     * and {@code message}
+     *
+     * @param currentState the current state of the Cassandra node
+     * @param desiredState the intended state of the Cassandra node
+     * @param status the result of the last lifecycle operation
+     * @param lastUpdate a message providing additional context about the last 
lifecycle operation
+     */
+    @JsonCreator
+    public LifecycleInfoResponse(@JsonProperty("current_state") 
LifecycleCassandraState currentState,
+                                 @JsonProperty("desired_state") 
LifecycleCassandraState desiredState,
+                                 @JsonProperty("status") LifecycleStatus 
status,
+                                 @JsonProperty("last_update") String 
lastUpdate)
+    {
+        this.currentState = Objects.requireNonNull(currentState, "State must 
be non-null");
+        this.desiredState = desiredState;
+        this.status = status;
+        this.lastUpdate = lastUpdate;
+    }
+
+    /**
+     * @return the current state of the Cassandra node
+     */
+    @JsonProperty("current_state")
+    public LifecycleCassandraState currentState()
+    {
+        return currentState;
+    }
+
+    /**
+     * @return the intended state of the Cassandra node
+     */
+    @JsonProperty("desired_state")
+    public LifecycleCassandraState desiredState()
+    {
+        return desiredState;
+    }
+
+    /**
+     * @return the status of the last lifecycle state
+     */
+    @JsonProperty("status")
+    public LifecycleStatus status()
+    {
+        return status;
+    }
+
+    /**
+     * @return message providing additional context about the last lifecycle 
operation
+     */
+    @JsonProperty("last_update")
+    public String lastUpdate()
+    {
+        return lastUpdate;
+    }
+
+    public boolean equals(Object o)
+    {
+        if (this == o) return true;
+        if (!(o instanceof LifecycleInfoResponse)) return false;
+        LifecycleInfoResponse that = (LifecycleInfoResponse) o;
+        return currentState == that.currentState && desiredState == 
that.desiredState && status == that.status && Objects.equals(lastUpdate, 
that.lastUpdate);
+    }
+
+    public int hashCode()

Review Comment:
   NIT
   ```suggestion
       @Override
       public int hashCode()
   ```



##########
server/src/main/java/org/apache/cassandra/sidecar/handlers/LifecycleUpdateHandler.java:
##########
@@ -0,0 +1,121 @@
+/*
+ * 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.apache.cassandra.sidecar.handlers;
+
+import java.util.Collections;
+import java.util.Set;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.json.Json;
+import io.vertx.core.net.SocketAddress;
+import io.vertx.ext.auth.authorization.Authorization;
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.handler.HttpException;
+import org.apache.cassandra.sidecar.acl.authorization.BasicPermissions;
+import org.apache.cassandra.sidecar.common.data.LifecycleCassandraState;
+import 
org.apache.cassandra.sidecar.common.request.data.NodeCommandRequestPayload;
+import org.apache.cassandra.sidecar.concurrent.ExecutorPools;
+import org.apache.cassandra.sidecar.exceptions.LifecycleTaskConflictException;
+import org.apache.cassandra.sidecar.lifecycle.LifecycleManager;
+import org.apache.cassandra.sidecar.utils.HttpExceptions;
+
+import org.apache.cassandra.sidecar.utils.InstanceMetadataFetcher;
+import org.jetbrains.annotations.NotNull;
+
+import static 
org.apache.cassandra.sidecar.common.data.LifecycleCassandraState.fromNodeCommandState;
+
+/**
+ * Handles {@code PUT /api/v1/cassandra/lifecycle} requests to start or stop a 
Cassandra node.
+ *
+ * <p> Expects a JSON payload:
+ * { "state": "start" } or { "state": "stop" }
+ * and will record the desired state. </p>
+ */
+@Singleton
+public class LifecycleUpdateHandler extends NodeCommandHandler implements 
AccessProtected
+{
+    private final LifecycleManager lifecycleManager;
+
+    @Inject
+    public LifecycleUpdateHandler(InstanceMetadataFetcher metadataFetcher, 
ExecutorPools executorPools, LifecycleManager lifecycleManager)
+    {
+        super(metadataFetcher, executorPools, null);
+        this.lifecycleManager = lifecycleManager;
+    }
+
+    @Override
+    public Set<Authorization> requiredAuthorizations()
+    {
+        return 
Collections.singleton(BasicPermissions.MODIFY_LIFECYCLE.toAuthorization());
+    }
+
+    @Override
+    protected void handleInternal(RoutingContext context,
+                                  HttpServerRequest httpRequest,
+                                  @NotNull String host,
+                                  SocketAddress remoteAddress,
+                                  NodeCommandRequestPayload request)
+    {
+        LifecycleCassandraState desiredState = 
fromNodeCommandState(request.state());
+        executorPools.service()
+                     .executeBlocking(() -> 
lifecycleManager.updateDesiredState(host, desiredState))
+                     .onSuccess(info ->
+                                {
+                                    switch (info.status())
+                                    {
+                                        case CONVERGED:
+                                            
context.response().putHeader("Content-Type", "application/json")
+                                                   
.setStatusCode(HttpResponseStatus.OK.code())
+                                                   .end(Json.encode(info));
+                                            break;
+                                        case CONVERGING:
+                                            
context.response().putHeader("Content-Type", "application/json")
+                                                   
.setStatusCode(HttpResponseStatus.ACCEPTED.code())
+                                                   .end(Json.encode(info));
+                                            break;
+                                        default:
+                                            logger.warn("{} request failed 
with unexpected result. request={}, remoteAddress={}, instance={}",
+                                                        
this.getClass().getSimpleName(), request, remoteAddress, host);
+                                            
context.response().putHeader("Content-Type", "application/json")
+                                                   
.setStatusCode(HttpResponseStatus.INTERNAL_SERVER_ERROR.code())
+                                                   .end(Json.encode(info));
+                                    }
+                                })
+                     .onFailure(cause -> processFailure(cause, context, host, 
remoteAddress, request));
+    }
+
+    protected void processFailure(Throwable cause, RoutingContext context, 
String host, SocketAddress remoteAddress, NodeCommandRequestPayload request)

Review Comment:
   ```suggestion
       @Override
       protected void processFailure(Throwable cause, RoutingContext context, 
String host, SocketAddress remoteAddress, NodeCommandRequestPayload request)
   ```



##########
server/src/main/java/org/apache/cassandra/sidecar/modules/LifecycleModule.java:
##########
@@ -0,0 +1,142 @@
+/*
+ * 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.apache.cassandra.sidecar.modules;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
+import com.google.inject.Singleton;
+import com.google.inject.multibindings.ProvidesIntoMap;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadata;
+import org.apache.cassandra.sidecar.common.ApiEndpointsV1;
+import org.apache.cassandra.sidecar.common.response.LifecycleInfoResponse;
+import org.apache.cassandra.sidecar.config.LifecycleConfiguration;
+import org.apache.cassandra.sidecar.config.ParameterizedClassConfiguration;
+import org.apache.cassandra.sidecar.config.SidecarConfiguration;
+import org.apache.cassandra.sidecar.exceptions.ConfigurationException;
+import org.apache.cassandra.sidecar.handlers.LifecycleInfoHandler;
+import org.apache.cassandra.sidecar.handlers.LifecycleUpdateHandler;
+import org.apache.cassandra.sidecar.lifecycle.LifecycleProvider;
+import org.apache.cassandra.sidecar.lifecycle.ProcessLifecycleProvider;
+import org.apache.cassandra.sidecar.modules.multibindings.KeyClassMapKey;
+import org.apache.cassandra.sidecar.modules.multibindings.VertxRouteMapKeys;
+import org.apache.cassandra.sidecar.routes.RouteBuilder;
+import org.apache.cassandra.sidecar.routes.VertxRoute;
+import org.eclipse.microprofile.openapi.annotations.Operation;
+import org.eclipse.microprofile.openapi.annotations.media.Content;
+import org.eclipse.microprofile.openapi.annotations.media.Schema;
+import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Provides the telemetry capability
+ */
+public class LifecycleModule extends AbstractModule
+{
+    @Provides
+    @Singleton
+    LifecycleProvider lifecycleProvider(SidecarConfiguration 
sidecarConfiguration)
+    {
+        LifecycleConfiguration lifecycleConfiguration = 
sidecarConfiguration.lifecycleConfiguration();
+        if (!lifecycleConfiguration.enabled())
+        {
+            return getNoopProvider();
+        }
+
+        ParameterizedClassConfiguration providerClass = 
lifecycleConfiguration.lifecycleProvider();
+        if (providerClass == null)
+        {
+            throw new ConfigurationException("Lifecycle management is enabled, 
but provider not set.");
+        }
+
+        if 
(providerClass.className().equalsIgnoreCase(ProcessLifecycleProvider.class.getName()))
+        {
+            Map<String, String> params = new HashMap<>();

Review Comment:
   any reason why we copy the params here?



##########
server/src/main/java/org/apache/cassandra/sidecar/modules/LifecycleModule.java:
##########
@@ -0,0 +1,142 @@
+/*
+ * 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.apache.cassandra.sidecar.modules;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
+import com.google.inject.Singleton;
+import com.google.inject.multibindings.ProvidesIntoMap;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadata;
+import org.apache.cassandra.sidecar.common.ApiEndpointsV1;
+import org.apache.cassandra.sidecar.common.response.LifecycleInfoResponse;
+import org.apache.cassandra.sidecar.config.LifecycleConfiguration;
+import org.apache.cassandra.sidecar.config.ParameterizedClassConfiguration;
+import org.apache.cassandra.sidecar.config.SidecarConfiguration;
+import org.apache.cassandra.sidecar.exceptions.ConfigurationException;
+import org.apache.cassandra.sidecar.handlers.LifecycleInfoHandler;
+import org.apache.cassandra.sidecar.handlers.LifecycleUpdateHandler;
+import org.apache.cassandra.sidecar.lifecycle.LifecycleProvider;
+import org.apache.cassandra.sidecar.lifecycle.ProcessLifecycleProvider;
+import org.apache.cassandra.sidecar.modules.multibindings.KeyClassMapKey;
+import org.apache.cassandra.sidecar.modules.multibindings.VertxRouteMapKeys;
+import org.apache.cassandra.sidecar.routes.RouteBuilder;
+import org.apache.cassandra.sidecar.routes.VertxRoute;
+import org.eclipse.microprofile.openapi.annotations.Operation;
+import org.eclipse.microprofile.openapi.annotations.media.Content;
+import org.eclipse.microprofile.openapi.annotations.media.Schema;
+import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Provides the telemetry capability
+ */
+public class LifecycleModule extends AbstractModule
+{
+    @Provides
+    @Singleton
+    LifecycleProvider lifecycleProvider(SidecarConfiguration 
sidecarConfiguration)
+    {
+        LifecycleConfiguration lifecycleConfiguration = 
sidecarConfiguration.lifecycleConfiguration();
+        if (!lifecycleConfiguration.enabled())
+        {
+            return getNoopProvider();
+        }
+
+        ParameterizedClassConfiguration providerClass = 
lifecycleConfiguration.lifecycleProvider();
+        if (providerClass == null)
+        {
+            throw new ConfigurationException("Lifecycle management is enabled, 
but provider not set.");
+        }
+
+        if 
(providerClass.className().equalsIgnoreCase(ProcessLifecycleProvider.class.getName()))

Review Comment:
   not to be addressed here, but we can always use reflection to instantiate 
these, similar to what Cassandra does.



##########
server/src/main/java/org/apache/cassandra/sidecar/lifecycle/ProcessLifecycleProvider.java:
##########
@@ -0,0 +1,52 @@
+/*
+ * 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.apache.cassandra.sidecar.lifecycle;
+
+import java.util.Map;
+
+import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadata;
+
+/**
+ * A {@link LifecycleProvider} that manages Cassandra instances as OS 
processes.
+ * <p>
+ * This implementation is a placeholder and is not yet implemented.
+ */
+public class ProcessLifecycleProvider implements LifecycleProvider
+{
+    public ProcessLifecycleProvider(Map<String, String> params)
+    {
+        // Params unused for now
+    }
+
+    public void start(InstanceMetadata instance)
+    {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public void stop(InstanceMetadata instance)

Review Comment:
   ```suggestion
       @Override
       public void stop(InstanceMetadata instance)
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to