Merge pull request #12491 from yifan-gu/bump_appc

Godeps: bump appc/spec to v0.6.1+git.
This commit is contained in:
Alex Robinson 2015-08-10 16:38:49 -07:00
commit e899a36de8
50 changed files with 1379 additions and 108 deletions

4
Godeps/Godeps.json generated
View File

@ -29,8 +29,8 @@
},
{
"ImportPath": "github.com/appc/spec/schema",
"Comment": "v0.5.1-55-g87808a3",
"Rev": "87808a37061a4a2e6204ccea5fd2fc930576db94"
"Comment": "v0.6.1-30-gc928a0c",
"Rev": "c928a0c907c96034dfc0a69098b2179db5ae7e37"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws",

View File

@ -1,3 +1,17 @@
// 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 provides definitions for the JSON schema of the different
// manifests in the App Container Specification. The manifests are canonically
// represented in their respective structs:

View File

@ -1,3 +1,17 @@
// 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 (
@ -15,7 +29,7 @@ const (
type ImageManifest struct {
ACKind types.ACKind `json:"acKind"`
ACVersion types.SemVer `json:"acVersion"`
Name types.ACName `json:"name"`
Name types.ACIdentifier `json:"name"`
Labels types.Labels `json:"labels,omitempty"`
App *types.App `json:"app,omitempty"`
Annotations types.Annotations `json:"annotations,omitempty"`

View File

@ -1,3 +1,17 @@
// 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 "testing"
@ -6,7 +20,7 @@ func TestEmptyApp(t *testing.T) {
imj := `
{
"acKind": "ImageManifest",
"acVersion": "0.5.1",
"acVersion": "0.6.1",
"name": "example.com/test"
}
`

View File

@ -1,3 +1,17 @@
// 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 (

View File

@ -1,3 +1,17 @@
// 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 (
@ -140,7 +154,7 @@ type RuntimeApp struct {
// RuntimeImage describes an image referenced in a RuntimeApp
type RuntimeImage struct {
Name *types.ACName `json:"name,omitempty"`
ID types.Hash `json:"id"`
Labels types.Labels `json:"labels,omitempty"`
Name *types.ACIdentifier `json:"name,omitempty"`
ID types.Hash `json:"id"`
Labels types.Labels `json:"labels,omitempty"`
}

View File

@ -1,3 +1,17 @@
// 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 (

View File

@ -0,0 +1,145 @@
// 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 types
import (
"encoding/json"
"errors"
"regexp"
"strings"
)
var (
// ValidACIdentifier is a regular expression that defines a valid ACIdentifier
ValidACIdentifier = regexp.MustCompile("^[a-z0-9]+([-._~/][a-z0-9]+)*$")
invalidACIdentifierChars = regexp.MustCompile("[^a-z0-9-._~/]")
invalidACIdentifierEdges = regexp.MustCompile("(^[-._~/]+)|([-._~/]+$)")
ErrEmptyACIdentifier = ACIdentifierError("ACIdentifier cannot be empty")
ErrInvalidEdgeInACIdentifier = ACIdentifierError("ACIdentifier must start and end with only lower case " +
"alphanumeric characters")
ErrInvalidCharInACIdentifier = ACIdentifierError("ACIdentifier must contain only lower case " +
`alphanumeric characters plus "-._~/"`)
)
// ACIdentifier (an App-Container Identifier) is a format used by keys in image names
// and image labels of the App Container Standard. An ACIdentifier is restricted to numeric
// and lowercase URI unreserved characters defined in URI RFC[1]; all alphabetical characters
// must be lowercase only. Furthermore, the first and last character ("edges") must be
// alphanumeric, and an ACIdentifier cannot be empty. Programmatically, an ACIdentifier must
// conform to the regular expression ValidACIdentifier.
//
// [1] http://tools.ietf.org/html/rfc3986#section-2.3
type ACIdentifier string
func (n ACIdentifier) String() string {
return string(n)
}
// Set sets the ACIdentifier to the given value, if it is valid; if not,
// an error is returned.
func (n *ACIdentifier) Set(s string) error {
nn, err := NewACIdentifier(s)
if err == nil {
*n = *nn
}
return err
}
// Equals checks whether a given ACIdentifier is equal to this one.
func (n ACIdentifier) Equals(o ACIdentifier) bool {
return strings.ToLower(string(n)) == strings.ToLower(string(o))
}
// Empty returns a boolean indicating whether this ACIdentifier is empty.
func (n ACIdentifier) Empty() bool {
return n.String() == ""
}
// NewACIdentifier generates a new ACIdentifier from a string. If the given string is
// not a valid ACIdentifier, nil and an error are returned.
func NewACIdentifier(s string) (*ACIdentifier, error) {
n := ACIdentifier(s)
if err := n.assertValid(); err != nil {
return nil, err
}
return &n, nil
}
// MustACIdentifier generates a new ACIdentifier from a string, If the given string is
// not a valid ACIdentifier, it panics.
func MustACIdentifier(s string) *ACIdentifier {
n, err := NewACIdentifier(s)
if err != nil {
panic(err)
}
return n
}
func (n ACIdentifier) assertValid() error {
s := string(n)
if len(s) == 0 {
return ErrEmptyACIdentifier
}
if invalidACIdentifierChars.MatchString(s) {
return ErrInvalidCharInACIdentifier
}
if invalidACIdentifierEdges.MatchString(s) {
return ErrInvalidEdgeInACIdentifier
}
return nil
}
// UnmarshalJSON implements the json.Unmarshaler interface
func (n *ACIdentifier) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
nn, err := NewACIdentifier(s)
if err != nil {
return err
}
*n = *nn
return nil
}
// MarshalJSON implements the json.Marshaler interface
func (n ACIdentifier) MarshalJSON() ([]byte, error) {
if err := n.assertValid(); err != nil {
return nil, err
}
return json.Marshal(n.String())
}
// SanitizeACIdentifier replaces every invalid ACIdentifier character in s with an underscore
// making it a legal ACIdentifier string. If the character is an upper case letter it
// replaces it with its lower case. It also removes illegal edge characters
// (hyphens, period, underscore, tilde and slash).
//
// This is a helper function and its algorithm is not part of the spec. It
// should not be called without the user explicitly asking for a suggestion.
func SanitizeACIdentifier(s string) (string, error) {
s = strings.ToLower(s)
s = invalidACIdentifierChars.ReplaceAllString(s, "_")
s = invalidACIdentifierEdges.ReplaceAllString(s, "")
if s == "" {
return "", errors.New("must contain at least one valid character")
}
return s, nil
}

View File

@ -0,0 +1,279 @@
// 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 types
import (
"encoding/json"
"reflect"
"testing"
)
var (
goodIdentifiers = []string{
"asdf",
"foo-bar-baz",
"zab_rab_oof",
"database",
"my~database",
"example.com/database",
"example.com/~bob/database",
"example.com/ourapp-1.0.0",
"sub-domain.example.com/org/product/release-1.0.0",
"sub-domain.example.com/org/product/~alice/release-1.0.0",
}
badIdentifiers = []string{
"",
"BAR",
"foo#",
"EXAMPLE.com",
"foo.com/BAR",
"/app",
"app/",
"-app",
"app-",
".app",
"app.",
"_app",
"app_",
"~app",
"app~",
}
)
func TestNewACIdentifier(t *testing.T) {
for i, in := range goodIdentifiers {
l, err := NewACIdentifier(in)
if err != nil {
t.Errorf("#%d: got err=%v, want nil", i, err)
}
if l == nil {
t.Errorf("#%d: got l=nil, want non-nil", i)
}
}
}
func TestNewACIdentifierBad(t *testing.T) {
for i, in := range badIdentifiers {
l, err := NewACIdentifier(in)
if l != nil {
t.Errorf("#%d: got l=%v, want nil", i, l)
}
if err == nil {
t.Errorf("#%d: got err=nil, want non-nil", i)
}
}
}
func TestMustACIdentifier(t *testing.T) {
for i, in := range goodIdentifiers {
l := MustACIdentifier(in)
if l == nil {
t.Errorf("#%d: got l=nil, want non-nil", i)
}
}
}
func expectPanicMustACIdentifier(i int, in string, t *testing.T) {
defer func() {
recover()
}()
_ = MustACIdentifier(in)
t.Errorf("#%d: panic expected", i)
}
func TestMustACIdentifierBad(t *testing.T) {
for i, in := range badIdentifiers {
expectPanicMustACIdentifier(i, in, t)
}
}
func TestSanitizeACIdentifier(t *testing.T) {
tests := map[string]string{
"foo#": "foo",
"FOO": "foo",
"EXAMPLE.com": "example.com",
"foo.com/BAR": "foo.com/bar",
"/app": "app",
"app/": "app",
"-app": "app",
"app-": "app",
".app": "app",
"app.": "app",
"_app": "app",
"app_": "app",
"~app": "app",
"app~": "app",
"app///": "app",
"-/.app..": "app",
"-/app.name-test/-/": "app.name-test",
"sub-domain.example.com/org/product/~alice/release-1.0.0": "sub-domain.example.com/org/product/~alice/release-1.0.0",
}
for in, ex := range tests {
o, err := SanitizeACIdentifier(in)
if err != nil {
t.Errorf("got err=%v, want nil", err)
}
if o != ex {
t.Errorf("got l=%s, want %s", o, ex)
}
}
}
func TestACIdentifierSetGood(t *testing.T) {
tests := map[string]ACIdentifier{
"blargh": ACIdentifier("blargh"),
"example-ourapp-1-0-0": ACIdentifier("example-ourapp-1-0-0"),
}
for in, w := range tests {
// Ensure an empty name is set appropriately
var a ACIdentifier
err := a.Set(in)
if err != nil {
t.Errorf("%v: got err=%v, want nil", in, err)
continue
}
if !reflect.DeepEqual(a, w) {
t.Errorf("%v: a=%v, want %v", in, a, w)
}
// Ensure an existing name is overwritten
var b ACIdentifier = ACIdentifier("orig")
err = b.Set(in)
if err != nil {
t.Errorf("%v: got err=%v, want nil", in, err)
continue
}
if !reflect.DeepEqual(b, w) {
t.Errorf("%v: b=%v, want %v", in, b, w)
}
}
}
func TestACIdentifierSetBad(t *testing.T) {
for i, in := range badIdentifiers {
// Ensure an empty name stays empty
var a ACIdentifier
err := a.Set(in)
if err == nil {
t.Errorf("#%d: err=%v, want nil", i, err)
continue
}
if w := ACIdentifier(""); !reflect.DeepEqual(a, w) {
t.Errorf("%d: a=%v, want %v", i, a, w)
}
// Ensure an existing name is not overwritten
var b ACIdentifier = ACIdentifier("orig")
err = b.Set(in)
if err == nil {
t.Errorf("#%d: err=%v, want nil", i, err)
continue
}
if w := ACIdentifier("orig"); !reflect.DeepEqual(b, w) {
t.Errorf("%d: b=%v, want %v", i, b, w)
}
}
}
func TestSanitizeACIdentifierBad(t *testing.T) {
tests := []string{
"__",
"..",
"//",
"",
".//-"}
for i, in := range tests {
l, err := SanitizeACIdentifier(in)
if l != "" {
t.Errorf("#%d: got l=%v, want nil", i, l)
}
if err == nil {
t.Errorf("#%d: got err=nil, want non-nil", i)
}
}
}
func TestACIdentifierUnmarshalBad(t *testing.T) {
tests := []string{
"",
"garbage",
`""`,
`"EXAMPLE"`,
`"example.com/app#1"`,
`"~example.com/app1"`,
}
for i, in := range tests {
var a, b ACIdentifier
err := a.UnmarshalJSON([]byte(in))
if err == nil {
t.Errorf("#%d: err=nil, want non-nil", i)
} else if !reflect.DeepEqual(a, b) {
t.Errorf("#%d: a=%v, want empty", i, a)
}
}
}
func TestACIdentifierUnmarshalGood(t *testing.T) {
tests := map[string]ACIdentifier{
`"example"`: ACIdentifier("example"),
`"foo-bar"`: ACIdentifier("foo-bar"),
}
for in, w := range tests {
var a ACIdentifier
err := json.Unmarshal([]byte(in), &a)
if err != nil {
t.Errorf("%v: err=%v, want nil", in, err)
} else if !reflect.DeepEqual(a, w) {
t.Errorf("%v: a=%v, want %v", in, a, w)
}
}
}
func TestACIdentifierMarshalBad(t *testing.T) {
tests := map[string]error{
"Foo": ErrInvalidCharInACIdentifier,
"foo#": ErrInvalidCharInACIdentifier,
"-foo": ErrInvalidEdgeInACIdentifier,
"example-": ErrInvalidEdgeInACIdentifier,
"": ErrEmptyACIdentifier,
}
for in, werr := range tests {
a := ACIdentifier(in)
b, gerr := json.Marshal(a)
if b != nil {
t.Errorf("ACIdentifier(%q): want b=nil, got %v", in, b)
}
if jerr, ok := gerr.(*json.MarshalerError); !ok {
t.Errorf("expected JSONMarshalerError")
} else {
if e := jerr.Err; e != werr {
t.Errorf("err=%#v, want %#v", e, werr)
}
}
}
}
func TestACIdentifierMarshalGood(t *testing.T) {
for i, in := range goodIdentifiers {
a := ACIdentifier(in)
b, err := json.Marshal(a)
if !reflect.DeepEqual(b, []byte(`"`+in+`"`)) {
t.Errorf("#%d: marshalled=%v, want %v", i, b, []byte(in))
}
if err != nil {
t.Errorf("#%d: err=%v, want nil", i, err)
}
}
}

View File

@ -1,3 +1,17 @@
// 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 types
import (

View File

@ -1,3 +1,17 @@
// 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 types
import (

View File

@ -1,3 +1,17 @@
// 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 types
import (
@ -9,24 +23,24 @@ import (
var (
// ValidACName is a regular expression that defines a valid ACName
ValidACName = regexp.MustCompile("^[a-z0-9]+([-./][a-z0-9]+)*$")
ValidACName = regexp.MustCompile("^[a-z0-9]+([-][a-z0-9]+)*$")
invalidChars = regexp.MustCompile("[^a-z0-9./-]")
invalidEdges = regexp.MustCompile("(^[./-]+)|([./-]+$)")
invalidACNameChars = regexp.MustCompile("[^a-z0-9-]")
invalidACNameEdges = regexp.MustCompile("(^[-]+)|([-]+$)")
ErrEmptyACName = ACNameError("ACName cannot be empty")
ErrInvalidEdge = ACNameError("ACName must start and end with only lower case " +
ErrEmptyACName = ACNameError("ACName cannot be empty")
ErrInvalidEdgeInACName = ACNameError("ACName must start and end with only lower case " +
"alphanumeric characters")
ErrInvalidChar = ACNameError("ACName must contain only lower case " +
`alphanumeric characters plus ".", "-", "/"`)
ErrInvalidCharInACName = ACNameError("ACName must contain only lower case " +
`alphanumeric characters plus "-"`)
)
// ACName (an App-Container Name) is a format used by keys in different formats
// of the App Container Standard. An ACName is restricted to characters
// accepted by the DNS RFC[1] and "/"; all alphabetical characters must be
// lowercase only. Furthermore, the first and last character ("edges") must be
// alphanumeric, and an ACName cannot be empty. Programmatically, an ACName
// must conform to the regular expression ValidACName.
// of the App Container Standard. An ACName is restricted to numeric and lowercase
// characters accepted by the DNS RFC[1] plus "-"; all alphabetical characters must
// be lowercase only. Furthermore, the first and last character ("edges") must be
// alphanumeric, and an ACName cannot be empty. Programmatically, an ACName must
// conform to the regular expression ValidACName.
//
// [1] http://tools.ietf.org/html/rfc1123#page-13
type ACName string
@ -80,11 +94,11 @@ func (n ACName) assertValid() error {
if len(s) == 0 {
return ErrEmptyACName
}
if invalidChars.MatchString(s) {
return ErrInvalidChar
if invalidACNameChars.MatchString(s) {
return ErrInvalidCharInACName
}
if invalidEdges.MatchString(s) {
return ErrInvalidEdge
if invalidACNameEdges.MatchString(s) {
return ErrInvalidEdgeInACName
}
return nil
}
@ -114,14 +128,14 @@ func (n ACName) MarshalJSON() ([]byte, error) {
// SanitizeACName replaces every invalid ACName character in s with a dash
// making it a legal ACName string. If the character is an upper case letter it
// replaces it with its lower case. It also removes illegal edge characters
// (hyphens, periods and slashes).
// (hyphens).
//
// This is a helper function and its algorithm is not part of the spec. It
// should not be called without the user explicitly asking for a suggestion.
func SanitizeACName(s string) (string, error) {
s = strings.ToLower(s)
s = invalidChars.ReplaceAllString(s, "-")
s = invalidEdges.ReplaceAllString(s, "")
s = invalidACNameChars.ReplaceAllString(s, "-")
s = invalidACNameEdges.ReplaceAllString(s, "")
if s == "" {
return "", errors.New("must contain at least one valid character")

View File

@ -1,3 +1,17 @@
// 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 types
import (
@ -11,14 +25,14 @@ var (
"asdf",
"foo-bar-baz",
"database",
"example.com/database",
"example.com/ourapp-1.0.0",
"sub-domain.example.com/org/product/release-1.0.0",
}
badNames = []string{
"",
"foo#",
"example.com",
"EXAMPLE.com",
"example/database",
"example/database-1.0.0",
"foo.com/BAR",
"example.com/app_1",
"/app",
@ -80,9 +94,9 @@ func TestMustACNameBad(t *testing.T) {
func TestSanitizeACName(t *testing.T) {
tests := map[string]string{
"foo#": "foo",
"EXAMPLE.com": "example.com",
"foo.com/BAR": "foo.com/bar",
"example.com/app_1": "example.com/app-1",
"EXAMPLE.com": "example-com",
"foo.com/BAR": "foo-com-bar",
"example.com/app_1": "example-com-app-1",
"/app": "app",
"app/": "app",
"-app": "app",
@ -91,8 +105,8 @@ func TestSanitizeACName(t *testing.T) {
"app.": "app",
"app///": "app",
"-/.app..": "app",
"-/app.name-test/-/": "app.name-test",
"sub-domain.example.com/org/product/release-1.0.0": "sub-domain.example.com/org/product/release-1.0.0",
"-/app.name-test/-/": "app-name-test",
"sub-domain.example.com/org/product/release-1.0.0": "sub-domain-example-com-org-product-release-1-0-0",
}
for in, ex := range tests {
o, err := SanitizeACName(in)
@ -107,8 +121,8 @@ func TestSanitizeACName(t *testing.T) {
func TestACNameSetGood(t *testing.T) {
tests := map[string]ACName{
"blargh": ACName("blargh"),
"example.com/ourapp-1.0.0": ACName("example.com/ourapp-1.0.0"),
"blargh": ACName("blargh"),
"example-ourapp-1-0-0": ACName("example-ourapp-1-0-0"),
}
for in, w := range tests {
// Ensure an empty name is set appropriately
@ -200,8 +214,8 @@ func TestACNameUnmarshalBad(t *testing.T) {
func TestACNameUnmarshalGood(t *testing.T) {
tests := map[string]ACName{
`"example"`: ACName("example"),
`"foo.com/bar"`: ACName("foo.com/bar"),
`"example"`: ACName("example"),
`"foo-bar"`: ACName("foo-bar"),
}
for in, w := range tests {
var a ACName
@ -216,11 +230,11 @@ func TestACNameUnmarshalGood(t *testing.T) {
func TestACNameMarshalBad(t *testing.T) {
tests := map[string]error{
"Foo": ErrInvalidChar,
"foo#": ErrInvalidChar,
"/foo": ErrInvalidEdge,
"example.com/": ErrInvalidEdge,
"": ErrEmptyACName,
"Foo": ErrInvalidCharInACName,
"foo#": ErrInvalidCharInACName,
"-foo": ErrInvalidEdgeInACName,
"example-": ErrInvalidEdgeInACName,
"": ErrEmptyACName,
}
for in, werr := range tests {
a := ACName(in)

View File

@ -1,3 +1,17 @@
// 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 types
import (
@ -10,12 +24,12 @@ type Annotations []Annotation
type annotations Annotations
type Annotation struct {
Name ACName `json:"name"`
Value string `json:"value"`
Name ACIdentifier `json:"name"`
Value string `json:"value"`
}
func (a Annotations) assertValid() error {
seen := map[ACName]string{}
seen := map[ACIdentifier]string{}
for _, anno := range a {
_, ok := seen[anno.Name]
if ok {
@ -74,7 +88,7 @@ func (a Annotations) Get(name string) (val string, ok bool) {
}
// Set sets the value of an annotation by the given name, overwriting if one already exists.
func (a *Annotations) Set(name ACName, value string) {
func (a *Annotations) Set(name ACIdentifier, value string) {
for i, anno := range *a {
if anno.Name.Equals(name) {
(*a)[i] = Annotation{

View File

@ -1,3 +1,17 @@
// 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 types
import (
@ -6,7 +20,7 @@ import (
)
func makeAnno(n, v string) Annotation {
name, err := NewACName(n)
name, err := NewACIdentifier(n)
if err != nil {
panic(err)
}
@ -191,7 +205,7 @@ func TestAnnotationsSet(t *testing.T) {
a.Set("foo", "bar")
w := Annotations{
Annotation{ACName("foo"), "bar"},
Annotation{ACIdentifier("foo"), "bar"},
}
if !reflect.DeepEqual(w, a) {
t.Fatalf("want %v, got %v", w, a)
@ -199,17 +213,19 @@ func TestAnnotationsSet(t *testing.T) {
a.Set("dog", "woof")
w = Annotations{
Annotation{ACName("foo"), "bar"},
Annotation{ACName("dog"), "woof"},
Annotation{ACIdentifier("foo"), "bar"},
Annotation{ACIdentifier("dog"), "woof"},
}
if !reflect.DeepEqual(w, a) {
t.Fatalf("want %v, got %v", w, a)
}
a.Set("foo", "baz")
a.Set("example.com/foo_bar", "quux")
w = Annotations{
Annotation{ACName("foo"), "baz"},
Annotation{ACName("dog"), "woof"},
Annotation{ACIdentifier("foo"), "baz"},
Annotation{ACIdentifier("dog"), "woof"},
Annotation{ACIdentifier("example.com/foo_bar"), "quux"},
}
if !reflect.DeepEqual(w, a) {
t.Fatalf("want %v, got %v", w, a)

View File

@ -1,3 +1,17 @@
// 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 types
import (

View File

@ -1,3 +1,17 @@
// 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 types
import (

View File

@ -1,3 +1,17 @@
// 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 types
import (

View File

@ -1,3 +1,17 @@
// 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 types
import (

View File

@ -1,3 +1,17 @@
// 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 types
import (
@ -8,16 +22,17 @@ import (
type Dependencies []Dependency
type Dependency struct {
App ACName `json:"app"`
ImageID *Hash `json:"imageID,omitempty"`
Labels Labels `json:"labels,omitempty"`
ImageName ACIdentifier `json:"imageName"`
ImageID *Hash `json:"imageID,omitempty"`
Labels Labels `json:"labels,omitempty"`
Size uint `json:"size,omitempty"`
}
type dependency Dependency
func (d Dependency) assertValid() error {
if len(d.App) < 1 {
return errors.New(`App cannot be empty`)
if len(d.ImageName) < 1 {
return errors.New(`imageName cannot be empty`)
}
return nil
}

View File

@ -1,9 +1,23 @@
// 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 types
import "testing"
func TestEmptyHash(t *testing.T) {
dj := `{"app": "example.com/reduce-worker-base"}`
dj := `{"imageName": "example.com/reduce-worker-base"}`
var d Dependency

View File

@ -1,3 +1,17 @@
// 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 types contains structs representing the various types in the app
// container specification. It is used by the [schema manifest types](../)
// to enforce validation.

View File

@ -1,3 +1,17 @@
// 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 types
import (

View File

@ -1,3 +1,17 @@
// 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 types
import (

View File

@ -1,3 +1,17 @@
// 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 types
import "fmt"
@ -20,6 +34,13 @@ func (e ACVersionError) Error() string {
return string(e)
}
// An ACIdentifierError is returned when a bad value is used for an ACIdentifier
type ACIdentifierError string
func (e ACIdentifierError) Error() string {
return string(e)
}
// An ACNameError is returned when a bad value is used for an ACName
type ACNameError string

View File

@ -1,3 +1,17 @@
// 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 types
import (

View File

@ -1,3 +1,17 @@
// 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 types
import (

View File

@ -1,3 +1,17 @@
// 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 types
import "testing"

View File

@ -1,3 +1,17 @@
// 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 types
import (

View File

@ -1,3 +1,17 @@
// 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 types
import (

View File

@ -1,3 +1,17 @@
// 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 types
import (
@ -5,23 +19,27 @@ import (
)
var (
isolatorMap map[ACName]IsolatorValueConstructor
isolatorMap map[ACIdentifier]IsolatorValueConstructor
)
func init() {
isolatorMap = make(map[ACName]IsolatorValueConstructor)
isolatorMap = make(map[ACIdentifier]IsolatorValueConstructor)
}
type IsolatorValueConstructor func() IsolatorValue
func AddIsolatorValueConstructor(n ACName, i IsolatorValueConstructor) {
func AddIsolatorValueConstructor(n ACIdentifier, i IsolatorValueConstructor) {
isolatorMap[n] = i
}
func AddIsolatorName(n ACIdentifier, ns map[ACIdentifier]struct{}) {
ns[n] = struct{}{}
}
type Isolators []Isolator
// GetByName returns the last isolator in the list by the given name.
func (is *Isolators) GetByName(name ACName) *Isolator {
func (is *Isolators) GetByName(name ACIdentifier) *Isolator {
var i Isolator
for j := len(*is) - 1; j >= 0; j-- {
i = []Isolator(*is)[j]
@ -50,7 +68,7 @@ type IsolatorValue interface {
AssertValid() error
}
type Isolator struct {
Name ACName `json:"name"`
Name ACIdentifier `json:"name"`
ValueRaw *json.RawMessage `json:"value"`
value IsolatorValue
}

View File

@ -1,3 +1,17 @@
// 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 types
import (
@ -7,10 +21,15 @@ import (
const (
LinuxCapabilitiesRetainSetName = "os/linux/capabilities-retain-set"
LinuxCapabilitiesRevokeSetName = "os/linux/capabilities-revoke-set"
LinuxCapabilitiesRevokeSetName = "os/linux/capabilities-remove-set"
)
var LinuxIsolatorNames = make(map[ACIdentifier]struct{})
func init() {
AddIsolatorName(LinuxCapabilitiesRetainSetName, LinuxIsolatorNames)
AddIsolatorName(LinuxCapabilitiesRevokeSetName, LinuxIsolatorNames)
AddIsolatorValueConstructor(LinuxCapabilitiesRetainSetName, NewLinuxCapabilitiesRetainSet)
AddIsolatorValueConstructor(LinuxCapabilitiesRevokeSetName, NewLinuxCapabilitiesRevokeSet)
}

View File

@ -1,3 +1,17 @@
// 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 types
import (
@ -11,6 +25,8 @@ var (
ErrDefaultTrue = errors.New("default must be false")
ErrDefaultRequired = errors.New("default must be true")
ErrRequestNonEmpty = errors.New("request not supported by this resource, must be empty")
ResourceIsolatorNames = make(map[ACIdentifier]struct{})
)
const (
@ -22,6 +38,12 @@ const (
)
func init() {
AddIsolatorName(ResourceBlockBandwidthName, ResourceIsolatorNames)
AddIsolatorName(ResourceBlockIOPSName, ResourceIsolatorNames)
AddIsolatorName(ResourceCPUName, ResourceIsolatorNames)
AddIsolatorName(ResourceMemoryName, ResourceIsolatorNames)
AddIsolatorName(ResourceNetworkBandwidthName, ResourceIsolatorNames)
AddIsolatorValueConstructor(ResourceBlockBandwidthName, NewResourceBlockBandwidth)
AddIsolatorValueConstructor(ResourceBlockIOPSName, NewResourceBlockIOPS)
AddIsolatorValueConstructor(ResourceCPUName, NewResourceCPU)

View File

@ -1,3 +1,17 @@
// 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 types
import (
@ -84,7 +98,7 @@ func TestIsolatorUnmarshal(t *testing.T) {
{
`{
"name": "resource/cpu",
"value": {"request": "30", "limit": "1"}
"value": {"request": "30m", "limit": "1m"}
}`,
false,
},
@ -139,7 +153,7 @@ func TestIsolatorsGetByName(t *testing.T) {
[
{
"name": "resource/cpu",
"value": {"request": "30", "limit": "1"}
"value": {"request": "30m", "limit": "1m"}
},
{
"name": "resource/memory",
@ -150,14 +164,14 @@ func TestIsolatorsGetByName(t *testing.T) {
"value": {"set": ["CAP_KILL"]}
},
{
"name": "os/linux/capabilities-revoke-set",
"name": "os/linux/capabilities-remove-set",
"value": {"set": ["CAP_KILL"]}
}
]
`
tests := []struct {
name ACName
name ACIdentifier
wlimit int64
wrequest int64
wset []LinuxCapability
@ -165,7 +179,7 @@ func TestIsolatorsGetByName(t *testing.T) {
{"resource/cpu", 1, 30, nil},
{"resource/memory", 2147483648, 1000000000, nil},
{"os/linux/capabilities-retain-set", 0, 0, []LinuxCapability{"CAP_KILL"}},
{"os/linux/capabilities-revoke-set", 0, 0, []LinuxCapability{"CAP_KILL"}},
{"os/linux/capabilities-remove-set", 0, 0, []LinuxCapability{"CAP_KILL"}},
}
var is Isolators
@ -188,9 +202,21 @@ func TestIsolatorsGetByName(t *testing.T) {
var r Resource = v
glimit := r.Limit()
grequest := r.Request()
if glimit.Value() != tt.wlimit || grequest.Value() != tt.wrequest {
t.Errorf("#%d: glimit=%v, want %v, grequest=%v, want %v", i, glimit.Value(), tt.wlimit, grequest.Value(), tt.wrequest)
var vlimit, vrequest int64
if tt.name == "resource/cpu" {
vlimit, vrequest = glimit.MilliValue(), grequest.MilliValue()
} else {
vlimit, vrequest = glimit.Value(), grequest.Value()
}
if vlimit != tt.wlimit {
t.Errorf("#%d: glimit=%v, want %v", i, vlimit, tt.wlimit)
}
if vrequest != tt.wrequest {
t.Errorf("#%d: grequest=%v, want %v", i, vrequest, tt.wrequest)
}
case LinuxCapabilitiesSet:
var s LinuxCapabilitiesSet = v
if !reflect.DeepEqual(s.Set(), tt.wset) {

View File

@ -1,3 +1,17 @@
// 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 types
import (
@ -7,7 +21,7 @@ import (
)
var ValidOSArch = map[string][]string{
"linux": {"amd64", "i386", "aarch64", "armv7l", "armv7b"},
"linux": {"amd64", "i386", "aarch64", "aarch64_be", "armv6l", "armv7l", "armv7b"},
"freebsd": {"amd64", "i386", "arm"},
"darwin": {"x86_64", "i386"},
}
@ -17,27 +31,18 @@ type Labels []Label
type labels Labels
type Label struct {
Name ACName `json:"name"`
Value string `json:"value"`
Name ACIdentifier `json:"name"`
Value string `json:"value"`
}
func (l Labels) assertValid() error {
seen := map[ACName]string{}
for _, lbl := range l {
if lbl.Name == "name" {
return fmt.Errorf(`invalid label name: "name"`)
}
_, ok := seen[lbl.Name]
if ok {
return fmt.Errorf(`duplicate labels of name %q`, lbl.Name)
}
seen[lbl.Name] = lbl.Value
}
if os, ok := seen["os"]; ok {
if validArchs, ok := ValidOSArch[os]; !ok {
// IsValidOsArch checks if a OS-architecture combination is valid given a map
// of valid OS-architectures
func IsValidOSArch(labels map[ACIdentifier]string, validOSArch map[string][]string) error {
if os, ok := labels["os"]; ok {
if validArchs, ok := validOSArch[os]; !ok {
// Not a whitelisted OS. TODO: how to warn rather than fail?
validOses := make([]string, 0, len(ValidOSArch))
for validOs := range ValidOSArch {
validOses := make([]string, 0, len(validOSArch))
for validOs := range validOSArch {
validOses = append(validOses, validOs)
}
sort.Strings(validOses)
@ -45,7 +50,7 @@ func (l Labels) assertValid() error {
} else {
// Whitelisted OS. We check arch here, as arch makes sense only
// when os is defined.
if arch, ok := seen["arch"]; ok {
if arch, ok := labels["arch"]; ok {
found := false
for _, validArch := range validArchs {
if arch == validArch {
@ -62,6 +67,21 @@ func (l Labels) assertValid() error {
return nil
}
func (l Labels) assertValid() error {
seen := map[ACIdentifier]string{}
for _, lbl := range l {
if lbl.Name == "name" {
return fmt.Errorf(`invalid label name: "name"`)
}
_, ok := seen[lbl.Name]
if ok {
return fmt.Errorf(`duplicate labels of name %q`, lbl.Name)
}
seen[lbl.Name] = lbl.Value
}
return IsValidOSArch(seen, ValidOSArch)
}
func (l Labels) MarshalJSON() ([]byte, error) {
if err := l.assertValid(); err != nil {
return nil, err
@ -92,17 +112,17 @@ func (l Labels) Get(name string) (val string, ok bool) {
return "", false
}
// ToMap creates a map[ACName]string.
func (l Labels) ToMap() map[ACName]string {
labelsMap := make(map[ACName]string)
// ToMap creates a map[ACIdentifier]string.
func (l Labels) ToMap() map[ACIdentifier]string {
labelsMap := make(map[ACIdentifier]string)
for _, lbl := range l {
labelsMap[lbl.Name] = lbl.Value
}
return labelsMap
}
// LabelsFromMap creates Labels from a map[ACName]string
func LabelsFromMap(labelsMap map[ACName]string) (Labels, error) {
// LabelsFromMap creates Labels from a map[ACIdentifier]string
func LabelsFromMap(labelsMap map[ACIdentifier]string) (Labels, error) {
labels := Labels{}
for n, v := range labelsMap {
labels = append(labels, Label{Name: n, Value: v})

View File

@ -1,3 +1,17 @@
// 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 types
import (
@ -19,6 +33,26 @@ func TestLabels(t *testing.T) {
`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "aarch64"}]`,
"",
},
{
`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "arm64"}]`,
`bad arch "arm64" for linux`,
},
{
`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "aarch64_be"}]`,
"",
},
{
`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "arm64_be"}]`,
`bad arch "arm64_be" for linux`,
},
{
`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "arm"}]`,
`bad arch "arm" for linux`,
},
{
`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "armv6l"}]`,
"",
},
{
`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "armv7l"}]`,
"",
@ -39,10 +73,6 @@ func TestLabels(t *testing.T) {
`[{"name": "os", "value": "freebsd"}, {"name": "arch", "value": "armv7b"}]`,
`bad arch "armv7b" for freebsd`,
},
{
`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "arm"}]`,
`bad arch "arm" for linux`,
},
{
`[{"name": "name"}]`,
`invalid label name: "name"`,

View File

@ -1,3 +1,17 @@
// 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 types
import (
@ -43,7 +57,6 @@ func MountPointFromString(mp string) (*MountPoint, error) {
return nil, fmt.Errorf("label %s with multiple values %q", key, val)
}
// TOOD(philips): make this less hardcoded
switch key {
case "name":
acn, err := NewACName(val[0])

View File

@ -1,3 +1,17 @@
// 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 types
import (

View File

@ -1,9 +1,33 @@
// 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 types
import (
"encoding/json"
"errors"
"fmt"
"net/url"
"strconv"
"strings"
)
type Port struct {
Name ACName `json:"name"`
Protocol string `json:"protocol"`
Port uint `json:"port"`
Count uint `json:"count"`
SocketActivated bool `json:"socketActivated"`
}
@ -11,3 +35,99 @@ type ExposedPort struct {
Name ACName `json:"name"`
HostPort uint `json:"hostPort"`
}
type port Port
func (p *Port) UnmarshalJSON(data []byte) error {
var pp port
if err := json.Unmarshal(data, &pp); err != nil {
return err
}
np := Port(pp)
if err := np.assertValid(); err != nil {
return err
}
if np.Count == 0 {
np.Count = 1
}
*p = np
return nil
}
func (p Port) MarshalJSON() ([]byte, error) {
if err := p.assertValid(); err != nil {
return nil, err
}
return json.Marshal(port(p))
}
func (p Port) assertValid() error {
// Although there are no guarantees, most (if not all)
// transport protocols use 16 bit ports
if p.Port > 65535 || p.Port < 1 {
return errors.New("port must be in 1-65535 range")
}
if p.Port+p.Count > 65536 {
return errors.New("end of port range must be in 1-65535 range")
}
return nil
}
// PortFromString takes a command line port parameter and returns a port
//
// It is useful for actool patch-manifest --ports
//
// Example port parameters:
// health-check,protocol=udp,port=8000
// query,protocol=tcp,port=8080,count=1,socketActivated=true
func PortFromString(pt string) (*Port, error) {
var port Port
pt = "name=" + pt
v, err := url.ParseQuery(strings.Replace(pt, ",", "&", -1))
if err != nil {
return nil, err
}
for key, val := range v {
if len(val) > 1 {
return nil, fmt.Errorf("label %s with multiple values %q", key, val)
}
switch key {
case "name":
acn, err := NewACName(val[0])
if err != nil {
return nil, err
}
port.Name = *acn
case "protocol":
port.Protocol = val[0]
case "port":
p, err := strconv.ParseUint(val[0], 10, 16)
if err != nil {
return nil, err
}
port.Port = uint(p)
case "count":
cnt, err := strconv.ParseUint(val[0], 10, 16)
if err != nil {
return nil, err
}
port.Count = uint(cnt)
case "socketActivated":
sa, err := strconv.ParseBool(val[0])
if err != nil {
return nil, err
}
port.SocketActivated = sa
default:
return nil, fmt.Errorf("unknown port parameter %q", key)
}
}
err = port.assertValid()
if err != nil {
return nil, err
}
return &port, nil
}

View File

@ -0,0 +1,48 @@
// 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 types
import (
"testing"
)
func TestGoodPort(t *testing.T) {
p := Port{
Port: 32456,
Count: 100,
}
if err := p.assertValid(); err != nil {
t.Errorf("good port assertion failed: %v", err)
}
}
func TestBadPort(t *testing.T) {
p := Port{
Port: 88888,
}
if p.assertValid() == nil {
t.Errorf("bad port asserted valid")
}
}
func TestBadRange(t *testing.T) {
p := Port{
Port: 32456,
Count: 45678,
}
if p.assertValid() == nil {
t.Errorf("bad port range asserted valid")
}
}

View File

@ -1,3 +1,17 @@
// 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 types
import (

View File

@ -1,3 +1,17 @@
// 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 types
import (

View File

@ -1,3 +1,17 @@
// 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 types
import (

View File

@ -1,3 +1,17 @@
// 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 types
import (

View File

@ -1,3 +1,17 @@
// 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 types
import (

View File

@ -1,3 +1,17 @@
// 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 types
import "testing"

View File

@ -1,3 +1,17 @@
// 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 types
import (
@ -93,7 +107,6 @@ func VolumeFromString(vp string) (*Volume, error) {
return nil, fmt.Errorf("label %s with multiple values %q", key, val)
}
// TOOD(philips): make this less hardcoded
switch key {
case "name":
acn, err := NewACName(val[0])

View File

@ -1,3 +1,17 @@
// 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 types
import (

View File

@ -1,3 +1,17 @@
// 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 (
@ -8,7 +22,7 @@ const (
// version represents the canonical version of the appc spec and tooling.
// For now, the schema and tooling is coupled with the spec itself, so
// this must be kept in sync with the VERSION file in the root of the repo.
version string = "0.5.1+git"
version string = "0.6.1+git"
)
var (

View File

@ -273,7 +273,7 @@ func setIsolators(app *appctypes.App, c *api.Container) error {
r.request = quantity.String()
resources[name] = r
}
var acName appctypes.ACName
var acName appctypes.ACIdentifier
for name, res := range resources {
switch name {
case api.ResourceCPU:
@ -412,10 +412,14 @@ func (r *runtime) makePodManifest(pod *api.Pod) (*appcschema.PodManifest, error)
return nil, err
}
name, err := appctypes.SanitizeACName(c.Name)
if err != nil {
return nil, err
}
appName := appctypes.MustACName(name)
manifest.Apps = append(manifest.Apps, appcschema.RuntimeApp{
// TODO(yifan): We should allow app name to be different with
// image name. See https://github.com/coreos/rkt/pull/640.
Name: imgManifest.Name,
Name: *appName,
Image: appcschema.RuntimeImage{ID: *hash},
App: imgManifest.App,
})