public Environment(String environment) { this.environment = com.yahoo.config.provision.Environment.from(environment); } }
@JsonValue public String value() { return environment.value(); }
/** * Throw if the node count is 1 for container and content clusters and we're in a production zone * * @return the argument node count * @throws IllegalArgumentException if only one node is requested and we can fail */ private int ensureRedundancy(int nodeCount, ClusterSpec.Type clusterType, boolean canFail) { if (canFail && nodeCount == 1 && Arrays.asList(ClusterSpec.Type.container, ClusterSpec.Type.content).contains(clusterType) && zone.environment().isProduction()) throw new IllegalArgumentException("Deployments to prod require at least 2 nodes per cluster for redundancy"); return nodeCount; }
public void setContainerCluster(ContainerCluster containerCluster) { this.containerCluster = containerCluster; // If we are in a config server cluster the correct zone is propagated through cloud config options, // not through config to deployment options (see StandaloneContainerApplication.scala), // so we need to propagate the zone options into the container from here Environment environment = options.environment().isPresent() ? Environment.from(options.environment().get()) : Environment.defaultEnvironment(); RegionName region = options.region().isPresent() ? RegionName.from(options.region().get()) : RegionName.defaultName(); SystemName system = options.system().isPresent() ? SystemName.from(options.system().get()) : SystemName.defaultSystem(); containerCluster.setZone(new Zone(system, environment, region)); }
/** Returns whether this is an automated test job */ public boolean isTest() { return environment() != null && environment().isTest(); }
/** Do not use */ public static Zone defaultZone() { return new Zone(SystemName.defaultSystem(), Environment.defaultEnvironment(), RegionName.defaultName()); }
DefaultTimes(Zone zone) { failGrace = Duration.ofMinutes(60); periodicRedeployInterval = Duration.ofMinutes(30); redeployMaintainerInterval = Duration.ofMinutes(1); operatorChangeRedeployInterval = Duration.ofMinutes(1); failedExpirerInterval = Duration.ofMinutes(10); provisionedExpiry = Duration.ofHours(4); rebootInterval = Duration.ofDays(30); nodeRetirerInterval = Duration.ofMinutes(30); metricsInterval = Duration.ofMinutes(1); infrastructureProvisionInterval = Duration.ofMinutes(3); throttlePolicy = NodeFailer.ThrottlePolicy.hosted; loadBalancerExpiry = Duration.ofHours(1); reservationExpiry = Duration.ofMinutes(20); // Need to be long enough for deployment to be finished for all config model versions hostProvisionerInterval = Duration.ofMinutes(5); hostDeprovisionerInterval = Duration.ofMinutes(5); if (zone.environment().equals(Environment.prod) && zone.system() != SystemName.cd) { inactiveExpiry = Duration.ofHours(4); // enough time for the application owner to discover and redeploy retiredInterval = Duration.ofMinutes(29); dirtyExpiry = Duration.ofHours(2); // enough time to clean the node retiredExpiry = Duration.ofDays(4); // give up migrating data after 4 days } else { inactiveExpiry = Duration.ofSeconds(2); // support interactive wipe start over retiredInterval = Duration.ofMinutes(1); dirtyExpiry = Duration.ofMinutes(30); retiredExpiry = Duration.ofMinutes(20); } }
int nodeCount = application.instance().isTester() ? 1 : capacityPolicies.decideSize(requestedCapacity, cluster.type()); if (zone.environment().isManuallyDeployed() && nodeCount < requestedCapacity.nodeCount()) logger.log(Level.INFO, "Requested " + requestedCapacity.nodeCount() + " nodes for " + cluster + ", downscaling to " + nodeCount + " nodes in " + zone.environment());
/** Returns whether to reboot node as part of transition to given state. This is done to get rid of any lingering * unwanted state (e.g. processes) on non-host nodes. */ private boolean rebootOnTransitionTo(Node.State state, Node node) { if (node.type().isDockerHost()) return false; // Reboot of host nodes is handled by NodeRebooter if (zone.environment().isTest()) return false; // We want to reuse nodes quickly in test environments return node.state() != Node.State.dirty && state == Node.State.dirty; }
@Inject public NodeRepositoryProvisioner(NodeRepository nodeRepository, NodeFlavors flavors, Zone zone, ProvisionServiceProvider provisionServiceProvider) { this.nodeRepository = nodeRepository; this.capacityPolicies = new CapacityPolicies(zone, flavors); this.zone = zone; this.preparer = new Preparer(nodeRepository, zone.environment().equals(Environment.prod) ? SPARE_CAPACITY_PROD : SPARE_CAPACITY_NONPROD); this.activator = new Activator(nodeRepository); this.loadBalancerProvisioner = provisionServiceProvider.getLoadBalancerService().map(lbService -> new LoadBalancerProvisioner(nodeRepository, lbService)); }
/** This may be invoked by a continuous build */ public static void main(String[] args) { if (args.length != 2 && args.length != 3) { System.err.println("Usage: DeploymentSpec [file] [environment] [region]?" + "Returns 0 if the specified zone matches the deployment spec, 1 otherwise"); System.exit(1); } try (BufferedReader reader = new BufferedReader(new FileReader(args[0]))) { DeploymentSpec spec = DeploymentSpec.fromXml(reader); Environment environment = Environment.from(args[1]); Optional<RegionName> region = args.length == 3 ? Optional.of(RegionName.from(args[2])) : Optional.empty(); if (spec.includes(environment, region)) System.exit(0); else System.exit(1); } catch (Exception e) { System.err.println("Exception checking deployment spec: " + toMessageString(e)); System.exit(1); } }
private String getConfigserverIdentityName() { return String.format("%s.provider_%s_%s", zone.system() == SystemName.main ? "vespa.vespa" : "vespa.vespa.cd", zone.environment().value(), zone.region().value()); } }
private boolean checkForClashingParentHost() { return nodeRepository.zone().system() == SystemName.main && nodeRepository.zone().environment().isProduction(); }
public static ZoneId from(String environment, String region) { return from(Environment.from(environment), RegionName.from(region)); }
private String getHostFromVespaCertificate(List<SubjectAlternativeName> sans) { // TODO Remove this branch once all BM nodes are gone if (sans.stream().anyMatch(san -> san.getValue().endsWith("ostk.yahoo.cloud"))) { return getHostFromCalypsoCertificate(sans); } VespaUniqueInstanceId instanceId = VespaUniqueInstanceId.fromDottedString(getUniqueInstanceId(sans)); if (!zone.environment().value().equals(instanceId.environment())) throw new NodeIdentifierException("Invalid environment: " + instanceId.environment()); if (!zone.region().value().equals(instanceId.region())) throw new NodeIdentifierException("Invalid region(): " + instanceId.region()); List<Node> applicationNodes = nodeRepository.getNodes(ApplicationId.from(instanceId.tenant(), instanceId.application(), instanceId.instance())); return applicationNodes.stream() .filter( node -> node.allocation() .map(allocation -> allocation.membership().index() == instanceId.clusterIndex() && allocation.membership().cluster().id().value().equals(instanceId.clusterId())) .orElse(false)) .map(Node::hostname) .findFirst() .orElseThrow(() -> new NodeIdentifierException("Could not find any node with instance id: " + instanceId.asDottedString())); }
@Override public void validate(VespaModel model, DeployState deployState) { if (! deployState.isHosted()) return; if (! deployState.zone().environment().isProduction()) return; if (model.getAdmin().getApplicationType() != ApplicationType.DEFAULT) return; List<String> offendingClusters = new ArrayList<>(); for (ContainerCluster cluster : model.getContainerClusters().values()) { if (cluster.getHttp() == null || ! cluster.getHttp().getAccessControl().isPresent() || ! cluster.getHttp().getAccessControl().get().writeEnabled) if (hasHandlerThatNeedsProtection(cluster) || ! cluster.getAllServlets().isEmpty()) offendingClusters.add(cluster.getName()); } if (! offendingClusters.isEmpty()) throw new IllegalArgumentException( "Access-control must be enabled for write operations to container clusters in production zones: " + mkString(offendingClusters, "[", ", ", "].")); }
public static ZoneId from(String environment, String region, String cloud) { return new ZoneId(Environment.from(environment), RegionName.from(region), CloudName.from(cloud)); }
private void addIdentityProvider(ContainerCluster cluster, List<ConfigServerSpec> configServerSpecs, HostName loadBalancerName, URI ztsUrl, String athenzDnsSuffix, Zone zone, DeploymentSpec spec) { spec.athenzDomain().ifPresent(domain -> { AthenzService service = spec.athenzService(zone.environment(), zone.region()) .orElseThrow(() -> new RuntimeException("Missing Athenz service configuration")); String zoneDnsSuffix = zone.environment().value() + "-" + zone.region().value() + "." + athenzDnsSuffix; IdentityProvider identityProvider = new IdentityProvider(domain, service, getLoadBalancerName(loadBalancerName, configServerSpecs), ztsUrl, zoneDnsSuffix, zone); cluster.addComponent(identityProvider); cluster.getContainers().forEach(container -> { container.setProp("identity.domain", domain.value()); container.setProp("identity.service", service.value()); }); }); }
@Inject public Zone(ConfigserverConfig configserverConfig, NodeFlavors nodeFlavors) { this(SystemName.from(configserverConfig.system()), Environment.from(configserverConfig.environment()), RegionName.from(configserverConfig.region()), new FlavorDefaults(configserverConfig), nodeFlavors); }
/** * Returns a config server config containing the right zone settings (and defaults for the rest). * This is useful to allow applications to find out in which zone they are runnung by having the Zone * object (which is constructed from this config) injected. */ @Override public void getConfig(ConfigserverConfig.Builder builder) { builder.system(zone.system().name()); builder.environment(zone.environment().value()); builder.region(zone.region().value()); }