Skip to content

Simple way to generate mock servers written in Go

License

Notifications You must be signed in to change notification settings

0xlsa/killgrave

 
 

Repository files navigation

Golang Killgrave

Killgrave

Killgrave is a simulator for HTTP-based APIs, in simple words a Mock Server, very easy to use, made in Go.

CircleCI Version codecov Go Report Card Total alerts FriendsOfGo

Buy Me A Coffee

Table of Content

Overview

Killgrave is a tool providing a simple way to create a powerful simulator for HTTP-based APIs.

The Killgrave's philosophy is provides an easy way to configure your mock server, trying always that you don't need learn another tooling language, for that reason we use json and yaml to generate all necessary configurations.

Killgrave provides:

  • Easy way to create imposters files, using json
  • The possibility to validate your owns json schemas on requests.
  • Validations the requests headers.
  • Using regex to allow different parameters on headers and urls.
  • Custom body and dynamic body responses
  • Using all content-types bodies, (application/json, text/html, text/plain, image/jpeg, etc. )
  • Configure the headers on body responses.
  • Configure your CORS server options.
  • Simulate network issues and server high loads with imposter responses delay.
  • Using configurable proxy server to call to the original server.
  • Run the tool using flag or using a config file.
  • Run your mock server using a watcher to not reload on each change.

Concepts

Imposters

Maybe the most common concept inside of Killgrave tool is the imposters. Imposters are the files that we will use to configure how we want that our server respond.

You can identify an imposter file on Killgrave because they must have the extension .imp.json.

Yo can learn more about how to configure the imposters on the Imposter Configuration Section.

Installing

⚠️ Killgrave is a very robust tool and is using on some companies even on production environments, but we don't have a version 1 for today. For that Killgrave works using SemVer but in 0 version, which means that the 'minor' will be changed when some broken changes are introduced into the application, and the 'patch' will be changed when a new feature with new changes is added or for bug fixing. As soon as v1.0.0 be released, Killgrave will start to use SemVer as usual.

You can install Killgrave on different ways, but all of them are very simple:

Go Toolchain

One of them is of course using go get, Killgrave is a Go project so could be compiled using the go toolchain:

$ go get -u github.com/friendsofgo/killgrave/cmd/killgrave@{version}

version must be substituted by the version that you want to install, otherwise master would be installed.

Homebrew

If you are a mac user, you could install Killgrave using, Homebrew:

$ brew install friendsofgo/tap/killgrave

⚠️ If you are using the hombrew way, you only get the last Killgrave version, we hope fix this soon.

Docker

The application is also available through Docker, just run:

docker run -it --rm -p 3000:3000 -v $PWD/:/home -w /home friendsofgo/killgrave

Remember to use the -p flag to expose the container port where the application is listening (3000 by default).

NOTE: If you want to use killgrave in Docker at the same time as using your own dockerised HTTP-based API please be careful with networking issues.

Other

If you are using, windows or linux you could get the binary for your arch on the release section:

https://github.com/friendsofgo/killgrave/releases

Getting Started

The very easy way to start with Killgrave is just execute at it is:

$ killgrave

While you are welcome to provide your own configuration, Killgrave will follow the next default configuration:

  • imposters path: imposters
  • host: localhost
  • port: 3000
  • CORS: []
  • proxy: none
  • watcher: false

Using Killgrave by command line

As we see below Killgrave have a default configuration to start using out of the box, but you could change all of this using his command tool, except for the CORS that you only will can configure using the config file.

$ killgrave -h

  -config string
        path with configuration file
  -host string
        if you run your server on a different host (default "localhost")
  -imposters string
        directory where your imposters are saved (default "imposters")
  -port int
        port to run the server (default 3000)
  -proxy-mode string
        proxy mode you can choose between (all, missing or none) (default "none")
  -proxy-url string
        proxy url, you need to choose a proxy-mode
  -version
        show the _version of the application
  -watcher
        file watcher, reload the server with each file change

Using Killgrave by config file

If we want a more permanent configuration, we could use the option -config to define where is our config file.

The config file must be a yml file, his structure should be like this:

#config.yml

imposters_path: "imposters"
port: 3000
host: "localhost"
proxy:
  url: https://example.com
  mode: missing
watcher: true
cors:
  methods: ["GET"]
  headers: ["Content-Type"]
  exposed_headers: ["Cache-Control"]
  origins: ["*"]
  allow_credentials: true

As you can see, you could configure all the options on a very easy way. Keep in mind that the routes are based on the config file are.

example:

mymock/
    imposters/
        config.yml
        swapi_people.imp.json
        swapi_planets.imp.json
    Dockerfile
    MakeFile

Then on your config file, you will need configure imposters option like . because the imposters file are located in the same folder.

Historically, the options imposters_path, port, host were mandatory when using a configuration file.

However, since the last version, they are no longer needed, so you can simply override those options if you want to. Furthermore, the imposters_path option in previous version towards reference to the path where the app was launched, but in the last version it is relative on where the config file is.

The option cors still being optional and its options can be an empty array. If you want more information about the CORS options, visit the CORS section.

The option proxy allow to configure the mock on a proxy mode, that means that you could configure an fallback urls, for all your calls or only the missing ones or none. More information: Proxy Section

Configure CORS

If you want to use killgrave on your client application you must consider to configure correctly all about CORS, thus we offer the possibility to configure it as you need through a config file.

In the CORS section of the file you can find the following options:

  • methods (string array)

    Represent the Access-Control-Request-Method header, if you don't specify it or if you do leave it as any empty array, the default values will be:

    "GET", "HEAD", "POST", "PUT", "OPTIONS", "DELETE", "PATCH", "TRACE", "CONNECT"

  • headers (string array)

    Represent the Access-Control-Request-Headers header, if you don't specify it or if you do leave it as any empty array, the default values will be:

    "X-Requested-With", "Content-Type", "Authorization"

  • exposed_headers (string array)

    Represent the Access-Control-Expose-Headers header, if you don't specify it or if you do leave it as any empty array, the default values will be:

    "Cache-Control", "Content-Language", "Content-Type", "Expires", "Last-Modified", "Pragma"

  • origins (string array)

    Represent the Access-Control-Allow-Origin header, if you don't specify or leave as empty array this option has not default value

  • allow_credentials (boolean)

    Represent the Access-Control-Allow-Credentials header you must indicate if true or false

Prepare Killgrave for Proxy Mode

You can use Killgrave with a proxy mode which means that you can use the flags proxy-mode and proxy-url or the configuration file to declare one of these three modes:

  • none: by default mode, with this mode you don't use any proxy, and the mock server will always look into the files with .imp.json extension.
  • missing: with this mode, the mock server will look into the files with .imp.json extension, but if you call to an endpoint that doesn't exist, then the mock server will call to the real server, declared on the proxy-url configuration variable.
  • all: the mock server only will call to the real server, declared on the proxy-url configuration variable.

The proxy-url must be the root path. For example, if we have endpoint api like, http://example.com/things, the proxy-url will be, http://example.com

Create an Imposter

You must be creating an imposter to start using the application, only files with the .imp.json extension will be interpreted as imposter files, and the base path for the rest of the files will be the path of the .imp.json file.

You need to organize your imposters from more restrictive to less. We use a rule-based system for creating each imposter, for this reason you need to organize your imposters from the most restrictive to the least, like the example below.

imposter example:

[
    {
        "request": {
            "method": "GET",
            "endpoint": "/gophers/01D8EMQ185CA8PRGE20DKZTGSR"            
        },
        "response": {
            "status": 200,
            "headers": {
                "Content-Type": "application/json"
            },
            "body": "{\"data\":{\"type\":\"gophers\",\"id\":\"01D8EMQ185CA8PRGE20DKZTGSR\",\"attributes\":{\"name\":\"Zebediah\",\"color\":\"Purples\",\"age\":55}}}"
        }
    }
]

This a very simple example but Killgrave have more possibilities to configure your imposters, let see some of them on the next sections.

⚠️ Remember that you will need escape any special char, in the properties that admit text.

Imposters Structure

The imposter object can be divided on two parts:

Request

For configure our imposter we will need to declare a request, the request is the object which allow the Killgrave engine identify with which endpoint should match, and have the next properties:

  • method (mandatory): with this property we will define the method of our request, you could configure whatever verb on the http protocol was allowed.
  • endpoint (mandatory): it is a relative path with the endpoint that you want to mock. Admit regex to configure dynamic params.
  • schemaFile: if you want that the request could be validated using a json schema, you will need to indicate the path where your json schema are.
  • `params: if you want to specify a restrictive query param, you can add a list of them, more information: Create an imposter with query params, this option admit regex.
  • headers: if you want to specify a restrictive headers, like authorization or any other, you can define a list of headers that you request will need to match, more info: Create an imposter with headers.

Response

On the other hand we have the response object, the response object will be in charge to generate the output that we want, related with the request that was executed.

The response, allow the next properties:

  • status (mandatory): this will be the status that the response will return.
  • body or bodyFile: if you want that your response return any kind result, you need to define body or bodyFile param, the difference between them, is that body allow writing directly the output, and bodyFile, is a route to the file with the output, this is very useful in case of a large output. You can also remove, this property if you want not return any content.
  • headers: sometime we need a specific header on our response, for example if you want to specify a special `content-type`` so with this property you can define which headers the response will return.
  • delay: is the time the server waits before responding. This can help simulate network issues, or server high load. You must write delay as a string with postfix indicating time unit (see this for more info about an actual format). Also, you can specify minimum and maximum delays using separator ':', the server respond delay will be chosen at random between these values, default value is no delay at all.

Create an imposter using regex

Regex on the endpoint

If we want to define a regex in our endpoint, we will need using the gorilla mux nomenclature, that is {anyvariablename:{regex}}.

In the next example, we have an endpoint configured to match with any kind of ULID Id:

[
  {
    "request": {
      "method": "GET",
      "endpoint": "/gophers/{_id:[\\w]{26}}"
    },
    "response": {
      "status": 200,
      "headers": {
        "Content-Type": "application/json"
      },
      "body": "{\"data\":{\"type\":\"gophers\",\"id\":\"01D8EMQ185CA8PRGE20DKZTGSR\",\"attributes\":{\"name\":\"Zebediah\",\"color\":\"Purples\",\"age\":55}}}"
    }
  }
]

Regex on the params:

In the case of use a regex in our query params, we also will need using the gorilla mux nomenclature, that is {anyvariablename:{regex}}.

For example, we want an imposter that only do match if we receive an apiKey as param:

[
  {
    "request": {
      "method": "GET",
      "endpoint": "/gophers/{_id:[\\w]{26}}",
      "params": {
        "apiKey": "{_apiKey:[\\w]+}"
      }
    },
    "response": {
      "status": 200,
      "headers": {
        "Content-Type": "application/json"
      },
      "body": "{\"data\":{\"type\":\"gophers\",\"id\":\"01D8EMQ185CA8PRGE20DKZTGSR\",\"attributes\":{\"name\":\"Zebediah\",\"color\":\"Purples\",\"age\":55}}}"
    }
  }
]

Regex on the headers:

In this case we will not need the gorilla mux nomenclature to write our regex.

Then if we want for example do a restrictive imposter that need an Authorization header to do match, we could do something like this:

[
  {
    "request": {
      "method": "GET",
      "endpoint": "/gophers/{id:[\\w]{26}}",
      "headers": {
        "Authorization": "\\w+"
      }
    },
    "response": {
      "status": 200,
      "headers": {
        "Content-Type": "application/json"
      },
      "body": "{\"data\":{\"type\":\"gophers\",\"id\":\"01D8EMQ185CA8PRGE20DKZTGSR\",\"attributes\":{\"name\":\"Zebediah\",\"color\":\"Purples\",\"age\":55}}}"
    }
  }
]

So as you can see, in the headers option you can write directly your value or your regex.

Create an imposter using JSON Schema

Sometime, we will need to ensure that the request that we will receive is a valid request, for that reason we can create an imposter that only match with a valid json schema.

To do that we will need to define our json schema first:

imposters/schemas/create_gopher_request.json

{
    "type": "object",
    "properties": {
        "data": {
            "type": "object",
            "properties": {
                "type": {
                    "type": "string",
                    "enum": [
                        "gophers"
                    ]
                },
                "attributes": {
                    "type": "object",
                    "properties": {
                        "name": {
                            "type": "string"
                        },
                        "color": {
                            "type": "string"
                        },
                        "age": {
                            "type": "integer"
                        }
                    },
                    "required": [
                        "name",
                        "color",
                        "age"
                    ]
                }
            },
            "required": [
                "type",
                "attributes"
            ]
        }
    },
    "required": [
        "data"
    ]
}

So with this json schema, we expect a request like this:

{
    "data": {
        "type": "gophers",
        "attributes": {
            "name": "Zebediah",
            "color": "Purples",
            "age": 55
        }
    }
}

Then our imposter will be configure in the next way:

[
  {
    "request": {
        "method": "POST",
        "endpoint": "/gophers",
        "schemaFile": "schemas/create_gopher_request.json",
        "headers": {
            "Content-Type": "application/json"
        }
    },
    "response": {
        "status": 201,
        "headers": {
            "Content-Type": "application/json"
        }
    }
  }
]

The path where the schema is located is relative to where the imposters are.

Create an imposter with delay

If we want to simulate a problem with the network, or create a more realistic response, we can use the delay property.

delay property is a range, between two times that use parser unit of time of Go language.

For example, we can modify our previous POST call to add a delay, we want that our call wait between 1 second to 5 second:

[
  {
    "request": {
        "method": "POST",
        "endpoint": "/gophers",
        "schemaFile": "schemas/create_gopher_request.json",
        "headers": {
            "Content-Type": "application/json"
        }
    },
    "response": {
        "status": 201,
        "headers": {
            "Content-Type": "application/json"
        },
        "delay": "1s:5s"
    }
  }
]

Create an Imposter with dynamic responses

Killgrave allow dynamic responses, with that feature we can use one endpoint and obtain different response or even errors.

We need to understand that to do Killgrave need all the imposters order from more restrictive to less, because the server try to do match with each request and stop when his find one that have the requirements to be executed.

For example, think on our previous POST example, we want to create a gopher, but the request doesn't comply with the json schema declared.

[
  {
    "request": {
        "method": "POST",
        "endpoint": "/gophers",
        "schemaFile": "schemas/create_gopher_request.json",
        "headers": {
            "Content-Type": "application/json"
        }
    },
    "response": {
        "status": 201,
        "headers": {
            "Content-Type": "application/json"
        }
    }
  },
  {
      "request": {
          "method": "POST",
          "endpoint": "/gophers"
      },
      "response": {
          "status": 400,
          "headers": {
              "Content-Type": "application/json"
          },
          "body": "{\"errors\":\"bad request\"}"
      }
  }
]

So as you can see, we have first of all the imposters with the restrictive, headers and json schema, but our last imposter is a simple imposter that it will match with any call via POST to /gophers.

Contributing

Contributions are more than welcome, if you are interested please follow our guidelines to help you get started.

License

MIT License, see LICENSE

About

Simple way to generate mock servers written in Go

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Go 97.1%
  • Dockerfile 1.5%
  • Makefile 1.4%