AIP-128

Declarative-friendly interfaces

Many services need to interact with common DevOps tools, particularly those that create and manage network-addressible resources (such as virtual machines, load balancers, database instances, and so on). These tools revolve around the principle of "configuration as code": the user specifies the complete intended landscape, and tooling is responsible for making whatever changes are necessary to achieve the user's specification.

These tools are declarative: rather than specifying specific actions to take, they specify the desired outcome, with the actions being derived based on the differences between the current landscape and the intended one.

Furthermore, there are numerous popular DevOps tools, with more being introduced each year. Integrating hundreds of resource types with multiple tools requires uniformity, so that integration can be automated.

Guidance

Services should clearly delineate between "control plane" operations and "data plane" operations, ideally through the use of distinct services with their own interface definition documents.

  • Control plane operations are responsible for managing the lifecycle of resources.
  • Data plane operations are responsible for managing the content of resources.

The same resource may have both control plane operations and data plane operations associated with it. For example, a database API would have operations to create or delete database tables (control plane) as well as operations to write and read rows to that table (data plane).

Resources

Resources that are declarative-friendly must use only strongly-consistent standard methods for managing resource lifecycle, which allows tools to support these resources generically, as well as conforming to other declarative-friendly guidance (see further reading).

Declarative-friendly resources should designate that they follow the declarative-friendly style:

message Book {
  option (google.api.resource) = {
    type: "library.googleapis.com/Book"
    pattern: "publishers/{publisher}/books/{book}"
    style: DECLARATIVE_FRIENDLY
  };

  string name = 1;
  // Other fields...
}

Annotations

Declarative-friendly resources must include a map<string, string> annotations field to allow clients to store small amounts of arbitrary data.

The annotations field must use the Kubernetes limits to maintain wire compatibility, and should require dot-namespaced annotation keys to prevent tools from trampling over one another.

Note: Annotations are distinct from various forms of labels. Labels can be used by server-side policies, such as IAM conditions. Annotations exist to allow client tools to store their own state information without requiring a database.

Reconciliation

If a resource takes time (more than a few seconds) for updates to be realized, the resource should include a bool reconciling field to disclose that changes are in flight. This field must be output only.

A resource must set the reconciling field to true if the current state of the resource does not match the user's intended state, and the system is working to reconcile them. This is regardless of whether the root cause of going into reconciliation was user or system action.

Note: Services responding to a GET request must return the resource's current state (not the intended state).

Further reading

A significant amount of guidance is more strict for declarative-friendly interfaces, due to the focus on automation on top of these resources. This list is a comprehensive reference to declarative-friendly guidance in other AIPs:

  • Resources should not employ custom methods: see AIP-136.
  • Resources must use the Update method for repeated fields: see AIP-144.
  • Resources must include certain standard fields: see AIP-148.
  • Resources must have an etag field: see AIP-154.
  • Resources should provide change validation: see AIP-163.
  • Resources should not implement soft-delete. If the id cannot be re-used, the resource must implement soft-delete and the undelete RPC: see AIP-164

Changelog

  • 2023-05-11: removed must on resource_id, which was upstreamd to a general must