Skip to content

Commit

Permalink
Simplify create process for into AbstractMultiBranchCreateRequest (#1036
Browse files Browse the repository at this point in the history
)
  • Loading branch information
James William Dumay authored May 9, 2017
1 parent fa633c2 commit b895f12
Show file tree
Hide file tree
Showing 17 changed files with 306 additions and 144 deletions.
20 changes: 19 additions & 1 deletion blueocean-git-pipeline/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,28 @@
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>blueocean-pipeline-api-impl</artifactId>
<artifactId>blueocean-pipeline-api</artifactId>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>git</artifactId>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</dependency>

<!-- test dependencies -->
<dependency>
<groupId>io.jenkins.blueocean</groupId>
<artifactId>blueocean-rest-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.jenkins.blueocean</groupId>
<artifactId>blueocean-pipeline-api-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>blueocean-pipeline-api-impl</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,145 +1,57 @@
package io.jenkins.blueocean.blueocean_git_pipeline;

import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.cloudbees.plugins.credentials.domains.Domain;
import hudson.model.Cause;
import hudson.model.Failure;
import hudson.model.TopLevelItem;
import hudson.model.User;
import com.google.common.collect.Lists;
import io.jenkins.blueocean.commons.ErrorMessage;
import io.jenkins.blueocean.commons.ServiceException;
import io.jenkins.blueocean.rest.Reachable;
import io.jenkins.blueocean.rest.impl.pipeline.MultiBranchPipelineImpl;
import io.jenkins.blueocean.rest.impl.pipeline.credential.BlueOceanCredentialsProvider;
import io.jenkins.blueocean.rest.impl.pipeline.credential.BlueOceanDomainRequirement;
import io.jenkins.blueocean.rest.impl.pipeline.credential.CredentialsUtils;
import io.jenkins.blueocean.rest.model.BluePipeline;
import io.jenkins.blueocean.commons.ErrorMessage.Error;
import io.jenkins.blueocean.pipeline.api.AbstractMultiBranchCreateRequest;
import io.jenkins.blueocean.rest.model.BlueScmConfig;
import io.jenkins.blueocean.service.embedded.rest.AbstractPipelineCreateRequestImpl;
import jenkins.branch.BranchSource;
import jenkins.branch.MultiBranchProjectDescriptor;
import jenkins.branch.MultiBranchProject;
import jenkins.model.Jenkins;
import jenkins.plugins.git.GitSCMSource;
import org.apache.commons.lang3.StringUtils;
import org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject;
import jenkins.scm.api.SCMSource;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.DataBoundConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import javax.annotation.Nonnull;
import java.util.List;

/**
* @author Vivek Pandey
*/
public class GitPipelineCreateRequest extends AbstractPipelineCreateRequestImpl {

private static final String MODE = "org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject";
private static final Logger logger = LoggerFactory.getLogger(GitPipelineCreateRequest.class);

private BlueScmConfig scmConfig;
public class GitPipelineCreateRequest extends AbstractMultiBranchCreateRequest {

@DataBoundConstructor
public GitPipelineCreateRequest(String name, BlueScmConfig scmConfig) {
validate(name, scmConfig);
setName(name);
this.scmConfig = scmConfig;
super(name, scmConfig);
}

@Override
public BluePipeline create(Reachable parent) throws IOException {
User authenticatedUser = User.current();
if(authenticatedUser == null){
throw new ServiceException.UnauthorizedException("Must login to create a pipeline");
}

String sourceUri = scmConfig.getUri();

if (sourceUri == null) {
throw new ServiceException.BadRequestExpception(new ErrorMessage(400, "Failed to create Git pipeline:"+getName())
.add(new ErrorMessage.Error("scmConfig.uri", ErrorMessage.Error.ErrorCodes.MISSING.toString(), "uri is required")));
}

TopLevelItem item = create(Jenkins.getInstance(), getName(), MODE, MultiBranchProjectDescriptor.class);

if (item instanceof WorkflowMultiBranchProject) {
WorkflowMultiBranchProject project = (WorkflowMultiBranchProject) item;

if(StringUtils.isNotBlank(scmConfig.getCredentialId())) {
Domain domain = CredentialsUtils.findDomain(scmConfig.getCredentialId(), authenticatedUser);
if(domain == null){
throw new ServiceException.BadRequestExpception(
new ErrorMessage(400, "Failed to create pipeline")
.add(new ErrorMessage.Error("scm.credentialId",
ErrorMessage.Error.ErrorCodes.INVALID.toString(),
"No domain in user credentials found for credentialId: "+ scmConfig.getCredentialId())));
}
if(domain.test(new BlueOceanDomainRequirement())) { //this is blueocean specific domain
project.addProperty(
new BlueOceanCredentialsProvider.FolderPropertyImpl(authenticatedUser.getId(),
scmConfig.getCredentialId(),
BlueOceanCredentialsProvider.createDomain(sourceUri)));
}
}

String credentialId = StringUtils.defaultString(scmConfig.getCredentialId());

project.getSourcesList().add(new BranchSource(new GitSCMSource(null, sourceUri, credentialId, "*", "", false)));
project.scheduleBuild(new Cause.UserIdCause());
return new MultiBranchPipelineImpl(project);
} else {
try {
item.delete(); // we don't know about this project type
} catch (InterruptedException e) {
throw new ServiceException.UnexpectedErrorException("Failed to delete pipeline: " + getName());
}
}
return null;
protected SCMSource createSource(@Nonnull MultiBranchProject project, @Nonnull BlueScmConfig scmConfig) {
return new GitSCMSource(null, StringUtils.defaultString(scmConfig.getUri()), scmConfig.getCredentialId(), "*", "", false);
}

private void validate(String name, BlueScmConfig scmConfig){
if(scmConfig == null){
throw new ServiceException.BadRequestExpception(new ErrorMessage(400, "Failed to create Git pipeline")
.add(new ErrorMessage.Error("scmConfig", ErrorMessage.Error.ErrorCodes.MISSING.toString(), "scmConfig is required")));
}

List<ErrorMessage.Error> errors = new ArrayList<>();

String sourceUri = scmConfig.getUri();

if (sourceUri == null) {
@Override
protected List<Error> validate(String name, BlueScmConfig scmConfig) {
List<Error> errors = Lists.newArrayList();
if (scmConfig.getUri() == null) {
errors.add(new ErrorMessage.Error("scmConfig.uri", ErrorMessage.Error.ErrorCodes.MISSING.toString(), "uri is required"));
}else {
StandardCredentials credentials = null;
if(scmConfig.getCredentialId() != null){
credentials = GitUtils.getCredentials(Jenkins.getInstance(), sourceUri, scmConfig.getCredentialId());
credentials = GitUtils.getCredentials(Jenkins.getInstance(), scmConfig.getUri(), scmConfig.getCredentialId());
if (credentials == null) {
errors.add(new ErrorMessage.Error("scmConfig.credentialId",
ErrorMessage.Error.ErrorCodes.NOT_FOUND.toString(),
String.format("credentialId: %s not found", scmConfig.getCredentialId())));
ErrorMessage.Error.ErrorCodes.NOT_FOUND.toString(),
String.format("credentialId: %s not found", scmConfig.getCredentialId())));
}
}
//validate credentials if no credential id (perhaps git repo doesn't need auth or credentials is present)
if(scmConfig.getCredentialId() == null || credentials != null) {
errors.addAll(GitUtils.validateCredentials(sourceUri, credentials));
errors.addAll(GitUtils.validateCredentials(scmConfig.getUri(), credentials));
}
}

try {
Jenkins.getInstance().getProjectNamingStrategy().checkName(getName());
}catch (Failure f){
errors.add(new ErrorMessage.Error("scmConfig.name", ErrorMessage.Error.ErrorCodes.INVALID.toString(), name + "in not a valid name"));
}

if(Jenkins.getInstance().getItem(name)!=null) {
errors.add(new ErrorMessage.Error("name",
ErrorMessage.Error.ErrorCodes.ALREADY_EXISTS.toString(), name + " already exists"));
}

if(!errors.isEmpty()){
throw new ServiceException.BadRequestExpception(new ErrorMessage(400, "Failed to create Git pipeline:"+name).addAll(errors));
}
return errors;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ private String needsGithubAccessToken(){
public void shouldCreateWithRemoteGitRepo() throws IOException, UnirestException {
String accessToken = needsGithubAccessToken();
User user = login();
this.jwtToken = getJwtToken(j.jenkins, user.getId(), user.getId());

Map resp = createCredentials(user, ImmutableMap.of("credentials", new ImmutableMap.Builder<String,Object>()
.put("password", accessToken)
Expand Down Expand Up @@ -90,6 +91,7 @@ public void shouldCreateWithRemoteGitRepo() throws IOException, UnirestException
@Test
public void shouldGetForbiddenForBadCredentialIdOnCreate1() throws IOException, UnirestException {
User user = login();
this.jwtToken = getJwtToken(j.jenkins, user.getId(), user.getId());

Map resp = createCredentials(user, ImmutableMap.of("credentials",
new ImmutableMap.Builder<String,Object>()
Expand Down Expand Up @@ -120,6 +122,7 @@ public void shouldGetForbiddenForBadCredentialIdOnCreate1() throws IOException,
@Test
public void shouldGetForbiddenForBadCredentialIdOnCreate2() throws IOException, UnirestException {
User user = login();
this.jwtToken = getJwtToken(j.jenkins, user.getId(), user.getId());

Map resp = createCredentials(user, ImmutableMap.of("credentials", new ImmutableMap.Builder<String,Object>()
.put("password", "abcd")
Expand All @@ -144,7 +147,10 @@ public void shouldGetForbiddenForBadCredentialIdOnCreate2() throws IOException,
}

@Test
public void shouldGetBadRequestForBadGitUriOnCreate() throws IOException {
public void shouldGetBadRequestForBadGitUriOnCreate() throws Exception {

User user = login();
this.jwtToken = getJwtToken(j.jenkins, user.getId(), user.getId());

post("/organizations/jenkins/pipelines/",
ImmutableMap.of("name", "demo",
Expand Down Expand Up @@ -267,14 +273,18 @@ public void shouldFailOnValidation1(){

List<Map> errors = (List<Map>) resp.get("errors");

assertEquals(errors.get(0).get("field"), "name");
assertEquals(errors.get(0).get("code"), "MISSING");
assertEquals(errors.get(1).get("field"), "$class");
assertEquals(errors.get(1).get("code"), "MISSING");
assertEquals("name", errors.get(0).get("field"));
assertEquals("MISSING", errors.get(0).get("code"));
assertEquals("$class", errors.get(1).get("field"));
assertEquals("MISSING", errors.get(1).get("code"));
}

@Test
public void shouldFailOnValidation2(){
public void shouldFailOnValidation2() throws Exception {

User user = login();
this.jwtToken = getJwtToken(j.jenkins, user.getId(), user.getId());

Map<String,Object> resp = post("/organizations/jenkins/pipelines/",
ImmutableMap.of("name", "demo",
"$class", "io.jenkins.blueocean.blueocean_git_pipeline.GitPipelineCreateRequest"
Expand All @@ -284,8 +294,8 @@ public void shouldFailOnValidation2(){

List<Map> errors = (List<Map>) resp.get("errors");

assertEquals(errors.get(0).get("field"), "scmConfig");
assertEquals(errors.get(0).get("code"), "MISSING");
assertEquals("scmConfig", errors.get(0).get("field"));
assertEquals("MISSING", errors.get(0).get("code"));
assertNull(Jenkins.getInstance().getItem("demo"));
}

Expand Down Expand Up @@ -344,18 +354,21 @@ public void shouldFailOnValidation4() throws IOException, UnirestException {
for(Map<String,String> error:errors){
if(error.get("field").equals("name")){
nameFound = true;
assertEquals(error.get("code"), "ALREADY_EXISTS");
assertEquals("ALREADY_EXISTS", error.get("code"));
}else if(error.get("field").equals("scmConfig.credentialId")){
credentialIdFound = true;
assertEquals(error.get("code"), "NOT_FOUND");
assertEquals("NOT_FOUND", error.get("code"));
}
}
assertTrue(nameFound);
assertTrue(credentialIdFound);
}

@Test
public void shouldFailOnValidation5(){
public void shouldFailOnValidation5() throws Exception {

User user = login();
this.jwtToken = getJwtToken(j.jenkins, user.getId(), user.getId());

Map<String,Object> resp = post("/organizations/jenkins/pipelines/",
ImmutableMap.of("name", "demo",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
import hudson.model.User;
import io.jenkins.blueocean.commons.ErrorMessage;
import io.jenkins.blueocean.commons.ServiceException;
import io.jenkins.blueocean.pipeline.api.AbstractPipelineCreateRequest;
import io.jenkins.blueocean.rest.Reachable;
import io.jenkins.blueocean.rest.impl.pipeline.credential.BlueOceanCredentialsProvider;
import io.jenkins.blueocean.rest.impl.pipeline.credential.BlueOceanDomainRequirement;
import io.jenkins.blueocean.rest.impl.pipeline.credential.CredentialsUtils;
import io.jenkins.blueocean.rest.model.BluePipeline;
import io.jenkins.blueocean.rest.model.BlueScmConfig;
import io.jenkins.blueocean.service.embedded.rest.AbstractPipelineCreateRequestImpl;
import jenkins.branch.CustomOrganizationFolderDescriptor;
import jenkins.branch.MultiBranchProject;
import jenkins.branch.OrganizationFolder;
Expand Down Expand Up @@ -54,17 +54,14 @@
/**
* @author Vivek Pandey
*/
public class GithubPipelineCreateRequest extends AbstractPipelineCreateRequestImpl {
public class GithubPipelineCreateRequest extends AbstractPipelineCreateRequest {

private static final String DESCRIPTOR = "jenkins.branch.OrganizationFolder.org.jenkinsci.plugins.github_branch_source.GitHubSCMNavigator";
private static final Logger logger = LoggerFactory.getLogger(GithubPipelineCreateRequest.class);

private BlueScmConfig scmConfig;

@DataBoundConstructor
public GithubPipelineCreateRequest(String name, BlueScmConfig scmConfig) {
setName(name);
this.scmConfig = scmConfig;
super(name, scmConfig);
}

@SuppressWarnings("unchecked")
Expand Down Expand Up @@ -95,9 +92,6 @@ public BluePipeline create(Reachable parent) throws IOException {
String singleRepo = repos.size() == 1 ? repos.get(0) : null;

User authenticatedUser = User.current();
if(authenticatedUser == null){
throw new ServiceException.UnauthorizedException("Must login to create a pipeline");
}

Item item = Jenkins.getInstance().getItemByFullName(orgName);
boolean creatingNewItem = item == null;
Expand All @@ -108,7 +102,7 @@ public BluePipeline create(Reachable parent) throws IOException {
}

if (item == null) {
item = create(Jenkins.getInstance(), getName(), DESCRIPTOR, CustomOrganizationFolderDescriptor.class);
item = createProject(getName(), DESCRIPTOR, CustomOrganizationFolderDescriptor.class);
}

if (item instanceof OrganizationFolder) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
import java.util.List;
import java.util.Map;

import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

/**
* @author Vivek Pandey
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
import java.util.Collections;
import java.util.Map;

import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;


/**
Expand Down
6 changes: 5 additions & 1 deletion blueocean-pipeline-api-impl/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@
<artifactId>blueocean-pipeline-api-impl</artifactId>
<packaging>hpi</packaging>

<name>Pipeline REST API for Blue Ocean</name>
<name>Pipeline implementation for Blue Ocean</name>
<url>https://wiki.jenkins-ci.org/display/JENKINS/Blue+Ocean+Plugin</url>

<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>blueocean-pipeline-api</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>blueocean-rest-impl</artifactId>
Expand Down
Loading

0 comments on commit b895f12

Please sign in to comment.