Application Layer
Setup
ALFI is currently in an open beta.
Step by step
In a hurry? Skip to Complete examples.
- Construct an ApplicationCoordinates instance.
- Construct a TrafficCoordinates instance.
- Optionally (if using a custom TrafficCoordinates instance) construct a GremlinService singleton.
- Optionally (if using a custom TrafficCoordinates instance) inject the fault using
com.gremlin.GremlinService#applyImpact(trafficCoordinates)
. Add this line of code anywhere in your application, you wish the fault to be injected. - Click here to create a new Attack.
- Select an
Application Query
. - Set the necessary fields for the selected
Application Query
.- These are defined when setting up the ApplicationCoordinates.
- Select a
Traffic Query
.- These are defined when setting up the TrafficCoordinates.
- Choose a Gremlin attack - Set the amount of latency in ms to apply and optionally throw a
RuntimeException
within your application. - Run the attack - Set the duration in seconds for how long the attack will last.
- Test your application to observe the impact of the attack.
Complete examples
ALFI AWS
- This example has been developed for AWS Lambda but could be used in any application one deploys to AWS. Include alfi-aws jar in your calasspath to use this library.
- Source code: https://github.com/gremlin/alfi-lambda
java
1package com.alfilambda;23import com.amazonaws.services.lambda.runtime.Context;4import com.amazonaws.services.lambda.runtime.LambdaLogger;5import com.amazonaws.services.lambda.runtime.RequestHandler;6import com.gremlin.*;7import com.gremlin.aws.AwsApplicationCoordinatesResolver;89import java.time.Duration;10import java.time.Instant;11import java.util.Map;1213public class AlfiDemoHandler implements RequestHandler<Map<String,String>, String> {1415 private final GremlinService gremlinService;1617 public AlfiDemoHandler() {18 final GremlinServiceFactory factory = new GremlinServiceFactory(new GremlinCoordinatesProvider() {19 @Override20 public ApplicationCoordinates initializeApplicationCoordinates() {21 ApplicationCoordinates coords = AwsApplicationCoordinatesResolver.inferFromEnvironment()22 .orElseThrow(IllegalStateException::new);23 return coords;24 }25 });26 gremlinService = factory.getGremlinService();27 }2829 @Override30 public String handleRequest(Map<String, String> input, Context context) {31 Instant start = Instant.now();32 TrafficCoordinates trafficCoordinates = new TrafficCoordinates.Builder()33 .withType(this.getClass().getSimpleName())34 .withField("method", "handleRequest")35 .build();36 gremlinService.applyImpact(trafficCoordinates);37 LambdaLogger logger = context.getLogger();38 Instant finish = Instant.now();39 long timeElapsed = Duration.between(start, finish).toMillis(); //in millis40 logger.log(String.format("Lambda took %s millis", timeElapsed));41 return new String("200 OK");42 }43}
- Click here to create a new Attack
- See Attack and Lambda setup here: https://github.com/gremlin/alfi-lambda/blob/master/README.md
ALFI DynamoDB
- This example uses Spring for Dependency Injection and the alfi-aws-dynamodb-client jar.
- Source code: https://github.com/gremlin/alfi-dynamodb
java
1package com.example.alfidynamodb.config;23import com.amazonaws.ClientConfiguration;4import com.amazonaws.handlers.RequestHandler2;5import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;6import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;7import com.gremlin.*;8import com.gremlin.aws.GremlinDynamoRequestInterceptor;9import org.springframework.beans.factory.annotation.Value;10import org.springframework.context.annotation.Bean;11import org.springframework.context.annotation.Configuration;12@Configuration13public class AlfiConfig {1415 private static final String APPLICATION_QUERY_NAME = "ALFIDemoApplication";16 private static final int CLIENT_EXECUTION_TIMEOUT = 1500;17 private static final int CLIENT_REQUEST_TIMEOUT = 500;1819 @Value("${aws.region}")20 private String region;212223 public GremlinCoordinatesProvider gremlinCoordinatesProvider() {24 return new GremlinCoordinatesProvider() {25 @Override26 public ApplicationCoordinates initializeApplicationCoordinates() {27 return new ApplicationCoordinates.Builder()28 .withType(APPLICATION_QUERY_NAME)29 .build();30 }31 };32 }3334 public GremlinServiceFactory gremlinServiceFactory() {35 return new GremlinServiceFactory(gremlinCoordinatesProvider());36 }3738 public GremlinService gremlinService() {39 return gremlinServiceFactory().getGremlinService();40 }4142 @Bean43 public AmazonDynamoDB amazonDynamoDB() {44 final RequestHandler2 gremlinDynamoInterceptor = new GremlinDynamoRequestInterceptor(gremlinService(), CLIENT_EXECUTION_TIMEOUT, CLIENT_REQUEST_TIMEOUT);45 return AmazonDynamoDBClientBuilder.standard()46 .withRegion(region)47 .withClientConfiguration(new ClientConfiguration()48 .withClientExecutionTimeout(CLIENT_EXECUTION_TIMEOUT)49 .withConnectionTimeout(CLIENT_REQUEST_TIMEOUT)50 .withMaxErrorRetry(2)51 )52 .withRequestHandlers(gremlinDynamoInterceptor).build();53 }5455}
java
1package com.example.alfidynamodb.persistence;23import com.amazonaws.AmazonServiceException;4import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;5import com.amazonaws.services.dynamodbv2.model.AttributeValue;6import com.amazonaws.services.dynamodbv2.model.GetItemRequest;7import com.amazonaws.services.kms.model.NotFoundException;8import org.slf4j.Logger;9import org.slf4j.LoggerFactory;10import org.springframework.beans.factory.annotation.Autowired;11import org.springframework.beans.factory.annotation.Value;12import org.springframework.stereotype.Component;1314import java.util.HashMap;15import java.util.Map;16@Component17public class GetItemRequester {1819 private final Logger LOG = LoggerFactory.getLogger(getClass().getName());2021 @Value("${dynamo.db.table}")22 private String table;2324 private final AmazonDynamoDB amazonDynamoDB;2526 public GetItemRequester(@Autowired AmazonDynamoDB amazonDynamoDB) {27 this.amazonDynamoDB = amazonDynamoDB;28 }2930 public Map<String, AttributeValue> getItem(String id) {31 long startTime = System.currentTimeMillis();32 try {3334 LOG.info(String.format("Querying DynamoDB for item with ID %s...", id));35 Map<String, AttributeValue> returnedItem = amazonDynamoDB.getItem(createRequestWithId(id)).getItem();36 if (returnedItem != null) {37 return returnedItem;38 } else {39 throw new NotFoundException(String.format("Item with id %s not found!", id));40 }41 } catch (AmazonServiceException e) {42 LOG.error(e.getMessage());43 throw e;44 } finally {45 long endTime = System.currentTimeMillis();46 long duration = (endTime - startTime);47 LOG.info(String.format("Call to DynamoDB took %s milliseconds.", duration));48 }49 }5051 private GetItemRequest createRequestWithId(String id) {52 HashMap<String, AttributeValue> keyToGet = new HashMap<>();53 keyToGet.put("id", new AttributeValue(id));54 return new GetItemRequest().withKey(keyToGet).withTableName(table);55 }56}
- Click here to create a new Attack
- Fill out the Application Query and Traffic Query fields to match this example:
ALFI HTTP Servlet Filter
- This example uses Spring for Dependency Injection and the alfi-http-servlet-filter jar.
- Source code: https://github.com/gremlin/books-demo
java
1package com.example.rec;23import com.gremlin.ApplicationCoordinates;4import com.gremlin.GremlinCoordinatesProvider;5import com.gremlin.GremlinService;6import com.gremlin.GremlinServiceFactory;7import com.gremlin.http.servlet.GremlinServletFilter;8import org.springframework.boot.web.servlet.FilterRegistrationBean;9import org.springframework.context.annotation.Bean;10import org.springframework.context.annotation.Configuration;1112@Configuration13public class WebConfig {1415 @Bean16 public FilterRegistrationBean recommendationsFilterRegistrationBean() {17 FilterRegistrationBean registrationBean = new FilterRegistrationBean();18 registrationBean.setName("recs");1920 final GremlinCoordinatesProvider alfiCoordinatesProvider = new GremlinCoordinatesProvider() {21 @Override22 public ApplicationCoordinates initializeApplicationCoordinates() {23 return new ApplicationCoordinates.Builder()24 .withType("local")25 .withField("service", "recommendations")26 .build();27 }28 };29 final GremlinServiceFactory alfiFactory = new GremlinServiceFactory(alfiCoordinatesProvider);30 final GremlinService alfi = alfiFactory.getGremlinService();3132 GremlinServletFilter alfiFilter = new GremlinServletFilter(alfi);33 registrationBean.setFilter(alfiFilter);34 registrationBean.setOrder(1);35 return registrationBean;36 }3738}
There is no need to define a TrafficCoordinates
when using the GremlinServletFilter
. This library takes care of that for you.
This enables you to target any verb and any route hosted by your application! For example, you could narrow the blast radius of an attack to only GET
requests to https://somehost/recommendations
.
- Click here to create a new Attack
- Fill out the Application Query and Traffic Query fields to match this example:
ALFI Apache Http Client
- This example uses Spring for Dependency Injection and the alfi-apache-http-client jar.
- Source code: https://github.com/gremlin/alfi-apache-http-client
java
1package com.example.alfiapachehttpclient.config;23import org.springframework.context.annotation.Bean;4import org.springframework.context.annotation.Configuration;5import com.gremlin.*;67@Configuration8public class ALFIConfig {910 private static final String APPLICATION_QUERY_NAME = "ALFIApacheHttpClientDemo";1112 public GremlinCoordinatesProvider gremlinCoordinatesProvider() {13 return new GremlinCoordinatesProvider() {14 @Override15 public ApplicationCoordinates initializeApplicationCoordinates() {16 return new ApplicationCoordinates.Builder()17 .withType(APPLICATION_QUERY_NAME)18 .build();19 }20 };21 }2223 public GremlinServiceFactory gremlinServiceFactory() {24 return new GremlinServiceFactory(gremlinCoordinatesProvider());25 }2627 @Bean28 public GremlinService gremlinService() {29 return gremlinServiceFactory().getGremlinService();30 }3132}
java
1package com.example.alfiapachehttpclient.config;23import com.gremlin.GremlinService;4import com.gremlin.http.client.GremlinApacheHttpRequestInterceptor;5import org.apache.http.client.config.RequestConfig;6import org.apache.http.impl.client.CloseableHttpClient;7import org.apache.http.impl.client.HttpClientBuilder;8import org.springframework.beans.factory.annotation.Autowired;9import org.springframework.context.annotation.Bean;10import org.springframework.context.annotation.Configuration;1112@Configuration13public class ApacheClientConfig {1415 private final GremlinService gremlinService;16 private static final int CONNECTION_TIMEOUT = 1000;17 private static final int SOCKET_TIMEOUT = 3000;1819 @Autowired20 public ApacheClientConfig(GremlinService gremlinService) {21 this.gremlinService = gremlinService;22 }2324 @Bean25 public CloseableHttpClient closableHttpClient() {26 RequestConfig requestConfig = RequestConfig.custom()27 .setConnectTimeout(CONNECTION_TIMEOUT)28 .setSocketTimeout(SOCKET_TIMEOUT)29 .build();3031 final GremlinApacheHttpRequestInterceptor gremlinInterceptor =32 new GremlinApacheHttpRequestInterceptor(gremlinService, "alfi-client-demo");33 final HttpClientBuilder clientBuilder = HttpClientBuilder34 .create()35 .addInterceptorFirst(gremlinInterceptor)36 .setDefaultRequestConfig(requestConfig);3738 return clientBuilder.build();39 }404142}
java
1package com.example.alfiapachehttpclient.controller;23import org.apache.http.HttpEntity;4import org.apache.http.client.methods.CloseableHttpResponse;5import org.apache.http.client.methods.HttpGet;6import org.apache.http.impl.client.CloseableHttpClient;7import org.apache.http.util.EntityUtils;8import org.slf4j.Logger;9import org.slf4j.LoggerFactory;10import org.springframework.beans.factory.annotation.Autowired;11import org.springframework.http.HttpStatus;12import org.springframework.http.ResponseEntity;13import org.springframework.web.bind.annotation.GetMapping;14import org.springframework.web.bind.annotation.ResponseBody;15import org.springframework.web.bind.annotation.RestController;1617import java.io.IOException;1819@RestController20public class MainController {2122 private final Logger LOG = LoggerFactory.getLogger(getClass().getName());2324 private final CloseableHttpClient closeableHttpClient;25 private CloseableHttpResponse closeableHttpResponse;2627 @Autowired28 public MainController(CloseableHttpClient closeableHttpClient) {29 this.closeableHttpClient = closeableHttpClient;30 }3132 @GetMapping("/")33 public @ResponseBody34 ResponseEntity<String> hello() {35 final String URI = "https://www.gremlin.com/";36 HttpGet httpGet = new HttpGet(URI);37 String responseContent = null;38 long startTime = System.currentTimeMillis();39 try {40 LOG.info(String.format("Executing GET request to %s...", URI));41 closeableHttpResponse = closeableHttpClient.execute(httpGet);42 HttpEntity httpEntity = closeableHttpResponse.getEntity();43 responseContent = EntityUtils.toString(httpEntity);44 EntityUtils.consume(httpEntity);45 LOG.info(responseContent);46 } catch (IOException e) {47 e.printStackTrace();48 } finally {49 long endTime = System.currentTimeMillis();50 long duration = (endTime - startTime);51 LOG.info(String.format("GET Request took %d milliseconds", duration));52 try {53 closeableHttpResponse.close();54 } catch (IOException e) {55 e.printStackTrace();56 }57 }58 return new ResponseEntity<>(responseContent, HttpStatus.OK);59 }60}
- Click here to create a new Attack
- Fill out the Application Query and Traffic Query fields to match the following:
ALFI Core
- This example uses Spring for Dependency Injection and the alfi-core jar.
- Source code: https://github.com/gremlin/alfi-spring-boot
java
1package com.gremlin.todo.config;23import com.gremlin.*;4import com.gremlin.todo.ToDoApplication;5import org.springframework.context.annotation.Bean;6import org.springframework.stereotype.Service;78import javax.annotation.PostConstruct;910@Configuration11public class ALFIConfig {1213 public GremlinCoordinatesProvider gremlinCoordinatesProvider() {14 return new GremlinCoordinatesProvider() {15 @Override16 public ApplicationCoordinates initializeApplicationCoordinates() {17 return new ApplicationCoordinates.Builder()18 .withType("MyApplication")19 .withField("service", "to-do")20 .build();21 }22 };23 }2425 public GremlinServiceFactory gremlinServiceFactory() {26 return new GremlinServiceFactory(gremlinCoordinatesProvider());27 }2829 @Bean30 public GremlinService gremlinService() {31 return gremlinServiceFactory().getGremlinService();32 }3334}
java
1package com.gremlin.todo.controller;23import com.gremlin.todo.aspect.AdvancedAttack;4import com.gremlin.todo.aspect.Attack;5import com.gremlin.todo.dto.ToDoDto;6import com.gremlin.todo.model.ToDo;7import com.gremlin.todo.service.ToDoService;8import org.bson.types.ObjectId;9import org.springframework.beans.factory.annotation.Autowired;10import org.springframework.http.HttpEntity;11import org.springframework.http.HttpStatus;12import org.springframework.http.ResponseEntity;13import org.springframework.web.bind.annotation.*;1415import java.util.Collection;1617@RestController18public class MyController {19 private final GremlinService gremlinService;20 private TrafficCoordinates getAllToDosCoordinates;2122 @Autowired23 public MyController(GremlinService gremlinService) {24 this.gremlinService = gremlinService;25 }2627 @GetMapping("/all")28 public Collection<ToDo> getAllToDos() {29 gremlinService.applyImpact(this.getAllToDosCoordinates);30 return toDoService.findAll();31 }3233 @PostConstruct() {34 getAllToDosCoordinates = new TrafficCoordinates35 .Builder()36 .withType("MyController")37 .withField("method", "getAllToDos")38 .build();39 }4041}
- Click here to create a new Attack
- Fill out the Application Query and Traffic Query fields to match the following:
The custom value for the traffic type is hidden behind elipses in that screenshot. The value is getAllToDos
.