/* * Copyright 2020 the original author or authors. * * 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 * * https://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.springframework.cloud.deployer.spi.cloudfoundry; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; import org.cloudfoundry.client.CloudFoundryClient; import org.cloudfoundry.client.v2.Metadata; import org.cloudfoundry.client.v2.organizations.ListOrganizationsResponse; import org.cloudfoundry.client.v2.organizations.OrganizationResource; import org.cloudfoundry.client.v2.organizations.Organizations; import org.cloudfoundry.client.v2.spaces.ListSpacesResponse; import org.cloudfoundry.client.v2.spaces.SpaceResource; import org.cloudfoundry.client.v2.spaces.Spaces; import org.cloudfoundry.client.v3.Pagination; import org.cloudfoundry.client.v3.tasks.ListTasksResponse; import org.cloudfoundry.client.v3.tasks.TaskResource; import org.cloudfoundry.client.v3.tasks.TaskState; import org.cloudfoundry.client.v3.tasks.Tasks; import org.cloudfoundry.operations.CloudFoundryOperations; import org.junit.jupiter.api.Test; import org.springframework.cloud.deployer.spi.core.RuntimeEnvironmentInfo; import reactor.core.publisher.Mono; public class CloudFoundryTaskLauncherCachingTests { @Test public void testOrgSpaceCachingRetries() { CloudFoundryClient client = mock(CloudFoundryClient.class); AtomicBoolean spaceError = new AtomicBoolean(true); AtomicBoolean orgError = new AtomicBoolean(true); Spaces spaces = mock(Spaces.class); given(client.spaces()).willReturn(spaces); given(spaces.list(any())).willReturn(listSpacesResponse(spaceError)); Organizations organizations = mock(Organizations.class); given(client.organizations()).willReturn(organizations); given(organizations.list(any())).willReturn(listOrganizationsResponse(orgError)); Tasks tasks = mock(Tasks.class); given(client.tasks()).willReturn(tasks); given(tasks.list(any())).willReturn(runningTasksResponse()); CloudFoundryDeploymentProperties deploymentProperties = new CloudFoundryDeploymentProperties(); CloudFoundryOperations operations = mock(CloudFoundryOperations.class); RuntimeEnvironmentInfo runtimeEnvironmentInfo = mock(RuntimeEnvironmentInfo.class); Map<String, String> orgAndSpace = new HashMap<>(); orgAndSpace.put(CloudFoundryPlatformSpecificInfo.ORG, "this-org"); orgAndSpace.put(CloudFoundryPlatformSpecificInfo.SPACE, "this-space"); given(runtimeEnvironmentInfo.getPlatformSpecificInfo()).willReturn(orgAndSpace); CloudFoundryTaskLauncher launcher = new CloudFoundryTaskLauncher(client, deploymentProperties, operations, runtimeEnvironmentInfo); Throwable thrown1 = catchThrowable(() -> { launcher.getRunningTaskExecutionCount(); }); assertThat(thrown1).isInstanceOf(RuntimeException.class).hasNoCause(); // space should still error orgError.set(false); Throwable thrown2 = catchThrowable(() -> { launcher.getRunningTaskExecutionCount(); }); assertThat(thrown2).isInstanceOf(RuntimeException.class).hasNoCause(); // cache should now be getting cleared as space doesn't error spaceError.set(false); Throwable thrown3 = catchThrowable(() -> { launcher.getRunningTaskExecutionCount(); }); assertThat(thrown3).doesNotThrowAnyException(); assertThat(launcher.getRunningTaskExecutionCount()).isEqualTo(1); } private Mono<ListOrganizationsResponse> listOrganizationsResponse(AtomicBoolean error) { // defer so that we can conditionally throw within mono return Mono.defer(() -> { if (error.get()) { throw new RuntimeException(); } ListOrganizationsResponse response = ListOrganizationsResponse.builder() .addAllResources(Collections.<OrganizationResource>singletonList( OrganizationResource.builder() .metadata(Metadata.builder().id("123").build()).build()) ) .build(); return Mono.just(response); }); } private Mono<ListSpacesResponse> listSpacesResponse(AtomicBoolean error) { // defer so that we can conditionally throw within mono return Mono.defer(() -> { if (error.get()) { throw new RuntimeException(); } ListSpacesResponse response = ListSpacesResponse.builder() .addAllResources(Collections.<SpaceResource>singletonList( SpaceResource.builder() .metadata(Metadata.builder().id("123").build()).build()) ) .build(); return Mono.just(response); }); } private Mono<ListTasksResponse> runningTasksResponse() { List<TaskResource> taskResources = new ArrayList<>(); for (int i = 0; i < 1; i++) { taskResources.add(TaskResource.builder() .name("task-" + i) .dropletId(UUID.randomUUID().toString()) .id(UUID.randomUUID().toString()) .diskInMb(2048) .sequenceId(i) .state(TaskState.RUNNING) .memoryInMb(2048) .createdAt(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))) .build()); } ListTasksResponse listTasksResponse = ListTasksResponse.builder().resources(taskResources) .pagination(Pagination.builder().totalResults(taskResources.size()).build()) .build(); return Mono.just(listTasksResponse); } }