mirror of
https://github.com/containers/podman.git
synced 2025-09-25 15:55:32 +08:00
Use buildah commit and bud in podman
Vendor in buildah and use as much of commit and bug as possible for podman build and commit. Resolves #586 Signed-off-by: baude <bbaude@redhat.com> Closes: #681 Approved by: mheon
This commit is contained in:
192
vendor/github.com/openshift/imagebuilder/LICENSE
generated
vendored
Normal file
192
vendor/github.com/openshift/imagebuilder/LICENSE
generated
vendored
Normal file
@ -0,0 +1,192 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
https://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2013-2016 Docker, Inc.
|
||||
Copyright 2016 The OpenShift Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
104
vendor/github.com/openshift/imagebuilder/README.md
generated
vendored
Normal file
104
vendor/github.com/openshift/imagebuilder/README.md
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
Docker / OCI Image Builder
|
||||
==========================
|
||||
|
||||
[](https://goreportcard.com/report/github.com/openshift/imagebuilder)
|
||||
[](https://godoc.org/github.com/openshift/imagebuilder)
|
||||
[](https://travis-ci.org/openshift/imagebuilder)
|
||||
[](http://webchat.freenode.net/?channels=%23openshift-dev)
|
||||
|
||||
Note: this library is beta and may contain bugs that prevent images from being identical to Docker build. Test your images (and add to our conformance suite)!
|
||||
|
||||
This library supports using the Dockerfile syntax to build Docker
|
||||
compatible images, without invoking Docker build. It is intended to give
|
||||
clients more control over how a Docker build is run, including:
|
||||
|
||||
* Instead of building one layer per line, run all instructions in the
|
||||
same container
|
||||
* Set Docker HostConfig settings like network and memory controls that
|
||||
are not available when running Docker builds
|
||||
* Mount external files into the build that are not persisted as part of
|
||||
the final image (i.e. "secrets")
|
||||
* If there are no RUN commands in the Dockerfile, the container is created
|
||||
and committed, but never started.
|
||||
|
||||
The final image should be 99.9% compatible with regular docker builds,
|
||||
but bugs are always possible.
|
||||
|
||||
Future goals include:
|
||||
|
||||
* Output OCI compatible images
|
||||
* Support other container execution engines, like runc or rkt
|
||||
* Better conformance testing
|
||||
* Windows support
|
||||
|
||||
## Install and Run
|
||||
|
||||
To download and install the library and the binary, set up a Golang build environment and with `GOPATH` set run:
|
||||
|
||||
```
|
||||
$ go get -u github.com/openshift/imagebuilder/cmd/imagebuilder
|
||||
```
|
||||
|
||||
The included command line takes one argument, a path to a directory containing a Dockerfile. The `-t` option
|
||||
can be used to specify an image to tag as:
|
||||
|
||||
```
|
||||
$ imagebuilder [-t TAG] DIRECTORY
|
||||
```
|
||||
|
||||
To mount a file into the image for build that will not be present in the final output image, run:
|
||||
|
||||
```
|
||||
$ imagebuilder --mount ~/secrets/private.key:/etc/keys/private.key path/to/my/code testimage
|
||||
```
|
||||
|
||||
Any processes in the Dockerfile will have access to `/etc/keys/private.key`, but that file will not be part of the committed image.
|
||||
|
||||
Running `--mount` requires Docker 1.10 or newer, as it uses a Docker volume to hold the mounted files and the volume API was not
|
||||
available in earlier versions.
|
||||
|
||||
You can also customize which Dockerfile is run, or run multiple Dockerfiles in sequence (the FROM is ignored on
|
||||
later files):
|
||||
|
||||
```
|
||||
$ imagebuilder -f Dockerfile:Dockerfile.extra .
|
||||
```
|
||||
|
||||
will build the current directory and combine the first Dockerfile with the second. The FROM in the second image
|
||||
is ignored.
|
||||
|
||||
|
||||
## Code Example
|
||||
|
||||
```
|
||||
f, err := os.Open("path/to/Dockerfile")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
e := builder.NewClientExecutor(o.Client)
|
||||
e.Out, e.ErrOut = os.Stdout, os.Stderr
|
||||
e.AllowPull = true
|
||||
e.Directory = "context/directory"
|
||||
e.Tag = "name/of-image:and-tag"
|
||||
e.AuthFn = nil // ... pass a function to retrieve authorization info
|
||||
e.LogFn = func(format string, args ...interface{}) {
|
||||
fmt.Fprintf(e.ErrOut, "--> %s\n", fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
buildErr := e.Build(f, map[string]string{"arg1":"value1"})
|
||||
if err := e.Cleanup(); err != nil {
|
||||
fmt.Fprintf(e.ErrOut, "error: Unable to clean up build: %v\n", err)
|
||||
}
|
||||
|
||||
return buildErr
|
||||
```
|
||||
|
||||
Example of usage from OpenShift's experimental `dockerbuild` [command with mount secrets](https://github.com/openshift/origin/blob/26c9e032ff42f613fe10649cd7c5fa1b4c33501b/pkg/cmd/cli/cmd/dockerbuild/dockerbuild.go)
|
||||
|
||||
## Run conformance tests (very slow):
|
||||
|
||||
```
|
||||
go test ./dockerclient/conformance_test.go -tags conformance
|
||||
```
|
510
vendor/github.com/openshift/imagebuilder/builder.go
generated
vendored
Normal file
510
vendor/github.com/openshift/imagebuilder/builder.go
generated
vendored
Normal file
@ -0,0 +1,510 @@
|
||||
package imagebuilder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
docker "github.com/fsouza/go-dockerclient"
|
||||
|
||||
"github.com/docker/docker/builder/dockerfile/command"
|
||||
"github.com/docker/docker/builder/dockerfile/parser"
|
||||
)
|
||||
|
||||
// Copy defines a copy operation required on the container.
|
||||
type Copy struct {
|
||||
// If true, this is a copy from the file system to the container. If false,
|
||||
// the copy is from the context.
|
||||
FromFS bool
|
||||
// If set, this is a copy from the named stage or image to the container.
|
||||
From string
|
||||
Src []string
|
||||
Dest string
|
||||
Download bool
|
||||
}
|
||||
|
||||
// Run defines a run operation required in the container.
|
||||
type Run struct {
|
||||
Shell bool
|
||||
Args []string
|
||||
}
|
||||
|
||||
type Executor interface {
|
||||
Preserve(path string) error
|
||||
Copy(excludes []string, copies ...Copy) error
|
||||
Run(run Run, config docker.Config) error
|
||||
UnrecognizedInstruction(step *Step) error
|
||||
}
|
||||
|
||||
type logExecutor struct{}
|
||||
|
||||
func (logExecutor) Preserve(path string) error {
|
||||
log.Printf("PRESERVE %s", path)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (logExecutor) Copy(excludes []string, copies ...Copy) error {
|
||||
for _, c := range copies {
|
||||
log.Printf("COPY %v -> %s (from:%s download:%t)", c.Src, c.Dest, c.From, c.Download)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (logExecutor) Run(run Run, config docker.Config) error {
|
||||
log.Printf("RUN %v %t (%v)", run.Args, run.Shell, config.Env)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (logExecutor) UnrecognizedInstruction(step *Step) error {
|
||||
log.Printf("Unknown instruction: %s", strings.ToUpper(step.Command))
|
||||
return nil
|
||||
}
|
||||
|
||||
type noopExecutor struct{}
|
||||
|
||||
func (noopExecutor) Preserve(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (noopExecutor) Copy(excludes []string, copies ...Copy) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (noopExecutor) Run(run Run, config docker.Config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (noopExecutor) UnrecognizedInstruction(step *Step) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type VolumeSet []string
|
||||
|
||||
func (s *VolumeSet) Add(path string) bool {
|
||||
if path == "/" {
|
||||
set := len(*s) != 1 || (*s)[0] != ""
|
||||
*s = []string{""}
|
||||
return set
|
||||
}
|
||||
path = strings.TrimSuffix(path, "/")
|
||||
var adjusted []string
|
||||
for _, p := range *s {
|
||||
if p == path || strings.HasPrefix(path, p+"/") {
|
||||
return false
|
||||
}
|
||||
if strings.HasPrefix(p, path+"/") {
|
||||
continue
|
||||
}
|
||||
adjusted = append(adjusted, p)
|
||||
}
|
||||
adjusted = append(adjusted, path)
|
||||
*s = adjusted
|
||||
return true
|
||||
}
|
||||
|
||||
func (s VolumeSet) Has(path string) bool {
|
||||
if path == "/" {
|
||||
return len(s) == 1 && s[0] == ""
|
||||
}
|
||||
path = strings.TrimSuffix(path, "/")
|
||||
for _, p := range s {
|
||||
if p == path {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s VolumeSet) Covers(path string) bool {
|
||||
if path == "/" {
|
||||
return len(s) == 1 && s[0] == ""
|
||||
}
|
||||
path = strings.TrimSuffix(path, "/")
|
||||
for _, p := range s {
|
||||
if p == path || strings.HasPrefix(path, p+"/") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var (
|
||||
LogExecutor = logExecutor{}
|
||||
NoopExecutor = noopExecutor{}
|
||||
)
|
||||
|
||||
type Stages []Stage
|
||||
|
||||
func (stages Stages) ByName(name string) (Stage, bool) {
|
||||
for _, stage := range stages {
|
||||
if stage.Name == name {
|
||||
return stage, true
|
||||
}
|
||||
}
|
||||
return Stage{}, false
|
||||
}
|
||||
|
||||
func (stages Stages) ByTarget(target string) (Stages, bool) {
|
||||
if len(target) == 0 {
|
||||
return stages, true
|
||||
}
|
||||
for i, stage := range stages {
|
||||
if stage.Name == target {
|
||||
return stages[i : i+1], true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
type Stage struct {
|
||||
Position int
|
||||
Name string
|
||||
Builder *Builder
|
||||
Node *parser.Node
|
||||
}
|
||||
|
||||
func NewStages(node *parser.Node, b *Builder) Stages {
|
||||
var stages Stages
|
||||
for i, root := range SplitBy(node, command.From) {
|
||||
name, _ := extractNameFromNode(root.Children[0])
|
||||
if len(name) == 0 {
|
||||
name = strconv.Itoa(i)
|
||||
}
|
||||
stages = append(stages, Stage{
|
||||
Position: i,
|
||||
Name: name,
|
||||
Builder: &Builder{
|
||||
Args: b.Args,
|
||||
AllowedArgs: b.AllowedArgs,
|
||||
},
|
||||
Node: root,
|
||||
})
|
||||
}
|
||||
return stages
|
||||
}
|
||||
|
||||
func extractNameFromNode(node *parser.Node) (string, bool) {
|
||||
if node.Value != command.From {
|
||||
return "", false
|
||||
}
|
||||
n := node.Next
|
||||
if n == nil || n.Next == nil {
|
||||
return "", false
|
||||
}
|
||||
n = n.Next
|
||||
if !strings.EqualFold(n.Value, "as") || n.Next == nil || len(n.Next.Value) == 0 {
|
||||
return "", false
|
||||
}
|
||||
return n.Next.Value, true
|
||||
}
|
||||
|
||||
type Builder struct {
|
||||
RunConfig docker.Config
|
||||
|
||||
Env []string
|
||||
Args map[string]string
|
||||
CmdSet bool
|
||||
Author string
|
||||
|
||||
AllowedArgs map[string]bool
|
||||
Volumes VolumeSet
|
||||
Excludes []string
|
||||
|
||||
PendingVolumes VolumeSet
|
||||
PendingRuns []Run
|
||||
PendingCopies []Copy
|
||||
|
||||
Warnings []string
|
||||
}
|
||||
|
||||
func NewBuilder(args map[string]string) *Builder {
|
||||
allowed := make(map[string]bool)
|
||||
for k, v := range builtinAllowedBuildArgs {
|
||||
allowed[k] = v
|
||||
}
|
||||
return &Builder{
|
||||
Args: args,
|
||||
AllowedArgs: allowed,
|
||||
}
|
||||
}
|
||||
|
||||
func ParseFile(path string) (*parser.Node, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return ParseDockerfile(f)
|
||||
}
|
||||
|
||||
// Step creates a new step from the current state.
|
||||
func (b *Builder) Step() *Step {
|
||||
dst := make([]string, len(b.Env)+len(b.RunConfig.Env))
|
||||
copy(dst, b.Env)
|
||||
dst = append(dst, b.RunConfig.Env...)
|
||||
dst = append(dst, b.Arguments()...)
|
||||
return &Step{Env: dst}
|
||||
}
|
||||
|
||||
// Run executes a step, transforming the current builder and
|
||||
// invoking any Copy or Run operations. noRunsRemaining is an
|
||||
// optimization hint that allows the builder to avoid performing
|
||||
// unnecessary work.
|
||||
func (b *Builder) Run(step *Step, exec Executor, noRunsRemaining bool) error {
|
||||
fn, ok := evaluateTable[step.Command]
|
||||
if !ok {
|
||||
return exec.UnrecognizedInstruction(step)
|
||||
}
|
||||
if err := fn(b, step.Args, step.Attrs, step.Flags, step.Original); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
copies := b.PendingCopies
|
||||
b.PendingCopies = nil
|
||||
runs := b.PendingRuns
|
||||
b.PendingRuns = nil
|
||||
|
||||
// Once a VOLUME is defined, future ADD/COPY instructions are
|
||||
// all that may mutate that path. Instruct the executor to preserve
|
||||
// the path. The executor must handle invalidating preserved info.
|
||||
for _, path := range b.PendingVolumes {
|
||||
if b.Volumes.Add(path) && !noRunsRemaining {
|
||||
if err := exec.Preserve(path); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := exec.Copy(b.Excludes, copies...); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, run := range runs {
|
||||
config := b.Config()
|
||||
config.Env = step.Env
|
||||
if err := exec.Run(run, *config); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RequiresStart returns true if a running container environment is necessary
|
||||
// to invoke the provided commands
|
||||
func (b *Builder) RequiresStart(node *parser.Node) bool {
|
||||
for _, child := range node.Children {
|
||||
if child.Value == command.Run {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Config returns a snapshot of the current RunConfig intended for
|
||||
// use with a container commit.
|
||||
func (b *Builder) Config() *docker.Config {
|
||||
config := b.RunConfig
|
||||
if config.OnBuild == nil {
|
||||
config.OnBuild = []string{}
|
||||
}
|
||||
if config.Entrypoint == nil {
|
||||
config.Entrypoint = []string{}
|
||||
}
|
||||
config.Image = ""
|
||||
return &config
|
||||
}
|
||||
|
||||
// Arguments returns the currently active arguments.
|
||||
func (b *Builder) Arguments() []string {
|
||||
var envs []string
|
||||
for key, val := range b.Args {
|
||||
if _, ok := b.AllowedArgs[key]; ok {
|
||||
envs = append(envs, fmt.Sprintf("%s=%s", key, val))
|
||||
}
|
||||
}
|
||||
return envs
|
||||
}
|
||||
|
||||
// ErrNoFROM is returned if the Dockerfile did not contain a FROM
|
||||
// statement.
|
||||
var ErrNoFROM = fmt.Errorf("no FROM statement found")
|
||||
|
||||
// From returns the image this dockerfile depends on, or an error
|
||||
// if no FROM is found or if multiple FROM are specified. If a
|
||||
// single from is found the passed node is updated with only
|
||||
// the remaining statements. The builder's RunConfig.Image field
|
||||
// is set to the first From found, or left unchanged if already
|
||||
// set.
|
||||
func (b *Builder) From(node *parser.Node) (string, error) {
|
||||
children := SplitChildren(node, command.From)
|
||||
switch {
|
||||
case len(children) == 0:
|
||||
return "", ErrNoFROM
|
||||
case len(children) > 1:
|
||||
return "", fmt.Errorf("multiple FROM statements are not supported")
|
||||
default:
|
||||
step := b.Step()
|
||||
if err := step.Resolve(children[0]); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := b.Run(step, NoopExecutor, false); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return b.RunConfig.Image, nil
|
||||
}
|
||||
}
|
||||
|
||||
// FromImage updates the builder to use the provided image (resetting RunConfig
|
||||
// and recording the image environment), and updates the node with any ONBUILD
|
||||
// statements extracted from the parent image.
|
||||
func (b *Builder) FromImage(image *docker.Image, node *parser.Node) error {
|
||||
SplitChildren(node, command.From)
|
||||
|
||||
b.RunConfig = *image.Config
|
||||
b.Env = b.RunConfig.Env
|
||||
b.RunConfig.Env = nil
|
||||
|
||||
// Check to see if we have a default PATH, note that windows won't
|
||||
// have one as its set by HCS
|
||||
if runtime.GOOS != "windows" && !hasEnvName(b.Env, "PATH") {
|
||||
b.RunConfig.Env = append(b.RunConfig.Env, "PATH="+defaultPathEnv)
|
||||
}
|
||||
|
||||
// Join the image onbuild statements into node
|
||||
if image.Config == nil || len(image.Config.OnBuild) == 0 {
|
||||
return nil
|
||||
}
|
||||
extra, err := ParseDockerfile(bytes.NewBufferString(strings.Join(image.Config.OnBuild, "\n")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, child := range extra.Children {
|
||||
switch strings.ToUpper(child.Value) {
|
||||
case "ONBUILD":
|
||||
return fmt.Errorf("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed")
|
||||
case "MAINTAINER", "FROM":
|
||||
return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", child.Value)
|
||||
}
|
||||
}
|
||||
node.Children = append(extra.Children, node.Children...)
|
||||
// Since we've processed the OnBuild statements, clear them from the runconfig state.
|
||||
b.RunConfig.OnBuild = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// SplitChildren removes any children with the provided value from node
|
||||
// and returns them as an array. node.Children is updated.
|
||||
func SplitChildren(node *parser.Node, value string) []*parser.Node {
|
||||
var split []*parser.Node
|
||||
var children []*parser.Node
|
||||
for _, child := range node.Children {
|
||||
if child.Value == value {
|
||||
split = append(split, child)
|
||||
} else {
|
||||
children = append(children, child)
|
||||
}
|
||||
}
|
||||
node.Children = children
|
||||
return split
|
||||
}
|
||||
|
||||
func SplitBy(node *parser.Node, value string) []*parser.Node {
|
||||
var split []*parser.Node
|
||||
var current *parser.Node
|
||||
for _, child := range node.Children {
|
||||
if current == nil || child.Value == value {
|
||||
copied := *node
|
||||
current = &copied
|
||||
current.Children = nil
|
||||
current.Next = nil
|
||||
split = append(split, current)
|
||||
}
|
||||
current.Children = append(current.Children, child)
|
||||
}
|
||||
return split
|
||||
}
|
||||
|
||||
// StepFunc is invoked with the result of a resolved step.
|
||||
type StepFunc func(*Builder, []string, map[string]bool, []string, string) error
|
||||
|
||||
var evaluateTable = map[string]StepFunc{
|
||||
command.Env: env,
|
||||
command.Label: label,
|
||||
command.Maintainer: maintainer,
|
||||
command.Add: add,
|
||||
command.Copy: dispatchCopy, // copy() is a go builtin
|
||||
command.From: from,
|
||||
command.Onbuild: onbuild,
|
||||
command.Workdir: workdir,
|
||||
command.Run: run,
|
||||
command.Cmd: cmd,
|
||||
command.Entrypoint: entrypoint,
|
||||
command.Expose: expose,
|
||||
command.Volume: volume,
|
||||
command.User: user,
|
||||
command.StopSignal: stopSignal,
|
||||
command.Arg: arg,
|
||||
command.Healthcheck: healthcheck,
|
||||
command.Shell: shell,
|
||||
}
|
||||
|
||||
// builtinAllowedBuildArgs is list of built-in allowed build args
|
||||
var builtinAllowedBuildArgs = map[string]bool{
|
||||
"HTTP_PROXY": true,
|
||||
"http_proxy": true,
|
||||
"HTTPS_PROXY": true,
|
||||
"https_proxy": true,
|
||||
"FTP_PROXY": true,
|
||||
"ftp_proxy": true,
|
||||
"NO_PROXY": true,
|
||||
"no_proxy": true,
|
||||
}
|
||||
|
||||
// ParseDockerIgnore returns a list of the excludes in the .dockerignore file.
|
||||
// extracted from fsouza/go-dockerclient.
|
||||
func ParseDockerignore(root string) ([]string, error) {
|
||||
var excludes []string
|
||||
ignore, err := ioutil.ReadFile(filepath.Join(root, ".dockerignore"))
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return excludes, fmt.Errorf("error reading .dockerignore: '%s'", err)
|
||||
}
|
||||
return strings.Split(string(ignore), "\n"), nil
|
||||
}
|
||||
|
||||
// ExportEnv creates an export statement for a shell that contains all of the
|
||||
// provided environment.
|
||||
func ExportEnv(env []string) string {
|
||||
if len(env) == 0 {
|
||||
return ""
|
||||
}
|
||||
out := "export"
|
||||
for _, e := range env {
|
||||
if len(e) == 0 {
|
||||
continue
|
||||
}
|
||||
out += " " + BashQuote(e)
|
||||
}
|
||||
return out + "; "
|
||||
}
|
||||
|
||||
// BashQuote escapes the provided string and surrounds it with double quotes.
|
||||
// TODO: verify that these are all we have to escape.
|
||||
func BashQuote(env string) string {
|
||||
out := []rune{'"'}
|
||||
for _, r := range env {
|
||||
switch r {
|
||||
case '$', '\\', '"':
|
||||
out = append(out, '\\', r)
|
||||
default:
|
||||
out = append(out, r)
|
||||
}
|
||||
}
|
||||
out = append(out, '"')
|
||||
return string(out)
|
||||
}
|
13
vendor/github.com/openshift/imagebuilder/constants.go
generated
vendored
Normal file
13
vendor/github.com/openshift/imagebuilder/constants.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
package imagebuilder
|
||||
|
||||
const (
|
||||
// in docker/system
|
||||
NoBaseImageSpecifier = "scratch"
|
||||
|
||||
// not yet part of our import
|
||||
commandArg = "arg"
|
||||
commandStopSignal = "stopsignal"
|
||||
|
||||
// in docker/system
|
||||
defaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
)
|
570
vendor/github.com/openshift/imagebuilder/dispatchers.go
generated
vendored
Normal file
570
vendor/github.com/openshift/imagebuilder/dispatchers.go
generated
vendored
Normal file
@ -0,0 +1,570 @@
|
||||
package imagebuilder
|
||||
|
||||
// This file contains the dispatchers for each command. Note that
|
||||
// `nullDispatch` is not actually a command, but support for commands we parse
|
||||
// but do nothing with.
|
||||
//
|
||||
// See evaluator.go for a higher level discussion of the whole evaluator
|
||||
// package.
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
docker "github.com/fsouza/go-dockerclient"
|
||||
|
||||
"github.com/openshift/imagebuilder/signal"
|
||||
"github.com/openshift/imagebuilder/strslice"
|
||||
)
|
||||
|
||||
var (
|
||||
obRgex = regexp.MustCompile(`(?i)^\s*ONBUILD\s*`)
|
||||
)
|
||||
|
||||
// dispatch with no layer / parsing. This is effectively not a command.
|
||||
func nullDispatch(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ENV foo bar
|
||||
//
|
||||
// Sets the environment variable foo to bar, also makes interpolation
|
||||
// in the dockerfile available from the next statement on via ${foo}.
|
||||
//
|
||||
func env(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
|
||||
if len(args) == 0 {
|
||||
return errAtLeastOneArgument("ENV")
|
||||
}
|
||||
|
||||
if len(args)%2 != 0 {
|
||||
// should never get here, but just in case
|
||||
return errTooManyArguments("ENV")
|
||||
}
|
||||
|
||||
// TODO/FIXME/NOT USED
|
||||
// Just here to show how to use the builder flags stuff within the
|
||||
// context of a builder command. Will remove once we actually add
|
||||
// a builder command to something!
|
||||
/*
|
||||
flBool1 := b.flags.AddBool("bool1", false)
|
||||
flStr1 := b.flags.AddString("str1", "HI")
|
||||
|
||||
if err := b.flags.Parse(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Bool1:%v\n", flBool1)
|
||||
fmt.Printf("Str1:%v\n", flStr1)
|
||||
*/
|
||||
|
||||
for j := 0; j < len(args); j++ {
|
||||
// name ==> args[j]
|
||||
// value ==> args[j+1]
|
||||
newVar := args[j] + "=" + args[j+1] + ""
|
||||
gotOne := false
|
||||
for i, envVar := range b.RunConfig.Env {
|
||||
envParts := strings.SplitN(envVar, "=", 2)
|
||||
if envParts[0] == args[j] {
|
||||
b.RunConfig.Env[i] = newVar
|
||||
b.Env = append([]string{newVar}, b.Env...)
|
||||
gotOne = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !gotOne {
|
||||
b.RunConfig.Env = append(b.RunConfig.Env, newVar)
|
||||
b.Env = append([]string{newVar}, b.Env...)
|
||||
}
|
||||
j++
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MAINTAINER some text <maybe@an.email.address>
|
||||
//
|
||||
// Sets the maintainer metadata.
|
||||
func maintainer(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
|
||||
if len(args) != 1 {
|
||||
return errExactlyOneArgument("MAINTAINER")
|
||||
}
|
||||
b.Author = args[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
// LABEL some json data describing the image
|
||||
//
|
||||
// Sets the Label variable foo to bar,
|
||||
//
|
||||
func label(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
|
||||
if len(args) == 0 {
|
||||
return errAtLeastOneArgument("LABEL")
|
||||
}
|
||||
if len(args)%2 != 0 {
|
||||
// should never get here, but just in case
|
||||
return errTooManyArguments("LABEL")
|
||||
}
|
||||
|
||||
if b.RunConfig.Labels == nil {
|
||||
b.RunConfig.Labels = map[string]string{}
|
||||
}
|
||||
|
||||
for j := 0; j < len(args); j++ {
|
||||
// name ==> args[j]
|
||||
// value ==> args[j+1]
|
||||
b.RunConfig.Labels[args[j]] = args[j+1]
|
||||
j++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ADD foo /path
|
||||
//
|
||||
// Add the file 'foo' to '/path'. Tarball and Remote URL (git, http) handling
|
||||
// exist here. If you do not wish to have this automatic handling, use COPY.
|
||||
//
|
||||
func add(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
|
||||
if len(args) < 2 {
|
||||
return errAtLeastOneArgument("ADD")
|
||||
}
|
||||
last := len(args) - 1
|
||||
dest := makeAbsolute(args[last], b.RunConfig.WorkingDir)
|
||||
b.PendingCopies = append(b.PendingCopies, Copy{Src: args[0:last], Dest: dest, Download: true})
|
||||
return nil
|
||||
}
|
||||
|
||||
// COPY foo /path
|
||||
//
|
||||
// Same as 'ADD' but without the tar and remote url handling.
|
||||
//
|
||||
func dispatchCopy(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
|
||||
if len(args) < 2 {
|
||||
return errAtLeastOneArgument("COPY")
|
||||
}
|
||||
last := len(args) - 1
|
||||
dest := makeAbsolute(args[last], b.RunConfig.WorkingDir)
|
||||
var from string
|
||||
if len(flagArgs) > 0 {
|
||||
for _, arg := range flagArgs {
|
||||
switch {
|
||||
case strings.HasPrefix(arg, "--from="):
|
||||
from = strings.TrimPrefix(arg, "--from=")
|
||||
default:
|
||||
return fmt.Errorf("COPY only supports the --from=<image|stage> flag")
|
||||
}
|
||||
}
|
||||
}
|
||||
b.PendingCopies = append(b.PendingCopies, Copy{From: from, Src: args[0:last], Dest: dest, Download: false})
|
||||
return nil
|
||||
}
|
||||
|
||||
// FROM imagename
|
||||
//
|
||||
// This sets the image the dockerfile will build on top of.
|
||||
//
|
||||
func from(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
|
||||
switch {
|
||||
case len(args) == 1:
|
||||
case len(args) == 3 && len(args[0]) > 0 && strings.EqualFold(args[1], "as") && len(args[2]) > 0:
|
||||
|
||||
default:
|
||||
return fmt.Errorf("FROM requires either one argument, or three: FROM <source> [as <name>]")
|
||||
}
|
||||
|
||||
name := args[0]
|
||||
// Windows cannot support a container with no base image.
|
||||
if name == NoBaseImageSpecifier {
|
||||
if runtime.GOOS == "windows" {
|
||||
return fmt.Errorf("Windows does not support FROM scratch")
|
||||
}
|
||||
}
|
||||
b.RunConfig.Image = name
|
||||
// TODO: handle onbuild
|
||||
return nil
|
||||
}
|
||||
|
||||
// ONBUILD RUN echo yo
|
||||
//
|
||||
// ONBUILD triggers run when the image is used in a FROM statement.
|
||||
//
|
||||
// ONBUILD handling has a lot of special-case functionality, the heading in
|
||||
// evaluator.go and comments around dispatch() in the same file explain the
|
||||
// special cases. search for 'OnBuild' in internals.go for additional special
|
||||
// cases.
|
||||
//
|
||||
func onbuild(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
|
||||
if len(args) == 0 {
|
||||
return errAtLeastOneArgument("ONBUILD")
|
||||
}
|
||||
|
||||
triggerInstruction := strings.ToUpper(strings.TrimSpace(args[0]))
|
||||
switch triggerInstruction {
|
||||
case "ONBUILD":
|
||||
return fmt.Errorf("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed")
|
||||
case "MAINTAINER", "FROM":
|
||||
return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", triggerInstruction)
|
||||
}
|
||||
|
||||
original = obRgex.ReplaceAllString(original, "")
|
||||
|
||||
b.RunConfig.OnBuild = append(b.RunConfig.OnBuild, original)
|
||||
return nil
|
||||
}
|
||||
|
||||
// WORKDIR /tmp
|
||||
//
|
||||
// Set the working directory for future RUN/CMD/etc statements.
|
||||
//
|
||||
func workdir(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
|
||||
if len(args) != 1 {
|
||||
return errExactlyOneArgument("WORKDIR")
|
||||
}
|
||||
|
||||
// This is from the Dockerfile and will not necessarily be in platform
|
||||
// specific semantics, hence ensure it is converted.
|
||||
workdir := filepath.FromSlash(args[0])
|
||||
|
||||
if !filepath.IsAbs(workdir) {
|
||||
current := filepath.FromSlash(b.RunConfig.WorkingDir)
|
||||
workdir = filepath.Join(string(os.PathSeparator), current, workdir)
|
||||
}
|
||||
|
||||
b.RunConfig.WorkingDir = workdir
|
||||
return nil
|
||||
}
|
||||
|
||||
// RUN some command yo
|
||||
//
|
||||
// run a command and commit the image. Args are automatically prepended with
|
||||
// 'sh -c' under linux or 'cmd /S /C' under Windows, in the event there is
|
||||
// only one argument. The difference in processing:
|
||||
//
|
||||
// RUN echo hi # sh -c echo hi (Linux)
|
||||
// RUN echo hi # cmd /S /C echo hi (Windows)
|
||||
// RUN [ "echo", "hi" ] # echo hi
|
||||
//
|
||||
func run(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
|
||||
if b.RunConfig.Image == "" {
|
||||
return fmt.Errorf("Please provide a source image with `from` prior to run")
|
||||
}
|
||||
|
||||
args = handleJSONArgs(args, attributes)
|
||||
|
||||
run := Run{Args: args}
|
||||
|
||||
if !attributes["json"] {
|
||||
run.Shell = true
|
||||
}
|
||||
b.PendingRuns = append(b.PendingRuns, run)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CMD foo
|
||||
//
|
||||
// Set the default command to run in the container (which may be empty).
|
||||
// Argument handling is the same as RUN.
|
||||
//
|
||||
func cmd(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
|
||||
cmdSlice := handleJSONArgs(args, attributes)
|
||||
|
||||
if !attributes["json"] {
|
||||
if runtime.GOOS != "windows" {
|
||||
cmdSlice = append([]string{"/bin/sh", "-c"}, cmdSlice...)
|
||||
} else {
|
||||
cmdSlice = append([]string{"cmd", "/S", "/C"}, cmdSlice...)
|
||||
}
|
||||
}
|
||||
|
||||
b.RunConfig.Cmd = strslice.StrSlice(cmdSlice)
|
||||
if len(args) != 0 {
|
||||
b.CmdSet = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ENTRYPOINT /usr/sbin/nginx
|
||||
//
|
||||
// Set the entrypoint (which defaults to sh -c on linux, or cmd /S /C on Windows) to
|
||||
// /usr/sbin/nginx. Will accept the CMD as the arguments to /usr/sbin/nginx.
|
||||
//
|
||||
// Handles command processing similar to CMD and RUN, only b.RunConfig.Entrypoint
|
||||
// is initialized at NewBuilder time instead of through argument parsing.
|
||||
//
|
||||
func entrypoint(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
|
||||
parsed := handleJSONArgs(args, attributes)
|
||||
|
||||
switch {
|
||||
case attributes["json"]:
|
||||
// ENTRYPOINT ["echo", "hi"]
|
||||
b.RunConfig.Entrypoint = strslice.StrSlice(parsed)
|
||||
case len(parsed) == 0:
|
||||
// ENTRYPOINT []
|
||||
b.RunConfig.Entrypoint = nil
|
||||
default:
|
||||
// ENTRYPOINT echo hi
|
||||
if runtime.GOOS != "windows" {
|
||||
b.RunConfig.Entrypoint = strslice.StrSlice{"/bin/sh", "-c", parsed[0]}
|
||||
} else {
|
||||
b.RunConfig.Entrypoint = strslice.StrSlice{"cmd", "/S", "/C", parsed[0]}
|
||||
}
|
||||
}
|
||||
|
||||
// when setting the entrypoint if a CMD was not explicitly set then
|
||||
// set the command to nil
|
||||
if !b.CmdSet {
|
||||
b.RunConfig.Cmd = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EXPOSE 6667/tcp 7000/tcp
|
||||
//
|
||||
// Expose ports for links and port mappings. This all ends up in
|
||||
// b.RunConfig.ExposedPorts for runconfig.
|
||||
//
|
||||
func expose(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
|
||||
if len(args) == 0 {
|
||||
return errAtLeastOneArgument("EXPOSE")
|
||||
}
|
||||
|
||||
if b.RunConfig.ExposedPorts == nil {
|
||||
b.RunConfig.ExposedPorts = make(map[docker.Port]struct{})
|
||||
}
|
||||
|
||||
existing := map[string]struct{}{}
|
||||
for k := range b.RunConfig.ExposedPorts {
|
||||
existing[k.Port()] = struct{}{}
|
||||
}
|
||||
|
||||
for _, port := range args {
|
||||
dp := docker.Port(port)
|
||||
if _, exists := existing[dp.Port()]; !exists {
|
||||
b.RunConfig.ExposedPorts[docker.Port(fmt.Sprintf("%s/%s", dp.Port(), dp.Proto()))] = struct{}{}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// USER foo
|
||||
//
|
||||
// Set the user to 'foo' for future commands and when running the
|
||||
// ENTRYPOINT/CMD at container run time.
|
||||
//
|
||||
func user(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
|
||||
if len(args) != 1 {
|
||||
return errExactlyOneArgument("USER")
|
||||
}
|
||||
|
||||
b.RunConfig.User = args[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
// VOLUME /foo
|
||||
//
|
||||
// Expose the volume /foo for use. Will also accept the JSON array form.
|
||||
//
|
||||
func volume(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
|
||||
if len(args) == 0 {
|
||||
return errAtLeastOneArgument("VOLUME")
|
||||
}
|
||||
|
||||
if b.RunConfig.Volumes == nil {
|
||||
b.RunConfig.Volumes = map[string]struct{}{}
|
||||
}
|
||||
for _, v := range args {
|
||||
v = strings.TrimSpace(v)
|
||||
if v == "" {
|
||||
return fmt.Errorf("Volume specified can not be an empty string")
|
||||
}
|
||||
b.RunConfig.Volumes[v] = struct{}{}
|
||||
b.PendingVolumes.Add(v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// STOPSIGNAL signal
|
||||
//
|
||||
// Set the signal that will be used to kill the container.
|
||||
func stopSignal(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
|
||||
if len(args) != 1 {
|
||||
return errExactlyOneArgument("STOPSIGNAL")
|
||||
}
|
||||
|
||||
sig := args[0]
|
||||
if err := signal.CheckSignal(sig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.RunConfig.StopSignal = sig
|
||||
return nil
|
||||
}
|
||||
|
||||
// HEALTHCHECK foo
|
||||
//
|
||||
// Set the default healthcheck command to run in the container (which may be empty).
|
||||
// Argument handling is the same as RUN.
|
||||
//
|
||||
func healthcheck(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
|
||||
if len(args) == 0 {
|
||||
return errAtLeastOneArgument("HEALTHCHECK")
|
||||
}
|
||||
typ := strings.ToUpper(args[0])
|
||||
args = args[1:]
|
||||
if typ == "NONE" {
|
||||
if len(args) != 0 {
|
||||
return fmt.Errorf("HEALTHCHECK NONE takes no arguments")
|
||||
}
|
||||
test := strslice.StrSlice{typ}
|
||||
b.RunConfig.Healthcheck = &docker.HealthConfig{
|
||||
Test: test,
|
||||
}
|
||||
} else {
|
||||
if b.RunConfig.Healthcheck != nil {
|
||||
oldCmd := b.RunConfig.Healthcheck.Test
|
||||
if len(oldCmd) > 0 && oldCmd[0] != "NONE" {
|
||||
b.Warnings = append(b.Warnings, fmt.Sprintf("Note: overriding previous HEALTHCHECK: %v\n", oldCmd))
|
||||
}
|
||||
}
|
||||
|
||||
healthcheck := docker.HealthConfig{}
|
||||
|
||||
flags := flag.NewFlagSet("", flag.ContinueOnError)
|
||||
flags.String("interval", "", "")
|
||||
flags.String("timeout", "", "")
|
||||
flRetries := flags.String("retries", "", "")
|
||||
|
||||
if err := flags.Parse(flagArgs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch typ {
|
||||
case "CMD":
|
||||
cmdSlice := handleJSONArgs(args, attributes)
|
||||
if len(cmdSlice) == 0 {
|
||||
return fmt.Errorf("Missing command after HEALTHCHECK CMD")
|
||||
}
|
||||
|
||||
if !attributes["json"] {
|
||||
typ = "CMD-SHELL"
|
||||
}
|
||||
|
||||
healthcheck.Test = strslice.StrSlice(append([]string{typ}, cmdSlice...))
|
||||
default:
|
||||
return fmt.Errorf("Unknown type %#v in HEALTHCHECK (try CMD)", typ)
|
||||
}
|
||||
|
||||
interval, err := parseOptInterval(flags.Lookup("interval"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
healthcheck.Interval = interval
|
||||
|
||||
timeout, err := parseOptInterval(flags.Lookup("timeout"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
healthcheck.Timeout = timeout
|
||||
|
||||
if *flRetries != "" {
|
||||
retries, err := strconv.ParseInt(*flRetries, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if retries < 1 {
|
||||
return fmt.Errorf("--retries must be at least 1 (not %d)", retries)
|
||||
}
|
||||
healthcheck.Retries = int(retries)
|
||||
} else {
|
||||
healthcheck.Retries = 0
|
||||
}
|
||||
b.RunConfig.Healthcheck = &healthcheck
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ARG name[=value]
|
||||
//
|
||||
// Adds the variable foo to the trusted list of variables that can be passed
|
||||
// to builder using the --build-arg flag for expansion/subsitution or passing to 'run'.
|
||||
// Dockerfile author may optionally set a default value of this variable.
|
||||
func arg(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
|
||||
if len(args) != 1 {
|
||||
return fmt.Errorf("ARG requires exactly one argument definition")
|
||||
}
|
||||
|
||||
var (
|
||||
name string
|
||||
value string
|
||||
hasDefault bool
|
||||
)
|
||||
|
||||
arg := args[0]
|
||||
// 'arg' can just be a name or name-value pair. Note that this is different
|
||||
// from 'env' that handles the split of name and value at the parser level.
|
||||
// The reason for doing it differently for 'arg' is that we support just
|
||||
// defining an arg and not assign it a value (while 'env' always expects a
|
||||
// name-value pair). If possible, it will be good to harmonize the two.
|
||||
if strings.Contains(arg, "=") {
|
||||
parts := strings.SplitN(arg, "=", 2)
|
||||
name = parts[0]
|
||||
value = parts[1]
|
||||
hasDefault = true
|
||||
} else {
|
||||
name = arg
|
||||
hasDefault = false
|
||||
}
|
||||
// add the arg to allowed list of build-time args from this step on.
|
||||
b.AllowedArgs[name] = true
|
||||
|
||||
// If there is a default value associated with this arg then add it to the
|
||||
// b.buildArgs if one is not already passed to the builder. The args passed
|
||||
// to builder override the default value of 'arg'.
|
||||
if _, ok := b.Args[name]; !ok && hasDefault {
|
||||
b.Args[name] = value
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SHELL powershell -command
|
||||
//
|
||||
// Set the non-default shell to use.
|
||||
func shell(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
|
||||
shellSlice := handleJSONArgs(args, attributes)
|
||||
switch {
|
||||
case len(shellSlice) == 0:
|
||||
// SHELL []
|
||||
return errAtLeastOneArgument("SHELL")
|
||||
case attributes["json"]:
|
||||
// SHELL ["powershell", "-command"]
|
||||
b.RunConfig.Shell = strslice.StrSlice(shellSlice)
|
||||
// b.RunConfig.Shell = strslice.StrSlice(shellSlice)
|
||||
default:
|
||||
// SHELL powershell -command - not JSON
|
||||
return errNotJSON("SHELL")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func errAtLeastOneArgument(command string) error {
|
||||
return fmt.Errorf("%s requires at least one argument", command)
|
||||
}
|
||||
|
||||
func errExactlyOneArgument(command string) error {
|
||||
return fmt.Errorf("%s requires exactly one argument", command)
|
||||
}
|
||||
|
||||
func errTooManyArguments(command string) error {
|
||||
return fmt.Errorf("Bad input to %s, too many arguments", command)
|
||||
}
|
||||
|
||||
func errNotJSON(command string) error {
|
||||
return fmt.Errorf("%s requires the arguments to be in JSON form", command)
|
||||
}
|
6
vendor/github.com/openshift/imagebuilder/doc.go
generated
vendored
Normal file
6
vendor/github.com/openshift/imagebuilder/doc.go
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
// Package builder uses code from github.com/docker/docker/builder/* to implement
|
||||
// a Docker builder that does not create individual layers, but instead creates a
|
||||
// single layer.
|
||||
//
|
||||
// TODO: full windows support
|
||||
package imagebuilder
|
160
vendor/github.com/openshift/imagebuilder/evaluator.go
generated
vendored
Normal file
160
vendor/github.com/openshift/imagebuilder/evaluator.go
generated
vendored
Normal file
@ -0,0 +1,160 @@
|
||||
package imagebuilder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/builder/dockerfile/command"
|
||||
"github.com/docker/docker/builder/dockerfile/parser"
|
||||
)
|
||||
|
||||
// ParseDockerfile parses the provided stream as a canonical Dockerfile
|
||||
func ParseDockerfile(r io.Reader) (*parser.Node, error) {
|
||||
result, err := parser.Parse(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result.AST, nil
|
||||
}
|
||||
|
||||
// Environment variable interpolation will happen on these statements only.
|
||||
var replaceEnvAllowed = map[string]bool{
|
||||
command.Env: true,
|
||||
command.Label: true,
|
||||
command.Add: true,
|
||||
command.Copy: true,
|
||||
command.Workdir: true,
|
||||
command.Expose: true,
|
||||
command.Volume: true,
|
||||
command.User: true,
|
||||
commandStopSignal: true,
|
||||
commandArg: true,
|
||||
}
|
||||
|
||||
// Certain commands are allowed to have their args split into more
|
||||
// words after env var replacements. Meaning:
|
||||
// ENV foo="123 456"
|
||||
// EXPOSE $foo
|
||||
// should result in the same thing as:
|
||||
// EXPOSE 123 456
|
||||
// and not treat "123 456" as a single word.
|
||||
// Note that: EXPOSE "$foo" and EXPOSE $foo are not the same thing.
|
||||
// Quotes will cause it to still be treated as single word.
|
||||
var allowWordExpansion = map[string]bool{
|
||||
command.Expose: true,
|
||||
}
|
||||
|
||||
// Step represents the input Env and the output command after all
|
||||
// post processing of the command arguments is done.
|
||||
type Step struct {
|
||||
Env []string
|
||||
|
||||
Command string
|
||||
Args []string
|
||||
Flags []string
|
||||
Attrs map[string]bool
|
||||
Message string
|
||||
Original string
|
||||
}
|
||||
|
||||
// Resolve transforms a parsed Dockerfile line into a command to execute,
|
||||
// resolving any arguments.
|
||||
//
|
||||
// Almost all nodes will have this structure:
|
||||
// Child[Node, Node, Node] where Child is from parser.Node.Children and each
|
||||
// node comes from parser.Node.Next. This forms a "line" with a statement and
|
||||
// arguments and we process them in this normalized form by hitting
|
||||
// evaluateTable with the leaf nodes of the command and the Builder object.
|
||||
//
|
||||
// ONBUILD is a special case; in this case the parser will emit:
|
||||
// Child[Node, Child[Node, Node...]] where the first node is the literal
|
||||
// "onbuild" and the child entrypoint is the command of the ONBUILD statement,
|
||||
// such as `RUN` in ONBUILD RUN foo. There is special case logic in here to
|
||||
// deal with that, at least until it becomes more of a general concern with new
|
||||
// features.
|
||||
func (b *Step) Resolve(ast *parser.Node) error {
|
||||
cmd := ast.Value
|
||||
upperCasedCmd := strings.ToUpper(cmd)
|
||||
|
||||
// To ensure the user is given a decent error message if the platform
|
||||
// on which the daemon is running does not support a builder command.
|
||||
if err := platformSupports(strings.ToLower(cmd)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
attrs := ast.Attributes
|
||||
original := ast.Original
|
||||
flags := ast.Flags
|
||||
strList := []string{}
|
||||
msg := upperCasedCmd
|
||||
|
||||
if len(ast.Flags) > 0 {
|
||||
msg += " " + strings.Join(ast.Flags, " ")
|
||||
}
|
||||
|
||||
if cmd == "onbuild" {
|
||||
if ast.Next == nil {
|
||||
return fmt.Errorf("ONBUILD requires at least one argument")
|
||||
}
|
||||
ast = ast.Next.Children[0]
|
||||
strList = append(strList, ast.Value)
|
||||
msg += " " + ast.Value
|
||||
|
||||
if len(ast.Flags) > 0 {
|
||||
msg += " " + strings.Join(ast.Flags, " ")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// count the number of nodes that we are going to traverse first
|
||||
// so we can pre-create the argument and message array. This speeds up the
|
||||
// allocation of those list a lot when they have a lot of arguments
|
||||
cursor := ast
|
||||
var n int
|
||||
for cursor.Next != nil {
|
||||
cursor = cursor.Next
|
||||
n++
|
||||
}
|
||||
msgList := make([]string, n)
|
||||
|
||||
var i int
|
||||
envs := b.Env
|
||||
for ast.Next != nil {
|
||||
ast = ast.Next
|
||||
var str string
|
||||
str = ast.Value
|
||||
if replaceEnvAllowed[cmd] {
|
||||
var err error
|
||||
var words []string
|
||||
|
||||
if allowWordExpansion[cmd] {
|
||||
words, err = ProcessWords(str, envs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
strList = append(strList, words...)
|
||||
} else {
|
||||
str, err = ProcessWord(str, envs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
strList = append(strList, str)
|
||||
}
|
||||
} else {
|
||||
strList = append(strList, str)
|
||||
}
|
||||
msgList[i] = ast.Value
|
||||
i++
|
||||
}
|
||||
|
||||
msg += " " + strings.Join(msgList, " ")
|
||||
|
||||
b.Message = msg
|
||||
b.Command = cmd
|
||||
b.Args = strList
|
||||
b.Original = original
|
||||
b.Attrs = attrs
|
||||
b.Flags = flags
|
||||
return nil
|
||||
}
|
83
vendor/github.com/openshift/imagebuilder/internals.go
generated
vendored
Normal file
83
vendor/github.com/openshift/imagebuilder/internals.go
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
package imagebuilder
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// hasEnvName returns true if the provided environment contains the named ENV var.
|
||||
func hasEnvName(env []string, name string) bool {
|
||||
for _, e := range env {
|
||||
if strings.HasPrefix(e, name+"=") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// platformSupports is a short-term function to give users a quality error
|
||||
// message if a Dockerfile uses a command not supported on the platform.
|
||||
func platformSupports(command string) error {
|
||||
if runtime.GOOS != "windows" {
|
||||
return nil
|
||||
}
|
||||
switch command {
|
||||
case "expose", "user", "stopsignal", "arg":
|
||||
return fmt.Errorf("The daemon on this platform does not support the command '%s'", command)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleJSONArgs(args []string, attributes map[string]bool) []string {
|
||||
if len(args) == 0 {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
if attributes != nil && attributes["json"] {
|
||||
return args
|
||||
}
|
||||
|
||||
// literal string command, not an exec array
|
||||
return []string{strings.Join(args, " ")}
|
||||
}
|
||||
|
||||
// makeAbsolute ensures that the provided path is absolute.
|
||||
func makeAbsolute(dest, workingDir string) string {
|
||||
// Twiddle the destination when its a relative path - meaning, make it
|
||||
// relative to the WORKINGDIR
|
||||
if !filepath.IsAbs(dest) {
|
||||
hasSlash := strings.HasSuffix(dest, string(os.PathSeparator)) || strings.HasSuffix(dest, string(os.PathSeparator)+".")
|
||||
dest = filepath.Join(string(os.PathSeparator), filepath.FromSlash(workingDir), dest)
|
||||
|
||||
// Make sure we preserve any trailing slash
|
||||
if hasSlash {
|
||||
dest += string(os.PathSeparator)
|
||||
}
|
||||
}
|
||||
return dest
|
||||
}
|
||||
|
||||
// parseOptInterval(flag) is the duration of flag.Value, or 0 if
|
||||
// empty. An error is reported if the value is given and is not positive.
|
||||
func parseOptInterval(f *flag.Flag) (time.Duration, error) {
|
||||
if f == nil {
|
||||
return 0, fmt.Errorf("No flag defined")
|
||||
}
|
||||
s := f.Value.String()
|
||||
if s == "" {
|
||||
return 0, nil
|
||||
}
|
||||
d, err := time.ParseDuration(s)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if d <= 0 {
|
||||
return 0, fmt.Errorf("Interval %#v must be positive", f.Name)
|
||||
}
|
||||
return d, nil
|
||||
}
|
314
vendor/github.com/openshift/imagebuilder/shell_parser.go
generated
vendored
Normal file
314
vendor/github.com/openshift/imagebuilder/shell_parser.go
generated
vendored
Normal file
@ -0,0 +1,314 @@
|
||||
package imagebuilder
|
||||
|
||||
// This will take a single word and an array of env variables and
|
||||
// process all quotes (" and ') as well as $xxx and ${xxx} env variable
|
||||
// tokens. Tries to mimic bash shell process.
|
||||
// It doesn't support all flavors of ${xx:...} formats but new ones can
|
||||
// be added by adding code to the "special ${} format processing" section
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/scanner"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type shellWord struct {
|
||||
word string
|
||||
scanner scanner.Scanner
|
||||
envs []string
|
||||
pos int
|
||||
}
|
||||
|
||||
// ProcessWord will use the 'env' list of environment variables,
|
||||
// and replace any env var references in 'word'.
|
||||
func ProcessWord(word string, env []string) (string, error) {
|
||||
sw := &shellWord{
|
||||
word: word,
|
||||
envs: env,
|
||||
pos: 0,
|
||||
}
|
||||
sw.scanner.Init(strings.NewReader(word))
|
||||
word, _, err := sw.process()
|
||||
return word, err
|
||||
}
|
||||
|
||||
// ProcessWords will use the 'env' list of environment variables,
|
||||
// and replace any env var references in 'word' then it will also
|
||||
// return a slice of strings which represents the 'word'
|
||||
// split up based on spaces - taking into account quotes. Note that
|
||||
// this splitting is done **after** the env var substitutions are done.
|
||||
// Note, each one is trimmed to remove leading and trailing spaces (unless
|
||||
// they are quoted", but ProcessWord retains spaces between words.
|
||||
func ProcessWords(word string, env []string) ([]string, error) {
|
||||
sw := &shellWord{
|
||||
word: word,
|
||||
envs: env,
|
||||
pos: 0,
|
||||
}
|
||||
sw.scanner.Init(strings.NewReader(word))
|
||||
_, words, err := sw.process()
|
||||
return words, err
|
||||
}
|
||||
|
||||
func (sw *shellWord) process() (string, []string, error) {
|
||||
return sw.processStopOn(scanner.EOF)
|
||||
}
|
||||
|
||||
type wordsStruct struct {
|
||||
word string
|
||||
words []string
|
||||
inWord bool
|
||||
}
|
||||
|
||||
func (w *wordsStruct) addChar(ch rune) {
|
||||
if unicode.IsSpace(ch) && w.inWord {
|
||||
if len(w.word) != 0 {
|
||||
w.words = append(w.words, w.word)
|
||||
w.word = ""
|
||||
w.inWord = false
|
||||
}
|
||||
} else if !unicode.IsSpace(ch) {
|
||||
w.addRawChar(ch)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *wordsStruct) addRawChar(ch rune) {
|
||||
w.word += string(ch)
|
||||
w.inWord = true
|
||||
}
|
||||
|
||||
func (w *wordsStruct) addString(str string) {
|
||||
var scan scanner.Scanner
|
||||
scan.Init(strings.NewReader(str))
|
||||
for scan.Peek() != scanner.EOF {
|
||||
w.addChar(scan.Next())
|
||||
}
|
||||
}
|
||||
|
||||
func (w *wordsStruct) addRawString(str string) {
|
||||
w.word += str
|
||||
w.inWord = true
|
||||
}
|
||||
|
||||
func (w *wordsStruct) getWords() []string {
|
||||
if len(w.word) > 0 {
|
||||
w.words = append(w.words, w.word)
|
||||
|
||||
// Just in case we're called again by mistake
|
||||
w.word = ""
|
||||
w.inWord = false
|
||||
}
|
||||
return w.words
|
||||
}
|
||||
|
||||
// Process the word, starting at 'pos', and stop when we get to the
|
||||
// end of the word or the 'stopChar' character
|
||||
func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) {
|
||||
var result string
|
||||
var words wordsStruct
|
||||
|
||||
var charFuncMapping = map[rune]func() (string, error){
|
||||
'\'': sw.processSingleQuote,
|
||||
'"': sw.processDoubleQuote,
|
||||
'$': sw.processDollar,
|
||||
}
|
||||
|
||||
for sw.scanner.Peek() != scanner.EOF {
|
||||
ch := sw.scanner.Peek()
|
||||
|
||||
if stopChar != scanner.EOF && ch == stopChar {
|
||||
sw.scanner.Next()
|
||||
break
|
||||
}
|
||||
if fn, ok := charFuncMapping[ch]; ok {
|
||||
// Call special processing func for certain chars
|
||||
tmp, err := fn()
|
||||
if err != nil {
|
||||
return "", []string{}, err
|
||||
}
|
||||
result += tmp
|
||||
|
||||
if ch == rune('$') {
|
||||
words.addString(tmp)
|
||||
} else {
|
||||
words.addRawString(tmp)
|
||||
}
|
||||
} else {
|
||||
// Not special, just add it to the result
|
||||
ch = sw.scanner.Next()
|
||||
|
||||
if ch == '\\' {
|
||||
// '\' escapes, except end of line
|
||||
|
||||
ch = sw.scanner.Next()
|
||||
|
||||
if ch == scanner.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
words.addRawChar(ch)
|
||||
} else {
|
||||
words.addChar(ch)
|
||||
}
|
||||
|
||||
result += string(ch)
|
||||
}
|
||||
}
|
||||
|
||||
return result, words.getWords(), nil
|
||||
}
|
||||
|
||||
func (sw *shellWord) processSingleQuote() (string, error) {
|
||||
// All chars between single quotes are taken as-is
|
||||
// Note, you can't escape '
|
||||
var result string
|
||||
|
||||
sw.scanner.Next()
|
||||
|
||||
for {
|
||||
ch := sw.scanner.Next()
|
||||
if ch == '\'' || ch == scanner.EOF {
|
||||
break
|
||||
}
|
||||
result += string(ch)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (sw *shellWord) processDoubleQuote() (string, error) {
|
||||
// All chars up to the next " are taken as-is, even ', except any $ chars
|
||||
// But you can escape " with a \
|
||||
var result string
|
||||
|
||||
sw.scanner.Next()
|
||||
|
||||
for sw.scanner.Peek() != scanner.EOF {
|
||||
ch := sw.scanner.Peek()
|
||||
if ch == '"' {
|
||||
sw.scanner.Next()
|
||||
break
|
||||
}
|
||||
if ch == '$' {
|
||||
tmp, err := sw.processDollar()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
result += tmp
|
||||
} else {
|
||||
ch = sw.scanner.Next()
|
||||
if ch == '\\' {
|
||||
chNext := sw.scanner.Peek()
|
||||
|
||||
if chNext == scanner.EOF {
|
||||
// Ignore \ at end of word
|
||||
continue
|
||||
}
|
||||
|
||||
if chNext == '"' || chNext == '$' {
|
||||
// \" and \$ can be escaped, all other \'s are left as-is
|
||||
ch = sw.scanner.Next()
|
||||
}
|
||||
}
|
||||
result += string(ch)
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (sw *shellWord) processDollar() (string, error) {
|
||||
sw.scanner.Next()
|
||||
ch := sw.scanner.Peek()
|
||||
if ch == '{' {
|
||||
sw.scanner.Next()
|
||||
name := sw.processName()
|
||||
ch = sw.scanner.Peek()
|
||||
if ch == '}' {
|
||||
// Normal ${xx} case
|
||||
sw.scanner.Next()
|
||||
return sw.getEnv(name), nil
|
||||
}
|
||||
if ch == ':' {
|
||||
// Special ${xx:...} format processing
|
||||
// Yes it allows for recursive $'s in the ... spot
|
||||
|
||||
sw.scanner.Next() // skip over :
|
||||
modifier := sw.scanner.Next()
|
||||
|
||||
word, _, err := sw.processStopOn('}')
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Grab the current value of the variable in question so we
|
||||
// can use it to determine what to do based on the modifier
|
||||
newValue := sw.getEnv(name)
|
||||
|
||||
switch modifier {
|
||||
case '+':
|
||||
if newValue != "" {
|
||||
newValue = word
|
||||
}
|
||||
return newValue, nil
|
||||
|
||||
case '-':
|
||||
if newValue == "" {
|
||||
newValue = word
|
||||
}
|
||||
return newValue, nil
|
||||
|
||||
default:
|
||||
return "", fmt.Errorf("Unsupported modifier (%c) in substitution: %s", modifier, sw.word)
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("Missing ':' in substitution: %s", sw.word)
|
||||
}
|
||||
// $xxx case
|
||||
name := sw.processName()
|
||||
if name == "" {
|
||||
return "$", nil
|
||||
}
|
||||
return sw.getEnv(name), nil
|
||||
}
|
||||
|
||||
func (sw *shellWord) processName() string {
|
||||
// Read in a name (alphanumeric or _)
|
||||
// If it starts with a numeric then just return $#
|
||||
var name string
|
||||
|
||||
for sw.scanner.Peek() != scanner.EOF {
|
||||
ch := sw.scanner.Peek()
|
||||
if len(name) == 0 && unicode.IsDigit(ch) {
|
||||
ch = sw.scanner.Next()
|
||||
return string(ch)
|
||||
}
|
||||
if !unicode.IsLetter(ch) && !unicode.IsDigit(ch) && ch != '_' {
|
||||
break
|
||||
}
|
||||
ch = sw.scanner.Next()
|
||||
name += string(ch)
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
func (sw *shellWord) getEnv(name string) string {
|
||||
for _, env := range sw.envs {
|
||||
i := strings.Index(env, "=")
|
||||
if i < 0 {
|
||||
if name == env {
|
||||
// Should probably never get here, but just in case treat
|
||||
// it like "var" and "var=" are the same
|
||||
return ""
|
||||
}
|
||||
continue
|
||||
}
|
||||
if name != env[:i] {
|
||||
continue
|
||||
}
|
||||
return env[i+1:]
|
||||
}
|
||||
return ""
|
||||
}
|
1
vendor/github.com/openshift/imagebuilder/signal/README.md
generated
vendored
Normal file
1
vendor/github.com/openshift/imagebuilder/signal/README.md
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
This package provides helper functions for dealing with signals across various operating systems
|
25
vendor/github.com/openshift/imagebuilder/signal/signal.go
generated
vendored
Normal file
25
vendor/github.com/openshift/imagebuilder/signal/signal.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
// Package signal provides helper functions for dealing with signals across
|
||||
// various operating systems.
|
||||
package signal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CheckSignal translates a string to a valid syscall signal.
|
||||
// It returns an error if the signal map doesn't include the given signal.
|
||||
func CheckSignal(rawSignal string) error {
|
||||
s, err := strconv.Atoi(rawSignal)
|
||||
if err == nil {
|
||||
if s == 0 {
|
||||
return fmt.Errorf("Invalid signal: %s", rawSignal)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if _, ok := SignalMap[strings.TrimPrefix(strings.ToUpper(rawSignal), "SIG")]; !ok {
|
||||
return fmt.Errorf("Invalid signal: %s", rawSignal)
|
||||
}
|
||||
return nil
|
||||
}
|
79
vendor/github.com/openshift/imagebuilder/signal/signals.go
generated
vendored
Normal file
79
vendor/github.com/openshift/imagebuilder/signal/signals.go
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
package signal
|
||||
|
||||
// SignalMap is a map of supported signals.
|
||||
var SignalMap = map[string]struct{}{
|
||||
"ABRT": {},
|
||||
"ALRM": {},
|
||||
"BUS": {},
|
||||
"CHLD": {},
|
||||
"CLD": {},
|
||||
"CONT": {},
|
||||
"FPE": {},
|
||||
"HUP": {},
|
||||
"ILL": {},
|
||||
"INT": {},
|
||||
"IO": {},
|
||||
"IOT": {},
|
||||
"KILL": {},
|
||||
"PIPE": {},
|
||||
"POLL": {},
|
||||
"PROF": {},
|
||||
"PWR": {},
|
||||
"QUIT": {},
|
||||
"SEGV": {},
|
||||
"STKFLT": {},
|
||||
"STOP": {},
|
||||
"SYS": {},
|
||||
"TERM": {},
|
||||
"TRAP": {},
|
||||
"TSTP": {},
|
||||
"TTIN": {},
|
||||
"TTOU": {},
|
||||
"UNUSED": {},
|
||||
"URG": {},
|
||||
"USR1": {},
|
||||
"USR2": {},
|
||||
"VTALRM": {},
|
||||
"WINCH": {},
|
||||
"XCPU": {},
|
||||
"XFSZ": {},
|
||||
"RTMIN": {},
|
||||
"RTMIN+1": {},
|
||||
"RTMIN+2": {},
|
||||
"RTMIN+3": {},
|
||||
"RTMIN+4": {},
|
||||
"RTMIN+5": {},
|
||||
"RTMIN+6": {},
|
||||
"RTMIN+7": {},
|
||||
"RTMIN+8": {},
|
||||
"RTMIN+9": {},
|
||||
"RTMIN+10": {},
|
||||
"RTMIN+11": {},
|
||||
"RTMIN+12": {},
|
||||
"RTMIN+13": {},
|
||||
"RTMIN+14": {},
|
||||
"RTMIN+15": {},
|
||||
"RTMAX-14": {},
|
||||
"RTMAX-13": {},
|
||||
"RTMAX-12": {},
|
||||
"RTMAX-11": {},
|
||||
"RTMAX-10": {},
|
||||
"RTMAX-9": {},
|
||||
"RTMAX-8": {},
|
||||
"RTMAX-7": {},
|
||||
"RTMAX-6": {},
|
||||
"RTMAX-5": {},
|
||||
"RTMAX-4": {},
|
||||
"RTMAX-3": {},
|
||||
"RTMAX-2": {},
|
||||
"RTMAX-1": {},
|
||||
"RTMAX": {},
|
||||
|
||||
"BUG": {},
|
||||
"EMT": {},
|
||||
"INFO": {},
|
||||
|
||||
"BUF": {},
|
||||
"LWP": {},
|
||||
"THR": {},
|
||||
}
|
30
vendor/github.com/openshift/imagebuilder/strslice/strslice.go
generated
vendored
Normal file
30
vendor/github.com/openshift/imagebuilder/strslice/strslice.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
package strslice
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// StrSlice represents a string or an array of strings.
|
||||
// We need to override the json decoder to accept both options.
|
||||
type StrSlice []string
|
||||
|
||||
// UnmarshalJSON decodes the byte slice whether it's a string or an array of
|
||||
// strings. This method is needed to implement json.Unmarshaler.
|
||||
func (e *StrSlice) UnmarshalJSON(b []byte) error {
|
||||
if len(b) == 0 {
|
||||
// With no input, we preserve the existing value by returning nil and
|
||||
// leaving the target alone. This allows defining default values for
|
||||
// the type.
|
||||
return nil
|
||||
}
|
||||
|
||||
p := make([]string, 0, 1)
|
||||
if err := json.Unmarshal(b, &p); err != nil {
|
||||
var s string
|
||||
if err := json.Unmarshal(b, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
p = append(p, s)
|
||||
}
|
||||
|
||||
*e = p
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user