1+ // Package cincinnati provides a client for interacting with the Cincinnati update service.
2+ //
3+ // Cincinnati is the OpenShift update recommendation service that provides update graphs
4+ // indicating which cluster versions can safely upgrade to which target versions. The service
5+ // returns both unconditional update recommendations (via edges in the update graph) and
6+ // conditional update recommendations (via conditionalEdges with associated risks).
7+ //
8+ // The Client fetches update graphs from an upstream Cincinnati server using the Cincinnati v1
9+ // Graph API. It handles:
10+ // - Querying for available updates based on the current cluster version, architecture, and channel
11+ // - Parsing the update graph response to identify recommended next-hop updates
12+ // - Processing conditional updates that may have associated risks requiring evaluation
13+ // - Handling transitions between single and multi-architecture deployments
14+ //
15+ // Update graphs consist of nodes (representing cluster versions) and edges (representing
16+ // valid upgrade paths). Conditional edges represent upgrades that are only recommended
17+ // when certain risk conditions are evaluated and deemed acceptable.
118package cincinnati
219
320import (
@@ -20,29 +37,40 @@ import (
2037)
2138
2239const (
23- // GraphMediaType is the media- type specified in the HTTP Accept header
24- // of requests sent to the Cincinnati- v1 Graph API.
40+ // GraphMediaType is the media type specified in the HTTP Accept header
41+ // for requests sent to the Cincinnati v1 Graph API.
2542 GraphMediaType = "application/json"
2643
27- // Timeout when calling upstream Cincinnati stack.
44+ // getUpdatesTimeout is the maximum duration allowed for a single request
45+ // to the upstream Cincinnati service.
2846 getUpdatesTimeout = time .Minute * 60
2947)
3048
31- // Client is a Cincinnati client which can be used to fetch update graphs from
32- // an upstream Cincinnati stack.
49+ // Client is a Cincinnati client for fetching update graphs from an upstream
50+ // Cincinnati service. It maintains connection settings and cluster identification
51+ // for making requests to the update service.
3352type Client struct {
34- id uuid.UUID
53+ // id is the unique cluster identifier sent with each update request.
54+ id uuid.UUID
55+
56+ // transport configures HTTP transport settings including TLS configuration,
57+ // proxy settings, and connection pooling.
3558 transport * http.Transport
3659
37- // userAgent configures the User-Agent header for upstream
38- // requests. If empty, the User-Agent header will not be
39- // populated.
60+ // userAgent is the User-Agent header value for upstream requests.
61+ // If empty, the User-Agent header will not be set.
4062 userAgent string
4163
64+ // conditionRegistry evaluates conditional update risks and prunes invalid
65+ // matching rules before storing conditional updates.
4266 conditionRegistry clusterconditions.ConditionRegistry
4367}
4468
45- // NewClient creates a new Cincinnati client with the given client identifier.
69+ // NewClient creates a new Cincinnati client with the specified configuration.
70+ // The id parameter uniquely identifies the cluster making update requests.
71+ // The transport parameter configures HTTP settings including TLS and proxy configuration.
72+ // The userAgent parameter sets the User-Agent header for requests (optional).
73+ // The conditionRegistry parameter is used to evaluate and prune conditional update risks.
4674func NewClient (id uuid.UUID , transport * http.Transport , userAgent string , conditionRegistry clusterconditions.ConditionRegistry ) Client {
4775 return Client {
4876 id : id ,
@@ -52,15 +80,17 @@ func NewClient(id uuid.UUID, transport *http.Transport, userAgent string, condit
5280 }
5381}
5482
55- // Error is returned when are unable to get updates .
83+ // Error represents a failure when fetching updates from the Cincinnati service .
5684type Error struct {
57- // Reason is the reason suggested for the ClusterOperator status condition.
85+ // Reason is the machine-readable reason code for the ClusterVersion
86+ // RetrievedUpdates condition (e.g., "RemoteFailed", "ResponseInvalid").
5887 Reason string
5988
60- // Message is the message suggested for the ClusterOperator status condition.
89+ // Message is the human-readable error message for the ClusterVersion
90+ // RetrievedUpdates condition.
6191 Message string
6292
63- // cause is the upstream error, if any, being wrapped by this error .
93+ // cause is the underlying error that triggered this failure, if any .
6494 cause error
6595}
6696
@@ -69,19 +99,35 @@ func (err *Error) Error() string {
6999 return fmt .Sprintf ("%s: %s" , err .Reason , err .Message )
70100}
71101
72- // GetUpdates fetches the current and next-applicable update payloads from the specified
73- // upstream Cincinnati stack given the current version, desired architecture, and channel.
74- // The command:
102+ // GetUpdates retrieves available cluster updates from the Cincinnati service.
103+ // It returns the current release information, a list of unconditionally recommended
104+ // updates, a list of conditionally recommended updates, and any error encountered.
75105//
76- // 1. Downloads the update graph from the requested URI for the requested desired arch and channel.
77- // 2. Finds the current version entry under .nodes.
78- // 3. If a transition from single to multi architecture has been requested, the only valid
79- // version is the current version so it's returned.
80- // 4. Finds recommended next-hop updates by searching .edges for updates from the current
81- // version. Returns a slice of target Releases with these unconditional recommendations.
82- // 5. Finds conditionally recommended next-hop updates by searching .conditionalEdges for
83- // updates from the current version. Returns a slice of ConditionalUpdates with these
84- // conditional recommendations.
106+ // The method performs the following steps:
107+ // 1. Constructs a query to the Cincinnati service including the cluster architecture,
108+ // update channel, cluster ID, and current version.
109+ // 2. Downloads and parses the update graph from the service.
110+ // 3. Locates the current version within the graph nodes.
111+ // 4. For single-to-multi architecture transitions, returns only the current version
112+ // in multi-architecture form as the sole valid update.
113+ // 5. Identifies unconditional update recommendations by following edges from the
114+ // current version to destination nodes.
115+ // 6. Identifies conditional update recommendations from conditionalEdges, filtering
116+ // out duplicates and pruning invalid risk matching rules.
117+ //
118+ // Parameters:
119+ // - ctx: Context for the HTTP request, allowing cancellation
120+ // - uri: Base URI of the Cincinnati service
121+ // - desiredArch: Target architecture for updates (e.g., "amd64", "Multi")
122+ // - currentArch: Current cluster architecture
123+ // - channel: Update channel name (e.g., "stable-4.14", "fast-4.15")
124+ // - version: Current semantic version of the cluster
125+ //
126+ // Returns:
127+ // - Current release metadata from the update graph
128+ // - Slice of unconditionally recommended update releases (nil if none)
129+ // - Slice of conditionally recommended updates with associated risks (nil if none)
130+ // - Error if the request fails or the response is invalid
85131func (c Client ) GetUpdates (ctx context.Context , uri * url.URL , desiredArch , currentArch , channel string ,
86132 version semver.Version ) (configv1.Release , []configv1.Release , []configv1.ConditionalUpdate , error ) {
87133
@@ -283,36 +329,69 @@ func (c Client) GetUpdates(ctx context.Context, uri *url.URL, desiredArch, curre
283329 return current , updates , conditionalUpdates , nil
284330}
285331
332+ // graph represents the update graph structure returned by the Cincinnati service.
333+ // It defines all available cluster versions and the valid upgrade paths between them.
286334type graph struct {
287- Nodes []node
288- Edges []edge
335+ // Nodes contains all cluster version releases available in this channel.
336+ Nodes []node
337+
338+ // Edges defines unconditional upgrade paths as index pairs referencing Nodes.
339+ // Each edge indicates a recommended upgrade from one version to another.
340+ Edges []edge
341+
342+ // ConditionalEdges defines upgrade paths that require risk evaluation.
343+ // These upgrades are only recommended if their associated risks are acceptable.
289344 ConditionalEdges []conditionalEdges `json:"conditionalEdges"`
290345}
291346
347+ // node represents a single cluster version in the update graph.
292348type node struct {
293- Version semver.Version `json:"version"`
294- Image string `json:"payload"`
349+ // Version is the semantic version of this release.
350+ Version semver.Version `json:"version"`
351+
352+ // Image is the release image pullspec (payload) for this version.
353+ Image string `json:"payload"`
354+
355+ // Metadata contains additional release information such as URL, architecture,
356+ // and supported update channels.
295357 Metadata map [string ]interface {} `json:"metadata,omitempty"`
296358}
297359
360+ // edge represents an unconditional upgrade path between two versions in the graph.
361+ // It is serialized as a two-element array [origin, destination] in JSON.
298362type edge struct {
299- Origin int
363+ // Origin is the index of the source version node.
364+ Origin int
365+
366+ // Destination is the index of the target version node.
300367 Destination int
301368}
302369
370+ // conditionalEdge represents a single conditional upgrade path between two versions
371+ // identified by their version strings rather than node indices.
303372type conditionalEdge struct {
373+ // From is the semantic version string of the source release.
304374 From string `json:"from"`
305- To string `json:"to"`
375+
376+ // To is the semantic version string of the target release.
377+ To string `json:"to"`
306378}
307379
380+ // conditionalEdges groups a set of conditional upgrade edges with their shared risks.
381+ // All edges in this group are subject to the same risk conditions.
308382type conditionalEdges struct {
309- Edges []conditionalEdge `json:"edges"`
383+ // Edges contains the conditional upgrade paths sharing these risks.
384+ Edges []conditionalEdge `json:"edges"`
385+
386+ // Risks defines the conditions that must be evaluated to determine if
387+ // these conditional updates are recommended for a particular cluster.
310388 Risks []configv1.ConditionalUpdateRisk `json:"risks"`
311389}
312390
313- // UnmarshalJSON unmarshals an edge in the update graph. The edge's JSON
314- // representation is a two-element array of indices, but Go's representation is
315- // a struct with two elements so this custom unmarshal method is required.
391+ // UnmarshalJSON deserializes an edge from its JSON representation.
392+ // Edges are represented in JSON as two-element arrays [origin, destination],
393+ // but are stored in Go as a struct with named fields, requiring this custom
394+ // unmarshaling logic.
316395func (e * edge ) UnmarshalJSON (data []byte ) error {
317396 var fields []int
318397 if err := json .Unmarshal (data , & fields ); err != nil {
@@ -329,14 +408,23 @@ func (e *edge) UnmarshalJSON(data []byte) error {
329408 return nil
330409}
331410
411+ // convertRetrievedUpdateToRelease converts a Cincinnati graph node to a ClusterVersion Release.
412+ // It combines the node's version and image with metadata parsed from the node's metadata map.
332413func convertRetrievedUpdateToRelease (update node ) (configv1.Release , error ) {
333414 release , err := ParseMetadata (update .Metadata )
334415 release .Version = update .Version .String ()
335416 release .Image = update .Image
336417 return release , err
337418}
338419
339- // ParseMetadata parses release metadata (URL, channels, etc.). It does not populate the version or image properties.
420+ // ParseMetadata extracts release metadata from a node's metadata map.
421+ // It parses the release URL, architecture, and supported update channels.
422+ // The version and image fields are not populated by this function and must
423+ // be set separately by the caller.
424+ //
425+ // Returns a partially populated Release struct and an aggregated error containing
426+ // all parsing errors encountered. Parsing continues even after errors to extract
427+ // as much valid metadata as possible.
340428func ParseMetadata (metadata map [string ]interface {}) (configv1.Release , error ) {
341429 release := configv1.Release {}
342430 errs := []error {}
0 commit comments