vinegar.request_handler.sqlite_update

Request handler that allows for making changes to an SQLite database.

This request handler is backed by an instance of vinegar.utils.sqlite_store.DataStore for actually accessing the database.

This can be useful when a flag for a system has to be updated dynamically. For example, there might be a flag that indicates whether a system should boot into an installer when booting from the network. Once the system installation has finished, this flag would need to be reset to avoid running the installer again and again. With this handler, this can be achieved by making a HTTP request to an instance of this handler once the installation process has finished. This handler can then reset the flag in the database, ensuring that the system will not boot into the installer again.

This handler only handles POST requests. The semantics of GET requests are that are idempotent, meaning that they do not change any state. As this request handler makes changes to the state of the database, allowing GET requests would violate the semantics of the HTTP procol.

It is very easy to trigger a POST request from the command line. Depending on which tools are available on the system, either of the following two commands can be used:

curl -X POST http://vinegar.example.com/sqlite-prefix/system-id
wget -O - --post-data= http://vinegar.example.com/sqlite-prefix/system-id

Access restrictions

By default, this request handler will allow any client to update the data for all systems. This might not be desirable, in particular when the network in which the Vinegar server is running cannot be deemed secure.

Therefore, it is possible to limit access to the data for each system to specific clients. This is achieved through the client_address_key configuration option. This option specifies a key into the system data. The key can consist of multiple components separated by colons (:) to point into nested dictionary.

For example, suppose that client_address_key is set to net:ip_addr. If the handler gets a request for the system ID myid, it will ask the data source for the system data for this system by calling data_source.get_data('myid', {}, '').

If the system data returned for this system does not contain a value for the specified key, the request is denied with HTTP status code 403 (forbidden) and the database is not modified.

For this example, let us assume that the data source returns the following data for the system myid (expressed as YAML):

net:
    ip_addr: 192.0.2.1

In this case, the request handler will only allow the request, if it comes from the IP address 192.0.2.1. In all other cases, it will reject the request.

Instead of a single IP address, the system data may also contain a list or set of IP addresses. For example, if get_data returned the following system data

net:
    ip_addr:
        - 192.0.2.1
        - 2001:db8::1

the request would be allowed if it came from 192.0.2.1 or 2001:db8::1.

When the client_address_key option is used, this request handler needs a data source in order to get information about the system. This request handler implements the DataSourceAware interface, so when it is used inside a container, that container will typically take care of setting the data source. If instantiated directly, the data source has to be set explicitly by calling the handler’s set_data_source method.

Configuration options

The sqlite_update request handler have several configuration options that can be used to control its behavior.

action (mandatory)

Action to be taken when this handler is triggers. If set to delete_data, all data stored for the specified system is deleted. If set to delete_value, only the key and value specified by the key option are deleted. If set to set_value the value for the key specified by the key option is set to the value specified by the value option.

db_file (mandatory)

Path to the database file that contains the SQLite database that will be updated by this handler. This database must be a database in the format expected by vinegar.utils.sqlite.data_store.

request_path (mandatory)

request path for which this request handler shall be used. The specified path must start with a /. It will match any request that has a path that starts with the configured request path and is followed by a string. For example, when the request_path is set to /prefix the handler will match /prefix/name. In this example, name is the string that is used as the system ID.

client_address_key (optional)

Key into the system data that points to the place in the data where the allowed client address or addresses are stored. If this option is not set (the default), each client can access this handler for arbitrary system IDs and thus modify the data for arbitrary systems. When this is not desired, this option can be used to limit the allowe client (IP) addresses for each system. The key can point into a nested dictionary, using the colon (:) to separate key components for the various levels. The value can be a string (matching exactly one IP address) or a list or set of IP addresses (matching any of the addresses in the list or set). Please refer to Access restrictions for a more detailed discussion of how this option can be used.

key (optional)

Name of the key in the database that shall be deleted or updated. If the action is set to delete_value or set_value, this option must be specified.

value (optional)

Value to be set for the key denoted by the key. When the action is set to set_value, this option must be specified.

class vinegar.request_handler.sqlite_update.HttpSQLiteUpdateRequestHandler(config: Mapping[Any, Any])

HTTP request handler that applies updates to an SQLite database.

For information about the configuration options supported by this request handler, please refer to the module documentation.

can_handle(filename: str, context: Any) → bool

Tell whether the request can be handled by this request handler.

Returns True if the request can be handled and False if it cannot be handled and the next request handler should be tried.

Parameters
  • filename – filename that has been requested by the client. The filename includes the full request path including the query string (if present) and has not been URL decoded.

  • context – context object that was returned by prepare_context.

Returns

True if this request handler can handle the specified request, False if the request should be deferred to the next handler.

close()

Close the data store backing this request handle. All operations that involve the data store will fail by raising an exception after calling this method.

For most applications, where request handlers are long-lived, relying on Python’s garbage collection is fine for closing the underlying data store. However, if an application rapidly creates and discards request handler instances (e.g. for automated tests), closing the request handler explicitly can be beneficial because it helps to release resources early on.

handle(filename: str, method: str, headers: http.client.HTTPMessage, body: io.BufferedIOBase, client_address: Tuple, context: Any) → Tuple[http.HTTPStatus, Mapping[str, str], io.BufferedIOBase]

Handle the request.

This method returns a tuple of three items. The first item is the HTTP status code, the second item are the headers that shall be sent to the client, and the third is a file-like object from which the data for the requested file can be read. The returned file-like object must supply its data in binary form.

Parameters
  • filename – filename that has been requested by the client. The filename includes the full request path including the query string (if present) and has not been URL decoded.

  • method – the HTTP method used for the request (e.g. “GET”).

  • headers – HTTP headers provided by the client.

  • body – file-like object that provides the request body sent by the client. This file-like object returns bytes when reading.

  • client_address – client address. The structure of the tuple depends on the address family in use, but typically the first element is the client’s host address and the second element is the client’s port number.

  • context – context object that was returned by prepare_context.

Returns

tuple of the HTTP status code, the response headers, and a file-like object that provides the data that is transferred to the client. The file-like object must provide binary data.

prepare_context(filename: str) → Any

Prepare a context object for use by can_handle and handle. This method is called for each request before calling can_handle.

This is useful when both function need to do some processing on the filename or client address. This processing can be implemented in prepare_context and passed to the two other methods through the context so that it does not have to be done twice.

The return value of this method is passed to can_handle and handle. The default implementation simply returns None.

Parameters

filename – filename that has been requested by the client. The filename includes the full request path including the query string (if present) and has not been URL decoded.

Returns

context object that is passed to can_handle and handle.

set_data_source(data_source: vinegar.data_source.DataSource) → None

Set the data source to be used by this component.

Calling inject_data_source might be preferable to calling this method directly.

In general, code using this method should not assume that this method is thread safe, even if the object that implements it is considered safe in general. This means that this method should typically be called only once, directly after creating an object.

Parameters

data_source – data source to be injected.

vinegar.request_handler.sqlite_update.get_instance_http(config: Mapping[Any, Any]) → vinegar.request_handler.sqlite_update.HttpSQLiteUpdateRequestHandler

Create a HTTP request handler that applies updates to an SQLite database.

Parameters

config – configuration for this request handler. Please refer to the module documentation for a list of supported options.

Returns

HTTP request handler applying updates to an SQLite database.