183 lines
5.1 KiB
Go
183 lines
5.1 KiB
Go
|
// Copyright 2015 The appc Authors
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
package schema
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/json"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
|
||
|
"github.com/appc/spec/schema/types"
|
||
|
|
||
|
"go4.org/errorutil"
|
||
|
)
|
||
|
|
||
|
const PodManifestKind = types.ACKind("PodManifest")
|
||
|
|
||
|
type PodManifest struct {
|
||
|
ACVersion types.SemVer `json:"acVersion"`
|
||
|
ACKind types.ACKind `json:"acKind"`
|
||
|
Apps AppList `json:"apps"`
|
||
|
Volumes []types.Volume `json:"volumes"`
|
||
|
Isolators []types.Isolator `json:"isolators"`
|
||
|
Annotations types.Annotations `json:"annotations"`
|
||
|
Ports []types.ExposedPort `json:"ports"`
|
||
|
UserAnnotations types.UserAnnotations `json:"userAnnotations,omitempty"`
|
||
|
UserLabels types.UserLabels `json:"userLabels,omitempty"`
|
||
|
}
|
||
|
|
||
|
// podManifest is a model to facilitate extra validation during the
|
||
|
// unmarshalling of the PodManifest
|
||
|
type podManifest PodManifest
|
||
|
|
||
|
func BlankPodManifest() *PodManifest {
|
||
|
return &PodManifest{ACKind: PodManifestKind, ACVersion: AppContainerVersion}
|
||
|
}
|
||
|
|
||
|
func (pm *PodManifest) UnmarshalJSON(data []byte) error {
|
||
|
p := podManifest(*pm)
|
||
|
err := json.Unmarshal(data, &p)
|
||
|
if err != nil {
|
||
|
if serr, ok := err.(*json.SyntaxError); ok {
|
||
|
line, col, highlight := errorutil.HighlightBytePosition(bytes.NewReader(data), serr.Offset)
|
||
|
return fmt.Errorf("\nError at line %d, column %d\n%s%v", line, col, highlight, err)
|
||
|
}
|
||
|
return err
|
||
|
}
|
||
|
npm := PodManifest(p)
|
||
|
if err := npm.assertValid(); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
*pm = npm
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (pm PodManifest) MarshalJSON() ([]byte, error) {
|
||
|
if err := pm.assertValid(); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return json.Marshal(podManifest(pm))
|
||
|
}
|
||
|
|
||
|
var pmKindError = types.InvalidACKindError(PodManifestKind)
|
||
|
|
||
|
// assertValid performs extra assertions on an PodManifest to
|
||
|
// ensure that fields are set appropriately, etc. It is used exclusively when
|
||
|
// marshalling and unmarshalling an PodManifest. Most
|
||
|
// field-specific validation is performed through the individual types being
|
||
|
// marshalled; assertValid() should only deal with higher-level validation.
|
||
|
func (pm *PodManifest) assertValid() error {
|
||
|
if pm.ACKind != PodManifestKind {
|
||
|
return pmKindError
|
||
|
}
|
||
|
|
||
|
// ensure volumes names are unique (unique key)
|
||
|
volNames := make(map[types.ACName]bool, len(pm.Volumes))
|
||
|
for _, vol := range pm.Volumes {
|
||
|
if volNames[vol.Name] {
|
||
|
return fmt.Errorf("duplicate volume name %q", vol.Name)
|
||
|
}
|
||
|
volNames[vol.Name] = true
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
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.
|
||
|
func (al AppList) Get(name types.ACName) *RuntimeApp {
|
||
|
for _, a := range al {
|
||
|
if name.Equals(a.Name) {
|
||
|
aa := a
|
||
|
return &aa
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Mount describes the mapping between a volume and the path it is mounted
|
||
|
// inside of an app's filesystem.
|
||
|
// The AppVolume is optional. If missing, the pod-level Volume of the
|
||
|
// same name shall be used.
|
||
|
type Mount struct {
|
||
|
Volume types.ACName `json:"volume"`
|
||
|
Path string `json:"path"`
|
||
|
AppVolume *types.Volume `json:"appVolume,omitempty"`
|
||
|
}
|
||
|
|
||
|
func (r Mount) assertValid() error {
|
||
|
if r.Volume.Empty() {
|
||
|
return errors.New("volume must be set")
|
||
|
}
|
||
|
if r.Path == "" {
|
||
|
return errors.New("path must be set")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// RuntimeApp describes an application referenced in a PodManifest
|
||
|
type RuntimeApp struct {
|
||
|
Name types.ACName `json:"name"`
|
||
|
Image RuntimeImage `json:"image"`
|
||
|
App *types.App `json:"app,omitempty"`
|
||
|
ReadOnlyRootFS bool `json:"readOnlyRootFS,omitempty"`
|
||
|
Mounts []Mount `json:"mounts,omitempty"`
|
||
|
Annotations types.Annotations `json:"annotations,omitempty"`
|
||
|
}
|
||
|
|
||
|
// RuntimeImage describes an image referenced in a RuntimeApp
|
||
|
type RuntimeImage struct {
|
||
|
Name *types.ACIdentifier `json:"name,omitempty"`
|
||
|
ID types.Hash `json:"id"`
|
||
|
Labels types.Labels `json:"labels,omitempty"`
|
||
|
}
|