From febee1dba47c1e68c5c3b233a280dc57f1b81002 Mon Sep 17 00:00:00 2001 From: Gilvan Filho Date: Fri, 28 Oct 2022 16:53:09 -0300 Subject: [PATCH] [CAMEL-18646] Provide custom configuration Allows user to provide a custom git config file through a endpoint query param --- .../component/git/GitEndpointConfigurer.java | 6 + .../component/git/GitEndpointUriFactory.java | 3 +- .../org/apache/camel/component/git/git.json | 3 +- .../src/main/docs/git-component.adoc | 15 +++ .../component/CustomConfigSystemReader.java | 85 +++++++++++++ .../camel/component/RepositoryFactory.java | 114 ++++++++++++++++++ .../camel/component/git/GitEndpoint.java | 14 +++ .../git/consumer/AbstractGitConsumer.java | 20 +-- .../component/git/producer/GitProducer.java | 15 +-- .../git/consumer/GitConsumerTest.java | 24 +++- .../camel-git/src/test/resources/git.config | 2 + 11 files changed, 270 insertions(+), 31 deletions(-) create mode 100644 components/camel-git/src/main/java/org/apache/camel/component/CustomConfigSystemReader.java create mode 100644 components/camel-git/src/main/java/org/apache/camel/component/RepositoryFactory.java create mode 100644 components/camel-git/src/test/resources/git.config diff --git a/components/camel-git/src/generated/java/org/apache/camel/component/git/GitEndpointConfigurer.java b/components/camel-git/src/generated/java/org/apache/camel/component/git/GitEndpointConfigurer.java index 422aa5674bc7c..228a4041b48b7 100644 --- a/components/camel-git/src/generated/java/org/apache/camel/component/git/GitEndpointConfigurer.java +++ b/components/camel-git/src/generated/java/org/apache/camel/component/git/GitEndpointConfigurer.java @@ -31,6 +31,8 @@ public boolean configure(CamelContext camelContext, Object obj, String name, Obj case "exceptionHandler": target.setExceptionHandler(property(camelContext, org.apache.camel.spi.ExceptionHandler.class, value)); return true; case "exchangepattern": case "exchangePattern": target.setExchangePattern(property(camelContext, org.apache.camel.ExchangePattern.class, value)); return true; + case "gitconfigfile": + case "gitConfigFile": target.setGitConfigFile(property(camelContext, java.lang.String.class, value)); return true; case "lazystartproducer": case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true; case "operation": target.setOperation(property(camelContext, java.lang.String.class, value)); return true; @@ -62,6 +64,8 @@ public Class getOptionType(String name, boolean ignoreCase) { case "exceptionHandler": return org.apache.camel.spi.ExceptionHandler.class; case "exchangepattern": case "exchangePattern": return org.apache.camel.ExchangePattern.class; + case "gitconfigfile": + case "gitConfigFile": return java.lang.String.class; case "lazystartproducer": case "lazyStartProducer": return boolean.class; case "operation": return java.lang.String.class; @@ -94,6 +98,8 @@ public Object getOptionValue(Object obj, String name, boolean ignoreCase) { case "exceptionHandler": return target.getExceptionHandler(); case "exchangepattern": case "exchangePattern": return target.getExchangePattern(); + case "gitconfigfile": + case "gitConfigFile": return target.getGitConfigFile(); case "lazystartproducer": case "lazyStartProducer": return target.isLazyStartProducer(); case "operation": return target.getOperation(); diff --git a/components/camel-git/src/generated/java/org/apache/camel/component/git/GitEndpointUriFactory.java b/components/camel-git/src/generated/java/org/apache/camel/component/git/GitEndpointUriFactory.java index 0dd639fff8672..2ae9173342799 100644 --- a/components/camel-git/src/generated/java/org/apache/camel/component/git/GitEndpointUriFactory.java +++ b/components/camel-git/src/generated/java/org/apache/camel/component/git/GitEndpointUriFactory.java @@ -21,12 +21,13 @@ public class GitEndpointUriFactory extends org.apache.camel.support.component.En private static final Set SECRET_PROPERTY_NAMES; private static final Set MULTI_VALUE_PREFIXES; static { - Set props = new HashSet<>(15); + Set props = new HashSet<>(16); props.add("allowEmpty"); props.add("branchName"); props.add("bridgeErrorHandler"); props.add("exceptionHandler"); props.add("exchangePattern"); + props.add("gitConfigFile"); props.add("lazyStartProducer"); props.add("localPath"); props.add("operation"); diff --git a/components/camel-git/src/generated/resources/org/apache/camel/component/git/git.json b/components/camel-git/src/generated/resources/org/apache/camel/component/git/git.json index 7dcb8c41c8097..0ba084eb6326d 100644 --- a/components/camel-git/src/generated/resources/org/apache/camel/component/git/git.json +++ b/components/camel-git/src/generated/resources/org/apache/camel/component/git/git.json @@ -55,6 +55,7 @@ "tagName": { "kind": "parameter", "displayName": "Tag Name", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The tag name to work on" }, "targetBranchName": { "kind": "parameter", "displayName": "Target Branch Name", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "master", "description": "Name of target branch in merge operation. If not supplied will try to use init.defaultBranch git configs. If not configured will use default value" }, "username": { "kind": "parameter", "displayName": "Username", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Remote repository username" }, - "lazyStartProducer": { "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." } + "lazyStartProducer": { "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." }, + "gitConfigFile": { "kind": "parameter", "displayName": "Git Config File", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "A String with path to a .gitconfig file" } } } diff --git a/components/camel-git/src/main/docs/git-component.adoc b/components/camel-git/src/main/docs/git-component.adoc index 0080cad95f71e..957a29a51c660 100644 --- a/components/camel-git/src/main/docs/git-component.adoc +++ b/components/camel-git/src/main/docs/git-component.adoc @@ -83,5 +83,20 @@ from("git:///tmp/testRepo?type=commit") .to(....) --------------------------------------- +== Custom config file +By default camel-git will load ``.gitconfig`` file from user home folder. You +can override this by providing your own ``.gitconfig`` file. + +[source,java] +--------------------------------------- +from("git:///tmp/testRepo?type=commit&gitConfigFile=file:/tmp/configfile") + .to(....) //will load from os dirs + +from("git:///tmp/testRepo?type=commit&gitConfigFile=classpath:configfile") + .to(....) //will load from resources dir + +from("git:///tmp/testRepo?type=commit&gitConfigFile=http://somedomain.xyz/gitconfigfile") + .to(....) //will load from http. You could also use https +--------------------------------------- include::spring-boot:partial$starter.adoc[] diff --git a/components/camel-git/src/main/java/org/apache/camel/component/CustomConfigSystemReader.java b/components/camel-git/src/main/java/org/apache/camel/component/CustomConfigSystemReader.java new file mode 100644 index 0000000000000..df3d882528f34 --- /dev/null +++ b/components/camel-git/src/main/java/org/apache/camel/component/CustomConfigSystemReader.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 org.apache.camel.component; + +import java.io.File; + +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.storage.file.FileBasedConfig; +import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.SystemReader; + +public class CustomConfigSystemReader extends SystemReader { + private static final SystemReader PROXY = SystemReader.getInstance(); + private File userGitConfig; + + public CustomConfigSystemReader(File userGitConfig) { + super(); + this.userGitConfig = userGitConfig; + } + + @Override + public String getenv(String variable) { + return PROXY.getenv(variable); + } + + @Override + public String getHostname() { + return PROXY.getHostname(); + } + + @Override + public String getProperty(String key) { + return PROXY.getProperty(key); + } + + @Override + public long getCurrentTime() { + return PROXY.getCurrentTime(); + } + + @Override + public int getTimezone(long when) { + return PROXY.getTimezone(when); + } + + @Override + public FileBasedConfig openUserConfig(Config parent, FS fs) { + return new FileBasedConfig(parent, userGitConfig, fs); + } + + @Override + public FileBasedConfig openJGitConfig(Config parent, FS fs) { + return PROXY.openJGitConfig(parent, fs); + } + + // Return an empty system configuration, based on example in SystemReader.Default#openSystemConfig + @Override + public FileBasedConfig openSystemConfig(Config parent, FS fs) { + return new FileBasedConfig(parent, null, fs) { + @Override + public void load() { + } + + @Override + public boolean isOutdated() { + return false; + } + }; + } + +} diff --git a/components/camel-git/src/main/java/org/apache/camel/component/RepositoryFactory.java b/components/camel-git/src/main/java/org/apache/camel/component/RepositoryFactory.java new file mode 100644 index 0000000000000..2950415e39a50 --- /dev/null +++ b/components/camel-git/src/main/java/org/apache/camel/component/RepositoryFactory.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 org.apache.camel.component; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URL; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; + +import org.apache.camel.RuntimeCamelException; +import org.apache.camel.component.git.GitEndpoint; +import org.apache.camel.support.ResourceHelper; +import org.apache.camel.util.ObjectHelper; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; +import org.eclipse.jgit.util.SystemReader; + +public abstract class RepositoryFactory { + + private static final SystemReader DEFAULT_INSTANCE; + private static final List VALID_SCHEMES = Arrays.asList("classpath:", "file:", "http:", "https:"); + + static { + DEFAULT_INSTANCE = SystemReader.getInstance(); + } + + private RepositoryFactory() { + } + + public static Repository of(GitEndpoint endpoint) { + if (ObjectHelper.isNotEmpty(endpoint.getGitConfigFile())) { + return resolveConfigFile(endpoint, endpoint.getGitConfigFile()); + } + return getRepository(endpoint, DEFAULT_INSTANCE); + } + + private static Repository resolveConfigFile(GitEndpoint endpoint, String uri) { + if (ObjectHelper.isEmpty(uri)) { + throw new IllegalArgumentException("URI to git config file must be supplied"); + } + + if (!ResourceHelper.hasScheme(uri) || !VALID_SCHEMES.contains(ResourceHelper.getScheme(uri))) { + throw new IllegalArgumentException( + "URI to git config file must have scheme:path pattern where scheme could be classpath, file, http or https"); + } + + String schema = ResourceHelper.getScheme(uri); + String path = uri.substring(schema.length()); + + File gitConfigFile; + if (ResourceHelper.isClasspathUri(uri)) { + gitConfigFile = new File(endpoint.getClass().getClassLoader().getResource(path).getFile()); + } else if (ResourceHelper.isHttpUri(uri)) { + try { + gitConfigFile = getTempFileFromHttp(uri); + } catch (IOException e) { + throw new RuntimeCamelException(String.format("Something went wrong when loading: %s", uri), e); + } + } else { //load from system + gitConfigFile = new File(path); + if (Files.isDirectory(gitConfigFile.toPath()) || !Files.isReadable(gitConfigFile.toPath())) { + throw new IllegalArgumentException( + String.format( + "The configuration file at %s is unreadable (either missing, lacking proper access permission or is not a regular file)", + path)); + } + } + + return getRepository(endpoint, new CustomConfigSystemReader(gitConfigFile)); + } + + private static Repository getRepository(GitEndpoint endpoint, SystemReader instance) { + FileRepositoryBuilder builder = new FileRepositoryBuilder(); + try { + SystemReader.setInstance(instance); + // scan environment GIT_* variables + return builder.setGitDir(new File(endpoint.getLocalPath(), ".git")).readEnvironment() + .findGitDir() // scan up the file system tree + .build(); + } catch (IOException e) { + throw new RuntimeCamelException( + String.format("There was an error opening the repository at %s", endpoint.getLocalPath()), e); + } + } + + private static File getTempFileFromHttp(String url) throws IOException { + Path tempFile = Files.createTempFile(null, null); + FileOutputStream outputStream = new FileOutputStream(tempFile.toString()); + ReadableByteChannel byteChannel = Channels.newChannel(new URL(url).openStream()); + outputStream.getChannel().transferFrom(byteChannel, 0, Long.MAX_VALUE); + return tempFile.toFile(); + } + +} diff --git a/components/camel-git/src/main/java/org/apache/camel/component/git/GitEndpoint.java b/components/camel-git/src/main/java/org/apache/camel/component/git/GitEndpoint.java index 8537f2f3071ac..165ac92efcd1a 100644 --- a/components/camel-git/src/main/java/org/apache/camel/component/git/GitEndpoint.java +++ b/components/camel-git/src/main/java/org/apache/camel/component/git/GitEndpoint.java @@ -77,6 +77,9 @@ public class GitEndpoint extends DefaultEndpoint { label = "producer") private String operation; + @UriParam(description = "A String with path to a .gitconfig file", label = "advanced") + private String gitConfigFile; + public GitEndpoint(String uri, GitComponent component) { super(uri, component); } @@ -219,4 +222,15 @@ public String getTargetBranchName() { public void setTargetBranchName(String targetBranchName) { this.targetBranchName = targetBranchName; } + + /** + * A String with path to a .gitconfig file", label = "producer,consumer,advanced + */ + public String getGitConfigFile() { + return this.gitConfigFile; + } + + public void setGitConfigFile(String gitConfigFile) { + this.gitConfigFile = gitConfigFile; + } } diff --git a/components/camel-git/src/main/java/org/apache/camel/component/git/consumer/AbstractGitConsumer.java b/components/camel-git/src/main/java/org/apache/camel/component/git/consumer/AbstractGitConsumer.java index a99156224ae99..355d324901f90 100644 --- a/components/camel-git/src/main/java/org/apache/camel/component/git/consumer/AbstractGitConsumer.java +++ b/components/camel-git/src/main/java/org/apache/camel/component/git/consumer/AbstractGitConsumer.java @@ -16,15 +16,12 @@ */ package org.apache.camel.component.git.consumer; -import java.io.File; -import java.io.IOException; - import org.apache.camel.Processor; +import org.apache.camel.component.RepositoryFactory; import org.apache.camel.component.git.GitEndpoint; import org.apache.camel.support.ScheduledPollConsumer; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.storage.file.FileRepositoryBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,20 +54,11 @@ protected void doStop() throws Exception { git.close(); } - private Repository getLocalRepository() throws IOException { - FileRepositoryBuilder builder = new FileRepositoryBuilder(); - try { - // scan environment GIT_* variables - return builder.setGitDir(new File(endpoint.getLocalPath(), ".git")).readEnvironment() - .findGitDir() // scan up the file system tree - .build(); - } catch (IOException e) { - LOG.error("There was an error, cannot open {} repository", endpoint.getLocalPath()); - throw e; - } + private Repository getLocalRepository() { + return RepositoryFactory.of(endpoint); } - protected Repository getRepository() { + public Repository getRepository() { return repo; } diff --git a/components/camel-git/src/main/java/org/apache/camel/component/git/producer/GitProducer.java b/components/camel-git/src/main/java/org/apache/camel/component/git/producer/GitProducer.java index 51267dc1d47cc..3b9c9e35e58ff 100644 --- a/components/camel-git/src/main/java/org/apache/camel/component/git/producer/GitProducer.java +++ b/components/camel-git/src/main/java/org/apache/camel/component/git/producer/GitProducer.java @@ -24,6 +24,7 @@ import java.util.Set; import org.apache.camel.Exchange; +import org.apache.camel.component.RepositoryFactory; import org.apache.camel.component.git.GitConstants; import org.apache.camel.component.git.GitEndpoint; import org.apache.camel.support.DefaultProducer; @@ -46,7 +47,6 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; -import org.eclipse.jgit.storage.file.FileRepositoryBuilder; import org.eclipse.jgit.transport.PushResult; import org.eclipse.jgit.transport.RemoteConfig; import org.eclipse.jgit.transport.URIish; @@ -658,17 +658,8 @@ protected void doRemoteList(Exchange exchange, String operation) throws GitAPIEx updateExchange(exchange, result); } - private Repository getLocalRepository() throws IOException { - FileRepositoryBuilder builder = new FileRepositoryBuilder(); - try { - // scan environment GIT_* variables - return builder.setGitDir(new File(endpoint.getLocalPath(), ".git")).readEnvironment() - .findGitDir() // scan up the file system tree - .build(); - } catch (IOException e) { - LOG.error("There was an error, cannot open {} repository", endpoint.getLocalPath()); - throw e; - } + private Repository getLocalRepository() { + return RepositoryFactory.of(endpoint); } private void updateExchange(Exchange exchange, Object body) { diff --git a/components/camel-git/src/test/java/org/apache/camel/component/git/consumer/GitConsumerTest.java b/components/camel-git/src/test/java/org/apache/camel/component/git/consumer/GitConsumerTest.java index c99a570ee83a6..6bf059fde31bd 100644 --- a/components/camel-git/src/test/java/org/apache/camel/component/git/consumer/GitConsumerTest.java +++ b/components/camel-git/src/test/java/org/apache/camel/component/git/consumer/GitConsumerTest.java @@ -29,6 +29,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; public class GitConsumerTest extends GitTestSupport { @@ -160,6 +161,20 @@ public void branchConsumerTest() throws Exception { git.close(); } + @Test + public void injectConfigFileTest() throws Exception { + GitBranchConsumer consumer; + + consumer = (GitBranchConsumer) context.getRoute("injectConfigFileFromClasspath").getConsumer(); + assertEquals("fromClasspath", consumer.getRepository().getConfig().getString("init", null, "defaultBranch")); + + consumer = (GitBranchConsumer) context.getRoute("injectConfigFileFromHttp").getConsumer(); + assertEquals("fromHttp", consumer.getRepository().getConfig().getString("init", null, "defaultBranch")); + + consumer = (GitBranchConsumer) context.getRoute("defaultBranchTest").getConsumer(); + assertNull(consumer.getRepository().getConfig().getString("init", null, "defaultBranch")); + } + @Override protected RouteBuilder createRouteBuilder() { return new RouteBuilder() { @@ -175,7 +190,14 @@ public void configure() { from("git://" + gitLocalRepo + "?type=commit&branchName=master").to("mock:result-commit"); from("git://" + gitLocalRepo + "?type=commit&branchName=notexisting").to("mock:result-commit-notexistent"); from("git://" + gitLocalRepo + "?type=tag").to("mock:result-tag"); - from("git://" + gitLocalRepo + "?type=branch").to("mock:result-branch"); + from("git://" + gitLocalRepo + "?type=branch&gitConfigFile=classpath:git.config") + .id("injectConfigFileFromClasspath") + .to("mock:result-branch-configfile"); + from("git://" + gitLocalRepo + + "?type=branch&gitConfigFile=https://gist.githubusercontent.com/gilvansfilho/a61f6ab811a5e8e9d46c4fba1235abc1/raw/a1f614c90e29f1cdd83534aa913f5d276beace2c/gitconfig") + .id("injectConfigFileFromHttp") + .to("mock:result-branch-configfile"); + from("git://" + gitLocalRepo + "?type=branch").id("defaultBranchTest").to("mock:result-branch"); } }; } diff --git a/components/camel-git/src/test/resources/git.config b/components/camel-git/src/test/resources/git.config new file mode 100644 index 0000000000000..b97a502c08a42 --- /dev/null +++ b/components/camel-git/src/test/resources/git.config @@ -0,0 +1,2 @@ +[init] + defaultBranch = fromClasspath