mirror of
https://github.com/kemko/nomad.git
synced 2026-01-06 18:35:44 +03:00
Expose rkt DriverNetwork
Currently the rkt driver does not expose a DriverNetwork instance after starting the container, which means that address_mode = 'driver' does not work. To get the container network information, we can call `rkt status` on the UUID of the container and grab the container IP from there. For the port map, we need to grab the pod manifest as it will tell us which ports the container exposes. We then cross-reference the configured port name with the container port names, and use that to create a correct port mapping. To avoid doing a (bad) reimplementation of the appc schema(which rkt uses for its manifest) and rkt apis, we pull those in as vendored dependencies. The versions used are the same ones that rkt use in their glide dependency configuration for version 1.28.0.
This commit is contained in:
202
vendor/github.com/appc/spec/LICENSE
generated
vendored
Normal file
202
vendor/github.com/appc/spec/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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.
|
||||
|
||||
40
vendor/github.com/appc/spec/schema/common/common.go
generated
vendored
Normal file
40
vendor/github.com/appc/spec/schema/common/common.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MakeQueryString takes a comma-separated LABEL=VALUE string and returns an
|
||||
// "&"-separated string with URL escaped values.
|
||||
//
|
||||
// Examples:
|
||||
// version=1.0.0,label=v1+v2 -> version=1.0.0&label=v1%2Bv2
|
||||
// name=db,source=/tmp$1 -> name=db&source=%2Ftmp%241
|
||||
func MakeQueryString(app string) (string, error) {
|
||||
parts := strings.Split(app, ",")
|
||||
escapedParts := make([]string, len(parts))
|
||||
for i, s := range parts {
|
||||
p := strings.SplitN(s, "=", 2)
|
||||
if len(p) != 2 {
|
||||
return "", fmt.Errorf("malformed string %q - has a label without a value: %s", app, p[0])
|
||||
}
|
||||
escapedParts[i] = fmt.Sprintf("%s=%s", p[0], url.QueryEscape(p[1]))
|
||||
}
|
||||
return strings.Join(escapedParts, "&"), nil
|
||||
}
|
||||
25
vendor/github.com/appc/spec/schema/doc.go
generated
vendored
Normal file
25
vendor/github.com/appc/spec/schema/doc.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// 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:
|
||||
// - `ImageManifest`
|
||||
// - `PodManifest`
|
||||
//
|
||||
// Validation is performed through serialization: if a blob of JSON data will
|
||||
// unmarshal to one of the *Manifests, it is considered a valid implementation
|
||||
// of the standard. Similarly, if a constructed *Manifest struct marshals
|
||||
// successfully to JSON, it must be valid.
|
||||
package schema
|
||||
103
vendor/github.com/appc/spec/schema/image.go
generated
vendored
Normal file
103
vendor/github.com/appc/spec/schema/image.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
// 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 (
|
||||
ACIExtension = ".aci"
|
||||
ImageManifestKind = types.ACKind("ImageManifest")
|
||||
)
|
||||
|
||||
type ImageManifest struct {
|
||||
ACKind types.ACKind `json:"acKind"`
|
||||
ACVersion types.SemVer `json:"acVersion"`
|
||||
Name types.ACIdentifier `json:"name"`
|
||||
Labels types.Labels `json:"labels,omitempty"`
|
||||
App *types.App `json:"app,omitempty"`
|
||||
Annotations types.Annotations `json:"annotations,omitempty"`
|
||||
Dependencies types.Dependencies `json:"dependencies,omitempty"`
|
||||
PathWhitelist []string `json:"pathWhitelist,omitempty"`
|
||||
}
|
||||
|
||||
// imageManifest is a model to facilitate extra validation during the
|
||||
// unmarshalling of the ImageManifest
|
||||
type imageManifest ImageManifest
|
||||
|
||||
func BlankImageManifest() *ImageManifest {
|
||||
return &ImageManifest{ACKind: ImageManifestKind, ACVersion: AppContainerVersion}
|
||||
}
|
||||
|
||||
func (im *ImageManifest) UnmarshalJSON(data []byte) error {
|
||||
a := imageManifest(*im)
|
||||
err := json.Unmarshal(data, &a)
|
||||
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
|
||||
}
|
||||
nim := ImageManifest(a)
|
||||
if err := nim.assertValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
*im = nim
|
||||
return nil
|
||||
}
|
||||
|
||||
func (im ImageManifest) MarshalJSON() ([]byte, error) {
|
||||
if err := im.assertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(imageManifest(im))
|
||||
}
|
||||
|
||||
var imKindError = types.InvalidACKindError(ImageManifestKind)
|
||||
|
||||
// assertValid performs extra assertions on an ImageManifest to ensure that
|
||||
// fields are set appropriately, etc. It is used exclusively when marshalling
|
||||
// and unmarshalling an ImageManifest. Most field-specific validation is
|
||||
// performed through the individual types being marshalled; assertValid()
|
||||
// should only deal with higher-level validation.
|
||||
func (im *ImageManifest) assertValid() error {
|
||||
if im.ACKind != ImageManifestKind {
|
||||
return imKindError
|
||||
}
|
||||
if im.ACVersion.Empty() {
|
||||
return errors.New(`acVersion must be set`)
|
||||
}
|
||||
if im.Name.Empty() {
|
||||
return errors.New(`name must be set`)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (im *ImageManifest) GetLabel(name string) (val string, ok bool) {
|
||||
return im.Labels.Get(name)
|
||||
}
|
||||
|
||||
func (im *ImageManifest) GetAnnotation(name string) (val string, ok bool) {
|
||||
return im.Annotations.Get(name)
|
||||
}
|
||||
42
vendor/github.com/appc/spec/schema/kind.go
generated
vendored
Normal file
42
vendor/github.com/appc/spec/schema/kind.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
// 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 (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/appc/spec/schema/types"
|
||||
)
|
||||
|
||||
type Kind struct {
|
||||
ACVersion types.SemVer `json:"acVersion"`
|
||||
ACKind types.ACKind `json:"acKind"`
|
||||
}
|
||||
|
||||
type kind Kind
|
||||
|
||||
func (k *Kind) UnmarshalJSON(data []byte) error {
|
||||
nk := kind{}
|
||||
err := json.Unmarshal(data, &nk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*k = Kind(nk)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k Kind) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(kind(k))
|
||||
}
|
||||
182
vendor/github.com/appc/spec/schema/pod.go
generated
vendored
Normal file
182
vendor/github.com/appc/spec/schema/pod.go
generated
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
// 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"`
|
||||
}
|
||||
145
vendor/github.com/appc/spec/schema/types/acidentifier.go
generated
vendored
Normal file
145
vendor/github.com/appc/spec/schema/types/acidentifier.go
generated
vendored
Normal 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
|
||||
}
|
||||
67
vendor/github.com/appc/spec/schema/types/ackind.go
generated
vendored
Normal file
67
vendor/github.com/appc/spec/schema/types/ackind.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
// 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"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoACKind = ACKindError("ACKind must be set")
|
||||
)
|
||||
|
||||
// ACKind wraps a string to define a field which must be set with one of
|
||||
// several ACKind values. If it is unset, or has an invalid value, the field
|
||||
// will refuse to marshal/unmarshal.
|
||||
type ACKind string
|
||||
|
||||
func (a ACKind) String() string {
|
||||
return string(a)
|
||||
}
|
||||
|
||||
func (a ACKind) assertValid() error {
|
||||
s := a.String()
|
||||
switch s {
|
||||
case "ImageManifest", "PodManifest":
|
||||
return nil
|
||||
case "":
|
||||
return ErrNoACKind
|
||||
default:
|
||||
msg := fmt.Sprintf("bad ACKind: %s", s)
|
||||
return ACKindError(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (a ACKind) MarshalJSON() ([]byte, error) {
|
||||
if err := a.assertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(a.String())
|
||||
}
|
||||
|
||||
func (a *ACKind) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
err := json.Unmarshal(data, &s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
na := ACKind(s)
|
||||
if err := na.assertValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
*a = na
|
||||
return nil
|
||||
}
|
||||
145
vendor/github.com/appc/spec/schema/types/acname.go
generated
vendored
Normal file
145
vendor/github.com/appc/spec/schema/types/acname.go
generated
vendored
Normal 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 (
|
||||
// ValidACName is a regular expression that defines a valid ACName
|
||||
ValidACName = regexp.MustCompile("^[a-z0-9]+([-][a-z0-9]+)*$")
|
||||
|
||||
invalidACNameChars = regexp.MustCompile("[^a-z0-9-]")
|
||||
invalidACNameEdges = regexp.MustCompile("(^[-]+)|([-]+$)")
|
||||
|
||||
ErrEmptyACName = ACNameError("ACName cannot be empty")
|
||||
ErrInvalidEdgeInACName = ACNameError("ACName must start and end with only lower case " +
|
||||
"alphanumeric characters")
|
||||
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 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
|
||||
|
||||
func (n ACName) String() string {
|
||||
return string(n)
|
||||
}
|
||||
|
||||
// Set sets the ACName to the given value, if it is valid; if not,
|
||||
// an error is returned.
|
||||
func (n *ACName) Set(s string) error {
|
||||
nn, err := NewACName(s)
|
||||
if err == nil {
|
||||
*n = *nn
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Equals checks whether a given ACName is equal to this one.
|
||||
func (n ACName) Equals(o ACName) bool {
|
||||
return strings.ToLower(string(n)) == strings.ToLower(string(o))
|
||||
}
|
||||
|
||||
// Empty returns a boolean indicating whether this ACName is empty.
|
||||
func (n ACName) Empty() bool {
|
||||
return n.String() == ""
|
||||
}
|
||||
|
||||
// NewACName generates a new ACName from a string. If the given string is
|
||||
// not a valid ACName, nil and an error are returned.
|
||||
func NewACName(s string) (*ACName, error) {
|
||||
n := ACName(s)
|
||||
if err := n.assertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &n, nil
|
||||
}
|
||||
|
||||
// MustACName generates a new ACName from a string, If the given string is
|
||||
// not a valid ACName, it panics.
|
||||
func MustACName(s string) *ACName {
|
||||
n, err := NewACName(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (n ACName) assertValid() error {
|
||||
s := string(n)
|
||||
if len(s) == 0 {
|
||||
return ErrEmptyACName
|
||||
}
|
||||
if invalidACNameChars.MatchString(s) {
|
||||
return ErrInvalidCharInACName
|
||||
}
|
||||
if invalidACNameEdges.MatchString(s) {
|
||||
return ErrInvalidEdgeInACName
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface
|
||||
func (n *ACName) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
nn, err := NewACName(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*n = *nn
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface
|
||||
func (n ACName) MarshalJSON() ([]byte, error) {
|
||||
if err := n.assertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(n.String())
|
||||
}
|
||||
|
||||
// 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).
|
||||
//
|
||||
// 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 = invalidACNameChars.ReplaceAllString(s, "-")
|
||||
s = invalidACNameEdges.ReplaceAllString(s, "")
|
||||
|
||||
if s == "" {
|
||||
return "", errors.New("must contain at least one valid character")
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
106
vendor/github.com/appc/spec/schema/types/annotations.go
generated
vendored
Normal file
106
vendor/github.com/appc/spec/schema/types/annotations.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
// 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"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Annotations []Annotation
|
||||
|
||||
type annotations Annotations
|
||||
|
||||
type Annotation struct {
|
||||
Name ACIdentifier `json:"name"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
func (a Annotations) assertValid() error {
|
||||
seen := map[ACIdentifier]string{}
|
||||
for _, anno := range a {
|
||||
_, ok := seen[anno.Name]
|
||||
if ok {
|
||||
return fmt.Errorf(`duplicate annotations of name %q`, anno.Name)
|
||||
}
|
||||
seen[anno.Name] = anno.Value
|
||||
}
|
||||
if c, ok := seen["created"]; ok {
|
||||
if _, err := NewDate(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if h, ok := seen["homepage"]; ok {
|
||||
if _, err := NewURL(h); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if d, ok := seen["documentation"]; ok {
|
||||
if _, err := NewURL(d); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a Annotations) MarshalJSON() ([]byte, error) {
|
||||
if err := a.assertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(annotations(a))
|
||||
}
|
||||
|
||||
func (a *Annotations) UnmarshalJSON(data []byte) error {
|
||||
var ja annotations
|
||||
if err := json.Unmarshal(data, &ja); err != nil {
|
||||
return err
|
||||
}
|
||||
na := Annotations(ja)
|
||||
if err := na.assertValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
*a = na
|
||||
return nil
|
||||
}
|
||||
|
||||
// Retrieve the value of an annotation by the given name from Annotations, if
|
||||
// it exists.
|
||||
func (a Annotations) Get(name string) (val string, ok bool) {
|
||||
for _, anno := range a {
|
||||
if anno.Name.String() == name {
|
||||
return anno.Value, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Set sets the value of an annotation by the given name, overwriting if one already exists.
|
||||
func (a *Annotations) Set(name ACIdentifier, value string) {
|
||||
for i, anno := range *a {
|
||||
if anno.Name.Equals(name) {
|
||||
(*a)[i] = Annotation{
|
||||
Name: name,
|
||||
Value: value,
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
anno := Annotation{
|
||||
Name: name,
|
||||
Value: value,
|
||||
}
|
||||
*a = append(*a, anno)
|
||||
}
|
||||
95
vendor/github.com/appc/spec/schema/types/app.go
generated
vendored
Normal file
95
vendor/github.com/appc/spec/schema/types/app.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
// 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"
|
||||
"path"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
Exec Exec `json:"exec"`
|
||||
EventHandlers []EventHandler `json:"eventHandlers,omitempty"`
|
||||
User string `json:"user"`
|
||||
Group string `json:"group"`
|
||||
SupplementaryGIDs []int `json:"supplementaryGIDs,omitempty"`
|
||||
WorkingDirectory string `json:"workingDirectory,omitempty"`
|
||||
Environment Environment `json:"environment,omitempty"`
|
||||
MountPoints []MountPoint `json:"mountPoints,omitempty"`
|
||||
Ports []Port `json:"ports,omitempty"`
|
||||
Isolators Isolators `json:"isolators,omitempty"`
|
||||
UserAnnotations UserAnnotations `json:"userAnnotations,omitempty"`
|
||||
UserLabels UserLabels `json:"userLabels,omitempty"`
|
||||
}
|
||||
|
||||
// app is a model to facilitate extra validation during the
|
||||
// unmarshalling of the App
|
||||
type app App
|
||||
|
||||
func (a *App) UnmarshalJSON(data []byte) error {
|
||||
ja := app(*a)
|
||||
err := json.Unmarshal(data, &ja)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
na := App(ja)
|
||||
if err := na.assertValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
if na.Environment == nil {
|
||||
na.Environment = make(Environment, 0)
|
||||
}
|
||||
*a = na
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a App) MarshalJSON() ([]byte, error) {
|
||||
if err := a.assertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(app(a))
|
||||
}
|
||||
|
||||
func (a *App) assertValid() error {
|
||||
if err := a.Exec.assertValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
if a.User == "" {
|
||||
return errors.New(`user is required`)
|
||||
}
|
||||
if a.Group == "" {
|
||||
return errors.New(`group is required`)
|
||||
}
|
||||
if !path.IsAbs(a.WorkingDirectory) && a.WorkingDirectory != "" {
|
||||
return errors.New("workingDirectory must be an absolute path")
|
||||
}
|
||||
eh := make(map[string]bool)
|
||||
for _, e := range a.EventHandlers {
|
||||
name := e.Name
|
||||
if eh[name] {
|
||||
return fmt.Errorf("Only one eventHandler of name %q allowed", name)
|
||||
}
|
||||
eh[name] = true
|
||||
}
|
||||
if err := a.Environment.assertValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := a.Isolators.assertValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
60
vendor/github.com/appc/spec/schema/types/date.go
generated
vendored
Normal file
60
vendor/github.com/appc/spec/schema/types/date.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
// 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"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Date wraps time.Time to marshal/unmarshal to/from JSON strings in strict
|
||||
// accordance with RFC3339
|
||||
// TODO(jonboulle): golang's implementation seems slightly buggy here;
|
||||
// according to http://tools.ietf.org/html/rfc3339#section-5.6 , applications
|
||||
// may choose to separate the date and time with a space instead of a T
|
||||
// character (for example, `date --rfc-3339` on GNU coreutils) - but this is
|
||||
// considered an error by go's parser. File a bug?
|
||||
type Date time.Time
|
||||
|
||||
func NewDate(s string) (*Date, error) {
|
||||
t, err := time.Parse(time.RFC3339, s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("bad Date: %v", err)
|
||||
}
|
||||
d := Date(t)
|
||||
return &d, nil
|
||||
}
|
||||
|
||||
func (d Date) String() string {
|
||||
return time.Time(d).Format(time.RFC3339)
|
||||
}
|
||||
|
||||
func (d *Date) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
nd, err := NewDate(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*d = *nd
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d Date) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(d.String())
|
||||
}
|
||||
58
vendor/github.com/appc/spec/schema/types/dependencies.go
generated
vendored
Normal file
58
vendor/github.com/appc/spec/schema/types/dependencies.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
type Dependencies []Dependency
|
||||
|
||||
type Dependency struct {
|
||||
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.ImageName) < 1 {
|
||||
return errors.New(`imageName cannot be empty`)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d Dependency) MarshalJSON() ([]byte, error) {
|
||||
if err := d.assertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(dependency(d))
|
||||
}
|
||||
|
||||
func (d *Dependency) UnmarshalJSON(data []byte) error {
|
||||
var jd dependency
|
||||
if err := json.Unmarshal(data, &jd); err != nil {
|
||||
return err
|
||||
}
|
||||
nd := Dependency(jd)
|
||||
if err := nd.assertValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
*d = nd
|
||||
return nil
|
||||
}
|
||||
18
vendor/github.com/appc/spec/schema/types/doc.go
generated
vendored
Normal file
18
vendor/github.com/appc/spec/schema/types/doc.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
// 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.
|
||||
package types
|
||||
110
vendor/github.com/appc/spec/schema/types/environment.go
generated
vendored
Normal file
110
vendor/github.com/appc/spec/schema/types/environment.go
generated
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
// 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"
|
||||
"fmt"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var (
|
||||
envPattern = regexp.MustCompile("^[A-Za-z_][A-Za-z_0-9.-]*$")
|
||||
)
|
||||
|
||||
type Environment []EnvironmentVariable
|
||||
|
||||
type environment Environment
|
||||
|
||||
type EnvironmentVariable struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
func (ev EnvironmentVariable) assertValid() error {
|
||||
if len(ev.Name) == 0 {
|
||||
return fmt.Errorf(`environment variable name must not be empty`)
|
||||
}
|
||||
if !envPattern.MatchString(ev.Name) {
|
||||
return fmt.Errorf(`environment variable does not have valid identifier %q`, ev.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e Environment) assertValid() error {
|
||||
seen := map[string]bool{}
|
||||
for _, env := range e {
|
||||
if err := env.assertValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, ok := seen[env.Name]
|
||||
if ok {
|
||||
return fmt.Errorf(`duplicate environment variable of name %q`, env.Name)
|
||||
}
|
||||
seen[env.Name] = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e Environment) MarshalJSON() ([]byte, error) {
|
||||
if err := e.assertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(environment(e))
|
||||
}
|
||||
|
||||
func (e *Environment) UnmarshalJSON(data []byte) error {
|
||||
var je environment
|
||||
if err := json.Unmarshal(data, &je); err != nil {
|
||||
return err
|
||||
}
|
||||
ne := Environment(je)
|
||||
if err := ne.assertValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
*e = ne
|
||||
return nil
|
||||
}
|
||||
|
||||
// Retrieve the value of an environment variable by the given name from
|
||||
// Environment, if it exists.
|
||||
func (e Environment) Get(name string) (value string, ok bool) {
|
||||
for _, env := range e {
|
||||
if env.Name == name {
|
||||
return env.Value, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Set sets the value of an environment variable by the given name,
|
||||
// overwriting if one already exists.
|
||||
func (e *Environment) Set(name string, value string) {
|
||||
for i, env := range *e {
|
||||
if env.Name == name {
|
||||
(*e)[i] = EnvironmentVariable{
|
||||
Name: name,
|
||||
Value: value,
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
env := EnvironmentVariable{
|
||||
Name: name,
|
||||
Value: value,
|
||||
}
|
||||
*e = append(*e, env)
|
||||
}
|
||||
49
vendor/github.com/appc/spec/schema/types/errors.go
generated
vendored
Normal file
49
vendor/github.com/appc/spec/schema/types/errors.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// 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"
|
||||
|
||||
// An ACKindError is returned when the wrong ACKind is set in a manifest
|
||||
type ACKindError string
|
||||
|
||||
func (e ACKindError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
func InvalidACKindError(kind ACKind) ACKindError {
|
||||
return ACKindError(fmt.Sprintf("missing or bad ACKind (must be %#v)", kind))
|
||||
}
|
||||
|
||||
// An ACVersionError is returned when a bad ACVersion is set in a manifest
|
||||
type ACVersionError string
|
||||
|
||||
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
|
||||
|
||||
func (e ACNameError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
61
vendor/github.com/appc/spec/schema/types/event_handler.go
generated
vendored
Normal file
61
vendor/github.com/appc/spec/schema/types/event_handler.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
type EventHandler struct {
|
||||
Name string `json:"name"`
|
||||
Exec Exec `json:"exec"`
|
||||
}
|
||||
|
||||
type eventHandler EventHandler
|
||||
|
||||
func (e EventHandler) assertValid() error {
|
||||
s := e.Name
|
||||
switch s {
|
||||
case "pre-start", "post-stop":
|
||||
return nil
|
||||
case "":
|
||||
return errors.New(`eventHandler "name" cannot be empty`)
|
||||
default:
|
||||
return fmt.Errorf(`bad eventHandler "name": %q`, s)
|
||||
}
|
||||
}
|
||||
|
||||
func (e EventHandler) MarshalJSON() ([]byte, error) {
|
||||
if err := e.assertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(eventHandler(e))
|
||||
}
|
||||
|
||||
func (e *EventHandler) UnmarshalJSON(data []byte) error {
|
||||
var je eventHandler
|
||||
err := json.Unmarshal(data, &je)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ne := EventHandler(je)
|
||||
if err := ne.assertValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
*e = ne
|
||||
return nil
|
||||
}
|
||||
46
vendor/github.com/appc/spec/schema/types/exec.go
generated
vendored
Normal file
46
vendor/github.com/appc/spec/schema/types/exec.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
// 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"
|
||||
|
||||
type Exec []string
|
||||
|
||||
type exec Exec
|
||||
|
||||
func (e Exec) assertValid() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e Exec) MarshalJSON() ([]byte, error) {
|
||||
if err := e.assertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(exec(e))
|
||||
}
|
||||
|
||||
func (e *Exec) UnmarshalJSON(data []byte) error {
|
||||
var je exec
|
||||
err := json.Unmarshal(data, &je)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ne := Exec(je)
|
||||
if err := ne.assertValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
*e = ne
|
||||
return nil
|
||||
}
|
||||
118
vendor/github.com/appc/spec/schema/types/hash.go
generated
vendored
Normal file
118
vendor/github.com/appc/spec/schema/types/hash.go
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
// 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 (
|
||||
"crypto/sha512"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
maxHashSize = (sha512.Size / 2) + len("sha512-")
|
||||
)
|
||||
|
||||
// Hash encodes a hash specified in a string of the form:
|
||||
// "<type>-<value>"
|
||||
// for example
|
||||
// "sha512-06c733b1838136838e6d2d3e8fa5aea4c7905e92[...]"
|
||||
// Valid types are currently:
|
||||
// * sha512
|
||||
type Hash struct {
|
||||
typ string
|
||||
Val string
|
||||
}
|
||||
|
||||
func NewHash(s string) (*Hash, error) {
|
||||
elems := strings.Split(s, "-")
|
||||
if len(elems) != 2 {
|
||||
return nil, errors.New("badly formatted hash string")
|
||||
}
|
||||
nh := Hash{
|
||||
typ: elems[0],
|
||||
Val: elems[1],
|
||||
}
|
||||
if err := nh.assertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &nh, nil
|
||||
}
|
||||
|
||||
func (h Hash) String() string {
|
||||
return fmt.Sprintf("%s-%s", h.typ, h.Val)
|
||||
}
|
||||
|
||||
func (h *Hash) Set(s string) error {
|
||||
nh, err := NewHash(s)
|
||||
if err == nil {
|
||||
*h = *nh
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (h Hash) Empty() bool {
|
||||
return reflect.DeepEqual(h, Hash{})
|
||||
}
|
||||
|
||||
func (h Hash) assertValid() error {
|
||||
switch h.typ {
|
||||
case "sha512":
|
||||
case "":
|
||||
return fmt.Errorf("unexpected empty hash type")
|
||||
default:
|
||||
return fmt.Errorf("unrecognized hash type: %v", h.typ)
|
||||
}
|
||||
if h.Val == "" {
|
||||
return fmt.Errorf("unexpected empty hash value")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Hash) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
nh, err := NewHash(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*h = *nh
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h Hash) MarshalJSON() ([]byte, error) {
|
||||
if err := h.assertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(h.String())
|
||||
}
|
||||
|
||||
func NewHashSHA512(b []byte) *Hash {
|
||||
h := sha512.New()
|
||||
h.Write(b)
|
||||
nh, _ := NewHash(fmt.Sprintf("sha512-%x", h.Sum(nil)))
|
||||
return nh
|
||||
}
|
||||
|
||||
func ShortHash(hash string) string {
|
||||
if len(hash) > maxHashSize {
|
||||
return hash[:maxHashSize]
|
||||
}
|
||||
return hash
|
||||
}
|
||||
190
vendor/github.com/appc/spec/schema/types/isolator.go
generated
vendored
Normal file
190
vendor/github.com/appc/spec/schema/types/isolator.go
generated
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
var (
|
||||
isolatorMap map[ACIdentifier]IsolatorValueConstructor
|
||||
|
||||
// ErrIncompatibleIsolator is returned whenever an Isolators set contains
|
||||
// conflicting IsolatorValue instances
|
||||
ErrIncompatibleIsolator = errors.New("isolators set contains incompatible types")
|
||||
// ErrInvalidIsolator is returned upon validation failures due to improper
|
||||
// or partially constructed Isolator instances (eg. from incomplete direct construction)
|
||||
ErrInvalidIsolator = errors.New("invalid isolator")
|
||||
)
|
||||
|
||||
func init() {
|
||||
isolatorMap = make(map[ACIdentifier]IsolatorValueConstructor)
|
||||
}
|
||||
|
||||
type IsolatorValueConstructor func() IsolatorValue
|
||||
|
||||
func AddIsolatorValueConstructor(n ACIdentifier, i IsolatorValueConstructor) {
|
||||
isolatorMap[n] = i
|
||||
}
|
||||
|
||||
func AddIsolatorName(n ACIdentifier, ns map[ACIdentifier]struct{}) {
|
||||
ns[n] = struct{}{}
|
||||
}
|
||||
|
||||
// Isolators encapsulates a list of individual Isolators for the ImageManifest
|
||||
// and PodManifest schemas.
|
||||
type Isolators []Isolator
|
||||
|
||||
// assertValid checks that every single isolator is valid and that
|
||||
// the whole set is well built
|
||||
func (isolators Isolators) assertValid() error {
|
||||
typesMap := make(map[ACIdentifier]bool)
|
||||
for _, i := range isolators {
|
||||
v := i.Value()
|
||||
if v == nil {
|
||||
return ErrInvalidIsolator
|
||||
}
|
||||
if err := v.AssertValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := typesMap[i.Name]; ok {
|
||||
if !v.multipleAllowed() {
|
||||
return fmt.Errorf(`isolators set contains too many instances of type %s"`, i.Name)
|
||||
}
|
||||
}
|
||||
for _, c := range v.Conflicts() {
|
||||
if _, found := typesMap[c]; found {
|
||||
return ErrIncompatibleIsolator
|
||||
}
|
||||
}
|
||||
typesMap[i.Name] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetByName returns the last isolator in the list by the given name.
|
||||
func (is *Isolators) GetByName(name ACIdentifier) *Isolator {
|
||||
var i Isolator
|
||||
for j := len(*is) - 1; j >= 0; j-- {
|
||||
i = []Isolator(*is)[j]
|
||||
if i.Name == name {
|
||||
return &i
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReplaceIsolatorsByName overrides matching isolator types with a new
|
||||
// isolator, deleting them all and appending the new one instead
|
||||
func (is *Isolators) ReplaceIsolatorsByName(newIs Isolator, oldNames []ACIdentifier) {
|
||||
var i Isolator
|
||||
for j := len(*is) - 1; j >= 0; j-- {
|
||||
i = []Isolator(*is)[j]
|
||||
for _, name := range oldNames {
|
||||
if i.Name == name {
|
||||
*is = append((*is)[:j], (*is)[j+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
*is = append((*is)[:], newIs)
|
||||
return
|
||||
}
|
||||
|
||||
// Unrecognized returns a set of isolators that are not recognized.
|
||||
// An isolator is not recognized if it has not had an associated
|
||||
// constructor registered with AddIsolatorValueConstructor.
|
||||
func (is *Isolators) Unrecognized() Isolators {
|
||||
u := Isolators{}
|
||||
for _, i := range *is {
|
||||
if i.value == nil {
|
||||
u = append(u, i)
|
||||
}
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// IsolatorValue encapsulates the actual value of an Isolator which may be
|
||||
// serialized as any arbitrary JSON blob. Specific Isolator types should
|
||||
// implement this interface to facilitate unmarshalling and validation.
|
||||
type IsolatorValue interface {
|
||||
// UnmarshalJSON unserialize a JSON-encoded isolator
|
||||
UnmarshalJSON(b []byte) error
|
||||
// AssertValid returns a non-nil error value if an IsolatorValue is not valid
|
||||
// according to appc spec
|
||||
AssertValid() error
|
||||
// Conflicts returns a list of conflicting isolators types, which cannot co-exist
|
||||
// together with this IsolatorValue
|
||||
Conflicts() []ACIdentifier
|
||||
// multipleAllowed specifies whether multiple isolator instances are allowed
|
||||
// for this isolator type
|
||||
multipleAllowed() bool
|
||||
}
|
||||
|
||||
// Isolator is a model for unmarshalling isolator types from their JSON-encoded
|
||||
// representation.
|
||||
type Isolator struct {
|
||||
// Name is the name of the Isolator type as defined in the specification.
|
||||
Name ACIdentifier `json:"name"`
|
||||
// ValueRaw captures the raw JSON value of an Isolator that was
|
||||
// unmarshalled. This field is used for unmarshalling only. It MUST NOT
|
||||
// be referenced by external users of the Isolator struct. It is
|
||||
// exported only to satisfy Go's unfortunate requirement that fields
|
||||
// must be capitalized to be unmarshalled successfully.
|
||||
ValueRaw *json.RawMessage `json:"value"`
|
||||
// value captures the "true" value of the isolator.
|
||||
value IsolatorValue
|
||||
}
|
||||
|
||||
// isolator is a shadow type used for unmarshalling.
|
||||
type isolator Isolator
|
||||
|
||||
// Value returns the raw Value of this Isolator. Users should perform a type
|
||||
// switch/assertion on this value to extract the underlying isolator type.
|
||||
func (i *Isolator) Value() IsolatorValue {
|
||||
return i.value
|
||||
}
|
||||
|
||||
// UnmarshalJSON populates this Isolator from a JSON-encoded representation. To
|
||||
// unmarshal the Value of the Isolator, it will use the appropriate constructor
|
||||
// as registered by AddIsolatorValueConstructor.
|
||||
func (i *Isolator) UnmarshalJSON(b []byte) error {
|
||||
var ii isolator
|
||||
err := json.Unmarshal(b, &ii)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var dst IsolatorValue
|
||||
con, ok := isolatorMap[ii.Name]
|
||||
if ok {
|
||||
dst = con()
|
||||
err = dst.UnmarshalJSON(*ii.ValueRaw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dst.AssertValid()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
i.value = dst
|
||||
i.ValueRaw = ii.ValueRaw
|
||||
i.Name = ii.Name
|
||||
|
||||
return nil
|
||||
}
|
||||
533
vendor/github.com/appc/spec/schema/types/isolator_linux_specific.go
generated
vendored
Normal file
533
vendor/github.com/appc/spec/schema/types/isolator_linux_specific.go
generated
vendored
Normal file
@@ -0,0 +1,533 @@
|
||||
// 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"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const (
|
||||
LinuxCapabilitiesRetainSetName = "os/linux/capabilities-retain-set"
|
||||
LinuxCapabilitiesRevokeSetName = "os/linux/capabilities-remove-set"
|
||||
LinuxNoNewPrivilegesName = "os/linux/no-new-privileges"
|
||||
LinuxSeccompRemoveSetName = "os/linux/seccomp-remove-set"
|
||||
LinuxSeccompRetainSetName = "os/linux/seccomp-retain-set"
|
||||
LinuxOOMScoreAdjName = "os/linux/oom-score-adj"
|
||||
LinuxCPUSharesName = "os/linux/cpu-shares"
|
||||
LinuxSELinuxContextName = "os/linux/selinux-context"
|
||||
)
|
||||
|
||||
var LinuxIsolatorNames = make(map[ACIdentifier]struct{})
|
||||
|
||||
func init() {
|
||||
for name, con := range map[ACIdentifier]IsolatorValueConstructor{
|
||||
LinuxCapabilitiesRevokeSetName: func() IsolatorValue { return &LinuxCapabilitiesRevokeSet{} },
|
||||
LinuxCapabilitiesRetainSetName: func() IsolatorValue { return &LinuxCapabilitiesRetainSet{} },
|
||||
LinuxNoNewPrivilegesName: func() IsolatorValue { v := LinuxNoNewPrivileges(false); return &v },
|
||||
LinuxOOMScoreAdjName: func() IsolatorValue { v := LinuxOOMScoreAdj(0); return &v },
|
||||
LinuxCPUSharesName: func() IsolatorValue { v := LinuxCPUShares(1024); return &v },
|
||||
LinuxSeccompRemoveSetName: func() IsolatorValue { return &LinuxSeccompRemoveSet{} },
|
||||
LinuxSeccompRetainSetName: func() IsolatorValue { return &LinuxSeccompRetainSet{} },
|
||||
LinuxSELinuxContextName: func() IsolatorValue { return &LinuxSELinuxContext{} },
|
||||
} {
|
||||
AddIsolatorName(name, LinuxIsolatorNames)
|
||||
AddIsolatorValueConstructor(name, con)
|
||||
}
|
||||
}
|
||||
|
||||
type LinuxNoNewPrivileges bool
|
||||
|
||||
func (l LinuxNoNewPrivileges) AssertValid() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(lucab): both need to be clarified in spec,
|
||||
// see https://github.com/appc/spec/issues/625
|
||||
func (l LinuxNoNewPrivileges) multipleAllowed() bool {
|
||||
return true
|
||||
}
|
||||
func (l LinuxNoNewPrivileges) Conflicts() []ACIdentifier {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LinuxNoNewPrivileges) UnmarshalJSON(b []byte) error {
|
||||
var v bool
|
||||
err := json.Unmarshal(b, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*l = LinuxNoNewPrivileges(v)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type AsIsolator interface {
|
||||
AsIsolator() (*Isolator, error)
|
||||
}
|
||||
|
||||
type LinuxCapabilitiesSet interface {
|
||||
Set() []LinuxCapability
|
||||
AssertValid() error
|
||||
}
|
||||
|
||||
type LinuxCapability string
|
||||
|
||||
type linuxCapabilitiesSetValue struct {
|
||||
Set []LinuxCapability `json:"set"`
|
||||
}
|
||||
|
||||
type linuxCapabilitiesSetBase struct {
|
||||
val linuxCapabilitiesSetValue
|
||||
}
|
||||
|
||||
func (l linuxCapabilitiesSetBase) AssertValid() error {
|
||||
if len(l.val.Set) == 0 {
|
||||
return errors.New("set must be non-empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(lucab): both need to be clarified in spec,
|
||||
// see https://github.com/appc/spec/issues/625
|
||||
func (l linuxCapabilitiesSetBase) multipleAllowed() bool {
|
||||
return true
|
||||
}
|
||||
func (l linuxCapabilitiesSetBase) Conflicts() []ACIdentifier {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *linuxCapabilitiesSetBase) UnmarshalJSON(b []byte) error {
|
||||
var v linuxCapabilitiesSetValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.val = v
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (l linuxCapabilitiesSetBase) Set() []LinuxCapability {
|
||||
return l.val.Set
|
||||
}
|
||||
|
||||
type LinuxCapabilitiesRetainSet struct {
|
||||
linuxCapabilitiesSetBase
|
||||
}
|
||||
|
||||
func NewLinuxCapabilitiesRetainSet(caps ...string) (*LinuxCapabilitiesRetainSet, error) {
|
||||
l := LinuxCapabilitiesRetainSet{
|
||||
linuxCapabilitiesSetBase{
|
||||
linuxCapabilitiesSetValue{
|
||||
make([]LinuxCapability, len(caps)),
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, c := range caps {
|
||||
l.linuxCapabilitiesSetBase.val.Set[i] = LinuxCapability(c)
|
||||
}
|
||||
if err := l.AssertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &l, nil
|
||||
}
|
||||
|
||||
func (l LinuxCapabilitiesRetainSet) AsIsolator() (*Isolator, error) {
|
||||
b, err := json.Marshal(l.linuxCapabilitiesSetBase.val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rm := json.RawMessage(b)
|
||||
return &Isolator{
|
||||
Name: LinuxCapabilitiesRetainSetName,
|
||||
ValueRaw: &rm,
|
||||
value: &l,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type LinuxCapabilitiesRevokeSet struct {
|
||||
linuxCapabilitiesSetBase
|
||||
}
|
||||
|
||||
func NewLinuxCapabilitiesRevokeSet(caps ...string) (*LinuxCapabilitiesRevokeSet, error) {
|
||||
l := LinuxCapabilitiesRevokeSet{
|
||||
linuxCapabilitiesSetBase{
|
||||
linuxCapabilitiesSetValue{
|
||||
make([]LinuxCapability, len(caps)),
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, c := range caps {
|
||||
l.linuxCapabilitiesSetBase.val.Set[i] = LinuxCapability(c)
|
||||
}
|
||||
if err := l.AssertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &l, nil
|
||||
}
|
||||
|
||||
func (l LinuxCapabilitiesRevokeSet) AsIsolator() (*Isolator, error) {
|
||||
b, err := json.Marshal(l.linuxCapabilitiesSetBase.val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rm := json.RawMessage(b)
|
||||
return &Isolator{
|
||||
Name: LinuxCapabilitiesRevokeSetName,
|
||||
ValueRaw: &rm,
|
||||
value: &l,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type LinuxSeccompSet interface {
|
||||
Set() []LinuxSeccompEntry
|
||||
Errno() LinuxSeccompErrno
|
||||
AssertValid() error
|
||||
}
|
||||
|
||||
type LinuxSeccompEntry string
|
||||
type LinuxSeccompErrno string
|
||||
|
||||
type linuxSeccompValue struct {
|
||||
Set []LinuxSeccompEntry `json:"set"`
|
||||
Errno LinuxSeccompErrno `json:"errno"`
|
||||
}
|
||||
|
||||
type linuxSeccompBase struct {
|
||||
val linuxSeccompValue
|
||||
}
|
||||
|
||||
func (l linuxSeccompBase) multipleAllowed() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (l linuxSeccompBase) AssertValid() error {
|
||||
if len(l.val.Set) == 0 {
|
||||
return errors.New("set must be non-empty")
|
||||
}
|
||||
if l.val.Errno == "" {
|
||||
return nil
|
||||
}
|
||||
for i, c := range l.val.Errno {
|
||||
if i == 0 && c != 'E' {
|
||||
s := fmt.Sprintf("errno must start with an 'E' character, got %s", l.val.Errno)
|
||||
return errors.New(s)
|
||||
} else if !(unicode.IsUpper(c) || unicode.IsNumber(c)) {
|
||||
s := fmt.Sprintf("errno can only contain upper case or numeric characters, got %s", l.val.Errno)
|
||||
return errors.New(s)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *linuxSeccompBase) UnmarshalJSON(b []byte) error {
|
||||
var v linuxSeccompValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.val = v
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l linuxSeccompBase) Set() []LinuxSeccompEntry {
|
||||
return l.val.Set
|
||||
}
|
||||
|
||||
func (l linuxSeccompBase) Errno() LinuxSeccompErrno {
|
||||
return l.val.Errno
|
||||
}
|
||||
|
||||
type LinuxSeccompRetainSet struct {
|
||||
linuxSeccompBase
|
||||
}
|
||||
|
||||
func (l LinuxSeccompRetainSet) Conflicts() []ACIdentifier {
|
||||
return []ACIdentifier{LinuxSeccompRemoveSetName}
|
||||
}
|
||||
|
||||
func NewLinuxSeccompRetainSet(errno string, syscall ...string) (*LinuxSeccompRetainSet, error) {
|
||||
l := LinuxSeccompRetainSet{
|
||||
linuxSeccompBase{
|
||||
linuxSeccompValue{
|
||||
make([]LinuxSeccompEntry, len(syscall)),
|
||||
LinuxSeccompErrno(errno),
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, c := range syscall {
|
||||
l.linuxSeccompBase.val.Set[i] = LinuxSeccompEntry(c)
|
||||
}
|
||||
if err := l.AssertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &l, nil
|
||||
}
|
||||
|
||||
func (l LinuxSeccompRetainSet) AsIsolator() (*Isolator, error) {
|
||||
b, err := json.Marshal(l.linuxSeccompBase.val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rm := json.RawMessage(b)
|
||||
return &Isolator{
|
||||
Name: LinuxSeccompRetainSetName,
|
||||
ValueRaw: &rm,
|
||||
value: &l,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type LinuxSeccompRemoveSet struct {
|
||||
linuxSeccompBase
|
||||
}
|
||||
|
||||
func (l LinuxSeccompRemoveSet) Conflicts() []ACIdentifier {
|
||||
return []ACIdentifier{LinuxSeccompRetainSetName}
|
||||
}
|
||||
|
||||
func NewLinuxSeccompRemoveSet(errno string, syscall ...string) (*LinuxSeccompRemoveSet, error) {
|
||||
l := LinuxSeccompRemoveSet{
|
||||
linuxSeccompBase{
|
||||
linuxSeccompValue{
|
||||
make([]LinuxSeccompEntry, len(syscall)),
|
||||
LinuxSeccompErrno(errno),
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, c := range syscall {
|
||||
l.linuxSeccompBase.val.Set[i] = LinuxSeccompEntry(c)
|
||||
}
|
||||
if err := l.AssertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &l, nil
|
||||
}
|
||||
|
||||
func (l LinuxSeccompRemoveSet) AsIsolator() (*Isolator, error) {
|
||||
b, err := json.Marshal(l.linuxSeccompBase.val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rm := json.RawMessage(b)
|
||||
return &Isolator{
|
||||
Name: LinuxSeccompRemoveSetName,
|
||||
ValueRaw: &rm,
|
||||
value: &l,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// LinuxCPUShares assigns the CPU time share weight to the processes executed.
|
||||
// See https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight,
|
||||
// https://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt
|
||||
type LinuxCPUShares int
|
||||
|
||||
func NewLinuxCPUShares(val int) (*LinuxCPUShares, error) {
|
||||
l := LinuxCPUShares(val)
|
||||
if err := l.AssertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &l, nil
|
||||
}
|
||||
|
||||
func (l LinuxCPUShares) AssertValid() error {
|
||||
if l < 2 || l > 262144 {
|
||||
return fmt.Errorf("%s must be between 2 and 262144, got %d", LinuxCPUSharesName, l)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l LinuxCPUShares) multipleAllowed() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (l LinuxCPUShares) Conflicts() []ACIdentifier {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LinuxCPUShares) UnmarshalJSON(b []byte) error {
|
||||
var v int
|
||||
err := json.Unmarshal(b, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*l = LinuxCPUShares(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l LinuxCPUShares) AsIsolator() Isolator {
|
||||
b, err := json.Marshal(l)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
rm := json.RawMessage(b)
|
||||
return Isolator{
|
||||
Name: LinuxCPUSharesName,
|
||||
ValueRaw: &rm,
|
||||
value: &l,
|
||||
}
|
||||
}
|
||||
|
||||
// LinuxOOMScoreAdj is equivalent to /proc/[pid]/oom_score_adj
|
||||
type LinuxOOMScoreAdj int // -1000 to 1000
|
||||
|
||||
func NewLinuxOOMScoreAdj(val int) (*LinuxOOMScoreAdj, error) {
|
||||
l := LinuxOOMScoreAdj(val)
|
||||
if err := l.AssertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &l, nil
|
||||
}
|
||||
|
||||
func (l LinuxOOMScoreAdj) AssertValid() error {
|
||||
if l < -1000 || l > 1000 {
|
||||
return fmt.Errorf("%s must be between -1000 and 1000, got %d", LinuxOOMScoreAdjName, l)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l LinuxOOMScoreAdj) multipleAllowed() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (l LinuxOOMScoreAdj) Conflicts() []ACIdentifier {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LinuxOOMScoreAdj) UnmarshalJSON(b []byte) error {
|
||||
var v int
|
||||
err := json.Unmarshal(b, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*l = LinuxOOMScoreAdj(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l LinuxOOMScoreAdj) AsIsolator() Isolator {
|
||||
b, err := json.Marshal(l)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
rm := json.RawMessage(b)
|
||||
return Isolator{
|
||||
Name: LinuxOOMScoreAdjName,
|
||||
ValueRaw: &rm,
|
||||
value: &l,
|
||||
}
|
||||
}
|
||||
|
||||
type LinuxSELinuxUser string
|
||||
type LinuxSELinuxRole string
|
||||
type LinuxSELinuxType string
|
||||
type LinuxSELinuxLevel string
|
||||
|
||||
type linuxSELinuxValue struct {
|
||||
User LinuxSELinuxUser `json:"user"`
|
||||
Role LinuxSELinuxRole `json:"role"`
|
||||
Type LinuxSELinuxType `json:"type"`
|
||||
Level LinuxSELinuxLevel `json:"level"`
|
||||
}
|
||||
|
||||
type LinuxSELinuxContext struct {
|
||||
val linuxSELinuxValue
|
||||
}
|
||||
|
||||
func (l LinuxSELinuxContext) AssertValid() error {
|
||||
if l.val.User == "" || strings.Contains(string(l.val.User), ":") {
|
||||
return fmt.Errorf("invalid user value %q", l.val.User)
|
||||
}
|
||||
if l.val.Role == "" || strings.Contains(string(l.val.Role), ":") {
|
||||
return fmt.Errorf("invalid role value %q", l.val.Role)
|
||||
}
|
||||
if l.val.Type == "" || strings.Contains(string(l.val.Type), ":") {
|
||||
return fmt.Errorf("invalid type value %q", l.val.Type)
|
||||
}
|
||||
if l.val.Level == "" {
|
||||
return fmt.Errorf("invalid level value %q", l.val.Level)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LinuxSELinuxContext) UnmarshalJSON(b []byte) error {
|
||||
var v linuxSELinuxValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.val = v
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l LinuxSELinuxContext) User() LinuxSELinuxUser {
|
||||
return l.val.User
|
||||
}
|
||||
|
||||
func (l LinuxSELinuxContext) Role() LinuxSELinuxRole {
|
||||
return l.val.Role
|
||||
}
|
||||
|
||||
func (l LinuxSELinuxContext) Type() LinuxSELinuxType {
|
||||
return l.val.Type
|
||||
}
|
||||
|
||||
func (l LinuxSELinuxContext) Level() LinuxSELinuxLevel {
|
||||
return l.val.Level
|
||||
}
|
||||
|
||||
func (l LinuxSELinuxContext) multipleAllowed() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (l LinuxSELinuxContext) Conflicts() []ACIdentifier {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewLinuxSELinuxContext(selinuxUser, selinuxRole, selinuxType, selinuxLevel string) (*LinuxSELinuxContext, error) {
|
||||
l := LinuxSELinuxContext{
|
||||
linuxSELinuxValue{
|
||||
LinuxSELinuxUser(selinuxUser),
|
||||
LinuxSELinuxRole(selinuxRole),
|
||||
LinuxSELinuxType(selinuxType),
|
||||
LinuxSELinuxLevel(selinuxLevel),
|
||||
},
|
||||
}
|
||||
if err := l.AssertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &l, nil
|
||||
}
|
||||
|
||||
func (l LinuxSELinuxContext) AsIsolator() (*Isolator, error) {
|
||||
b, err := json.Marshal(l.val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rm := json.RawMessage(b)
|
||||
return &Isolator{
|
||||
Name: LinuxSELinuxContextName,
|
||||
ValueRaw: &rm,
|
||||
value: &l,
|
||||
}, nil
|
||||
}
|
||||
245
vendor/github.com/appc/spec/schema/types/isolator_resources.go
generated
vendored
Normal file
245
vendor/github.com/appc/spec/schema/types/isolator_resources.go
generated
vendored
Normal file
@@ -0,0 +1,245 @@
|
||||
// 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"
|
||||
|
||||
"github.com/appc/spec/schema/types/resource"
|
||||
)
|
||||
|
||||
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 (
|
||||
ResourceBlockBandwidthName = "resource/block-bandwidth"
|
||||
ResourceBlockIOPSName = "resource/block-iops"
|
||||
ResourceCPUName = "resource/cpu"
|
||||
ResourceMemoryName = "resource/memory"
|
||||
ResourceNetworkBandwidthName = "resource/network-bandwidth"
|
||||
)
|
||||
|
||||
func init() {
|
||||
for name, con := range map[ACIdentifier]IsolatorValueConstructor{
|
||||
ResourceBlockBandwidthName: func() IsolatorValue { return &ResourceBlockBandwidth{} },
|
||||
ResourceBlockIOPSName: func() IsolatorValue { return &ResourceBlockIOPS{} },
|
||||
ResourceCPUName: func() IsolatorValue { return &ResourceCPU{} },
|
||||
ResourceMemoryName: func() IsolatorValue { return &ResourceMemory{} },
|
||||
ResourceNetworkBandwidthName: func() IsolatorValue { return &ResourceNetworkBandwidth{} },
|
||||
} {
|
||||
AddIsolatorName(name, ResourceIsolatorNames)
|
||||
AddIsolatorValueConstructor(name, con)
|
||||
}
|
||||
}
|
||||
|
||||
type Resource interface {
|
||||
Limit() *resource.Quantity
|
||||
Request() *resource.Quantity
|
||||
Default() bool
|
||||
}
|
||||
|
||||
type ResourceBase struct {
|
||||
val resourceValue
|
||||
}
|
||||
|
||||
type resourceValue struct {
|
||||
Default bool `json:"default"`
|
||||
Request *resource.Quantity `json:"request"`
|
||||
Limit *resource.Quantity `json:"limit"`
|
||||
}
|
||||
|
||||
func (r ResourceBase) Limit() *resource.Quantity {
|
||||
return r.val.Limit
|
||||
}
|
||||
func (r ResourceBase) Request() *resource.Quantity {
|
||||
return r.val.Request
|
||||
}
|
||||
func (r ResourceBase) Default() bool {
|
||||
return r.val.Default
|
||||
}
|
||||
|
||||
func (r *ResourceBase) UnmarshalJSON(b []byte) error {
|
||||
return json.Unmarshal(b, &r.val)
|
||||
}
|
||||
|
||||
func (r ResourceBase) AssertValid() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(lucab): both need to be clarified in spec,
|
||||
// see https://github.com/appc/spec/issues/625
|
||||
func (l ResourceBase) multipleAllowed() bool {
|
||||
return true
|
||||
}
|
||||
func (l ResourceBase) Conflicts() []ACIdentifier {
|
||||
return nil
|
||||
}
|
||||
|
||||
type ResourceBlockBandwidth struct {
|
||||
ResourceBase
|
||||
}
|
||||
|
||||
func (r ResourceBlockBandwidth) AssertValid() error {
|
||||
if r.Default() != true {
|
||||
return ErrDefaultRequired
|
||||
}
|
||||
if r.Request() != nil {
|
||||
return ErrRequestNonEmpty
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ResourceBlockIOPS struct {
|
||||
ResourceBase
|
||||
}
|
||||
|
||||
func (r ResourceBlockIOPS) AssertValid() error {
|
||||
if r.Default() != true {
|
||||
return ErrDefaultRequired
|
||||
}
|
||||
if r.Request() != nil {
|
||||
return ErrRequestNonEmpty
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ResourceCPU struct {
|
||||
ResourceBase
|
||||
}
|
||||
|
||||
func (r ResourceCPU) String() string {
|
||||
return fmt.Sprintf("ResourceCPU(request=%s, limit=%s)", r.Request(), r.Limit())
|
||||
}
|
||||
|
||||
func (r ResourceCPU) AssertValid() error {
|
||||
if r.Default() != false {
|
||||
return ErrDefaultTrue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r ResourceCPU) AsIsolator() Isolator {
|
||||
isol := isolatorMap[ResourceCPUName]()
|
||||
|
||||
b, err := json.Marshal(r.val)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
valRaw := json.RawMessage(b)
|
||||
return Isolator{
|
||||
Name: ResourceCPUName,
|
||||
ValueRaw: &valRaw,
|
||||
value: isol,
|
||||
}
|
||||
}
|
||||
|
||||
func NewResourceCPUIsolator(request, limit string) (*ResourceCPU, error) {
|
||||
req, err := resource.ParseQuantity(request)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing request: %v", err)
|
||||
}
|
||||
lim, err := resource.ParseQuantity(limit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing limit: %v", err)
|
||||
}
|
||||
res := &ResourceCPU{
|
||||
ResourceBase{
|
||||
resourceValue{
|
||||
Request: &req,
|
||||
Limit: &lim,
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := res.AssertValid(); err != nil {
|
||||
// should never happen
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
type ResourceMemory struct {
|
||||
ResourceBase
|
||||
}
|
||||
|
||||
func (r ResourceMemory) String() string {
|
||||
return fmt.Sprintf("ResourceMemory(request=%s, limit=%s)", r.Request(), r.Limit())
|
||||
}
|
||||
|
||||
func (r ResourceMemory) AssertValid() error {
|
||||
if r.Default() != false {
|
||||
return ErrDefaultTrue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r ResourceMemory) AsIsolator() Isolator {
|
||||
isol := isolatorMap[ResourceMemoryName]()
|
||||
|
||||
b, err := json.Marshal(r.val)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
valRaw := json.RawMessage(b)
|
||||
return Isolator{
|
||||
Name: ResourceMemoryName,
|
||||
ValueRaw: &valRaw,
|
||||
value: isol,
|
||||
}
|
||||
}
|
||||
|
||||
func NewResourceMemoryIsolator(request, limit string) (*ResourceMemory, error) {
|
||||
req, err := resource.ParseQuantity(request)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing request: %v", err)
|
||||
}
|
||||
lim, err := resource.ParseQuantity(limit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing limit: %v", err)
|
||||
}
|
||||
res := &ResourceMemory{
|
||||
ResourceBase{
|
||||
resourceValue{
|
||||
Request: &req,
|
||||
Limit: &lim,
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := res.AssertValid(); err != nil {
|
||||
// should never happen
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
type ResourceNetworkBandwidth struct {
|
||||
ResourceBase
|
||||
}
|
||||
|
||||
func (r ResourceNetworkBandwidth) AssertValid() error {
|
||||
if r.Default() != true {
|
||||
return ErrDefaultRequired
|
||||
}
|
||||
if r.Request() != nil {
|
||||
return ErrRequestNonEmpty
|
||||
}
|
||||
return nil
|
||||
}
|
||||
83
vendor/github.com/appc/spec/schema/types/isolator_unix.go
generated
vendored
Normal file
83
vendor/github.com/appc/spec/schema/types/isolator_unix.go
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright 2016 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"
|
||||
)
|
||||
|
||||
var (
|
||||
UnixIsolatorNames = make(map[ACIdentifier]struct{})
|
||||
)
|
||||
|
||||
const (
|
||||
//TODO(lucab): add "ulimit" isolators
|
||||
UnixSysctlName = "os/unix/sysctl"
|
||||
)
|
||||
|
||||
func init() {
|
||||
for name, con := range map[ACIdentifier]IsolatorValueConstructor{
|
||||
UnixSysctlName: func() IsolatorValue { return &UnixSysctl{} },
|
||||
} {
|
||||
AddIsolatorName(name, UnixIsolatorNames)
|
||||
AddIsolatorValueConstructor(name, con)
|
||||
}
|
||||
}
|
||||
|
||||
type UnixSysctl map[string]string
|
||||
|
||||
func (s *UnixSysctl) UnmarshalJSON(b []byte) error {
|
||||
var v map[string]string
|
||||
err := json.Unmarshal(b, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*s = UnixSysctl(v)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s UnixSysctl) AssertValid() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s UnixSysctl) multipleAllowed() bool {
|
||||
return false
|
||||
}
|
||||
func (s UnixSysctl) Conflicts() []ACIdentifier {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s UnixSysctl) AsIsolator() Isolator {
|
||||
isol := isolatorMap[UnixSysctlName]()
|
||||
|
||||
b, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
valRaw := json.RawMessage(b)
|
||||
return Isolator{
|
||||
Name: UnixSysctlName,
|
||||
ValueRaw: &valRaw,
|
||||
value: isol,
|
||||
}
|
||||
}
|
||||
|
||||
func NewUnixSysctlIsolator(cfg map[string]string) (*UnixSysctl, error) {
|
||||
s := UnixSysctl(cfg)
|
||||
if err := s.AssertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s, nil
|
||||
}
|
||||
211
vendor/github.com/appc/spec/schema/types/labels.go
generated
vendored
Normal file
211
vendor/github.com/appc/spec/schema/types/labels.go
generated
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
// 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"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
var ValidOSArch = map[string][]string{
|
||||
"linux": {"amd64", "i386", "aarch64", "aarch64_be", "armv6l", "armv7l", "armv7b", "ppc64", "ppc64le", "s390x"},
|
||||
"freebsd": {"amd64", "i386", "arm"},
|
||||
"darwin": {"x86_64", "i386"},
|
||||
}
|
||||
|
||||
type Labels []Label
|
||||
|
||||
type labelsSlice Labels
|
||||
|
||||
func (l labelsSlice) Len() int { return len(l) }
|
||||
func (l labelsSlice) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
|
||||
func (l labelsSlice) Less(i, j int) bool { return l[i].Name < l[j].Name }
|
||||
|
||||
type Label struct {
|
||||
Name ACIdentifier `json:"name"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// {appc,go}ArchTuple are internal helper types used to translate arch tuple between go and appc
|
||||
type appcArchTuple struct {
|
||||
appcOs string
|
||||
appcArch string
|
||||
}
|
||||
type goArchTuple struct {
|
||||
goOs string
|
||||
goArch string
|
||||
goArchFlavor string
|
||||
}
|
||||
|
||||
// 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 = append(validOses, validOs)
|
||||
}
|
||||
sort.Strings(validOses)
|
||||
return fmt.Errorf(`bad os %#v (must be one of: %v)`, os, validOses)
|
||||
} else {
|
||||
// Whitelisted OS. We check arch here, as arch makes sense only
|
||||
// when os is defined.
|
||||
if arch, ok := labels["arch"]; ok {
|
||||
found := false
|
||||
for _, validArch := range validArchs {
|
||||
if arch == validArch {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf(`bad arch %#v for %v (must be one of: %v)`, arch, os, validArchs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
return json.Marshal(labelsSlice(l))
|
||||
}
|
||||
|
||||
func (l *Labels) UnmarshalJSON(data []byte) error {
|
||||
var jl labelsSlice
|
||||
if err := json.Unmarshal(data, &jl); err != nil {
|
||||
return err
|
||||
}
|
||||
nl := Labels(jl)
|
||||
if err := nl.assertValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
*l = nl
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get retrieves the value of the label by the given name from Labels, if it exists
|
||||
func (l Labels) Get(name string) (val string, ok bool) {
|
||||
for _, lbl := range l {
|
||||
if lbl.Name.String() == name {
|
||||
return lbl.Value, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// 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[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})
|
||||
}
|
||||
if err := labels.assertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sort.Sort(labelsSlice(labels))
|
||||
return labels, nil
|
||||
}
|
||||
|
||||
// ToAppcOSArch translates a Golang arch tuple (OS, architecture, flavor) into
|
||||
// an appc arch tuple (OS, architecture)
|
||||
func ToAppcOSArch(goOs string, goArch string, goArchFlavor string) (appcOs string, appcArch string, e error) {
|
||||
tabularAppcToGo := map[goArchTuple]appcArchTuple{
|
||||
{"linux", "amd64", ""}: {"linux", "amd64"},
|
||||
{"linux", "386", ""}: {"linux", "i386"},
|
||||
{"linux", "arm64", ""}: {"linux", "aarch64"},
|
||||
{"linux", "arm", ""}: {"linux", "armv6l"},
|
||||
{"linux", "arm", "6"}: {"linux", "armv6l"},
|
||||
{"linux", "arm", "7"}: {"linux", "armv7l"},
|
||||
{"linux", "ppc64", ""}: {"linux", "ppc64"},
|
||||
{"linux", "ppc64le", ""}: {"linux", "ppc64le"},
|
||||
{"linux", "s390x", ""}: {"linux", "s390x"},
|
||||
|
||||
{"freebsd", "amd64", ""}: {"freebsd", "amd64"},
|
||||
{"freebsd", "386", ""}: {"freebsd", "i386"},
|
||||
{"freebsd", "arm", ""}: {"freebsd", "arm"},
|
||||
{"freebsd", "arm", "5"}: {"freebsd", "arm"},
|
||||
{"freebsd", "arm", "6"}: {"freebsd", "arm"},
|
||||
{"freebsd", "arm", "7"}: {"freebsd", "arm"},
|
||||
|
||||
{"darwin", "amd64", ""}: {"darwin", "x86_64"},
|
||||
{"darwin", "386", ""}: {"darwin", "i386"},
|
||||
}
|
||||
archTuple, ok := tabularAppcToGo[goArchTuple{goOs, goArch, goArchFlavor}]
|
||||
if !ok {
|
||||
return "", "", fmt.Errorf("unknown arch tuple: %q - %q - %q", goOs, goArch, goArchFlavor)
|
||||
}
|
||||
return archTuple.appcOs, archTuple.appcArch, nil
|
||||
}
|
||||
|
||||
// ToGoOSArch translates an appc arch tuple (OS, architecture) into
|
||||
// a Golang arch tuple (OS, architecture, flavor)
|
||||
func ToGoOSArch(appcOs string, appcArch string) (goOs string, goArch string, goArchFlavor string, e error) {
|
||||
tabularGoToAppc := map[appcArchTuple]goArchTuple{
|
||||
// {"linux", "aarch64_be"}: nil,
|
||||
// {"linux", "armv7b"}: nil,
|
||||
{"linux", "aarch64"}: {"linux", "arm64", ""},
|
||||
{"linux", "amd64"}: {"linux", "amd64", ""},
|
||||
{"linux", "armv6l"}: {"linux", "arm", "6"},
|
||||
{"linux", "armv7l"}: {"linux", "arm", "7"},
|
||||
{"linux", "i386"}: {"linux", "386", ""},
|
||||
{"linux", "ppc64"}: {"linux", "ppc64", ""},
|
||||
{"linux", "ppc64le"}: {"linux", "ppc64le", ""},
|
||||
{"linux", "s390x"}: {"linux", "s390x", ""},
|
||||
|
||||
{"freebsd", "amd64"}: {"freebsd", "amd64", ""},
|
||||
{"freebsd", "arm"}: {"freebsd", "arm", "6"},
|
||||
{"freebsd", "386"}: {"freebsd", "i386", ""},
|
||||
|
||||
{"darwin", "amd64"}: {"darwin", "x86_64", ""},
|
||||
{"darwin", "386"}: {"darwin", "i386", ""},
|
||||
}
|
||||
|
||||
archTuple, ok := tabularGoToAppc[appcArchTuple{appcOs, appcArch}]
|
||||
if !ok {
|
||||
return "", "", "", fmt.Errorf("unknown arch tuple: %q - %q", appcOs, appcArch)
|
||||
}
|
||||
return archTuple.goOs, archTuple.goArch, archTuple.goArchFlavor, nil
|
||||
}
|
||||
92
vendor/github.com/appc/spec/schema/types/mountpoint.go
generated
vendored
Normal file
92
vendor/github.com/appc/spec/schema/types/mountpoint.go
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
// 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/appc/spec/schema/common"
|
||||
)
|
||||
|
||||
// MountPoint is the application-side manifestation of a Volume.
|
||||
type MountPoint struct {
|
||||
Name ACName `json:"name"`
|
||||
Path string `json:"path"`
|
||||
ReadOnly bool `json:"readOnly,omitempty"`
|
||||
}
|
||||
|
||||
func (mount MountPoint) assertValid() error {
|
||||
if mount.Name.Empty() {
|
||||
return errors.New("name must be set")
|
||||
}
|
||||
if len(mount.Path) == 0 {
|
||||
return errors.New("path must be set")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MountPointFromString takes a command line mountpoint parameter and returns a mountpoint
|
||||
//
|
||||
// It is useful for actool patch-manifest --mounts
|
||||
//
|
||||
// Example mountpoint parameters:
|
||||
// database,path=/tmp,readOnly=true
|
||||
func MountPointFromString(mp string) (*MountPoint, error) {
|
||||
var mount MountPoint
|
||||
|
||||
mp = "name=" + mp
|
||||
mpQuery, err := common.MakeQueryString(mp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v, err := url.ParseQuery(mpQuery)
|
||||
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
|
||||
}
|
||||
mount.Name = *acn
|
||||
case "path":
|
||||
mount.Path = val[0]
|
||||
case "readOnly":
|
||||
ro, err := strconv.ParseBool(val[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mount.ReadOnly = ro
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown mountpoint parameter %q", key)
|
||||
}
|
||||
}
|
||||
err = mount.assertValid()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &mount, nil
|
||||
}
|
||||
147
vendor/github.com/appc/spec/schema/types/port.go
generated
vendored
Normal file
147
vendor/github.com/appc/spec/schema/types/port.go
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
// 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"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/appc/spec/schema/common"
|
||||
)
|
||||
|
||||
// Port represents a port as offered by an application *inside*
|
||||
// the pod.
|
||||
type Port struct {
|
||||
Name ACName `json:"name"`
|
||||
Protocol string `json:"protocol"`
|
||||
Port uint `json:"port"`
|
||||
Count uint `json:"count"`
|
||||
SocketActivated bool `json:"socketActivated"`
|
||||
}
|
||||
|
||||
// ExposedPort represents a port listening on the host side.
|
||||
// The PodPort is optional -- if missing, then try and find the pod-side
|
||||
// information by matching names
|
||||
type ExposedPort struct {
|
||||
Name ACName `json:"name"`
|
||||
HostPort uint `json:"hostPort"`
|
||||
HostIP net.IP `json:"hostIP,omitempty"` // optional
|
||||
PodPort *Port `json:"podPort,omitempty"` // optional. If missing, try and find a corresponding App's port
|
||||
}
|
||||
|
||||
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
|
||||
ptQuery, err := common.MakeQueryString(pt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v, err := url.ParseQuery(ptQuery)
|
||||
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
|
||||
}
|
||||
4
vendor/github.com/appc/spec/schema/types/resource/README.md
generated
vendored
Normal file
4
vendor/github.com/appc/spec/schema/types/resource/README.md
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
This package was copied in from the Kubernetes repo to avoid a cyclic
|
||||
dependency. These files were taken from master from
|
||||
github.com/kubernetes/kubernetes at commit hash
|
||||
b0deb2eb8f4037421077f77cb163dbb4c0a2a9f5.
|
||||
298
vendor/github.com/appc/spec/schema/types/resource/amount.go
generated
vendored
Normal file
298
vendor/github.com/appc/spec/schema/types/resource/amount.go
generated
vendored
Normal file
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 resource
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
inf "gopkg.in/inf.v0"
|
||||
)
|
||||
|
||||
// Scale is used for getting and setting the base-10 scaled value.
|
||||
// Base-2 scales are omitted for mathematical simplicity.
|
||||
// See Quantity.ScaledValue for more details.
|
||||
type Scale int32
|
||||
|
||||
// infScale adapts a Scale value to an inf.Scale value.
|
||||
func (s Scale) infScale() inf.Scale {
|
||||
return inf.Scale(-s) // inf.Scale is upside-down
|
||||
}
|
||||
|
||||
const (
|
||||
Nano Scale = -9
|
||||
Micro Scale = -6
|
||||
Milli Scale = -3
|
||||
Kilo Scale = 3
|
||||
Mega Scale = 6
|
||||
Giga Scale = 9
|
||||
Tera Scale = 12
|
||||
Peta Scale = 15
|
||||
Exa Scale = 18
|
||||
)
|
||||
|
||||
var (
|
||||
Zero = int64Amount{}
|
||||
|
||||
// Used by quantity strings - treat as read only
|
||||
zeroBytes = []byte("0")
|
||||
)
|
||||
|
||||
// int64Amount represents a fixed precision numerator and arbitrary scale exponent. It is faster
|
||||
// than operations on inf.Dec for values that can be represented as int64.
|
||||
type int64Amount struct {
|
||||
value int64
|
||||
scale Scale
|
||||
}
|
||||
|
||||
// Sign returns 0 if the value is zero, -1 if it is less than 0, or 1 if it is greater than 0.
|
||||
func (a int64Amount) Sign() int {
|
||||
switch {
|
||||
case a.value == 0:
|
||||
return 0
|
||||
case a.value > 0:
|
||||
return 1
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// AsInt64 returns the current amount as an int64 at scale 0, or false if the value cannot be
|
||||
// represented in an int64 OR would result in a loss of precision. This method is intended as
|
||||
// an optimization to avoid calling AsDec.
|
||||
func (a int64Amount) AsInt64() (int64, bool) {
|
||||
if a.scale == 0 {
|
||||
return a.value, true
|
||||
}
|
||||
if a.scale < 0 {
|
||||
// TODO: attempt to reduce factors, although it is assumed that factors are reduced prior
|
||||
// to the int64Amount being created.
|
||||
return 0, false
|
||||
}
|
||||
return positiveScaleInt64(a.value, a.scale)
|
||||
}
|
||||
|
||||
// AsScaledInt64 returns an int64 representing the value of this amount at the specified scale,
|
||||
// rounding up, or false if that would result in overflow. (1e20).AsScaledInt64(1) would result
|
||||
// in overflow because 1e19 is not representable as an int64. Note that setting a scale larger
|
||||
// than the current value may result in loss of precision - i.e. (1e-6).AsScaledInt64(0) would
|
||||
// return 1, because 0.000001 is rounded up to 1.
|
||||
func (a int64Amount) AsScaledInt64(scale Scale) (result int64, ok bool) {
|
||||
if a.scale < scale {
|
||||
result, _ = negativeScaleInt64(a.value, scale-a.scale)
|
||||
return result, true
|
||||
}
|
||||
return positiveScaleInt64(a.value, a.scale-scale)
|
||||
}
|
||||
|
||||
// AsDec returns an inf.Dec representation of this value.
|
||||
func (a int64Amount) AsDec() *inf.Dec {
|
||||
var base inf.Dec
|
||||
base.SetUnscaled(a.value)
|
||||
base.SetScale(inf.Scale(-a.scale))
|
||||
return &base
|
||||
}
|
||||
|
||||
// Cmp returns 0 if a and b are equal, 1 if a is greater than b, or -1 if a is less than b.
|
||||
func (a int64Amount) Cmp(b int64Amount) int {
|
||||
switch {
|
||||
case a.scale == b.scale:
|
||||
// compare only the unscaled portion
|
||||
case a.scale > b.scale:
|
||||
result, remainder, exact := divideByScaleInt64(b.value, a.scale-b.scale)
|
||||
if !exact {
|
||||
return a.AsDec().Cmp(b.AsDec())
|
||||
}
|
||||
if result == a.value {
|
||||
switch {
|
||||
case remainder == 0:
|
||||
return 0
|
||||
case remainder > 0:
|
||||
return -1
|
||||
default:
|
||||
return 1
|
||||
}
|
||||
}
|
||||
b.value = result
|
||||
default:
|
||||
result, remainder, exact := divideByScaleInt64(a.value, b.scale-a.scale)
|
||||
if !exact {
|
||||
return a.AsDec().Cmp(b.AsDec())
|
||||
}
|
||||
if result == b.value {
|
||||
switch {
|
||||
case remainder == 0:
|
||||
return 0
|
||||
case remainder > 0:
|
||||
return 1
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
a.value = result
|
||||
}
|
||||
|
||||
switch {
|
||||
case a.value == b.value:
|
||||
return 0
|
||||
case a.value < b.value:
|
||||
return -1
|
||||
default:
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds two int64Amounts together, matching scales. It will return false and not mutate
|
||||
// a if overflow or underflow would result.
|
||||
func (a *int64Amount) Add(b int64Amount) bool {
|
||||
switch {
|
||||
case b.value == 0:
|
||||
return true
|
||||
case a.value == 0:
|
||||
a.value = b.value
|
||||
a.scale = b.scale
|
||||
return true
|
||||
case a.scale == b.scale:
|
||||
c, ok := int64Add(a.value, b.value)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
a.value = c
|
||||
case a.scale > b.scale:
|
||||
c, ok := positiveScaleInt64(a.value, a.scale-b.scale)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
c, ok = int64Add(c, b.value)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
a.scale = b.scale
|
||||
a.value = c
|
||||
default:
|
||||
c, ok := positiveScaleInt64(b.value, b.scale-a.scale)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
c, ok = int64Add(a.value, c)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
a.value = c
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Sub removes the value of b from the current amount, or returns false if underflow would result.
|
||||
func (a *int64Amount) Sub(b int64Amount) bool {
|
||||
return a.Add(int64Amount{value: -b.value, scale: b.scale})
|
||||
}
|
||||
|
||||
// AsScale adjusts this amount to set a minimum scale, rounding up, and returns true iff no precision
|
||||
// was lost. (1.1e5).AsScale(5) would return 1.1e5, but (1.1e5).AsScale(6) would return 1e6.
|
||||
func (a int64Amount) AsScale(scale Scale) (int64Amount, bool) {
|
||||
if a.scale >= scale {
|
||||
return a, true
|
||||
}
|
||||
result, exact := negativeScaleInt64(a.value, scale-a.scale)
|
||||
return int64Amount{value: result, scale: scale}, exact
|
||||
}
|
||||
|
||||
// AsCanonicalBytes accepts a buffer to write the base-10 string value of this field to, and returns
|
||||
// either that buffer or a larger buffer and the current exponent of the value. The value is adjusted
|
||||
// until the exponent is a multiple of 3 - i.e. 1.1e5 would return "110", 3.
|
||||
func (a int64Amount) AsCanonicalBytes(out []byte) (result []byte, exponent int32) {
|
||||
mantissa := a.value
|
||||
exponent = int32(a.scale)
|
||||
|
||||
amount, times := removeInt64Factors(mantissa, 10)
|
||||
exponent += int32(times)
|
||||
|
||||
// make sure exponent is a multiple of 3
|
||||
var ok bool
|
||||
switch exponent % 3 {
|
||||
case 1, -2:
|
||||
amount, ok = int64MultiplyScale10(amount)
|
||||
if !ok {
|
||||
return infDecAmount{a.AsDec()}.AsCanonicalBytes(out)
|
||||
}
|
||||
exponent = exponent - 1
|
||||
case 2, -1:
|
||||
amount, ok = int64MultiplyScale100(amount)
|
||||
if !ok {
|
||||
return infDecAmount{a.AsDec()}.AsCanonicalBytes(out)
|
||||
}
|
||||
exponent = exponent - 2
|
||||
}
|
||||
return strconv.AppendInt(out, amount, 10), exponent
|
||||
}
|
||||
|
||||
// AsCanonicalBase1024Bytes accepts a buffer to write the base-1024 string value of this field to, and returns
|
||||
// either that buffer or a larger buffer and the current exponent of the value. 2048 is 2 * 1024 ^ 1 and would
|
||||
// return []byte("2048"), 1.
|
||||
func (a int64Amount) AsCanonicalBase1024Bytes(out []byte) (result []byte, exponent int32) {
|
||||
value, ok := a.AsScaledInt64(0)
|
||||
if !ok {
|
||||
return infDecAmount{a.AsDec()}.AsCanonicalBase1024Bytes(out)
|
||||
}
|
||||
amount, exponent := removeInt64Factors(value, 1024)
|
||||
return strconv.AppendInt(out, amount, 10), exponent
|
||||
}
|
||||
|
||||
// infDecAmount implements common operations over an inf.Dec that are specific to the quantity
|
||||
// representation.
|
||||
type infDecAmount struct {
|
||||
*inf.Dec
|
||||
}
|
||||
|
||||
// AsScale adjusts this amount to set a minimum scale, rounding up, and returns true iff no precision
|
||||
// was lost. (1.1e5).AsScale(5) would return 1.1e5, but (1.1e5).AsScale(6) would return 1e6.
|
||||
func (a infDecAmount) AsScale(scale Scale) (infDecAmount, bool) {
|
||||
tmp := &inf.Dec{}
|
||||
tmp.Round(a.Dec, scale.infScale(), inf.RoundUp)
|
||||
return infDecAmount{tmp}, tmp.Cmp(a.Dec) == 0
|
||||
}
|
||||
|
||||
// AsCanonicalBytes accepts a buffer to write the base-10 string value of this field to, and returns
|
||||
// either that buffer or a larger buffer and the current exponent of the value. The value is adjusted
|
||||
// until the exponent is a multiple of 3 - i.e. 1.1e5 would return "110", 3.
|
||||
func (a infDecAmount) AsCanonicalBytes(out []byte) (result []byte, exponent int32) {
|
||||
mantissa := a.Dec.UnscaledBig()
|
||||
exponent = int32(-a.Dec.Scale())
|
||||
amount := big.NewInt(0).Set(mantissa)
|
||||
// move all factors of 10 into the exponent for easy reasoning
|
||||
amount, times := removeBigIntFactors(amount, bigTen)
|
||||
exponent += times
|
||||
|
||||
// make sure exponent is a multiple of 3
|
||||
for exponent%3 != 0 {
|
||||
amount.Mul(amount, bigTen)
|
||||
exponent--
|
||||
}
|
||||
|
||||
return append(out, amount.String()...), exponent
|
||||
}
|
||||
|
||||
// AsCanonicalBase1024Bytes accepts a buffer to write the base-1024 string value of this field to, and returns
|
||||
// either that buffer or a larger buffer and the current exponent of the value. 2048 is 2 * 1024 ^ 1 and would
|
||||
// return []byte("2048"), 1.
|
||||
func (a infDecAmount) AsCanonicalBase1024Bytes(out []byte) (result []byte, exponent int32) {
|
||||
tmp := &inf.Dec{}
|
||||
tmp.Round(a.Dec, 0, inf.RoundUp)
|
||||
amount, exponent := removeBigIntFactors(tmp.UnscaledBig(), big1024)
|
||||
return append(out, amount.String()...), exponent
|
||||
}
|
||||
327
vendor/github.com/appc/spec/schema/types/resource/math.go
generated
vendored
Normal file
327
vendor/github.com/appc/spec/schema/types/resource/math.go
generated
vendored
Normal file
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 resource
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
inf "gopkg.in/inf.v0"
|
||||
)
|
||||
|
||||
const (
|
||||
// maxInt64Factors is the highest value that will be checked when removing factors of 10 from an int64.
|
||||
// It is also the maximum decimal digits that can be represented with an int64.
|
||||
maxInt64Factors = 18
|
||||
)
|
||||
|
||||
var (
|
||||
// Commonly needed big.Int values-- treat as read only!
|
||||
bigTen = big.NewInt(10)
|
||||
bigZero = big.NewInt(0)
|
||||
bigOne = big.NewInt(1)
|
||||
bigThousand = big.NewInt(1000)
|
||||
big1024 = big.NewInt(1024)
|
||||
|
||||
// Commonly needed inf.Dec values-- treat as read only!
|
||||
decZero = inf.NewDec(0, 0)
|
||||
decOne = inf.NewDec(1, 0)
|
||||
decMinusOne = inf.NewDec(-1, 0)
|
||||
decThousand = inf.NewDec(1000, 0)
|
||||
dec1024 = inf.NewDec(1024, 0)
|
||||
decMinus1024 = inf.NewDec(-1024, 0)
|
||||
|
||||
// Largest (in magnitude) number allowed.
|
||||
maxAllowed = infDecAmount{inf.NewDec((1<<63)-1, 0)} // == max int64
|
||||
|
||||
// The maximum value we can represent milli-units for.
|
||||
// Compare with the return value of Quantity.Value() to
|
||||
// see if it's safe to use Quantity.MilliValue().
|
||||
MaxMilliValue = int64(((1 << 63) - 1) / 1000)
|
||||
)
|
||||
|
||||
const mostNegative = -(mostPositive + 1)
|
||||
const mostPositive = 1<<63 - 1
|
||||
|
||||
// int64Add returns a+b, or false if that would overflow int64.
|
||||
func int64Add(a, b int64) (int64, bool) {
|
||||
c := a + b
|
||||
switch {
|
||||
case a > 0 && b > 0:
|
||||
if c < 0 {
|
||||
return 0, false
|
||||
}
|
||||
case a < 0 && b < 0:
|
||||
if c > 0 {
|
||||
return 0, false
|
||||
}
|
||||
if a == mostNegative && b == mostNegative {
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
return c, true
|
||||
}
|
||||
|
||||
// int64Multiply returns a*b, or false if that would overflow or underflow int64.
|
||||
func int64Multiply(a, b int64) (int64, bool) {
|
||||
if a == 0 || b == 0 || a == 1 || b == 1 {
|
||||
return a * b, true
|
||||
}
|
||||
if a == mostNegative || b == mostNegative {
|
||||
return 0, false
|
||||
}
|
||||
c := a * b
|
||||
return c, c/b == a
|
||||
}
|
||||
|
||||
// int64MultiplyScale returns a*b, assuming b is greater than one, or false if that would overflow or underflow int64.
|
||||
// Use when b is known to be greater than one.
|
||||
func int64MultiplyScale(a int64, b int64) (int64, bool) {
|
||||
if a == 0 || a == 1 {
|
||||
return a * b, true
|
||||
}
|
||||
if a == mostNegative && b != 1 {
|
||||
return 0, false
|
||||
}
|
||||
c := a * b
|
||||
return c, c/b == a
|
||||
}
|
||||
|
||||
// int64MultiplyScale10 multiplies a by 10, or returns false if that would overflow. This method is faster than
|
||||
// int64Multiply(a, 10) because the compiler can optimize constant factor multiplication.
|
||||
func int64MultiplyScale10(a int64) (int64, bool) {
|
||||
if a == 0 || a == 1 {
|
||||
return a * 10, true
|
||||
}
|
||||
if a == mostNegative {
|
||||
return 0, false
|
||||
}
|
||||
c := a * 10
|
||||
return c, c/10 == a
|
||||
}
|
||||
|
||||
// int64MultiplyScale100 multiplies a by 100, or returns false if that would overflow. This method is faster than
|
||||
// int64Multiply(a, 100) because the compiler can optimize constant factor multiplication.
|
||||
func int64MultiplyScale100(a int64) (int64, bool) {
|
||||
if a == 0 || a == 1 {
|
||||
return a * 100, true
|
||||
}
|
||||
if a == mostNegative {
|
||||
return 0, false
|
||||
}
|
||||
c := a * 100
|
||||
return c, c/100 == a
|
||||
}
|
||||
|
||||
// int64MultiplyScale1000 multiplies a by 1000, or returns false if that would overflow. This method is faster than
|
||||
// int64Multiply(a, 1000) because the compiler can optimize constant factor multiplication.
|
||||
func int64MultiplyScale1000(a int64) (int64, bool) {
|
||||
if a == 0 || a == 1 {
|
||||
return a * 1000, true
|
||||
}
|
||||
if a == mostNegative {
|
||||
return 0, false
|
||||
}
|
||||
c := a * 1000
|
||||
return c, c/1000 == a
|
||||
}
|
||||
|
||||
// positiveScaleInt64 multiplies base by 10^scale, returning false if the
|
||||
// value overflows. Passing a negative scale is undefined.
|
||||
func positiveScaleInt64(base int64, scale Scale) (int64, bool) {
|
||||
switch scale {
|
||||
case 0:
|
||||
return base, true
|
||||
case 1:
|
||||
return int64MultiplyScale10(base)
|
||||
case 2:
|
||||
return int64MultiplyScale100(base)
|
||||
case 3:
|
||||
return int64MultiplyScale1000(base)
|
||||
case 6:
|
||||
return int64MultiplyScale(base, 1000000)
|
||||
case 9:
|
||||
return int64MultiplyScale(base, 1000000000)
|
||||
default:
|
||||
value := base
|
||||
var ok bool
|
||||
for i := Scale(0); i < scale; i++ {
|
||||
if value, ok = int64MultiplyScale(value, 10); !ok {
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
return value, true
|
||||
}
|
||||
}
|
||||
|
||||
// negativeScaleInt64 reduces base by the provided scale, rounding up, until the
|
||||
// value is zero or the scale is reached. Passing a negative scale is undefined.
|
||||
// The value returned, if not exact, is rounded away from zero.
|
||||
func negativeScaleInt64(base int64, scale Scale) (result int64, exact bool) {
|
||||
if scale == 0 {
|
||||
return base, true
|
||||
}
|
||||
|
||||
value := base
|
||||
var fraction bool
|
||||
for i := Scale(0); i < scale; i++ {
|
||||
if !fraction && value%10 != 0 {
|
||||
fraction = true
|
||||
}
|
||||
value = value / 10
|
||||
if value == 0 {
|
||||
if fraction {
|
||||
if base > 0 {
|
||||
return 1, false
|
||||
}
|
||||
return -1, false
|
||||
}
|
||||
return 0, true
|
||||
}
|
||||
}
|
||||
if fraction {
|
||||
if base > 0 {
|
||||
value += 1
|
||||
} else {
|
||||
value += -1
|
||||
}
|
||||
}
|
||||
return value, !fraction
|
||||
}
|
||||
|
||||
func pow10Int64(b int64) int64 {
|
||||
switch b {
|
||||
case 0:
|
||||
return 1
|
||||
case 1:
|
||||
return 10
|
||||
case 2:
|
||||
return 100
|
||||
case 3:
|
||||
return 1000
|
||||
case 4:
|
||||
return 10000
|
||||
case 5:
|
||||
return 100000
|
||||
case 6:
|
||||
return 1000000
|
||||
case 7:
|
||||
return 10000000
|
||||
case 8:
|
||||
return 100000000
|
||||
case 9:
|
||||
return 1000000000
|
||||
case 10:
|
||||
return 10000000000
|
||||
case 11:
|
||||
return 100000000000
|
||||
case 12:
|
||||
return 1000000000000
|
||||
case 13:
|
||||
return 10000000000000
|
||||
case 14:
|
||||
return 100000000000000
|
||||
case 15:
|
||||
return 1000000000000000
|
||||
case 16:
|
||||
return 10000000000000000
|
||||
case 17:
|
||||
return 100000000000000000
|
||||
case 18:
|
||||
return 1000000000000000000
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// powInt64 raises a to the bth power. Is not overflow aware.
|
||||
func powInt64(a, b int64) int64 {
|
||||
p := int64(1)
|
||||
for b > 0 {
|
||||
if b&1 != 0 {
|
||||
p *= a
|
||||
}
|
||||
b >>= 1
|
||||
a *= a
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// negativeScaleInt64 returns the result of dividing base by scale * 10 and the remainder, or
|
||||
// false if no such division is possible. Dividing by negative scales is undefined.
|
||||
func divideByScaleInt64(base int64, scale Scale) (result, remainder int64, exact bool) {
|
||||
if scale == 0 {
|
||||
return base, 0, true
|
||||
}
|
||||
// the max scale representable in base 10 in an int64 is 18 decimal places
|
||||
if scale >= 18 {
|
||||
return 0, base, false
|
||||
}
|
||||
divisor := pow10Int64(int64(scale))
|
||||
return base / divisor, base % divisor, true
|
||||
}
|
||||
|
||||
// removeInt64Factors divides in a loop; the return values have the property that
|
||||
// value == result * base ^ scale
|
||||
func removeInt64Factors(value int64, base int64) (result int64, times int32) {
|
||||
times = 0
|
||||
result = value
|
||||
negative := result < 0
|
||||
if negative {
|
||||
result = -result
|
||||
}
|
||||
switch base {
|
||||
// allow the compiler to optimize the common cases
|
||||
case 10:
|
||||
for result >= 10 && result%10 == 0 {
|
||||
times++
|
||||
result = result / 10
|
||||
}
|
||||
// allow the compiler to optimize the common cases
|
||||
case 1024:
|
||||
for result >= 1024 && result%1024 == 0 {
|
||||
times++
|
||||
result = result / 1024
|
||||
}
|
||||
default:
|
||||
for result >= base && result%base == 0 {
|
||||
times++
|
||||
result = result / base
|
||||
}
|
||||
}
|
||||
if negative {
|
||||
result = -result
|
||||
}
|
||||
return result, times
|
||||
}
|
||||
|
||||
// removeBigIntFactors divides in a loop; the return values have the property that
|
||||
// d == result * factor ^ times
|
||||
// d may be modified in place.
|
||||
// If d == 0, then the return values will be (0, 0)
|
||||
func removeBigIntFactors(d, factor *big.Int) (result *big.Int, times int32) {
|
||||
q := big.NewInt(0)
|
||||
m := big.NewInt(0)
|
||||
for d.Cmp(bigZero) != 0 {
|
||||
q.DivMod(d, factor, m)
|
||||
if m.Cmp(bigZero) != 0 {
|
||||
break
|
||||
}
|
||||
times++
|
||||
d, q = q, d
|
||||
}
|
||||
return d, times
|
||||
}
|
||||
768
vendor/github.com/appc/spec/schema/types/resource/quantity.go
generated
vendored
Normal file
768
vendor/github.com/appc/spec/schema/types/resource/quantity.go
generated
vendored
Normal file
@@ -0,0 +1,768 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 resource
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
flag "github.com/spf13/pflag"
|
||||
|
||||
inf "gopkg.in/inf.v0"
|
||||
)
|
||||
|
||||
// Quantity is a fixed-point representation of a number.
|
||||
// It provides convenient marshaling/unmarshaling in JSON and YAML,
|
||||
// in addition to String() and Int64() accessors.
|
||||
//
|
||||
// The serialization format is:
|
||||
//
|
||||
// <quantity> ::= <signedNumber><suffix>
|
||||
// (Note that <suffix> may be empty, from the "" case in <decimalSI>.)
|
||||
// <digit> ::= 0 | 1 | ... | 9
|
||||
// <digits> ::= <digit> | <digit><digits>
|
||||
// <number> ::= <digits> | <digits>.<digits> | <digits>. | .<digits>
|
||||
// <sign> ::= "+" | "-"
|
||||
// <signedNumber> ::= <number> | <sign><number>
|
||||
// <suffix> ::= <binarySI> | <decimalExponent> | <decimalSI>
|
||||
// <binarySI> ::= Ki | Mi | Gi | Ti | Pi | Ei
|
||||
// (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)
|
||||
// <decimalSI> ::= m | "" | k | M | G | T | P | E
|
||||
// (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)
|
||||
// <decimalExponent> ::= "e" <signedNumber> | "E" <signedNumber>
|
||||
//
|
||||
// No matter which of the three exponent forms is used, no quantity may represent
|
||||
// a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal
|
||||
// places. Numbers larger or more precise will be capped or rounded up.
|
||||
// (E.g.: 0.1m will rounded up to 1m.)
|
||||
// This may be extended in the future if we require larger or smaller quantities.
|
||||
//
|
||||
// When a Quantity is parsed from a string, it will remember the type of suffix
|
||||
// it had, and will use the same type again when it is serialized.
|
||||
//
|
||||
// Before serializing, Quantity will be put in "canonical form".
|
||||
// This means that Exponent/suffix will be adjusted up or down (with a
|
||||
// corresponding increase or decrease in Mantissa) such that:
|
||||
// a. No precision is lost
|
||||
// b. No fractional digits will be emitted
|
||||
// c. The exponent (or suffix) is as large as possible.
|
||||
// The sign will be omitted unless the number is negative.
|
||||
//
|
||||
// Examples:
|
||||
// 1.5 will be serialized as "1500m"
|
||||
// 1.5Gi will be serialized as "1536Mi"
|
||||
//
|
||||
// NOTE: We reserve the right to amend this canonical format, perhaps to
|
||||
// allow 1.5 to be canonical.
|
||||
// TODO: Remove above disclaimer after all bikeshedding about format is over,
|
||||
// or after March 2015.
|
||||
//
|
||||
// Note that the quantity will NEVER be internally represented by a
|
||||
// floating point number. That is the whole point of this exercise.
|
||||
//
|
||||
// Non-canonical values will still parse as long as they are well formed,
|
||||
// but will be re-emitted in their canonical form. (So always use canonical
|
||||
// form, or don't diff.)
|
||||
//
|
||||
// This format is intended to make it difficult to use these numbers without
|
||||
// writing some sort of special handling code in the hopes that that will
|
||||
// cause implementors to also use a fixed point implementation.
|
||||
//
|
||||
// +gencopy=false
|
||||
// +protobuf=true
|
||||
// +protobuf.embed=string
|
||||
// +protobuf.options.marshal=false
|
||||
// +protobuf.options.(gogoproto.goproto_stringer)=false
|
||||
type Quantity struct {
|
||||
// i is the quantity in int64 scaled form, if d.Dec == nil
|
||||
i int64Amount
|
||||
// d is the quantity in inf.Dec form if d.Dec != nil
|
||||
d infDecAmount
|
||||
// s is the generated value of this quantity to avoid recalculation
|
||||
s string
|
||||
|
||||
// Change Format at will. See the comment for Canonicalize for
|
||||
// more details.
|
||||
Format
|
||||
}
|
||||
|
||||
// CanonicalValue allows a quantity amount to be converted to a string.
|
||||
type CanonicalValue interface {
|
||||
// AsCanonicalBytes returns a byte array representing the string representation
|
||||
// of the value mantissa and an int32 representing its exponent in base-10. Callers may
|
||||
// pass a byte slice to the method to avoid allocations.
|
||||
AsCanonicalBytes(out []byte) ([]byte, int32)
|
||||
// AsCanonicalBase1024Bytes returns a byte array representing the string representation
|
||||
// of the value mantissa and an int32 representing its exponent in base-1024. Callers
|
||||
// may pass a byte slice to the method to avoid allocations.
|
||||
AsCanonicalBase1024Bytes(out []byte) ([]byte, int32)
|
||||
}
|
||||
|
||||
// Format lists the three possible formattings of a quantity.
|
||||
type Format string
|
||||
|
||||
const (
|
||||
DecimalExponent = Format("DecimalExponent") // e.g., 12e6
|
||||
BinarySI = Format("BinarySI") // e.g., 12Mi (12 * 2^20)
|
||||
DecimalSI = Format("DecimalSI") // e.g., 12M (12 * 10^6)
|
||||
)
|
||||
|
||||
// MustParse turns the given string into a quantity or panics; for tests
|
||||
// or others cases where you know the string is valid.
|
||||
func MustParse(str string) Quantity {
|
||||
q, err := ParseQuantity(str)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot parse '%v': %v", str, err))
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
const (
|
||||
// splitREString is used to separate a number from its suffix; as such,
|
||||
// this is overly permissive, but that's OK-- it will be checked later.
|
||||
splitREString = "^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$"
|
||||
)
|
||||
|
||||
var (
|
||||
// splitRE is used to get the various parts of a number.
|
||||
splitRE = regexp.MustCompile(splitREString)
|
||||
|
||||
// Errors that could happen while parsing a string.
|
||||
ErrFormatWrong = errors.New("quantities must match the regular expression '" + splitREString + "'")
|
||||
ErrNumeric = errors.New("unable to parse numeric part of quantity")
|
||||
ErrSuffix = errors.New("unable to parse quantity's suffix")
|
||||
)
|
||||
|
||||
// parseQuantityString is a fast scanner for quantity values.
|
||||
func parseQuantityString(str string) (positive bool, value, num, denom, suffix string, err error) {
|
||||
positive = true
|
||||
pos := 0
|
||||
end := len(str)
|
||||
|
||||
// handle leading sign
|
||||
if pos < end {
|
||||
switch str[0] {
|
||||
case '-':
|
||||
positive = false
|
||||
pos++
|
||||
case '+':
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
// strip leading zeros
|
||||
Zeroes:
|
||||
for i := pos; ; i++ {
|
||||
if i >= end {
|
||||
num = "0"
|
||||
value = num
|
||||
return
|
||||
}
|
||||
switch str[i] {
|
||||
case '0':
|
||||
pos++
|
||||
default:
|
||||
break Zeroes
|
||||
}
|
||||
}
|
||||
|
||||
// extract the numerator
|
||||
Num:
|
||||
for i := pos; ; i++ {
|
||||
if i >= end {
|
||||
num = str[pos:end]
|
||||
value = str[0:end]
|
||||
return
|
||||
}
|
||||
switch str[i] {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
default:
|
||||
num = str[pos:i]
|
||||
pos = i
|
||||
break Num
|
||||
}
|
||||
}
|
||||
|
||||
// if we stripped all numerator positions, always return 0
|
||||
if len(num) == 0 {
|
||||
num = "0"
|
||||
}
|
||||
|
||||
// handle a denominator
|
||||
if pos < end && str[pos] == '.' {
|
||||
pos++
|
||||
Denom:
|
||||
for i := pos; ; i++ {
|
||||
if i >= end {
|
||||
denom = str[pos:end]
|
||||
value = str[0:end]
|
||||
return
|
||||
}
|
||||
switch str[i] {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
default:
|
||||
denom = str[pos:i]
|
||||
pos = i
|
||||
break Denom
|
||||
}
|
||||
}
|
||||
// TODO: we currently allow 1.G, but we may not want to in the future.
|
||||
// if len(denom) == 0 {
|
||||
// err = ErrFormatWrong
|
||||
// return
|
||||
// }
|
||||
}
|
||||
value = str[0:pos]
|
||||
|
||||
// grab the elements of the suffix
|
||||
suffixStart := pos
|
||||
for i := pos; ; i++ {
|
||||
if i >= end {
|
||||
suffix = str[suffixStart:end]
|
||||
return
|
||||
}
|
||||
if !strings.ContainsAny(str[i:i+1], "eEinumkKMGTP") {
|
||||
pos = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if pos < end {
|
||||
switch str[pos] {
|
||||
case '-', '+':
|
||||
pos++
|
||||
}
|
||||
}
|
||||
Suffix:
|
||||
for i := pos; ; i++ {
|
||||
if i >= end {
|
||||
suffix = str[suffixStart:end]
|
||||
return
|
||||
}
|
||||
switch str[i] {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
default:
|
||||
break Suffix
|
||||
}
|
||||
}
|
||||
// we encountered a non decimal in the Suffix loop, but the last character
|
||||
// was not a valid exponent
|
||||
err = ErrFormatWrong
|
||||
return
|
||||
}
|
||||
|
||||
// ParseQuantity turns str into a Quantity, or returns an error.
|
||||
func ParseQuantity(str string) (Quantity, error) {
|
||||
if len(str) == 0 {
|
||||
return Quantity{}, ErrFormatWrong
|
||||
}
|
||||
if str == "0" {
|
||||
return Quantity{Format: DecimalSI, s: str}, nil
|
||||
}
|
||||
|
||||
positive, value, num, denom, suf, err := parseQuantityString(str)
|
||||
if err != nil {
|
||||
return Quantity{}, err
|
||||
}
|
||||
|
||||
base, exponent, format, ok := quantitySuffixer.interpret(suffix(suf))
|
||||
if !ok {
|
||||
return Quantity{}, ErrSuffix
|
||||
}
|
||||
|
||||
precision := int32(0)
|
||||
scale := int32(0)
|
||||
mantissa := int64(1)
|
||||
switch format {
|
||||
case DecimalExponent, DecimalSI:
|
||||
scale = exponent
|
||||
precision = maxInt64Factors - int32(len(num)+len(denom))
|
||||
case BinarySI:
|
||||
scale = 0
|
||||
switch {
|
||||
case exponent >= 0 && len(denom) == 0:
|
||||
// only handle positive binary numbers with the fast path
|
||||
mantissa = int64(int64(mantissa) << uint64(exponent))
|
||||
// 1Mi (2^20) has ~6 digits of decimal precision, so exponent*3/10 -1 is roughly the precision
|
||||
precision = 15 - int32(len(num)) - int32(float32(exponent)*3/10) - 1
|
||||
default:
|
||||
precision = -1
|
||||
}
|
||||
}
|
||||
|
||||
if precision >= 0 {
|
||||
// if we have a denominator, shift the entire value to the left by the number of places in the
|
||||
// denominator
|
||||
scale -= int32(len(denom))
|
||||
if scale >= int32(Nano) {
|
||||
shifted := num + denom
|
||||
|
||||
var value int64
|
||||
value, err := strconv.ParseInt(shifted, 10, 64)
|
||||
if err != nil {
|
||||
return Quantity{}, ErrNumeric
|
||||
}
|
||||
if result, ok := int64Multiply(value, int64(mantissa)); ok {
|
||||
if !positive {
|
||||
result = -result
|
||||
}
|
||||
// if the number is in canonical form, reuse the string
|
||||
switch format {
|
||||
case BinarySI:
|
||||
if exponent%10 == 0 && (value&0x07 != 0) {
|
||||
return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format, s: str}, nil
|
||||
}
|
||||
default:
|
||||
if scale%3 == 0 && !strings.HasSuffix(shifted, "000") && shifted[0] != '0' {
|
||||
return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format, s: str}, nil
|
||||
}
|
||||
}
|
||||
return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
amount := new(inf.Dec)
|
||||
if _, ok := amount.SetString(value); !ok {
|
||||
return Quantity{}, ErrNumeric
|
||||
}
|
||||
|
||||
// So that no one but us has to think about suffixes, remove it.
|
||||
if base == 10 {
|
||||
amount.SetScale(amount.Scale() + Scale(exponent).infScale())
|
||||
} else if base == 2 {
|
||||
// numericSuffix = 2 ** exponent
|
||||
numericSuffix := big.NewInt(1).Lsh(bigOne, uint(exponent))
|
||||
ub := amount.UnscaledBig()
|
||||
amount.SetUnscaledBig(ub.Mul(ub, numericSuffix))
|
||||
}
|
||||
|
||||
// Cap at min/max bounds.
|
||||
sign := amount.Sign()
|
||||
if sign == -1 {
|
||||
amount.Neg(amount)
|
||||
}
|
||||
|
||||
// This rounds non-zero values up to the minimum representable value, under the theory that
|
||||
// if you want some resources, you should get some resources, even if you asked for way too small
|
||||
// of an amount. Arguably, this should be inf.RoundHalfUp (normal rounding), but that would have
|
||||
// the side effect of rounding values < .5n to zero.
|
||||
if v, ok := amount.Unscaled(); v != int64(0) || !ok {
|
||||
amount.Round(amount, Nano.infScale(), inf.RoundUp)
|
||||
}
|
||||
|
||||
// The max is just a simple cap.
|
||||
// TODO: this prevents accumulating quantities greater than int64, for instance quota across a cluster
|
||||
if format == BinarySI && amount.Cmp(maxAllowed.Dec) > 0 {
|
||||
amount.Set(maxAllowed.Dec)
|
||||
}
|
||||
|
||||
if format == BinarySI && amount.Cmp(decOne) < 0 && amount.Cmp(decZero) > 0 {
|
||||
// This avoids rounding and hopefully confusion, too.
|
||||
format = DecimalSI
|
||||
}
|
||||
if sign == -1 {
|
||||
amount.Neg(amount)
|
||||
}
|
||||
|
||||
return Quantity{d: infDecAmount{amount}, Format: format}, nil
|
||||
}
|
||||
|
||||
// CanonicalizeBytes returns the canonical form of q and its suffix (see comment on Quantity).
|
||||
//
|
||||
// Note about BinarySI:
|
||||
// * If q.Format is set to BinarySI and q.Amount represents a non-zero value between
|
||||
// -1 and +1, it will be emitted as if q.Format were DecimalSI.
|
||||
// * Otherwise, if q.Format is set to BinarySI, frational parts of q.Amount will be
|
||||
// rounded up. (1.1i becomes 2i.)
|
||||
func (q *Quantity) CanonicalizeBytes(out []byte) (result, suffix []byte) {
|
||||
if q.IsZero() {
|
||||
return zeroBytes, nil
|
||||
}
|
||||
|
||||
var rounded CanonicalValue
|
||||
format := q.Format
|
||||
switch format {
|
||||
case DecimalExponent, DecimalSI:
|
||||
case BinarySI:
|
||||
if q.CmpInt64(-1024) > 0 && q.CmpInt64(1024) < 0 {
|
||||
// This avoids rounding and hopefully confusion, too.
|
||||
format = DecimalSI
|
||||
} else {
|
||||
var exact bool
|
||||
if rounded, exact = q.AsScale(0); !exact {
|
||||
// Don't lose precision-- show as DecimalSI
|
||||
format = DecimalSI
|
||||
}
|
||||
}
|
||||
default:
|
||||
format = DecimalExponent
|
||||
}
|
||||
|
||||
// TODO: If BinarySI formatting is requested but would cause rounding, upgrade to
|
||||
// one of the other formats.
|
||||
switch format {
|
||||
case DecimalExponent, DecimalSI:
|
||||
number, exponent := q.AsCanonicalBytes(out)
|
||||
suffix, _ := quantitySuffixer.constructBytes(10, exponent, format)
|
||||
return number, suffix
|
||||
default:
|
||||
// format must be BinarySI
|
||||
number, exponent := rounded.AsCanonicalBase1024Bytes(out)
|
||||
suffix, _ := quantitySuffixer.constructBytes(2, exponent*10, format)
|
||||
return number, suffix
|
||||
}
|
||||
}
|
||||
|
||||
// AsInt64 returns a representation of the current value as an int64 if a fast conversion
|
||||
// is possible. If false is returned, callers must use the inf.Dec form of this quantity.
|
||||
func (q *Quantity) AsInt64() (int64, bool) {
|
||||
if q.d.Dec != nil {
|
||||
return 0, false
|
||||
}
|
||||
return q.i.AsInt64()
|
||||
}
|
||||
|
||||
// ToDec promotes the quantity in place to use an inf.Dec representation and returns itself.
|
||||
func (q *Quantity) ToDec() *Quantity {
|
||||
if q.d.Dec == nil {
|
||||
q.d.Dec = q.i.AsDec()
|
||||
q.i = int64Amount{}
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
// AsDec returns the quantity as represented by a scaled inf.Dec.
|
||||
func (q *Quantity) AsDec() *inf.Dec {
|
||||
if q.d.Dec != nil {
|
||||
return q.d.Dec
|
||||
}
|
||||
q.d.Dec = q.i.AsDec()
|
||||
q.i = int64Amount{}
|
||||
return q.d.Dec
|
||||
}
|
||||
|
||||
// AsCanonicalBytes returns the canonical byte representation of this quantity as a mantissa
|
||||
// and base 10 exponent. The out byte slice may be passed to the method to avoid an extra
|
||||
// allocation.
|
||||
func (q *Quantity) AsCanonicalBytes(out []byte) (result []byte, exponent int32) {
|
||||
if q.d.Dec != nil {
|
||||
return q.d.AsCanonicalBytes(out)
|
||||
}
|
||||
return q.i.AsCanonicalBytes(out)
|
||||
}
|
||||
|
||||
// IsZero returns true if the quantity is equal to zero.
|
||||
func (q *Quantity) IsZero() bool {
|
||||
if q.d.Dec != nil {
|
||||
return q.d.Dec.Sign() == 0
|
||||
}
|
||||
return q.i.value == 0
|
||||
}
|
||||
|
||||
// Sign returns 0 if the quantity is zero, -1 if the quantity is less than zero, or 1 if the
|
||||
// quantity is greater than zero.
|
||||
func (q *Quantity) Sign() int {
|
||||
if q.d.Dec != nil {
|
||||
return q.d.Dec.Sign()
|
||||
}
|
||||
return q.i.Sign()
|
||||
}
|
||||
|
||||
// AsScaled returns the current value, rounded up to the provided scale, and returns
|
||||
// false if the scale resulted in a loss of precision.
|
||||
func (q *Quantity) AsScale(scale Scale) (CanonicalValue, bool) {
|
||||
if q.d.Dec != nil {
|
||||
return q.d.AsScale(scale)
|
||||
}
|
||||
return q.i.AsScale(scale)
|
||||
}
|
||||
|
||||
// RoundUp updates the quantity to the provided scale, ensuring that the value is at
|
||||
// least 1. False is returned if the rounding operation resulted in a loss of precision.
|
||||
// Negative numbers are rounded away from zero (-9 scale 1 rounds to -10).
|
||||
func (q *Quantity) RoundUp(scale Scale) bool {
|
||||
if q.d.Dec != nil {
|
||||
q.s = ""
|
||||
d, exact := q.d.AsScale(scale)
|
||||
q.d = d
|
||||
return exact
|
||||
}
|
||||
// avoid clearing the string value if we have already calculated it
|
||||
if q.i.scale >= scale {
|
||||
return true
|
||||
}
|
||||
q.s = ""
|
||||
i, exact := q.i.AsScale(scale)
|
||||
q.i = i
|
||||
return exact
|
||||
}
|
||||
|
||||
// Add adds the provide y quantity to the current value. If the current value is zero,
|
||||
// the format of the quantity will be updated to the format of y.
|
||||
func (q *Quantity) Add(y Quantity) {
|
||||
q.s = ""
|
||||
if q.d.Dec == nil && y.d.Dec == nil {
|
||||
if q.i.value == 0 {
|
||||
q.Format = y.Format
|
||||
}
|
||||
if q.i.Add(y.i) {
|
||||
return
|
||||
}
|
||||
} else if q.IsZero() {
|
||||
q.Format = y.Format
|
||||
}
|
||||
q.ToDec().d.Dec.Add(q.d.Dec, y.AsDec())
|
||||
}
|
||||
|
||||
// Sub subtracts the provided quantity from the current value in place. If the current
|
||||
// value is zero, the format of the quantity will be updated to the format of y.
|
||||
func (q *Quantity) Sub(y Quantity) {
|
||||
q.s = ""
|
||||
if q.IsZero() {
|
||||
q.Format = y.Format
|
||||
}
|
||||
if q.d.Dec == nil && y.d.Dec == nil && q.i.Sub(y.i) {
|
||||
return
|
||||
}
|
||||
q.ToDec().d.Dec.Sub(q.d.Dec, y.AsDec())
|
||||
}
|
||||
|
||||
// Cmp returns 0 if the quantity is equal to y, -1 if the quantity is less than y, or 1 if the
|
||||
// quantity is greater than y.
|
||||
func (q *Quantity) Cmp(y Quantity) int {
|
||||
if q.d.Dec == nil && y.d.Dec == nil {
|
||||
return q.i.Cmp(y.i)
|
||||
}
|
||||
return q.AsDec().Cmp(y.AsDec())
|
||||
}
|
||||
|
||||
// CmpInt64 returns 0 if the quantity is equal to y, -1 if the quantity is less than y, or 1 if the
|
||||
// quantity is greater than y.
|
||||
func (q *Quantity) CmpInt64(y int64) int {
|
||||
if q.d.Dec != nil {
|
||||
return q.d.Dec.Cmp(inf.NewDec(y, inf.Scale(0)))
|
||||
}
|
||||
return q.i.Cmp(int64Amount{value: y})
|
||||
}
|
||||
|
||||
// Neg sets quantity to be the negative value of itself.
|
||||
func (q *Quantity) Neg() {
|
||||
q.s = ""
|
||||
if q.d.Dec == nil {
|
||||
q.i.value = -q.i.value
|
||||
return
|
||||
}
|
||||
q.d.Dec.Neg(q.d.Dec)
|
||||
}
|
||||
|
||||
// int64QuantityExpectedBytes is the expected width in bytes of the canonical string representation
|
||||
// of most Quantity values.
|
||||
const int64QuantityExpectedBytes = 18
|
||||
|
||||
// String formats the Quantity as a string, caching the result if not calculated.
|
||||
// String is an expensive operation and caching this result significantly reduces the cost of
|
||||
// normal parse / marshal operations on Quantity.
|
||||
func (q *Quantity) String() string {
|
||||
if len(q.s) == 0 {
|
||||
result := make([]byte, 0, int64QuantityExpectedBytes)
|
||||
number, suffix := q.CanonicalizeBytes(result)
|
||||
number = append(number, suffix...)
|
||||
q.s = string(number)
|
||||
}
|
||||
return q.s
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaller interface.
|
||||
func (q Quantity) MarshalJSON() ([]byte, error) {
|
||||
if len(q.s) > 0 {
|
||||
out := make([]byte, len(q.s)+2)
|
||||
out[0], out[len(out)-1] = '"', '"'
|
||||
copy(out[1:], q.s)
|
||||
return out, nil
|
||||
}
|
||||
result := make([]byte, int64QuantityExpectedBytes, int64QuantityExpectedBytes)
|
||||
result[0] = '"'
|
||||
number, suffix := q.CanonicalizeBytes(result[1:1])
|
||||
// if the same slice was returned to us that we passed in, avoid another allocation by copying number into
|
||||
// the source slice and returning that
|
||||
if len(number) > 0 && &number[0] == &result[1] && (len(number)+len(suffix)+2) <= int64QuantityExpectedBytes {
|
||||
number = append(number, suffix...)
|
||||
number = append(number, '"')
|
||||
return result[:1+len(number)], nil
|
||||
}
|
||||
// if CanonicalizeBytes needed more space than our slice provided, we may need to allocate again so use
|
||||
// append
|
||||
result = result[:1]
|
||||
result = append(result, number...)
|
||||
result = append(result, suffix...)
|
||||
result = append(result, '"')
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaller interface.
|
||||
// TODO: Remove support for leading/trailing whitespace
|
||||
func (q *Quantity) UnmarshalJSON(value []byte) error {
|
||||
l := len(value)
|
||||
if l == 4 && bytes.Equal(value, []byte("null")) {
|
||||
q.d.Dec = nil
|
||||
q.i = int64Amount{}
|
||||
return nil
|
||||
}
|
||||
if l >= 2 && value[0] == '"' && value[l-1] == '"' {
|
||||
value = value[1 : l-1]
|
||||
}
|
||||
|
||||
parsed, err := ParseQuantity(strings.TrimSpace(string(value)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// This copy is safe because parsed will not be referred to again.
|
||||
*q = parsed
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewQuantity returns a new Quantity representing the given
|
||||
// value in the given format.
|
||||
func NewQuantity(value int64, format Format) *Quantity {
|
||||
return &Quantity{
|
||||
i: int64Amount{value: value},
|
||||
Format: format,
|
||||
}
|
||||
}
|
||||
|
||||
// NewMilliQuantity returns a new Quantity representing the given
|
||||
// value * 1/1000 in the given format. Note that BinarySI formatting
|
||||
// will round fractional values, and will be changed to DecimalSI for
|
||||
// values x where (-1 < x < 1) && (x != 0).
|
||||
func NewMilliQuantity(value int64, format Format) *Quantity {
|
||||
return &Quantity{
|
||||
i: int64Amount{value: value, scale: -3},
|
||||
Format: format,
|
||||
}
|
||||
}
|
||||
|
||||
// NewScaledQuantity returns a new Quantity representing the given
|
||||
// value * 10^scale in DecimalSI format.
|
||||
func NewScaledQuantity(value int64, scale Scale) *Quantity {
|
||||
return &Quantity{
|
||||
i: int64Amount{value: value, scale: scale},
|
||||
Format: DecimalSI,
|
||||
}
|
||||
}
|
||||
|
||||
// Value returns the value of q; any fractional part will be lost.
|
||||
func (q *Quantity) Value() int64 {
|
||||
return q.ScaledValue(0)
|
||||
}
|
||||
|
||||
// MilliValue returns the value of ceil(q * 1000); this could overflow an int64;
|
||||
// if that's a concern, call Value() first to verify the number is small enough.
|
||||
func (q *Quantity) MilliValue() int64 {
|
||||
return q.ScaledValue(Milli)
|
||||
}
|
||||
|
||||
// ScaledValue returns the value of ceil(q * 10^scale); this could overflow an int64.
|
||||
// To detect overflow, call Value() first and verify the expected magnitude.
|
||||
func (q *Quantity) ScaledValue(scale Scale) int64 {
|
||||
if q.d.Dec == nil {
|
||||
i, _ := q.i.AsScaledInt64(scale)
|
||||
return i
|
||||
}
|
||||
dec := q.d.Dec
|
||||
return scaledValue(dec.UnscaledBig(), int(dec.Scale()), int(scale.infScale()))
|
||||
}
|
||||
|
||||
// Set sets q's value to be value.
|
||||
func (q *Quantity) Set(value int64) {
|
||||
q.SetScaled(value, 0)
|
||||
}
|
||||
|
||||
// SetMilli sets q's value to be value * 1/1000.
|
||||
func (q *Quantity) SetMilli(value int64) {
|
||||
q.SetScaled(value, Milli)
|
||||
}
|
||||
|
||||
// SetScaled sets q's value to be value * 10^scale
|
||||
func (q *Quantity) SetScaled(value int64, scale Scale) {
|
||||
q.s = ""
|
||||
q.d.Dec = nil
|
||||
q.i = int64Amount{value: value, scale: scale}
|
||||
}
|
||||
|
||||
// Copy is a convenience function that makes a deep copy for you. Non-deep
|
||||
// copies of quantities share pointers and you will regret that.
|
||||
func (q *Quantity) Copy() *Quantity {
|
||||
if q.d.Dec == nil {
|
||||
return &Quantity{
|
||||
s: q.s,
|
||||
i: q.i,
|
||||
Format: q.Format,
|
||||
}
|
||||
}
|
||||
tmp := &inf.Dec{}
|
||||
return &Quantity{
|
||||
s: q.s,
|
||||
d: infDecAmount{tmp.Set(q.d.Dec)},
|
||||
Format: q.Format,
|
||||
}
|
||||
}
|
||||
|
||||
// qFlag is a helper type for the Flag function
|
||||
type qFlag struct {
|
||||
dest *Quantity
|
||||
}
|
||||
|
||||
// Sets the value of the internal Quantity. (used by flag & pflag)
|
||||
func (qf qFlag) Set(val string) error {
|
||||
q, err := ParseQuantity(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// This copy is OK because q will not be referenced again.
|
||||
*qf.dest = q
|
||||
return nil
|
||||
}
|
||||
|
||||
// Converts the value of the internal Quantity to a string. (used by flag & pflag)
|
||||
func (qf qFlag) String() string {
|
||||
return qf.dest.String()
|
||||
}
|
||||
|
||||
// States the type of flag this is (Quantity). (used by pflag)
|
||||
func (qf qFlag) Type() string {
|
||||
return "quantity"
|
||||
}
|
||||
|
||||
// QuantityFlag is a helper that makes a quantity flag (using standard flag package).
|
||||
// Will panic if defaultValue is not a valid quantity.
|
||||
func QuantityFlag(flagName, defaultValue, description string) *Quantity {
|
||||
q := MustParse(defaultValue)
|
||||
flag.Var(NewQuantityFlagValue(&q), flagName, description)
|
||||
return &q
|
||||
}
|
||||
|
||||
// NewQuantityFlagValue returns an object that can be used to back a flag,
|
||||
// pointing at the given Quantity variable.
|
||||
func NewQuantityFlagValue(q *Quantity) flag.Value {
|
||||
return qFlag{q}
|
||||
}
|
||||
95
vendor/github.com/appc/spec/schema/types/resource/scale_int.go
generated
vendored
Normal file
95
vendor/github.com/appc/spec/schema/types/resource/scale_int.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 resource
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/big"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
// A sync pool to reduce allocation.
|
||||
intPool sync.Pool
|
||||
maxInt64 = big.NewInt(math.MaxInt64)
|
||||
)
|
||||
|
||||
func init() {
|
||||
intPool.New = func() interface{} {
|
||||
return &big.Int{}
|
||||
}
|
||||
}
|
||||
|
||||
// scaledValue scales given unscaled value from scale to new Scale and returns
|
||||
// an int64. It ALWAYS rounds up the result when scale down. The final result might
|
||||
// overflow.
|
||||
//
|
||||
// scale, newScale represents the scale of the unscaled decimal.
|
||||
// The mathematical value of the decimal is unscaled * 10**(-scale).
|
||||
func scaledValue(unscaled *big.Int, scale, newScale int) int64 {
|
||||
dif := scale - newScale
|
||||
if dif == 0 {
|
||||
return unscaled.Int64()
|
||||
}
|
||||
|
||||
// Handle scale up
|
||||
// This is an easy case, we do not need to care about rounding and overflow.
|
||||
// If any intermediate operation causes overflow, the result will overflow.
|
||||
if dif < 0 {
|
||||
return unscaled.Int64() * int64(math.Pow10(-dif))
|
||||
}
|
||||
|
||||
// Handle scale down
|
||||
// We have to be careful about the intermediate operations.
|
||||
|
||||
// fast path when unscaled < max.Int64 and exp(10,dif) < max.Int64
|
||||
const log10MaxInt64 = 19
|
||||
if unscaled.Cmp(maxInt64) < 0 && dif < log10MaxInt64 {
|
||||
divide := int64(math.Pow10(dif))
|
||||
result := unscaled.Int64() / divide
|
||||
mod := unscaled.Int64() % divide
|
||||
if mod != 0 {
|
||||
return result + 1
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// We should only convert back to int64 when getting the result.
|
||||
divisor := intPool.Get().(*big.Int)
|
||||
exp := intPool.Get().(*big.Int)
|
||||
result := intPool.Get().(*big.Int)
|
||||
defer func() {
|
||||
intPool.Put(divisor)
|
||||
intPool.Put(exp)
|
||||
intPool.Put(result)
|
||||
}()
|
||||
|
||||
// divisor = 10^(dif)
|
||||
// TODO: create loop up table if exp costs too much.
|
||||
divisor.Exp(bigTen, exp.SetInt64(int64(dif)), nil)
|
||||
// reuse exp
|
||||
remainder := exp
|
||||
|
||||
// result = unscaled / divisor
|
||||
// remainder = unscaled % divisor
|
||||
result.DivMod(unscaled, divisor, remainder)
|
||||
if remainder.Sign() != 0 {
|
||||
return result.Int64() + 1
|
||||
}
|
||||
|
||||
return result.Int64()
|
||||
}
|
||||
198
vendor/github.com/appc/spec/schema/types/resource/suffix.go
generated
vendored
Normal file
198
vendor/github.com/appc/spec/schema/types/resource/suffix.go
generated
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 resource
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type suffix string
|
||||
|
||||
// suffixer can interpret and construct suffixes.
|
||||
type suffixer interface {
|
||||
interpret(suffix) (base, exponent int32, fmt Format, ok bool)
|
||||
construct(base, exponent int32, fmt Format) (s suffix, ok bool)
|
||||
constructBytes(base, exponent int32, fmt Format) (s []byte, ok bool)
|
||||
}
|
||||
|
||||
// quantitySuffixer handles suffixes for all three formats that quantity
|
||||
// can handle.
|
||||
var quantitySuffixer = newSuffixer()
|
||||
|
||||
type bePair struct {
|
||||
base, exponent int32
|
||||
}
|
||||
|
||||
type listSuffixer struct {
|
||||
suffixToBE map[suffix]bePair
|
||||
beToSuffix map[bePair]suffix
|
||||
beToSuffixBytes map[bePair][]byte
|
||||
}
|
||||
|
||||
func (ls *listSuffixer) addSuffix(s suffix, pair bePair) {
|
||||
if ls.suffixToBE == nil {
|
||||
ls.suffixToBE = map[suffix]bePair{}
|
||||
}
|
||||
if ls.beToSuffix == nil {
|
||||
ls.beToSuffix = map[bePair]suffix{}
|
||||
}
|
||||
if ls.beToSuffixBytes == nil {
|
||||
ls.beToSuffixBytes = map[bePair][]byte{}
|
||||
}
|
||||
ls.suffixToBE[s] = pair
|
||||
ls.beToSuffix[pair] = s
|
||||
ls.beToSuffixBytes[pair] = []byte(s)
|
||||
}
|
||||
|
||||
func (ls *listSuffixer) lookup(s suffix) (base, exponent int32, ok bool) {
|
||||
pair, ok := ls.suffixToBE[s]
|
||||
if !ok {
|
||||
return 0, 0, false
|
||||
}
|
||||
return pair.base, pair.exponent, true
|
||||
}
|
||||
|
||||
func (ls *listSuffixer) construct(base, exponent int32) (s suffix, ok bool) {
|
||||
s, ok = ls.beToSuffix[bePair{base, exponent}]
|
||||
return
|
||||
}
|
||||
|
||||
func (ls *listSuffixer) constructBytes(base, exponent int32) (s []byte, ok bool) {
|
||||
s, ok = ls.beToSuffixBytes[bePair{base, exponent}]
|
||||
return
|
||||
}
|
||||
|
||||
type suffixHandler struct {
|
||||
decSuffixes listSuffixer
|
||||
binSuffixes listSuffixer
|
||||
}
|
||||
|
||||
type fastLookup struct {
|
||||
*suffixHandler
|
||||
}
|
||||
|
||||
func (l fastLookup) interpret(s suffix) (base, exponent int32, format Format, ok bool) {
|
||||
switch s {
|
||||
case "":
|
||||
return 10, 0, DecimalSI, true
|
||||
case "n":
|
||||
return 10, -9, DecimalSI, true
|
||||
case "u":
|
||||
return 10, -6, DecimalSI, true
|
||||
case "m":
|
||||
return 10, -3, DecimalSI, true
|
||||
case "k":
|
||||
return 10, 3, DecimalSI, true
|
||||
case "M":
|
||||
return 10, 6, DecimalSI, true
|
||||
case "G":
|
||||
return 10, 9, DecimalSI, true
|
||||
}
|
||||
return l.suffixHandler.interpret(s)
|
||||
}
|
||||
|
||||
func newSuffixer() suffixer {
|
||||
sh := &suffixHandler{}
|
||||
|
||||
// IMPORTANT: if you change this section you must change fastLookup
|
||||
|
||||
sh.binSuffixes.addSuffix("Ki", bePair{2, 10})
|
||||
sh.binSuffixes.addSuffix("Mi", bePair{2, 20})
|
||||
sh.binSuffixes.addSuffix("Gi", bePair{2, 30})
|
||||
sh.binSuffixes.addSuffix("Ti", bePair{2, 40})
|
||||
sh.binSuffixes.addSuffix("Pi", bePair{2, 50})
|
||||
sh.binSuffixes.addSuffix("Ei", bePair{2, 60})
|
||||
// Don't emit an error when trying to produce
|
||||
// a suffix for 2^0.
|
||||
sh.decSuffixes.addSuffix("", bePair{2, 0})
|
||||
|
||||
sh.decSuffixes.addSuffix("n", bePair{10, -9})
|
||||
sh.decSuffixes.addSuffix("u", bePair{10, -6})
|
||||
sh.decSuffixes.addSuffix("m", bePair{10, -3})
|
||||
sh.decSuffixes.addSuffix("", bePair{10, 0})
|
||||
sh.decSuffixes.addSuffix("k", bePair{10, 3})
|
||||
sh.decSuffixes.addSuffix("M", bePair{10, 6})
|
||||
sh.decSuffixes.addSuffix("G", bePair{10, 9})
|
||||
sh.decSuffixes.addSuffix("T", bePair{10, 12})
|
||||
sh.decSuffixes.addSuffix("P", bePair{10, 15})
|
||||
sh.decSuffixes.addSuffix("E", bePair{10, 18})
|
||||
|
||||
return fastLookup{sh}
|
||||
}
|
||||
|
||||
func (sh *suffixHandler) construct(base, exponent int32, fmt Format) (s suffix, ok bool) {
|
||||
switch fmt {
|
||||
case DecimalSI:
|
||||
return sh.decSuffixes.construct(base, exponent)
|
||||
case BinarySI:
|
||||
return sh.binSuffixes.construct(base, exponent)
|
||||
case DecimalExponent:
|
||||
if base != 10 {
|
||||
return "", false
|
||||
}
|
||||
if exponent == 0 {
|
||||
return "", true
|
||||
}
|
||||
return suffix("e" + strconv.FormatInt(int64(exponent), 10)), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func (sh *suffixHandler) constructBytes(base, exponent int32, format Format) (s []byte, ok bool) {
|
||||
switch format {
|
||||
case DecimalSI:
|
||||
return sh.decSuffixes.constructBytes(base, exponent)
|
||||
case BinarySI:
|
||||
return sh.binSuffixes.constructBytes(base, exponent)
|
||||
case DecimalExponent:
|
||||
if base != 10 {
|
||||
return nil, false
|
||||
}
|
||||
if exponent == 0 {
|
||||
return nil, true
|
||||
}
|
||||
result := make([]byte, 8, 8)
|
||||
result[0] = 'e'
|
||||
number := strconv.AppendInt(result[1:1], int64(exponent), 10)
|
||||
if &result[1] == &number[0] {
|
||||
return result[:1+len(number)], true
|
||||
}
|
||||
result = append(result[:1], number...)
|
||||
return result, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (sh *suffixHandler) interpret(suffix suffix) (base, exponent int32, fmt Format, ok bool) {
|
||||
// Try lookup tables first
|
||||
if b, e, ok := sh.decSuffixes.lookup(suffix); ok {
|
||||
return b, e, DecimalSI, true
|
||||
}
|
||||
if b, e, ok := sh.binSuffixes.lookup(suffix); ok {
|
||||
return b, e, BinarySI, true
|
||||
}
|
||||
|
||||
if len(suffix) > 1 && (suffix[0] == 'E' || suffix[0] == 'e') {
|
||||
parsed, err := strconv.ParseInt(string(suffix[1:]), 10, 64)
|
||||
if err != nil {
|
||||
return 0, 0, DecimalExponent, false
|
||||
}
|
||||
return 10, int32(parsed), DecimalExponent, true
|
||||
}
|
||||
|
||||
return 0, 0, DecimalExponent, false
|
||||
}
|
||||
91
vendor/github.com/appc/spec/schema/types/semver.go
generated
vendored
Normal file
91
vendor/github.com/appc/spec/schema/types/semver.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
// 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"
|
||||
|
||||
"github.com/coreos/go-semver/semver"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoZeroSemVer = ACVersionError("SemVer cannot be zero")
|
||||
ErrBadSemVer = ACVersionError("SemVer is bad")
|
||||
)
|
||||
|
||||
// SemVer implements the Unmarshaler interface to define a field that must be
|
||||
// a semantic version string
|
||||
// TODO(jonboulle): extend upstream instead of wrapping?
|
||||
type SemVer semver.Version
|
||||
|
||||
// NewSemVer generates a new SemVer from a string. If the given string does
|
||||
// not represent a valid SemVer, nil and an error are returned.
|
||||
func NewSemVer(s string) (*SemVer, error) {
|
||||
nsv, err := semver.NewVersion(s)
|
||||
if err != nil {
|
||||
return nil, ErrBadSemVer
|
||||
}
|
||||
v := SemVer(*nsv)
|
||||
if v.Empty() {
|
||||
return nil, ErrNoZeroSemVer
|
||||
}
|
||||
return &v, nil
|
||||
}
|
||||
|
||||
func (sv SemVer) LessThanMajor(versionB SemVer) bool {
|
||||
majorA := semver.Version(sv).Major
|
||||
majorB := semver.Version(versionB).Major
|
||||
if majorA < majorB {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (sv SemVer) LessThanExact(versionB SemVer) bool {
|
||||
vA := semver.Version(sv)
|
||||
vB := semver.Version(versionB)
|
||||
return vA.LessThan(vB)
|
||||
}
|
||||
|
||||
func (sv SemVer) String() string {
|
||||
s := semver.Version(sv)
|
||||
return s.String()
|
||||
}
|
||||
|
||||
func (sv SemVer) Empty() bool {
|
||||
return semver.Version(sv) == semver.Version{}
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface
|
||||
func (sv *SemVer) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
v, err := NewSemVer(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*sv = *v
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface
|
||||
func (sv SemVer) MarshalJSON() ([]byte, error) {
|
||||
if sv.Empty() {
|
||||
return nil, ErrNoZeroSemVer
|
||||
}
|
||||
return json.Marshal(sv.String())
|
||||
}
|
||||
71
vendor/github.com/appc/spec/schema/types/url.go
generated
vendored
Normal file
71
vendor/github.com/appc/spec/schema/types/url.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
// 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"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// URL wraps url.URL to marshal/unmarshal to/from JSON strings and enforce
|
||||
// that the scheme is HTTP/HTTPS only
|
||||
type URL url.URL
|
||||
|
||||
func NewURL(s string) (*URL, error) {
|
||||
uu, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("bad URL: %v", err)
|
||||
}
|
||||
nu := URL(*uu)
|
||||
if err := nu.assertValidScheme(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &nu, nil
|
||||
}
|
||||
|
||||
func (u URL) String() string {
|
||||
uu := url.URL(u)
|
||||
return uu.String()
|
||||
}
|
||||
|
||||
func (u URL) assertValidScheme() error {
|
||||
switch u.Scheme {
|
||||
case "http", "https":
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("bad URL scheme, must be http/https")
|
||||
}
|
||||
}
|
||||
|
||||
func (u *URL) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
nu, err := NewURL(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*u = *nu
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u URL) MarshalJSON() ([]byte, error) {
|
||||
if err := u.assertValidScheme(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(u.String())
|
||||
}
|
||||
18
vendor/github.com/appc/spec/schema/types/user_annotations.go
generated
vendored
Normal file
18
vendor/github.com/appc/spec/schema/types/user_annotations.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2016 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
|
||||
|
||||
// UserAnnotations are arbitrary key-value pairs, to be supplied and interpreted by the user
|
||||
type UserAnnotations map[string]string
|
||||
18
vendor/github.com/appc/spec/schema/types/user_labels.go
generated
vendored
Normal file
18
vendor/github.com/appc/spec/schema/types/user_labels.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
// 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
|
||||
|
||||
// UserLabels are arbitrary key-value pairs, to be supplied and interpreted by the user
|
||||
type UserLabels map[string]string
|
||||
92
vendor/github.com/appc/spec/schema/types/uuid.go
generated
vendored
Normal file
92
vendor/github.com/appc/spec/schema/types/uuid.go
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
// 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/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoEmptyUUID = errors.New("UUID cannot be empty")
|
||||
)
|
||||
|
||||
// UUID encodes an RFC4122-compliant UUID, marshaled to/from a string
|
||||
// TODO(jonboulle): vendor a package for this?
|
||||
// TODO(jonboulle): consider more flexibility in input string formats.
|
||||
// Right now, we only accept:
|
||||
// "6733C088-A507-4694-AABF-EDBE4FC5266F"
|
||||
// "6733C088A5074694AABFEDBE4FC5266F"
|
||||
type UUID [16]byte
|
||||
|
||||
func (u UUID) String() string {
|
||||
return fmt.Sprintf("%x-%x-%x-%x-%x", u[0:4], u[4:6], u[6:8], u[8:10], u[10:16])
|
||||
}
|
||||
|
||||
func (u *UUID) Set(s string) error {
|
||||
nu, err := NewUUID(s)
|
||||
if err == nil {
|
||||
*u = *nu
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// NewUUID generates a new UUID from the given string. If the string does not
|
||||
// represent a valid UUID, nil and an error are returned.
|
||||
func NewUUID(s string) (*UUID, error) {
|
||||
s = strings.Replace(s, "-", "", -1)
|
||||
if len(s) != 32 {
|
||||
return nil, errors.New("bad UUID length != 32")
|
||||
}
|
||||
dec, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var u UUID
|
||||
for i, b := range dec {
|
||||
u[i] = b
|
||||
}
|
||||
return &u, nil
|
||||
}
|
||||
|
||||
func (u UUID) Empty() bool {
|
||||
return reflect.DeepEqual(u, UUID{})
|
||||
}
|
||||
|
||||
func (u *UUID) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
uu, err := NewUUID(s)
|
||||
if uu.Empty() {
|
||||
return ErrNoEmptyUUID
|
||||
}
|
||||
if err == nil {
|
||||
*u = *uu
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (u UUID) MarshalJSON() ([]byte, error) {
|
||||
if u.Empty() {
|
||||
return nil, ErrNoEmptyUUID
|
||||
}
|
||||
return json.Marshal(u.String())
|
||||
}
|
||||
249
vendor/github.com/appc/spec/schema/types/volume.go
generated
vendored
Normal file
249
vendor/github.com/appc/spec/schema/types/volume.go
generated
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
// 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"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/appc/spec/schema/common"
|
||||
)
|
||||
|
||||
const (
|
||||
emptyVolumeDefaultMode = "0755"
|
||||
emptyVolumeDefaultUID = 0
|
||||
emptyVolumeDefaultGID = 0
|
||||
)
|
||||
|
||||
// Volume encapsulates a volume which should be mounted into the filesystem
|
||||
// of all apps in a PodManifest
|
||||
type Volume struct {
|
||||
Name ACName `json:"name"`
|
||||
Kind string `json:"kind"`
|
||||
|
||||
// currently used only by "host"
|
||||
// TODO(jonboulle): factor out?
|
||||
Source string `json:"source,omitempty"`
|
||||
ReadOnly *bool `json:"readOnly,omitempty"`
|
||||
Recursive *bool `json:"recursive,omitempty"`
|
||||
|
||||
// currently used only by "empty"
|
||||
Mode *string `json:"mode,omitempty"`
|
||||
UID *int `json:"uid,omitempty"`
|
||||
GID *int `json:"gid,omitempty"`
|
||||
}
|
||||
|
||||
type volume Volume
|
||||
|
||||
func (v Volume) assertValid() error {
|
||||
if v.Name.Empty() {
|
||||
return errors.New("name must be set")
|
||||
}
|
||||
|
||||
switch v.Kind {
|
||||
case "empty":
|
||||
if v.Source != "" {
|
||||
return errors.New("source for empty volume must be empty")
|
||||
}
|
||||
if v.Mode == nil {
|
||||
return errors.New("mode for empty volume must be set")
|
||||
}
|
||||
if v.UID == nil {
|
||||
return errors.New("uid for empty volume must be set")
|
||||
}
|
||||
if v.GID == nil {
|
||||
return errors.New("gid for empty volume must be set")
|
||||
}
|
||||
return nil
|
||||
case "host":
|
||||
if v.Source == "" {
|
||||
return errors.New("source for host volume cannot be empty")
|
||||
}
|
||||
if v.Mode != nil {
|
||||
return errors.New("mode for host volume cannot be set")
|
||||
}
|
||||
if v.UID != nil {
|
||||
return errors.New("uid for host volume cannot be set")
|
||||
}
|
||||
if v.GID != nil {
|
||||
return errors.New("gid for host volume cannot be set")
|
||||
}
|
||||
if !filepath.IsAbs(v.Source) {
|
||||
return errors.New("source for host volume must be absolute path")
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return errors.New(`unrecognized volume kind: should be one of "empty", "host"`)
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Volume) UnmarshalJSON(data []byte) error {
|
||||
var vv volume
|
||||
if err := json.Unmarshal(data, &vv); err != nil {
|
||||
return err
|
||||
}
|
||||
nv := Volume(vv)
|
||||
maybeSetDefaults(&nv)
|
||||
if err := nv.assertValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
*v = nv
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v Volume) MarshalJSON() ([]byte, error) {
|
||||
if err := v.assertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(volume(v))
|
||||
}
|
||||
|
||||
func (v Volume) String() string {
|
||||
s := []string{
|
||||
v.Name.String(),
|
||||
",kind=",
|
||||
v.Kind,
|
||||
}
|
||||
if v.Source != "" {
|
||||
s = append(s, ",source=")
|
||||
s = append(s, v.Source)
|
||||
}
|
||||
if v.ReadOnly != nil {
|
||||
s = append(s, ",readOnly=")
|
||||
s = append(s, strconv.FormatBool(*v.ReadOnly))
|
||||
}
|
||||
if v.Recursive != nil {
|
||||
s = append(s, ",recursive=")
|
||||
s = append(s, strconv.FormatBool(*v.Recursive))
|
||||
}
|
||||
switch v.Kind {
|
||||
case "empty":
|
||||
if *v.Mode != emptyVolumeDefaultMode {
|
||||
s = append(s, ",mode=")
|
||||
s = append(s, *v.Mode)
|
||||
}
|
||||
if *v.UID != emptyVolumeDefaultUID {
|
||||
s = append(s, ",uid=")
|
||||
s = append(s, strconv.Itoa(*v.UID))
|
||||
}
|
||||
if *v.GID != emptyVolumeDefaultGID {
|
||||
s = append(s, ",gid=")
|
||||
s = append(s, strconv.Itoa(*v.GID))
|
||||
}
|
||||
}
|
||||
return strings.Join(s, "")
|
||||
}
|
||||
|
||||
// VolumeFromString takes a command line volume parameter and returns a volume
|
||||
//
|
||||
// Example volume parameters:
|
||||
// database,kind=host,source=/tmp,readOnly=true,recursive=true
|
||||
func VolumeFromString(vp string) (*Volume, error) {
|
||||
vp = "name=" + vp
|
||||
vpQuery, err := common.MakeQueryString(vp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v, err := url.ParseQuery(vpQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return VolumeFromParams(v)
|
||||
}
|
||||
|
||||
func VolumeFromParams(params map[string][]string) (*Volume, error) {
|
||||
var vol Volume
|
||||
for key, val := range params {
|
||||
val := val
|
||||
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
|
||||
}
|
||||
vol.Name = *acn
|
||||
case "kind":
|
||||
vol.Kind = val[0]
|
||||
case "source":
|
||||
vol.Source = val[0]
|
||||
case "readOnly":
|
||||
ro, err := strconv.ParseBool(val[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vol.ReadOnly = &ro
|
||||
case "recursive":
|
||||
rec, err := strconv.ParseBool(val[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vol.Recursive = &rec
|
||||
case "mode":
|
||||
vol.Mode = &val[0]
|
||||
case "uid":
|
||||
u, err := strconv.Atoi(val[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vol.UID = &u
|
||||
case "gid":
|
||||
g, err := strconv.Atoi(val[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vol.GID = &g
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown volume parameter %q", key)
|
||||
}
|
||||
}
|
||||
|
||||
maybeSetDefaults(&vol)
|
||||
|
||||
if err := vol.assertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &vol, nil
|
||||
}
|
||||
|
||||
// maybeSetDefaults sets the correct default values for certain fields on a
|
||||
// Volume if they are not already been set. These fields are not
|
||||
// pre-populated on all Volumes as the Volume type is polymorphic.
|
||||
func maybeSetDefaults(vol *Volume) {
|
||||
if vol.Kind == "empty" {
|
||||
if vol.Mode == nil {
|
||||
m := emptyVolumeDefaultMode
|
||||
vol.Mode = &m
|
||||
}
|
||||
if vol.UID == nil {
|
||||
u := emptyVolumeDefaultUID
|
||||
vol.UID = &u
|
||||
}
|
||||
if vol.GID == nil {
|
||||
g := emptyVolumeDefaultGID
|
||||
vol.GID = &g
|
||||
}
|
||||
}
|
||||
}
|
||||
39
vendor/github.com/appc/spec/schema/version.go
generated
vendored
Normal file
39
vendor/github.com/appc/spec/schema/version.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
// 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 (
|
||||
"github.com/appc/spec/schema/types"
|
||||
)
|
||||
|
||||
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.8.11"
|
||||
)
|
||||
|
||||
var (
|
||||
// AppContainerVersion is the SemVer representation of version
|
||||
AppContainerVersion types.SemVer
|
||||
)
|
||||
|
||||
func init() {
|
||||
v, err := types.NewSemVer(version)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
AppContainerVersion = *v
|
||||
}
|
||||
Reference in New Issue
Block a user