private static <T extends Object> T getConfiguration(final ConfigurationSource configSource, final Class<T> type) throws ScalerException { try { return configSource.getConfiguration(type); } catch (final ConfigurationException ex) { throw new ScalerException("Unable to get configuration.", ex); } } }
private void patchInstances(final String appId, final int instances, final boolean force) throws ScalerException { final JsonObject details = new JsonObject(); details.addProperty("id", appId); details.addProperty("instances", instances); final JsonArray appArray = new JsonArray(); appArray.add(details); try(final CloseableHttpClient client = HttpClientBuilder.create().build()){ final URIBuilder uriBuilder = new URIBuilder(marathonUri).setPath("/v2/apps"); uriBuilder.setParameters(Arrays.asList(new BasicNameValuePair("force", Boolean.toString(force)))); final HttpPatch patch = new HttpPatch(uriBuilder.build()); patch.setEntity(new StringEntity(appArray.toString(), ContentType.APPLICATION_JSON)); final HttpResponse response = client.execute(patch); if(!force && response.getStatusLine().getStatusCode()==409){ patchInstances(appId, instances, true); return; } if(response.getStatusLine().getStatusCode()!=200){ throw new ScalerException(response.getStatusLine().getReasonPhrase()); } } catch (URISyntaxException | IOException ex) { throw new ScalerException(String.format("Exception patching %s to %s instances.", appId, instances), ex); } } }
private Collection<App> getGroupApps() throws ScalerException { try { Collection<App> apps = new ArrayList<>(); String[] groupPaths; if(groupPath.contains(",")){ LOG.info("Multiple Marathon groups detected: {} splitting on ','.", groupPath); groupPaths = groupPath.split(","); } else{ groupPaths = new String[]{groupPath}; } for(String groupPath:groupPaths){ apps.addAll(getAllGroupApps(marathon.getGroup(groupPath))); } return apps; } catch (MarathonException e) { throw new ScalerException("Failed to get group apps", e); } }
@Override public AlertDispatcher getAlertDispatcher(final ConfigurationSource configs) throws ScalerException { try { return new EmailDispatcher(configs.getConfiguration(EmailDispatcherConfiguration.class)); } catch (final ConfigurationException ex) { throw new ScalerException("Unable to create email dispatcher, cannot find email dispatcher configuration.", ex); } } }
throw new ScalerException("Unable to send email alert.", ex);
@Override public ServiceScaler getServiceScaler(final ConfigurationSource configurationSource) throws ScalerException { try { MarathonAutoscaleConfiguration config = configurationSource.getConfiguration(MarathonAutoscaleConfiguration.class); URL url = new URL(config.getEndpoint()); Marathon marathon = MarathonClient.getInstance(url.toString()); return new MarathonServiceScaler(marathon, config.getMaximumInstances(), url, new AppInstancePatcher(url.toURI())); } catch (ConfigurationException | MalformedURLException | URISyntaxException e) { throw new ScalerException("Failed to create service scaler", e); } } }
@Override public ServiceSource getServiceSource(final ConfigurationSource configurationSource, final ServicePath servicePath) throws ScalerException { try { final MarathonAutoscaleConfiguration config = configurationSource.getConfiguration(MarathonAutoscaleConfiguration.class); final String groupId = getGroupId(config, servicePath); Marathon marathon = MarathonClient.getInstance(config.getEndpoint()); return new MarathonServiceSource(marathon, groupId, new URL(config.getEndpoint())); } catch (ConfigurationException | MalformedURLException e) { throw new ScalerException("Failed to create service source", e); } }
@Override public void scaleDown(final String serviceReference, final int amount) throws ScalerException { try { GetAppResponse appGet = marathon.getApp(serviceReference); App app = appGet.getApp(); int current = app.getTasksRunning() + app.getTasksStaged(); if ( current > 0 ) { LOG.info("Scaling service {} down by {} instances", serviceReference, amount); appInstancePatcher.patchInstances(app.getId(), Math.max(0, current - amount)); } } catch (MarathonException e) { throw new ScalerException("Failed to scale down service " + serviceReference, e); } }
/** * Each service to monitor, described by a ScalingConfiguration, should have a 'workload metric' which is a named * reference to a type of WorkloadAnalyser that will be used to monitor and scale the service. Here, try and acquire * the WorkloadAnalyser the ScalingConfiguration has requested from the available WorkloadAnalyserFactory objects * acquired from the classpath at boot time. * @param s the service to get a WorkloadAnalyser for * @return the WorkloadAnalyser for this service * @throws ScalerException if the requested WorkloadAnalyser is not available */ private WorkloadAnalyser getAnalyser(final ScalingConfiguration s) throws ScalerException { if ( analyserFactories.containsKey(s.getWorkloadMetric()) ) { return analyserFactories.get(s.getWorkloadMetric()).getAnalyser(s.getScalingTarget(), s.getScalingProfile()); } else { throw new ScalerException("Invalid workload metric " + s.getWorkloadMetric()); } }
@Override public void scaleUp(final String serviceReference, final int amount) throws ScalerException { try { GetAppResponse appGet = marathon.getApp(serviceReference); App app = appGet.getApp(); int current = app.getTasksRunning() + app.getTasksStaged(); int target = Math.min(maximumInstances, current + amount); if ( target > current ) { LOG.info("Scaling service {} up by {} instances", serviceReference, amount); appInstancePatcher.patchInstances(app.getId(), target); } } catch (MarathonException e) { throw new ScalerException("Failed to scale up service " + serviceReference, e); } }
public AutoscaleCore(final ConfigurationSource configSource, final ServiceSource serviceSource, final ServiceScaler serviceScaler, final Collection<WorkloadAnalyserFactoryProvider> workloadProviders, final ElectionFactory electionFactory, final ScheduledExecutorService scheduler, final ServicePath servicePath, final Collection<AlertDispatcherFactory> alertDispatcherFactories) throws ScalerException { if (workloadProviders.isEmpty()) { throw new ScalerException("No instances of WorkloadAnalyserFactory found"); } for (WorkloadAnalyserFactoryProvider provider : workloadProviders) { LOG.debug("Registering workload analyser: {}", provider.getWorkloadAnalyserName()); analyserFactoryMap.put(provider.getWorkloadAnalyserName(), provider.getWorkloadAnalyserFactory(configSource)); } for (final AlertDispatcherFactory factory : alertDispatcherFactories) { LOG.debug("Registering workload analyser: {}", factory.getAlertDispatcherName()); alertDispatcherMap.put(factory.getAlertDispatcherName(), factory.getAlertDispatcher(configSource)); } this.scaler = new ScalerDecorator(serviceScaler, false); this.scheduler = scheduler; this.source = serviceSource; ServiceValidator validator = new ServiceValidator(Collections.unmodifiableCollection(analyserFactoryMap.keySet())); this.autoscaleScheduler = new AutoscaleScheduler(Collections.unmodifiableMap(analyserFactoryMap), scaler, scheduler, validator, alertDispatcherMap, getConfiguration(configSource, ResourceMonitoringConfiguration.class), getConfiguration(configSource, AlertDispatchConfiguration.class)); this.election = electionFactory.getElection(servicePath.getGroup() + "-" + AUTOSCALE_SERVICE_NAME, new AutoscaleElectionCallback()); }
@Override public InstanceInfo getInstanceInfo(final String serviceReference) throws ScalerException { try { GetAppResponse appGet = marathon.getApp(serviceReference); final String appShutdownPriorityLabel = appGet.getApp().getLabels().get("autoscale.shutdownPriority"); final int appShutdownPriority = appShutdownPriorityLabel != null ? Integer.parseInt(appShutdownPriorityLabel) : -1; Collection<ServiceHost> hosts = appGet.getApp() .getTasks() .stream() .map(t -> new ServiceHost(t.getHost(), t.getPorts())) .collect(Collectors.toCollection(LinkedList::new)); return new InstanceInfo(appGet.getApp().getTasksRunning(), appGet.getApp().getTasksStaged(), hosts, appShutdownPriority); } catch (MarathonException e) { throw new ScalerException("Failed to get number of instances of " + serviceReference, e); } }