google-nomulus/java/google/registry/tools/SetNumInstancesCommand.java
mcilwain 6082addb86 Make some quality of life improvements to set_num_instances command
Allows correct service names (i.e. lowercased, as they appear in App Engine
configuration files and the GCP console), and adds single letter parameters for
common flags.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=229194479
2019-01-14 16:29:45 -05:00

192 lines
7.6 KiB
Java

// Copyright 2018 The Nomulus Authors. 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 google.registry.tools;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.ImmutableSetMultimap.flatteningToImmutableSetMultimap;
import static google.registry.util.CollectionUtils.nullToEmpty;
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.google.api.services.appengine.v1.Appengine;
import com.google.common.base.Enums;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.flogger.FluentLogger;
import google.registry.config.RegistryConfig.Config;
import google.registry.tools.AppEngineConnection.Service;
import google.registry.util.AppEngineServiceUtils;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import javax.inject.Inject;
/** A command to set the number of instances for an App Engine service. */
@Parameters(
separators = " =",
commandDescription =
"Set the number of instances for a given service and version. "
+ "Note that this command only works for manual scaling service.")
final class SetNumInstancesCommand implements CommandWithRemoteApi {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final ImmutableSet<Service> ALL_DEPLOYED_SERVICES =
ImmutableSet.copyOf(Service.values());
private static final ImmutableMap<String, Service> SERVICE_ID_TO_SERVICE =
ALL_DEPLOYED_SERVICES.stream()
.collect(toImmutableMap(service -> service.getServiceId(), service -> service));
// TODO(b/119629679): Use List<Service> after upgrading jcommander to latest version.
@Parameter(
names = {"-s", "--services"},
description =
"Comma-delimited list of App Engine services to set. "
+ "Allowed values: [DEFAULT, TOOLS, BACKEND, PUBAPI]")
private List<String> serviceNames = ImmutableList.of();
@Parameter(
names = {"-v", "--versions"},
description =
"Comma-delimited list of App Engine versions to set, e.g., canary. "
+ "Cannot be set if --non_live_versions is set.")
private List<String> versions = ImmutableList.of();
@Parameter(
names = {"-n", "--num_instances"},
description =
"The new number of instances for the given versions "
+ "or for all non-live versions if --non_live_versions is set.",
required = true)
private Long numInstances;
@Parameter(
names = "--non_live_versions",
description = "Whether to set number of instances for all non-live versions.",
arity = 1)
private Boolean nonLiveVersions = false;
@Inject AppEngineServiceUtils appEngineServiceUtils;
@Inject Appengine appengine;
@Inject
@Config("projectId")
String projectId;
@Override
public void run() {
ImmutableSet<Service> services =
serviceNames.stream()
.map(s -> s.toUpperCase(Locale.US))
.map(
name ->
checkArgumentPresent(
Enums.getIfPresent(Service.class, name).toJavaUtil(),
"Invalid service '%s'. Allowed values are %s",
name,
ALL_DEPLOYED_SERVICES))
.collect(toImmutableSet());
if (nonLiveVersions) {
checkArgument(versions.isEmpty(), "--versions cannot be set if --non_live_versions is set");
services = services.isEmpty() ? ALL_DEPLOYED_SERVICES : services;
ImmutableSetMultimap<Service, String> allLiveVersionsMap = getAllLiveVersionsMap(services);
ImmutableSetMultimap<Service, String> manualScalingVersionsMap =
getManualScalingVersionsMap(services);
// Set number of instances for versions which are manual scaling and non-live
manualScalingVersionsMap.forEach(
(service, versionId) -> {
if (!allLiveVersionsMap.containsEntry(service, versionId)) {
setNumInstances(service, versionId, numInstances);
}
});
} else {
checkArgument(!services.isEmpty(), "Service must be specified");
checkArgument(!versions.isEmpty(), "Version must be specified");
checkArgument(numInstances > 0, "Number of instances must be greater than zero");
ImmutableSetMultimap<Service, String> manualScalingVersionsMap =
getManualScalingVersionsMap(services);
for (Service service : services) {
for (String versionId : versions) {
checkArgument(
manualScalingVersionsMap.containsEntry(service, versionId),
"Version %s of service %s is not managed through manual scaling",
versionId,
service);
setNumInstances(service, versionId, numInstances);
}
}
}
}
private void setNumInstances(Service service, String version, long numInstances) {
appEngineServiceUtils.setNumInstances(service.getServiceId(), version, numInstances);
logger.atInfo().log(
"Successfully set version %s of service %s to %d instances.",
version, service, numInstances);
}
private ImmutableSetMultimap<Service, String> getAllLiveVersionsMap(Set<Service> services) {
try {
return nullToEmpty(appengine.apps().services().list(projectId).execute().getServices())
.stream()
.filter(
service ->
services.contains(SERVICE_ID_TO_SERVICE.getOrDefault(service.getId(), null)))
.collect(
flatteningToImmutableSetMultimap(
service -> SERVICE_ID_TO_SERVICE.get(service.getId()),
service -> nullToEmpty(service.getSplit().getAllocations()).keySet().stream()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private ImmutableSetMultimap<Service, String> getManualScalingVersionsMap(Set<Service> services) {
return services.stream()
.collect(
flatteningToImmutableSetMultimap(
service -> service,
service -> {
try {
return nullToEmpty(
appengine
.apps()
.services()
.versions()
.list(projectId, service.getServiceId())
.execute()
.getVersions())
.stream()
.filter(version -> version.getManualScaling() != null)
.map(version -> version.getId());
} catch (IOException e) {
throw new RuntimeException(e);
}
}));
}
}