Skip to content
This repository has been archived by the owner on Feb 24, 2020. It is now read-only.

appc/spec: make version a first-class required field #177

Merged
merged 2 commits into from
Dec 3, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 29 additions & 21 deletions app-container/SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,11 @@ This is codified in a [Container Runtime Manifest](#container-runtime-manifest-s

This example container will use a set of three apps:

| Name | Image hash |
| Name | Version | Image hash |
|-----------------------------------------------|-------------------------------------------------|
| example.com/reduce-worker-1.0.0 | sha256-277205b3ae3eb3a8e042a62ae46934b470e431ac |
| example.com/worker-backup-1.0.0 | sha256-3e86b59982e49066c5d813af1c2e2579cbf573de |
| example.com/reduce-worker-register-1.0.0 | sha256-86298e1fdb95ec9a45b5935504e26ec29b8feffa |
| example.com/reduce-worker | 1.0.0 | sha256-277205b3ae3eb3a8e042a62ae46934b470e431ac |
| example.com/worker-backup | 1.0.0 | sha256-3e86b59982e49066c5d813af1c2e2579cbf573de |
| example.com/reduce-worker-register | 1.0.0 | sha256-86298e1fdb95ec9a45b5935504e26ec29b8feffa |

#### Volume Setup

Expand Down Expand Up @@ -223,26 +223,29 @@ Additional isolators will be added to this specification over time.

## App Container Image Discovery

An app name has a URL-like structure, for example `example.com/reduce-worker-1.0.0`.
An app name has a URL-like structure, for example `example.com/reduce-worker`.
However, there is no scheme on this app name so we can't directly resolve it to an app container image.
App Container Image Discovery prescribes a discovery process to retrieve an image based on the app name

### Simple Discovery

First, try to fetch the app container image by prepending `https://` and appending `.aci` and directly retrieving the resulting URL.
For example, given the app name `example.com/reduce-worker-1.0.0`, try to retrieve:
First, try to fetch the app container image by rendering the following template and directly retrieving the resulting URL:

https://example.com/reduce-worker-1.0.0.aci
https://{name}-{version}-{os}-{arch}.aci

For example, given the app name `example.com/reduce-worker`, with version `1.0.0`, arch `amd64`, and os `linux`, try to retrieve:

https://example.com/reduce-worker-1.0.0-linux-amd64.aci

If this fails, move on to meta discovery.
If this succeeds, try fetching the signature using the `.sig` extension:
If this succeeds, try fetching the signature using the same template but with a `.sig` extension:

https://example.com/reduce-worker-1.0.0.sig
https://example.com/reduce-worker-1.0.0-linux-amd64.sig

### Meta Discovery

If simple discovery fails, then we use HTTPS+HTML meta tags to resolve an app name to a downloadable URL.
For example, if the ACE is looking for `example.com/reduce-worker-1.0.0` it will request:
For example, if the ACE is looking for `example.com/reduce-worker` it will request:

https://example.com/reduce-worker?ac-discovery=1

Expand All @@ -253,8 +256,8 @@ Then inspect the HTML returned for meta tags that have the following format:
<meta name="ac-discovery-pubkeys" content="prefix-match url">
```

* ac-discovery should give a URL that can have `.aci` or `.sig` extensions appended to retrieve the app image or signature
* ac-discovery-pubkeys should give a URL that provides a set of public keys that can be used to verify the signature of the app image
* `ac-discovery` should contain a URL template that can be rendered to retrieve the app image or signature
* `ac-discovery-pubkeys` should contain a URL that provides a set of public keys that can be used to verify the signature of the app image

Some examples for different schemes and URLs:

Expand All @@ -267,9 +270,11 @@ Some examples for different schemes and URLs:
The algorithm first ensures that the prefix of the AC Name matches the prefix-match and then if there is a match it will request the equivalent of:

```
curl $(echo "$urltmpl" | sed -e "s/{name}/$appname/" -e "s/{os}/$os/" -e "s/{arch}/$arch/")
curl $(echo "$urltmpl" | sed -e "s/{name}/$appname/" -e "s/{version}/$version/ -e "s/{os}/$os/" -e "s/{arch}/$arch/" -e "s/{ext}/$ext/")
```

where _appname_, _version_, _os_, and _arch_ are set to their respective values for the application, and _ext_ is either `aci` or `sig` for retrieving an app image or signature respectively.

In our example above this would be:

```
Expand Down Expand Up @@ -349,9 +354,10 @@ Examples:

* database
* example.com/database
* example.com/ourapp-1.0.0
* sub-domain.example.com/org/product/release-1.0.0
* example.com/ourapp
* sub-domain.example.com/org/product/release

An AC Name Type cannot be an empty string.
The AC Name Type is used as the primary key for a number of fields in the schemas below.
The schema validator will ensure that the keys conform to these constraints.

Expand All @@ -366,7 +372,8 @@ JSON Schema for the App Image Manifest
{
"acVersion": "0.1.0",
"acKind": "AppManifest",
"name": "example.com/reduce-worker-1.0.0",
"name": "example.com/reduce-worker",
"version": "1.0.0",
"os": "linux",
"arch": "amd64",
"exec": [
Expand Down Expand Up @@ -442,10 +449,11 @@ JSON Schema for the App Image Manifest

* **acVersion** is required and represents the version of the schema specification that the manifest implements (string, must be in [semver](http://semver.org/) format)
* **acKind** is required and must be set to "AppManifest"
* **name** should be unique for every build of an app. It will be used as a human readable index to the container image. The name is restricted to the AC Name formatting.
* **os** is required (string; currently, the only supported value is "linux"). Together with “Arch”, this can be considered to describe the syscall ABI this image requires.
* **arch** is required (string; currently, the only supported value is "amd64"). Together with “OS”, this can be considered to describe the syscall ABI this image requires.
* **exec** the executable to launch and any flags (array of strings) (ACE can append or override)
* **name** is required, and will be used as a human readable index to the container image. (string, restricted to the AC Name formatting)
* **version** is required (string, restricted to the AC Name formatting). When combined with "name", this should be unique for every build of an app.
* **os** is required (string; currently, the only supported value is "linux"). Together with “arch”, this can be considered to describe the syscall ABI this image requires.
* **arch** is required (string; currently, the only supported value is "amd64"). Together with “os”, this can be considered to describe the syscall ABI this image requires.
* **exec** the executable to launch and any flags (array of strings, must be non-empty; ACE can append or override)
* **user/group** are required, and indicate either the GID/UID or the username/group name the app should run as inside of the container (freeform string). If the user or group field begins with a "/" the owner and group of the file found at that absolute path is used as the GID/UID of the process.
* **eventHandlers** are optional, and should be a list of eventHandler objects. eventHandlers allow the app to have several hooks based on lifecycle events. For example, you may want to execute a script before the main process starts up to download a dataset or backup onto the filesystem. An eventHandler is a simple object with two fields - an **exec** (array of strings, ACE can append or override), and a **name**, which should be one of:
* **pre-start** - will be executed and must exit before the long running main **exec** binary is launched
Expand Down
3 changes: 2 additions & 1 deletion app-container/examples/app.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"acKind": "AppManifest",
"name": "example.com/reduce-worker-1.0.0",
"name": "example.com/reduce-worker",
"version": "1.0.0",
"os": "linux",
"arch": "amd64",
"exec": [
Expand Down
15 changes: 11 additions & 4 deletions app-container/schema/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ type AppManifest struct {
ACVersion types.SemVer `json:"acVersion"`
ACKind types.ACKind `json:"acKind"`
Name types.ACName `json:"name"`
OS string `json:"os"`
Arch string `json:"arch"`
Version types.ACName `json:"version"`
OS types.ACName `json:"os"`
Arch types.ACName `json:"arch"`
Exec []string `json:"exec"`
EventHandlers []types.EventHandler `json:"eventHandlers"`
User string `json:"user"`
Expand Down Expand Up @@ -68,10 +69,16 @@ func (am *AppManifest) assertValid() error {
if am.ACVersion.Empty() {
return errors.New(`acVersion must be set`)
}
if am.OS != "linux" {
if am.Name.Empty() {
return errors.New(`name must be set`)
}
if am.Version.Empty() {
return errors.New(`version must be set`)
}
if am.OS.String() != "linux" {
return errors.New(`missing or bad OS (must be "linux")`)
}
if am.Arch != "amd64" {
if am.Arch.String() != "amd64" {
return errors.New(`missing or bad Arch (must be "amd64")`)
}
if len(am.Exec) < 1 {
Expand Down
22 changes: 13 additions & 9 deletions app-container/schema/types/acname.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@ const (
// [1] http://tools.ietf.org/html/rfc1123#page-13
type ACName string

func (l ACName) String() string {
return string(l)
func (n ACName) String() string {
return string(n)
}

// Equals checks whether a given ACName is equal to this one.
func (l ACName) Equals(o ACName) bool {
return strings.ToLower(string(l)) == strings.ToLower(string(o))
func (n ACName) Equals(o ACName) bool {
return strings.ToLower(string(n)) == strings.ToLower(string(o))
}

func (n ACName) Empty() bool {
return n.String() == ""
}

// NewACName generates a new ACName from a string. If the given string is
Expand All @@ -43,20 +47,20 @@ func NewACName(s string) (*ACName, error) {
}

// UnmarshalJSON implements the json.Unmarshaler interface
func (l *ACName) UnmarshalJSON(data []byte) error {
func (n *ACName) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
nl, err := NewACName(s)
nn, err := NewACName(s)
if err != nil {
return err
}
*l = *nl
*n = *nn
return nil
}

// MarshalJSON implements the json.Marshaler interface
func (l *ACName) MarshalJSON() ([]byte, error) {
return json.Marshal(l.String())
func (n *ACName) MarshalJSON() ([]byte, error) {
return json.Marshal(n.String())
}