From b6a03552ccbc18fa7615afa2ff97b291255363bc Mon Sep 17 00:00:00 2001 From: Charlie Drage Date: Fri, 29 Jul 2016 12:40:15 -0400 Subject: [PATCH] Merge Nulecule specification into Atomic App This commit merges the documentation located at github.com/projectatomic/nulecule into Atomic App. The changes included with the merge are: * Remove reference to developer tooling being added * Remove reference to example folder, now directs to nulecule-library * Remove redundant information (links to #nulecule irc channel) already present in root README.md * Move .json spec files to json folder * Clean up documentation links to link to the correct directory * Capitalize spec doc files, move them all to a single folder * Add link to NULECULE_FILE.md reference in main spec README.md * Add link to specification in the root README.md * Update description of Nulecule in the main README.md --- README.md | 7 +- docs/spec/GETTING_STARTED.md | 90 +++++ docs/spec/GLOSSARY.md | 13 + docs/spec/IMPLEMENTATION_GUIDE.md | 34 ++ docs/spec/LIFECYCLE.md | 54 +++ docs/spec/NULECULE_FILE.md | 375 ++++++++++++++++++ docs/spec/README.md | 128 ++++++ docs/spec/json/constraint.json | 23 ++ docs/spec/json/files | 2 + docs/spec/json/graph.json | 44 ++ docs/spec/json/license.json | 20 + docs/spec/json/metadata.json | 27 ++ docs/spec/json/param.json | 33 ++ docs/spec/json/provider.json | 63 +++ docs/spec/json/requirement.json | 12 + .../json/requirements/persistentvolume.json | 34 ++ docs/spec/json/schema.json | 36 ++ 17 files changed, 994 insertions(+), 1 deletion(-) create mode 100644 docs/spec/GETTING_STARTED.md create mode 100644 docs/spec/GLOSSARY.md create mode 100644 docs/spec/IMPLEMENTATION_GUIDE.md create mode 100644 docs/spec/LIFECYCLE.md create mode 100644 docs/spec/NULECULE_FILE.md create mode 100644 docs/spec/README.md create mode 100644 docs/spec/json/constraint.json create mode 100644 docs/spec/json/files create mode 100644 docs/spec/json/graph.json create mode 100644 docs/spec/json/license.json create mode 100644 docs/spec/json/metadata.json create mode 100644 docs/spec/json/param.json create mode 100644 docs/spec/json/provider.json create mode 100644 docs/spec/json/requirement.json create mode 100644 docs/spec/json/requirements/persistentvolume.json create mode 100644 docs/spec/json/schema.json diff --git a/README.md b/README.md index c65a116e..f5b7cf7b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![](docs/images/logo.png "Project Atomic") -Atomic App is a reference implementation of the [Nulecule](https://github.com/projectatomic/nulecule) specification. Packaged Atomic App containers are "Nuleculized" and each component of the package is a "Nulecule". +Atomic App is a reference implementation of the [Nulecule](docs/spec/README.md) specification. Packaged Atomic App containers are "Nuleculized" and each component of the package is a "Nulecule". Atomic App is used to bootstrap packaged container environments and run them on multiple container orchestrators. It is designed from the ground-up to be portable and provider pluggable. @@ -48,6 +48,7 @@ This README contains some high level overview information on Atomic App. The det 7. [Specification coverage](docs/spec_coverage.md) 8. [Contributing](CONTRIBUTING.md) 9. [Dependencies](docs/requirements.md) +10. [Specification](docs/spec/README.md) ## Getting started @@ -89,6 +90,10 @@ If you have any issues or get stuck, feel free to open a GitHub issue or reach u See [REQUIREMENTS.md](docs/requirements.md) for a list of current Atomic App dependencies. +## Specification + +Want to view the specification and contribute to changes? See the [Nulecule spec](docs/spec/README.MD) for more information. + ## Communication channels * IRC: __#nulecule__ on irc.freenode.net diff --git a/docs/spec/GETTING_STARTED.md b/docs/spec/GETTING_STARTED.md new file mode 100644 index 00000000..a24f5554 --- /dev/null +++ b/docs/spec/GETTING_STARTED.md @@ -0,0 +1,90 @@ +# Getting Started with Nulecule + +You have an application you want to package up as a Nulecule for distribution. It's composed of one or more containers that together link to provide your application. + +## Plan +1. Determine what components of your application are custom and which are "stock" parts. For example, do you need a custom web server, or do you just need to load a specific configuration onto an already packaged web server. +1. Find your resources. + - **nulecule applications** Are there existing Nulecule Applications you can leverage in your own application? + - **container images** Carefully consider if you really need to build your own containers. For example, do you really need your own web server or database image? If you're writing a Dockerfile for a common service, try to find a well-known, supported, certified, stable image that you can build on. + - **provider orchestration templates** When you are considering how to provide configuration for orchestration providers, such as kubernetes files (service, replication controller, pod) or OpenShift or Docker Compose files, see if you can use exising templates or known good files. As with container images, if you're writing files for common services, try to find well-known, supported, certified, stable templates that you can build on. + +## Prepare +From the planning phase, you've got a collection of remote and local sources that your application will be comprised of. + +1. Start with the containers. Understand how they run standalone. Get them running. Make sure the entire application runs manually. +1. Orchestrate the containers on the target provider. Start simply and build up. For example, with kubernetes just deploy as a pod. Once that succeeds, add a service, and then some replication controllers. There are many opportunities for error -- so make small changes, test and iterate slowly. Verify your [YAML](http://codebeautify.org/yaml-validator) or [JSON](http://jsonlint.com/) frequently. Use a method that can be easily incorporated into your development workflow: small change -> save -> validate -> test -> rinse and repeat. +1. Test both custom and stock services together. Nulecule won't do magical things. The pieces must all work together before they can be packaged up as a unit. + +## Package +Only when everything is working are you ready to package the application. In this phase you'll be interacting with the [Nulecule specification](/spec). + +1. Download a [Nulecule template](/spec/examples/template) to start from. +1. In the Nulecule file, create one or more lists of things under `graph`. These represent the different components that make up your application. Names are arbitrary. Remember to verify your [YAML](http://codebeautify.org/yaml-validator) or [JSON](http://jsonlint.com/) frequently. + + 1. If your sources are remote, then all that is needed is a name and source. Remote sources are other Nulecule applications. + + graph: + - name: mydb + source: "docker://registry.example.com/some/database" + 1. If your sources are local, then provide a name and an artifacts key that will reference the source file(s). Each provider will have a key specifying the provider. For example, "docker" or "kubernetes". + + graph: + - name: myapp + artifacts: + kubernetes: + - file:///artifacts/kubernetes/pod.json + - file:///artifacts/kubernetes/service.json + +1. Put all of the provider files into a directory structure that corresponds to the provider artifacts section in the Nulecule file. Using the above example, `artifacts/kubernetes/.json`. The structure should resemble something like this: + + ├── Dockerfile + ├── artifacts + │   └── kubernetes + │   ├── pod.json + │   └── service.json + ├── Nulecule + └── README.md + +1. Consider the different ways your application may be deployed. There will likely be many parameters that need to be exposed at deployment. It's best to overdo this and provide defaults whenever possible. Go through the provider files and change any values. For example `database_pass: changeme` becomes `database_pass: $db_pass`. The name of the parameter is `db_pass`. These go into the params section of the Nulecule file under each provider. For example: + + + graph: + - mydb: + ... + params: + - name: db_pass + description: database passphrase + - name: port + description: frontend TCP port + default: 80 + +1. Consider any additional information that is useful for deployment. Write a README file focused on deployment. Use a popular format such as Markdown or asciidoc so it can be read from a terminal window or rendered in a graphical interface. + * what does this application do? + * what provider environment(s) do I need to have setup before I deploy it? + * how do I verify that it has been deployed correctly? + +1. Add a metadata section, including a name, description and license information. Arbitrary metadata may also be added. Consider using keyword tags that may be useful for deployment management. For example: + + metadata: + name: My Cool App + appversion: 1.0.0 + description: Lorem ipsum dolor sit amet, consectetur adipiscing elit + license: + name: GPLv3 + url: http://www.example.com/license + tags: + - foo + - bar + +1. Before packaging up into a container, try running it in a test mode if one is provided by your Nulecule implementation. If you are using the [Atomic App reference implementation](https://github.com/projectatomic/atomicapp), use the `dry-run` and `verbose` options as follows: `atomicapp --dry-run --verbose run`. This should output the commands that will run. Common errors: + * provider files don't match the artifact relative path + * yaml or json is not valid + * missing parameter + +1. Once the Nulecule file and provider artifacts are working, package the application as a container. Typically, this means basing it off of an executable image provided by the implementation of Nulecule you are using. If you are using the [Atomic App reference implementation](https://github.com/projectatomic/atomicapp), the stock Dockerfile may be used, unaltered, unless you have a special use case. + + [sudo] docker build -t mydb-app . + +## Push & Pull +Push the image to a registry. Tell people about it and see if they can deploy your application without any assistance. If they have questions, you probably should enhance the application and parameter descriptions so they are clear. diff --git a/docs/spec/GLOSSARY.md b/docs/spec/GLOSSARY.md new file mode 100644 index 00000000..ad89c4fe --- /dev/null +++ b/docs/spec/GLOSSARY.md @@ -0,0 +1,13 @@ +# Nulecule Glossary + +* __Container Image__ - Platform-agnostic term referring to Docker, Rkt or other packaging and transport protocol +* __Layered Image__ - The foundation image of a container plus other tools, applications and content added +* __Association__ of container images to the multi-container Nulecule application: + + __Aggregation__ of one or more discrete container images integral to the operation and coupled to the lifecycle of the Nulecule application - can be another Nulecule Application or container image reference + + __Composition__ refers to one or more container images that are required and tightly coupled to the Nulecule application - can be another Nulecule Application or container image reference +* __Include__ - Refers to the ability to include common resources, parameters or definitions needed to deploy onto a orchestration provider. For example, an OpenShift provider may include the kubernetes provider artifacts and add OpenShift functionality on top of kubernetes capabilities. +* __Provider__ - Plugin interface for specific deployment platform, an orchestration provider +* __Dependency Management__ - Refers to the ability to define order of deployment and managed dependencies including configurable parameters layered on top of stock container images, as well as the providers included in the application definition +* __Directed Graph__ - Declarative representation of dependencies in the context of a multi-container Nulecule application +* __Parameters__ - Variables that can have default values and can be overridden by answerfile.conf + diff --git a/docs/spec/IMPLEMENTATION_GUIDE.md b/docs/spec/IMPLEMENTATION_GUIDE.md new file mode 100644 index 00000000..e6226fb4 --- /dev/null +++ b/docs/spec/IMPLEMENTATION_GUIDE.md @@ -0,0 +1,34 @@ +# Implementation Guide + +This specification has been fully described in the [schema.json](/spec/schema.json) file. Developer and deployment tools should be implemented using this file. + +## Developer Tools + +Developer tooling helps application developers or designers get going quickly. Tools may be template-based or wizard-style tools, command line or graphical interface. When creating a tool for developers decide how much assistance you want to expose for the providers. Each provider has its own documentation and potential tooling but integrating provider features can be a big help to getting something working quickly. + +Wizard-style tools that generate the files for an application require these fields for input: + +* name +* description +* version (application) + +Each "application" component the user wants to define will compose the "graph" for the Nulecule. A component may either be a remote application or defined locally in the directory structure. + +**Remote applications** + +Remote applications are other nulecule container images, for example `someuser/mariadb-app`. No other information is needed. + +**Local applications** + +Local applications are defined by a directory in the graph. These fields are required for input: + +* application name: this is added to the Nulecule graph and creates a directory in the graph. +* provider: a subdirectory of the application directory + +**Providers** + +Provider files may be generated based on some templates. Providing a mechanism to parameterize these files helps the developer understand how parameterization works. For example, if a set of kubernetes template files are pulled in allowing the developer to parameterize some values in the pod file would update the pod file and create a `key = value` pair in the application section of the `params.conf` file. For required values without defaults set the value to `None` in `params.conf`. With this example as a starting point the developer can then easily manipulate parameters by manually editing the files based on the demonstrated pattern. + +## Runtime Tools + +The Reference implementation, Atomic App, coded in python is located at: https://github.com/projectatomic/atomicapp diff --git a/docs/spec/LIFECYCLE.md b/docs/spec/LIFECYCLE.md new file mode 100644 index 00000000..886612b3 --- /dev/null +++ b/docs/spec/LIFECYCLE.md @@ -0,0 +1,54 @@ +# Lifecycle of the Specification + +This document and the processes it describes will become effective starting Nulecule Specification 0.0.2. It is valid until replaced by a newer version or noted otherwise. + +## Normative Document + +The normative Nulecule Specification document will be published at http://www.projectatomic.io/nulecule/spec// +Versioning is using the [semantic versioning scheme](http://semver.org/spec/v2.0.0.html). + +In addition to the human readable HTML document, a JSON formated machine readable version of the specification will be published at the same URL path as the HTML document. The document name will be schema.json and may reference other files using the JSON DRAFT4 references. + +The normative machine readable Nulecule Specification document will be published at https://github.com/projectatomic/nulecule/blob/v/spec//schema.json + +## States + +The Nulecule Specification will have a certain set of releases, we will use semantic versioning to identify the releases. +Prior each release there will be a draft version of the release. This will be used to work/collaborate on the spec itself. + +## Contributors and release process + +Everybody is welcome to contribute to the draft version of the upcoming release. This will be documented by pull +requests (to the github repository of the Nulecule Specification) to the draft of the specification. Once a draft +has stabilized, it will be prepared by the specification maintainers and prepared for release. The maintainers +will release a new release of the specification. + +### Changes to a Releases + +Changes to released versions of the specification will not change the structure or feature set of the specification. +They are only meant to fix spelling or language errors, add or correct examples. + +Collaboration on the draft of the next release of the Nulecule Specification will be done on the master branch of the github +repository of the Nulecule Specification. The release task itself is rather short: the maintainers will tag the repository +and provide the human and machine readable versions of the normative documents. + +## Release tasks + +This chapter will walk you thru the steps to be taken to + + * prepare a draft - so that the community can work on it + * release - so that a new version of the spec is created + +### prepare a draft + +Given the example that the current version of the spec is 0.5.0, collaboration of the specification will continue on the master branch +of https://github.com/projectatomic/nulecule + +### release (move from draft to new version) + +This will bring the draft version of the spec to a released version of the spec: `git tag v0.6.0 -m 'v0.6.0'` After that, one of the maintainers will +publish the human and machine readable files to http://projectatomic.io/nulecule/spec/0.6.0/ + +## Maintainers + +Please see the MAINTAINERS file for a list of maintainers of the Nulecule Specification. diff --git a/docs/spec/NULECULE_FILE.md b/docs/spec/NULECULE_FILE.md new file mode 100644 index 00000000..8a7e0d16 --- /dev/null +++ b/docs/spec/NULECULE_FILE.md @@ -0,0 +1,375 @@ +# Container Application Specification + +**NOTE**: This is a work in progress effort that is expected to change quickly. Feel free to join the initiative! + +#### Version 0.0.2 + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](http://www.ietf.org/rfc/rfc2119.txt). + +The Container Application Specification is licensed under [GNU Free Documentation License Version 1.3, 3 November 2008](https://www.gnu.org/copyleft/fdl.html). + +## Introduction + +The Container Application specification is a project to describe 'an Application' that is composed of a set of dependent Container Applications (containerapp). The Container Application specification defines a set of files required to describe such a containerapp. These files can then be used by other tools to deploy a containerapp. Developers may use other tools to generate most of the required containerapp files. Additional utilities can also take advantage of the resulting files, such as testing tools. + +### Versioning + +Within this specification we follow [the semantic versioning pattern](http://semver.org/spec/v2.0.0.html). + +## Revision History + +Version | Date | Notes +--- | --- | --- +0.0.2 | 2015-05-07 | close issue #35 the graph is now a list of named items +0.0.1-alpha | 2015-mm-dd | TBD +v1-alpha | 2015-04-10 | reversioned to 0.0.1-alpha + +## Examples + +For a list of examples that conform to the spec, check out [github.com/projectatomic/nulecule-library](https://github.com/projectatomic/nulecule-library). + + +## Specification + +### Format + +The files describing a containerapp in accordance with the Container Application Specification are represented using [YAML 1.2](http://www.yaml.org/spec/1.2/spec.html) or [JSON](http://json.org/). + +All field names in the specification are **case sensitive**. + +By convention, the containerapp definition file is named `Nulecule`. The Nulecule is the primary file defining the containerapp and it's relationship to dependencies. + +### Data Types + +Primitive data types in the Container Application Specification are based on the types supported by the [JSON-Schema Draft 4](http://json-schema.org/latest/json-schema-core.html#anchor8). + +The formats defined by the Container Application Specification are: + +Common Name | [`type`](#dataTypeType) | [`format`](#dataTypeFormat) | Comments +----------- | ------ | -------- | -------- +integer | `integer` | `int32` | signed 64 bits +float | `number` | `float` | +string | `string` | | +byte | `string` | `byte` | +boolean | `boolean` | | +date | `string` | `date` | As defined by `full-date` - [RFC3339](http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14) +dateTime | `string` | `date-time` | As defined by `date-time` - [RFC3339](http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14) +password | `string` | `password` | Used to hint UIs the input needs to be obscured. +URL | `URL` | `URL` | As defined by `URL` - [RFC3986 Section 1.1.3](https://tools.ietf.org/html/rfc3986#section-1.1.3) + +### Terminology + +Container Application + +Provider + + +### Schema + +#### Container Application Object + +This is the root object for the specification. + +##### Fields + +Field Name | Type | Description +---|:---:|--- +id | `string` | **Required.** The machine readable id of the Container Application. +specversion | `string` | **Required.** The semantic version string of the Container Application Specification used to describe the app. The value MUST be `"0.0.2"`. +metadata | [ [MetadataObject](#metadataObject) ] | **Optional** An object holding optional metadata related to the Container Application, this may include license information or human readable information. +params | [ [ParamsObject](#paramsObject) ] | **Optional** A list of [ParamsObject](#paramsObject) that contain provider specific information. +graph | [ [GraphObject](#graphObject) ] | **Required.** A list of depending containerapps. Strings may either match a local sub directory or another containerapp-spec compliant containerapp image that can be pulled via a provider. +requirements | [ [RequirementsObject](#requirementsObject) ] | **Optional** A list of requirements of this containerapp. + + +#### Metadata Object + +Metadata for the Container Application. + +##### Fields + +Field Name | Type | Description +---|:---:|--- +name | `string` | **Optional** A human readable name of the containerapp. +appversion | `string` | **Optional** The semantic version string of the Container Application. +description | `string` | **Optional** A human readable description of the Container Application. This may contain information for the deployer of the containerapp. +license | [License Object](#licenseObject) | **Optional** The license information for the containerapp. +arbitrary_data | `string` | **Optional** Arbitrary `key: value` pair(s) of metadata. May contain nested objects. + +##### Metadata Object Example: + +```yaml +name: myapp +appversion: 1.0.0 +description: description of myapp +foo: bar +othermetadata: + foo: bar + files: file://path/to/local/file +``` + +```js +{ + "name": "myapp", + "appversion": "1.0.0", + "description": "description of myapp", + "foo": "bar", + "othermetadata": { + "foo": "bar", + "files": "file://path/to/local/file" + } +} +``` + +#### License Object + +License information for the Container Application. + +##### Fields + +Field Name | Type | Description +---|:---:|--- +name | `string` | **Required.** The human readable license name used for the Container Application, no format imposed. +url | `string` | **Optional** A URL to the license used for the API. MUST be in the format of a URL. + +##### License Object Example: + + +```yaml +name: Apache 2.0 +url: http://www.apache.org/licenses/LICENSE-2.0.html +``` +```js +{ + "name": "GNU GPL, Version 3", + "url": "https://www.gnu.org/copyleft/gpl.html" +} +``` + + +#### Graph Object + +The graph is a list of items (containerapps) the Container Application depends on. + +##### Fields of a Graph Item Object + +Field Name | Type | Description +---|:---:|--- +name | `string` | **Required.** The name of the depending Container Application. +source | `URL` | **Optional** Source location of the Container Application, the source MUST be specified by a valid URL. If source is present, all other fields SHALL be ignored. +params | [ [ParamsObject](#paramsObject) ] | **Optional** A list of [ParamsObject](#paramsObject) that contain provider specific information. If params is present, source field SHALL be ignored. +artifacts | [ [ArtifactsObject](#artifactsObject) ] | **Optional** A list of [ArtifactsObject](#artifactsObject) that contain providr specific information. If artifacts is present, source field SHALL be ignored. + +##### Graph Item Object Example: + +```yaml +--- +name: atomicapp-zabbix-mongodb +source: uri://registry.devops.example.com +# if no "artifacts" is specified, then it is an external Atomic App to be pulled +# and installed from the specified source +``` + +```js +{ +"name": "atomicapp-zabbix-mongodb", +"source": "uri://registry.devops.example.com" +} +``` + +#### Parameters Object + +A list of Parameters the containerapp requires, has set some defaults for or needs user input. + +##### Fields + +Field Name | Type | Description +---|:---:|--- +name| `string` | **Required.** The name of the parameter. +description | `string` | **Required.** A human readable description of the parameter. +constraints | [ConstraintObject](#constraintObject) | **Optional** An optional definition of constraints to the parameter. +default | `string` | **Optional** An optional default value for the parameter. +hidden | `string` | **Optional** An optional boolean signifying the parameter should be obscured when displayed. + +##### Parameters Object Example: + +```yaml +name: password +description: mongoDB Admin password +hidden: true +constraints: + - allowed_pattern: "[A-Z0-9]+" + description: Must consist of characters and numbers only. +``` +```js +{ + "name": "password", + "description": "mongoDB Admin password", + "hidden": true, + "constraints": [ + { + "allowed_pattern": "[A-Z0-9]+", + "description": "Must consist of characters and numbers only." + } + ] +} +``` + +#### Constraint Object + +Constraints to the parameter. + +##### Fields + +Field Name | Type | Description +---|:---:|--- +allowed_pattern | `string` | **Required.** A regexp declaring the allowed pattern. +description | `string` | **Required.** A human readable description of the parameter. + + + +#### Requirements Object + +The list of requirements of the Container Application. It may be [Storage Requirement Objects](#storageRequirementsObject) (for a persistent Volume). + + +#### Storage Requirements Object + +This describes a requirement for persistent, read-only or read-write storage that should be available to the containerapp on runtime. The name of this object MUST be `"persistentVolume"`. + +##### Fields of Storage Requirement + +Field Name | Type | Description +---|:---:|--- +name | `string` | **Required.** A name associated with the storage requirement. +accessModes | `string` | **Required.** May be `"ReadWrite"` or `"ReadOnly"`. +size | `integer` | **Required.** Size of required the storage. + +##### Storage Requirement Example: + +```yaml +--- +- persistentVolume: + name: "var-lib-mongodb-data" + accessMode: "ReadWrite" + size: 4 # GB by default +``` +```js + { + "persistentVolume": { + "name": "var-lib-mongodb-data", + "accessMode": "ReadWrite", + "size": 4 + } + } +``` + + +#### Artifacts Object + +The Artifacts Object describes a list of provider specific artifact items. These artifact items will be used during installation of the containerapp to deploy it to the provider. Each provider key contains a list of artifacts. Each artifact list item is either a `URL` string or a [source control repository object](#repositoryObject). + +* URL: must be a URL string prepended by URI type such as `http://`, `https://`, `file:` (relative path) or `file://` (absolute path). URI type `file:` may be a single file or a directory path to multiple files. Directories must end with a trailing slash such as `file:relative/path/to/multiple/artifact/files/`. +* [SourceControlRepositoryObject](#repositoryObject) + +##### Artifacts Example: + +```yaml +--- +artifacts: # list of local or remote files or remote repository path to be processed by the provider selected at install-time + kubernetes: + - source: https://github.com/aweiteka/kube-files.git + tag: release-1 + openshift: + - file:relative/path/openshift/artifacts/ + - https://example.com/openshift/strategies.json + - inherit: + - kubernetes +``` +```js +{ + "artifacts": { + "kubernetes": [ + { + "source": "https://github.com/aweiteka/kube-files.git", + "path": "/artifacts/kubernetes/", + "tag": "release-1" + } + ], + "openshift": [ + "file:relative/path/openshift/artifacts/", + "https://example.com/openshift/strategies.json", + { + "inherit": [ + "kubernetes" + ] + } + ] + } +} +``` + +#### Source Control Repository Object + +Source Control Repository Object for artifact sources. + +##### Fields of a Source Control Repository Object + +Field Name | Type | Description +---|:---:|--- +source | `URL` | **Required** Source location of the source control repository. The source MUST be specified by a valid URL. +path | `string` | **Optional** The path to a specific artifact file or directory of artifact files. Default value is "/" which would reference all of the files in the repository. +type | `string` | **Optional** The source control type. Default value is "git". +branch | `string` | **Optional** The source control branch. Default value is "master". +tag | `string` | **Optional** The source control tag. + + +## Directory Layout + +Names of files that must be present are contained in the file `files` in +the root directory of the specification. These filenames support globbing. + +A filesystem layout of a typical app is this: +``` +├── Nulecule +├── Dockerfile +├── +│ ├── ... +│ └── +└── README.md +``` + +* `Nulecule`: Container Application definition +* `Dockerfile`: standard packaging for this containerapp +* ``: directories of provider-specific files referenced in a containerapp definition file + * `PROVIDER_FILES`: provider-specific files necessary for deploying to provider +* `README.md`: information for deploying this application + + +## README.md + +The README.md is the human-readable document. It describes the containerapp in enough detail so an operator can make parameterization and other deployment decisions. + +NOTE: This is optional. It is possible for some applications to be "self-describing" through well-written descriptions and input validation. + +## Good Practices + +An implementation of the Nulecule Specification should declare what providers it supports. This should be done by adding a Label to the container image, by adding a line to the Dockerfile: +``` +LABEL io.projectatomic.nulecule.providers "kubernetes,docker,openshift" +``` + +## Conventions + +A few conventions are used in the context of Container Applications. + +### Parameters for Providers + +Each provider in the [ArtifactsObject](#artifactsObject) of the [GraphObject](#graphObject) may correspond to a containerapp level [ParamsObject](#paramsObject). + +### Version Label + +The Dockerfile must carry a Label declaring the version of the specification that is used: +``` +LABEL io.projectatomic.nulecule.specversion 0.0.2 +``` diff --git a/docs/spec/README.md b/docs/spec/README.md new file mode 100644 index 00000000..7091e415 --- /dev/null +++ b/docs/spec/README.md @@ -0,0 +1,128 @@ +# Composite Container-based Application Specification + +`\ˈnü-li-ˌkyül\` (n.) a made-up word meaning ["the mother of all atomic particles"](http://simpsons.wikia.com/wiki/Made-up_words). + +**Your installer for container-based applications.** Replace your shell script and deployment instructions with some metadata. + +**Change runtime parameters for different environments.** No need to edit files before deployment. Users can choose interactive or unattended deployment. Guide web interface users with parameter metadata to validate user input and provide descriptive help. + +**Bridge between Enterprise IT and PaaS** With pluggable orchestration providers you can package your application to run on OpenShift, Kubernetes, Docker Compose, Helios, Panamax, Docker Machine, etc. and allow the user to choose the target when deployed. + +**Compose applications from a catalog.** No need to re-package common services. Create composite applications by referencing other Nulecule-compliant apps. For example, adding a well-designed, orchestrated database is simply a reference to another container image. + +## Problem Statement +Currently there is no standard way of defining a multi-container application's configuration without distributing instructions and files to the end-user. Additionally, these files must be managed and distributed via different systems than the containers themselves. + +Containers in the OCI (Open Container Initiative) format derived from Docker offers a new approach for application packaging. OCI enables application-centric aggregate packaging, optimized for deployment into containers. However most applications will consist of multiple containers, which surfaces two issues: the relationships between containers need to be expressed in order to manage dependencies and orchestrate the deployment (e.g. set up network connections) with consideration of environmental factors, and this application-level meta-data needs to be distributed. OCI itself, however, stops at the individual container. Orchestration tools such as Kubernetes offer a generic description model for multi-container applications, however they do not define a transport model, nor a standard way to parameterize a generic template. The mindset of most, if not all, current container orchestration systems is to treat the aggregate, multi-container application as state of the cluster rather than an entity in it's own right and therefore they regress beyond the portability that OCI introduced. This means that it's very easy to put a individual service into a Docker-style Registry, however there is no way to represent a full application at the distribution level - I can create a single MariaDB container, but not a MariaDB/Galera cluster or even a full application such as [Kolab](https://kolab.org/). So what is missing? A standard way to describe and package a multi-container application. + +## What is Nulecule? + +Nulecule defines a pattern and model for packaging complex multi-container applications and services, referencing all their dependencies, including orchestration metadata in a container image for building, deploying, monitoring, and active management. + +The Nulecule specification enables complex applications to be defined, packaged and distributed using standard container technologies. The resulting container includes dependencies, supports multiple orchestration providers, and has the ability to specify resource requirements. The Nulecule specification also supports the aggregation of multiple composite applications. The Nulecule specification is container and orchestration agnostic, enabling the use of any container and orchestration technology. + +**[Glossary of terms](GLOSSARY.md)** + +## Nulecule Specification Highlights + +* Application description and context maintained in a single container through extensible metadata +* Composable definition of complex applications through inheritance and composition of containers into a single, standards-based, portable description. +* Simplified dependency management for the most complex applications through a directed graph to reflect relationships. +* Container and orchestration engine agnostic, enabling the use of any container technology and/or orchestration technology + +Detailed explanation on the **Nulecule** file-format is explained at [NULECULE_FILE.md](NULECULE_FILE.md). + +## “The Big Picture” + +![Alt Nulecule specification high-level story.](/docs//images/logo.png "Nulecule specification high-level story") + +## Deployment User Experience + +The Nulecule specification has been implemented in the [Atomic App reference implementation](https://github.com/projectatomic/atomicapp). Atomic App currently supports docker containers and kubernetes and docker orchestration providers. The [atomic command](https://github.com/projectatomic/atomic) is used to run the container that contains the Nulecule specification and the Atomic App implementation. + +This example is a single container application based on the centos/httpd image, but you can use your own. + +You may wish to run the Nulecule from an empty directory as it will copy the Nulecule files to the working directory for inspection every time it is run. + +### Option 1: Non-interactive defaults + +Run the image. It will automatically use kubernetes as the orchestration provider. This will become interactive and prompt for defaults if the Nulecule file doesn't provide defaults for all of the parameters. + +``` +[sudo] atomic run projectatomic/helloapache +``` + +### Option 2: Unattended + +1. Create the file `answers.conf` with these contents: + + This sets up the values for the two configurable parameters (image and hostport) and indicates that kubernetes should be the orchestration provider. + + [general] + provider = kubernetes + + [helloapache-app] + image = centos/httpd # optional: choose a different image + hostport = 80 # optional: choose a different port to expose +1. Run the application from the current working directory + + $ [sudo] atomic run projectatomic/helloapache + ... + helloapache + + +1. As an additional experiment, remove the kubernetes pod and change the provider to 'docker' and re-run the application to see it get deployed on native docker. + +### Option 3: Install and Run + +You may want to download the application, review the configuration and parameters as specified in the Nulecule file, and edit the answerfile before running the application. + +1. Download the application files using `atomic install` + + [sudo] atomic install projectatomic/helloapache + +1. Rename `answers.conf.sample` + + mv answers.conf.sample answers.conf + +1. Edit `answers.conf`, review files if desired and then run + + $ [sudo] atomic run projectatomic/helloapache + ... + helloapache + +## Test +Any of these approaches should create a kubernetes pod or a running docker container. + +With a kubernetes pod, once its state is "Running" curl the minion it's running on. + +``` +$ kubectl get pod helloapache +POD IP CONTAINER(S) IMAGE(S) HOST LABELS STATUS +helloapache 172.17.0.8 helloapache centos/httpd 10.3.9.216/ name=helloapache Running +$ curl 10.3.9.216 + +``` + +If you test the docker provider, once the container is running, curl the port on your localhost. + +``` +$ curl localhost + +``` + +Additional examples that conform to the Nulecule spec can be found at [github.com/projectatomic/nulecule-library](https://github.com/projectatomic/nulecule-library). + +## Developer User Experience + +See the [Getting Started with Nulecule guide](GETTING_STARTED.md). + +## Implementations + +This is only a specification. Implementations may be written in any language. See [implementation guide](IMPLEMENTATION_GUIDE.md) + +**Reference implementation** https://github.com/projectatomic/atomicapp + +## Examples / Library + +For a library of examples conforming to the current reference implementation [atomicapp](https://github.com/projectatomic/atomicapp) please visit [github.com/projectatomic/nulecule-library](https://github.com/projectatomic/nulecule-library) diff --git a/docs/spec/json/constraint.json b/docs/spec/json/constraint.json new file mode 100644 index 00000000..6d7f7478 --- /dev/null +++ b/docs/spec/json/constraint.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + + "title": "Constraint", + "description": "Constraint to the parameter.", + "type": "array", + "items": { + "type": "object", + "required": [ "allowed_pattern", "description" ], + "properties": { + "allowed_pattern": { + "description": "A regular expression pattern.", + "type": "string", + "default": "null" + }, + "description": { + "description": "A human readable description of the constraint.", + "type": "string", + "default": "null" + } + } + } +} diff --git a/docs/spec/json/files b/docs/spec/json/files new file mode 100644 index 00000000..9c3dd392 --- /dev/null +++ b/docs/spec/json/files @@ -0,0 +1,2 @@ +Dockerfile +Nulecule diff --git a/docs/spec/json/graph.json b/docs/spec/json/graph.json new file mode 100644 index 00000000..a53949a8 --- /dev/null +++ b/docs/spec/json/graph.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + + "title": "Graph", + "description": "A list of components that constitute this Container Application. Components are either container-based services or other Container Applications. Components that are container-based services are specified as a collection of artifacts for providers that can accept a parameters specified by the deployer. These artifacts are references to files located in local sub-directories. Components that are other Container Applications are specified as URLs.", + "type": "array", + "items" : { + "$ref": "#/definitions/component" + }, + + "definitions": { + "component": { + "description": "ID of a component", + "type": "object", + "required": [ "name" ], + "properties": { + "name": { + "description": "The name of the component.", + "type": "string", + "default": "null" + }, + "source": { + "description": "If the component is another Container Application, source MUST be a valid URL to the source location. If source is present, all other fields SHALL be ignored.", + "type": "string", + "default": "null" + }, + "params": { + "description": "A list of ParamsObject that contain information to be used by providers in conjunction with their ArtifactsObject. If params is present, the source field SHALL be ignored.", + "type": "array", + "items": { + "$ref": "file:param.json" + } + }, + "artifacts": { + "description": "A list of ArtifactsObject that contain provider specific information. If artifacts is present, the source field SHALL be ignored.", + "type": "object", + "additionalProperties": { + "$ref": "file:provider.json" + } + } + } + } + } +} diff --git a/docs/spec/json/license.json b/docs/spec/json/license.json new file mode 100644 index 00000000..1fae538e --- /dev/null +++ b/docs/spec/json/license.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + + "title": "License", + "description": "License information for the Container Application.", + "type": "object", + "required": [ "name" ], + "properties": { + "name": { + "description": "The human readable license name used for the Container Application, no format imposed.", + "type": "string", + "default": "null" + }, + "url": { + "description": "A URL to the license used for the API. MUST be in the format of a URL.", + "type": "string", + "default": "null" + } + } +} diff --git a/docs/spec/json/metadata.json b/docs/spec/json/metadata.json new file mode 100644 index 00000000..30c3388c --- /dev/null +++ b/docs/spec/json/metadata.json @@ -0,0 +1,27 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + + "title": "Metadata", + "description": "An object holding optional metadata related to the Container Application. This will typically include the container application name, version, description, license information and other human readable information.", + "type": "object", + "properties": { + "name": { + "description": "A human readable name of the containerapp.", + "type": "string", + "default": "null" + }, + "appversion": { + "description": "The semantic version string of the Container Application.", + "type": "string", + "default": "null" + }, + "description": { + "description": "A human readable description of the Container Application. This may contain information for the deployer of the containerapp.", + "type": "string", + "default": "null" + }, + "license": { + "$ref": "file:license.json" + } + } +} diff --git a/docs/spec/json/param.json b/docs/spec/json/param.json new file mode 100644 index 00000000..18f9811b --- /dev/null +++ b/docs/spec/json/param.json @@ -0,0 +1,33 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + + "title": "Parameter", + "description": "Name of the parameter as used in artifacts", + "type": "object", + "required": [ "name", "description" ], + "properties": { + "name": { + "description": "", + "type": "string", + "default": "null" + }, + "description": { + "description": "A human readable description of the parameter.", + "type": "string", + "default": "null" + }, + "constraints": { + "$ref": "file:constraint.json" + }, + "default": { + "description": "An optional default value for the parameter.", + "type": "string", + "default": "null" + }, + "hidden": { + "description": "An optional boolean signifying the parameter should be obscured when displayed.", + "type": "boolean", + "default": false + } + } +} diff --git a/docs/spec/json/provider.json b/docs/spec/json/provider.json new file mode 100644 index 00000000..99399758 --- /dev/null +++ b/docs/spec/json/provider.json @@ -0,0 +1,63 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + + "title": "Provider", + "description": "A provider is a deployment platform or orchestrator.", + "type": "array", + "items": { + "oneOf": [ { "$ref": "#/definitions/path" }, { "$ref": "#/definitions/repository" }, { "$ref": "#/definitions/inheritance" } ] + }, + + "definitions": { + "path": { + "description": "Path to the artifact", + "type": "string", + "default": "null" + }, + "repository": { + "type": "object", + "properties": { + "source": { + "name": "source", + "description": "Source location of the source control repository. The source MUST be specified by a valid URL.", + "type": "string", + "default": "null" + }, + "path": { + "name": "path", + "description": "The path to a specific artifact file or directory of artifact files. Default value is '/' which would reference all of the files in the repository.", + "type": "string", + "default": "/" + }, + "type": { + "name": "type", + "description": "The source control type. Default value is 'git'.", + "type": "string", + "default": "git" + }, + "branch": { + "name": "branch", + "description": "The source control branch. Default value is 'master'.", + "type": "string", + "default": "master" + }, + "tag": { + "name": "tag", + "description": "The source control tag.", + "type": "string", + "default": "null" + } + } + }, + "inheritance": { + "type": "object", + "properties": { + "inherit": { + "name": "inherit", + "description": "List of components whose artifacts will be added to the list of artifacts for the provider.", + "type": "array" + } + } + } + } +} diff --git a/docs/spec/json/requirement.json b/docs/spec/json/requirement.json new file mode 100644 index 00000000..fa97fe32 --- /dev/null +++ b/docs/spec/json/requirement.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + + "title": "Requirements", + "description": "Requirement objects", + "type": "array", + "items": { + "oneOf": [ + { "$ref": "file:requirements/persistentvolume.json" } + ] + } +} diff --git a/docs/spec/json/requirements/persistentvolume.json b/docs/spec/json/requirements/persistentvolume.json new file mode 100644 index 00000000..70b65a17 --- /dev/null +++ b/docs/spec/json/requirements/persistentvolume.json @@ -0,0 +1,34 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + + "title": "PersistentVolume", + "description": "This describes a requirement for persistent, read-only or read-write storage that should be available to the Container Application at runtime. The name of this object MUST be 'persistentVolume'", + "type": "object", + "properties": { + "persistentVolume": { + "type": "object", + "required": [ "name", "accessMode", "size" ], + "properties": { + "name": { + "description": "A name associated with the storage requirement.", + "type": "string", + "default": "null" + }, + "accessMode": { + "description": "The access mode, read-write or read-only, for the storage", + "type": "string", + "enum": [ + "ReadWrite", + "ReadOnly" + ] + }, + "size": { + "description": "Size of the storage.", + "type": "number", + "minimum": 0 + } + } + } + }, + "additionalProperties": false +} diff --git a/docs/spec/json/schema.json b/docs/spec/json/schema.json new file mode 100644 index 00000000..e7e2d56e --- /dev/null +++ b/docs/spec/json/schema.json @@ -0,0 +1,36 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + + "title": "Schema", + "version": "0.0.2", + "description": "The Container Application specification defines a set of configuration files that describe a Container Application. A Container Application is composed of a set of container-based services and/or other Container Applications that together provide an application. These configuration files can be used by tools to deploy the application in an automated way or with customizations as specified by the deployer. Developers tools can generate most of the required files and utilities, such as testing tools, can take advantage of these files.", + "required": [ "id", "specversion", "graph" ], + "properties": { + "id": { + "description": "The machine readable id of the Container Application.", + "type": "string", + "default": "null" + }, + "specversion": { + "description": "The semantic version string of the Container Application Specification used to describe the app. The value SHOULD be '0.0.2'.", + "type": "string", + "default": "0.0.2" + }, + "metadata": { + "$ref": "file:metadata.json" + }, + "params": { + "description": "A list of ParamsObject that contain information in the global context of the application, accessible to it's child graph items.", + "type": "array", + "items": { + "$ref": "file:param.json" + } + }, + "graph": { + "$ref": "file:graph.json" + }, + "requirements": { + "$ref": "file:requirement.json" + } + } +}