Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2020 Hazelcast Inc.
*
* Licensed under the Hazelcast Community License (the "License"); you may not use
* this file except in compliance with the License. You may obtain a copy of the
* License at
*
* http://hazelcast.com/hazelcast-community-license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.hazelcast.gcp;
import com.hazelcast.logging.ILogger;
import com.hazelcast.logging.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
/**
* Responsible for fetching the discovery information from GCP APIs.
*/
class GcpClient {
private static final ILogger LOGGER = Logger.getLogger(GcpDiscoveryStrategy.class);
private static final int HTTP_UNAUTHORIZED = 401;
private static final int HTTP_FORBIDDEN = 403;
private static final int HTTP_NOT_FOUND = 404;
private static final int RETRIES = 3;
private static final List NON_RETRYABLE_KEYWORDS = asList("Private key json file not found",
"Request had insufficient authentication scopes", "Required 'compute.instances.list' permission",
"Service account not enabled on this instance");
private boolean isKnownExceptionAlreadyLogged;
private final GcpMetadataApi gcpMetadataApi;
private final GcpComputeApi gcpComputeApi;
private final GcpAuthenticator gcpAuthenticator;
private final String privateKeyPath;
private final List projects;
private final List zones;
private final Label label;
GcpClient(GcpMetadataApi gcpMetadataApi, GcpComputeApi gcpComputeApi, GcpAuthenticator gcpAuthenticator,
GcpConfig gcpConfig) {
this.gcpMetadataApi = gcpMetadataApi;
this.gcpComputeApi = gcpComputeApi;
this.gcpAuthenticator = gcpAuthenticator;
this.privateKeyPath = gcpConfig.getPrivateKeyPath();
this.projects = projectFromConfigOrMetadataApi(gcpConfig);
this.zones = zonesFromConfigOrComputeApi(gcpConfig);
this.label = gcpConfig.getLabel();
}
private List projectFromConfigOrMetadataApi(final GcpConfig gcpConfig) {
if (!gcpConfig.getProjects().isEmpty()) {
return gcpConfig.getProjects();
}
LOGGER.finest("Property 'projects' not configured, fetching the current GCP project");
return singletonList(RetryUtils.retry(new Callable() {
@Override
public String call() {
return gcpMetadataApi.currentProject();
}
}, RETRIES, NON_RETRYABLE_KEYWORDS));
}
private List zonesFromConfigOrComputeApi(final GcpConfig gcpConfig) {
try {
if (gcpConfig.getRegion() != null) {
LOGGER.finest("Property 'region' configured, fetching GCP zones of the specified GCP region");
return RetryUtils.retry(new Callable>() {
@Override
public List call() {
return fetchZones(gcpConfig.getRegion());
}
}, RETRIES, NON_RETRYABLE_KEYWORDS);
}
if (!gcpConfig.getZones().isEmpty()) {
return gcpConfig.getZones();
}
LOGGER.finest("Property 'zones' not configured, fetching GCP zones of the current GCP region");
return RetryUtils.retry(new Callable>() {
@Override
public List call() {
String region = gcpMetadataApi.currentRegion();
return fetchZones(region);
}
}, RETRIES, NON_RETRYABLE_KEYWORDS);
} catch (RestClientException e) {
handleKnownException(e);
return emptyList();
}
}
List getAddresses() {
try {
return RetryUtils.retry(new Callable>() {
@Override
public List call() {
return fetchGcpAddresses();
}
}, RETRIES, NON_RETRYABLE_KEYWORDS);
} catch (RestClientException e) {
handleKnownException(e);
return emptyList();
}
}
private List fetchZones(String region) {
List zones = new ArrayList<>();
String accessToken = fetchAccessToken();
for (String project : projects) {
zones.addAll(gcpComputeApi.zones(project, region, accessToken));
}
return zones;
}
private List fetchGcpAddresses() {
LOGGER.finest("Fetching OAuth Access Token");
final String accessToken = fetchAccessToken();
List result = new ArrayList();
for (final String project : projects) {
for (final String zone : zones) {
LOGGER.finest(String.format("Fetching instances for project '%s' and zone '%s'", project, zone));
List addresses = gcpComputeApi.instances(project, zone, label, accessToken);
LOGGER.finest(String.format("Found the following instances for project '%s' and zone '%s': %s", project, zone,
addresses));
result.addAll(addresses);
}
}
return result;
}
private String fetchAccessToken() {
if (privateKeyPath != null) {
return gcpAuthenticator.refreshAccessToken(privateKeyPath);
}
return gcpMetadataApi.accessToken();
}
String getAvailabilityZone() {
return gcpMetadataApi.currentZone();
}
private void handleKnownException(RestClientException e) {
if (e.getHttpErrorCode() == HTTP_UNAUTHORIZED) {
if (!isKnownExceptionAlreadyLogged) {
LOGGER.warning("Google Cloud API Authorization failed! Check your credentials. Starting standalone.");
isKnownExceptionAlreadyLogged = true;
}
} else if (e.getHttpErrorCode() == HTTP_FORBIDDEN) {
if (!isKnownExceptionAlreadyLogged) {
LOGGER.warning("Google Cloud API access is forbidden! Starting standalone. To use Hazelcast GCP discovery, "
+ "make sure that your service account has at minimum \"Read Only\" Access Scope to Compute Engine API.");
isKnownExceptionAlreadyLogged = true;
}
} else if (e.getHttpErrorCode() == HTTP_NOT_FOUND) {
if (!isKnownExceptionAlreadyLogged) {
LOGGER.warning("Google Cloud API Not Found! Starting standalone. Please check that you have a service account "
+ "assigned to your VM instance or `private-key-path` property defined.");
isKnownExceptionAlreadyLogged = true;
}
} else {
throw e;
}
LOGGER.finest(e);
}
}