diff --git a/projects/reactor/pom.xml b/projects/reactor/pom.xml index f181053f..7b827a0c 100644 --- a/projects/reactor/pom.xml +++ b/projects/reactor/pom.xml @@ -17,6 +17,7 @@ UTF-8 1.8 + 4.3.0.RELEASE @@ -25,6 +26,16 @@ releaser-spring ${project.version} + + org.cloudfoundry + cloudfoundry-client-reactor + ${cloudfoundry-client.version} + + + org.cloudfoundry + cloudfoundry-operations + ${cloudfoundry-client.version} + org.springframework.boot diff --git a/projects/reactor/src/main/java/releaser/reactor/ReactorConfiguration.java b/projects/reactor/src/main/java/releaser/reactor/ReactorConfiguration.java new file mode 100644 index 00000000..434360a1 --- /dev/null +++ b/projects/reactor/src/main/java/releaser/reactor/ReactorConfiguration.java @@ -0,0 +1,101 @@ +/* + * Copyright 2013-2019 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 releaser.reactor; + +import org.cloudfoundry.client.CloudFoundryClient; +import org.cloudfoundry.doppler.DopplerClient; +import org.cloudfoundry.operations.CloudFoundryOperations; +import org.cloudfoundry.operations.DefaultCloudFoundryOperations; +import org.cloudfoundry.reactor.ConnectionContext; +import org.cloudfoundry.reactor.DefaultConnectionContext; +import org.cloudfoundry.reactor.TokenProvider; +import org.cloudfoundry.reactor.client.ReactorCloudFoundryClient; +import org.cloudfoundry.reactor.doppler.ReactorDopplerClient; +import org.cloudfoundry.reactor.tokenprovider.PasswordGrantTokenProvider; +import org.cloudfoundry.reactor.uaa.ReactorUaaClient; +import org.cloudfoundry.uaa.UaaClient; +import releaser.internal.Releaser; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +class ReactorConfiguration { + + @Bean + @ConditionalOnMissingBean + CfClient cfClient(CloudFoundryOperations cloudFoundryOperations) { + return new CfClient(cloudFoundryOperations); + } + + @Bean + RestartSiteProjectPostReleaseTask restartSiteProjectPostReleaseTask(Releaser releaser, + CfClient cfClient, @Value("${cf.reactorAppName}") String reactorAppName) { + return new RestartSiteProjectPostReleaseTask(releaser, cfClient, reactorAppName); + } + +} + +@Configuration +class CfConfiguration { + + @Bean + DefaultConnectionContext connectionContext(@Value("${cf.apiHost}") String apiHost) { + return DefaultConnectionContext.builder().apiHost(apiHost).build(); + } + + @Bean + PasswordGrantTokenProvider tokenProvider(@Value("${cf.username}") String username, + @Value("${cf.password}") String password) { + return PasswordGrantTokenProvider.builder().password(password).username(username) + .build(); + } + + @Bean + ReactorCloudFoundryClient cloudFoundryClient(ConnectionContext connectionContext, + TokenProvider tokenProvider) { + return ReactorCloudFoundryClient.builder().connectionContext(connectionContext) + .tokenProvider(tokenProvider).build(); + } + + @Bean + ReactorDopplerClient dopplerClient(ConnectionContext connectionContext, + TokenProvider tokenProvider) { + return ReactorDopplerClient.builder().connectionContext(connectionContext) + .tokenProvider(tokenProvider).build(); + } + + @Bean + ReactorUaaClient uaaClient(ConnectionContext connectionContext, + TokenProvider tokenProvider) { + return ReactorUaaClient.builder().connectionContext(connectionContext) + .tokenProvider(tokenProvider).build(); + } + + @Bean + DefaultCloudFoundryOperations defaultCloudFoundryOperations( + CloudFoundryClient cloudFoundryClient, DopplerClient dopplerClient, + UaaClient uaaClient, @Value("${cf.organization}") String organizationId, + @Value("${cf.space}") String spaceId) { + return DefaultCloudFoundryOperations.builder() + .cloudFoundryClient(cloudFoundryClient).dopplerClient(dopplerClient) + .uaaClient(uaaClient).organization(organizationId).space(spaceId).build(); + } + +} diff --git a/projects/reactor/src/main/java/releaser/reactor/RestartSiteProjectPostReleaseTask.java b/projects/reactor/src/main/java/releaser/reactor/RestartSiteProjectPostReleaseTask.java new file mode 100644 index 00000000..cc75607c --- /dev/null +++ b/projects/reactor/src/main/java/releaser/reactor/RestartSiteProjectPostReleaseTask.java @@ -0,0 +1,67 @@ +/* + * Copyright 2013-2019 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 releaser.reactor; + +import org.cloudfoundry.operations.CloudFoundryOperations; +import org.cloudfoundry.operations.applications.RestartApplicationRequest; +import releaser.internal.Releaser; +import releaser.internal.spring.Arguments; +import releaser.internal.spring.ExecutionResult; +import releaser.internal.tasks.DryRunReleaseReleaserTask; +import releaser.internal.tasks.release.PublishDocsReleaseTask; + +public class RestartSiteProjectPostReleaseTask extends PublishDocsReleaseTask + implements DryRunReleaseReleaserTask { + + private static final String REACTOR_CORE_PROJECT_NAME = "reactor-core"; + + private final CfClient cfClient; + + private final String reactorAppName; + + public RestartSiteProjectPostReleaseTask(Releaser releaser, CfClient cfClient, + String reactorAppName) { + super(releaser); + this.cfClient = cfClient; + this.reactorAppName = reactorAppName; + } + + @Override + public ExecutionResult runTask(Arguments args) { + if (!REACTOR_CORE_PROJECT_NAME.equals(args.projectToRun.name())) { + return ExecutionResult.success(); + } + this.cfClient.restartApp(this.reactorAppName); + return ExecutionResult.success(); + } + +} + +class CfClient { + + private final CloudFoundryOperations cloudFoundryOperations; + + CfClient(CloudFoundryOperations cloudFoundryOperations) { + this.cloudFoundryOperations = cloudFoundryOperations; + } + + void restartApp(String name) { + this.cloudFoundryOperations.applications() + .restart(RestartApplicationRequest.builder().name(name).build()); + } + +} diff --git a/projects/reactor/src/main/resources/application.yml b/projects/reactor/src/main/resources/application.yml index 2eea9d59..56cb4819 100644 --- a/projects/reactor/src/main/resources/application.yml +++ b/projects/reactor/src/main/resources/application.yml @@ -14,4 +14,9 @@ releaser: release-train-project-name: reactor release-train-dependency-names: - reactor - git-org-url: https://github.com/reactor \ No newline at end of file + git-org-url: https://github.com/reactor +cf: + organization: FrameworksAndRuntimes + space: Reactor + reactorAppName: projectreactor + apiHost: api.run.pivotal.io \ No newline at end of file diff --git a/projects/reactor/src/test/java/releaser/ReleaserApplicationTests.java b/projects/reactor/src/test/java/releaser/ReleaserApplicationTests.java index 6d0c2bf8..df77e517 100644 --- a/projects/reactor/src/test/java/releaser/ReleaserApplicationTests.java +++ b/projects/reactor/src/test/java/releaser/ReleaserApplicationTests.java @@ -16,26 +16,49 @@ package releaser; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + import org.junit.jupiter.api.Test; import org.mockito.Mockito; import releaser.internal.options.Parser; import releaser.internal.spring.ExecutionResultHandler; import releaser.internal.spring.SpringReleaser; +import releaser.internal.tasks.DryRunReleaseReleaserTask; +import releaser.internal.tasks.ReleaserTask; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotationAwareOrderComparator; +import org.springframework.test.context.ActiveProfiles; @SpringBootTest( classes = { ReleaserApplicationTests.Config.class, ReleaserApplication.class }, properties = { "releaser.sagan.update-sagan=false" }) +@ActiveProfiles("test") class ReleaserApplicationTests { + @Autowired + ApplicationContext context; + @Test void contextLoads() { } + @Test + void should_load_reactor_version_of_publish_docs() { + Map beans = context + .getBeansOfType(DryRunReleaseReleaserTask.class); + List inOrder = new LinkedList<>(beans.values()); + inOrder.sort(AnnotationAwareOrderComparator.INSTANCE); + System.out.println(inOrder); + } + @Configuration static class Config { diff --git a/projects/reactor/src/test/java/releaser/reactor/RestartSiteProjectPostReleaseTaskTests.java b/projects/reactor/src/test/java/releaser/reactor/RestartSiteProjectPostReleaseTaskTests.java new file mode 100644 index 00000000..0b17573f --- /dev/null +++ b/projects/reactor/src/test/java/releaser/reactor/RestartSiteProjectPostReleaseTaskTests.java @@ -0,0 +1,116 @@ +/* + * Copyright 2013-2019 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 releaser.reactor; + +import org.assertj.core.api.BDDAssertions; +import org.cloudfoundry.operations.CloudFoundryOperations; +import org.junit.jupiter.api.Test; +import org.mockito.BDDMockito; +import releaser.internal.Releaser; +import releaser.internal.ReleaserProperties; +import releaser.internal.options.Options; +import releaser.internal.project.ProjectVersion; +import releaser.internal.project.Projects; +import releaser.internal.spring.Arguments; +import releaser.internal.spring.ExecutionResult; +import releaser.internal.spring.ProjectToRun; +import releaser.internal.spring.ProjectsFromBom; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ActiveProfiles; + +@SpringBootTest(classes = RestartSiteProjectPostReleaseTaskTests.Config.class) +@ActiveProfiles("test") +class RestartSiteProjectPostReleaseTaskTests { + + @Autowired + RestartSiteProjectPostReleaseTask task; + + @Autowired + CfClient cfClient; + + @Test + void should_update_the_website() { + Arguments arguments = Arguments.forProject(reactorCoreProject()); + + ExecutionResult result = task.runTask(arguments); + + BDDAssertions.then(result.isSuccess()).isTrue(); + BDDMockito.then(this.cfClient).should() + .restartApp(BDDMockito.eq("projectreactor")); + } + + @Test + void should_not_update_the_website_if_project_not_reactor_core() { + Arguments arguments = Arguments.forProject(nonReactorCoreProject()); + + ExecutionResult result = task.runTask(arguments); + + BDDAssertions.then(result.isSuccess()).isTrue(); + BDDMockito.then(this.cfClient).shouldHaveNoInteractions(); + } + + private ProjectToRun reactorCoreProject() { + return new ProjectToRun(null, + new ProjectsFromBom(new Projects(), new ProjectVersion("foo", "1.0.0")), + new ProjectVersion("foo", "1.0.0"), new ReleaserProperties(), + BDDMockito.mock(Options.class)) { + @Override + public String name() { + return "reactor-core"; + } + }; + } + + private ProjectToRun nonReactorCoreProject() { + return new ProjectToRun(null, + new ProjectsFromBom(new Projects(), new ProjectVersion("foo", "1.0.0")), + new ProjectVersion("foo", "1.0.0"), new ReleaserProperties(), + BDDMockito.mock(Options.class)) { + @Override + public String name() { + return "whatever"; + } + }; + } + + @Configuration + @EnableAutoConfiguration + static class Config extends ReactorConfiguration { + + @Bean + Releaser mockReleaser() { + return BDDMockito.mock(Releaser.class); + } + + @Bean + CloudFoundryOperations mockCloudFoundryOperations() { + return BDDMockito.mock(CloudFoundryOperations.class); + } + + @Override + CfClient cfClient(CloudFoundryOperations cloudFoundryOperations) { + return BDDMockito.mock(CfClient.class); + } + + } + +} diff --git a/projects/reactor/src/test/resources/application-test.yml b/projects/reactor/src/test/resources/application-test.yml new file mode 100644 index 00000000..79678c85 --- /dev/null +++ b/projects/reactor/src/test/resources/application-test.yml @@ -0,0 +1,3 @@ +cf: + username: foo + password: bar \ No newline at end of file