diff --git a/cincinnati/src/lib.rs b/cincinnati/src/lib.rs index d56d3c2f7..238ddfd2e 100644 --- a/cincinnati/src/lib.rs +++ b/cincinnati/src/lib.rs @@ -54,7 +54,7 @@ impl Release { #[derive(Debug, Deserialize, Serialize, PartialEq, Clone)] pub struct ConcreteRelease { pub version: String, - pub payload: String, + pub image: String, pub metadata: HashMap, } @@ -332,17 +332,17 @@ mod tests { let mut graph = Graph::default(); let v1 = graph.dag.add_node(Release::Concrete(ConcreteRelease { version: String::from("1.0.0"), - payload: String::from("image/1.0.0"), + image: String::from("image/1.0.0"), metadata: HashMap::new(), })); let v2 = graph.dag.add_node(Release::Concrete(ConcreteRelease { version: String::from("2.0.0"), - payload: String::from("image/2.0.0"), + image: String::from("image/2.0.0"), metadata: HashMap::new(), })); let v3 = graph.dag.add_node(Release::Concrete(ConcreteRelease { version: String::from("3.0.0"), - payload: String::from("image/3.0.0"), + image: String::from("image/3.0.0"), metadata: HashMap::new(), })); graph.dag.add_edge(v1, v2, Empty {}).unwrap(); @@ -355,12 +355,12 @@ mod tests { #[test] fn serialize_graph() { let graph = generate_graph(); - assert_eq!(serde_json::to_string(&graph).unwrap(), r#"{"nodes":[{"version":"1.0.0","payload":"image/1.0.0","metadata":{}},{"version":"2.0.0","payload":"image/2.0.0","metadata":{}},{"version":"3.0.0","payload":"image/3.0.0","metadata":{}}],"edges":[[0,1],[1,2],[0,2]]}"#); + assert_eq!(serde_json::to_string(&graph).unwrap(), r#"{"nodes":[{"version":"1.0.0","image":"image/1.0.0","metadata":{}},{"version":"2.0.0","image":"image/2.0.0","metadata":{}},{"version":"3.0.0","image":"image/3.0.0","metadata":{}}],"edges":[[0,1],[1,2],[0,2]]}"#); } #[test] fn deserialize_graph() { - let json = r#"{"nodes":[{"version":"1.0.0","payload":"image/1.0.0","metadata":{}},{"version":"2.0.0","payload":"image/2.0.0","metadata":{}},{"version":"3.0.0","payload":"image/3.0.0","metadata":{}}],"edges":[[0,1],[1,2],[0,2]]}"#; + let json = r#"{"nodes":[{"version":"1.0.0","image":"image/1.0.0","metadata":{}},{"version":"2.0.0","image":"image/2.0.0","metadata":{}},{"version":"3.0.0","image":"image/3.0.0","metadata":{}}],"edges":[[0,1],[1,2],[0,2]]}"#; let de: Graph = serde_json::from_str(json).unwrap(); assert_eq!(de.releases_count(), 3); @@ -375,12 +375,12 @@ mod tests { let mut graph = Graph::default(); let v1 = graph.dag.add_node(Release::Concrete(ConcreteRelease { version: String::from("1.0.0"), - payload: String::from("image/1.0.0"), + image: String::from("image/1.0.0"), metadata: HashMap::new(), })); let v2 = graph.dag.add_node(Release::Concrete(ConcreteRelease { version: String::from("2.0.0"), - payload: String::from("image/2.0.0"), + image: String::from("image/2.0.0"), metadata: HashMap::new(), })); graph.dag.add_edge(v1, v2, Empty {}).unwrap(); @@ -391,12 +391,12 @@ mod tests { let mut graph = Graph::default(); let v3 = graph.dag.add_node(Release::Concrete(ConcreteRelease { version: String::from("3.0.0"), - payload: String::from("image/3.0.0"), + image: String::from("image/3.0.0"), metadata: HashMap::new(), })); let v2 = graph.dag.add_node(Release::Concrete(ConcreteRelease { version: String::from("2.0.0"), - payload: String::from("image/2.0.0"), + image: String::from("image/2.0.0"), metadata: HashMap::new(), })); graph.dag.add_edge(v2, v3, Empty {}).unwrap(); @@ -415,18 +415,18 @@ mod tests { fn test_graph_eq_is_agnostic_to_node_and_edge_order() { let r1 = Release::Concrete(ConcreteRelease { version: String::from("1.0.0"), - payload: String::from("image/1.0.0"), + image: String::from("image/1.0.0"), metadata: HashMap::new(), }); let r2 = Release::Concrete(ConcreteRelease { version: String::from("2.0.0"), - payload: String::from("image/2.0.0"), + image: String::from("image/2.0.0"), metadata: HashMap::new(), }); let r3 = Release::Concrete(ConcreteRelease { version: String::from("3.0.0"), - payload: String::from("image/3.0.0"), + image: String::from("image/3.0.0"), metadata: HashMap::new(), }); diff --git a/docs/design/cincinnati.md b/docs/design/cincinnati.md index d3ba5128c..e6f5f007c 100644 --- a/docs/design/cincinnati.md +++ b/docs/design/cincinnati.md @@ -14,14 +14,14 @@ Cincinnati is the successor to the [Omaha update protocol][google-omaha]. It des ## Update Graph ## -Cincinnati uses a [directed acyclic graph][dag] (DAG) to represent the valid updates. Each node in the graph is a release payload and each edge is a valid transition. The Graph Builder is responsible for iterating over the set of releases and building a DAG that can be queried. +Cincinnati uses a [directed acyclic graph][dag] (DAG) to represent the valid updates. Each node in the graph is a release image and each edge is a valid transition. The Graph Builder is responsible for iterating over the set of releases and building a DAG that can be queried. -Release payloads within Cincinnati contain a small JSON-formatted metadata document which declares the following information about the payload: +Release images within Cincinnati contain a small JSON-formatted metadata document which declares the following information about the release: | Key | Optional | Description | |:--------:|:--------:|:----------------------------------------------------------------------------------------| | kind | required | the document type - Must be `cincinnati-metadata-v0` | -| version | required | the version of the release in the current payload | +| version | required | the version of the release in the current image | | previous | optional | the list of valid previous versions [1](#1) | | next | optional | the list of valid next versions [2](#2) | | metadata | optional | an opaque object that allows a release to convey arbitrary information to its consumers | @@ -87,14 +87,14 @@ There are a number of components that make up the Cincinnati update system. Most ### Storage ### -The storage component is responsible for actually storing and hosting the release payloads. The storage implementation is entirely up to this component and the hosting implementation will be coupled to the Graph Builder. Examples of storage include the Docker v2 Registry and Amazon S3. +The storage component is responsible for actually storing and hosting the release images. The storage implementation is entirely up to this component and the hosting implementation will be coupled to the Graph Builder. Examples of storage include the Docker v2 Registry and Amazon S3. ### Graph Builder ### -The Graph Builder iterates over the release payloads hosted by the storage component and builds a DAG of the releases. It is responsible for verifying that the graph described by the releases is acyclic and connected. +The Graph Builder iterates over the release images hosted by the storage component and builds a DAG of the releases. It is responsible for verifying that the graph described by the releases is acyclic and connected. -In order to avoid the Graph Builder from having to serve the full payloads, Cincinnati implementations may choose to instead serve a modified payload which directs the client to download the full release payload directly from the storage component. It is important to note that in these implementations, the Graph Builder should not generate these modified payloads directly. Doing so would directly couple the Graph Builder to the client, which would make it difficult to change the format of the modified payload in future versions while still supporting arbitrarily old clients. +In order to avoid the Graph Builder from having to serve the full images, Cincinnati implementations may choose to instead serve a modified image which directs the client to download the full release image directly from the storage component. It is important to note that in these implementations, the Graph Builder should not generate these modified images directly. Doing so would directly couple the Graph Builder to the client, which would make it difficult to change the format of the modified image in future versions while still supporting arbitrarily old clients. There is no defined interface between the Graph Builder and storage since the Graph Builder will be specific to the storage. There is, however, a [defined interface](#graph-api) between the Graph Builder and the Policy Engine. This is to allow alternate implementations of the Policy Engine including the lack of an implementation altogether. @@ -113,12 +113,12 @@ Clients periodically query a Policy Engine for any updates they should perform. ### Client ### -Cincinnati clients are the end consumers of the release payloads. The clients periodically query the Policy Engine for updates and apply them if available. +Cincinnati clients are the end consumers of the release images. The clients periodically query the Policy Engine for updates and apply them if available. ## Graph API ## -The Graph API defines the interface exposed by implementations of Graph Builders and Policy Engines. This will most commonly be used by Policy Engines to discover the available update payloads, but may be used by visualization utilities or by clients themselves. +The Graph API defines the interface exposed by implementations of Graph Builders and Policy Engines. This will most commonly be used by Policy Engines to discover the available update images, but may be used by visualization utilities or by clients themselves. ### Request ### @@ -133,12 +133,12 @@ Clients may provide additional parameters as URL query parameters in the request ### Response ### -The response to the `/v1/graph` endpoint is a JSON representation of the release graph. Each of the releases are represented in an entry in the top-level `nodes` array. Each of these entries includes the release version label, a payload identifier and any metadata according to the following schema: +The response to the `/v1/graph` endpoint is a JSON representation of the release graph. Each of the releases are represented in an entry in the top-level `nodes` array. Each of these entries includes the release version label, an image identifier and any metadata according to the following schema: | Key | Optional | Description | |:--------:|:--------:|:----------------------------------------------------------------------------------------| | version | required | the version of the release, as a unique (across "nodes" array) non-empty JSON string | -| payload | required | payload identifier, as a JSON string | +| image | required | image identifier, as a JSON string | | metadata | required | an opaque object that allows a release to convey arbitrary information to its consumers | The transitions between releases are represented as an array in the top-level `edges` array. Each of these arrays has two entries: the index of the starting node, and the index of the ending node. Both are non-negative integers, ranging from 0 to `len(nodes)-1`. @@ -154,27 +154,27 @@ The following response represents the graph shown in Figure 2: { "version": "1.0.0", "metadata": {}, - "payload": "quay.io/openshift/manifest:v1.0.0" + "image": "quay.io/openshift/manifest:v1.0.0" }, { "version": "1.1.0", "metadata": { "kind": "security" }, - "payload": "quay.io/openshift/manifest:v1.1.0" + "image": "quay.io/openshift/manifest:v1.1.0" }, { "version": "1.1.1", "metadata": { "kind": "security" }, - "payload": "quay.io/openshift/manifest:v1.1.1" + "image": "quay.io/openshift/manifest:v1.1.1" }, { "version": "1.2.0", "metadata": { "kind": "bug-fix" }, - "payload": "quay.io/openshift/manifest:v1.2.0" + "image": "quay.io/openshift/manifest:v1.2.0" }, { "version": "1.3.0", "metadata": { "kind": "feature" }, - "payload": "quay.io/openshift/manifest:v1.3.0" + "image": "quay.io/openshift/manifest:v1.3.0" } ], "edges": [ @@ -192,4 +192,4 @@ The following response represents the graph shown in Figure 2: ## Alternatives Considered ## * **Continue using Omaha** - Tectonic used an augmented Omaha flow. It made use of the Omaha mechanism for discovering updates, but then instead of following the server's instruction, it fetched a list of all packages from the server and then chose from this list. Other than the initial discover and reporting events, Tectonic did not use any other aspects of the Omaha protocol. -* **No central server** - From a point of correctness, the Cincinnati components are not required for updates. This is because the update payloads include the Cincinnati metadata, which defines the valid transitions. The cluster can validate the update payload before attempting to apply it. Without the policy engine, though, it would be difficult to impose rate-limits on updates (which allows those in charge of rolling out updates to stop updates if a problem is discovered). It would also be difficult to make changes to the policies that govern update paths since each cluster would have to make the decisions themselves if there was no central server. +* **No central server** - From a point of correctness, the Cincinnati components are not required for updates. This is because the update images include the Cincinnati metadata, which defines the valid transitions. The cluster can validate the update image before attempting to apply it. Without the policy engine, though, it would be difficult to impose rate-limits on updates (which allows those in charge of rolling out updates to stop updates if a problem is discovered). It would also be difficult to make changes to the policies that govern update paths since each cluster would have to make the decisions themselves if there was no central server. diff --git a/docs/design/openshift.md b/docs/design/openshift.md index 4a1c17926..c62ce53fa 100644 --- a/docs/design/openshift.md +++ b/docs/design/openshift.md @@ -1,6 +1,6 @@ # Cincinnati in OpenShift # -OpenShift will make use of the [Cincinnati update scheme](cincinnati.md) to provide over-the-air updates to all clusters. Because Cincinnati is a generic scheme, OpenShift specifics will need to be defined. This document serves to document those specifics, which include: the Quay Graph Builder, OpenShift Policy Engine, update payload format, and client update protocol. +OpenShift will make use of the [Cincinnati update scheme](cincinnati.md) to provide over-the-air updates to all clusters. Because Cincinnati is a generic scheme, OpenShift specifics will need to be defined. This document serves to document those specifics, which include: the Quay Graph Builder, OpenShift Policy Engine, update image format, and client update protocol.
Figure 1: An overview of the relationships between the Cincinnati components within OpenShift @@ -10,11 +10,11 @@ OpenShift will make use of the [Cincinnati update scheme](cincinnati.md) to prov ## Key Decisions ## -* **Payloads declare transitions** - In order to ensure the integrity of updates, the declared transitions (a window into the overall DAG) should remain a part of the update payload. Coupling the declaration of valid transitions allows the client to validate that the update was intended for the currently running release. This also makes the future maintenance of a project easier if the DAG can be committed alongside the code. This coupling is the result of the fact that the DAG is really just an easy-to-parse declaration of the intention of the implementation. -* **Decouple payload delivery** - By decoupling the payload delivery mechanism from Cincinnati, it allows many different forms of payload and their delivery to be supported. The Cincinnati server is able to provide metadata about payloads in a uniform manner but leaves the retrieval up to each of the clients. -* **Payload availability declared in Quay** - Using Quay’s image labels to annotate images allows Quay to be treated as the sole source of truth for the Graph Builders. -* **Payload digest and signature is self contained** - If the payload contains its digest and signature, the cluster will be able to verify the authenticity of the payload without any additional metadata. This is a requirement for offline installations since clusters in these environments won’t communicate with the root Policy Engines. -* **Each graph API endpoint represents a single product** - All of the versions and channels in a graph are expected to refer to a related set of content, and all payloads can be assumed to be related. +* **Images declare transitions** - In order to ensure the integrity of updates, the declared transitions (a window into the overall DAG) should remain a part of the update image. Coupling the declaration of valid transitions allows the client to validate that the update was intended for the currently running release. This also makes the future maintenance of a project easier if the DAG can be committed alongside the code. This coupling is the result of the fact that the DAG is really just an easy-to-parse declaration of the intention of the implementation. +* **Decouple image delivery** - By decoupling the image delivery mechanism from Cincinnati, it allows many different forms of image and their delivery to be supported. The Cincinnati server is able to provide metadata about images in a uniform manner but leaves the retrieval up to each of the clients. +* **image availability declared in Quay** - Using Quay’s image labels to annotate images allows Quay to be treated as the sole source of truth for the Graph Builders. +* **image digest and signature is self contained** - If the image contains its digest and signature, the cluster will be able to verify the authenticity of the image without any additional metadata. This is a requirement for offline installations since clusters in these environments won’t communicate with the root Policy Engines. +* **Each graph API endpoint represents a single product** - All of the versions and channels in a graph are expected to refer to a related set of content, and all images can be assumed to be related. ## Update Process ## @@ -119,9 +119,9 @@ The response from the policy engine will conform to the [Cincinnati Graph API re ## Update Format ## -### Update Payload ### +### Update Image ### -The Update Payloads served by Cincinnati to the Cluster Version Operator are formatted as a JSON document containing a reference to a container image. This document is versioned so that it may change over time. An example of this document is shown below: +The Update Images served by Cincinnati to the Cluster Version Operator are formatted as a JSON document containing a reference to a container image. This document is versioned so that it may change over time. An example of this document is shown below: ```json { diff --git a/graph-builder/src/registry.rs b/graph-builder/src/registry.rs index 80b2a1c8a..131d75126 100644 --- a/graph-builder/src/registry.rs +++ b/graph-builder/src/registry.rs @@ -36,7 +36,7 @@ impl Into for Release { fn into(self) -> cincinnati::Release { cincinnati::Release::Concrete(cincinnati::ConcreteRelease { version: self.metadata.version.to_string(), - payload: self.source, + image: self.source, metadata: self.metadata.metadata, }) } diff --git a/policy-engine/src/openapiv3.json b/policy-engine/src/openapiv3.json index 6d86ca88a..13f4855f2 100644 --- a/policy-engine/src/openapiv3.json +++ b/policy-engine/src/openapiv3.json @@ -60,14 +60,14 @@ "Node": { "required": [ "version", - "payload", + "image", "metadata" ], "properties": { "version": { "type": "string" }, - "payload": { + "image": { "type": "string" }, "metadata": {