diff --git a/cli/build.gradle b/cli/build.gradle index 490ddde5..8994bf8f 100644 --- a/cli/build.gradle +++ b/cli/build.gradle @@ -22,6 +22,7 @@ dependencies { testCompile 'junit:junit:4.12' testCompile 'com.squareup.okhttp:mockwebserver:2.4.0' testCompile 'org.assertj:assertj-core:1.7.1' // last version supporting JDK 7 + testCompile 'com.github.stefanbirkner:system-rules:1.12.0' } // create a self-contained jar that is executable diff --git a/cli/src/main/java/denominator/cli/Denominator.java b/cli/src/main/java/denominator/cli/Denominator.java index c0eaf6aa..93b762c0 100644 --- a/cli/src/main/java/denominator/cli/Denominator.java +++ b/cli/src/main/java/denominator/cli/Denominator.java @@ -1,18 +1,8 @@ package denominator.cli; -import com.google.common.base.CaseFormat; -import com.google.common.base.Charsets; -import com.google.common.base.Function; -import com.google.common.base.Functions; -import com.google.common.base.Joiner; -import com.google.common.base.Predicate; -import com.google.common.collect.FluentIterable; -import com.google.common.collect.ImmutableList; +import com.google.common.base.*; +import com.google.common.collect.*; import com.google.common.collect.ImmutableList.Builder; -import com.google.common.collect.ImmutableSortedSet; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Ordering; import com.google.common.io.Files; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -23,31 +13,6 @@ import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; - -import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.jce.provider.X509CertParser; -import org.bouncycastle.openssl.PEMKeyPair; -import org.bouncycastle.openssl.PEMParser; -import org.yaml.snakeyaml.Yaml; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.StringReader; -import java.lang.reflect.Type; -import java.security.KeyFactory; -import java.security.spec.PKCS8EncodedKeySpec; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import javax.inject.Singleton; - import dagger.ObjectGraph; import dagger.Provides; import denominator.Credentials; @@ -58,19 +23,8 @@ import denominator.Denominator.Version; import denominator.Provider; import denominator.Providers; -import denominator.cli.GeoResourceRecordSetCommands.GeoRegionList; -import denominator.cli.GeoResourceRecordSetCommands.GeoResourceRecordAddRegions; -import denominator.cli.GeoResourceRecordSetCommands.GeoResourceRecordSetApplyTTL; -import denominator.cli.GeoResourceRecordSetCommands.GeoResourceRecordSetGet; -import denominator.cli.GeoResourceRecordSetCommands.GeoResourceRecordSetList; -import denominator.cli.GeoResourceRecordSetCommands.GeoTypeList; -import denominator.cli.ResourceRecordSetCommands.ResourceRecordSetAdd; -import denominator.cli.ResourceRecordSetCommands.ResourceRecordSetApplyTTL; -import denominator.cli.ResourceRecordSetCommands.ResourceRecordSetDelete; -import denominator.cli.ResourceRecordSetCommands.ResourceRecordSetGet; -import denominator.cli.ResourceRecordSetCommands.ResourceRecordSetList; -import denominator.cli.ResourceRecordSetCommands.ResourceRecordSetRemove; -import denominator.cli.ResourceRecordSetCommands.ResourceRecordSetReplace; +import denominator.cli.GeoResourceRecordSetCommands.*; +import denominator.cli.ResourceRecordSetCommands.*; import denominator.cli.ZoneCommands.ZoneAdd; import denominator.cli.ZoneCommands.ZoneDelete; import denominator.cli.ZoneCommands.ZoneList; @@ -80,12 +34,27 @@ import denominator.ultradns.UltraDNSProvider; import feign.Logger; import feign.Logger.Level; -import io.airlift.airline.Cli; +import io.airlift.airline.*; import io.airlift.airline.Cli.CliBuilder; -import io.airlift.airline.Command; -import io.airlift.airline.Help; -import io.airlift.airline.Option; -import io.airlift.airline.OptionType; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.provider.X509CertParser; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.yaml.snakeyaml.Yaml; + +import javax.inject.Singleton; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.Type; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.KeyFactory; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.*; +import java.util.Map.Entry; import static com.google.common.base.Preconditions.checkArgument; import static denominator.CredentialsConfiguration.credentials; @@ -274,6 +243,8 @@ public static abstract class DenominatorCommand implements Runnable { @SuppressWarnings("unchecked") public void run() { + setProxyFromEnv(); + if (providerName != null && credentialArgs != null) { credentials = ListCredentials.from(Lists.transform(credentialArgs, decodeAnyPems)); } else if (providerConfigurationName != null) { @@ -426,6 +397,30 @@ void overrideFromEnv(Map env) { } } + static void setProxyFromEnv() { + setProtocolProxyFromEnv("http", System.getenv("HTTP_PROXY")); + setProtocolProxyFromEnv("https", System.getenv("HTTPS_PROXY")); + } + + static void setProtocolProxyFromEnv(String proto, String envProxy) { + if (envProxy != null && !envProxy.isEmpty()) { + try { + URL proxyUrl = new URL(envProxy); + + String proxyHost = System.getProperty(proto + ".proxyHost"); + if ((proxyHost == null || proxyHost.isEmpty())) { + System.setProperty(proto + ".proxyHost", proxyUrl.getHost()); + System.setProperty(proto + ".proxyPort", + Integer.toString( + proxyUrl.getPort() == -1 ? proxyUrl.getDefaultPort() : proxyUrl.getPort())); + } + } catch (MalformedURLException e) { + System.err.println("invalid " + proto + " proxy configuration: " + e.getMessage()); + System.exit(1); + } + } + } + /** * return a lazy iterator where possible to improve the perceived responsiveness of the cli */ diff --git a/cli/src/test/java/denominator/cli/DenominatorTest.java b/cli/src/test/java/denominator/cli/DenominatorTest.java index 9d73a3d4..7ee3332f 100644 --- a/cli/src/test/java/denominator/cli/DenominatorTest.java +++ b/cli/src/test/java/denominator/cli/DenominatorTest.java @@ -1,40 +1,13 @@ package denominator.cli; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import java.io.IOException; -import java.net.URL; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; - import denominator.AllProfileResourceRecordSetApi; import denominator.Credentials.ListCredentials; import denominator.Credentials.MapCredentials; import denominator.DNSApiManager; import denominator.ResourceRecordSetApi; import denominator.cli.Denominator.ListProviders; -import denominator.cli.GeoResourceRecordSetCommands.GeoRegionList; -import denominator.cli.GeoResourceRecordSetCommands.GeoResourceRecordAddRegions; -import denominator.cli.GeoResourceRecordSetCommands.GeoResourceRecordSetApplyTTL; -import denominator.cli.GeoResourceRecordSetCommands.GeoResourceRecordSetGet; -import denominator.cli.GeoResourceRecordSetCommands.GeoResourceRecordSetList; -import denominator.cli.GeoResourceRecordSetCommands.GeoTypeList; -import denominator.cli.ResourceRecordSetCommands.ResourceRecordSetAdd; -import denominator.cli.ResourceRecordSetCommands.ResourceRecordSetApplyTTL; -import denominator.cli.ResourceRecordSetCommands.ResourceRecordSetDelete; -import denominator.cli.ResourceRecordSetCommands.ResourceRecordSetGet; -import denominator.cli.ResourceRecordSetCommands.ResourceRecordSetList; -import denominator.cli.ResourceRecordSetCommands.ResourceRecordSetRemove; -import denominator.cli.ResourceRecordSetCommands.ResourceRecordSetReplace; +import denominator.cli.GeoResourceRecordSetCommands.*; +import denominator.cli.ResourceRecordSetCommands.*; import denominator.cli.ZoneCommands.ZoneAdd; import denominator.cli.ZoneCommands.ZoneDelete; import denominator.cli.ZoneCommands.ZoneList; @@ -47,11 +20,23 @@ import denominator.model.rdata.AData; import denominator.model.rdata.CNAMEData; import denominator.route53.AliasTarget; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.Assertion; +import org.junit.contrib.java.lang.system.ExpectedSystemExit; +import org.junit.contrib.java.lang.system.RestoreSystemProperties; +import org.junit.contrib.java.lang.system.SystemErrRule; +import org.junit.rules.ExpectedException; + +import java.io.IOException; +import java.net.URL; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.*; import static denominator.assertj.ModelAssertions.assertThat; -import static denominator.model.ResourceRecordSets.a; -import static denominator.model.ResourceRecordSets.cert; -import static denominator.model.ResourceRecordSets.srv; +import static denominator.model.ResourceRecordSets.*; import static java.util.Arrays.asList; import static org.junit.Assert.fail; @@ -60,6 +45,17 @@ public class DenominatorTest { @Rule public final ExpectedException thrown = ExpectedException.none(); + @Rule + public final RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(); + + // Use this to make sure the message logged + @Rule + public final SystemErrRule systemErrRule = new SystemErrRule().enableLog(); + + // use this to test exit occurred on malformed + @Rule + public final ExpectedSystemExit exit = ExpectedSystemExit.none(); + DNSApiManager mgr = denominator.Denominator.create(new MockProvider()); @Test // denominator providers @@ -1200,6 +1196,60 @@ public void testGeoResourceRecordSetAddRegionsBadJson() { .isEqualTo(old); } + @Test + public void testProxySettingsWithPort() { + Denominator.DenominatorCommand.setProtocolProxyFromEnv("http", "http://localhost:7878"); + Denominator.DenominatorCommand.setProtocolProxyFromEnv("https", "https://10.0.0.1:8989"); + assertThat(System.getProperty("http.proxyHost")).isEqualTo("localhost"); + assertThat(System.getProperty("http.proxyPort")).isEqualTo("7878"); + assertThat(System.getProperty("https.proxyHost")).isEqualTo("10.0.0.1"); + assertThat(System.getProperty("https.proxyPort")).isEqualTo("8989"); + } + + @Test + public void testProxySettingsWithDefaultPorts() { + Denominator.DenominatorCommand.setProtocolProxyFromEnv("http", "http://localhost"); + Denominator.DenominatorCommand.setProtocolProxyFromEnv("https", "https://10.0.0.1"); + assertThat(System.getProperty("http.proxyHost")).isEqualTo("localhost"); + assertThat(System.getProperty("http.proxyPort")).isEqualTo("80"); + assertThat(System.getProperty("https.proxyHost")).isEqualTo("10.0.0.1"); + assertThat(System.getProperty("https.proxyPort")).isEqualTo("443"); + } + + @Test + public void testProxySettingsFromProperties() { + System.setProperty("http.proxyHost", "192.168.0.1"); + System.setProperty("https.proxyHost", "192.168.0.2"); + + Denominator.DenominatorCommand.setProtocolProxyFromEnv("http", "http://localhost"); + Denominator.DenominatorCommand.setProtocolProxyFromEnv("https", "https://10.0.0.1"); + + assertThat(System.getProperty("http.proxyHost")).isEqualTo("192.168.0.1"); + assertThat(System.getProperty("http.proxyPort")).isNull(); + assertThat(System.getProperty("https.proxyHost")).isEqualTo("192.168.0.2"); + assertThat(System.getProperty("https.proxyPort")).isNull(); + } + + @Test + public void testInvalidEnvProxySettings() { + exit.expectSystemExitWithStatus(1); + exit.checkAssertionAfterwards(new Assertion() { + @Override + public void checkAssertion() throws Exception { + assertThat(System.getProperty("http.proxyHost")).isEqualTo("localhost"); + assertThat(System.getProperty("http.proxyPort")).isEqualTo("80"); + assertThat(System.getProperty("https.proxyHost")).isNull(); + assertThat(System.getProperty("https.proxyPort")).isNull(); + assertThat(systemErrRule.getLog()) + .isEqualToIgnoringCase( + "invalid https proxy configuration: no protocol: 10.0.0.1:8443\n"); + } + }); + + Denominator.DenominatorCommand.setProtocolProxyFromEnv("http", "http://localhost"); + Denominator.DenominatorCommand.setProtocolProxyFromEnv("https", "10.0.0.1:8443"); + } + @Before public void stockRecords() { {