/** * Copyright 2005-2015 Red Hat, Inc. * * Red Hat licenses this file to you 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 io.fabric8.forge.rest.git; import io.fabric8.forge.rest.Constants; import io.fabric8.forge.rest.main.GitUserHelper; import io.fabric8.forge.rest.main.ProjectFileSystem; import io.fabric8.forge.rest.main.RepositoryCache; import io.fabric8.forge.rest.utils.StopWatch; import io.fabric8.project.support.UserDetails; import io.fabric8.kubernetes.api.Controller; import io.fabric8.kubernetes.api.model.LocalObjectReference; import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.openshift.api.model.BuildConfig; import io.fabric8.openshift.api.model.BuildConfigSpec; import io.fabric8.openshift.api.model.BuildSource; import io.fabric8.openshift.api.model.GitBuildSource; import io.fabric8.openshift.client.OpenShiftClient; import io.fabric8.repo.git.GitRepoClient; import io.fabric8.repo.git.RepositoryDTO; import io.fabric8.utils.Base64Encoder; import io.fabric8.utils.Files; import io.fabric8.utils.Strings; import org.eclipse.jgit.api.errors.GitAPIException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Singleton; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.NotFoundException; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import java.io.File; import java.io.IOException; import java.util.List; import java.util.Map; @Singleton @Path("/api/forge/repos") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class RepositoriesResource { private static final transient Logger LOG = LoggerFactory.getLogger(RepositoriesResource.class); public static final String SSH_PRIVATE_KEY_DATA_KEY = "ssh-privatekey"; public static final String SSH_PUBLIC_KEY_DATA_KEY = "ssh-publickey"; public static final String SSH_PRIVATE_KEY_DATA_KEY2 = "ssh-key"; public static final String SSH_PUBLIC_KEY_DATA_KEY2 = "ssh-key.pub"; public static final String USERNAME_DATA_KEY = "username"; public static final String PASSWORD_DATA_KEY = "password"; private final GitUserHelper gitUserHelper; private final RepositoryCache repositoryCache; private final ProjectFileSystem projectFileSystem; private final GitLockManager lockManager; private final KubernetesClient kubernetes; @Context private HttpServletRequest request; @Inject public RepositoriesResource(GitUserHelper gitUserHelper, RepositoryCache repositoryCache, ProjectFileSystem projectFileSystem, GitLockManager lockManager, KubernetesClient kubernetes) { this.gitUserHelper = gitUserHelper; this.repositoryCache = repositoryCache; this.projectFileSystem = projectFileSystem; this.lockManager = lockManager; this.kubernetes = kubernetes; } @GET @Path("_ping") public String ping() { return "true"; } @GET public List<RepositoryDTO> getUserRepositories() { GitRepoClient repoClient = createGitRepoClient(); List<RepositoryDTO> repositoryDTOs = repoClient.listRepositories(); repositoryCache.updateUserRepositories(repositoryDTOs); for (RepositoryDTO repositoryDTO : repositoryDTOs) { enrichRepository(repositoryDTO); } return repositoryDTOs; } @GET @Path("user/{name}") public RepositoryDTO getUserRepository(@PathParam("name") String name) { StopWatch watch = new StopWatch(); UserDetails userDetails = gitUserHelper.createUserDetails(request); String user = userDetails.getUser(); GitRepoClient repoClient = userDetails.createRepoClient(); RepositoryDTO answer = repositoryCache.getOrFindUserRepository(user, name, repoClient); LOG.info("getUserRepository took " + watch.taken()); return answer; } @Path("user/{owner}/{repo}") public RepositoryResource repositoryResource(@PathParam("owner") String userId, @PathParam("repo") String repositoryName) throws IOException, GitAPIException { StopWatch watch = new StopWatch(); UserDetails userDetails = gitUserHelper.createUserDetails(request); String origin = projectFileSystem.getRemote(); String branch = request.getParameter("branch"); if (Strings.isNullOrBlank(branch)) { branch = "master"; } String objectId = request.getParameter("ref"); //File projectFolder = projectFileSystem.cloneOrPullProjectFolder(userId, repositoryName, userDetails); File projectFolder = projectFileSystem.getUserProjectFolder(userId, repositoryName); String cloneUrl = projectFileSystem.getCloneUrl(userId, repositoryName, userDetails); File gitFolder = new File(projectFolder, ".git"); String remoteRepository = userId + "/" + repositoryName; RepositoryResource resource = new RepositoryResource(projectFolder, gitFolder, userDetails, origin, branch, remoteRepository, lockManager, projectFileSystem, cloneUrl, objectId); try { String message = request.getParameter("message"); if (Strings.isNotBlank(message)) { resource.setMessage(message); } } catch (Exception e) { LOG.warn("failed to load message parameter: " + e, e); } LOG.info("repositoryResource took " + watch.taken()); return resource; } @Path("project/{namespace}/{projectId}") public RepositoryResource projectRepositoryResource(@PathParam("namespace") String namespace, @PathParam("projectId") String projectId) throws IOException, GitAPIException { StopWatch watch = new StopWatch(); UserDetails userDetails = gitUserHelper.createUserDetails(request); String origin = projectFileSystem.getRemote(); String remoteRepository = namespace + "/" + projectId; String branch = request.getParameter("branch"); if (Strings.isNullOrBlank(branch)) { branch = "master"; } String objectId = request.getParameter("ref"); String uri = request.getParameter("gitUrl"); BuildSource source = null; if (Strings.isNullOrBlank(uri)) { // lets get the BuildConfig OpenShiftClient osClient = new Controller(kubernetes).getOpenShiftClientOrJenkinshift(); if (osClient != null) { BuildConfig buildConfig = osClient.buildConfigs().withName(projectId).get(); if (buildConfig == null) { throw new NotFoundException("No BuildConfig for " + remoteRepository); } BuildConfigSpec spec = buildConfig.getSpec(); if (spec == null) { throw new NotFoundException("No BuildConfig spec for " + remoteRepository); } source = spec.getSource(); if (source == null) { throw new NotFoundException("No BuildConfig source for " + remoteRepository); } GitBuildSource gitSource = source.getGit(); if (gitSource == null) { throw new NotFoundException("No BuildConfig git source for " + remoteRepository); } uri = gitSource.getUri(); } } if (Strings.isNullOrBlank(uri)) { throw new NotFoundException("No BuildConfig git URI for " + remoteRepository); } String sourceSecretName = request.getParameter(Constants.RequestParameters.SECRET); String secretNamespace = request.getParameter(Constants.RequestParameters.SECRET_NAMESPACE); if (Strings.isNullOrBlank(secretNamespace)) { secretNamespace = namespace; } if (Strings.isNullOrBlank(sourceSecretName) && source != null) { LocalObjectReference sourceSecret = source.getSourceSecret(); if (sourceSecret != null) { sourceSecretName = sourceSecret.getName(); } } File projectFolder = projectFileSystem.getNamespaceProjectFolder(namespace, projectId, secretNamespace, sourceSecretName); String cloneUrl = uri; File gitFolder = new File(projectFolder, ".git"); LOG.debug("Cloning " + cloneUrl); RepositoryResource resource = new RepositoryResource(projectFolder, gitFolder, userDetails, origin, branch, remoteRepository, lockManager, projectFileSystem, cloneUrl, objectId); if (sourceSecretName != null) { try { Secret secret = kubernetes.secrets().inNamespace(secretNamespace).withName(sourceSecretName).get(); if (secret != null) { Map<String, String> data = secret.getData(); File privateKeyFile = createSshKeyFile(namespace, sourceSecretName, SSH_PRIVATE_KEY_DATA_KEY, data.get(SSH_PRIVATE_KEY_DATA_KEY)); if (privateKeyFile == null) { privateKeyFile = createSshKeyFile(namespace, sourceSecretName, SSH_PRIVATE_KEY_DATA_KEY2, data.get(SSH_PRIVATE_KEY_DATA_KEY2)); } userDetails.setSshPrivateKey(privateKeyFile); if (privateKeyFile != null) { privateKeyFile.setReadable(true, true); } File publicKeyFile = createSshKeyFile(namespace, sourceSecretName, SSH_PUBLIC_KEY_DATA_KEY, data.get(SSH_PUBLIC_KEY_DATA_KEY)); if (publicKeyFile == null) { publicKeyFile = createSshKeyFile(namespace, sourceSecretName, SSH_PUBLIC_KEY_DATA_KEY2, data.get(SSH_PUBLIC_KEY_DATA_KEY2)); } userDetails.setSshPublicKey(publicKeyFile); String username = decodeSecretData(data.get(USERNAME_DATA_KEY)); String password = decodeSecretData(data.get(PASSWORD_DATA_KEY)); if (Strings.isNotBlank(username)) { userDetails.setUser(username); if (LOG.isDebugEnabled()) { LOG.debug("Using user: " + username); } } if (Strings.isNotBlank(password)) { userDetails.setPassword(password); } } } catch (IOException e) { LOG.error("Failed to load secret key " + sourceSecretName + ". " + e, e); throw new RuntimeException("Failed to load secret key " + sourceSecretName + ". " + e, e); } } try { String message = request.getParameter("message"); if (Strings.isNotBlank(message)) { resource.setMessage(message); } } catch (Exception e) { LOG.warn("failed to load message parameter: " + e, e); } LOG.info("projectRepositoryResource took " + watch.taken()); return resource; } protected String decodeSecretData(String text) { if (Strings.isNotBlank(text)) { return Base64Encoder.decode(text); } else { return text; } } public HttpServletRequest getRequest() { return request; } public void setRequest(HttpServletRequest request) { this.request = request; } protected File createSshKeyFile(String namespace, String sourceSecretName, String privateKeyName, String privateKey) throws IOException { File keyFile = null; if (privateKey != null) { String text = Base64Encoder.decode(privateKey); keyFile = projectFileSystem.getSecretsFolder(namespace, sourceSecretName, privateKeyName); Files.writeToFile(keyFile, text.getBytes()); } return keyFile; } protected void enrichRepository(RepositoryDTO repositoryDTO) { String repoName = repositoryDTO.getName(); if (Strings.isNullOrBlank(repoName)) { String fullName = repositoryDTO.getFullName(); if (Strings.isNotBlank(fullName)) { String[] split = fullName.split("/", 2); if (split != null && split.length > 1) { String user = split[0]; String name = split[1]; //repositoryDTO.setUser(user); repositoryDTO.setName(name); } } } } protected GitRepoClient createGitRepoClient() { UserDetails userDetails = gitUserHelper.createUserDetails(request); LOG.debug("Using user " + userDetails.getUser() + " at " + userDetails.getAddress()); return userDetails.createRepoClient(); } }