Mimic – An API Compatible Mock Service For OpenStack



Mimic – An API Compatible Mock Service For OpenStack

0 0


mimic-presentation

Mimic Presentation

On Github glyph / mimic-presentation

Mimic

An API Compatible Mock Service For OpenStack

Lekha

software developer in test

(at) Rackspace

github: lekhajee freenode irc: lekha twitter: @lekha_j1 Lekha: Hi, I am Lekha Jeevan, a software developer in test at Rackspace

Glyph

software developer

(at) Rackspace

github: glyph freenode irc: glyph twitter: @glyph Glyph: and I'm Glyph, also a software developer at Rackspace

What?

Lekha: Today Glyph and I are here to talk about Mimic. An open source framework that allows for testing of OpenStack and Rackspace APIs. Mimic has been making testing across products in Rackspace a cake walk. And we think it could do the same for the OpenStack- backed applications someday. Today, We are not there yet. But your contributions will help us get there soon.

Who?

Lekha: I originally created Mimic to help with testing Rackspace Auto Scale because I hate waiting for test to run. Also, as I was creating it I realized I wanted to make something re-usable for projects across Rackspace and OpenStack. And Glyph volunteered to help

Glyph: I saw Mimic had a lot of promise but needed some help, so I came on to the project and I've been helping improve its architecture, making it more generic and modular.

Sneak Peek

Lekha: Before we even begin to dive into the details, let us take a quick sneak peek to see how easy it is to get started with Mimic.
Lekha: It is only a 3 step process! In a virtual env, we pip install mimic, run mimic and hit the endpoint! And Mimic returns the Authentication endpoint, to be able to Authenticate, and get a service catalog containing the (OpenStack) services that Mimic implements.

Why?

Lekha: While today Mimic is for general purpose testing, it was originally created for testing a specific application: Rackspace Auto Scale. So let's talk about what autoscale is and why we needed Mimic.

Rackspace Auto Scale

An open source project

Source: https://github.com/rackerlabs/otter Lekha: Rackspace Auto Scale is an open source project. A solution that Rackspace utilizes to automate the process of getting the right amount of compute capacity for an application, by creating (scaling up) and deleting(scaling down) servers and associating them with load balancers.

Dependencies

(of Auto Scale)

Lekha: In order to perform these tasks, Auto Scale speaks to three back-end Rackspace APIs.

RackspaceIdentity

Lekha: Identity for authentication and impersonation.

RackspaceCloud Servers

Lekha: Cloud Servers for provisioning and deleting servers.

RackspaceCloud Load Balancers

Lekha: Rackspace Cloud Load Balancers for adding and removing servers to load balancers as they are created or deleted.

Rackspace Identity

is API compatible with

Openstack Identity v2

Lekha: Rackspace Identity is API-compatible with (CLICK) OpenStack Identity v2.

Rackspace Cloud Servers

is powered by

Openstack Compute

Lekha: Rackspace Cloud Servers is powered by (CLICK) OpenStack Compute.

Rackspace Cloud Load Balancers

is a

Custom API

Lekha: And Rackspace Cloud Load Balancers is a custom API.

Testing

(for Auto Scale)

Lekha: As Auto Scale was interacting with so many other systems, testing Auto Scale did not mean just testing features of Auto Scale. But also that, if any of these systems it depended on did not behave as expected, Auto Scale did not crumble and crash, but was consistent and able to handle these upstream failures gracefully.

So, there were two kinds tests for Auto Scale:

Functional

API contracts

Lekha: One, was the Functional tests to validate the API contracts. These tests verified the responses of the Auto Scale API calls given valid, or malformed requests.

System Integration

Identity

Auto Scale

Compute

Load Balancers

Lekha: And the other was the System integration tests. Theese, were more complex. These tests verified integration between Auto Scale and Identity, Compute, and Load balancers.

System Integration

Success

Failure

Lekha: For example: When a scaling group was created, one such test will verify that the servers on that group were provisioned successfully. That is, that the servers went into an 'active' state and were then added as a nodes to the load balancer on the scaling group.(DOUBLE CLICK) Or, if a server went into an error state, (yes! that can happen!), Auto Scale was able to re-provision that server successfully, and then add that active server to the load balancer on the scaling group.

Testing Problems

Test run time ≥ server build time

Lekha: All these tests were set up to run against the real services. And... here are some observations I had whilst writing the tests: (CLICK) Servers could take over a minute, or ten minutes, or longer to provision. And the tests would run that-much-longer.

BUILD → ACTIVE ERROR ACTIVE

Lekha: Sometimes, the tests would fail! due to raandom upstream failures. Like a test would expect a building server to go into an 'active' state, but it would (CLICK) go into an ERROR state

unknown errors

Lekha: And tests for such negative scenarios, like actually testing how Auto Scale would behave if the server did go into 'error' state, could not be tested. This is something that could not be reproduced consistently.

However...

Improving test coverage

Tests → gate

Lekha: However, (CLICK)the overall test coverage was improving. And I continued to add tests, oblivious of the time it was taking run the entire test suite! Later, (CLICK) we had started using these tests as a gate, in the Autoscale merge pipeline.

And...

Slow, flaky tests

Unhappy peers

Lekha: And, (CLICK) the tests were running for so long and were sometimes flaky. Nobody dared to run these tests locally! Not even me, when I was adding more tests! (CLICK)

Also, our peers from the compute and load balancers teams, whose resources we were using up for our "Auto-scale" testing, were not happy! So much, so that, we were pretty glad, we were in a remote office!

We've Had Enough!

(on Auto Scale)

Lekha: But! We had had enough! This had to change! we needed something! to save us from these slow flaky tests!

There And Back Again

Specific

General

Auto Scale Mimic

General

Specific

Mocking Failure Mimic Mimicking OpenStack Glyph: Now that we've had enough, how are we going to solve this problem? (CLICK) Since we've been proceeding from the specific case of Auto Scale to the general utility of Mimic, (click) let's go back to the general problem of testing for failure, and proceed to the specific benefits that Mimic provides.

General →

Negative Path Testing

Glyph: Whenever you have code that handles failures, (CLICK) you need to have tests to ensure that that code works properly.

Real Services

FAIL

Glyph: And if you have code that talks to external services, those services are going to fail, and you're going to need to write code to handle that.

But Not When You

WANT THEM TO

Glyph: But if your only integration tests are against real versions of those external services, then only your unit tests are going to give you any idea of whether you have handled those failure cases correctly.

Succeeding

At Success

Glyph: Your positive-path code - the code that submits a request and gets the response that it expects - is going to get lots of testing in the real world. Services usually work, and when they don't, the whole business of service providers is to fix it so they do. So most likely, the positive-path code is going to get exercised all the time and you will have plenty of opportunities to flush out bugs.

Means Failing

At Failure

Glyph: If you test against real services, your negative-path code will only get invoked in production when there's a real error. If everything is going as planned, this should be infrequent, which is great for your real service but terrible for your test coverage.

Mimic Succeeds

At Failure!

Glyph: It's really important to get negative-path code right. If all the external services you rely on are working fine, then it's probably okay if your code has a couple of bugs. You might be able to manually work around them.

😈 ☁

(Production)

Glyph: But if things are starting to fail with some regularity in your cloud - that is to say - if you are using a cloud - that is exactly the time you want to make sure your system is behaving correctly: accurately reporting the errors, measuring the statistics on those errors, and allowing you to stay on top of incident management for your service and your cloud.

😇 ☁

(Staging)

Glyph: Even worse, when you test against a real service, you are probably testing against a staging instance. And, if your staging instance is typical, it probably doesn't have as much hardware, or as many concurrent users, as your production environment. Every additional piece of harware or concurrent user is another opportunity for failure, so that means your staging environment is even less likely to fail.
import unittest
Glyph: I remember the bad old days of the 1990s when most projects didn't have any unit tests. Things are better than that now. OpenStack itself has great test coverage. We have unit tests for individual unit tests and integration tests for testing real components together.

test_stuff ... [OK]

Glyph: We all know that when you have code like this:
try:
    result = service_request()
except:
    return error
else:
    return ok(result)
Glyph: ... that we need to write tests for this part:
try:
    result = service_request()
except: return error
else: return ok(result) Glyph: ... and one popular way to get test coverage for those error lines is by writing a custom mock for it in your unit tests.
Glyph: So if we can't trust real systems for error conditions, why isn't it sufficient to simply trust your unit tests to cover error conditions, and have your integration tests for making sure that things work in a more realistic scenario?

For those of you who don't recognize it, this is the Mock Turtle from Alice in Wonderland. As you can see, he's not quite the same as a real turtle, just like your test mocks aren't quite the same as a real system.

Glyph: There's a reason that the mock turtle is crying. He knows that he can't quite do the things a real turtle can do, just like your test mocks can't quite replace those real systems.

Let's take a specific example from OpenStack Compute.

if not os.chdir(ca_folder(project_id)):
    raise exception.ProjectNotFound(
        project_id=project_id)
Glyph: In June of this year, OpenStack Compute introduced a bug making it impossible to revoke a certificate. The lines of code at fault were these two additions here. This is not a criticism of Nova itself; the bug has already been fixed. My point is that they fell into a very common trap.
if not os.chdir(ca_folder(project_id)):
    raise exception.ProjectNotFound(
        project_id=project_id)
Glyph: The bug here is that chdir does not actually return a value.
@mock.patch.object(os, 'chdir', return_value=True)
def test_revoke_cert_process_execution_error(self):
    "..."

@mock.patch.object(os, 'chdir', return_value=False)
def test_revoke_cert_project_not_found_chdir_fails(self):
    "..."
Glyph: Because the unit tests introduced with that change construct their own mocks for chdir, Nova's unit tests properly cover all the code, but the code is not integrated with a system that is verified in any way against what the real system (in this case, Python's chdir) does.
Glyph: In this specific case, Nova might have simply tested against a real directory structure in the file system, because relative to the value of testing against a real implementation, "New Folder" is not a terribly expensive operation.
Glyph: However, standing up an OpenStack cloud is significantly more work than running mkdir. If you are developing an application against OpenStack, deploying a real cloud to test against can be expensive, error-prone, and slow, as Auto Scale's experience shows.

The Best Of

Both Worlds?

Creating a one-off mock for every test is quick, but error prone. Good mocks rapidily become a significant maintainance burden in their own right. Auto Scale needed something that could produce all possible behaviors like a unit test mock, but ensure those behaviors accurately reflected a production environment. (CLICK) It should be something maintained as a separate project, not part of a test suite, that can have its own tests and code review to ensure its behavior is accurate.

→Specific

Mimic

Glyph: Since we've been proceeding from the general to the specific, (CLICK) right here, where we need a realistic mock of a back-end openstack service, is where the specific value of Mimic comes in.

Mimic

Version 0.0

Lekha: The first version of Mimic was built as a stand-in service for Identity, Compute and Rackspace Load balancers, the services that Auto Scale depends on.

Pretending

...

Lekha: The essence of Mimic is pretending. The first thing that you must do to interact with it is to...

Pretending

to authenticate

Lekha: ...pretend to authenticate. Mimic does not validate credentials - all authentications will succeed. As with the real Identity endpoint, Mimic's identity endpoint has a service catalog which includes endpoints for all the services implemented within Mimic. A well behaved OpenStack client will use the service catalog to look up URLs for its service endpoints. Such a client will only need two pieces of configuration to begin communicating with the cloud, i.e. credentials and the identity endpoint. A client written this way will only need to change the Identity endpoint to be that of Mimic.

Pretending

to Boot Servers

Lekha: When you ask Mimic to create a server, it pretends to create one. This is not like stubbing with static responses: when Mimic pretends to build a server, it remembers the information about that server and will tell you about it in the subsequent requests.

Pretending

is faster

Lekha: Mimic was originally created to speed things up. So, it was very important that - it be fast both to respond to requests, and to have developers setup.

in-memory

Lekha: It uses in-memory data structures.

minimaldependencies

(almost entirely pure Python)

Lekha: with minimal software dependencies, almost entirely pure Python.

Service Dependencies

Lekha: With no service dependencies

Configuration

Lekha: and no configuration

self-contained

Lekha: And is entirely self-contained.

Demo!

Nova command-line client

Lekha: Lets see how we can run the python nova command-line client against Mimic

config.sh

export OS_USERNAME=username
export OS_PASSWORD=password
export OS_TENANT_NAME=11111
export OS_AUTH_URL=http://localhost:8900/identity/v2.0/tokens
Lekha: Here is the config file that holds the environment variables required for the OpenStack command-line clients.

config.sh

export OS_USERNAME=username
export OS_PASSWORD=password
export OS_TENANT_NAME=11111
export OS_AUTH_URL=http://localhost:8900/identity/v2.0/tokens
Lekha: We have set a random username, password and tenant name, as Mimic only pretends to authenticate

config.sh

export OS_USERNAME=username
export OS_PASSWORD=password
export OS_TENANT_NAME=11111
export OS_AUTH_URL=http://localhost:8900/identity/v2.0/tokens
Lekha: And the Auth url is set to be that of Mimic.

Now, let's continue where we left off with our first demo. So we already have an instance of mimic running.

Lekha: Let's pip install the python nova-client and ensure the config file has the AUTH_URL pointing to that of Mimic. We source the config file and we see that no servers exist on Mimic start up! Let's create a server with a random flavor and image. The server created is in an active state. Lets create a second server, which also is built immediately and is an active state. Now we have 2 active servers that Mimic knows of. Lets delete the second server... and now Mimic knows of the deleted server and has only the one server remaining.

Using Mimic

(on Auto Scale)

Lekha: We did the same thing with Auto Scale. We pointed the tests and the Autoscale API to run against an instance of Mimic.

The Results!

(Functional tests using Mimic)

Lekha: This reduced the test time exponentially! Before Mimic the functional tests would take...

Functional Tests:

15 minutes

against a real system

vs.

30 seconds

against Mimic

Lekha: (CLICK) 15 minutes to complete, and now they run in (CLICK)less than 30 seconds!

The Results!

(Integration tests using Mimic)

Lekha: In the system integration tests, if one of the servers in the test remained in "building" for fifteen minutes longer than usual, then the tests would run fifteen minutes slower.

Integration Tests:

3 hours or more

against a real system

vs.

3 minutes

against Mimic

Lekha: These tests took (CLICK)over 3 hours to complete and using Mimic this went down to be (CLICK) less than 3 *minutes* consistently, to complete!

Lekha: All our dev VMs are now configured to run against Mimic. One of our devs from the Rackspace Cloud Intelligence team, calls this "Developing on Airplane Mode!", as we can work offline without having to worry about uptimes of the upstream systems and get immediate feedback on the code being written.

What aboutnegative paths?

Glyph: But Lekha, what about all the negative-path testing stuff I was talking about before? Does Mimic simulate errors? How did this dev VM test Auto Scale's error conditions?

Mimic doessimulate errors

Lekha: Well Glyph, I am as pleased as I am suprised that you ask that. Mimic does simulate errors.

Error injection using metadata

Lekha: Earlier, when I said Mimic pretends to create a server, that wasn't entirely true - sometimes Mimic pretends to *not* create a server. It uses the metadata provided during the creation of the server, inspects the metadata, and sets the state of the server respectively.

Lets go back to the demo and see how this can be done.

Lekha: So, we had the one active server. Now, lets create a server with the `metadata`: `"server_building": 30`. This will keep the server in build state for 30 seconds. Now we have 2 servers. The active and building sever. Also, We can create a server that goes into an error state, using the `metadata`: `"server_error": True`. As you can see, we now have 3 different servers, with 3 different states.

Retry On Errors

Lekha: For the purposes of Auto Scale it was important that we have right number of servers on a scaling group, even if a number of attempts to create one failed. We chose to use metadata for error injection so that requests with injected errors could also be run against real services. For Auto Scale, the expected end result is the same number of servers created, irrespective of the number of failures. But this behavior may also be useful to many other applications because retrying is a common pattern for handling errors.

Mimic 0.0 was...

Too Limited

Lekha: (PAUSE)However, the first implementation of mimic had some flaws, it was fairly Rackspace specific and only implemented the endpoints of the services that Autoscale depends upon . And they were all implemented as part of Mimic's core. It ran each service on a different port, meaning that for N endpoints you would need not just N port numbers, but N *consecutive* port numbers. It allowed for testing error scenarios, but only using the metadata. This was not useful for all cases, for example, for a control panel that does not not allow the user to enter any metadata.

Mimic 0.0 was...

Single Region

Lekha: Mimic also did not implement multiple regions. It used global variables for storing all state, which meant that it was hard to add additional endpoints with different state in the same running mimic instance.

Beyond Auto Scale:

Refactoring Mimic

Glyph: Mimic had an ambitious vision: to be a one-stop mock for all OpenStack and Rackspace services that needed fast integration testing. However, its architecture at the time severely limited the ability of other teams to use it or contribute to it. As Lekha mentioned, it was specific not only to Rackspace but to Auto Scale.

YAGNI

Glyph: On balance, Mimic was also extremely simple. It followed the You Aren't Gonna Need It principle of extreme programming very well, and implemented just the bare minimum to satisfy its requirements, so there wasn't a whole lot of terrible code to throw out or much unnecessary complexity to eliminate.

E(ITO)YAGNI

Glyph: There is, however, a corrolary to YAGNI, which is E(ITO)YAGNI: Eventually, It Turns Out, You *Are* Going To Need It. As Mimic grew, other services within Rackspace wanted to make use of its functionality, and a couple of JSON response dictionaries in global variables were not going to cut it any more.

Plugins!

Glyph: So we created a plugin architecture.

Identity

Is the Entry Point

(Not A Plugin)

Glyph: Mimic's Identity endpoint is the top-level entry point to Mimic as a service. Every other URL to a mock is available from within the service catalog. As we were designing the plugin API, it was clear that this top-level Identity endpoint needed to be the core part of Mimic, and plug-ins would each add an entry for themselves to the service catalog.
http://localhost:8900/mimicking/ NovaApi-78bc54/ORD/ v2/tenant_id_f15c1028/servers Glyph: URLs within Mimic's service catalog all look similar. In order to prevent conflicts between plugins, Mimic's core one encodes the name of your plugin and the region name specified by your plugin's endpoint. Here we can see what a URL for the Compute mock looks like. (CLICK) This portion of the URL, which identifies which mock is being referenced, is handled by Mimic itself, so that it's always addressing the right plugin. (CLICK) Then there's the part of the URL that your plugin itself handles, which identifies the tenant and the endpoint within your API.

Plugin Interface:“API Mock”

Glyph: Each plugin is an API mock, which has only two methods:
class YourAPIMock():
  def catalog_entries(...)
  def resource_for_region(...)

(that's it!)

Glyph: (click) catalog_entries (click) and resource_for_region (click) That's it!.
def catalog_entries(self,
                    tenant_id):

Glyph: catalog_entries takes a tenant ID and returns the entries in Mimic's service catalog for that particular API mock.

APIs have catalog entries for each API type, which in turn have endpoints for each virtual region they represent.

return [
    Entry(
        tenant_id, "compute", "cloudServersOpenStack",
        [
            Endpoint(tenant_id, region="ORD",
                     endpoint_id=text_type(uuid4()),
                     prefix="v2"),
            Endpoint(tenant_id, region="DFW",
                     endpoint_id=text_type(uuid4()),
                     prefix="v2")
        ]
    )
]

Glyph: This takes the form of an iterable of a class called (CLICK) Entry, each of which is (CLICK) a tenant ID, (CLICK) a type, (CLICK) a name, (CLICK) and a collection of (CLICK) Endpoint objects, each (CLICK) containing (CLICK) the name of a pretend region, (CLICK) a URI version prefix that should appear in the service catalog after the generated service URL but before the tenant ID.

def resource_for_region(
    self, region, uri_prefix,
    session_store
):
    return (YourRegion(...)
            .app.resource())
Glyph: resource_for_region takes (CLICK) the name of a region, (CLICK) a URI prefix - produced by Mimic core to make URI for each service unique, so you can generate URLs to your services in any responses which need them - (CLICK) and a session store where the API mock may look up state of the resources it pretended to provision for the respective tenants. (CLICK) resource_for_region returns an HTTP resource associated with the top level of the given region. This resource then routes this request to any tenant- specific resources associated with the full URL path.
class YourRegion():

    app = MimicApp()

    @app.route('/v2/<string:tenant_id>/servers',
               methods=['GET'])

    def list_servers(self, request, tenant_id):
        return json.dumps({"servers": []})
Glyph: Once you've created a resource for your region, it has a route for the parts of the URI that starts at the end of the URI path. Here you can see what the nova "list servers" endpoint would look like using Mimic's API; as you can see, it's not a lot of work at all to return a canned response. It would be a little beyond the scope of this brief talk to do a full tutorial of how resource traversal works in the web framework that Mimic uses, but hopefully this slide - which is a fully working response - shows that it is pretty easy to get started.

Tell Mimic

To Load It

Glyph: Now that we have most of a plugin written, let's get Mimic to load it up.
# mimic/plugins/your_plugin.py

from your_api import YourAPIMock
the_mock_plugin = YourAPIMock()
Glyph: To register your plugin with Mimic, you just need to drop an instance of it into any module of the mimic.plugins package.

Mimic Remembers

(until you restart it)

Glyph: This, of course, just shows you how to create ephemeral, static responses - but as Lekha said previously, Mimic doesn't just create fake responses; it remembers - (CLICK) in memory - what you've asked it to do.
session = session_store.session_for_tenant_id(tenant_id)

class YourMockData():
    "..."

your_data = session.data_for_api(your_api_mock,
                                 YourMockData)
Glyph: That "session_store" object passed to resource_for_region is the place you can keep any relevant state. It gives you a per-tenant session object, and then you can ask that session for any mock-specific data you want to store for that tenant. All session data is created on demand, so you pass in a callable which will create your data if no data exists for that tentant/API pair.
session = session_store.session_for_tenant_id(tenant_id)

from mimic.plugins.other_mock import (other_api_mock,
                                      OtherMockData)

other_data = session.data_for_api(other_api_mock,
                                  OtherMockData)
Glyph: Note that you can pass other API mocks as well, so if you want to inspect a tenant's session state for other services and factor that into your responses, it's easy to do so. This pattern of inspecting and manipulating a different mock's data can also be used to create control planes for your plugins, so that one plugin can tell another plugin how and when to fail by storing information about the future expected failure on its session.

Errors As A Service

Glyph: We are still working on the first error-injection endpoint that works this way, by having a second plugin tell the first what its failures are, but this is an aspect of Mimic's development we are really excited about, because that control plane API also doubles as a memory of the unexpected, and even potentially undocumented, ways in which the mocked service can fail.

Error Conditions Repository

Lekha: Anyone testing a product, will run into unexpected errors. Thats why we test! But we dont know what we dont know, and cant be prepared for this ahead of time right!

Discovering Errors

Against Real Services

Lekha: When we were running the Auto Scale tests against Compute, we began to see some one-off errors. Like, when provisioning a server, the test expected a server to go into a building state for some time before it is active, but it would remain in building state for over an hour or even would sometimes go into an error state, after.

Record Those Errors

Within Mimic

Lekha: Auto Scale had to handle such scenarios gracefully and the code was changed to do so. And Mimic provided a way to tests this consistently.

Discover More Errors

Against Real Services

Lekha: However, like I said, we dont know what we dont know. We were not anticipating on finding any other such errors. But, there were more! And... this was slow process for us to uncover such errors, as we tested against the real services.

Record Those Errors

For The Next Project

Lekha: And, we continued to add such errors to Mimic.

Now, Wont it be great if not every client that depended on a service had to go through this same cycle. Not everyone had to find all the possible error conditions in the service by experience. And have to deal with them at the pace that they occur.

Share A Repository

For Future Projects

Lekha: What if we had a repository of all such known errors, that everyone contributes to. So the next person using the plugin can use the existing ones, and ensure there application behaves consistently irrespective of the errors. And be able add any new ones to it.

Mimic Is A Repository

Lekha: Mimic is just that, a repository of all known responses including the error responses.

Mimic Endpoint

/mimic/v1.0/presets Lekha: Mimic has an endpoint `presets` that today lists all the metadata related errors conditions that can be simulated using Mimic.

Control

Glyph: In addition to storing a repository of errors, Mimic allows for finer control of behavior beyond simple success and error. You can determine the behavior of a mimicked service in some detail.

Now & Later

Glyph: We're not just here today to talk about exactly what Mimic offers right now, but where we'd like it to go. And in that spirit I will discuss one feature that Mimic has for controlling behavior today, and one which we would like to have in the future.

Now

Glyph: Appropriately enough, since I'm talking about things now and things in the future, the behavior-control feature I'd like to talk about that that Mimic has right now is the ability to control time.

now()

Glyph: That is to say: when you do something against Mimic that will take some time, such as building a server, time does not actually pass ... for the purposes of that operation.

/mimic/v1.1/tick

Glyph: Instead of simply waiting 10 seconds, you can hit this second out-of-band endpoint, the "tick" endpoint ...
{
    "amount": 1.0
}
Glyph: with a payload like this. It will tell you that time has passed, like so:
{
    "advanced": 1.0,
    "now": "1970-01-01T00:00:01.000000Z"
}
Glyph: Now, you may notice there's something a little funny about that timestamp - it's suspiciously close to midnight, january first, 1970. Mimic begins each subsequent restart thinking it's 1970, at the unix epoch; if you want to advance the clock, just plug in the number of seconds since the epoch as the "amount" and your mimic will appear to catch up to real time.
{
  "server": {
    "status": "BUILD",
    "updated": "1970-01-01T00:00:00.000000Z",
    "OS-EXT-STS:task_state": null,
    "user_id": "170454",
    "addresses": {},
    "...": "..."
  }
}
Glyph: If you've previously created a server with "server_building" metadata that tells it to build for some number of seconds, and you hit the 'tick' endpoint telling it to advance time the server_building number of seconds...
{
  "server": {
    "status": "ACTIVE",
    "updated": "1970-01-01T00:00:01.000000Z",
    "OS-EXT-STS:task_state": null,
    "user_id": "170454",
    "addresses": {},
    "...": "..."
  }
}
Glyph: that server (and any others) will now show up as "active", as it should. This means you can set up very long timeouts, and have servers behave "realistically", but in a way where you can test several hours of timeouts a time.

--realtime

Glyph: You can ask Mimic to actually pay attention to the real clock with the --realtime command-line option; that disables this time-advancing endpoint, but it will allow any test suites that rely on real time passing to keep running.

Later

Glyph: Another feature that isn't implemented yet, that we hope to design later, is the ability to inject errors ahead of time, using a separate control-plane interface which is not part of a mock's endpoint.

Error Injection

Glyph: We've begun work on a branch doing this for Compute, but we feel that every service should have the ability to inject arbitrary errors.

Error Injection

Currently: Metadata-Based

Glyph: As Lekha explained, Mimic can already inject some errors by supplying metadata within a request itself.

Error Injection

Currently: In-Band

Glyph: However, this means that in order to cause an error to happen, you need to modify the request that you're making to mimic, which means your application isn't entirely unmodified.

Error Injection

Future: Separate Catalog Entry

Glyph: What we'd like to do in the future is to put the error-injection control plane into the service catalog, with a special entry type so that your testing infrastructure can talk to it.

Error Injection

Future: Out-Of-Band

Glyph: This way, your testing tool would authenticate to mimic, and tell Mimic to cause certain upcoming requests to succeed or fail before the system that you're testing even communicates with it. Your system would not need to relay any expected-failure data itself, and so no metadata would need to be passed through.

Error Injection

Future: With Your Help

Glyph: What we'd really like to build with these out-of-band failures, though, is not just a single feature, but an API that allows people developing applications against openstack to make those applications as robust as possible by easily determining how they will react at scale, under load, and under stress, even if they've never experienced those conditions. So we need you to contribute the errors and behaviors that you have experienced.

Even Later...

Glyph: Mimic is based on a networking framework ...
Glyph: ... some of you know which one I'm talking about ...

Even Later...

Future Possibilities,Crazy Features!

Glyph: ... which has such features as built-in DNS and SSH servers.

Even Later...

Real SSH Server

For Fake Servers

Glyph: It would be really cool if when a virtual server was booted, the advertised SSH port really did give you access to an SSH server, albeit one that can be cheaply created from a local shell as a restricted user or a container deployment, not a real virtual machine.

Even Later...

Real DNS Server

For Fake Zones

Glyph: Similarly, if we were to have a Desginate mock, it would be really cool to have real DNS entries.

Mimic for OpenStack

Lekha: Mimic, can be the tool, where you do not have to stand up the entire dev stack to understand how an OpenStack API behaves.

Mimic can be the tool which enables an OpenStack developer to get quick feedback on the code he/she is writing and not have to go through the gate multiple times to understand that - "maybe I should have handled that one error, that the upstream system decides to throw my way every now and then"

It's Easy!

Glyph: One of the things that I like to point out is that Mimic is not real software. It's tiny, self-contained, doesn't need to interact with a database, or any external services. Since it mimics exclusively existing APIs, there are very few design decisions. As a result, contributing to Mimic is a lot easier than contributing to OpenStack proper.

We need your help!

Source: https://github.com/rackerlabs/mimic Issues: https://github.com/rackerlabs/mimic/issues Chat: ##mimic on Freenode Lekha: So, please come join us build Mimic. Together we can make this a repository for all known reponses (including errors!) for the OpenStack APIs. As we mentioned earlier, Mimic is Open source and here is the github link to the repository. All the features or issues we are working on, or planning to work on, in the near future are under the issues tab on github. You can start by using Mimic and giving us your feedback. Or better yet, forking it and contributing to it, by adding plugins for services that do not exist today! Thank you!