The WebSocket CDPM Subprotocol

Last updated 3 months ago

The term WebSocket subprotocol refers to an application-level protocol layered on top of a WebSocket connection.

This document specifies the WebSocket CDPM subprotocol for carrying CDPM requests and responses through a WebSocket connection.

4.1 Handshake

The CDPM WebSocket Client and CDPM WebSocket Server negotiate usage of the WebSocket CDPM subprotocol during the WebSocket handshake procedure as defined in Section 1.3 of [RFC6455]. The client MUST include the value "cdpm" in the Sec-WebSocket-Protocol header in its handshake request. The 101 reply from the server MUST contain "cdpm" in its corresponding Sec-WebSocket-Protocol header. The WebSocket client initiates a WebSocket connection when attempting to send a CDPM request (unless there is an already established WebSocket connection for sending the CDPM request). In case there is no HTTP 101 response during the WebSocket handshake, it is considered a transaction error as per [RFC3261], Section 8.1.3.1., "Transaction Layer Errors". Below is an example of a WebSocket handshake in which the client requests the WebSocket CDPM subprotocol support from the server: GET / HTTP/1.1 Host: cdpm-ws.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Origin: http://www.example.com Sec-WebSocket-Protocol: cdpm Sec-WebSocket-Version: 13 The handshake response from the server accepting the WebSocket CDPM subprotocol would look as follows: HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= Sec-WebSocket-Protocol: cdpm

Once the negotiation has been completed, the WebSocket connection is established and can be used for the transport of CDPM requests and responses. Messages other than CDPM requests and responses MUST NOT be transmitted over this connection. 4.2 CDPM Encoding

WebSocket messages can be transported in either UTF-8 text frames or binary frames. CDPM only allows text bodies in CDPM requests and responses. Therefore, CDPM WebSocket Clients and CDPM WebSocket Servers MUST only accept text frames.

Given the nature of JavaScript and the WebSocket API, it is RECOMMENDED to use UTF-8 encoding (or ASCII, which is a subset of UTF-8) for CDPM messages carried over a WebSocket connection.

4.3 CDPM Protocol

Nodes

A pipeline messaging state machine MUST have a node. There MAY be zero, one, or many state machines running on a node. A state machine MUST be designated either a dependency or a dependent state machine. A state machine MUST be assigned a link to a yml file, in source control, with the semantically versioned dependency or dependent source code. A state machine MUST include a rsa256 public key in its yml configuration file (off band). A node must designate its location in its yml configuration file. A state machine’s events MAY be triggered by anything the CDPM implementation deems as a valid event from a corresponding CI/CD system.

Node Configuration Yaml File

A node’s yml file MUST be in source control with the semantically versioned dependency or dependent source code.

GET /rails/rails/blob/master/cdpm.yml HTTP/1.1 Host: github.com

Pipeline-node:

public-key: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJDgYSF6vUpwmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb13j+skZ6UtW+5u09lHNsj6tQ51s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQAB

url: https://rails.ci

Dependencies:

Nokogiri-master:

url: https://github.com/sparklemotion/nokogiri/blob/master/cdpm.yml

Nokogiri-stable:

url: https://github.com/sparklemotion/nokogiri/blob/v1.8.1/cdpm.yml

Dependents:

hulu-master:

url: http://mysourcecontrol.com/hulu/blob/master/cdpm.yml

Messages

Message Name

Message Description

subscribe

HTTP POST to a dependency

subscribed

HTTP Response to a dependent

new release

Build, test, and release finished message to dependent or dependency

vote unstable

Build, test, or release failed message to dependency

vote stable

Release successful message to dependency

Messaging Security

Every message between nodes MUST include a signature signed by the sending node’s rsa 256 public key. Every node that receives a message MUST verify the message using the off band public key originally found sender’s yml file in source control. A dependency MAY process a subscribe message from a dependant node. A dependency MUST verify a subscribe message using the public key found using the link in the subscribe message to the dependant’s sourced controlled yml file. Every dependency node MUST reject any message, other than a subscription message, from unsubscribed nodes. Every dependent node MUST reject any message from nodes not subscribed to.

State Machine

Dependent

example

Hulu

Major

Dependent

example

Rails

Dependency

example

Nokogiri

Event

Node

Subscribe dependency

Dependency

Check-in

Built

Tested

New

release

Tests

Failed

Tests

Success

Dependent

subscribed

building

Stability

vote unstable

vote stable

Project dependency

publishing

building

testing

deploying

production

deployed

demoted

promoted

Dependents

A dependent node MAY subscribe to a dependency node. Subscribable dependency nodes MUST be included as links in a dependent yml configuration file, in source control, versioned with the dependent’s source code. A link to a subscribable dependency MUST be a link to a yml file, in source control, with the semantically versioned dependency source code.

INITIAL state

A dependent state machine MUST be in the initial state when initialized. A dependent state machine in the INITIAL state MAY send a subscribe HTTP POST to a dependency.

Dependent Subscription POST

POST /subscribe HTTP/1.1 Host: nokogirl.ci

{ “message_type”:”subscribe”,

"caller_name": "rails",

"url": "http://www.github.com/rails/rails/blob/master/cdpm.yml", "subscriptions": [ { “ref_type”: “stable”,

“ref”: “v_3.2”, } ], "destination": { "url": "https://github.com/sparklemotion/nokogiri/blob/master/cdpm.yml } }

SUBSCRIBED state

A dependent state machine MUST change into the SUBSCRIBED state after receiving a subscribed message from its dependency AND it is in the initial state.

Dependent Subscribed POST response

{

“message_type”:”subscribed”,

"response": [

{"subscription_id": "e6a9bb54-da25-102b-9a03-2db401e887ec"}

],

"status_code": "200",

"status_msg": "OK",

"response_time_stamp": "2012-06-20T08:36:24"

}

A dependent node in the SUBSCRIBED state MUST negotiate a websocket connection with the dependency node.

BUILDING state

A dependent state machine MUST change into a BUILDING state when receiving a new release message from its dependency.

Dependency

A dependency’s source code MUST include a yml configuration file with the url and port address of the pipeline messaging node. A dependencies yml file MUST designate a reference. A reference MUST be either a branch or a tag. A reference that is a tag MUST be a semantic version.

INITIAL state

A dependency state machine MUST be in the INITIAL state when initialized.

PUBLISHING state

A dependency MAY send a subscribed message to a dependent when receiving a subscribe message. A dependency MUST change its stage from INITIAL to PUBLISHING when it sends a subscribed message to the dependent A dependency MAY send a not subscribed message to the dependent when receiving a subscribe message.

Dependent New Release Subscription POST response

{

“message_type”:”subscribed”,

"response": [

{"subscription_id": "e6a9bb54-da25-102b-9a03-2db401e887ec"}

],

"status_code": "200",

"status_msg": "OK",

"response_time_stamp": "2012-06-20T08:36:24"

}

Source

CHECK-IN state

A dependency MUST change its state to BUILDING when a code check-in event takes place.

TESTING state

A dependency MUST change its state to TESTING when a built event takes place.

DEPLOYING state

A dependency MAY change its state to DEPLOYING when a tested event takes place.

DEPLOYED state

A dependency MUST change its state to DEPLOYED when a new release event takes place. When a dependency has changed into a DEPLOYED state, it must make its artifacts available and then send a NEW RELEASE message to all of its

dependents.

* needs dependency project name, url, event, state, new version, list of urls to artifacts

{

"id": 42,

"ref": "master",

"created_at": "2016-08-11T11:32:35.444Z",

"project_details": {

“Id”: 32,

"name": "nokogiri",

"repo_url": "https://rubygems.org"

“maintainers” : [{

“name”: “joe blow”

“contact”: “joe@email.com”

}],

“dependents”: [“rails”, “sinatra”]

},

"deployable": {

"id": 664,

"status": "success",

"ref": "master",

"created_at": "2016-08-11T11:32:24.456Z",

"commit": {

"id": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",

"short_id": "a91957a8",

"title": "2.3 update",

"author_name": "joe blow",

"author_email": "joe@email.com",

"created_at": "2016-08-11T13:28:26.000+02:00",

"message": "update"

}

}

}

DEMOTED state

A dependency MAY change its state to DEMOTED when one or many vote unstable messages are received.

{

"response": [

{"subscription_id": "e6a9bb54-da25-102b-9a03-2db401e887ec"}

],

"status_code": "200",

"status_msg": "OK",

"response_time_stamp": "2012-06-20T08:36:24"

}

PROMOTED state

A dependency MAY change its state to PROMOTED when one or many vote stable messages are received.

Major Dependents

A dependent MAY be designated as a major dependent by its dependency. A major dependent MUST have an additional ‘stability’ state machine separate from the dependent state machine. A major dependent MUST message its dependency when its stability state changes to VOTE STABLE or VOTE UNSTABLE

INITIAL state

A major dependent MUST be in the INITIAL state when initialized

VOTE STABLE state

A major dependent MUST change its state to VOTE STABLE when tests success event occurs with a dependency reference. A dependent MUST send a vote stable message to the dependency when entering into a vote stable state.

VOTE UNSTABLE state

A major dependent MUST change its state from VOTE STABLE to VOTE UNSTABLE when a tests failure event occurs and the dependent new release event was triggered by a message from the dependency. A major dependent MUST NOT change its state from INITIAL to VOTE UNSTABLE. A dependent MUST send a vote unstable message to the dependency when entering into a vote unstable state.