diff --git a/build.gradle b/build.gradle index d99158dd..01623931 100644 --- a/build.gradle +++ b/build.gradle @@ -127,6 +127,11 @@ dependencies { implementation 'org.springframework.security:spring-security-jwt:1.1.0.RELEASE' implementation 'org.springframework.security.oauth:spring-security-oauth2:2.4.0.RELEASE' + implementation(group: 'com.openshift', name: 'openshift-restclient-java', version: '9.0.0.Final') { + exclude(group: 'org.slf4j', module: 'slf4j-api:') + exclude(group: 'org.slf4j', module: 'slf4j-log4j12') + } + // azure ad implementation('com.microsoft.azure:azure-active-directory-spring-boot-starter:2.2.2') diff --git a/docs/modules/provisioning-app/pages/configuration.adoc b/docs/modules/provisioning-app/pages/configuration.adoc index 1fb5cc44..e55b6d99 100644 --- a/docs/modules/provisioning-app/pages/configuration.adoc +++ b/docs/modules/provisioning-app/pages/configuration.adoc @@ -315,6 +315,21 @@ jira.admin_user=jira_admin Note: if the pair of properties is not defined for a third party tool, the logged in user's credentials are used to authenticate against the application. The credentials are read by caling the method _getUserName_ and _getUserPassword_ from https://github.com/opendevstack/ods-provisioning-app/blob/master/src/main/java/org/opendevstack/provision/adapter/IODSAuthnzAdapter[IODSAuthnzAdapter]]. See also implementation of _org.opendevstack.provision.services.BaseServiceAdapter#authenticatedCall()_ +=== Other configuration + +To adapt the provisioning app to your infrastructure following properties will help you to disable some adapters/services. + +To disable the confluence adapter you can add this property to the application properties: +``` +adapters.confluence.enabled=false +``` + +The Openshift Service currently is used to verify that a project key does not exists in the cluster before provisioning a project. +If you need to disable it, you can add this property to the application properties: +``` +services.openshift.enabled=false +``` + == FAQ . Where is the provision app deployed? + diff --git a/src/main/java/org/opendevstack/provision/config/OpenshiftServiceConfig.java b/src/main/java/org/opendevstack/provision/config/OpenshiftServiceConfig.java new file mode 100644 index 00000000..f5dde07d --- /dev/null +++ b/src/main/java/org/opendevstack/provision/config/OpenshiftServiceConfig.java @@ -0,0 +1,130 @@ +/* + * Copyright 2017-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 + * + * 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.opendevstack.provision.config; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.opendevstack.provision.services.openshift.OpenshiftClient; +import org.opendevstack.provision.services.openshift.OpenshiftService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.util.Assert; + +/** @author Sebastian Titakis */ +@Configuration +@ConditionalOnProperty( + name = "services.openshift.enabled", + havingValue = "true", + matchIfMissing = true) +public class OpenshiftServiceConfig { + + private static final Logger logger = LoggerFactory.getLogger(OpenshiftServiceConfig.class); + + public static final String DEFAULT_KUBERNETES_IO_SERVICEACCOUNT_TOKEN_FILE = + "file:///var/run/secrets/kubernetes.io/serviceaccount/token"; + + @Value( + "${openshift.provisioning-app.service-account.file:" + + DEFAULT_KUBERNETES_IO_SERVICEACCOUNT_TOKEN_FILE + + "}") + private String ocTokenResourceFile; + + @Value("${openshift.provisioning-app.service-account.token:}") + private String ocToken; + + @Value("${openshift.api.uri}") + private String openshiftApiUri; + + @Autowired private ResourceLoader resourceLoader; + + @Bean + public OpenshiftService openshiftService(OpenshiftClient openshiftClient) { + return new OpenshiftService(openshiftClient); + } + + @Bean + public OpenshiftClient openshiftClient() { + + if (null != ocToken && !ocToken.isEmpty()) { + logger.info( + "Found oc token configured in property 'openshift.provisioning-app.service-account.token'. Using it to log into to openshift! [openshift.api.uri={}]", + openshiftApiUri); + + return create(openshiftApiUri, ocToken); + + } else if (null != ocTokenResourceFile) { + + if (!ocTokenResourceFile.equals(DEFAULT_KUBERNETES_IO_SERVICEACCOUNT_TOKEN_FILE)) { + logger.info( + "Found oc service account file configured in property 'openshift.provisioning-app.service-account.file'. Using it to log into to openshift! [openshift.api.uri={}, serviceAccountTokenFile={}]", + openshiftApiUri, + ocTokenResourceFile); + } else { + logger.info( + "Using default service account file to connect to openshift! [openshift.api.uri={}, serviceAccountTokenFile={}]", + openshiftApiUri, + DEFAULT_KUBERNETES_IO_SERVICEACCOUNT_TOKEN_FILE); + } + + Resource resource = resourceLoader.getResource(ocTokenResourceFile); + + if (!resource.exists()) { + throw new RuntimeException( + "Cannot load oc token from file because file does not exists! [file=" + + ocTokenResourceFile + + "]"); + } + + return create(openshiftApiUri, resource); + + } else { + throw new RuntimeException("This should never happens! Ask developers to take a look!"); + } + } + + private OpenshiftClient create(String openshiftApiUri, Resource token) { + Assert.notNull(token, "Parameter 'token' is null!"); + + try { + if (!token.exists()) { + throw new RuntimeException( + String.format( + "File with oc token does not exists! [file=%s]!", token.getURI().toString())); + } + + String ocToken = new String(Files.readAllBytes(Path.of(token.getURI()))); + + return create(openshiftApiUri, ocToken); + + } catch (IOException ex) { + logger.error("Failed to create openshift service!", ex); + throw new RuntimeException(ex); + } + } + + private OpenshiftClient create(String openshiftApiUri, String token) { + Assert.notNull(openshiftApiUri, "Parameter 'openshiftApiUri' is null!"); + Assert.notNull(token, "Parameter 'token' is null!"); + + return new OpenshiftClient(openshiftApiUri, token); + } +} diff --git a/src/main/java/org/opendevstack/provision/controller/ProjectApiController.java b/src/main/java/org/opendevstack/provision/controller/ProjectApiController.java index b1d250c9..cde9680b 100644 --- a/src/main/java/org/opendevstack/provision/controller/ProjectApiController.java +++ b/src/main/java/org/opendevstack/provision/controller/ProjectApiController.java @@ -42,6 +42,7 @@ import org.opendevstack.provision.model.jenkins.Job; import org.opendevstack.provision.services.MailAdapter; import org.opendevstack.provision.services.StorageAdapter; +import org.opendevstack.provision.services.openshift.OpenshiftService; import org.opendevstack.provision.storage.IStorage; import org.opendevstack.provision.util.exception.ProjectAlreadyExistsException; import org.slf4j.Logger; @@ -87,6 +88,9 @@ public class ProjectApiController { @Autowired(required = false) private ICollaborationAdapter confluenceAdapter; + @Autowired(required = false) + private OpenshiftService openshiftService; + @Autowired private ISCMAdapter bitbucketAdapter; @Autowired private IJobExecutionAdapter jenkinsPipelineAdapter; @Autowired private MailAdapter mailAdapter; @@ -117,6 +121,9 @@ public class ProjectApiController { @Value("${adapters.confluence.enabled:true}") private boolean confluenceAdapterEnable; + @Value("${services.openshift.enabled:true}") + private boolean openshiftServiceEnable; + @PostConstruct public void postConstruct() { logger.info( @@ -338,6 +345,10 @@ private List checkPreconditions(OpenProjectData newPro if (newProject.platformRuntime) { results.addAll(bitbucketAdapter.checkCreateProjectPreconditions(newProject)); + + if (isOpenshiftServiceEnable() && null != openshiftService) { + results.addAll(openshiftService.checkCreateProjectPreconditions(newProject)); + } } if (results.isEmpty()) { @@ -958,6 +969,14 @@ public IStorage getDirectStorage() { return directStorage; } + public boolean isOpenshiftServiceEnable() { + return openshiftServiceEnable; + } + + public void setOpenshiftServiceEnable(boolean openshiftServiceEnable) { + this.openshiftServiceEnable = openshiftServiceEnable; + } + public boolean isConfluenceAdapterEnable() { return confluenceAdapterEnable; } diff --git a/src/main/java/org/opendevstack/provision/services/openshift/OpenshiftClient.java b/src/main/java/org/opendevstack/provision/services/openshift/OpenshiftClient.java new file mode 100644 index 00000000..b9030018 --- /dev/null +++ b/src/main/java/org/opendevstack/provision/services/openshift/OpenshiftClient.java @@ -0,0 +1,49 @@ +/* + * Copyright 2017-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 + * + * 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.opendevstack.provision.services.openshift; + +import com.openshift.restclient.ClientBuilder; +import com.openshift.restclient.IClient; +import com.openshift.restclient.model.IResource; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** @author Sebastian Titakis */ +public class OpenshiftClient { + + private final IClient iClient; + + private final String url; + + public OpenshiftClient(String url, String token) { + this(url, new ClientBuilder(url).usingToken(token).build()); + } + + public OpenshiftClient(String url, IClient ocClient) { + this.url = url; + this.iClient = ocClient; + } + + public Set projects() { + + List projectResource = iClient.list("Project"); + + return projectResource.stream().map(project -> project.getName()).collect(Collectors.toSet()); + } + + public String getUrl() { + return url; + } +} diff --git a/src/main/java/org/opendevstack/provision/services/openshift/OpenshiftService.java b/src/main/java/org/opendevstack/provision/services/openshift/OpenshiftService.java new file mode 100644 index 00000000..cba9ab14 --- /dev/null +++ b/src/main/java/org/opendevstack/provision/services/openshift/OpenshiftService.java @@ -0,0 +1,99 @@ +/* + * Copyright 2017-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 + * + * 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.opendevstack.provision.services.openshift; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import org.opendevstack.provision.adapter.exception.AdapterException; +import org.opendevstack.provision.adapter.exception.CreateProjectPreconditionException; +import org.opendevstack.provision.controller.CheckPreconditionFailure; +import org.opendevstack.provision.model.OpenProjectData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.Assert; + +/** @author Sebastian Titakis */ +public class OpenshiftService { + + private static final Logger logger = LoggerFactory.getLogger(OpenshiftService.class); + + public static final String SERVICE_NAME = "openshiftService"; + + private OpenshiftClient openshiftClient; + + public OpenshiftService(OpenshiftClient openshiftClient) { + this.openshiftClient = openshiftClient; + } + + public List checkCreateProjectPreconditions(OpenProjectData newProject) + throws CreateProjectPreconditionException { + + try { + Assert.notNull(newProject, "Parameter 'newProject' is null!"); + Assert.notNull( + newProject.projectKey, "Properties 'projectKey' of parameter 'newProject' is null!"); + + logger.info("checking create project preconditions for project '{}'!", newProject.projectKey); + + List preconditionFailures = + createProjectKeyExistsCheck(newProject.projectKey); + + logger.info( + "done with check create project preconditions for project '{}'!", newProject.projectKey); + + return preconditionFailures; + + } catch (AdapterException e) { + throw new CreateProjectPreconditionException(SERVICE_NAME, newProject.projectKey, e); + } catch (Exception e) { + String message = + String.format( + "Unexpected error when checking precondition for creation of project '%s'", + newProject.projectKey); + logger.error(message, e); + throw new CreateProjectPreconditionException(SERVICE_NAME, newProject.projectKey, message); + } + } + + public List createProjectKeyExistsCheck(String projectKey) { + + try { + logger.info("Checking if ODS project '{}-*' exists in openshift!", projectKey); + + List preconditionFailures = new ArrayList<>(); + + Set projects = openshiftClient.projects(); + List existingProjects = + projects.stream() + .filter(s -> s.toLowerCase().startsWith(projectKey.toLowerCase() + "-")) + .collect(Collectors.toList()); + + if (existingProjects.size() > 0) { + String message = + String.format( + "Project name (namespace) with prefix '%s' already exists in '%s'! [existingProjects=%s]", + projectKey, SERVICE_NAME, Arrays.asList(existingProjects.toArray())); + preconditionFailures.add(CheckPreconditionFailure.getProjectExistsInstance(message)); + } + + return preconditionFailures; + + } catch (Exception ex) { + throw new AdapterException(ex); + } + } +} diff --git a/src/main/resources/application-odsbox.properties b/src/main/resources/application-odsbox.properties index c51a2074..5de0f40e 100644 --- a/src/main/resources/application-odsbox.properties +++ b/src/main/resources/application-odsbox.properties @@ -16,6 +16,8 @@ bitbucket.opendevstack.project=opendevstack # openshift properties openshift.apps.basedomain=.ocp.odsbox.lan +openshift.provisioning-app.service-account-file-path=file:///var/run/secrets/kubernetes.io/serviceaccount/token +openshift.api.uri=https://api.odsbox.lan:8443 openshift.console.uri=https://ocp.odsbox.lan:8443/console/project/ # webhook proxy used to run create project jenkinsfile diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 635b01e6..7f3e8be1 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -83,6 +83,8 @@ ods.git-ref=master openshift.project.upgrade=false openshift.apps.basedomain=.192.168.56.101.nip.io openshift.console.uri=https://192.168.56.101:8443/console +openshift.api.uri=https://192.168.56.101:8443 + #openshift project name patterns openshift.test.project.name.pattern=%s/project/%s-test openshift.dev.project.name.pattern=%s/project/%s-dev diff --git a/src/test/java/org/opendevstack/provision/config/OpenshiftServiceConfigTest.java b/src/test/java/org/opendevstack/provision/config/OpenshiftServiceConfigTest.java new file mode 100644 index 00000000..229c4145 --- /dev/null +++ b/src/test/java/org/opendevstack/provision/config/OpenshiftServiceConfigTest.java @@ -0,0 +1,45 @@ +/* + * Copyright 2017-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 + * + * 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.opendevstack.provision.config; + +import static org.junit.Assert.*; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.opendevstack.provision.SpringBoot; +import org.opendevstack.provision.services.openshift.OpenshiftClient; +import org.opendevstack.provision.services.openshift.OpenshiftService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +/** @author Sebastian Titakis */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, classes = SpringBoot.class) +@DirtiesContext +@ActiveProfiles("utest") +public class OpenshiftServiceConfigTest { + + @Autowired private OpenshiftClient openshiftClient; + + @Autowired private OpenshiftService openshiftService; + + @Test + public void test() { + assertNotNull(openshiftClient); + assertNotNull(openshiftService); + } +} diff --git a/src/test/java/org/opendevstack/provision/controller/E2EProjectAPIControllerTest.java b/src/test/java/org/opendevstack/provision/controller/E2EProjectAPIControllerTest.java index f1240739..4c87e1c2 100644 --- a/src/test/java/org/opendevstack/provision/controller/E2EProjectAPIControllerTest.java +++ b/src/test/java/org/opendevstack/provision/controller/E2EProjectAPIControllerTest.java @@ -35,6 +35,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.assertj.core.api.Assertions; import org.junit.After; import org.junit.Assert; @@ -63,6 +64,7 @@ import org.opendevstack.provision.model.webhookproxy.CreateProjectResponse; import org.opendevstack.provision.services.*; import org.opendevstack.provision.services.jira.JiraRestApi; +import org.opendevstack.provision.services.openshift.OpenshiftClient; import org.opendevstack.provision.storage.LocalStorage; import org.opendevstack.provision.util.CreateProjectResponseUtil; import org.opendevstack.provision.util.RestClientCallArgumentMatcher; @@ -121,6 +123,8 @@ public class E2EProjectAPIControllerTest { @MockBean private RestClient restClient; + @MockBean private OpenshiftClient openshiftClient; + @MockBean private CrowdProjectIdentityMgmtAdapter crowdProjectIdentityMgmtAdapter; @Autowired private WebApplicationContext context; @@ -145,6 +149,9 @@ public class E2EProjectAPIControllerTest { @Value("${idmanager.group.opendevstack-administrators}") private String adminGroup; + @Value("${openshift.apps.basedomain}") + protected String projectOpenshiftBaseDomain; + private static TestDataFileReader fileReader = new TestDataFileReader(TestDataFileReader.TEST_DATA_FILE_DIR); @@ -179,6 +186,8 @@ public void setUp() { when(mockAuthnzAdapter.getUserName()).thenReturn(TEST_ADMIN_USERNAME); when(mockAuthnzAdapter.getUserEmail()).thenReturn(TEST_ADMIN_EMAIL); when(mockAuthnzAdapter.getUserPassword()).thenReturn(TEST_VALID_CREDENTIAL); + + when(openshiftClient.projects()).thenReturn(Set.of("default", "ods")); } public static void initLocalStorage(LocalStorage realStorageAdapter, File resultsDir) { @@ -672,8 +681,9 @@ private void assertActualJobMatchesInputParams( Assertions.assertThat(actualJob.getUrl()) .contains( format( - "https://jenkins-%s.192.168.56.101.nip.io/job/%s/job/%s-%s/%s", + "https://jenkins-%s%s/job/%s/job/%s-%s/%s", namespace, + projectOpenshiftBaseDomain, namespace, namespace, configuredResponse.extractBuildConfigName(), @@ -896,7 +906,9 @@ public OpenProjectData testQuickstarterProvisionOnNewOpenProject(boolean fail) t matchesClientCall() .url( containsString( - "https://webhook-proxy-testp-cd.192.168.56.101.nip.io/build?trigger_secret=")) + "https://webhook-proxy-testp-cd" + + projectOpenshiftBaseDomain + + "/build?trigger_secret=")) .url( containsString( "&jenkinsfile_path=be-python-flask/Jenkinsfile&component=ods-qs-be-python")) @@ -1066,7 +1078,8 @@ private T readTestDataTypeRef(String name, TypeReference returnType) thro private String createJenkinsJobPath(String namespace, String jenkinsfilePath, String component) { return "https://webhook-proxy-" + namespace - + ".192.168.56.101.nip.io/build?trigger_secret=secret101&jenkinsfile_path=" + + projectOpenshiftBaseDomain + + "/build?trigger_secret=secret101&jenkinsfile_path=" + jenkinsfilePath + "&component=" + component; @@ -1076,7 +1089,8 @@ private String createJenkinsJobPathForDeleteComponentAdminJob( String namespace, String jenkinsfilePath, String component, String secret) { return "https://webhook-proxy-" + namespace - + ".192.168.56.101.nip.io/build?trigger_secret=" + + projectOpenshiftBaseDomain + + "/build?trigger_secret=" + secret + "&jenkinsfile_path=" + jenkinsfilePath diff --git a/src/test/java/org/opendevstack/provision/controller/ProjectApiControllerTest.java b/src/test/java/org/opendevstack/provision/controller/ProjectApiControllerTest.java index 673b3862..4a04e29a 100644 --- a/src/test/java/org/opendevstack/provision/controller/ProjectApiControllerTest.java +++ b/src/test/java/org/opendevstack/provision/controller/ProjectApiControllerTest.java @@ -47,6 +47,7 @@ import org.opendevstack.provision.services.CrowdProjectIdentityMgmtAdapter; import org.opendevstack.provision.services.MailAdapter; import org.opendevstack.provision.services.StorageAdapter; +import org.opendevstack.provision.services.openshift.OpenshiftClient; import org.opendevstack.provision.storage.IStorage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -82,6 +83,8 @@ public class ProjectApiControllerTest { private static Logger logger = LoggerFactory.getLogger(ProjectApiControllerTest.class); + @MockBean private OpenshiftClient openshiftClient; + @MockBean private IBugtrackerAdapter jiraAdapter; @MockBean private ICollaborationAdapter confluenceAdapter; @@ -137,6 +140,8 @@ public String getAuthority() { when(jiraAdapter.isSpecialPermissionSchemeEnabled()).thenReturn(true); + when(openshiftClient.projects()).thenReturn(Set.of("default", "ods")); + apiController.setCheckPreconditionsEnabled(true); // Reset status of api controller for each test diff --git a/src/test/java/org/opendevstack/provision/services/openshift/OpenshiftClientTest.java b/src/test/java/org/opendevstack/provision/services/openshift/OpenshiftClientTest.java new file mode 100644 index 00000000..83b2a0de --- /dev/null +++ b/src/test/java/org/opendevstack/provision/services/openshift/OpenshiftClientTest.java @@ -0,0 +1,64 @@ +/* + * Copyright 2017-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 + * + * 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.opendevstack.provision.services.openshift; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.openshift.restclient.IClient; +import com.openshift.restclient.model.IResource; +import java.util.List; +import java.util.Set; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.junit4.SpringRunner; + +/** @author Sebastian Titakis */ +@RunWith(SpringRunner.class) +public class OpenshiftClientTest { + + @MockBean private IClient ocClient; + + private String url = "http://url.com"; + + private OpenshiftClient openshiftClient; + + @Before + public void setup() { + openshiftClient = new OpenshiftClient(url, ocClient); + } + + @Test + public void testOpenshiftClientReturnsProjectKeys() { + + String projectname = "ods"; + IResource resource = mock(IResource.class); + when(resource.getName()).thenReturn(projectname); + List.of(resource); + + when(ocClient.list("Project")).thenReturn(List.of(resource)); + + Set projects = openshiftClient.projects(); + assertEquals(1, projects.size()); + assertTrue(projects.contains(projectname)); + } + + @Test + public void testGetUrl() { + assertEquals(url, openshiftClient.getUrl()); + } +} diff --git a/src/test/java/org/opendevstack/provision/services/openshift/OpenshiftServiceTest.java b/src/test/java/org/opendevstack/provision/services/openshift/OpenshiftServiceTest.java new file mode 100644 index 00000000..75a15c84 --- /dev/null +++ b/src/test/java/org/opendevstack/provision/services/openshift/OpenshiftServiceTest.java @@ -0,0 +1,161 @@ +/* + * Copyright 2017-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 + * + * 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.opendevstack.provision.services.openshift; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Set; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.opendevstack.provision.SpringBoot; +import org.opendevstack.provision.adapter.exception.AdapterException; +import org.opendevstack.provision.adapter.exception.CreateProjectPreconditionException; +import org.opendevstack.provision.controller.CheckPreconditionFailure; +import org.opendevstack.provision.model.OpenProjectData; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +/** @author Sebastian Titakis */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, classes = SpringBoot.class) +@DirtiesContext +@ActiveProfiles("utest") +public class OpenshiftServiceTest { + + @MockBean private OpenshiftClient openshiftClient; + + @Autowired private OpenshiftService openshiftService; + + @Before + public void setup() { + openshiftService = new OpenshiftService(openshiftClient); + } + + @Test + public void givenCheckProjectKey_whenProjectKeyPrefixExists_thenFailure() + throws CreateProjectPreconditionException { + + Set openshiftProjects = Set.of("testp", "TESTP-cd", "TEStp-dev", "testp-test"); + when(openshiftClient.projects()).thenReturn(openshiftProjects); + String project = "TESTP"; + + // case project key with pattern "-*" does exist + List failures = openshiftService.createProjectKeyExistsCheck(project); + assertNotNull(CheckPreconditionFailure.ExceptionCodes.valueOf(failures.get(0).getCode())); + assertTrue(failures.get(0).getDetail().contains(project)); + + // case project key with pattern "-*" does not exist + failures = openshiftService.createProjectKeyExistsCheck("UNEXISTANT_PROJECT"); + assertTrue(failures.size() == 0); + } + + @Test + public void givenCheckProjectKey_whenProjectKeyPrefixDoesNotExists_thenNoFailure() + throws CreateProjectPreconditionException { + + when(openshiftClient.projects()) + .thenReturn(Set.of("project-cd", "project-dev", "project-test", "ods", "default")); + + // case project key with pattern "-*" does not exist + List failures = + openshiftService.createProjectKeyExistsCheck("UNEXISTANT_PROJECT"); + assertTrue(failures.size() == 0); + } + + @Test + public void givenCheckProjectKey_whenProjectKeyExistsButNotAsPrefix_thenNoFailure() + throws CreateProjectPreconditionException { + + String notOdsProject = "NOT_ODS_PROJECT"; + + // ods project are composed of 3 namespaces/project in openshift -> they start with a prefix in + // common with this pattern "-*" + // special case: project key exists but not as prefix + when(openshiftClient.projects()) + .thenReturn( + Set.of(notOdsProject + "odsproj-cd", "odsproj-dev", "odsproj-test", "ods", "default")); + + // case project key with pattern "-*" does not exist + List failures = + openshiftService.createProjectKeyExistsCheck("NOT_ODS_PROJECT"); + assertTrue(failures.size() == 0); + } + + @Test + public void givenCheckProjectKey_whenException_thenAdapterExceptionIsRaised() + throws CreateProjectPreconditionException { + + String notOdsProject = "NOT_ODS_PROJECT"; + + // ods project are composed of 3 namespaces/project in openshift -> they start with a prefix in + // common with this pattern "-*" + // special case: project key exists but not as prefix + when(openshiftClient.projects()).thenThrow(new RuntimeException("exception raised in test")); + + // case project key with pattern "-*" does not exist + try { + List failures = + openshiftService.createProjectKeyExistsCheck("NOT_ODS_PROJECT"); + assertTrue(failures.size() == 0); + fail(); + } catch (AdapterException ex) { + // expected exception + } + } + + @Test + public void givenCheckCreateProjectPreconditions_whenUnexceptedError_thenExceptionIsRaised() + throws CreateProjectPreconditionException { + + OpenProjectData projectData = new OpenProjectData(); + + try { + // case unexpected exception + openshiftService.checkCreateProjectPreconditions(projectData); + } catch (CreateProjectPreconditionException e) { + assertTrue(e.getMessage().contains("Unexpected error")); + } + + try { + // case adapter exception + projectData.setProjectKey("TESTP"); + when(openshiftClient.projects()).thenThrow(new RuntimeException("exception raised in test")); + openshiftService.checkCreateProjectPreconditions(projectData); + } catch (CreateProjectPreconditionException e) { + assertTrue(e.getCause() instanceof AdapterException); + assertTrue(e.getMessage().contains(projectData.getProjectKey())); + } + } + + @Test + public void givenCheckCreateProjectPreconditions_whenOpenshiftClientReturnsListOfProjects_thenOK() + throws CreateProjectPreconditionException { + + OpenProjectData projectData = new OpenProjectData(); + projectData.setProjectKey("TESTP"); + + // case no exception + when(openshiftClient.projects()).thenReturn(Set.of("default", "ods")); + List checkPreconditionFailures = + openshiftService.checkCreateProjectPreconditions(projectData); + assertEquals(0, checkPreconditionFailures.size()); + } +} diff --git a/src/test/resources/application-crowd.properties b/src/test/resources/application-crowd.properties index d8bc4b8e..cbdeec8d 100644 --- a/src/test/resources/application-crowd.properties +++ b/src/test/resources/application-crowd.properties @@ -20,3 +20,9 @@ crowd.sso.cookie.name=crowd.token_key provision.auth.provider=crowd provision.auth.basic-auth.enabled=true + +# openshift +openshift.apps.basedomain=.localhost +openshift.provisioning-app.service-account.file=classpath:oc-token +openshift.api.uri=https://localhost:8443 +openshift.console.uri=https://localhost:8443/console/project/ diff --git a/src/test/resources/application-utest.properties b/src/test/resources/application-utest.properties index 8c99d4e5..8b57d694 100644 --- a/src/test/resources/application-utest.properties +++ b/src/test/resources/application-utest.properties @@ -57,3 +57,9 @@ jira.project-templates.utest-project-template.permission-scheme-id=99999 jira.project-templates.utest-project-template.role-mapping.project-role-for-admin-group=55555 jira.project-templates.utest-project-template.role-mapping.project-role-for-user-group=55555 jira.project-templates.utest-project-template.role-mapping.project-role-for-readonly-group=55555 + +# openshift +openshift.apps.basedomain=.localhost +openshift.provisioning-app.service-account.file=classpath:oc-token +openshift.api.uri=https://localhost:8443 +openshift.console.uri=https://localhost:8443/console/project/ diff --git a/src/test/resources/oc-token b/src/test/resources/oc-token new file mode 100644 index 00000000..3d80f1cd --- /dev/null +++ b/src/test/resources/oc-token @@ -0,0 +1 @@ +not-a-real-token-only-for-unit-test