Search in sources :

Example 1 with LimitExceededException

use of com.thoughtworks.gocd.elasticagent.ecs.exceptions.LimitExceededException in project gocd-ecs-elastic-agent by gocd.

the class SpotInstanceService method create.

public Optional<ContainerInstance> create(PluginSettings pluginSettings, ElasticAgentProfileProperties elasticAgentProfileProperties, ConsoleLogAppender consoleLogAppender) throws LimitExceededException {
    Platform platform = elasticAgentProfileProperties.platform();
    synchronized (platform) {
        consoleLogAppender.accept("The elastic agent profile is configured to run on a spot instance. Initiating steps to request for a spot instance.");
        EC2Config ec2Config = ec2ConfigBuilder.withSettings(pluginSettings).withProfile(elasticAgentProfileProperties).build();
        List<Instance> allRegisteredSpotInstancesForPlatform = spotInstanceHelper.allRegisteredSpotInstancesForPlatform(pluginSettings, platform);
        synchronized (untaggedSpotRequests) {
            refreshUnTaggedSpotRequests(pluginSettings);
            List<SpotInstanceRequest> spotRequestsWithoutRegisteredInstances = spotRequestsWithoutRegisteredInstances(pluginSettings, platform, allRegisteredSpotInstancesForPlatform);
            Collection<SpotInstanceRequest> allSpotRequestsWithoutRunningInstance = union(spotRequestsWithoutRegisteredInstances, untaggedSpotRequests);
            LOG.info("[create-agent] For Platform: '{}',All-Registered-Spot-Instances count: '{}', Spot-Requests-Without-Registered-Instances count: '{}'," + "UnTagged-Spot-Requests count: '{}'", platform.name(), allRegisteredSpotInstancesForPlatform.size(), spotRequestsWithoutRegisteredInstances.size(), untaggedSpotRequests.size());
            boolean openSpotRequestAvailable = isThereAOpenSpotRequestMatchingProfile(allSpotRequestsWithoutRunningInstance, ec2Config);
            if (openSpotRequestAvailable) {
                consoleLogAppender.accept("There is an open spot request matching the profile, not requesting for a new spot instance.");
                LOG.debug("[create-agent] There is an open spot request matching the profile, not requesting for a new spot instance.");
                return Optional.empty();
            }
            boolean clusterMaxedOut = (allRegisteredSpotInstancesForPlatform.size() + allSpotRequestsWithoutRunningInstance.size()) >= pluginSettings.getMaxLinuxSpotInstanceAllowed();
            if (clusterMaxedOut) {
                consoleLogAppender.accept(format("The number of %s EC2 (spot instances running + open spot requests) is currently at the maximum permissible limit(%s). Not requesting for a spot instance.", platform, pluginSettings.getMaxLinuxSpotInstanceAllowed()));
                throw new LimitExceededException(format("The number of %s EC2 Spot Instances running is currently at the maximum permissible limit(%s). Not requesting for any more EC2 Spot Instances.", platform.name(), pluginSettings.getMaxLinuxSpotInstanceAllowed()));
            }
        }
        LOG.debug("[create-agent] Initiating a new spot instance request.");
        RequestSpotInstancesResult requestSpotInstancesResult = spotInstanceHelper.requestSpotInstanceRequest(pluginSettings, ec2Config, consoleLogAppender);
        SpotInstanceRequest spotInstanceRequest = requestSpotInstancesResult.getSpotInstanceRequests().get(0);
        /*
               All valid spot SpotRequests are tagged after making a request for a spot instance.
               There are times when tagging a spot request fails with 404 since aws does not find a spot request for the
               given SpotInstanceRequestId. Ensuring that spot requests can be lookedup by id before tagging them.
            */
        spotInstanceHelper.waitTillSpotRequestCanBeLookedUpById(pluginSettings, spotInstanceRequest.getSpotInstanceRequestId());
        SpotInstanceStatus status = spotInstanceRequest.getStatus();
        LOG.debug("[create-agent] Created spot instance request with request Id: {}, state: {}, status-code:{}, status-message:{}", spotInstanceRequest.getSpotInstanceRequestId(), spotInstanceRequest.getState(), status.getCode(), status.getMessage());
        tagSpotRequest(pluginSettings, elasticAgentProfileProperties, spotInstanceRequest);
        /*
              A spot instance request is tagged post creation. AWS takes time to sync up the the tags on the spot request, hence
              querying aws for the spot requests with tag filters does not yield results. Hence the SpotInstance service maintains
              a list of new spot instance requests and removes from the list in future calls or as part of server ping.
            */
        if (isSpotRequestValid(spotInstanceRequest)) {
            untaggedSpotRequests.add(spotInstanceRequest);
        }
        return Optional.empty();
    }
}
Also used : Platform(com.thoughtworks.gocd.elasticagent.ecs.domain.Platform) ContainerInstance(com.amazonaws.services.ecs.model.ContainerInstance) LimitExceededException(com.thoughtworks.gocd.elasticagent.ecs.exceptions.LimitExceededException)

Example 2 with LimitExceededException

use of com.thoughtworks.gocd.elasticagent.ecs.exceptions.LimitExceededException in project gocd-ecs-elastic-agent by gocd.

the class TaskHelper method create.

public Optional<ECSTask> create(CreateAgentRequest createAgentRequest, PluginSettings pluginSettings, ConsoleLogAppender consoleLogAppender) throws ContainerInstanceFailedToRegisterException, LimitExceededException, ContainerFailedToRegisterException {
    final String taskName = "GoCD" + UUID.randomUUID().toString().replaceAll("-", "");
    final ElasticAgentProfileProperties elasticAgentProfileProperties = createAgentRequest.elasticProfile();
    ContainerDefinition containerDefinition = new ContainerDefinitionBuilder().withName(taskName).pluginSettings(pluginSettings).createAgentRequest(createAgentRequest).withServerId(getServerId()).build();
    StopPolicy stopPolicy = elasticAgentProfileProperties.platform() == LINUX ? pluginSettings.getLinuxStopPolicy() : pluginSettings.getWindowsStopPolicy();
    Optional<ContainerInstance> containerInstance = instanceSelectionStrategyFactory.strategyFor(stopPolicy).instanceForScheduling(pluginSettings, elasticAgentProfileProperties, containerDefinition);
    if (!containerInstance.isPresent()) {
        consoleLogAppender.accept("No running instance(s) found to build the ECS Task to perform current job.");
        LOG.info(format("[create-agent] No running instances found to build container with profile {0}", createAgentRequest.elasticProfile().toJson()));
        if (elasticAgentProfileProperties.runAsSpotInstance()) {
            spotInstanceService.create(pluginSettings, elasticAgentProfileProperties, consoleLogAppender);
            return Optional.empty();
        } else {
            containerInstance = containerInstanceHelper.startOrCreateOneInstance(pluginSettings, elasticAgentProfileProperties, consoleLogAppender);
        }
    } else {
        consoleLogAppender.accept("Found existing running container instance platform matching ECS Task instance configuration. Not starting a new EC2 instance...");
    }
    final RegisterTaskDefinitionRequest registerTaskDefinitionRequest = registerTaskDefinitionRequestBuilder.build(pluginSettings, elasticAgentProfileProperties, containerDefinition).withFamily(taskName);
    consoleLogAppender.accept("Registering ECS Task definition with cluster...");
    LOG.debug(format("[create-agent] Registering task definition: {0} ", registerTaskDefinitionRequest.toString()));
    RegisterTaskDefinitionResult taskDefinitionResult = pluginSettings.ecsClient().registerTaskDefinition(registerTaskDefinitionRequest);
    consoleLogAppender.accept("Done registering ECS Task definition with cluster.");
    LOG.debug("[create-agent] Done registering task definition");
    TaskDefinition taskDefinitionFromNewTask = taskDefinitionResult.getTaskDefinition();
    StartTaskRequest startTaskRequest = new StartTaskRequest().withTaskDefinition(taskDefinitionFromNewTask.getTaskDefinitionArn()).withContainerInstances(containerInstance.get().getContainerInstanceArn()).withCluster(pluginSettings.getClusterName());
    consoleLogAppender.accept("Starting ECS Task to perform current job...");
    LOG.debug(format("[create-agent] Starting task : {0} ", startTaskRequest.toString()));
    StartTaskResult startTaskResult = pluginSettings.ecsClient().startTask(startTaskRequest);
    LOG.debug("[create-agent] Done executing start task request.");
    if (isStarted(startTaskResult)) {
        String message = elasticAgentProfileProperties.runAsSpotInstance() ? "[WARNING] The ECS task is scheduled on a Spot Instance. A spot instance termination would re-schedule the job." : String.format("ECS Task %s scheduled on container instance %s.", taskName, containerInstance.get().getEc2InstanceId());
        consoleLogAppender.accept(message);
        LOG.info(format("[create-agent] Task {0} scheduled on container instance {1}", taskName, containerInstance.get().getEc2InstanceId()));
        return Optional.of(new ECSTask(startTaskResult.getTasks().get(0), taskDefinitionFromNewTask, elasticAgentProfileProperties, createAgentRequest.getJobIdentifier(), createAgentRequest.environment(), containerInstance.get().getEc2InstanceId()));
    } else {
        deregisterTaskDefinition(pluginSettings, taskDefinitionFromNewTask.getTaskDefinitionArn());
        String errors = startTaskResult.getFailures().stream().map(failure -> "    " + failure.getArn() + " failed with reason :" + failure.getReason()).collect(Collectors.joining("\n"));
        throw new ContainerFailedToRegisterException("Fail to start task " + taskName + ":\n" + errors);
    }
}
Also used : Optional.empty(java.util.Optional.empty) com.amazonaws.services.ecs.model(com.amazonaws.services.ecs.model) LimitExceededException(com.thoughtworks.gocd.elasticagent.ecs.exceptions.LimitExceededException) java.util(java.util) ContainerInstanceFailedToRegisterException(com.thoughtworks.gocd.elasticagent.ecs.exceptions.ContainerInstanceFailedToRegisterException) InstanceSelectionStrategyFactory(com.thoughtworks.gocd.elasticagent.ecs.aws.strategy.InstanceSelectionStrategyFactory) Collectors(java.util.stream.Collectors) MessageFormat(java.text.MessageFormat) Constants(com.thoughtworks.gocd.elasticagent.ecs.Constants) ECSElasticPlugin.getServerId(com.thoughtworks.gocd.elasticagent.ecs.ECSElasticPlugin.getServerId) MessageFormat.format(java.text.MessageFormat.format) com.thoughtworks.gocd.elasticagent.ecs.domain(com.thoughtworks.gocd.elasticagent.ecs.domain) ContainerFailedToRegisterException(com.thoughtworks.gocd.elasticagent.ecs.exceptions.ContainerFailedToRegisterException) CreateAgentRequest(com.thoughtworks.gocd.elasticagent.ecs.requests.CreateAgentRequest) ECSTask(com.thoughtworks.gocd.elasticagent.ecs.ECSTask) LOG(com.thoughtworks.gocd.elasticagent.ecs.ECSElasticPlugin.LOG) AmazonECS(com.amazonaws.services.ecs.AmazonECS) StringUtils.equalsIgnoreCase(org.apache.commons.lang3.StringUtils.equalsIgnoreCase) LINUX(com.thoughtworks.gocd.elasticagent.ecs.domain.Platform.LINUX) ECSTask(com.thoughtworks.gocd.elasticagent.ecs.ECSTask) ContainerFailedToRegisterException(com.thoughtworks.gocd.elasticagent.ecs.exceptions.ContainerFailedToRegisterException)

Example 3 with LimitExceededException

use of com.thoughtworks.gocd.elasticagent.ecs.exceptions.LimitExceededException in project gocd-ecs-elastic-agent by gocd.

the class ContainerInstanceHelper method createInstances.

public Optional<List<ContainerInstance>> createInstances(PluginSettings pluginSettings, ElasticAgentProfileProperties elasticAgentProfileProperties, int numberOfInstancesToCreate, ConsoleLogAppender consoleLogAppender) throws LimitExceededException {
    synchronized (elasticAgentProfileProperties.platform()) {
        if (numberOfInstancesToCreate == 0) {
            LOG.info("Not creating new instances as number of requested ec2 instances are 0.");
            return Optional.empty();
        }
        final EC2Config ec2Config = new EC2Config.Builder().withSettings(pluginSettings).withProfile(elasticAgentProfileProperties).build();
        final List<Instance> allInstances = allInstances(pluginSettings, elasticAgentProfileProperties.platform());
        final List<Instance> allOnDemandInstances = filterBy(allInstances, isOnDemandInstance());
        final List<Instance> instancesForPlatform = filterByPlatform(allOnDemandInstances, ec2Config.getPlatform());
        final List<Instance> stoppedInstances = filterByState(instancesForPlatform, STOPPED);
        LOG.info(format("Found total {0} on-demand instances for platform {1} and from that {2} instances are in stopped state.", instancesForPlatform.size(), ec2Config.getPlatform(), stoppedInstances.size()));
        if (stoppedInstances.size() == ec2Config.getMaxInstancesAllowed()) {
            terminateMostIdleStoppedInstance(pluginSettings, stoppedInstances);
        } else if (ec2Config.getMaxInstancesAllowed() <= instancesForPlatform.size()) {
            throw new LimitExceededException(ec2Config.getPlatform().name(), ec2Config.getMaxInstancesAllowed());
        }
        final Subnet selectedSubnet = subnetSelector.selectSubnetWithMinimumEC2Instances(pluginSettings, ec2Config.getSubnetIds(), allInstances);
        final RunInstancesRequest runInstancesRequest = new RunInstanceRequestBuilder().withEC2Config(ec2Config).withSubnet(selectedSubnet).instanceToCreate(numberOfInstancesToCreate).withServerId(getServerId()).build();
        consoleLogAppender.accept("Creating a new container instance to schedule ECS Task.");
        LOG.info(format("Creating container instance with configuration: {0}", runInstancesRequest.toString()));
        RunInstancesResult runInstancesResult = pluginSettings.ec2Client().runInstances(runInstancesRequest);
        List<String> newlyLaunchedInstances = runInstancesResult.getReservation().getInstances().stream().map(Instance::getInstanceId).collect(toList());
        return waitInstanceToStart(pluginSettings, ec2Config, newlyLaunchedInstances, consoleLogAppender);
    }
}
Also used : LimitExceededException(com.thoughtworks.gocd.elasticagent.ecs.exceptions.LimitExceededException)

Example 4 with LimitExceededException

use of com.thoughtworks.gocd.elasticagent.ecs.exceptions.LimitExceededException in project gocd-ecs-elastic-agent by gocd.

the class CreateAgentRequestExecutor method execute.

@Override
public GoPluginApiResponse execute() {
    ConsoleLogAppender consoleLogAppender = text -> {
        final String message = String.format("%s %s\n", LocalTime.now().toString(MESSAGE_PREFIX_FORMATTER), text);
        pluginRequest.appendToConsoleLog(request.getJobIdentifier(), message);
    };
    try {
        consoleLogAppender.accept(String.format("Received a request to create an agent for the job: [%s]", request.getJobIdentifier().getRepresentation()));
        LOG.info(format("[create-agent] Creating agent with profile {0}", request.elasticProfile().toJson()));
        agentInstances.create(request, request.clusterProfileProperties(), consoleLogAppender);
        LOG.info(format("[create-agent] Done creating agent for profile : {0}", request.elasticProfile().toJson()));
        eventStream.remove(EventFingerprint.forElasticProfile(request.elasticProfile()));
        eventStream.remove(EventFingerprint.forCreateEC2Instance());
    } catch (LimitExceededException e) {
        eventStream.update(Event.warningEvent(EventFingerprint.forCreateEC2Instance(), e.getMessage(), null));
        LOG.warn(e.getMessage(), e);
    } catch (Exception e) {
        eventStream.update(Event.errorEvent(EventFingerprint.forElasticProfile(request.elasticProfile()), format("Error creating agent for profile: {0}", request.elasticProfile().toJson()), e.getMessage()));
        LOG.error(format("[create-agent] Failed to create an agent for profile : {0}", request.elasticProfile().toJson()), e);
    }
    return new DefaultGoPluginApiResponse(200);
}
Also used : DefaultGoPluginApiResponse(com.thoughtworks.go.plugin.api.response.DefaultGoPluginApiResponse) GoPluginApiResponse(com.thoughtworks.go.plugin.api.response.GoPluginApiResponse) DateTimeFormat(org.joda.time.format.DateTimeFormat) EventStream(com.thoughtworks.gocd.elasticagent.ecs.events.EventStream) Event(com.thoughtworks.gocd.elasticagent.ecs.events.Event) EventFingerprint(com.thoughtworks.gocd.elasticagent.ecs.events.EventFingerprint) LimitExceededException(com.thoughtworks.gocd.elasticagent.ecs.exceptions.LimitExceededException) DateTimeFormatter(org.joda.time.format.DateTimeFormatter) ConsoleLogAppender(com.thoughtworks.gocd.elasticagent.ecs.domain.ConsoleLogAppender) AgentInstances(com.thoughtworks.gocd.elasticagent.ecs.AgentInstances) LocalTime(org.joda.time.LocalTime) PluginRequest(com.thoughtworks.gocd.elasticagent.ecs.PluginRequest) MessageFormat.format(java.text.MessageFormat.format) CreateAgentRequest(com.thoughtworks.gocd.elasticagent.ecs.requests.CreateAgentRequest) RequestExecutor(com.thoughtworks.gocd.elasticagent.ecs.RequestExecutor) ECSTask(com.thoughtworks.gocd.elasticagent.ecs.ECSTask) LOG(com.thoughtworks.gocd.elasticagent.ecs.ECSElasticPlugin.LOG) DefaultGoPluginApiResponse(com.thoughtworks.go.plugin.api.response.DefaultGoPluginApiResponse) LimitExceededException(com.thoughtworks.gocd.elasticagent.ecs.exceptions.LimitExceededException) ConsoleLogAppender(com.thoughtworks.gocd.elasticagent.ecs.domain.ConsoleLogAppender) LimitExceededException(com.thoughtworks.gocd.elasticagent.ecs.exceptions.LimitExceededException)

Aggregations

LimitExceededException (com.thoughtworks.gocd.elasticagent.ecs.exceptions.LimitExceededException)4 LOG (com.thoughtworks.gocd.elasticagent.ecs.ECSElasticPlugin.LOG)2 ECSTask (com.thoughtworks.gocd.elasticagent.ecs.ECSTask)2 CreateAgentRequest (com.thoughtworks.gocd.elasticagent.ecs.requests.CreateAgentRequest)2 MessageFormat.format (java.text.MessageFormat.format)2 AmazonECS (com.amazonaws.services.ecs.AmazonECS)1 com.amazonaws.services.ecs.model (com.amazonaws.services.ecs.model)1 ContainerInstance (com.amazonaws.services.ecs.model.ContainerInstance)1 DefaultGoPluginApiResponse (com.thoughtworks.go.plugin.api.response.DefaultGoPluginApiResponse)1 GoPluginApiResponse (com.thoughtworks.go.plugin.api.response.GoPluginApiResponse)1 AgentInstances (com.thoughtworks.gocd.elasticagent.ecs.AgentInstances)1 Constants (com.thoughtworks.gocd.elasticagent.ecs.Constants)1 ECSElasticPlugin.getServerId (com.thoughtworks.gocd.elasticagent.ecs.ECSElasticPlugin.getServerId)1 PluginRequest (com.thoughtworks.gocd.elasticagent.ecs.PluginRequest)1 RequestExecutor (com.thoughtworks.gocd.elasticagent.ecs.RequestExecutor)1 InstanceSelectionStrategyFactory (com.thoughtworks.gocd.elasticagent.ecs.aws.strategy.InstanceSelectionStrategyFactory)1 com.thoughtworks.gocd.elasticagent.ecs.domain (com.thoughtworks.gocd.elasticagent.ecs.domain)1 ConsoleLogAppender (com.thoughtworks.gocd.elasticagent.ecs.domain.ConsoleLogAppender)1 Platform (com.thoughtworks.gocd.elasticagent.ecs.domain.Platform)1 LINUX (com.thoughtworks.gocd.elasticagent.ecs.domain.Platform.LINUX)1