Skip to content

Commit

Permalink
spec: enforce unique app names within a pod
Browse files Browse the repository at this point in the history
Since 9aafd97 landed, app references within a pod gained a name field
that is distinct from the name of the underlying image. However, the
spec did not address whether this field needed to be unique, which made
it unclear as to how to facilitate e.g. multiple apps within a pod using
the same image.

This makes the name field a) required, and b) unique within a pod.

Accordingly, apps in a running pod can now be uniquely, globally
identified by the combination of Pod UUID + name.

Fixes appc#84
  • Loading branch information
jonboulle committed Mar 19, 2015
1 parent 40f1966 commit 98141c8
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 7 deletions.
2 changes: 1 addition & 1 deletion SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -868,7 +868,7 @@ JSON Schema for the Pod Manifest, conforming to [RFC4627](https://tools.ietf.org
* **acVersion** (string, required) represents the version of the schema spec (must be in [semver](http://semver.org/) format)
* **acKind** (string, required) must be set to "PodManifest"
* **apps** (list of objects, required) list of apps that will execute inside of this pod. Each app object has the following set of key-value pairs:
* **name** (string, optional) name of the app (restricted to AC Name formatting)
* **name** (string, required) name of the app (restricted to AC Name formatting). This is used to identify an app within a pod, and hence MUST be unique within the list of apps. This may be different from the name of the underlying image (see below).
* **image** (object, required) identifiers of the image providing this app
* **id** (string, required) content hash of the image that this app will execute inside of (must be of the format "type-value", where "type" is "sha512" and value is the hex encoded string of the hash)
* **name** (string, optional) name of the image (restricted to AC Name formatting)
Expand Down
45 changes: 40 additions & 5 deletions schema/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package schema
import (
"encoding/json"
"errors"
"fmt"

"github.com/appc/spec/schema/types"
)
Expand Down Expand Up @@ -65,6 +66,40 @@ func (pm *PodManifest) assertValid() error {

type AppList []RuntimeApp

type appList AppList

func (al *AppList) UnmarshalJSON(data []byte) error {
a := appList{}
err := json.Unmarshal(data, &a)
if err != nil {
return err
}
nal := AppList(a)
if err := nal.assertValid(); err != nil {
return err
}
*al = nal
return nil
}

func (al AppList) MarshalJSON() ([]byte, error) {
if err := al.assertValid(); err != nil {
return nil, err
}
return json.Marshal(appList(al))
}

func (al AppList) assertValid() error {
seen := map[types.ACName]bool{}
for _, a := range al {
if _, ok := seen[a.Name]; ok {
return fmt.Errorf(`duplicate apps of name %q`, a.Name)
}
seen[a.Name] = true
}
return nil
}

// Get retrieves an app by the specified name from the AppList; if there is
// no such app, nil is returned. The returned *RuntimeApp MUST be considered
// read-only.
Expand Down Expand Up @@ -100,13 +135,13 @@ type RuntimeApp struct {
Name types.ACName `json:"name"`
Image RuntimeImage `json:"image"`
App *types.App `json:"app,omitempty"`
Mounts []Mount `json:"mounts"`
Annotations types.Annotations `json:"annotations"`
Mounts []Mount `json:"mounts,omitempty"`
Annotations types.Annotations `json:"annotations,omitempty"`
}

// RuntimeImage describes an image referenced in a RuntimeApp
type RuntimeImage struct {
Name types.ACName `json:"name"`
ID types.Hash `json:"id"`
Labels types.Labels `json:"labels"`
Name *types.ACName `json:"name,omitempty"`
ID types.Hash `json:"id"`
Labels types.Labels `json:"labels,omitempty"`
}
42 changes: 41 additions & 1 deletion schema/pod_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package schema

import "testing"
import (
"testing"

"github.com/appc/spec/schema/types"
)

func TestPodManifestMerge(t *testing.T) {
pmj := `{}`
Expand All @@ -17,3 +21,39 @@ func TestPodManifestMerge(t *testing.T) {
t.Errorf("unexpected error: %v", err)
}
}

func TestAppList(t *testing.T) {
ri := RuntimeImage{
ID: *types.NewHashSHA512([]byte{}),
}
al := AppList{
RuntimeApp{
Name: "foo",
Image: ri,
},
RuntimeApp{
Name: "bar",
Image: ri,
},
}
if _, err := al.MarshalJSON(); err != nil {
t.Errorf("want err=nil, got %v", err)
}
dal := AppList{
RuntimeApp{
Name: "foo",
Image: ri,
},
RuntimeApp{
Name: "bar",
Image: ri,
},
RuntimeApp{
Name: "foo",
Image: ri,
},
}
if _, err := dal.MarshalJSON(); err == nil {
t.Errorf("want err, got nil")
}
}

0 comments on commit 98141c8

Please sign in to comment.