Merge pull request #849 from runcom/fix-caps
container_create: handle cap add/drop ALL
This commit is contained in:
commit
631359ed69
25 changed files with 2057 additions and 283 deletions
|
@ -484,8 +484,21 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
|||
}
|
||||
}
|
||||
|
||||
var readOnlyRootfs bool
|
||||
var privileged bool
|
||||
if containerConfig.GetLinux().GetSecurityContext() != nil {
|
||||
if containerConfig.GetLinux().GetSecurityContext().Privileged {
|
||||
privileged = true
|
||||
}
|
||||
|
||||
if containerConfig.GetLinux().GetSecurityContext().ReadonlyRootfs {
|
||||
readOnlyRootfs = true
|
||||
specgen.SetRootReadonly(true)
|
||||
}
|
||||
}
|
||||
|
||||
// set this container's apparmor profile if it is set by sandbox
|
||||
if s.appArmorEnabled {
|
||||
if s.appArmorEnabled && !privileged {
|
||||
appArmorProfileName := s.getAppArmorProfileName(sb.Annotations(), metadata.Name)
|
||||
if appArmorProfileName != "" {
|
||||
// reload default apparmor profile if it is unloaded.
|
||||
|
@ -498,20 +511,6 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
|||
specgen.SetProcessApparmorProfile(appArmorProfileName)
|
||||
}
|
||||
}
|
||||
var readOnlyRootfs bool
|
||||
if containerConfig.GetLinux().GetSecurityContext() != nil {
|
||||
if containerConfig.GetLinux().GetSecurityContext().Privileged {
|
||||
if !sb.Privileged() {
|
||||
return nil, fmt.Errorf("no privileged container allowed in sandbox")
|
||||
}
|
||||
specgen.SetupPrivileged(true)
|
||||
}
|
||||
|
||||
if containerConfig.GetLinux().GetSecurityContext().ReadonlyRootfs {
|
||||
readOnlyRootfs = true
|
||||
specgen.SetRootReadonly(true)
|
||||
}
|
||||
}
|
||||
|
||||
logPath := containerConfig.LogPath
|
||||
if logPath == "" {
|
||||
|
@ -586,33 +585,59 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
|||
sb.UpdateCgroupParent(parent)
|
||||
|
||||
capabilities := linux.GetSecurityContext().GetCapabilities()
|
||||
if privileged {
|
||||
// this is setting correct capabilities as well for privileged mode
|
||||
specgen.SetupPrivileged(true)
|
||||
} else {
|
||||
toCAPPrefixed := func(cap string) string {
|
||||
if !strings.HasPrefix(strings.ToLower(cap), "cap_") {
|
||||
return "CAP_" + cap
|
||||
return "CAP_" + strings.ToUpper(cap)
|
||||
}
|
||||
return cap
|
||||
}
|
||||
if capabilities != nil {
|
||||
addCaps := capabilities.AddCapabilities
|
||||
if addCaps != nil {
|
||||
for _, cap := range addCaps {
|
||||
if err := specgen.AddProcessCapability(toCAPPrefixed(cap)); err != nil {
|
||||
|
||||
// Add/drop all capabilities if "all" is specified, so that
|
||||
// following individual add/drop could still work. E.g.
|
||||
// AddCapabilities: []string{"ALL"}, DropCapabilities: []string{"CHOWN"}
|
||||
// will be all capabilities without `CAP_CHOWN`.
|
||||
// see https://github.com/kubernetes/kubernetes/issues/51980
|
||||
if inStringSlice(capabilities.GetAddCapabilities(), "ALL") {
|
||||
for _, c := range getOCICapabilitiesList() {
|
||||
if err := specgen.AddProcessCapability(c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if inStringSlice(capabilities.GetDropCapabilities(), "ALL") {
|
||||
for _, c := range getOCICapabilitiesList() {
|
||||
if err := specgen.DropProcessCapability(c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dropCaps := capabilities.DropCapabilities
|
||||
if dropCaps != nil {
|
||||
for _, cap := range dropCaps {
|
||||
if err := specgen.DropProcessCapability(toCAPPrefixed(cap)); err != nil {
|
||||
logrus.Debugf("failed to drop cap %s: %v", toCAPPrefixed(cap), err)
|
||||
}
|
||||
if capabilities != nil {
|
||||
for _, cap := range capabilities.GetAddCapabilities() {
|
||||
if strings.ToUpper(cap) == "ALL" {
|
||||
continue
|
||||
}
|
||||
if err := specgen.AddProcessCapability(toCAPPrefixed(cap)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, cap := range capabilities.GetDropCapabilities() {
|
||||
if strings.ToUpper(cap) == "ALL" {
|
||||
continue
|
||||
}
|
||||
if err := specgen.DropProcessCapability(toCAPPrefixed(cap)); err != nil {
|
||||
return nil, fmt.Errorf("failed to drop cap %s %v", toCAPPrefixed(cap), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
specgen.SetProcessSelinuxLabel(sb.ProcessLabel())
|
||||
}
|
||||
|
||||
specgen.SetLinuxMountLabel(sb.MountLabel())
|
||||
|
||||
if containerConfig.GetLinux().GetSecurityContext() != nil &&
|
||||
|
@ -770,9 +795,11 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
|||
}
|
||||
specgen.AddAnnotation(annotations.Annotations, string(kubeAnnotationsJSON))
|
||||
|
||||
if !privileged {
|
||||
if err = s.setupSeccomp(&specgen, containerName, sb.Annotations()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
metaname := metadata.Name
|
||||
attempt := metadata.Attempt
|
||||
|
|
|
@ -7,6 +7,8 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/cri-o/ocicni/pkg/ocicni"
|
||||
"github.com/opencontainers/runtime-tools/validate"
|
||||
"github.com/syndtr/gocapability/capability"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -155,3 +157,26 @@ func newPodNetwork(namespace, name, id, netns string) ocicni.PodNetwork {
|
|||
NetNS: netns,
|
||||
}
|
||||
}
|
||||
|
||||
// inStringSlice checks whether a string is inside a string slice.
|
||||
// Comparison is case insensitive.
|
||||
func inStringSlice(ss []string, str string) bool {
|
||||
for _, s := range ss {
|
||||
if strings.ToLower(s) == strings.ToLower(str) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// getOCICapabilitiesList returns a list of all available capabilities.
|
||||
func getOCICapabilitiesList() []string {
|
||||
var caps []string
|
||||
for _, cap := range capability.List() {
|
||||
if cap > validate.LastCap() {
|
||||
continue
|
||||
}
|
||||
caps = append(caps, "CAP_"+strings.ToUpper(cap.String()))
|
||||
}
|
||||
return caps
|
||||
}
|
||||
|
|
1
test/testdata/container_config.json
vendored
1
test/testdata/container_config.json
vendored
|
@ -57,7 +57,6 @@
|
|||
"setgid"
|
||||
],
|
||||
"drop_capabilities": [
|
||||
"audit_read"
|
||||
]
|
||||
},
|
||||
"selinux_options": {
|
||||
|
|
|
@ -57,7 +57,6 @@
|
|||
"setgid"
|
||||
],
|
||||
"drop_capabilities": [
|
||||
"audit_read"
|
||||
]
|
||||
},
|
||||
"selinux_options": {
|
||||
|
|
1
test/testdata/container_config_hostport.json
vendored
1
test/testdata/container_config_hostport.json
vendored
|
@ -59,7 +59,6 @@
|
|||
"setgid"
|
||||
],
|
||||
"drop_capabilities": [
|
||||
"audit_read"
|
||||
]
|
||||
},
|
||||
"selinux_options": {
|
||||
|
|
1
test/testdata/container_config_logging.json
vendored
1
test/testdata/container_config_logging.json
vendored
|
@ -59,7 +59,6 @@
|
|||
"setgid"
|
||||
],
|
||||
"drop_capabilities": [
|
||||
"audit_read"
|
||||
]
|
||||
},
|
||||
"selinux_options": {
|
||||
|
|
|
@ -59,7 +59,6 @@
|
|||
"setgid"
|
||||
],
|
||||
"drop_capabilities": [
|
||||
"audit_read"
|
||||
]
|
||||
},
|
||||
"selinux_options": {
|
||||
|
|
|
@ -59,7 +59,6 @@
|
|||
"setgid"
|
||||
],
|
||||
"drop_capabilities": [
|
||||
"audit_read"
|
||||
]
|
||||
},
|
||||
"selinux_options": {
|
||||
|
|
1
test/testdata/container_config_seccomp.json
vendored
1
test/testdata/container_config_seccomp.json
vendored
|
@ -59,7 +59,6 @@
|
|||
"setgid"
|
||||
],
|
||||
"drop_capabilities": [
|
||||
"audit_read"
|
||||
]
|
||||
},
|
||||
"selinux_options": {
|
||||
|
|
|
@ -13,7 +13,7 @@ github.com/containernetworking/cni v0.4.0
|
|||
google.golang.org/grpc v1.0.4 https://github.com/grpc/grpc-go
|
||||
github.com/opencontainers/selinux v1.0.0-rc1
|
||||
github.com/opencontainers/go-digest v1.0.0-rc0
|
||||
github.com/opencontainers/runtime-tools 6bcd3b417fd6962ea04dafdbc2c07444e750572d
|
||||
github.com/opencontainers/runtime-tools d3f7e9e9e631c7e87552d67dc7c86de33c3fb68a
|
||||
github.com/opencontainers/runc 45bde006ca8c90e089894508708bcf0e2cdf9e13
|
||||
github.com/mrunalp/fileutils master
|
||||
github.com/vishvananda/netlink master
|
||||
|
@ -100,3 +100,5 @@ github.com/beorn7/perks 3ac7bf7a47d159a033b107610db8a1b6575507a4
|
|||
github.com/containerd/cgroups 7a5fdd8330119dc70d850260db8f3594d89d6943
|
||||
github.com/go-zoo/bone 031b4005dfe248ccba241a0c9de0f9e112fd6b7c
|
||||
github.com/soheilhy/cmux v0.1.3
|
||||
github.com/hashicorp/go-multierror 83588e72410abfbe4df460eeb6f30841ae47d4c4
|
||||
github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55
|
||||
|
|
353
vendor/github.com/hashicorp/errwrap/LICENSE
generated
vendored
Normal file
353
vendor/github.com/hashicorp/errwrap/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,353 @@
|
|||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
1.1. “Contributor”
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. “Contributor Version”
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor’s Contribution.
|
||||
|
||||
1.3. “Contribution”
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. “Covered Software”
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. “Incompatible With Secondary Licenses”
|
||||
means
|
||||
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
b. that the Covered Software was made available under the terms of version
|
||||
1.1 or earlier of the License, but not also under the terms of a
|
||||
Secondary License.
|
||||
|
||||
1.6. “Executable Form”
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. “Larger Work”
|
||||
|
||||
means a work that combines Covered Software with other material, in a separate
|
||||
file or files, that is not Covered Software.
|
||||
|
||||
1.8. “License”
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. “Licensable”
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether at the
|
||||
time of the initial grant or subsequently, any and all of the rights conveyed by
|
||||
this License.
|
||||
|
||||
1.10. “Modifications”
|
||||
|
||||
means any of the following:
|
||||
|
||||
a. any file in Source Code Form that results from an addition to, deletion
|
||||
from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. “Patent Claims” of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method, process,
|
||||
and apparatus claims, in any patent Licensable by such Contributor that
|
||||
would be infringed, but for the grant of the License, by the making,
|
||||
using, selling, offering for sale, having made, import, or transfer of
|
||||
either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. “Secondary License”
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. “Source Code Form”
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. “You” (or “Your”)
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, “You” includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, “control” means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or as
|
||||
part of a Larger Work; and
|
||||
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its Contributions
|
||||
or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution become
|
||||
effective for each Contribution on the date the Contributor first distributes
|
||||
such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under this
|
||||
License. No additional rights or licenses will be implied from the distribution
|
||||
or licensing of Covered Software under this License. Notwithstanding Section
|
||||
2.1(b) above, no patent license is granted by a Contributor:
|
||||
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
b. for infringements caused by: (i) Your and any other third party’s
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
c. under Patent Claims infringed by Covered Software in the absence of its
|
||||
Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks, or
|
||||
logos of any Contributor (except as may be necessary to comply with the
|
||||
notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this License
|
||||
(see Section 10.2) or under the terms of a Secondary License (if permitted
|
||||
under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its Contributions
|
||||
are its original creation(s) or it has sufficient rights to grant the
|
||||
rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under applicable
|
||||
copyright doctrines of fair use, fair dealing, or other equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under the
|
||||
terms of this License. You must inform recipients that the Source Code Form
|
||||
of the Covered Software is governed by the terms of this License, and how
|
||||
they can obtain a copy of this License. You may not attempt to alter or
|
||||
restrict the recipients’ rights in the Source Code Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
b. You may distribute such Executable Form under the terms of this License,
|
||||
or sublicense it under different terms, provided that the license for
|
||||
the Executable Form does not attempt to limit or alter the recipients’
|
||||
rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for the
|
||||
Covered Software. If the Larger Work is a combination of Covered Software
|
||||
with a work governed by one or more Secondary Licenses, and the Covered
|
||||
Software is not Incompatible With Secondary Licenses, this License permits
|
||||
You to additionally distribute such Covered Software under the terms of
|
||||
such Secondary License(s), so that the recipient of the Larger Work may, at
|
||||
their option, further distribute the Covered Software under the terms of
|
||||
either this License or such Secondary License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices (including
|
||||
copyright notices, patent notices, disclaimers of warranty, or limitations
|
||||
of liability) contained within the Source Code Form of the Covered
|
||||
Software, except that You may alter any license notices to the extent
|
||||
required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on behalf
|
||||
of any Contributor. You must make it absolutely clear that any such
|
||||
warranty, support, indemnity, or liability obligation is offered by You
|
||||
alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute, judicial
|
||||
order, or regulation then You must: (a) comply with the terms of this License
|
||||
to the maximum extent possible; and (b) describe the limitations and the code
|
||||
they affect. Such description must be placed in a text file included with all
|
||||
distributions of the Covered Software under this License. Except to the
|
||||
extent prohibited by statute or regulation, such description must be
|
||||
sufficiently detailed for a recipient of ordinary skill to be able to
|
||||
understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing basis,
|
||||
if such Contributor fails to notify You of the non-compliance by some
|
||||
reasonable means prior to 60 days after You have come back into compliance.
|
||||
Moreover, Your grants from a particular Contributor are reinstated on an
|
||||
ongoing basis if such Contributor notifies You of the non-compliance by
|
||||
some reasonable means, this is the first time You have received notice of
|
||||
non-compliance with this License from such Contributor, and You become
|
||||
compliant prior to 30 days after Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions, counter-claims,
|
||||
and cross-claims) alleging that a Contributor Version directly or
|
||||
indirectly infringes any patent, then the rights granted to You by any and
|
||||
all Contributors for the Covered Software under Section 2.1 of this License
|
||||
shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
Covered Software is provided under this License on an “as is” basis, without
|
||||
warranty of any kind, either expressed, implied, or statutory, including,
|
||||
without limitation, warranties that the Covered Software is free of defects,
|
||||
merchantable, fit for a particular purpose or non-infringing. The entire
|
||||
risk as to the quality and performance of the Covered Software is with You.
|
||||
Should any Covered Software prove defective in any respect, You (not any
|
||||
Contributor) assume the cost of any necessary servicing, repair, or
|
||||
correction. This disclaimer of warranty constitutes an essential part of this
|
||||
License. No use of any Covered Software is authorized under this License
|
||||
except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from such
|
||||
party’s negligence to the extent applicable law prohibits such limitation.
|
||||
Some jurisdictions do not allow the exclusion or limitation of incidental or
|
||||
consequential damages, so this exclusion and limitation may not apply to You.
|
||||
|
||||
8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts of
|
||||
a jurisdiction where the defendant maintains its principal place of business
|
||||
and such litigation shall be governed by laws of that jurisdiction, without
|
||||
reference to its conflict-of-law provisions. Nothing in this Section shall
|
||||
prevent a party’s ability to bring cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject matter
|
||||
hereof. If any provision of this License is held to be unenforceable, such
|
||||
provision shall be reformed only to the extent necessary to make it
|
||||
enforceable. Any law or regulation which provides that the language of a
|
||||
contract shall be construed against the drafter shall not be used to construe
|
||||
this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version of
|
||||
the License under which You originally received the Covered Software, or
|
||||
under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a modified
|
||||
version of this License if you rename the license and remove any
|
||||
references to the name of the license steward (except to note that such
|
||||
modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file, then
|
||||
You may include the notice in a location (such as a LICENSE file in a relevant
|
||||
directory) where a recipient would be likely to look for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - “Incompatible With Secondary Licenses” Notice
|
||||
|
||||
This Source Code Form is “Incompatible
|
||||
With Secondary Licenses”, as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
89
vendor/github.com/hashicorp/errwrap/README.md
generated
vendored
Normal file
89
vendor/github.com/hashicorp/errwrap/README.md
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
|||
# errwrap
|
||||
|
||||
`errwrap` is a package for Go that formalizes the pattern of wrapping errors
|
||||
and checking if an error contains another error.
|
||||
|
||||
There is a common pattern in Go of taking a returned `error` value and
|
||||
then wrapping it (such as with `fmt.Errorf`) before returning it. The problem
|
||||
with this pattern is that you completely lose the original `error` structure.
|
||||
|
||||
Arguably the _correct_ approach is that you should make a custom structure
|
||||
implementing the `error` interface, and have the original error as a field
|
||||
on that structure, such [as this example](http://golang.org/pkg/os/#PathError).
|
||||
This is a good approach, but you have to know the entire chain of possible
|
||||
rewrapping that happens, when you might just care about one.
|
||||
|
||||
`errwrap` formalizes this pattern (it doesn't matter what approach you use
|
||||
above) by giving a single interface for wrapping errors, checking if a specific
|
||||
error is wrapped, and extracting that error.
|
||||
|
||||
## Installation and Docs
|
||||
|
||||
Install using `go get github.com/hashicorp/errwrap`.
|
||||
|
||||
Full documentation is available at
|
||||
http://godoc.org/github.com/hashicorp/errwrap
|
||||
|
||||
## Usage
|
||||
|
||||
#### Basic Usage
|
||||
|
||||
Below is a very basic example of its usage:
|
||||
|
||||
```go
|
||||
// A function that always returns an error, but wraps it, like a real
|
||||
// function might.
|
||||
func tryOpen() error {
|
||||
_, err := os.Open("/i/dont/exist")
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Doesn't exist: {{err}}", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := tryOpen()
|
||||
|
||||
// We can use the Contains helpers to check if an error contains
|
||||
// another error. It is safe to do this with a nil error, or with
|
||||
// an error that doesn't even use the errwrap package.
|
||||
if errwrap.Contains(err, ErrNotExist) {
|
||||
// Do something
|
||||
}
|
||||
if errwrap.ContainsType(err, new(os.PathError)) {
|
||||
// Do something
|
||||
}
|
||||
|
||||
// Or we can use the associated `Get` functions to just extract
|
||||
// a specific error. This would return nil if that specific error doesn't
|
||||
// exist.
|
||||
perr := errwrap.GetType(err, new(os.PathError))
|
||||
}
|
||||
```
|
||||
|
||||
#### Custom Types
|
||||
|
||||
If you're already making custom types that properly wrap errors, then
|
||||
you can get all the functionality of `errwraps.Contains` and such by
|
||||
implementing the `Wrapper` interface with just one function. Example:
|
||||
|
||||
```go
|
||||
type AppError {
|
||||
Code ErrorCode
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *AppError) WrappedErrors() []error {
|
||||
return []error{e.Err}
|
||||
}
|
||||
```
|
||||
|
||||
Now this works:
|
||||
|
||||
```go
|
||||
err := &AppError{Err: fmt.Errorf("an error")}
|
||||
if errwrap.ContainsType(err, fmt.Errorf("")) {
|
||||
// This will work!
|
||||
}
|
||||
```
|
169
vendor/github.com/hashicorp/errwrap/errwrap.go
generated
vendored
Normal file
169
vendor/github.com/hashicorp/errwrap/errwrap.go
generated
vendored
Normal file
|
@ -0,0 +1,169 @@
|
|||
// Package errwrap implements methods to formalize error wrapping in Go.
|
||||
//
|
||||
// All of the top-level functions that take an `error` are built to be able
|
||||
// to take any error, not just wrapped errors. This allows you to use errwrap
|
||||
// without having to type-check and type-cast everywhere.
|
||||
package errwrap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// WalkFunc is the callback called for Walk.
|
||||
type WalkFunc func(error)
|
||||
|
||||
// Wrapper is an interface that can be implemented by custom types to
|
||||
// have all the Contains, Get, etc. functions in errwrap work.
|
||||
//
|
||||
// When Walk reaches a Wrapper, it will call the callback for every
|
||||
// wrapped error in addition to the wrapper itself. Since all the top-level
|
||||
// functions in errwrap use Walk, this means that all those functions work
|
||||
// with your custom type.
|
||||
type Wrapper interface {
|
||||
WrappedErrors() []error
|
||||
}
|
||||
|
||||
// Wrap defines that outer wraps inner, returning an error type that
|
||||
// can be cleanly used with the other methods in this package, such as
|
||||
// Contains, GetAll, etc.
|
||||
//
|
||||
// This function won't modify the error message at all (the outer message
|
||||
// will be used).
|
||||
func Wrap(outer, inner error) error {
|
||||
return &wrappedError{
|
||||
Outer: outer,
|
||||
Inner: inner,
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapf wraps an error with a formatting message. This is similar to using
|
||||
// `fmt.Errorf` to wrap an error. If you're using `fmt.Errorf` to wrap
|
||||
// errors, you should replace it with this.
|
||||
//
|
||||
// format is the format of the error message. The string '{{err}}' will
|
||||
// be replaced with the original error message.
|
||||
func Wrapf(format string, err error) error {
|
||||
outerMsg := "<nil>"
|
||||
if err != nil {
|
||||
outerMsg = err.Error()
|
||||
}
|
||||
|
||||
outer := errors.New(strings.Replace(
|
||||
format, "{{err}}", outerMsg, -1))
|
||||
|
||||
return Wrap(outer, err)
|
||||
}
|
||||
|
||||
// Contains checks if the given error contains an error with the
|
||||
// message msg. If err is not a wrapped error, this will always return
|
||||
// false unless the error itself happens to match this msg.
|
||||
func Contains(err error, msg string) bool {
|
||||
return len(GetAll(err, msg)) > 0
|
||||
}
|
||||
|
||||
// ContainsType checks if the given error contains an error with
|
||||
// the same concrete type as v. If err is not a wrapped error, this will
|
||||
// check the err itself.
|
||||
func ContainsType(err error, v interface{}) bool {
|
||||
return len(GetAllType(err, v)) > 0
|
||||
}
|
||||
|
||||
// Get is the same as GetAll but returns the deepest matching error.
|
||||
func Get(err error, msg string) error {
|
||||
es := GetAll(err, msg)
|
||||
if len(es) > 0 {
|
||||
return es[len(es)-1]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetType is the same as GetAllType but returns the deepest matching error.
|
||||
func GetType(err error, v interface{}) error {
|
||||
es := GetAllType(err, v)
|
||||
if len(es) > 0 {
|
||||
return es[len(es)-1]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAll gets all the errors that might be wrapped in err with the
|
||||
// given message. The order of the errors is such that the outermost
|
||||
// matching error (the most recent wrap) is index zero, and so on.
|
||||
func GetAll(err error, msg string) []error {
|
||||
var result []error
|
||||
|
||||
Walk(err, func(err error) {
|
||||
if err.Error() == msg {
|
||||
result = append(result, err)
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// GetAllType gets all the errors that are the same type as v.
|
||||
//
|
||||
// The order of the return value is the same as described in GetAll.
|
||||
func GetAllType(err error, v interface{}) []error {
|
||||
var result []error
|
||||
|
||||
var search string
|
||||
if v != nil {
|
||||
search = reflect.TypeOf(v).String()
|
||||
}
|
||||
Walk(err, func(err error) {
|
||||
var needle string
|
||||
if err != nil {
|
||||
needle = reflect.TypeOf(err).String()
|
||||
}
|
||||
|
||||
if needle == search {
|
||||
result = append(result, err)
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Walk walks all the wrapped errors in err and calls the callback. If
|
||||
// err isn't a wrapped error, this will be called once for err. If err
|
||||
// is a wrapped error, the callback will be called for both the wrapper
|
||||
// that implements error as well as the wrapped error itself.
|
||||
func Walk(err error, cb WalkFunc) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch e := err.(type) {
|
||||
case *wrappedError:
|
||||
cb(e.Outer)
|
||||
Walk(e.Inner, cb)
|
||||
case Wrapper:
|
||||
cb(err)
|
||||
|
||||
for _, err := range e.WrappedErrors() {
|
||||
Walk(err, cb)
|
||||
}
|
||||
default:
|
||||
cb(err)
|
||||
}
|
||||
}
|
||||
|
||||
// wrappedError is an implementation of error that has both the
|
||||
// outer and inner errors.
|
||||
type wrappedError struct {
|
||||
Outer error
|
||||
Inner error
|
||||
}
|
||||
|
||||
func (w *wrappedError) Error() string {
|
||||
return w.Outer.Error()
|
||||
}
|
||||
|
||||
func (w *wrappedError) WrappedErrors() []error {
|
||||
return []error{w.Outer, w.Inner}
|
||||
}
|
353
vendor/github.com/hashicorp/go-multierror/LICENSE
generated
vendored
Normal file
353
vendor/github.com/hashicorp/go-multierror/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,353 @@
|
|||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
1.1. “Contributor”
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. “Contributor Version”
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor’s Contribution.
|
||||
|
||||
1.3. “Contribution”
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. “Covered Software”
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. “Incompatible With Secondary Licenses”
|
||||
means
|
||||
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
b. that the Covered Software was made available under the terms of version
|
||||
1.1 or earlier of the License, but not also under the terms of a
|
||||
Secondary License.
|
||||
|
||||
1.6. “Executable Form”
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. “Larger Work”
|
||||
|
||||
means a work that combines Covered Software with other material, in a separate
|
||||
file or files, that is not Covered Software.
|
||||
|
||||
1.8. “License”
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. “Licensable”
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether at the
|
||||
time of the initial grant or subsequently, any and all of the rights conveyed by
|
||||
this License.
|
||||
|
||||
1.10. “Modifications”
|
||||
|
||||
means any of the following:
|
||||
|
||||
a. any file in Source Code Form that results from an addition to, deletion
|
||||
from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. “Patent Claims” of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method, process,
|
||||
and apparatus claims, in any patent Licensable by such Contributor that
|
||||
would be infringed, but for the grant of the License, by the making,
|
||||
using, selling, offering for sale, having made, import, or transfer of
|
||||
either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. “Secondary License”
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. “Source Code Form”
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. “You” (or “Your”)
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, “You” includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, “control” means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or as
|
||||
part of a Larger Work; and
|
||||
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its Contributions
|
||||
or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution become
|
||||
effective for each Contribution on the date the Contributor first distributes
|
||||
such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under this
|
||||
License. No additional rights or licenses will be implied from the distribution
|
||||
or licensing of Covered Software under this License. Notwithstanding Section
|
||||
2.1(b) above, no patent license is granted by a Contributor:
|
||||
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
b. for infringements caused by: (i) Your and any other third party’s
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
c. under Patent Claims infringed by Covered Software in the absence of its
|
||||
Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks, or
|
||||
logos of any Contributor (except as may be necessary to comply with the
|
||||
notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this License
|
||||
(see Section 10.2) or under the terms of a Secondary License (if permitted
|
||||
under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its Contributions
|
||||
are its original creation(s) or it has sufficient rights to grant the
|
||||
rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under applicable
|
||||
copyright doctrines of fair use, fair dealing, or other equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under the
|
||||
terms of this License. You must inform recipients that the Source Code Form
|
||||
of the Covered Software is governed by the terms of this License, and how
|
||||
they can obtain a copy of this License. You may not attempt to alter or
|
||||
restrict the recipients’ rights in the Source Code Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
b. You may distribute such Executable Form under the terms of this License,
|
||||
or sublicense it under different terms, provided that the license for
|
||||
the Executable Form does not attempt to limit or alter the recipients’
|
||||
rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for the
|
||||
Covered Software. If the Larger Work is a combination of Covered Software
|
||||
with a work governed by one or more Secondary Licenses, and the Covered
|
||||
Software is not Incompatible With Secondary Licenses, this License permits
|
||||
You to additionally distribute such Covered Software under the terms of
|
||||
such Secondary License(s), so that the recipient of the Larger Work may, at
|
||||
their option, further distribute the Covered Software under the terms of
|
||||
either this License or such Secondary License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices (including
|
||||
copyright notices, patent notices, disclaimers of warranty, or limitations
|
||||
of liability) contained within the Source Code Form of the Covered
|
||||
Software, except that You may alter any license notices to the extent
|
||||
required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on behalf
|
||||
of any Contributor. You must make it absolutely clear that any such
|
||||
warranty, support, indemnity, or liability obligation is offered by You
|
||||
alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute, judicial
|
||||
order, or regulation then You must: (a) comply with the terms of this License
|
||||
to the maximum extent possible; and (b) describe the limitations and the code
|
||||
they affect. Such description must be placed in a text file included with all
|
||||
distributions of the Covered Software under this License. Except to the
|
||||
extent prohibited by statute or regulation, such description must be
|
||||
sufficiently detailed for a recipient of ordinary skill to be able to
|
||||
understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing basis,
|
||||
if such Contributor fails to notify You of the non-compliance by some
|
||||
reasonable means prior to 60 days after You have come back into compliance.
|
||||
Moreover, Your grants from a particular Contributor are reinstated on an
|
||||
ongoing basis if such Contributor notifies You of the non-compliance by
|
||||
some reasonable means, this is the first time You have received notice of
|
||||
non-compliance with this License from such Contributor, and You become
|
||||
compliant prior to 30 days after Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions, counter-claims,
|
||||
and cross-claims) alleging that a Contributor Version directly or
|
||||
indirectly infringes any patent, then the rights granted to You by any and
|
||||
all Contributors for the Covered Software under Section 2.1 of this License
|
||||
shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
Covered Software is provided under this License on an “as is” basis, without
|
||||
warranty of any kind, either expressed, implied, or statutory, including,
|
||||
without limitation, warranties that the Covered Software is free of defects,
|
||||
merchantable, fit for a particular purpose or non-infringing. The entire
|
||||
risk as to the quality and performance of the Covered Software is with You.
|
||||
Should any Covered Software prove defective in any respect, You (not any
|
||||
Contributor) assume the cost of any necessary servicing, repair, or
|
||||
correction. This disclaimer of warranty constitutes an essential part of this
|
||||
License. No use of any Covered Software is authorized under this License
|
||||
except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from such
|
||||
party’s negligence to the extent applicable law prohibits such limitation.
|
||||
Some jurisdictions do not allow the exclusion or limitation of incidental or
|
||||
consequential damages, so this exclusion and limitation may not apply to You.
|
||||
|
||||
8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts of
|
||||
a jurisdiction where the defendant maintains its principal place of business
|
||||
and such litigation shall be governed by laws of that jurisdiction, without
|
||||
reference to its conflict-of-law provisions. Nothing in this Section shall
|
||||
prevent a party’s ability to bring cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject matter
|
||||
hereof. If any provision of this License is held to be unenforceable, such
|
||||
provision shall be reformed only to the extent necessary to make it
|
||||
enforceable. Any law or regulation which provides that the language of a
|
||||
contract shall be construed against the drafter shall not be used to construe
|
||||
this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version of
|
||||
the License under which You originally received the Covered Software, or
|
||||
under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a modified
|
||||
version of this License if you rename the license and remove any
|
||||
references to the name of the license steward (except to note that such
|
||||
modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file, then
|
||||
You may include the notice in a location (such as a LICENSE file in a relevant
|
||||
directory) where a recipient would be likely to look for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - “Incompatible With Secondary Licenses” Notice
|
||||
|
||||
This Source Code Form is “Incompatible
|
||||
With Secondary Licenses”, as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
97
vendor/github.com/hashicorp/go-multierror/README.md
generated
vendored
Normal file
97
vendor/github.com/hashicorp/go-multierror/README.md
generated
vendored
Normal file
|
@ -0,0 +1,97 @@
|
|||
# go-multierror
|
||||
|
||||
[![Build Status](http://img.shields.io/travis/hashicorp/go-multierror.svg?style=flat-square)][travis]
|
||||
[![Go Documentation](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)][godocs]
|
||||
|
||||
[travis]: https://travis-ci.org/hashicorp/go-multierror
|
||||
[godocs]: https://godoc.org/github.com/hashicorp/go-multierror
|
||||
|
||||
`go-multierror` is a package for Go that provides a mechanism for
|
||||
representing a list of `error` values as a single `error`.
|
||||
|
||||
This allows a function in Go to return an `error` that might actually
|
||||
be a list of errors. If the caller knows this, they can unwrap the
|
||||
list and access the errors. If the caller doesn't know, the error
|
||||
formats to a nice human-readable format.
|
||||
|
||||
`go-multierror` implements the
|
||||
[errwrap](https://github.com/hashicorp/errwrap) interface so that it can
|
||||
be used with that library, as well.
|
||||
|
||||
## Installation and Docs
|
||||
|
||||
Install using `go get github.com/hashicorp/go-multierror`.
|
||||
|
||||
Full documentation is available at
|
||||
http://godoc.org/github.com/hashicorp/go-multierror
|
||||
|
||||
## Usage
|
||||
|
||||
go-multierror is easy to use and purposely built to be unobtrusive in
|
||||
existing Go applications/libraries that may not be aware of it.
|
||||
|
||||
**Building a list of errors**
|
||||
|
||||
The `Append` function is used to create a list of errors. This function
|
||||
behaves a lot like the Go built-in `append` function: it doesn't matter
|
||||
if the first argument is nil, a `multierror.Error`, or any other `error`,
|
||||
the function behaves as you would expect.
|
||||
|
||||
```go
|
||||
var result error
|
||||
|
||||
if err := step1(); err != nil {
|
||||
result = multierror.Append(result, err)
|
||||
}
|
||||
if err := step2(); err != nil {
|
||||
result = multierror.Append(result, err)
|
||||
}
|
||||
|
||||
return result
|
||||
```
|
||||
|
||||
**Customizing the formatting of the errors**
|
||||
|
||||
By specifying a custom `ErrorFormat`, you can customize the format
|
||||
of the `Error() string` function:
|
||||
|
||||
```go
|
||||
var result *multierror.Error
|
||||
|
||||
// ... accumulate errors here, maybe using Append
|
||||
|
||||
if result != nil {
|
||||
result.ErrorFormat = func([]error) string {
|
||||
return "errors!"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Accessing the list of errors**
|
||||
|
||||
`multierror.Error` implements `error` so if the caller doesn't know about
|
||||
multierror, it will work just fine. But if you're aware a multierror might
|
||||
be returned, you can use type switches to access the list of errors:
|
||||
|
||||
```go
|
||||
if err := something(); err != nil {
|
||||
if merr, ok := err.(*multierror.Error); ok {
|
||||
// Use merr.Errors
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Returning a multierror only if there are errors**
|
||||
|
||||
If you build a `multierror.Error`, you can use the `ErrorOrNil` function
|
||||
to return an `error` implementation only if there are errors to return:
|
||||
|
||||
```go
|
||||
var result *multierror.Error
|
||||
|
||||
// ... accumulate errors here
|
||||
|
||||
// Return the `error` only if errors were added to the multierror, otherwise
|
||||
// return nil since there are no errors.
|
||||
return result.ErrorOrNil()
|
||||
```
|
41
vendor/github.com/hashicorp/go-multierror/append.go
generated
vendored
Normal file
41
vendor/github.com/hashicorp/go-multierror/append.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
package multierror
|
||||
|
||||
// Append is a helper function that will append more errors
|
||||
// onto an Error in order to create a larger multi-error.
|
||||
//
|
||||
// If err is not a multierror.Error, then it will be turned into
|
||||
// one. If any of the errs are multierr.Error, they will be flattened
|
||||
// one level into err.
|
||||
func Append(err error, errs ...error) *Error {
|
||||
switch err := err.(type) {
|
||||
case *Error:
|
||||
// Typed nils can reach here, so initialize if we are nil
|
||||
if err == nil {
|
||||
err = new(Error)
|
||||
}
|
||||
|
||||
// Go through each error and flatten
|
||||
for _, e := range errs {
|
||||
switch e := e.(type) {
|
||||
case *Error:
|
||||
if e != nil {
|
||||
err.Errors = append(err.Errors, e.Errors...)
|
||||
}
|
||||
default:
|
||||
if e != nil {
|
||||
err.Errors = append(err.Errors, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
default:
|
||||
newErrs := make([]error, 0, len(errs)+1)
|
||||
if err != nil {
|
||||
newErrs = append(newErrs, err)
|
||||
}
|
||||
newErrs = append(newErrs, errs...)
|
||||
|
||||
return Append(&Error{}, newErrs...)
|
||||
}
|
||||
}
|
26
vendor/github.com/hashicorp/go-multierror/flatten.go
generated
vendored
Normal file
26
vendor/github.com/hashicorp/go-multierror/flatten.go
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
package multierror
|
||||
|
||||
// Flatten flattens the given error, merging any *Errors together into
|
||||
// a single *Error.
|
||||
func Flatten(err error) error {
|
||||
// If it isn't an *Error, just return the error as-is
|
||||
if _, ok := err.(*Error); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
// Otherwise, make the result and flatten away!
|
||||
flatErr := new(Error)
|
||||
flatten(err, flatErr)
|
||||
return flatErr
|
||||
}
|
||||
|
||||
func flatten(err error, flatErr *Error) {
|
||||
switch err := err.(type) {
|
||||
case *Error:
|
||||
for _, e := range err.Errors {
|
||||
flatten(e, flatErr)
|
||||
}
|
||||
default:
|
||||
flatErr.Errors = append(flatErr.Errors, err)
|
||||
}
|
||||
}
|
27
vendor/github.com/hashicorp/go-multierror/format.go
generated
vendored
Normal file
27
vendor/github.com/hashicorp/go-multierror/format.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
package multierror
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ErrorFormatFunc is a function callback that is called by Error to
|
||||
// turn the list of errors into a string.
|
||||
type ErrorFormatFunc func([]error) string
|
||||
|
||||
// ListFormatFunc is a basic formatter that outputs the number of errors
|
||||
// that occurred along with a bullet point list of the errors.
|
||||
func ListFormatFunc(es []error) string {
|
||||
if len(es) == 1 {
|
||||
return fmt.Sprintf("1 error occurred:\n\n* %s", es[0])
|
||||
}
|
||||
|
||||
points := make([]string, len(es))
|
||||
for i, err := range es {
|
||||
points[i] = fmt.Sprintf("* %s", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%d errors occurred:\n\n%s",
|
||||
len(es), strings.Join(points, "\n"))
|
||||
}
|
51
vendor/github.com/hashicorp/go-multierror/multierror.go
generated
vendored
Normal file
51
vendor/github.com/hashicorp/go-multierror/multierror.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
package multierror
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Error is an error type to track multiple errors. This is used to
|
||||
// accumulate errors in cases and return them as a single "error".
|
||||
type Error struct {
|
||||
Errors []error
|
||||
ErrorFormat ErrorFormatFunc
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
fn := e.ErrorFormat
|
||||
if fn == nil {
|
||||
fn = ListFormatFunc
|
||||
}
|
||||
|
||||
return fn(e.Errors)
|
||||
}
|
||||
|
||||
// ErrorOrNil returns an error interface if this Error represents
|
||||
// a list of errors, or returns nil if the list of errors is empty. This
|
||||
// function is useful at the end of accumulation to make sure that the value
|
||||
// returned represents the existence of errors.
|
||||
func (e *Error) ErrorOrNil() error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
if len(e.Errors) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Error) GoString() string {
|
||||
return fmt.Sprintf("*%#v", *e)
|
||||
}
|
||||
|
||||
// WrappedErrors returns the list of errors that this Error is wrapping.
|
||||
// It is an implementation of the errwrap.Wrapper interface so that
|
||||
// multierror.Error can be used with that library.
|
||||
//
|
||||
// This method is not safe to be called concurrently and is no different
|
||||
// than accessing the Errors field directly. It is implemented only to
|
||||
// satisfy the errwrap.Wrapper interface.
|
||||
func (e *Error) WrappedErrors() []error {
|
||||
return e.Errors
|
||||
}
|
37
vendor/github.com/hashicorp/go-multierror/prefix.go
generated
vendored
Normal file
37
vendor/github.com/hashicorp/go-multierror/prefix.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
package multierror
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
)
|
||||
|
||||
// Prefix is a helper function that will prefix some text
|
||||
// to the given error. If the error is a multierror.Error, then
|
||||
// it will be prefixed to each wrapped error.
|
||||
//
|
||||
// This is useful to use when appending multiple multierrors
|
||||
// together in order to give better scoping.
|
||||
func Prefix(err error, prefix string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
format := fmt.Sprintf("%s {{err}}", prefix)
|
||||
switch err := err.(type) {
|
||||
case *Error:
|
||||
// Typed nils can reach here, so initialize if we are nil
|
||||
if err == nil {
|
||||
err = new(Error)
|
||||
}
|
||||
|
||||
// Wrap each of the errors
|
||||
for i, e := range err.Errors {
|
||||
err.Errors[i] = errwrap.Wrapf(format, e)
|
||||
}
|
||||
|
||||
return err
|
||||
default:
|
||||
return errwrap.Wrapf(format, err)
|
||||
}
|
||||
}
|
92
vendor/github.com/opencontainers/runtime-tools/error/error.go
generated
vendored
Normal file
92
vendor/github.com/opencontainers/runtime-tools/error/error.go
generated
vendored
Normal file
|
@ -0,0 +1,92 @@
|
|||
// Package error implements generic tooling for tracking RFC 2119
|
||||
// violations and linking back to the appropriate specification section.
|
||||
package error
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Level represents the RFC 2119 compliance levels
|
||||
type Level int
|
||||
|
||||
const (
|
||||
// MAY-level
|
||||
|
||||
// May represents 'MAY' in RFC 2119.
|
||||
May Level = iota
|
||||
// Optional represents 'OPTIONAL' in RFC 2119.
|
||||
Optional
|
||||
|
||||
// SHOULD-level
|
||||
|
||||
// Should represents 'SHOULD' in RFC 2119.
|
||||
Should
|
||||
// ShouldNot represents 'SHOULD NOT' in RFC 2119.
|
||||
ShouldNot
|
||||
// Recommended represents 'RECOMMENDED' in RFC 2119.
|
||||
Recommended
|
||||
// NotRecommended represents 'NOT RECOMMENDED' in RFC 2119.
|
||||
NotRecommended
|
||||
|
||||
// MUST-level
|
||||
|
||||
// Must represents 'MUST' in RFC 2119
|
||||
Must
|
||||
// MustNot represents 'MUST NOT' in RFC 2119.
|
||||
MustNot
|
||||
// Shall represents 'SHALL' in RFC 2119.
|
||||
Shall
|
||||
// ShallNot represents 'SHALL NOT' in RFC 2119.
|
||||
ShallNot
|
||||
// Required represents 'REQUIRED' in RFC 2119.
|
||||
Required
|
||||
)
|
||||
|
||||
// Error represents an error with compliance level and specification reference.
|
||||
type Error struct {
|
||||
// Level represents the RFC 2119 compliance level.
|
||||
Level Level
|
||||
|
||||
// Reference is a URL for the violated specification requirement.
|
||||
Reference string
|
||||
|
||||
// Err holds additional details about the violation.
|
||||
Err error
|
||||
}
|
||||
|
||||
// ParseLevel takes a string level and returns the RFC 2119 compliance level constant.
|
||||
func ParseLevel(level string) (Level, error) {
|
||||
switch strings.ToUpper(level) {
|
||||
case "MAY":
|
||||
fallthrough
|
||||
case "OPTIONAL":
|
||||
return May, nil
|
||||
case "SHOULD":
|
||||
fallthrough
|
||||
case "SHOULDNOT":
|
||||
fallthrough
|
||||
case "RECOMMENDED":
|
||||
fallthrough
|
||||
case "NOTRECOMMENDED":
|
||||
return Should, nil
|
||||
case "MUST":
|
||||
fallthrough
|
||||
case "MUSTNOT":
|
||||
fallthrough
|
||||
case "SHALL":
|
||||
fallthrough
|
||||
case "SHALLNOT":
|
||||
fallthrough
|
||||
case "REQUIRED":
|
||||
return Must, nil
|
||||
}
|
||||
|
||||
var l Level
|
||||
return l, fmt.Errorf("%q is not a valid compliance level", level)
|
||||
}
|
||||
|
||||
// Error returns the error message with specification reference.
|
||||
func (err *Error) Error() string {
|
||||
return fmt.Sprintf("%s\nRefer to: %s", err.Err.Error(), err.Reference)
|
||||
}
|
153
vendor/github.com/opencontainers/runtime-tools/generate/generate.go
generated
vendored
153
vendor/github.com/opencontainers/runtime-tools/generate/generate.go
generated
vendored
|
@ -744,6 +744,38 @@ func (g *Generator) ClearPreStartHooks() {
|
|||
func (g *Generator) AddPreStartHook(path string, args []string) {
|
||||
g.initSpecHooks()
|
||||
hook := rspec.Hook{Path: path, Args: args}
|
||||
for i, hook := range g.spec.Hooks.Prestart {
|
||||
if hook.Path == path {
|
||||
g.spec.Hooks.Prestart[i] = hook
|
||||
return
|
||||
}
|
||||
}
|
||||
g.spec.Hooks.Prestart = append(g.spec.Hooks.Prestart, hook)
|
||||
}
|
||||
|
||||
// AddPreStartHookEnv adds envs of a prestart hook into g.spec.Hooks.Prestart.
|
||||
func (g *Generator) AddPreStartHookEnv(path string, envs []string) {
|
||||
g.initSpecHooks()
|
||||
for i, hook := range g.spec.Hooks.Prestart {
|
||||
if hook.Path == path {
|
||||
g.spec.Hooks.Prestart[i].Env = envs
|
||||
return
|
||||
}
|
||||
}
|
||||
hook := rspec.Hook{Path: path, Env: envs}
|
||||
g.spec.Hooks.Prestart = append(g.spec.Hooks.Prestart, hook)
|
||||
}
|
||||
|
||||
// AddPreStartHookTimeout adds timeout of a prestart hook into g.spec.Hooks.Prestart.
|
||||
func (g *Generator) AddPreStartHookTimeout(path string, timeout int) {
|
||||
g.initSpecHooks()
|
||||
for i, hook := range g.spec.Hooks.Prestart {
|
||||
if hook.Path == path {
|
||||
g.spec.Hooks.Prestart[i].Timeout = &timeout
|
||||
return
|
||||
}
|
||||
}
|
||||
hook := rspec.Hook{Path: path, Timeout: &timeout}
|
||||
g.spec.Hooks.Prestart = append(g.spec.Hooks.Prestart, hook)
|
||||
}
|
||||
|
||||
|
@ -762,6 +794,38 @@ func (g *Generator) ClearPostStopHooks() {
|
|||
func (g *Generator) AddPostStopHook(path string, args []string) {
|
||||
g.initSpecHooks()
|
||||
hook := rspec.Hook{Path: path, Args: args}
|
||||
for i, hook := range g.spec.Hooks.Poststop {
|
||||
if hook.Path == path {
|
||||
g.spec.Hooks.Poststop[i] = hook
|
||||
return
|
||||
}
|
||||
}
|
||||
g.spec.Hooks.Poststop = append(g.spec.Hooks.Poststop, hook)
|
||||
}
|
||||
|
||||
// AddPostStopHookEnv adds envs of a poststop hook into g.spec.Hooks.Poststop.
|
||||
func (g *Generator) AddPostStopHookEnv(path string, envs []string) {
|
||||
g.initSpecHooks()
|
||||
for i, hook := range g.spec.Hooks.Poststop {
|
||||
if hook.Path == path {
|
||||
g.spec.Hooks.Poststop[i].Env = envs
|
||||
return
|
||||
}
|
||||
}
|
||||
hook := rspec.Hook{Path: path, Env: envs}
|
||||
g.spec.Hooks.Poststop = append(g.spec.Hooks.Poststop, hook)
|
||||
}
|
||||
|
||||
// AddPostStopHookTimeout adds timeout of a poststop hook into g.spec.Hooks.Poststop.
|
||||
func (g *Generator) AddPostStopHookTimeout(path string, timeout int) {
|
||||
g.initSpecHooks()
|
||||
for i, hook := range g.spec.Hooks.Poststop {
|
||||
if hook.Path == path {
|
||||
g.spec.Hooks.Poststop[i].Timeout = &timeout
|
||||
return
|
||||
}
|
||||
}
|
||||
hook := rspec.Hook{Path: path, Timeout: &timeout}
|
||||
g.spec.Hooks.Poststop = append(g.spec.Hooks.Poststop, hook)
|
||||
}
|
||||
|
||||
|
@ -780,6 +844,38 @@ func (g *Generator) ClearPostStartHooks() {
|
|||
func (g *Generator) AddPostStartHook(path string, args []string) {
|
||||
g.initSpecHooks()
|
||||
hook := rspec.Hook{Path: path, Args: args}
|
||||
for i, hook := range g.spec.Hooks.Poststart {
|
||||
if hook.Path == path {
|
||||
g.spec.Hooks.Poststart[i] = hook
|
||||
return
|
||||
}
|
||||
}
|
||||
g.spec.Hooks.Poststart = append(g.spec.Hooks.Poststart, hook)
|
||||
}
|
||||
|
||||
// AddPostStartHookEnv adds envs of a poststart hook into g.spec.Hooks.Poststart.
|
||||
func (g *Generator) AddPostStartHookEnv(path string, envs []string) {
|
||||
g.initSpecHooks()
|
||||
for i, hook := range g.spec.Hooks.Poststart {
|
||||
if hook.Path == path {
|
||||
g.spec.Hooks.Poststart[i].Env = envs
|
||||
return
|
||||
}
|
||||
}
|
||||
hook := rspec.Hook{Path: path, Env: envs}
|
||||
g.spec.Hooks.Poststart = append(g.spec.Hooks.Poststart, hook)
|
||||
}
|
||||
|
||||
// AddPostStartHookTimeout adds timeout of a poststart hook into g.spec.Hooks.Poststart.
|
||||
func (g *Generator) AddPostStartHookTimeout(path string, timeout int) {
|
||||
g.initSpecHooks()
|
||||
for i, hook := range g.spec.Hooks.Poststart {
|
||||
if hook.Path == path {
|
||||
g.spec.Hooks.Poststart[i].Timeout = &timeout
|
||||
return
|
||||
}
|
||||
}
|
||||
hook := rspec.Hook{Path: path, Timeout: &timeout}
|
||||
g.spec.Hooks.Poststart = append(g.spec.Hooks.Poststart, hook)
|
||||
}
|
||||
|
||||
|
@ -860,11 +956,12 @@ func (g *Generator) SetupPrivileged(privileged bool) {
|
|||
}
|
||||
g.initSpecLinux()
|
||||
g.initSpecProcessCapabilities()
|
||||
g.spec.Process.Capabilities.Bounding = finalCapList
|
||||
g.spec.Process.Capabilities.Effective = finalCapList
|
||||
g.spec.Process.Capabilities.Inheritable = finalCapList
|
||||
g.spec.Process.Capabilities.Permitted = finalCapList
|
||||
g.spec.Process.Capabilities.Ambient = finalCapList
|
||||
g.ClearProcessCapabilities()
|
||||
g.spec.Process.Capabilities.Bounding = append(g.spec.Process.Capabilities.Bounding, finalCapList...)
|
||||
g.spec.Process.Capabilities.Effective = append(g.spec.Process.Capabilities.Effective, finalCapList...)
|
||||
g.spec.Process.Capabilities.Inheritable = append(g.spec.Process.Capabilities.Inheritable, finalCapList...)
|
||||
g.spec.Process.Capabilities.Permitted = append(g.spec.Process.Capabilities.Permitted, finalCapList...)
|
||||
g.spec.Process.Capabilities.Ambient = append(g.spec.Process.Capabilities.Ambient, finalCapList...)
|
||||
g.spec.Process.SelinuxLabel = ""
|
||||
g.spec.Process.ApparmorProfile = ""
|
||||
g.spec.Linux.Seccomp = nil
|
||||
|
@ -892,40 +989,60 @@ func (g *Generator) AddProcessCapability(c string) error {
|
|||
|
||||
g.initSpecProcessCapabilities()
|
||||
|
||||
var foundBounding bool
|
||||
for _, cap := range g.spec.Process.Capabilities.Bounding {
|
||||
if strings.ToUpper(cap) == cp {
|
||||
return nil
|
||||
foundBounding = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundBounding {
|
||||
g.spec.Process.Capabilities.Bounding = append(g.spec.Process.Capabilities.Bounding, cp)
|
||||
}
|
||||
|
||||
var foundEffective bool
|
||||
for _, cap := range g.spec.Process.Capabilities.Effective {
|
||||
if strings.ToUpper(cap) == cp {
|
||||
return nil
|
||||
foundEffective = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundEffective {
|
||||
g.spec.Process.Capabilities.Effective = append(g.spec.Process.Capabilities.Effective, cp)
|
||||
}
|
||||
|
||||
var foundInheritable bool
|
||||
for _, cap := range g.spec.Process.Capabilities.Inheritable {
|
||||
if strings.ToUpper(cap) == cp {
|
||||
return nil
|
||||
foundInheritable = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundInheritable {
|
||||
g.spec.Process.Capabilities.Inheritable = append(g.spec.Process.Capabilities.Inheritable, cp)
|
||||
}
|
||||
|
||||
var foundPermitted bool
|
||||
for _, cap := range g.spec.Process.Capabilities.Permitted {
|
||||
if strings.ToUpper(cap) == cp {
|
||||
return nil
|
||||
foundPermitted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundPermitted {
|
||||
g.spec.Process.Capabilities.Permitted = append(g.spec.Process.Capabilities.Permitted, cp)
|
||||
}
|
||||
|
||||
var foundAmbient bool
|
||||
for _, cap := range g.spec.Process.Capabilities.Ambient {
|
||||
if strings.ToUpper(cap) == cp {
|
||||
return nil
|
||||
foundAmbient = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundAmbient {
|
||||
g.spec.Process.Capabilities.Ambient = append(g.spec.Process.Capabilities.Ambient, cp)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -939,33 +1056,39 @@ func (g *Generator) DropProcessCapability(c string) error {
|
|||
|
||||
g.initSpecProcessCapabilities()
|
||||
|
||||
// we don't care about order...and this is way faster...
|
||||
removeFunc := func(s []string, i int) []string {
|
||||
s[i] = s[len(s)-1]
|
||||
return s[:len(s)-1]
|
||||
}
|
||||
|
||||
for i, cap := range g.spec.Process.Capabilities.Bounding {
|
||||
if strings.ToUpper(cap) == cp {
|
||||
g.spec.Process.Capabilities.Bounding = append(g.spec.Process.Capabilities.Bounding[:i], g.spec.Process.Capabilities.Bounding[i+1:]...)
|
||||
g.spec.Process.Capabilities.Bounding = removeFunc(g.spec.Process.Capabilities.Bounding, i)
|
||||
}
|
||||
}
|
||||
|
||||
for i, cap := range g.spec.Process.Capabilities.Effective {
|
||||
if strings.ToUpper(cap) == cp {
|
||||
g.spec.Process.Capabilities.Effective = append(g.spec.Process.Capabilities.Effective[:i], g.spec.Process.Capabilities.Effective[i+1:]...)
|
||||
g.spec.Process.Capabilities.Effective = removeFunc(g.spec.Process.Capabilities.Effective, i)
|
||||
}
|
||||
}
|
||||
|
||||
for i, cap := range g.spec.Process.Capabilities.Inheritable {
|
||||
if strings.ToUpper(cap) == cp {
|
||||
g.spec.Process.Capabilities.Inheritable = append(g.spec.Process.Capabilities.Inheritable[:i], g.spec.Process.Capabilities.Inheritable[i+1:]...)
|
||||
g.spec.Process.Capabilities.Inheritable = removeFunc(g.spec.Process.Capabilities.Inheritable, i)
|
||||
}
|
||||
}
|
||||
|
||||
for i, cap := range g.spec.Process.Capabilities.Permitted {
|
||||
if strings.ToUpper(cap) == cp {
|
||||
g.spec.Process.Capabilities.Permitted = append(g.spec.Process.Capabilities.Permitted[:i], g.spec.Process.Capabilities.Permitted[i+1:]...)
|
||||
g.spec.Process.Capabilities.Permitted = removeFunc(g.spec.Process.Capabilities.Permitted, i)
|
||||
}
|
||||
}
|
||||
|
||||
for i, cap := range g.spec.Process.Capabilities.Ambient {
|
||||
if strings.ToUpper(cap) == cp {
|
||||
g.spec.Process.Capabilities.Ambient = append(g.spec.Process.Capabilities.Ambient[:i], g.spec.Process.Capabilities.Ambient[i+1:]...)
|
||||
g.spec.Process.Capabilities.Ambient = removeFunc(g.spec.Process.Capabilities.Ambient, i)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
170
vendor/github.com/opencontainers/runtime-tools/specerror/error.go
generated
vendored
Normal file
170
vendor/github.com/opencontainers/runtime-tools/specerror/error.go
generated
vendored
Normal file
|
@ -0,0 +1,170 @@
|
|||
// Package specerror implements runtime-spec-specific tooling for
|
||||
// tracking RFC 2119 violations.
|
||||
package specerror
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
rfc2119 "github.com/opencontainers/runtime-tools/error"
|
||||
)
|
||||
|
||||
const referenceTemplate = "https://github.com/opencontainers/runtime-spec/blob/v%s/%s"
|
||||
|
||||
// Code represents the spec violation, enumerating both
|
||||
// configuration violations and runtime violations.
|
||||
type Code int
|
||||
|
||||
const (
|
||||
// NonError represents that an input is not an error
|
||||
NonError Code = iota
|
||||
// NonRFCError represents that an error is not a rfc2119 error
|
||||
NonRFCError
|
||||
|
||||
// ConfigFileExistence represents the error code of 'config.json' existence test
|
||||
ConfigFileExistence
|
||||
// ArtifactsInSingleDir represents the error code of artifacts place test
|
||||
ArtifactsInSingleDir
|
||||
|
||||
// SpecVersion represents the error code of specfication version test
|
||||
SpecVersion
|
||||
|
||||
// RootOnNonHyperV represents the error code of root setting test on non hyper-v containers
|
||||
RootOnNonHyperV
|
||||
// RootOnHyperV represents the error code of root setting test on hyper-v containers
|
||||
RootOnHyperV
|
||||
// PathFormatOnWindows represents the error code of the path format test on Window
|
||||
PathFormatOnWindows
|
||||
// PathName represents the error code of the path name test
|
||||
PathName
|
||||
// PathExistence represents the error code of the path existence test
|
||||
PathExistence
|
||||
// ReadonlyFilesystem represents the error code of readonly test
|
||||
ReadonlyFilesystem
|
||||
// ReadonlyOnWindows represents the error code of readonly setting test on Windows
|
||||
ReadonlyOnWindows
|
||||
|
||||
// DefaultFilesystems represents the error code of default filesystems test
|
||||
DefaultFilesystems
|
||||
|
||||
// CreateWithID represents the error code of 'create' lifecyle test with 'id' provided
|
||||
CreateWithID
|
||||
// CreateWithUniqueID represents the error code of 'create' lifecyle test with unique 'id' provided
|
||||
CreateWithUniqueID
|
||||
// CreateNewContainer represents the error code 'create' lifecyle test that creates new container
|
||||
CreateNewContainer
|
||||
)
|
||||
|
||||
type errorTemplate struct {
|
||||
Level rfc2119.Level
|
||||
Reference func(version string) (reference string, err error)
|
||||
}
|
||||
|
||||
// Error represents a runtime-spec violation.
|
||||
type Error struct {
|
||||
// Err holds the RFC 2119 violation.
|
||||
Err rfc2119.Error
|
||||
|
||||
// Code is a matchable holds a Code
|
||||
Code Code
|
||||
}
|
||||
|
||||
var (
|
||||
containerFormatRef = func(version string) (reference string, err error) {
|
||||
return fmt.Sprintf(referenceTemplate, version, "bundle.md#container-format"), nil
|
||||
}
|
||||
specVersionRef = func(version string) (reference string, err error) {
|
||||
return fmt.Sprintf(referenceTemplate, version, "config.md#specification-version"), nil
|
||||
}
|
||||
rootRef = func(version string) (reference string, err error) {
|
||||
return fmt.Sprintf(referenceTemplate, version, "config.md#root"), nil
|
||||
}
|
||||
defaultFSRef = func(version string) (reference string, err error) {
|
||||
return fmt.Sprintf(referenceTemplate, version, "config-linux.md#default-filesystems"), nil
|
||||
}
|
||||
runtimeCreateRef = func(version string) (reference string, err error) {
|
||||
return fmt.Sprintf(referenceTemplate, version, "runtime.md#create"), nil
|
||||
}
|
||||
)
|
||||
|
||||
var ociErrors = map[Code]errorTemplate{
|
||||
// Bundle.md
|
||||
// Container Format
|
||||
ConfigFileExistence: {Level: rfc2119.Must, Reference: containerFormatRef},
|
||||
ArtifactsInSingleDir: {Level: rfc2119.Must, Reference: containerFormatRef},
|
||||
|
||||
// Config.md
|
||||
// Specification Version
|
||||
SpecVersion: {Level: rfc2119.Must, Reference: specVersionRef},
|
||||
// Root
|
||||
RootOnNonHyperV: {Level: rfc2119.Required, Reference: rootRef},
|
||||
RootOnHyperV: {Level: rfc2119.Must, Reference: rootRef},
|
||||
// TODO: add tests for 'PathFormatOnWindows'
|
||||
PathFormatOnWindows: {Level: rfc2119.Must, Reference: rootRef},
|
||||
PathName: {Level: rfc2119.Should, Reference: rootRef},
|
||||
PathExistence: {Level: rfc2119.Must, Reference: rootRef},
|
||||
ReadonlyFilesystem: {Level: rfc2119.Must, Reference: rootRef},
|
||||
ReadonlyOnWindows: {Level: rfc2119.Must, Reference: rootRef},
|
||||
|
||||
// Config-Linux.md
|
||||
// Default Filesystems
|
||||
DefaultFilesystems: {Level: rfc2119.Should, Reference: defaultFSRef},
|
||||
|
||||
// Runtime.md
|
||||
// Create
|
||||
CreateWithID: {Level: rfc2119.Must, Reference: runtimeCreateRef},
|
||||
CreateWithUniqueID: {Level: rfc2119.Must, Reference: runtimeCreateRef},
|
||||
CreateNewContainer: {Level: rfc2119.Must, Reference: runtimeCreateRef},
|
||||
}
|
||||
|
||||
// Error returns the error message with specification reference.
|
||||
func (err *Error) Error() string {
|
||||
return err.Err.Error()
|
||||
}
|
||||
|
||||
// NewError creates an Error referencing a spec violation. The error
|
||||
// can be cast to an *Error for extracting structured information
|
||||
// about the level of the violation and a reference to the violated
|
||||
// spec condition.
|
||||
//
|
||||
// A version string (for the version of the spec that was violated)
|
||||
// must be set to get a working URL.
|
||||
func NewError(code Code, err error, version string) error {
|
||||
template := ociErrors[code]
|
||||
reference, err2 := template.Reference(version)
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
return &Error{
|
||||
Err: rfc2119.Error{
|
||||
Level: template.Level,
|
||||
Reference: reference,
|
||||
Err: err,
|
||||
},
|
||||
Code: code,
|
||||
}
|
||||
}
|
||||
|
||||
// FindError finds an error from a source error (multiple error) and
|
||||
// returns the error code if found.
|
||||
// If the source error is nil or empty, return NonError.
|
||||
// If the source error is not a multiple error, return NonRFCError.
|
||||
func FindError(err error, code Code) Code {
|
||||
if err == nil {
|
||||
return NonError
|
||||
}
|
||||
|
||||
if merr, ok := err.(*multierror.Error); ok {
|
||||
if merr.ErrorOrNil() == nil {
|
||||
return NonError
|
||||
}
|
||||
for _, e := range merr.Errors {
|
||||
if rfcErr, ok := e.(*Error); ok {
|
||||
if rfcErr.Code == code {
|
||||
return code
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NonRFCError
|
||||
}
|
110
vendor/github.com/opencontainers/runtime-tools/validate/error.go
generated
vendored
110
vendor/github.com/opencontainers/runtime-tools/validate/error.go
generated
vendored
|
@ -1,110 +0,0 @@
|
|||
package validate
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
rspec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
// ComplianceLevel represents the OCI compliance levels
|
||||
type ComplianceLevel int
|
||||
|
||||
const (
|
||||
// MAY-level
|
||||
|
||||
// ComplianceMay represents 'MAY' in RFC2119
|
||||
ComplianceMay ComplianceLevel = iota
|
||||
// ComplianceOptional represents 'OPTIONAL' in RFC2119
|
||||
ComplianceOptional
|
||||
|
||||
// SHOULD-level
|
||||
|
||||
// ComplianceShould represents 'SHOULD' in RFC2119
|
||||
ComplianceShould
|
||||
// ComplianceShouldNot represents 'SHOULD NOT' in RFC2119
|
||||
ComplianceShouldNot
|
||||
// ComplianceRecommended represents 'RECOMMENDED' in RFC2119
|
||||
ComplianceRecommended
|
||||
// ComplianceNotRecommended represents 'NOT RECOMMENDED' in RFC2119
|
||||
ComplianceNotRecommended
|
||||
|
||||
// MUST-level
|
||||
|
||||
// ComplianceMust represents 'MUST' in RFC2119
|
||||
ComplianceMust
|
||||
// ComplianceMustNot represents 'MUST NOT' in RFC2119
|
||||
ComplianceMustNot
|
||||
// ComplianceShall represents 'SHALL' in RFC2119
|
||||
ComplianceShall
|
||||
// ComplianceShallNot represents 'SHALL NOT' in RFC2119
|
||||
ComplianceShallNot
|
||||
// ComplianceRequired represents 'REQUIRED' in RFC2119
|
||||
ComplianceRequired
|
||||
)
|
||||
|
||||
// ErrorCode represents the compliance content
|
||||
type ErrorCode int
|
||||
|
||||
const (
|
||||
// DefaultFilesystems represents the error code of default filesystems test
|
||||
DefaultFilesystems ErrorCode = iota
|
||||
)
|
||||
|
||||
// Error represents an error with compliance level and OCI reference
|
||||
type Error struct {
|
||||
Level ComplianceLevel
|
||||
Reference string
|
||||
Err error
|
||||
}
|
||||
|
||||
const referencePrefix = "https://github.com/opencontainers/runtime-spec/blob"
|
||||
|
||||
var ociErrors = map[ErrorCode]Error{
|
||||
DefaultFilesystems: Error{Level: ComplianceShould, Reference: "config-linux.md#default-filesystems"},
|
||||
}
|
||||
|
||||
// ParseLevel takes a string level and returns the OCI compliance level constant
|
||||
func ParseLevel(level string) (ComplianceLevel, error) {
|
||||
switch strings.ToUpper(level) {
|
||||
case "MAY":
|
||||
fallthrough
|
||||
case "OPTIONAL":
|
||||
return ComplianceMay, nil
|
||||
case "SHOULD":
|
||||
fallthrough
|
||||
case "SHOULDNOT":
|
||||
fallthrough
|
||||
case "RECOMMENDED":
|
||||
fallthrough
|
||||
case "NOTRECOMMENDED":
|
||||
return ComplianceShould, nil
|
||||
case "MUST":
|
||||
fallthrough
|
||||
case "MUSTNOT":
|
||||
fallthrough
|
||||
case "SHALL":
|
||||
fallthrough
|
||||
case "SHALLNOT":
|
||||
fallthrough
|
||||
case "REQUIRED":
|
||||
return ComplianceMust, nil
|
||||
}
|
||||
|
||||
var l ComplianceLevel
|
||||
return l, fmt.Errorf("%q is not a valid compliance level", level)
|
||||
}
|
||||
|
||||
// NewError creates an Error by ErrorCode and message
|
||||
func NewError(code ErrorCode, msg string) error {
|
||||
err := ociErrors[code]
|
||||
err.Err = errors.New(msg)
|
||||
|
||||
return &err
|
||||
}
|
||||
|
||||
// Error returns the error message with OCI reference
|
||||
func (oci *Error) Error() string {
|
||||
return fmt.Sprintf("%s\nRefer to: %s/v%s/%s", oci.Err.Error(), referencePrefix, rspec.Version, oci.Reference)
|
||||
}
|
435
vendor/github.com/opencontainers/runtime-tools/validate/validate.go
generated
vendored
435
vendor/github.com/opencontainers/runtime-tools/validate/validate.go
generated
vendored
|
@ -3,21 +3,27 @@ package validate
|
|||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
rspec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/syndtr/gocapability/capability"
|
||||
|
||||
"github.com/opencontainers/runtime-tools/specerror"
|
||||
)
|
||||
|
||||
const specConfig = "config.json"
|
||||
|
@ -70,7 +76,7 @@ func NewValidatorFromPath(bundlePath string, hostSpecific bool, platform string)
|
|||
platform = runtime.GOOS
|
||||
}
|
||||
if bundlePath == "" {
|
||||
return Validator{}, fmt.Errorf("Bundle path shouldn't be empty")
|
||||
return Validator{}, fmt.Errorf("bundle path shouldn't be empty")
|
||||
}
|
||||
|
||||
if _, err := os.Stat(bundlePath); err != nil {
|
||||
|
@ -80,7 +86,7 @@ func NewValidatorFromPath(bundlePath string, hostSpecific bool, platform string)
|
|||
configPath := filepath.Join(bundlePath, specConfig)
|
||||
content, err := ioutil.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return Validator{}, err
|
||||
return Validator{}, specerror.NewError(specerror.ConfigFileExistence, err, rspec.Version)
|
||||
}
|
||||
if !utf8.Valid(content) {
|
||||
return Validator{}, fmt.Errorf("%q is not encoded in UTF-8", configPath)
|
||||
|
@ -94,37 +100,45 @@ func NewValidatorFromPath(bundlePath string, hostSpecific bool, platform string)
|
|||
}
|
||||
|
||||
// CheckAll checks all parts of runtime bundle
|
||||
func (v *Validator) CheckAll() (msgs []string) {
|
||||
msgs = append(msgs, v.CheckPlatform()...)
|
||||
msgs = append(msgs, v.CheckRoot()...)
|
||||
msgs = append(msgs, v.CheckMandatoryFields()...)
|
||||
msgs = append(msgs, v.CheckSemVer()...)
|
||||
msgs = append(msgs, v.CheckMounts()...)
|
||||
msgs = append(msgs, v.CheckProcess()...)
|
||||
msgs = append(msgs, v.CheckHooks()...)
|
||||
msgs = append(msgs, v.CheckLinux()...)
|
||||
func (v *Validator) CheckAll() (errs error) {
|
||||
errs = multierror.Append(errs, v.CheckPlatform())
|
||||
errs = multierror.Append(errs, v.CheckRoot())
|
||||
errs = multierror.Append(errs, v.CheckMandatoryFields())
|
||||
errs = multierror.Append(errs, v.CheckSemVer())
|
||||
errs = multierror.Append(errs, v.CheckMounts())
|
||||
errs = multierror.Append(errs, v.CheckProcess())
|
||||
errs = multierror.Append(errs, v.CheckHooks())
|
||||
errs = multierror.Append(errs, v.CheckLinux())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CheckRoot checks status of v.spec.Root
|
||||
func (v *Validator) CheckRoot() (msgs []string) {
|
||||
func (v *Validator) CheckRoot() (errs error) {
|
||||
logrus.Debugf("check root")
|
||||
|
||||
if v.platform == "windows" && v.spec.Windows.HyperV != nil {
|
||||
if v.platform == "windows" && v.spec.Windows != nil && v.spec.Windows.HyperV != nil {
|
||||
if v.spec.Root != nil {
|
||||
msgs = append(msgs, fmt.Sprintf("for Hyper-V containers, Root must not be set"))
|
||||
errs = multierror.Append(errs,
|
||||
specerror.NewError(specerror.RootOnHyperV, fmt.Errorf("for Hyper-V containers, Root must not be set"), rspec.Version))
|
||||
return
|
||||
}
|
||||
return
|
||||
} else if v.spec.Root == nil {
|
||||
msgs = append(msgs, fmt.Sprintf("for non-Hyper-V containers, Root must be set"))
|
||||
errs = multierror.Append(errs,
|
||||
specerror.NewError(specerror.RootOnNonHyperV, fmt.Errorf("for non-Hyper-V containers, Root must be set"), rspec.Version))
|
||||
return
|
||||
}
|
||||
|
||||
absBundlePath, err := filepath.Abs(v.bundlePath)
|
||||
if err != nil {
|
||||
msgs = append(msgs, fmt.Sprintf("unable to convert %q to an absolute path", v.bundlePath))
|
||||
errs = multierror.Append(errs, fmt.Errorf("unable to convert %q to an absolute path", v.bundlePath))
|
||||
return
|
||||
}
|
||||
|
||||
if filepath.Base(v.spec.Root.Path) != "rootfs" {
|
||||
errs = multierror.Append(errs,
|
||||
specerror.NewError(specerror.PathName, fmt.Errorf("path name should be the conventional 'rootfs'"), rspec.Version))
|
||||
}
|
||||
|
||||
var rootfsPath string
|
||||
|
@ -137,24 +151,29 @@ func (v *Validator) CheckRoot() (msgs []string) {
|
|||
rootfsPath = filepath.Join(v.bundlePath, v.spec.Root.Path)
|
||||
absRootPath, err = filepath.Abs(rootfsPath)
|
||||
if err != nil {
|
||||
msgs = append(msgs, fmt.Sprintf("unable to convert %q to an absolute path", rootfsPath))
|
||||
errs = multierror.Append(errs, fmt.Errorf("unable to convert %q to an absolute path", rootfsPath))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if fi, err := os.Stat(rootfsPath); err != nil {
|
||||
msgs = append(msgs, fmt.Sprintf("Cannot find the root path %q", rootfsPath))
|
||||
errs = multierror.Append(errs,
|
||||
specerror.NewError(specerror.PathExistence, fmt.Errorf("cannot find the root path %q", rootfsPath), rspec.Version))
|
||||
} else if !fi.IsDir() {
|
||||
msgs = append(msgs, fmt.Sprintf("The root path %q is not a directory.", rootfsPath))
|
||||
errs = multierror.Append(errs,
|
||||
specerror.NewError(specerror.PathExistence, fmt.Errorf("root.path %q is not a directory", rootfsPath), rspec.Version))
|
||||
}
|
||||
|
||||
rootParent := filepath.Dir(absRootPath)
|
||||
if absRootPath == string(filepath.Separator) || rootParent != absBundlePath {
|
||||
msgs = append(msgs, fmt.Sprintf("root.path is %q, but it MUST be a child of %q", v.spec.Root.Path, absBundlePath))
|
||||
errs = multierror.Append(errs,
|
||||
specerror.NewError(specerror.ArtifactsInSingleDir, fmt.Errorf("root.path is %q, but it MUST be a child of %q", v.spec.Root.Path, absBundlePath), rspec.Version))
|
||||
}
|
||||
|
||||
if v.platform == "windows" {
|
||||
if v.spec.Root.Readonly {
|
||||
msgs = append(msgs, "root.readonly field MUST be omitted or false when target platform is windows")
|
||||
errs = multierror.Append(errs,
|
||||
specerror.NewError(specerror.ReadonlyOnWindows, fmt.Errorf("root.readonly field MUST be omitted or false when target platform is windows"), rspec.Version))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,53 +181,54 @@ func (v *Validator) CheckRoot() (msgs []string) {
|
|||
}
|
||||
|
||||
// CheckSemVer checks v.spec.Version
|
||||
func (v *Validator) CheckSemVer() (msgs []string) {
|
||||
func (v *Validator) CheckSemVer() (errs error) {
|
||||
logrus.Debugf("check semver")
|
||||
|
||||
version := v.spec.Version
|
||||
_, err := semver.Parse(version)
|
||||
if err != nil {
|
||||
msgs = append(msgs, fmt.Sprintf("%q is not valid SemVer: %s", version, err.Error()))
|
||||
errs = multierror.Append(errs,
|
||||
specerror.NewError(specerror.SpecVersion, fmt.Errorf("%q is not valid SemVer: %s", version, err.Error()), rspec.Version))
|
||||
}
|
||||
if version != rspec.Version {
|
||||
msgs = append(msgs, fmt.Sprintf("internal error: validate currently only handles version %s, but the supplied configuration targets %s", rspec.Version, version))
|
||||
errs = multierror.Append(errs, fmt.Errorf("validate currently only handles version %s, but the supplied configuration targets %s", rspec.Version, version))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CheckHooks check v.spec.Hooks
|
||||
func (v *Validator) CheckHooks() (msgs []string) {
|
||||
func (v *Validator) CheckHooks() (errs error) {
|
||||
logrus.Debugf("check hooks")
|
||||
|
||||
if v.spec.Hooks != nil {
|
||||
msgs = append(msgs, checkEventHooks("pre-start", v.spec.Hooks.Prestart, v.HostSpecific)...)
|
||||
msgs = append(msgs, checkEventHooks("post-start", v.spec.Hooks.Poststart, v.HostSpecific)...)
|
||||
msgs = append(msgs, checkEventHooks("post-stop", v.spec.Hooks.Poststop, v.HostSpecific)...)
|
||||
errs = multierror.Append(errs, checkEventHooks("pre-start", v.spec.Hooks.Prestart, v.HostSpecific))
|
||||
errs = multierror.Append(errs, checkEventHooks("post-start", v.spec.Hooks.Poststart, v.HostSpecific))
|
||||
errs = multierror.Append(errs, checkEventHooks("post-stop", v.spec.Hooks.Poststop, v.HostSpecific))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func checkEventHooks(hookType string, hooks []rspec.Hook, hostSpecific bool) (msgs []string) {
|
||||
func checkEventHooks(hookType string, hooks []rspec.Hook, hostSpecific bool) (errs error) {
|
||||
for _, hook := range hooks {
|
||||
if !filepath.IsAbs(hook.Path) {
|
||||
msgs = append(msgs, fmt.Sprintf("The %s hook %v: is not absolute path", hookType, hook.Path))
|
||||
errs = multierror.Append(errs, fmt.Errorf("the %s hook %v: is not absolute path", hookType, hook.Path))
|
||||
}
|
||||
|
||||
if hostSpecific {
|
||||
fi, err := os.Stat(hook.Path)
|
||||
if err != nil {
|
||||
msgs = append(msgs, fmt.Sprintf("Cannot find %s hook: %v", hookType, hook.Path))
|
||||
errs = multierror.Append(errs, fmt.Errorf("cannot find %s hook: %v", hookType, hook.Path))
|
||||
}
|
||||
if fi.Mode()&0111 == 0 {
|
||||
msgs = append(msgs, fmt.Sprintf("The %s hook %v: is not executable", hookType, hook.Path))
|
||||
errs = multierror.Append(errs, fmt.Errorf("the %s hook %v: is not executable", hookType, hook.Path))
|
||||
}
|
||||
}
|
||||
|
||||
for _, env := range hook.Env {
|
||||
if !envValid(env) {
|
||||
msgs = append(msgs, fmt.Sprintf("Env %q for hook %v is in the invalid form.", env, hook.Path))
|
||||
errs = multierror.Append(errs, fmt.Errorf("env %q for hook %v is in the invalid form", env, hook.Path))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -217,7 +237,7 @@ func checkEventHooks(hookType string, hooks []rspec.Hook, hostSpecific bool) (ms
|
|||
}
|
||||
|
||||
// CheckProcess checks v.spec.Process
|
||||
func (v *Validator) CheckProcess() (msgs []string) {
|
||||
func (v *Validator) CheckProcess() (errs error) {
|
||||
logrus.Debugf("check process")
|
||||
|
||||
if v.spec.Process == nil {
|
||||
|
@ -226,17 +246,17 @@ func (v *Validator) CheckProcess() (msgs []string) {
|
|||
|
||||
process := v.spec.Process
|
||||
if !filepath.IsAbs(process.Cwd) {
|
||||
msgs = append(msgs, fmt.Sprintf("cwd %q is not an absolute path", process.Cwd))
|
||||
errs = multierror.Append(errs, fmt.Errorf("cwd %q is not an absolute path", process.Cwd))
|
||||
}
|
||||
|
||||
for _, env := range process.Env {
|
||||
if !envValid(env) {
|
||||
msgs = append(msgs, fmt.Sprintf("env %q should be in the form of 'key=value'. The left hand side must consist solely of letters, digits, and underscores '_'.", env))
|
||||
errs = multierror.Append(errs, fmt.Errorf("env %q should be in the form of 'key=value'. The left hand side must consist solely of letters, digits, and underscores '_'", env))
|
||||
}
|
||||
}
|
||||
|
||||
if len(process.Args) == 0 {
|
||||
msgs = append(msgs, fmt.Sprintf("args must not be empty"))
|
||||
errs = multierror.Append(errs, fmt.Errorf("args must not be empty"))
|
||||
} else {
|
||||
if filepath.IsAbs(process.Args[0]) {
|
||||
var rootfsPath string
|
||||
|
@ -250,27 +270,27 @@ func (v *Validator) CheckProcess() (msgs []string) {
|
|||
if os.IsNotExist(err) {
|
||||
logrus.Warnf("executable %q is not available in rootfs currently", process.Args[0])
|
||||
} else if err != nil {
|
||||
msgs = append(msgs, err.Error())
|
||||
errs = multierror.Append(errs, err)
|
||||
} else {
|
||||
m := fileinfo.Mode()
|
||||
if m.IsDir() || m&0111 == 0 {
|
||||
msgs = append(msgs, fmt.Sprintf("arg %q is not executable", process.Args[0]))
|
||||
errs = multierror.Append(errs, fmt.Errorf("arg %q is not executable", process.Args[0]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if v.spec.Process.Capabilities != nil {
|
||||
msgs = append(msgs, v.CheckCapabilities()...)
|
||||
errs = multierror.Append(errs, v.CheckCapabilities())
|
||||
}
|
||||
msgs = append(msgs, v.CheckRlimits()...)
|
||||
errs = multierror.Append(errs, v.CheckRlimits())
|
||||
|
||||
if v.platform == "linux" {
|
||||
if len(process.ApparmorProfile) > 0 {
|
||||
profilePath := filepath.Join(v.bundlePath, v.spec.Root.Path, "/etc/apparmor.d", process.ApparmorProfile)
|
||||
_, err := os.Stat(profilePath)
|
||||
if err != nil {
|
||||
msgs = append(msgs, err.Error())
|
||||
errs = multierror.Append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -279,7 +299,7 @@ func (v *Validator) CheckProcess() (msgs []string) {
|
|||
}
|
||||
|
||||
// CheckCapabilities checks v.spec.Process.Capabilities
|
||||
func (v *Validator) CheckCapabilities() (msgs []string) {
|
||||
func (v *Validator) CheckCapabilities() (errs error) {
|
||||
process := v.spec.Process
|
||||
if v.platform == "linux" {
|
||||
var effective, permitted, inheritable, ambient bool
|
||||
|
@ -303,29 +323,33 @@ func (v *Validator) CheckCapabilities() (msgs []string) {
|
|||
|
||||
for capability, owns := range caps {
|
||||
if err := CapValid(capability, v.HostSpecific); err != nil {
|
||||
msgs = append(msgs, fmt.Sprintf("capability %q is not valid, man capabilities(7)", capability))
|
||||
errs = multierror.Append(errs, fmt.Errorf("capability %q is not valid, man capabilities(7)", capability))
|
||||
}
|
||||
|
||||
effective, permitted, ambient, inheritable = false, false, false, false
|
||||
for _, set := range owns {
|
||||
if set == "effective" {
|
||||
effective = true
|
||||
continue
|
||||
}
|
||||
if set == "inheritable" {
|
||||
inheritable = true
|
||||
continue
|
||||
}
|
||||
if set == "permitted" {
|
||||
permitted = true
|
||||
continue
|
||||
}
|
||||
if set == "ambient" {
|
||||
ambient = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
if effective && !permitted {
|
||||
msgs = append(msgs, fmt.Sprintf("effective capability %q is not allowed, as it's not permitted", capability))
|
||||
errs = multierror.Append(errs, fmt.Errorf("effective capability %q is not allowed, as it's not permitted", capability))
|
||||
}
|
||||
if ambient && !(effective && inheritable) {
|
||||
msgs = append(msgs, fmt.Sprintf("ambient capability %q is not allowed, as it's not permitted and inheribate", capability))
|
||||
errs = multierror.Append(errs, fmt.Errorf("ambient capability %q is not allowed, as it's not permitted and inheribate", capability))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -336,15 +360,15 @@ func (v *Validator) CheckCapabilities() (msgs []string) {
|
|||
}
|
||||
|
||||
// CheckRlimits checks v.spec.Process.Rlimits
|
||||
func (v *Validator) CheckRlimits() (msgs []string) {
|
||||
func (v *Validator) CheckRlimits() (errs error) {
|
||||
process := v.spec.Process
|
||||
for index, rlimit := range process.Rlimits {
|
||||
for i := index + 1; i < len(process.Rlimits); i++ {
|
||||
if process.Rlimits[index].Type == process.Rlimits[i].Type {
|
||||
msgs = append(msgs, fmt.Sprintf("rlimit can not contain the same type %q.", process.Rlimits[index].Type))
|
||||
errs = multierror.Append(errs, fmt.Errorf("rlimit can not contain the same type %q", process.Rlimits[index].Type))
|
||||
}
|
||||
}
|
||||
msgs = append(msgs, v.rlimitValid(rlimit)...)
|
||||
errs = multierror.Append(errs, v.rlimitValid(rlimit))
|
||||
}
|
||||
|
||||
return
|
||||
|
@ -392,24 +416,49 @@ func supportedMountTypes(OS string, hostSpecific bool) (map[string]bool, error)
|
|||
}
|
||||
|
||||
// CheckMounts checks v.spec.Mounts
|
||||
func (v *Validator) CheckMounts() (msgs []string) {
|
||||
func (v *Validator) CheckMounts() (errs error) {
|
||||
logrus.Debugf("check mounts")
|
||||
|
||||
supportedTypes, err := supportedMountTypes(v.platform, v.HostSpecific)
|
||||
if err != nil {
|
||||
msgs = append(msgs, err.Error())
|
||||
errs = multierror.Append(errs, err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, mount := range v.spec.Mounts {
|
||||
if supportedTypes != nil {
|
||||
if !supportedTypes[mount.Type] {
|
||||
msgs = append(msgs, fmt.Sprintf("Unsupported mount type %q", mount.Type))
|
||||
for i, mountA := range v.spec.Mounts {
|
||||
if supportedTypes != nil && !supportedTypes[mountA.Type] {
|
||||
errs = multierror.Append(errs, fmt.Errorf("unsupported mount type %q", mountA.Type))
|
||||
}
|
||||
if v.platform == "windows" {
|
||||
if err := pathValid(v.platform, mountA.Destination); err != nil {
|
||||
errs = multierror.Append(errs, err)
|
||||
}
|
||||
if err := pathValid(v.platform, mountA.Source); err != nil {
|
||||
errs = multierror.Append(errs, err)
|
||||
}
|
||||
} else {
|
||||
if err := pathValid(v.platform, mountA.Destination); err != nil {
|
||||
errs = multierror.Append(errs, err)
|
||||
}
|
||||
}
|
||||
for j, mountB := range v.spec.Mounts {
|
||||
if i == j {
|
||||
continue
|
||||
}
|
||||
// whether B.Desination is nested within A.Destination
|
||||
nested, err := nestedValid(v.platform, mountA.Destination, mountB.Destination)
|
||||
if err != nil {
|
||||
errs = multierror.Append(errs, err)
|
||||
continue
|
||||
}
|
||||
if nested {
|
||||
if v.platform == "windows" && i < j {
|
||||
errs = multierror.Append(errs, fmt.Errorf("on Windows, %v nested within %v is forbidden", mountB.Destination, mountA.Destination))
|
||||
}
|
||||
if i > j {
|
||||
logrus.Warnf("%v will be covered by %v", mountB.Destination, mountA.Destination)
|
||||
}
|
||||
}
|
||||
|
||||
if !filepath.IsAbs(mount.Destination) {
|
||||
msgs = append(msgs, fmt.Sprintf("destination %v is not an absolute path", mount.Destination))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -417,17 +466,17 @@ func (v *Validator) CheckMounts() (msgs []string) {
|
|||
}
|
||||
|
||||
// CheckPlatform checks v.platform
|
||||
func (v *Validator) CheckPlatform() (msgs []string) {
|
||||
func (v *Validator) CheckPlatform() (errs error) {
|
||||
logrus.Debugf("check platform")
|
||||
|
||||
if v.platform != "linux" && v.platform != "solaris" && v.platform != "windows" {
|
||||
msgs = append(msgs, fmt.Sprintf("platform %q is not supported", v.platform))
|
||||
errs = multierror.Append(errs, fmt.Errorf("platform %q is not supported", v.platform))
|
||||
return
|
||||
}
|
||||
|
||||
if v.platform == "windows" {
|
||||
if v.spec.Windows == nil {
|
||||
msgs = append(msgs, "'windows' MUST be set when platform is `windows`")
|
||||
errs = multierror.Append(errs, errors.New("'windows' MUST be set when platform is `windows`"))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -435,14 +484,14 @@ func (v *Validator) CheckPlatform() (msgs []string) {
|
|||
}
|
||||
|
||||
// CheckLinux checks v.spec.Linux
|
||||
func (v *Validator) CheckLinux() (msgs []string) {
|
||||
func (v *Validator) CheckLinux() (errs error) {
|
||||
logrus.Debugf("check linux")
|
||||
|
||||
if v.spec.Linux == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var typeList = map[rspec.LinuxNamespaceType]struct {
|
||||
var nsTypeList = map[rspec.LinuxNamespaceType]struct {
|
||||
num int
|
||||
newExist bool
|
||||
}{
|
||||
|
@ -458,58 +507,142 @@ func (v *Validator) CheckLinux() (msgs []string) {
|
|||
for index := 0; index < len(v.spec.Linux.Namespaces); index++ {
|
||||
ns := v.spec.Linux.Namespaces[index]
|
||||
if !namespaceValid(ns) {
|
||||
msgs = append(msgs, fmt.Sprintf("namespace %v is invalid.", ns))
|
||||
errs = multierror.Append(errs, fmt.Errorf("namespace %v is invalid", ns))
|
||||
}
|
||||
|
||||
tmpItem := typeList[ns.Type]
|
||||
tmpItem := nsTypeList[ns.Type]
|
||||
tmpItem.num = tmpItem.num + 1
|
||||
if tmpItem.num > 1 {
|
||||
msgs = append(msgs, fmt.Sprintf("duplicated namespace %q", ns.Type))
|
||||
errs = multierror.Append(errs, fmt.Errorf("duplicated namespace %q", ns.Type))
|
||||
}
|
||||
|
||||
if len(ns.Path) == 0 {
|
||||
tmpItem.newExist = true
|
||||
}
|
||||
typeList[ns.Type] = tmpItem
|
||||
nsTypeList[ns.Type] = tmpItem
|
||||
}
|
||||
|
||||
if (len(v.spec.Linux.UIDMappings) > 0 || len(v.spec.Linux.GIDMappings) > 0) && !typeList[rspec.UserNamespace].newExist {
|
||||
msgs = append(msgs, "UID/GID mappings requires a new User namespace to be specified as well")
|
||||
if (len(v.spec.Linux.UIDMappings) > 0 || len(v.spec.Linux.GIDMappings) > 0) && !nsTypeList[rspec.UserNamespace].newExist {
|
||||
errs = multierror.Append(errs, errors.New("the UID/GID mappings requires a new User namespace to be specified as well"))
|
||||
} else if len(v.spec.Linux.UIDMappings) > 5 {
|
||||
msgs = append(msgs, "Only 5 UID mappings are allowed (linux kernel restriction).")
|
||||
errs = multierror.Append(errs, errors.New("only 5 UID mappings are allowed (linux kernel restriction)"))
|
||||
} else if len(v.spec.Linux.GIDMappings) > 5 {
|
||||
msgs = append(msgs, "Only 5 GID mappings are allowed (linux kernel restriction).")
|
||||
errs = multierror.Append(errs, errors.New("only 5 GID mappings are allowed (linux kernel restriction)"))
|
||||
}
|
||||
|
||||
for k := range v.spec.Linux.Sysctl {
|
||||
if strings.HasPrefix(k, "net.") && !typeList[rspec.NetworkNamespace].newExist {
|
||||
msgs = append(msgs, fmt.Sprintf("Sysctl %v requires a new Network namespace to be specified as well", k))
|
||||
if strings.HasPrefix(k, "net.") && !nsTypeList[rspec.NetworkNamespace].newExist {
|
||||
errs = multierror.Append(errs, fmt.Errorf("sysctl %v requires a new Network namespace to be specified as well", k))
|
||||
}
|
||||
if strings.HasPrefix(k, "fs.mqueue.") {
|
||||
if !typeList[rspec.MountNamespace].newExist || !typeList[rspec.IPCNamespace].newExist {
|
||||
msgs = append(msgs, fmt.Sprintf("Sysctl %v requires a new IPC namespace and Mount namespace to be specified as well", k))
|
||||
if !nsTypeList[rspec.MountNamespace].newExist || !nsTypeList[rspec.IPCNamespace].newExist {
|
||||
errs = multierror.Append(errs, fmt.Errorf("sysctl %v requires a new IPC namespace and Mount namespace to be specified as well", k))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if v.platform == "linux" && !typeList[rspec.UTSNamespace].newExist && v.spec.Hostname != "" {
|
||||
msgs = append(msgs, fmt.Sprintf("On Linux, hostname requires a new UTS namespace to be specified as well"))
|
||||
if v.platform == "linux" && !nsTypeList[rspec.UTSNamespace].newExist && v.spec.Hostname != "" {
|
||||
errs = multierror.Append(errs, fmt.Errorf("on Linux, hostname requires a new UTS namespace to be specified as well"))
|
||||
}
|
||||
|
||||
// Linux devices validation
|
||||
devList := make(map[string]bool)
|
||||
devTypeList := make(map[string]bool)
|
||||
for index := 0; index < len(v.spec.Linux.Devices); index++ {
|
||||
if !deviceValid(v.spec.Linux.Devices[index]) {
|
||||
msgs = append(msgs, fmt.Sprintf("device %v is invalid.", v.spec.Linux.Devices[index]))
|
||||
device := v.spec.Linux.Devices[index]
|
||||
if !deviceValid(device) {
|
||||
errs = multierror.Append(errs, fmt.Errorf("device %v is invalid", device))
|
||||
}
|
||||
|
||||
if _, exists := devList[device.Path]; exists {
|
||||
errs = multierror.Append(errs, fmt.Errorf("device %s is duplicated", device.Path))
|
||||
} else {
|
||||
var rootfsPath string
|
||||
if filepath.IsAbs(v.spec.Root.Path) {
|
||||
rootfsPath = v.spec.Root.Path
|
||||
} else {
|
||||
rootfsPath = filepath.Join(v.bundlePath, v.spec.Root.Path)
|
||||
}
|
||||
absPath := filepath.Join(rootfsPath, device.Path)
|
||||
fi, err := os.Stat(absPath)
|
||||
if os.IsNotExist(err) {
|
||||
devList[device.Path] = true
|
||||
} else if err != nil {
|
||||
errs = multierror.Append(errs, err)
|
||||
} else {
|
||||
fStat, ok := fi.Sys().(*syscall.Stat_t)
|
||||
if !ok {
|
||||
errs = multierror.Append(errs, fmt.Errorf("cannot determine state for device %s", device.Path))
|
||||
continue
|
||||
}
|
||||
var devType string
|
||||
switch fStat.Mode & syscall.S_IFMT {
|
||||
case syscall.S_IFCHR:
|
||||
devType = "c"
|
||||
case syscall.S_IFBLK:
|
||||
devType = "b"
|
||||
case syscall.S_IFIFO:
|
||||
devType = "p"
|
||||
default:
|
||||
devType = "unmatched"
|
||||
}
|
||||
if devType != device.Type || (devType == "c" && device.Type == "u") {
|
||||
errs = multierror.Append(errs, fmt.Errorf("unmatched %s already exists in filesystem", device.Path))
|
||||
continue
|
||||
}
|
||||
if devType != "p" {
|
||||
dev := fStat.Rdev
|
||||
major := (dev >> 8) & 0xfff
|
||||
minor := (dev & 0xff) | ((dev >> 12) & 0xfff00)
|
||||
if int64(major) != device.Major || int64(minor) != device.Minor {
|
||||
errs = multierror.Append(errs, fmt.Errorf("unmatched %s already exists in filesystem", device.Path))
|
||||
continue
|
||||
}
|
||||
}
|
||||
if device.FileMode != nil {
|
||||
expectedPerm := *device.FileMode & os.ModePerm
|
||||
actualPerm := fi.Mode() & os.ModePerm
|
||||
if expectedPerm != actualPerm {
|
||||
errs = multierror.Append(errs, fmt.Errorf("unmatched %s already exists in filesystem", device.Path))
|
||||
continue
|
||||
}
|
||||
}
|
||||
if device.UID != nil {
|
||||
if *device.UID != fStat.Uid {
|
||||
errs = multierror.Append(errs, fmt.Errorf("unmatched %s already exists in filesystem", device.Path))
|
||||
continue
|
||||
}
|
||||
}
|
||||
if device.GID != nil {
|
||||
if *device.GID != fStat.Gid {
|
||||
errs = multierror.Append(errs, fmt.Errorf("unmatched %s already exists in filesystem", device.Path))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unify u->c when comparing, they are synonyms
|
||||
var devID string
|
||||
if device.Type == "u" {
|
||||
devID = fmt.Sprintf("%s:%d:%d", "c", device.Major, device.Minor)
|
||||
} else {
|
||||
devID = fmt.Sprintf("%s:%d:%d", device.Type, device.Major, device.Minor)
|
||||
}
|
||||
|
||||
if _, exists := devTypeList[devID]; exists {
|
||||
logrus.Warnf("type:%s, major:%d and minor:%d for linux devices is duplicated", device.Type, device.Major, device.Minor)
|
||||
} else {
|
||||
devTypeList[devID] = true
|
||||
}
|
||||
}
|
||||
|
||||
if v.spec.Linux.Resources != nil {
|
||||
ms := v.CheckLinuxResources()
|
||||
msgs = append(msgs, ms...)
|
||||
errs = multierror.Append(errs, v.CheckLinuxResources())
|
||||
}
|
||||
|
||||
if v.spec.Linux.Seccomp != nil {
|
||||
ms := v.CheckSeccomp()
|
||||
msgs = append(msgs, ms...)
|
||||
errs = multierror.Append(errs, v.CheckSeccomp())
|
||||
}
|
||||
|
||||
switch v.spec.Linux.RootfsPropagation {
|
||||
|
@ -523,18 +656,18 @@ func (v *Validator) CheckLinux() (msgs []string) {
|
|||
case "unbindable":
|
||||
case "runbindable":
|
||||
default:
|
||||
msgs = append(msgs, "rootfsPropagation must be empty or one of \"private|rprivate|slave|rslave|shared|rshared|unbindable|runbindable\"")
|
||||
errs = multierror.Append(errs, errors.New("rootfsPropagation must be empty or one of \"private|rprivate|slave|rslave|shared|rshared|unbindable|runbindable\""))
|
||||
}
|
||||
|
||||
for _, maskedPath := range v.spec.Linux.MaskedPaths {
|
||||
if !strings.HasPrefix(maskedPath, "/") {
|
||||
msgs = append(msgs, fmt.Sprintf("maskedPath %v is not an absolute path", maskedPath))
|
||||
errs = multierror.Append(errs, fmt.Errorf("maskedPath %v is not an absolute path", maskedPath))
|
||||
}
|
||||
}
|
||||
|
||||
for _, readonlyPath := range v.spec.Linux.ReadonlyPaths {
|
||||
if !strings.HasPrefix(readonlyPath, "/") {
|
||||
msgs = append(msgs, fmt.Sprintf("readonlyPath %v is not an absolute path", readonlyPath))
|
||||
errs = multierror.Append(errs, fmt.Errorf("readonlyPath %v is not an absolute path", readonlyPath))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -542,23 +675,23 @@ func (v *Validator) CheckLinux() (msgs []string) {
|
|||
}
|
||||
|
||||
// CheckLinuxResources checks v.spec.Linux.Resources
|
||||
func (v *Validator) CheckLinuxResources() (msgs []string) {
|
||||
func (v *Validator) CheckLinuxResources() (errs error) {
|
||||
logrus.Debugf("check linux resources")
|
||||
|
||||
r := v.spec.Linux.Resources
|
||||
if r.Memory != nil {
|
||||
if r.Memory.Limit != nil && r.Memory.Swap != nil && uint64(*r.Memory.Limit) > uint64(*r.Memory.Swap) {
|
||||
msgs = append(msgs, fmt.Sprintf("Minimum memoryswap should be larger than memory limit"))
|
||||
errs = multierror.Append(errs, fmt.Errorf("minimum memoryswap should be larger than memory limit"))
|
||||
}
|
||||
if r.Memory.Limit != nil && r.Memory.Reservation != nil && uint64(*r.Memory.Reservation) > uint64(*r.Memory.Limit) {
|
||||
msgs = append(msgs, fmt.Sprintf("Minimum memory limit should be larger than memory reservation"))
|
||||
errs = multierror.Append(errs, fmt.Errorf("minimum memory limit should be larger than memory reservation"))
|
||||
}
|
||||
}
|
||||
if r.Network != nil && v.HostSpecific {
|
||||
var exist bool
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
msgs = append(msgs, err.Error())
|
||||
errs = multierror.Append(errs, err)
|
||||
return
|
||||
}
|
||||
for _, prio := range r.Network.Priorities {
|
||||
|
@ -570,7 +703,24 @@ func (v *Validator) CheckLinuxResources() (msgs []string) {
|
|||
}
|
||||
}
|
||||
if !exist {
|
||||
msgs = append(msgs, fmt.Sprintf("Interface %s does not exist currently", prio.Name))
|
||||
errs = multierror.Append(errs, fmt.Errorf("interface %s does not exist currently", prio.Name))
|
||||
}
|
||||
}
|
||||
}
|
||||
for index := 0; index < len(r.Devices); index++ {
|
||||
switch r.Devices[index].Type {
|
||||
case "a", "b", "c":
|
||||
default:
|
||||
errs = multierror.Append(errs, fmt.Errorf("type of devices %s is invalid", r.Devices[index].Type))
|
||||
}
|
||||
|
||||
access := []byte(r.Devices[index].Access)
|
||||
for i := 0; i < len(access); i++ {
|
||||
switch access[i] {
|
||||
case 'r', 'w', 'm':
|
||||
default:
|
||||
errs = multierror.Append(errs, fmt.Errorf("access %s is invalid", r.Devices[index].Access))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -579,16 +729,16 @@ func (v *Validator) CheckLinuxResources() (msgs []string) {
|
|||
}
|
||||
|
||||
// CheckSeccomp checkc v.spec.Linux.Seccomp
|
||||
func (v *Validator) CheckSeccomp() (msgs []string) {
|
||||
func (v *Validator) CheckSeccomp() (errs error) {
|
||||
logrus.Debugf("check linux seccomp")
|
||||
|
||||
s := v.spec.Linux.Seccomp
|
||||
if !seccompActionValid(s.DefaultAction) {
|
||||
msgs = append(msgs, fmt.Sprintf("seccomp defaultAction %q is invalid.", s.DefaultAction))
|
||||
errs = multierror.Append(errs, fmt.Errorf("seccomp defaultAction %q is invalid", s.DefaultAction))
|
||||
}
|
||||
for index := 0; index < len(s.Syscalls); index++ {
|
||||
if !syscallValid(s.Syscalls[index]) {
|
||||
msgs = append(msgs, fmt.Sprintf("syscall %v is invalid.", s.Syscalls[index]))
|
||||
errs = multierror.Append(errs, fmt.Errorf("syscall %v is invalid", s.Syscalls[index]))
|
||||
}
|
||||
}
|
||||
for index := 0; index < len(s.Architectures); index++ {
|
||||
|
@ -612,7 +762,7 @@ func (v *Validator) CheckSeccomp() (msgs []string) {
|
|||
case rspec.ArchPARISC:
|
||||
case rspec.ArchPARISC64:
|
||||
default:
|
||||
msgs = append(msgs, fmt.Sprintf("seccomp architecture %q is invalid", s.Architectures[index]))
|
||||
errs = multierror.Append(errs, fmt.Errorf("seccomp architecture %q is invalid", s.Architectures[index]))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -629,7 +779,7 @@ func CapValid(c string, hostSpecific bool) error {
|
|||
for _, cap := range capability.List() {
|
||||
if c == fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String())) {
|
||||
if hostSpecific && cap > LastCap() {
|
||||
return fmt.Errorf("CAP_%s is not supported on the current host", c)
|
||||
return fmt.Errorf("%s is not supported on the current host", c)
|
||||
}
|
||||
isValid = true
|
||||
break
|
||||
|
@ -637,7 +787,7 @@ func CapValid(c string, hostSpecific bool) error {
|
|||
}
|
||||
|
||||
if !isValid {
|
||||
return fmt.Errorf("Invalid capability: %s", c)
|
||||
return fmt.Errorf("invalid capability: %s", c)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -669,9 +819,9 @@ func envValid(env string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (v *Validator) rlimitValid(rlimit rspec.POSIXRlimit) (msgs []string) {
|
||||
func (v *Validator) rlimitValid(rlimit rspec.POSIXRlimit) (errs error) {
|
||||
if rlimit.Hard < rlimit.Soft {
|
||||
msgs = append(msgs, fmt.Sprintf("hard limit of rlimit %s should not be less than soft limit", rlimit.Type))
|
||||
errs = multierror.Append(errs, fmt.Errorf("hard limit of rlimit %s should not be less than soft limit", rlimit.Type))
|
||||
}
|
||||
|
||||
if v.platform == "linux" {
|
||||
|
@ -680,7 +830,7 @@ func (v *Validator) rlimitValid(rlimit rspec.POSIXRlimit) (msgs []string) {
|
|||
return
|
||||
}
|
||||
}
|
||||
msgs = append(msgs, fmt.Sprintf("rlimit type %q is invalid", rlimit.Type))
|
||||
errs = multierror.Append(errs, fmt.Errorf("rlimit type %q is invalid", rlimit.Type))
|
||||
} else {
|
||||
logrus.Warnf("process.rlimits validation not yet implemented for platform %q", v.platform)
|
||||
}
|
||||
|
@ -708,6 +858,65 @@ func namespaceValid(ns rspec.LinuxNamespace) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func pathValid(os, path string) error {
|
||||
if os == "windows" {
|
||||
matched, err := regexp.MatchString("^[a-zA-Z]:(\\\\[^\\\\/<>|:*?\"]+)+$", path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !matched {
|
||||
return fmt.Errorf("invalid windows path %v", path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if !filepath.IsAbs(path) {
|
||||
return fmt.Errorf("%v is not an absolute path", path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check whether pathB is nested whithin pathA
|
||||
func nestedValid(os, pathA, pathB string) (bool, error) {
|
||||
if pathA == pathB {
|
||||
return false, nil
|
||||
}
|
||||
if pathA == "/" && pathB != "" {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
var sep string
|
||||
if os == "windows" {
|
||||
sep = "\\"
|
||||
} else {
|
||||
sep = "/"
|
||||
}
|
||||
|
||||
splitedPathA := strings.Split(filepath.Clean(pathA), sep)
|
||||
splitedPathB := strings.Split(filepath.Clean(pathB), sep)
|
||||
lenA := len(splitedPathA)
|
||||
lenB := len(splitedPathB)
|
||||
|
||||
if lenA > lenB {
|
||||
if (lenA - lenB) == 1 {
|
||||
// if pathA is longer but not end with separator
|
||||
if splitedPathA[lenA-1] != "" {
|
||||
return false, nil
|
||||
}
|
||||
splitedPathA = splitedPathA[:lenA-1]
|
||||
} else {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
for i, partA := range splitedPathA {
|
||||
if partA != splitedPathB[i] {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func deviceValid(d rspec.LinuxDevice) bool {
|
||||
switch d.Type {
|
||||
case "b", "c", "u":
|
||||
|
@ -767,38 +976,38 @@ func isStructPtr(t reflect.Type) bool {
|
|||
return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
|
||||
}
|
||||
|
||||
func checkMandatoryUnit(field reflect.Value, tagField reflect.StructField, parent string) (msgs []string) {
|
||||
func checkMandatoryUnit(field reflect.Value, tagField reflect.StructField, parent string) (errs error) {
|
||||
mandatory := !strings.Contains(tagField.Tag.Get("json"), "omitempty")
|
||||
switch field.Kind() {
|
||||
case reflect.Ptr:
|
||||
if mandatory && field.IsNil() {
|
||||
msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty.", parent, tagField.Name))
|
||||
errs = multierror.Append(errs, fmt.Errorf("'%s.%s' should not be empty", parent, tagField.Name))
|
||||
}
|
||||
case reflect.String:
|
||||
if mandatory && (field.Len() == 0) {
|
||||
msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty.", parent, tagField.Name))
|
||||
errs = multierror.Append(errs, fmt.Errorf("'%s.%s' should not be empty", parent, tagField.Name))
|
||||
}
|
||||
case reflect.Slice:
|
||||
if mandatory && (field.IsNil() || field.Len() == 0) {
|
||||
msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty.", parent, tagField.Name))
|
||||
errs = multierror.Append(errs, fmt.Errorf("'%s.%s' should not be empty", parent, tagField.Name))
|
||||
return
|
||||
}
|
||||
for index := 0; index < field.Len(); index++ {
|
||||
mValue := field.Index(index)
|
||||
if mValue.CanInterface() {
|
||||
msgs = append(msgs, checkMandatory(mValue.Interface())...)
|
||||
errs = multierror.Append(errs, checkMandatory(mValue.Interface()))
|
||||
}
|
||||
}
|
||||
case reflect.Map:
|
||||
if mandatory && (field.IsNil() || field.Len() == 0) {
|
||||
msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty.", parent, tagField.Name))
|
||||
return msgs
|
||||
errs = multierror.Append(errs, fmt.Errorf("'%s.%s' should not be empty", parent, tagField.Name))
|
||||
return
|
||||
}
|
||||
keys := field.MapKeys()
|
||||
for index := 0; index < len(keys); index++ {
|
||||
mValue := field.MapIndex(keys[index])
|
||||
if mValue.CanInterface() {
|
||||
msgs = append(msgs, checkMandatory(mValue.Interface())...)
|
||||
errs = multierror.Append(errs, checkMandatory(mValue.Interface()))
|
||||
}
|
||||
}
|
||||
default:
|
||||
|
@ -807,7 +1016,7 @@ func checkMandatoryUnit(field reflect.Value, tagField reflect.StructField, paren
|
|||
return
|
||||
}
|
||||
|
||||
func checkMandatory(obj interface{}) (msgs []string) {
|
||||
func checkMandatory(obj interface{}) (errs error) {
|
||||
objT := reflect.TypeOf(obj)
|
||||
objV := reflect.ValueOf(obj)
|
||||
if isStructPtr(objT) {
|
||||
|
@ -821,12 +1030,12 @@ func checkMandatory(obj interface{}) (msgs []string) {
|
|||
t := objT.Field(i).Type
|
||||
if isStructPtr(t) && objV.Field(i).IsNil() {
|
||||
if !strings.Contains(objT.Field(i).Tag.Get("json"), "omitempty") {
|
||||
msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty", objT.Name(), objT.Field(i).Name))
|
||||
errs = multierror.Append(errs, fmt.Errorf("'%s.%s' should not be empty", objT.Name(), objT.Field(i).Name))
|
||||
}
|
||||
} else if (isStruct(t) || isStructPtr(t)) && objV.Field(i).CanInterface() {
|
||||
msgs = append(msgs, checkMandatory(objV.Field(i).Interface())...)
|
||||
errs = multierror.Append(errs, checkMandatory(objV.Field(i).Interface()))
|
||||
} else {
|
||||
msgs = append(msgs, checkMandatoryUnit(objV.Field(i), objT.Field(i), objT.Name())...)
|
||||
errs = multierror.Append(errs, checkMandatoryUnit(objV.Field(i), objT.Field(i), objT.Name()))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -834,7 +1043,7 @@ func checkMandatory(obj interface{}) (msgs []string) {
|
|||
}
|
||||
|
||||
// CheckMandatoryFields checks mandatory field of container's config file
|
||||
func (v *Validator) CheckMandatoryFields() []string {
|
||||
func (v *Validator) CheckMandatoryFields() error {
|
||||
logrus.Debugf("check mandatory fields")
|
||||
|
||||
return checkMandatory(v.spec)
|
||||
|
|
Loading…
Reference in a new issue