NodeGcController extends BaseGcController<V1Node> { @VisibleForTesting boolean isNodeEligibleForGc(V1Node node) { String nodeName = KubeUtil.getMetadataName(node.getMetadata()); Optional<V1NodeCondition> readyNodeConditionOpt = KubeUtil.findNodeCondition(node, READY); if (StringExt.isNotEmpty(nodeName) && readyNodeConditionOpt.isPresent()) { V1NodeCondition readyNodeCondition = readyNodeConditionOpt.get(); boolean isReadyConditionTimestampPastGracePeriod = hasConditionGracePeriodElapsed(readyNodeCondition, kubeControllerConfiguration.getNodeGcGracePeriodMs()); return isReadyConditionTimestampPastGracePeriod && isAgentInstanceNotAvailable(nodeName); } return false; } @Inject  NodeGcController(
            TitusRuntime titusRuntime,
            LocalScheduler scheduler,
            @Named(NODE_GC_CONTROLLER) FixedIntervalTokenBucketConfiguration tokenBucketConfiguration,
            @Named(NODE_GC_CONTROLLER) ControllerConfiguration controllerConfiguration,
            AgentManagementService agentManagementService,
            KubeApiFacade kubeApiFacade,
            KubeControllerConfiguration kubeControllerConfiguration
    ); @Override boolean shouldGc(); @Override List<V1Node> getItemsToGc(); @Override boolean gcItem(V1Node item); static final String NODE_GC_CONTROLLER; static final String NODE_GC_CONTROLLER_DESCRIPTION; }
@Test void nodeIsInWrongAccount() { V1Node node = new V1Node() .metadata(new V1ObjectMeta().name(NODE_NAME).annotations(Collections.singletonMap(NODE_LABEL_ACCOUNT_ID, INCORRECT_ACCOUNT))) .spec(new V1NodeSpec()) .status(null); Assertions.assertThat(nodeGcController.isNodeEligibleForGc(node)).isFalse(); }
@Test void nodeIsInvalid() { V1Node node = new V1Node() .metadata(new V1ObjectMeta().name(NODE_NAME).annotations(Collections.singletonMap(NODE_LABEL_ACCOUNT_ID, CORRECT_ACCOUNT))) .spec(new V1NodeSpec()) .status(null); Assertions.assertThat(nodeGcController.isNodeEligibleForGc(node)).isFalse(); }
@Test void nodeReadyConditionTimestampIsNotPastGracePeriod() { long now = clock.wallTime(); V1NodeCondition readyCondition = new V1NodeCondition().type(READY).lastHeartbeatTime(new DateTime(now)); V1Node node = new V1Node() .metadata(new V1ObjectMeta().name(NODE_NAME).annotations(Collections.singletonMap(NODE_LABEL_ACCOUNT_ID, CORRECT_ACCOUNT))) .spec(new V1NodeSpec()) .status(new V1NodeStatus().addConditionsItem(readyCondition)); Assertions.assertThat(nodeGcController.isNodeEligibleForGc(node)).isFalse(); }
@Test void nodeReadyConditionTimestampIsPastGracePeriod() { long now = clock.wallTime(); clock.advanceTime(Duration.ofMillis(NODE_GC_GRACE_PERIOD + 1)); V1NodeCondition readyCondition = new V1NodeCondition().type(READY).lastHeartbeatTime(new DateTime(now)); V1Node node = new V1Node() .metadata(new V1ObjectMeta().name(NODE_NAME).annotations(Collections.singletonMap(NODE_LABEL_ACCOUNT_ID, CORRECT_ACCOUNT))) .spec(new V1NodeSpec()) .status(new V1NodeStatus().addConditionsItem(readyCondition)); Assertions.assertThat(nodeGcController.isNodeEligibleForGc(node)).isTrue(); }
@Test void agentInstanceNotInAgentManagement() { long now = clock.wallTime(); clock.advanceTime(Duration.ofMillis(NODE_GC_GRACE_PERIOD + 1)); V1NodeCondition readyCondition = new V1NodeCondition().type(READY).lastHeartbeatTime(new DateTime(now)); V1Node node = new V1Node() .metadata(new V1ObjectMeta().name(NODE_NAME).annotations(Collections.singletonMap(NODE_LABEL_ACCOUNT_ID, CORRECT_ACCOUNT))) .spec(new V1NodeSpec()) .status(new V1NodeStatus().addConditionsItem(readyCondition)); when(agentManagementService.findAgentInstance(NODE_NAME)).thenReturn(Optional.empty()); Assertions.assertThat(nodeGcController.isNodeEligibleForGc(node)).isTrue(); }
@Test void agentInstanceIsNotStopped() { long now = clock.wallTime(); clock.advanceTime(Duration.ofMillis(NODE_GC_GRACE_PERIOD + 1)); V1NodeCondition readyCondition = new V1NodeCondition().type(READY).lastHeartbeatTime(new DateTime(now)); V1Node node = new V1Node() .metadata(new V1ObjectMeta().name(NODE_NAME).annotations(Collections.singletonMap(NODE_LABEL_ACCOUNT_ID, CORRECT_ACCOUNT))) .spec(new V1NodeSpec()) .status(new V1NodeStatus().addConditionsItem(readyCondition)); AgentInstance agentInstance = AgentInstance.newBuilder() .withId(NODE_NAME) .withDeploymentStatus(InstanceLifecycleStatus.newBuilder().withState(InstanceLifecycleState.Started).build()) .build(); when(agentManagementService.findAgentInstance(NODE_NAME)).thenReturn(Optional.of(agentInstance)); Assertions.assertThat(nodeGcController.isNodeEligibleForGc(node)).isFalse(); }
@Test void agentInstanceIsStopped() { long now = clock.wallTime(); clock.advanceTime(Duration.ofMillis(NODE_GC_GRACE_PERIOD + 1)); V1NodeCondition readyCondition = new V1NodeCondition().type(READY).lastHeartbeatTime(new DateTime(now)); V1Node node = new V1Node() .metadata(new V1ObjectMeta().name(NODE_NAME).annotations(Collections.singletonMap(NODE_LABEL_ACCOUNT_ID, CORRECT_ACCOUNT))) .spec(new V1NodeSpec()) .status(new V1NodeStatus().addConditionsItem(readyCondition)); AgentInstance agentInstance = AgentInstance.newBuilder() .withId(NODE_NAME) .withDeploymentStatus(InstanceLifecycleStatus.newBuilder().withState(InstanceLifecycleState.Stopped).build()) .build(); when(agentManagementService.findAgentInstance(NODE_NAME)).thenReturn(Optional.of(agentInstance)); Assertions.assertThat(nodeGcController.isNodeEligibleForGc(node)).isTrue(); }