com.google.api.config.ServiceConfigSupplier Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of endpoints-management-config Show documentation
Show all versions of endpoints-management-config Show documentation
Handles service configuration via the service management API
/*
* Copyright 2016 Google Inc. All Rights Reserved.
*
* 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 com.google.api.config;
import com.google.api.Service;
import com.google.api.Service.Builder;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpStatusCodes;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.servicemanagement.ServiceManagement;
import com.google.api.services.servicemanagement.model.ListServiceConfigsResponse;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.protobuf.util.JsonFormat;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.List;
import javax.annotation.Nullable;
/**
* Supplies a service configuration fetched from Google Service Management APIs.
*/
public final class ServiceConfigSupplier implements Supplier {
private static final String SERVICE_NAME_KEY = "ENDPOINTS_SERVICE_NAME";
private static final String SERVICE_VERSION_KEY = "ENDPOINTS_SERVICE_VERSION";
private static final ImmutableMap ERROR_CODE_DETAILS =
ImmutableMap.builder()
.put(
403 /* forbidden */,
"This may occur if the App Engine service account has been deleted or does not " +
"have correct permissions. Visit https://goo.gl/UEKik4 and verify that the " +
"App Engine default service account has either the Editor or Service Controller " +
"role.")
.put(
404 /* not found */,
"The service config name and config id could not be found. Double check that " +
"filter initialization parameters endpoints.projectId and endpoints.serviceName " +
"are correctly set. See https://goo.gl/yp8QUN for details.")
.put(
429 /* too many requests */,
"Too many requests are being made to fetch the service config. Check to see if " +
"instances are crashing prematurely. If not, you may need to increase your " +
"Google Service Management API quota. See https://goo.gl/JjBQMu to manage quota.")
.build();
private static final List SCOPES =
ImmutableList.of("https://www.googleapis.com/auth/cloud-platform");
private static final String FIELD_MASKS = Joiner.on(',').join(
"authentication",
"http",
"id",
"logging",
"logs",
"metrics",
"monitored_resources",
"monitoring",
"name",
"producer_project_id",
"quota",
"system_parameters",
"usage");
private final Environment environment;
private final ServiceManagement serviceManagement;
@VisibleForTesting
ServiceConfigSupplier(
Environment environment,
HttpTransport httpTransport,
JsonFactory jsonFactory,
final GoogleCredential credential) {
this.environment = environment;
HttpRequestInitializer requestInitializer = new HttpRequestInitializer() {
@Override
public void initialize(HttpRequest request) throws IOException {
request.setThrowExceptionOnExecuteError(false);
credential.initialize(request);
}
};
this.serviceManagement =
new ServiceManagement.Builder(httpTransport, jsonFactory, requestInitializer)
.setApplicationName("Endpoints Frameworks Java")
.build();
}
/**
* Fetches the service configuration using the service name and the service
* version read from the environment variables.
*
* @return a {@link Service} object generated by the JSON response from Google
* Service Management.
*/
@Override
public Service get() {
String serviceName = this.environment.getVariable(SERVICE_NAME_KEY);
if (Strings.isNullOrEmpty(serviceName)) {
String errorMessage =
String.format("Environment variable '%s' is not set", SERVICE_NAME_KEY);
throw new IllegalArgumentException(errorMessage);
}
String serviceVersion = this.environment.getVariable(SERVICE_VERSION_KEY);
return fetch(serviceName, serviceVersion);
}
/**
* Fetches the service configuration with the given service name and service version.
*
* @param serviceName the given service name
* @param serviceVersion the given service version
* @return a {@link Service} object generated by the JSON response from Google Service Management.
*/
private Service fetch(String serviceName, @Nullable String serviceVersion) {
Preconditions.checkArgument(
!Strings.isNullOrEmpty(serviceName), "service name must be specified");
if (serviceVersion == null) {
serviceVersion = fetchLatestServiceVersion(serviceName);
}
final HttpResponse httpResponse;
try {
httpResponse = serviceManagement.services().configs().get(serviceName, serviceVersion)
.setFields(FIELD_MASKS)
.executeUnparsed();
} catch (IOException exception) {
throw new ServiceConfigException(exception);
}
int statusCode = httpResponse.getStatusCode();
if (statusCode != HttpStatusCodes.STATUS_CODE_OK) {
String extendedMessage = ERROR_CODE_DETAILS.get(statusCode);
String message = MessageFormat.format(
"Failed to fetch service config (status code {0})",
statusCode);
if (extendedMessage != null) {
message = String.format("%s: %s", message, extendedMessage);
}
throw new ServiceConfigException(message);
}
Service service = parseHttpResponse(httpResponse);
validateServiceConfig(service, serviceName, serviceVersion);
return service;
}
private String fetchLatestServiceVersion(String serviceName) {
try {
ListServiceConfigsResponse response =
serviceManagement.services().configs().list(serviceName).execute();
if (response.getServiceConfigs() == null || response.getServiceConfigs().isEmpty()) {
throw new ServiceConfigException(MessageFormat.format(
"Failed to fetch default config version for service ''{0}''. No versions exist!",
serviceName));
}
return response.getServiceConfigs().get(0).getId();
} catch (IOException e) {
throw new ServiceConfigException(e);
}
}
private static Service parseHttpResponse(HttpResponse httpResponse) {
try {
Builder builder = Service.newBuilder();
JsonFormat.parser().merge(httpResponse.parseAsString(), builder);
return builder.build();
} catch (IOException exception) {
throw new ServiceConfigException(
"Failed to parse the HTTP response as service configuration",
exception);
}
}
private static void validateServiceConfig(
Service service,
String expectedServiceName,
@Nullable String expectedServiceVersion) {
String serviceName = service.getName();
if (!expectedServiceName.equals(serviceName)) {
throw new ServiceConfigException("Unexpected service name in service config: " + serviceName);
}
String serviceVersion = service.getId();
if (expectedServiceVersion != null && !expectedServiceVersion.equals(serviceVersion)) {
throw new ServiceConfigException("Unexpected service version in service config: " + serviceVersion);
}
}
/**
* Create a {@link ServiceConfigSupplier} instance.
*
* @return a {@code ServiceConfigSuppler}
*/
public static ServiceConfigSupplier create() {
NetHttpTransport httpTransport = new NetHttpTransport();
JacksonFactory jsonFactory = new JacksonFactory();
GoogleCredential credential;
try {
credential = GoogleCredential.getApplicationDefault(httpTransport, jsonFactory)
.createScoped(SCOPES);
} catch (IOException e) {
throw new IllegalStateException("could not get credentials for fetching service config!");
}
return new ServiceConfigSupplier(
new SystemEnvironment(), httpTransport, jsonFactory, credential);
}
private static final class SystemEnvironment implements Environment {
@Override
public String getVariable(String variableName) {
return System.getenv(variableName);
}
}
}