mirror of https://github.com/grpc/grpc-java.git
xds: implement ZatarCertificateProviderProvider (#7526)
This commit is contained in:
parent
0b6f29371b
commit
0e7cd05bf4
|
|
@ -177,4 +177,47 @@ final class ZatarCertificateProvider extends CertificateProvider {
|
|||
checkAndReloadCertificates();
|
||||
}
|
||||
}
|
||||
|
||||
abstract static class Factory {
|
||||
private static final Factory DEFAULT_INSTANCE =
|
||||
new Factory() {
|
||||
@Override
|
||||
ZatarCertificateProvider create(
|
||||
DistributorWatcher watcher,
|
||||
boolean notifyCertUpdates,
|
||||
String directory,
|
||||
String certFile,
|
||||
String privateKeyFile,
|
||||
String trustFile,
|
||||
long refreshIntervalInSeconds,
|
||||
ScheduledExecutorService scheduledExecutorService,
|
||||
TimeProvider timeProvider) {
|
||||
return new ZatarCertificateProvider(
|
||||
watcher,
|
||||
notifyCertUpdates,
|
||||
directory,
|
||||
certFile,
|
||||
privateKeyFile,
|
||||
trustFile,
|
||||
refreshIntervalInSeconds,
|
||||
scheduledExecutorService,
|
||||
timeProvider);
|
||||
}
|
||||
};
|
||||
|
||||
static Factory getInstance() {
|
||||
return DEFAULT_INSTANCE;
|
||||
}
|
||||
|
||||
abstract ZatarCertificateProvider create(
|
||||
DistributorWatcher watcher,
|
||||
boolean notifyCertUpdates,
|
||||
String directory,
|
||||
String certFile,
|
||||
String privateKeyFile,
|
||||
String trustFile,
|
||||
long refreshIntervalInSeconds,
|
||||
ScheduledExecutorService scheduledExecutorService,
|
||||
TimeProvider timeProvider);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Copyright 2020 The gRPC 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 io.grpc.xds.internal.certprovider;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import io.grpc.internal.JsonUtil;
|
||||
import io.grpc.internal.TimeProvider;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
/**
|
||||
* Provider of {@link ZatarCertificateProvider}s.
|
||||
*/
|
||||
final class ZatarCertificateProviderProvider implements CertificateProviderProvider {
|
||||
|
||||
private static final String DIRECTORY_KEY = "directory";
|
||||
private static final String CERT_FILE_KEY = "certificate-file";
|
||||
private static final String KEY_FILE_KEY = "private-key-file";
|
||||
private static final String ROOT_FILE_KEY = "ca-certificate-file";
|
||||
private static final String REFRESH_INTERVAL_KEY = "refresh-interval";
|
||||
|
||||
@VisibleForTesting static final long REFRESH_INTERVAL_DEFAULT = 600L;
|
||||
|
||||
|
||||
static final String ZATAR_PROVIDER_NAME = "gke-cas-certs";
|
||||
|
||||
static {
|
||||
CertificateProviderRegistry.getInstance()
|
||||
.register(
|
||||
new ZatarCertificateProviderProvider(
|
||||
ZatarCertificateProvider.Factory.getInstance(),
|
||||
ScheduledExecutorServiceFactory.DEFAULT_INSTANCE,
|
||||
TimeProvider.SYSTEM_TIME_PROVIDER));
|
||||
}
|
||||
|
||||
final ZatarCertificateProvider.Factory zatarCertificateProviderFactory;
|
||||
private final ScheduledExecutorServiceFactory scheduledExecutorServiceFactory;
|
||||
private final TimeProvider timeProvider;
|
||||
|
||||
@VisibleForTesting
|
||||
ZatarCertificateProviderProvider(
|
||||
ZatarCertificateProvider.Factory zatarCertificateProviderFactory,
|
||||
ScheduledExecutorServiceFactory scheduledExecutorServiceFactory,
|
||||
TimeProvider timeProvider) {
|
||||
this.zatarCertificateProviderFactory = zatarCertificateProviderFactory;
|
||||
this.scheduledExecutorServiceFactory = scheduledExecutorServiceFactory;
|
||||
this.timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return ZATAR_PROVIDER_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CertificateProvider createCertificateProvider(
|
||||
Object config, CertificateProvider.DistributorWatcher watcher, boolean notifyCertUpdates) {
|
||||
|
||||
Config configObj = validateAndTranslateConfig(config);
|
||||
return zatarCertificateProviderFactory.create(
|
||||
watcher,
|
||||
notifyCertUpdates,
|
||||
configObj.directory,
|
||||
configObj.certFile,
|
||||
configObj.keyFile,
|
||||
configObj.rootFile,
|
||||
configObj.refrehInterval,
|
||||
scheduledExecutorServiceFactory.create(),
|
||||
timeProvider);
|
||||
}
|
||||
|
||||
private static String checkForNullAndGet(Map<String, ?> map, String key) {
|
||||
return checkNotNull(JsonUtil.getString(map, key), "'" + key + "' is required in the config");
|
||||
}
|
||||
|
||||
private static Config validateAndTranslateConfig(Object config) {
|
||||
checkArgument(config instanceof Map, "Only Map supported for config");
|
||||
@SuppressWarnings("unchecked") Map<String, ?> map = (Map<String, ?>)config;
|
||||
|
||||
Config configObj = new Config();
|
||||
configObj.directory = checkForNullAndGet(map, DIRECTORY_KEY);
|
||||
configObj.certFile = checkForNullAndGet(map, CERT_FILE_KEY);
|
||||
configObj.keyFile = checkForNullAndGet(map, KEY_FILE_KEY);
|
||||
configObj.rootFile = checkForNullAndGet(map, ROOT_FILE_KEY);
|
||||
configObj.refrehInterval = JsonUtil.getNumberAsLong(map, REFRESH_INTERVAL_KEY);
|
||||
if (configObj.refrehInterval == null) {
|
||||
configObj.refrehInterval = REFRESH_INTERVAL_DEFAULT;
|
||||
}
|
||||
return configObj;
|
||||
}
|
||||
|
||||
abstract static class ScheduledExecutorServiceFactory {
|
||||
|
||||
private static final ScheduledExecutorServiceFactory DEFAULT_INSTANCE =
|
||||
new ScheduledExecutorServiceFactory() {
|
||||
|
||||
@Override
|
||||
ScheduledExecutorService create() {
|
||||
return Executors.newSingleThreadScheduledExecutor(
|
||||
new ThreadFactoryBuilder()
|
||||
.setNameFormat("zatar" + "-%d")
|
||||
.setDaemon(true)
|
||||
.build());
|
||||
}
|
||||
};
|
||||
|
||||
abstract ScheduledExecutorService create();
|
||||
}
|
||||
|
||||
/** POJO class for storing various config values. */
|
||||
@VisibleForTesting
|
||||
static class Config {
|
||||
String directory;
|
||||
String certFile;
|
||||
String keyFile;
|
||||
String rootFile;
|
||||
Long refrehInterval;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* Copyright 2020 The gRPC 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 io.grpc.xds.internal.certprovider;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import io.grpc.internal.JsonParser;
|
||||
import io.grpc.internal.TimeProvider;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
/** Unit tests for {@link ZatarCertificateProviderProvider}. */
|
||||
@RunWith(JUnit4.class)
|
||||
public class ZatarCertificateProviderProviderTest {
|
||||
|
||||
@Mock ZatarCertificateProvider.Factory zatarCertificateProviderFactory;
|
||||
@Mock private ZatarCertificateProviderProvider.ScheduledExecutorServiceFactory
|
||||
scheduledExecutorServiceFactory;
|
||||
@Mock private TimeProvider timeProvider;
|
||||
|
||||
private ZatarCertificateProviderProvider provider;
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
provider =
|
||||
new ZatarCertificateProviderProvider(
|
||||
zatarCertificateProviderFactory, scheduledExecutorServiceFactory, timeProvider);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void providerRegisteredName() {
|
||||
CertificateProviderProvider certProviderProvider =
|
||||
CertificateProviderRegistry.getInstance()
|
||||
.getProvider(ZatarCertificateProviderProvider.ZATAR_PROVIDER_NAME);
|
||||
assertThat(certProviderProvider).isInstanceOf(ZatarCertificateProviderProvider.class);
|
||||
ZatarCertificateProviderProvider zatarCertificateProviderProvider =
|
||||
(ZatarCertificateProviderProvider) certProviderProvider;
|
||||
assertThat(zatarCertificateProviderProvider.zatarCertificateProviderFactory)
|
||||
.isSameInstanceAs(ZatarCertificateProvider.Factory.getInstance());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createProvider_minimalConfig() throws IOException {
|
||||
CertificateProvider.DistributorWatcher distWatcher =
|
||||
new CertificateProvider.DistributorWatcher();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, ?> map = (Map<String, ?>) JsonParser.parse(MINIMAL_ZATAR_CONFIG);
|
||||
ScheduledExecutorService mockService = mock(ScheduledExecutorService.class);
|
||||
when(scheduledExecutorServiceFactory.create()).thenReturn(mockService);
|
||||
provider.createCertificateProvider(map, distWatcher, true);
|
||||
verify(zatarCertificateProviderFactory, times(1))
|
||||
.create(
|
||||
eq(distWatcher),
|
||||
eq(true),
|
||||
eq("/var/run/gke-spiffe/certs/..data"),
|
||||
eq("certificates.pem"),
|
||||
eq("private_key.pem"),
|
||||
eq("ca_certificates.pem"),
|
||||
eq(600L),
|
||||
eq(mockService),
|
||||
eq(timeProvider));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createProvider_fullConfig() throws IOException {
|
||||
CertificateProvider.DistributorWatcher distWatcher =
|
||||
new CertificateProvider.DistributorWatcher();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, ?> map = (Map<String, ?>) JsonParser.parse(FULL_ZATAR_CONFIG);
|
||||
ScheduledExecutorService mockService = mock(ScheduledExecutorService.class);
|
||||
when(scheduledExecutorServiceFactory.create()).thenReturn(mockService);
|
||||
provider.createCertificateProvider(map, distWatcher, true);
|
||||
verify(zatarCertificateProviderFactory, times(1))
|
||||
.create(
|
||||
eq(distWatcher),
|
||||
eq(true),
|
||||
eq("/var/run/gke-spiffe/certs/..data1"),
|
||||
eq("certificates2.pem"),
|
||||
eq("private_key3.pem"),
|
||||
eq("ca_certificates4.pem"),
|
||||
eq(7890L),
|
||||
eq(mockService),
|
||||
eq(timeProvider));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createProvider_missingDir_expectException() throws IOException {
|
||||
CertificateProvider.DistributorWatcher distWatcher =
|
||||
new CertificateProvider.DistributorWatcher();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, ?> map = (Map<String, ?>) JsonParser.parse(MISSING_DIR_CONFIG);
|
||||
try {
|
||||
provider.createCertificateProvider(map, distWatcher, true);
|
||||
fail("exception expected");
|
||||
} catch (NullPointerException npe) {
|
||||
assertThat(npe).hasMessageThat().isEqualTo("'directory' is required in the config");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createProvider_missingCert_expectException() throws IOException {
|
||||
CertificateProvider.DistributorWatcher distWatcher =
|
||||
new CertificateProvider.DistributorWatcher();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, ?> map = (Map<String, ?>) JsonParser.parse(MISSING_CERT_CONFIG);
|
||||
try {
|
||||
provider.createCertificateProvider(map, distWatcher, true);
|
||||
fail("exception expected");
|
||||
} catch (NullPointerException npe) {
|
||||
assertThat(npe).hasMessageThat().isEqualTo("'certificate-file' is required in the config");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createProvider_missingKey_expectException() throws IOException {
|
||||
CertificateProvider.DistributorWatcher distWatcher =
|
||||
new CertificateProvider.DistributorWatcher();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, ?> map = (Map<String, ?>) JsonParser.parse(MISSING_KEY_CONFIG);
|
||||
try {
|
||||
provider.createCertificateProvider(map, distWatcher, true);
|
||||
fail("exception expected");
|
||||
} catch (NullPointerException npe) {
|
||||
assertThat(npe).hasMessageThat().isEqualTo("'private-key-file' is required in the config");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createProvider_missingRoot_expectException() throws IOException {
|
||||
CertificateProvider.DistributorWatcher distWatcher =
|
||||
new CertificateProvider.DistributorWatcher();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, ?> map = (Map<String, ?>) JsonParser.parse(MISSING_ROOT_CONFIG);
|
||||
try {
|
||||
provider.createCertificateProvider(map, distWatcher, true);
|
||||
fail("exception expected");
|
||||
} catch (NullPointerException npe) {
|
||||
assertThat(npe).hasMessageThat().isEqualTo("'ca-certificate-file' is required in the config");
|
||||
}
|
||||
}
|
||||
|
||||
private static final String MINIMAL_ZATAR_CONFIG =
|
||||
"{\n"
|
||||
+ " \"directory\": \"/var/run/gke-spiffe/certs/..data\","
|
||||
+ " \"certificate-file\": \"certificates.pem\","
|
||||
+ " \"private-key-file\": \"private_key.pem\","
|
||||
+ " \"ca-certificate-file\": \"ca_certificates.pem\""
|
||||
+ " }";
|
||||
|
||||
private static final String FULL_ZATAR_CONFIG =
|
||||
"{\n"
|
||||
+ " \"directory\": \"/var/run/gke-spiffe/certs/..data1\","
|
||||
+ " \"certificate-file\": \"certificates2.pem\","
|
||||
+ " \"private-key-file\": \"private_key3.pem\","
|
||||
+ " \"ca-certificate-file\": \"ca_certificates4.pem\","
|
||||
+ " \"refresh-interval\": 7890"
|
||||
+ " }";
|
||||
|
||||
private static final String MISSING_DIR_CONFIG =
|
||||
"{\n"
|
||||
+ " \"certificate-file\": \"certificates.pem\","
|
||||
+ " \"private-key-file\": \"private_key.pem\","
|
||||
+ " \"ca-certificate-file\": \"ca_certificates.pem\""
|
||||
+ " }";
|
||||
|
||||
private static final String MISSING_CERT_CONFIG =
|
||||
"{\n"
|
||||
+ " \"directory\": \"/var/run/gke-spiffe/certs/..data\","
|
||||
+ " \"private-key-file\": \"private_key.pem\","
|
||||
+ " \"ca-certificate-file\": \"ca_certificates.pem\""
|
||||
+ " }";
|
||||
|
||||
private static final String MISSING_KEY_CONFIG =
|
||||
"{\n"
|
||||
+ " \"directory\": \"/var/run/gke-spiffe/certs/..data\","
|
||||
+ " \"certificate-file\": \"certificates.pem\","
|
||||
+ " \"ca-certificate-file\": \"ca_certificates.pem\""
|
||||
+ " }";
|
||||
|
||||
private static final String MISSING_ROOT_CONFIG =
|
||||
"{\n"
|
||||
+ " \"directory\": \"/var/run/gke-spiffe/certs/..data\","
|
||||
+ " \"certificate-file\": \"certificates.pem\","
|
||||
+ " \"private-key-file\": \"private_key.pem\""
|
||||
+ " }";
|
||||
}
|
||||
Loading…
Reference in New Issue