Compare commits
3 Commits
921970c8c2
...
25a2bf6fb6
Author | SHA1 | Date |
---|---|---|
R Tyler Croy | 25a2bf6fb6 | |
R Tyler Croy | b89e1112e0 | |
R Tyler Croy | a598519f4d |
|
@ -7,3 +7,4 @@ build/
|
||||||
.otto-ebc-history
|
.otto-ebc-history
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
tmp*
|
tmp*
|
||||||
|
.hypothesis/
|
||||||
|
|
|
@ -1,200 +0,0 @@
|
||||||
---
|
|
||||||
swagger: '2.0'
|
|
||||||
info:
|
|
||||||
description: 'This specification describes the Otto event bus'
|
|
||||||
version: '1.0.0'
|
|
||||||
title: Otto Event Bus
|
|
||||||
contact:
|
|
||||||
email: 'rtyler@brokenco.de'
|
|
||||||
license:
|
|
||||||
name: 'GNU AGPL 3.0'
|
|
||||||
url: 'https://www.gnu.org/licenses/agpl-3.0.en.html'
|
|
||||||
host: 'localhost:8080'
|
|
||||||
externalDocs:
|
|
||||||
description: 'Find out more about Otto'
|
|
||||||
url: 'https://github.com/rtyler/otto'
|
|
||||||
basePath: '/v1'
|
|
||||||
schemes:
|
|
||||||
- 'https'
|
|
||||||
- 'http'
|
|
||||||
|
|
||||||
parameters:
|
|
||||||
channelName:
|
|
||||||
name: name
|
|
||||||
description: 'The named identifier of an event channel'
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
example: 'inbound-webooks'
|
|
||||||
|
|
||||||
channelConsumer:
|
|
||||||
name: consumer
|
|
||||||
description: 'The named identifier of an event consumer'
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
example: 'hooks-consumer-0a'
|
|
||||||
|
|
||||||
produces:
|
|
||||||
- 'application/json'
|
|
||||||
|
|
||||||
paths:
|
|
||||||
/channel:
|
|
||||||
get:
|
|
||||||
summary: 'List existing channels in the event bus'
|
|
||||||
description: |
|
|
||||||
Enumerate all the channels visible and available to the current client's permission scope
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: 'Channels successfully listed'
|
|
||||||
schema:
|
|
||||||
type: 'array'
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/Channel'
|
|
||||||
400:
|
|
||||||
description: 'Invalid request'
|
|
||||||
|
|
||||||
/channel/{name}:
|
|
||||||
get:
|
|
||||||
summary: 'Fetch the metadata about a specific channel'
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/parameters/channelName'
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: 'Successful retrieval of metadata'
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/Channel'
|
|
||||||
400:
|
|
||||||
description: 'Invalid formatted channel name or request'
|
|
||||||
403:
|
|
||||||
description: 'User is not authorized to access the channel'
|
|
||||||
404:
|
|
||||||
description: 'Could not find the named channel'
|
|
||||||
|
|
||||||
|
|
||||||
put:
|
|
||||||
summary: 'Publish an item to the channel'
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/parameters/channelName'
|
|
||||||
responses:
|
|
||||||
201:
|
|
||||||
description: 'Successful publish of the item'
|
|
||||||
403:
|
|
||||||
description: 'User is not authorized to publish to the channel'
|
|
||||||
404:
|
|
||||||
description: 'Could not find the named channel'
|
|
||||||
|
|
||||||
post:
|
|
||||||
summary: 'Create a channel'
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/parameters/channelName'
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: 'Channel created successfully'
|
|
||||||
400:
|
|
||||||
description: 'Suggested channel configuration was invalid'
|
|
||||||
403:
|
|
||||||
description: 'User is not authorized to create a channel'
|
|
||||||
|
|
||||||
patch:
|
|
||||||
summary: 'Modify the channel configuration'
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/parameters/channelName'
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: 'Successful update of the channel'
|
|
||||||
400:
|
|
||||||
description: 'Suggested channel configuration was invalid'
|
|
||||||
403:
|
|
||||||
description: 'User is not authorized to modify the channel'
|
|
||||||
404:
|
|
||||||
description: 'Could not find the named channel'
|
|
||||||
|
|
||||||
/channel/{name}/{offset}:
|
|
||||||
get:
|
|
||||||
summary: 'Fetch an item from the channel'
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/parameters/channelName'
|
|
||||||
- name: offset
|
|
||||||
description: 'The offset at which the item is located in the channel'
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: 'Successful fetch of the item'
|
|
||||||
404:
|
|
||||||
description: 'Could not find the named channel'
|
|
||||||
416:
|
|
||||||
description: 'Could not find an item at the given offset'
|
|
||||||
|
|
||||||
/offset/{consumer}:
|
|
||||||
get:
|
|
||||||
summary: 'List offset metadata about a named consumer'
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/parameters/channelConsumer'
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: 'Successful access of the consumer metadata'
|
|
||||||
400:
|
|
||||||
description: 'Improperly formatted consumer name'
|
|
||||||
403:
|
|
||||||
description: 'User is not authorized to access this consumer'
|
|
||||||
404:
|
|
||||||
description: 'Could not find the named consumer'
|
|
||||||
|
|
||||||
post:
|
|
||||||
summary: 'Create a named consumer to store metadata'
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/parameters/channelConsumer'
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: 'Successful creation of the named consumer'
|
|
||||||
400:
|
|
||||||
description: 'Improperly formatted consumer metadata'
|
|
||||||
403:
|
|
||||||
description: 'User is not authorized to create a consumer'
|
|
||||||
409:
|
|
||||||
description: 'The named consumer already exists and is in use'
|
|
||||||
|
|
||||||
patch:
|
|
||||||
summary: 'Update the offset for the named consumer'
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/parameters/channelConsumer'
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: 'Successful modification of the consumer metadata'
|
|
||||||
400:
|
|
||||||
description: 'Improperly formatted metadata'
|
|
||||||
403:
|
|
||||||
description: 'User is not authorized to modify this consumer'
|
|
||||||
404:
|
|
||||||
description: 'Could not find the named consumer'
|
|
||||||
definitions:
|
|
||||||
Channel:
|
|
||||||
type: object
|
|
||||||
xml:
|
|
||||||
name: 'Channel'
|
|
||||||
properties:
|
|
||||||
id:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
description: 'Name for the channel'
|
|
||||||
example: 'inbound-hooks'
|
|
||||||
consumers:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
description: 'Number of current consumers'
|
|
||||||
updatedAt:
|
|
||||||
type: string
|
|
||||||
format: date-time
|
|
||||||
description: 'Last time the channel metadata was updated'
|
|
||||||
status:
|
|
||||||
type: string
|
|
||||||
description: "The channel's status"
|
|
||||||
enum:
|
|
||||||
- 'ready'
|
|
||||||
- 'unavailable'
|
|
|
@ -1,117 +0,0 @@
|
||||||
---
|
|
||||||
swagger: '2.0'
|
|
||||||
info:
|
|
||||||
description: 'This specification describes the Otto orchestrator'
|
|
||||||
version: '1.0.0'
|
|
||||||
title: Otto Orchestrator
|
|
||||||
contact:
|
|
||||||
email: 'rtyler@brokenco.de'
|
|
||||||
license:
|
|
||||||
name: 'GNU AGPL 3.0'
|
|
||||||
url: 'https://www.gnu.org/licenses/agpl-3.0.en.html'
|
|
||||||
host: 'localhost:3030'
|
|
||||||
externalDocs:
|
|
||||||
description: 'Find out more about Otto'
|
|
||||||
url: 'https://github.com/rtyler/otto'
|
|
||||||
basePath: '/v1'
|
|
||||||
schemes:
|
|
||||||
- 'http'
|
|
||||||
paths:
|
|
||||||
/manifest/{agentId}:
|
|
||||||
get:
|
|
||||||
summary: 'Fetch manifest for execution by the given agent'
|
|
||||||
description: |
|
|
||||||
Return the full execution manifest for the given agent to execute.
|
|
||||||
operationId: 'fetchManifest'
|
|
||||||
produces:
|
|
||||||
- 'application/json'
|
|
||||||
parameters:
|
|
||||||
- name: agentId
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
x-example: otto-agent-1
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: 'Agent ID found and manifest generated'
|
|
||||||
# https://github.com/apiaryio/dredd/issues/553#issuecomment-412265413
|
|
||||||
headers:
|
|
||||||
Content-Type:
|
|
||||||
type: string
|
|
||||||
default: application/json; charset=utf-8
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/Manifest'
|
|
||||||
400:
|
|
||||||
description: 'Invalid request'
|
|
||||||
definitions:
|
|
||||||
Manifest:
|
|
||||||
type: object
|
|
||||||
description: 'Agent execution manifest'
|
|
||||||
xml:
|
|
||||||
name: 'Manifest'
|
|
||||||
properties:
|
|
||||||
self:
|
|
||||||
type: string
|
|
||||||
description: 'The identifier of the agent'
|
|
||||||
services:
|
|
||||||
type: object
|
|
||||||
$ref: '#/definitions/Service'
|
|
||||||
ops:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/Operation'
|
|
||||||
x-example:
|
|
||||||
self: 'otto-agent-1'
|
|
||||||
services:
|
|
||||||
$ref: '#/definitions/Service'
|
|
||||||
ops:
|
|
||||||
$ref: '#/definitions/Operation'
|
|
||||||
|
|
||||||
Service:
|
|
||||||
type: object
|
|
||||||
description: 'A service ID to URl mapping'
|
|
||||||
xml:
|
|
||||||
name: 'Service'
|
|
||||||
properties:
|
|
||||||
identifier:
|
|
||||||
type: string
|
|
||||||
description: 'Key to identify the different services'
|
|
||||||
url:
|
|
||||||
type: string
|
|
||||||
description: 'Resolvable URL to access APIs for the given service'
|
|
||||||
example:
|
|
||||||
datastore: 'http://localhost:3031/'
|
|
||||||
|
|
||||||
Operation:
|
|
||||||
type: object
|
|
||||||
description: 'A discrete idempotent operation'
|
|
||||||
properties:
|
|
||||||
id:
|
|
||||||
type: string
|
|
||||||
description: 'Globally unique ID to identify this specific operation in data stores, etc'
|
|
||||||
context:
|
|
||||||
type: string
|
|
||||||
description: 'Generally unique context ID to group different operations in the same context'
|
|
||||||
type:
|
|
||||||
type: string
|
|
||||||
description: 'Type of operation'
|
|
||||||
$ref: '#/definitions/OperationType'
|
|
||||||
data:
|
|
||||||
type: object
|
|
||||||
description: 'Operation type-specific data for the agent to use'
|
|
||||||
example:
|
|
||||||
id: '0xdeadbeef'
|
|
||||||
context: '0x1'
|
|
||||||
type: 'RUNPROC'
|
|
||||||
data:
|
|
||||||
script: 'echo "Hello World"'
|
|
||||||
env:
|
|
||||||
timeout_s: 600
|
|
||||||
|
|
||||||
OperationType:
|
|
||||||
type: string
|
|
||||||
description: 'Specific type of the given operation, implies different `data` fields'
|
|
||||||
enum:
|
|
||||||
- 'BEGINCTX'
|
|
||||||
- 'ENDCTX'
|
|
||||||
- 'RUNPROC'
|
|
|
@ -1,4 +0,0 @@
|
||||||
---
|
|
||||||
orchestrator:
|
|
||||||
host: OR_HOST
|
|
||||||
port: OR_PORT
|
|
|
@ -1,4 +0,0 @@
|
||||||
---
|
|
||||||
orchestrator:
|
|
||||||
host: localhost
|
|
||||||
port: 3030
|
|
22
eventbus.yml
22
eventbus.yml
|
@ -1,22 +0,0 @@
|
||||||
# This file is an example otto-eventbus configuration file
|
|
||||||
---
|
|
||||||
motd: 'Starting otto-eventbus'
|
|
||||||
# Time in seconds for the heartbeat to pulse on all connected clients
|
|
||||||
heartbeat: 30
|
|
||||||
# Channels are available for different types of information. Clients are able
|
|
||||||
# to read from some, none, or all channels.
|
|
||||||
#
|
|
||||||
# NOTE: there is a default "all" channel which is used to broadcast information
|
|
||||||
# to all clients.
|
|
||||||
#
|
|
||||||
# WARN: Channels are not merged with the default configuration, and will overwrite
|
|
||||||
# channels:
|
|
||||||
# stateful:
|
|
||||||
# - audit
|
|
||||||
# - tasks.for_auction
|
|
||||||
# - tasks.auction
|
|
||||||
# - tasks.bids
|
|
||||||
# stateless:
|
|
||||||
# - all
|
|
||||||
# - tasks.started
|
|
||||||
# - tasks.finished
|
|
|
@ -1,39 +0,0 @@
|
||||||
# List of .g4 files to compile
|
|
||||||
GRAMMAR=Otto.g4 OttoLexer.g4
|
|
||||||
# Target languages for the grammars
|
|
||||||
LANGS=JavaScript Java Cpp Go
|
|
||||||
# Antlr binary for execution
|
|
||||||
ANTLR_BIN=antlr-4.7.2-complete.jar
|
|
||||||
ANTLR=../contrib/$(ANTLR_BIN)
|
|
||||||
|
|
||||||
################################################################################
|
|
||||||
## Phony targets
|
|
||||||
################################################################################
|
|
||||||
|
|
||||||
# Cute hack thanks to:
|
|
||||||
# https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
|
|
||||||
help: ## Display this help text
|
|
||||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
|
||||||
|
|
||||||
|
|
||||||
build: $(GRAMMAR) $(ANTLR) ## Compile the grammars into their language stubs
|
|
||||||
@for target in $(LANGS); do \
|
|
||||||
java -cp $(ANTLR) org.antlr.v4.Tool \
|
|
||||||
-Dlanguage=$$target \
|
|
||||||
-o build/parser/$$target \
|
|
||||||
$(GRAMMAR); \
|
|
||||||
echo "--> Generated $$target stubs"; \
|
|
||||||
done;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
clean: ## Clean all temporary/working files
|
|
||||||
rm -f $(ANTLR)
|
|
||||||
|
|
||||||
|
|
||||||
.PHONY: help
|
|
||||||
################################################################################
|
|
||||||
|
|
||||||
|
|
||||||
$(ANTLR): ## Download the latest ANTLR4 binary
|
|
||||||
wget -O $(ANTLR) https://www.antlr.org/download/$(ANTLR_BIN)
|
|
272
grammar/Otto.g4
272
grammar/Otto.g4
|
@ -1,272 +0,0 @@
|
||||||
/*
|
|
||||||
* This file contains the parser for the Otto description language
|
|
||||||
*
|
|
||||||
* This is to be considered the reference grammar for all .otto files
|
|
||||||
*/
|
|
||||||
parser grammar Otto;
|
|
||||||
|
|
||||||
options {
|
|
||||||
tokenVocab=OttoLexer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start rule to parse the .otto pipeline declaration
|
|
||||||
pipeline
|
|
||||||
: use_block?
|
|
||||||
configure_block?
|
|
||||||
envs_block?
|
|
||||||
pipeline_block
|
|
||||||
;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The use {} block helps bring user defined libraries into scope for the
|
|
||||||
* runtime of the pipeline, but does not influence parse time
|
|
||||||
*
|
|
||||||
* Example:
|
|
||||||
use {
|
|
||||||
stdlib
|
|
||||||
}
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
use_block
|
|
||||||
: USE BEGIN statements? END
|
|
||||||
;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The configure {} block allows the user to configure libraries or other
|
|
||||||
* pipeline-specific settings.
|
|
||||||
*
|
|
||||||
* Example:
|
|
||||||
configure {
|
|
||||||
slack {
|
|
||||||
channel = '#otto'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
configure_block
|
|
||||||
: CONFIGURE BEGIN setting_block* END
|
|
||||||
;
|
|
||||||
|
|
||||||
/* The environments {} block allows the definition of logical environments for
|
|
||||||
* the pipeline to deliver into
|
|
||||||
*
|
|
||||||
* Example:
|
|
||||||
environments {
|
|
||||||
preprod {
|
|
||||||
settings {
|
|
||||||
HOSTNAME = "preprod-ottoapp.herokuapp.com"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
envs_block
|
|
||||||
: ENVIRONMENTS BEGIN env_block+ END
|
|
||||||
;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Handling an identified environment block.
|
|
||||||
*
|
|
||||||
* This block is typically responsible for configuring a single target
|
|
||||||
* environment for the delivery of this pipeline.
|
|
||||||
*
|
|
||||||
* Example:
|
|
||||||
preprod {
|
|
||||||
settings {
|
|
||||||
HOSTNAME = "preprod-ottoapp.herokuapp.com"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
env_block
|
|
||||||
: ID BEGIN settings_block? END
|
|
||||||
;
|
|
||||||
settings_block
|
|
||||||
: SETTINGS BEGIN settings? END
|
|
||||||
;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set settings for an identified subcomponent
|
|
||||||
*
|
|
||||||
* Example:
|
|
||||||
slack {
|
|
||||||
channel = '#otto'
|
|
||||||
}
|
|
||||||
*
|
|
||||||
* The identified subcomponent is not expected to be known at parse time, but
|
|
||||||
* should be looked up when the parsed .otto file has been processed to ensure
|
|
||||||
* that a corresponding subcomponent is available
|
|
||||||
*/
|
|
||||||
setting_block
|
|
||||||
: ID BEGIN settings? END
|
|
||||||
;
|
|
||||||
settings
|
|
||||||
: setting+
|
|
||||||
;
|
|
||||||
setting
|
|
||||||
: ID ASSIGN (StringLiteral | array | macro | macroKeywords)
|
|
||||||
;
|
|
||||||
array
|
|
||||||
: ARRAY_START (StringLiteral COMMA?)+ ARRAY_END
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The pipeline {} block contains the main execution definition of the
|
|
||||||
* pipeline. Roughly modeled after the Jenkins Pipeline declarative syntax.
|
|
||||||
*/
|
|
||||||
pipeline_block
|
|
||||||
: PIPELINE BEGIN stages_block END
|
|
||||||
;
|
|
||||||
|
|
||||||
stages_block
|
|
||||||
: STAGES BEGIN (macro? stages macro?)+ END
|
|
||||||
;
|
|
||||||
stages
|
|
||||||
: STAGE BEGIN stageStatements* END
|
|
||||||
;
|
|
||||||
|
|
||||||
stageStatements
|
|
||||||
: settings
|
|
||||||
| steps
|
|
||||||
| runtime
|
|
||||||
| cache
|
|
||||||
| gates
|
|
||||||
| deployExpr
|
|
||||||
| notifyExpr
|
|
||||||
| macro+
|
|
||||||
// And finally, allow nesting our stages!
|
|
||||||
| stages+
|
|
||||||
;
|
|
||||||
steps
|
|
||||||
: STEPS BEGIN statements+ END
|
|
||||||
;
|
|
||||||
cache
|
|
||||||
: CACHE BEGIN
|
|
||||||
(
|
|
||||||
(setting+)
|
|
||||||
| fromExpr
|
|
||||||
| cacheUseExpr
|
|
||||||
)
|
|
||||||
END
|
|
||||||
;
|
|
||||||
/*
|
|
||||||
* cache {} `use` expressions allow stages to pull in cached entries from
|
|
||||||
* elsewhere
|
|
||||||
*/
|
|
||||||
cacheUseExpr
|
|
||||||
: USE ID
|
|
||||||
;
|
|
||||||
|
|
||||||
runtime
|
|
||||||
: RUNTIME BEGIN
|
|
||||||
(
|
|
||||||
setting_block
|
|
||||||
| fromExpr
|
|
||||||
)
|
|
||||||
END
|
|
||||||
;
|
|
||||||
/*
|
|
||||||
* XXX: This syntax requires some test coverage to ensure that the grammar
|
|
||||||
* allows for order independence properly, while still restricting only a
|
|
||||||
* single enter block, for example
|
|
||||||
*/
|
|
||||||
gates
|
|
||||||
: GATES BEGIN
|
|
||||||
(
|
|
||||||
enter
|
|
||||||
| exit
|
|
||||||
| fromExpr
|
|
||||||
)+
|
|
||||||
END
|
|
||||||
;
|
|
||||||
enter
|
|
||||||
: ENTER BEGIN enterExpr+ END
|
|
||||||
;
|
|
||||||
exit
|
|
||||||
: EXIT BEGIN exitExpr+ END
|
|
||||||
;
|
|
||||||
enterExpr
|
|
||||||
: (BRANCH EQUALS StringLiteral)
|
|
||||||
| statements
|
|
||||||
| setting_block
|
|
||||||
;
|
|
||||||
exitExpr
|
|
||||||
: statements
|
|
||||||
| setting_block
|
|
||||||
;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A "deployment expression" signifies that the output of the given context
|
|
||||||
* will result in binaries or some form of delivery to the environment being
|
|
||||||
* pointed to
|
|
||||||
*/
|
|
||||||
deployExpr
|
|
||||||
: ENVIRONMENT TO ID
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
notifyExpr
|
|
||||||
: NOTIFY BEGIN
|
|
||||||
(
|
|
||||||
(SUCCESS | FAILURE | COMPLETE)
|
|
||||||
BEGIN
|
|
||||||
statements+
|
|
||||||
END
|
|
||||||
)+
|
|
||||||
END
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A "from" expression is a shorthand in the syntax for coping the contents of
|
|
||||||
* another block of "this" type, from another stage or location
|
|
||||||
*
|
|
||||||
* For exmaple, if one stage in the pipeline has a `cache` configuration
|
|
||||||
* defined, a later stage can use: cache { from 'StageA' } to copy the settings
|
|
||||||
* over verbatim
|
|
||||||
*/
|
|
||||||
fromExpr
|
|
||||||
: FROM StringLiteral
|
|
||||||
;
|
|
||||||
|
|
||||||
statements
|
|
||||||
: statement+
|
|
||||||
;
|
|
||||||
statement
|
|
||||||
: keyword
|
|
||||||
| step
|
|
||||||
| StringLiteral
|
|
||||||
;
|
|
||||||
|
|
||||||
step
|
|
||||||
: ID StringLiteral
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Macro expressions can be a single line, or a single line with a block
|
|
||||||
* attached
|
|
||||||
*/
|
|
||||||
macro
|
|
||||||
: ID OPEN macroArguments CLOSE
|
|
||||||
(BEGIN (stages+)? END)?
|
|
||||||
;
|
|
||||||
macroArguments
|
|
||||||
:
|
|
||||||
(
|
|
||||||
(StringLiteral | macroKeywords)
|
|
||||||
COMMA?
|
|
||||||
)+
|
|
||||||
;
|
|
||||||
macroKeywords
|
|
||||||
: IT
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Keywords are expected to be semantically important after parse time and
|
|
||||||
* effectively represent reserved words in the .otto language
|
|
||||||
*/
|
|
||||||
keyword
|
|
||||||
: STDLIB
|
|
||||||
;
|
|
|
@ -1,112 +0,0 @@
|
||||||
lexer grammar OttoLexer;
|
|
||||||
|
|
||||||
USE : 'use';
|
|
||||||
CONFIGURE : 'configure';
|
|
||||||
ENVIRONMENTS : 'environments';
|
|
||||||
ENVIRONMENT : 'environment';
|
|
||||||
SETTINGS : 'settings';
|
|
||||||
PIPELINE : 'pipeline';
|
|
||||||
STAGES : 'stages';
|
|
||||||
STAGE : 'stage';
|
|
||||||
STEPS : 'steps';
|
|
||||||
CACHE : 'cache';
|
|
||||||
RUNTIME : 'runtime';
|
|
||||||
|
|
||||||
NOTIFY : 'notify';
|
|
||||||
SUCCESS : 'success';
|
|
||||||
FAILURE : 'failure';
|
|
||||||
COMPLETE : 'complete';
|
|
||||||
|
|
||||||
GATES : 'gates';
|
|
||||||
ENTER : 'enter';
|
|
||||||
EXIT : 'exit';
|
|
||||||
BRANCH : 'branch';
|
|
||||||
EQUALS : '==';
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The "to" token helps signify the output of the current context going "to" a
|
|
||||||
* designated environment
|
|
||||||
*/
|
|
||||||
TO : '->';
|
|
||||||
|
|
||||||
FROM : 'from';
|
|
||||||
|
|
||||||
// Keyword tokens
|
|
||||||
STDLIB: 'stdlib';
|
|
||||||
|
|
||||||
// Begin block
|
|
||||||
BEGIN : '{';
|
|
||||||
// End block
|
|
||||||
END : '}';
|
|
||||||
OPEN : '(';
|
|
||||||
CLOSE : ')';
|
|
||||||
ARRAY_START : '[';
|
|
||||||
ARRAY_END : ']';
|
|
||||||
COMMA : ',';
|
|
||||||
ASSIGN : '=';
|
|
||||||
|
|
||||||
IT : 'it';
|
|
||||||
|
|
||||||
StringLiteral: ('"' DoubleStringCharacter* '"'
|
|
||||||
| '\'' SingleStringCharacter* '\'')
|
|
||||||
;
|
|
||||||
|
|
||||||
fragment DoubleStringCharacter
|
|
||||||
: ~["\\\r\n]
|
|
||||||
| '\\' EscapeSequence
|
|
||||||
| LineContinuation
|
|
||||||
;
|
|
||||||
fragment SingleStringCharacter
|
|
||||||
: ~['\\\r\n]
|
|
||||||
| '\\' EscapeSequence
|
|
||||||
| LineContinuation
|
|
||||||
;
|
|
||||||
fragment EscapeSequence
|
|
||||||
: CharacterEscapeSequence
|
|
||||||
| '0' // no digit ahead! TODO
|
|
||||||
| HexEscapeSequence
|
|
||||||
| UnicodeEscapeSequence
|
|
||||||
| ExtendedUnicodeEscapeSequence
|
|
||||||
;
|
|
||||||
|
|
||||||
fragment CharacterEscapeSequence
|
|
||||||
: SingleEscapeCharacter
|
|
||||||
| NonEscapeCharacter
|
|
||||||
;
|
|
||||||
fragment HexEscapeSequence
|
|
||||||
: 'x' HexDigit HexDigit
|
|
||||||
;
|
|
||||||
fragment UnicodeEscapeSequence
|
|
||||||
: 'u' HexDigit HexDigit HexDigit HexDigit
|
|
||||||
;
|
|
||||||
fragment ExtendedUnicodeEscapeSequence
|
|
||||||
: 'u' '{' HexDigit+ '}'
|
|
||||||
;
|
|
||||||
|
|
||||||
fragment HexDigit
|
|
||||||
: [0-9a-fA-F]
|
|
||||||
;
|
|
||||||
fragment SingleEscapeCharacter
|
|
||||||
: ['"\\bfnrtv]
|
|
||||||
;
|
|
||||||
fragment NonEscapeCharacter
|
|
||||||
: ~['"\\bfnrtv0-9xu\r\n]
|
|
||||||
;
|
|
||||||
fragment EscapeCharacter
|
|
||||||
: SingleEscapeCharacter
|
|
||||||
| [0-9]
|
|
||||||
| [xu]
|
|
||||||
;
|
|
||||||
|
|
||||||
fragment LineContinuation
|
|
||||||
: '\\' [\r\n\u2028\u2029]
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ID : [a-zA-Z_]+ ;
|
|
||||||
|
|
||||||
// skip spaces, tabs, newlines
|
|
||||||
WS : [ \t\r\n]+ -> skip ;
|
|
||||||
MultiLineComment: '/*' .*? '*/' -> channel(HIDDEN);
|
|
||||||
SingleLineComment: '//' ~[\r\n\u2028\u2029]* -> channel(HIDDEN);
|
|
|
@ -1,6 +0,0 @@
|
||||||
= Otto Grammars
|
|
||||||
|
|
||||||
This directory contains the link:https://github.com/antlr/antlr4/[Antlr v4]
|
|
||||||
grammars for parsing the modeling language that Otto uses for describing a
|
|
||||||
continuous delivery process.
|
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
---
|
||||||
|
openapi: 3.0.0
|
||||||
|
info:
|
||||||
|
title: Otto Parser Service
|
||||||
|
description: |
|
||||||
|
This specification describes the Otto Parser service which is responsible
|
||||||
|
for ingesting Otto Pipeline syntax (typically .otto files) and outputs
|
||||||
|
the internal Otto intermediate representation.
|
||||||
|
version: '1.0.0'
|
||||||
|
contact:
|
||||||
|
name: R Tyler Croy
|
||||||
|
email: 'rtyler@brokenco.de'
|
||||||
|
x-twitter: agentdero
|
||||||
|
license:
|
||||||
|
name: 'GNU AGPL 3.0'
|
||||||
|
url: 'https://www.gnu.org/licenses/agpl-3.0.en.html'
|
||||||
|
externalDocs:
|
||||||
|
description: 'Find out more about Otto'
|
||||||
|
url: 'https://github.com/rtyler/otto'
|
||||||
|
servers:
|
||||||
|
- url: 'http://localhost:7672'
|
||||||
|
description: 'Local dev server'
|
||||||
|
paths:
|
||||||
|
'/v1/parse':
|
||||||
|
post:
|
||||||
|
operationId: ParsePipeline
|
||||||
|
description: |
|
||||||
|
The primary interface for the parser service which takes an uploaded Otto
|
||||||
|
Pipeline string and will attempt to parse the pipeline into an intermediate
|
||||||
|
representation which other parts of Otto can work with.
|
||||||
|
requestBody:
|
||||||
|
description: Pipeline syntax
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
text/plain:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
examples:
|
||||||
|
success:
|
||||||
|
summary: 'Simple Empty Pipeline'
|
||||||
|
value: |
|
||||||
|
pipeline {
|
||||||
|
}
|
||||||
|
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Successfully parsed
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ParsePipelineResponse'
|
||||||
|
'400':
|
||||||
|
description: Failed to parse the pipeline for some reason
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ParsePipelineFailure'
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
ParsePipelineResponse:
|
||||||
|
description: |
|
||||||
|
This response is passed on a successful parse of the provided pipeline
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- meta
|
||||||
|
example: {}
|
||||||
|
properties:
|
||||||
|
meta:
|
||||||
|
type: object
|
||||||
|
|
||||||
|
ParsePipelineFailure:
|
||||||
|
type: object
|
||||||
|
example: {}
|
||||||
|
properties: {}
|
||||||
|
|
|
@ -3,7 +3,46 @@
|
||||||
* readable (YAML) structures which other components in Otto can use.
|
* readable (YAML) structures which other components in Otto can use.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use otto_parser::*;
|
||||||
|
use tide::{Request, Response};
|
||||||
|
|
||||||
|
async fn parse(mut req: Request<()>) -> tide::Result {
|
||||||
|
let buffer = req.body_string().await?;
|
||||||
|
|
||||||
|
let parsed = parse_pipeline_string(&buffer);
|
||||||
|
|
||||||
|
match parsed {
|
||||||
|
Err(e) => {
|
||||||
|
let resp = Response::builder(400)
|
||||||
|
.body("{}")
|
||||||
|
.content_type("application/json")
|
||||||
|
.build();
|
||||||
|
return Ok(resp);
|
||||||
|
},
|
||||||
|
Ok(pipeline) => {
|
||||||
|
let resp = Response::builder(200)
|
||||||
|
.body(r#"{"meta" : {}}"#)
|
||||||
|
.content_type("application/json")
|
||||||
|
.build();
|
||||||
|
return Ok(resp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[async_std::main]
|
#[async_std::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> Result<(), std::io::Error> {
|
||||||
|
use std::{env, net::TcpListener, os::unix::io::FromRawFd};
|
||||||
|
tide::log::start();
|
||||||
|
let mut app = tide::new();
|
||||||
|
app.at("/v1/parse").post(parse);
|
||||||
|
|
||||||
|
if let Some(fd) = env::var("LISTEN_FD").ok().and_then(|fd| fd.parse().ok()) {
|
||||||
|
app.listen(unsafe { TcpListener::from_raw_fd(fd) }).await?;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
app.listen("http://localhost:7672").await?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue