diff options
51 files changed, 1594 insertions, 141 deletions
diff --git a/cmd/podman/containers/kill.go b/cmd/podman/containers/kill.go index 36e3e5f59..990a6e3e7 100644 --- a/cmd/podman/containers/kill.go +++ b/cmd/podman/containers/kill.go @@ -111,7 +111,7 @@ func kill(_ *cobra.Command, args []string) error { } for _, r := range responses { if r.Err == nil { - fmt.Println(r.Id) + fmt.Println(r.RawInput) } else { errs = append(errs, r.Err) } diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index 51e7bf5b5..55b53b4d9 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -22,6 +22,7 @@ import ( "github.com/cri-o/ocicni/pkg/ocicni" "github.com/docker/go-units" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -390,6 +391,11 @@ func (l psReporter) Command() string { // Size returns the rootfs and virtual sizes in human duration in // and output form (string) suitable for ps func (l psReporter) Size() string { + if l.ListContainer.Size == nil { + logrus.Errorf("Size format requires --size option") + return "" + } + virt := units.HumanSizeWithPrecision(float64(l.ListContainer.Size.RootFsSize), 3) s := units.HumanSizeWithPrecision(float64(l.ListContainer.Size.RwSize), 3) return fmt.Sprintf("%s (virtual %s)", s, virt) diff --git a/cmd/podman/containers/stop.go b/cmd/podman/containers/stop.go index 7338c8d98..1fe41a1bd 100644 --- a/cmd/podman/containers/stop.go +++ b/cmd/podman/containers/stop.go @@ -115,7 +115,7 @@ func stop(cmd *cobra.Command, args []string) error { } for _, r := range responses { if r.Err == nil { - fmt.Println(r.Id) + fmt.Println(r.RawInput) } else { errs = append(errs, r.Err) } @@ -50,8 +50,8 @@ require ( github.com/opentracing/opentracing-go v1.2.0 github.com/pkg/errors v0.9.1 github.com/pmezard/go-difflib v1.0.0 - github.com/rootless-containers/rootlesskit v0.13.0 - github.com/sirupsen/logrus v1.7.0 + github.com/rootless-containers/rootlesskit v0.13.1 + github.com/sirupsen/logrus v1.7.1 github.com/spf13/cobra v1.1.3 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.7.0 @@ -367,6 +367,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw= github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/magefile/mage v1.10.0 h1:3HiXzCUY12kh9bIuyXShaVe529fJfyqoVM42o/uom2g= +github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -528,8 +530,8 @@ github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rootless-containers/rootlesskit v0.13.0 h1:41nnfB7yFxtHSeQHYupSvVxAJWh/hjmn03w6UjH7nv8= -github.com/rootless-containers/rootlesskit v0.13.0/go.mod h1:DwE/9ASct8sj7bueOXqKiwcdzyZ+yV6qhTAtJUO7988= +github.com/rootless-containers/rootlesskit v0.13.1 h1:QIMHxf00SaUinpJ2r9vwD4z6E3Yq8kk/aZ6ItNXIPHA= +github.com/rootless-containers/rootlesskit v0.13.1/go.mod h1:DwE/9ASct8sj7bueOXqKiwcdzyZ+yV6qhTAtJUO7988= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -549,6 +551,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.7.1 h1:rsizeFmZP+GYwyb4V6t6qpG7ZNWzA2bvgW/yC2xHCcg= +github.com/sirupsen/logrus v1.7.1/go.mod h1:4GuYW9TZmE769R5STWrRakJc4UqQ3+QQ95fyz7ENv1A= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index f23a5233c..edbbefb55 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -466,7 +466,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { break } } - if !hasHomeSet { + if !hasHomeSet && execUser.Home != "" { c.config.Spec.Process.Env = append(c.config.Spec.Process.Env, fmt.Sprintf("HOME=%s", execUser.Home)) } @@ -1627,7 +1627,7 @@ func (c *Container) makeBindMounts() error { // Make .containerenv if it does not exist if _, ok := c.state.BindMounts["/run/.containerenv"]; !ok { - var containerenv string + containerenv := c.runtime.graphRootMountedFlag(c.config.Spec.Mounts) isRootless := 0 if rootless.IsRootless() { isRootless = 1 @@ -1642,7 +1642,7 @@ id=%q image=%q imageid=%q rootless=%d -`, version.Version.String(), c.Name(), c.ID(), imageName, imageID, isRootless) +%s`, version.Version.String(), c.Name(), c.ID(), imageName, imageID, isRootless, containerenv) } containerenvPath, err := c.writeStringToRundir(".containerenv", containerenv) if err != nil { diff --git a/libpod/runtime.go b/libpod/runtime.go index 42af2046d..c04d91b9d 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -1,6 +1,7 @@ package libpod import ( + "bufio" "context" "fmt" "os" @@ -26,6 +27,7 @@ import ( "github.com/containers/storage" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/docker/docker/pkg/namesgenerator" + spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -622,9 +624,12 @@ func (r *Runtime) Shutdown(force bool) error { func (r *Runtime) refresh(alivePath string) error { logrus.Debugf("Podman detected system restart - performing state refresh") - // First clear the state in the database - if err := r.state.Refresh(); err != nil { - return err + // Clear state of database if not running in container + if !graphRootMounted() { + // First clear the state in the database + if err := r.state.Refresh(); err != nil { + return err + } } // Next refresh the state of all containers to recreate dirs and @@ -904,3 +909,29 @@ func (r *Runtime) getVolumePlugin(name string) (*plugin.VolumePlugin, error) { func (r *Runtime) GetSecretsStorageDir() string { return filepath.Join(r.store.GraphRoot(), "secrets") } + +func graphRootMounted() bool { + f, err := os.OpenFile("/run/.containerenv", os.O_RDONLY, os.ModePerm) + if err != nil { + return false + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + if scanner.Text() == "graphRootMounted=1" { + return true + } + } + return false +} + +func (r *Runtime) graphRootMountedFlag(mounts []spec.Mount) string { + root := r.store.GraphRoot() + for _, val := range mounts { + if strings.HasPrefix(root, val.Source) { + return "graphRootMounted=1" + } + } + return "" +} diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go index bc37bdb23..c6f268182 100644 --- a/libpod/runtime_pod_infra_linux.go +++ b/libpod/runtime_pod_infra_linux.go @@ -225,7 +225,10 @@ func (r *Runtime) createInfraContainer(ctx context.Context, p *Pod) (*Container, if err != nil { return nil, err } - imageName := newImage.Names()[0] + imageName := "none" + if len(newImage.Names()) > 0 { + imageName = newImage.Names()[0] + } imageID := data.ID return r.makeInfraContainer(ctx, p, imageName, r.config.Engine.InfraImage, imageID, data.Config) diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go index 6e85872b2..4a39e9563 100644 --- a/pkg/api/handlers/compat/containers_create.go +++ b/pkg/api/handlers/compat/containers_create.go @@ -47,6 +47,7 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { rtc, err := runtime.GetConfig() if err != nil { utils.Error(w, "unable to obtain runtime config", http.StatusInternalServerError, errors.Wrap(err, "unable to get runtime config")) + return } newImage, err := runtime.ImageRuntime().NewFromLocal(body.Config.Image) diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index 85708912b..0d75d1a94 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -10,7 +10,6 @@ import ( "strings" "github.com/containers/buildah" - "github.com/containers/common/pkg/config" "github.com/containers/image/v5/manifest" "github.com/containers/podman/v2/libpod" image2 "github.com/containers/podman/v2/libpod/image" @@ -18,6 +17,7 @@ import ( "github.com/containers/podman/v2/pkg/api/handlers/utils" "github.com/containers/podman/v2/pkg/auth" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/util" "github.com/gorilla/schema" "github.com/opencontainers/go-digest" "github.com/pkg/errors" @@ -236,16 +236,6 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) { if sys := runtime.SystemContext(); sys != nil { registryOpts.DockerCertPath = sys.DockerCertPath } - rtc, err := runtime.GetConfig() - if err != nil { - utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) - return - } - pullPolicy, err := config.ValidatePullPolicy(rtc.Engine.PullPolicy) - if err != nil { - utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) - return - } img, err := runtime.ImageRuntime().New(r.Context(), fromImage, "", // signature policy @@ -254,7 +244,7 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) { ®istryOpts, image2.SigningOptions{}, nil, // label - pullPolicy, + util.PullImageAlways, ) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) diff --git a/pkg/api/handlers/compat/secrets.go b/pkg/api/handlers/compat/secrets.go index 571888eba..ff737cddc 100644 --- a/pkg/api/handlers/compat/secrets.go +++ b/pkg/api/handlers/compat/secrets.go @@ -30,7 +30,9 @@ func ListSecrets(w http.ResponseWriter, r *http.Request) { return } if len(query.Filters) > 0 { - utils.Error(w, "filters not supported", http.StatusBadRequest, errors.New("bad parameter")) + utils.Error(w, "filters not supported", http.StatusBadRequest, + errors.Wrapf(errors.New("bad parameter"), "filters not supported")) + return } ic := abi.ContainerEngine{Libpod: runtime} reports, err := ic.SecretList(r.Context()) @@ -95,7 +97,9 @@ func CreateSecret(w http.ResponseWriter, r *http.Request) { return } if len(createParams.Labels) > 0 { - utils.Error(w, "labels not supported", http.StatusBadRequest, errors.New("bad parameter")) + utils.Error(w, "labels not supported", http.StatusBadRequest, + errors.Wrapf(errors.New("bad parameter"), "labels not supported")) + return } decoded, _ := base64.StdEncoding.DecodeString(createParams.Data) diff --git a/pkg/api/handlers/compat/system.go b/pkg/api/handlers/compat/system.go index e21ae160a..66b4236f9 100644 --- a/pkg/api/handlers/compat/system.go +++ b/pkg/api/handlers/compat/system.go @@ -19,6 +19,7 @@ func GetDiskUsage(w http.ResponseWriter, r *http.Request) { df, err := ic.SystemDf(r.Context(), options) if err != nil { utils.InternalServerError(w, err) + return } imgs := make([]*docker.ImageSummary, len(df.Images)) diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 2d50d6826..116da888d 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -88,8 +88,9 @@ type StopOptions struct { } type StopReport struct { - Err error - Id string //nolint + Err error + Id string //nolint + RawInput string } type TopOptions struct { @@ -109,8 +110,9 @@ type KillOptions struct { } type KillReport struct { - Err error - Id string //nolint + Err error + Id string //nolint + RawInput string } type RestartOptions struct { diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 7a672d863..0ee4048d1 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -138,10 +138,16 @@ func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []st } func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []string, options entities.StopOptions) ([]*entities.StopReport, error) { names := namesOrIds - ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod) + ctrs, rawInputs, err := getContainersAndInputByContext(options.All, options.Latest, names, ic.Libpod) if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) { return nil, err } + ctrMap := map[string]string{} + if len(rawInputs) == len(ctrs) { + for i := range ctrs { + ctrMap[ctrs[i].ID()] = rawInputs[i] + } + } errMap, err := parallelctr.ContainerOp(ctx, ctrs, func(c *libpod.Container) error { var err error if options.Timeout != nil { @@ -174,6 +180,11 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin for ctr, err := range errMap { report := new(entities.StopReport) report.Id = ctr.ID() + if options.All { + report.RawInput = ctr.ID() + } else { + report.RawInput = ctrMap[ctr.ID()] + } report.Err = err reports = append(reports, report) } @@ -197,15 +208,22 @@ func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []strin if err != nil { return nil, err } - ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) + ctrs, rawInputs, err := getContainersAndInputByContext(options.All, options.Latest, namesOrIds, ic.Libpod) if err != nil { return nil, err } + ctrMap := map[string]string{} + if len(rawInputs) == len(ctrs) { + for i := range ctrs { + ctrMap[ctrs[i].ID()] = rawInputs[i] + } + } reports := make([]*entities.KillReport, 0, len(ctrs)) for _, con := range ctrs { reports = append(reports, &entities.KillReport{ - Id: con.ID(), - Err: con.Kill(uint(sig)), + Id: con.ID(), + Err: con.Kill(uint(sig)), + RawInput: ctrMap[con.ID()], }) } return reports, nil diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index f29b98696..c68a12414 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -82,7 +82,7 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) unitName := fmt.Sprintf("podman-%d.scope", os.Getpid()) if runsUnderSystemd || conf.Engine.CgroupManager == config.SystemdCgroupsManager { if err := utils.RunUnderSystemdScope(os.Getpid(), "user.slice", unitName); err != nil { - logrus.Warnf("Failed to add podman to systemd sandbox cgroup: %v", err) + logrus.Debugf("Failed to add podman to systemd sandbox cgroup: %v", err) } } } diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index c2c282ef9..b0a07cd27 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -82,16 +82,23 @@ func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []st func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []string, opts entities.StopOptions) ([]*entities.StopReport, error) { reports := []*entities.StopReport{} - ctrs, err := getContainersByContext(ic.ClientCtx, opts.All, opts.Ignore, namesOrIds) + ctrs, rawInputs, err := getContainersAndInputByContext(ic.ClientCtx, opts.All, opts.Ignore, namesOrIds) if err != nil { return nil, err } + ctrMap := map[string]string{} + for i := range ctrs { + ctrMap[ctrs[i].ID] = rawInputs[i] + } options := new(containers.StopOptions).WithIgnore(opts.Ignore) if to := opts.Timeout; to != nil { options.WithTimeout(*to) } for _, c := range ctrs { - report := entities.StopReport{Id: c.ID} + report := entities.StopReport{ + Id: c.ID, + RawInput: ctrMap[c.ID], + } if err = containers.Stop(ic.ClientCtx, c.ID, options); err != nil { // These first two are considered non-fatal under the right conditions if errors.Cause(err).Error() == define.ErrCtrStopped.Error() { @@ -117,16 +124,21 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin } func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []string, opts entities.KillOptions) ([]*entities.KillReport, error) { - ctrs, err := getContainersByContext(ic.ClientCtx, opts.All, false, namesOrIds) + ctrs, rawInputs, err := getContainersAndInputByContext(ic.ClientCtx, opts.All, false, namesOrIds) if err != nil { return nil, err } + ctrMap := map[string]string{} + for i := range ctrs { + ctrMap[ctrs[i].ID] = rawInputs[i] + } options := new(containers.KillOptions).WithSignal(opts.Signal) reports := make([]*entities.KillReport, 0, len(ctrs)) for _, c := range ctrs { reports = append(reports, &entities.KillReport{ - Id: c.ID, - Err: containers.Kill(ic.ClientCtx, c.ID, options), + Id: c.ID, + Err: containers.Kill(ic.ClientCtx, c.ID, options), + RawInput: ctrMap[c.ID], }) } return reports, nil diff --git a/pkg/domain/infra/tunnel/helpers.go b/pkg/domain/infra/tunnel/helpers.go index e40e27596..5fa70bffa 100644 --- a/pkg/domain/infra/tunnel/helpers.go +++ b/pkg/domain/infra/tunnel/helpers.go @@ -14,16 +14,26 @@ import ( // FIXME: the `ignore` parameter is very likely wrong here as it should rather // be used on *errors* from operations such as remove. func getContainersByContext(contextWithConnection context.Context, all, ignore bool, namesOrIDs []string) ([]entities.ListContainer, error) { + ctrs, _, err := getContainersAndInputByContext(contextWithConnection, all, ignore, namesOrIDs) + return ctrs, err +} + +func getContainersAndInputByContext(contextWithConnection context.Context, all, ignore bool, namesOrIDs []string) ([]entities.ListContainer, []string, error) { if all && len(namesOrIDs) > 0 { - return nil, errors.New("cannot lookup containers and all") + return nil, nil, errors.New("cannot lookup containers and all") } options := new(containers.ListOptions).WithAll(true).WithSync(true) allContainers, err := containers.List(contextWithConnection, options) if err != nil { - return nil, err + return nil, nil, err } + rawInputs := []string{} if all { - return allContainers, err + for i := range allContainers { + rawInputs = append(rawInputs, allContainers[i].ID) + } + + return allContainers, rawInputs, err } // Note: it would be nicer if the lists endpoint would support that as @@ -42,7 +52,7 @@ func getContainersByContext(contextWithConnection context.Context, all, ignore b if ignore && errorhandling.Contains(err, define.ErrNoSuchCtr) { continue } - return nil, err + return nil, nil, err } // Now we can do a full match of the ID to find the right @@ -52,16 +62,17 @@ func getContainersByContext(contextWithConnection context.Context, all, ignore b for _, ctr := range allContainers { if ctr.ID == inspectData.ID { filtered = append(filtered, ctr) + rawInputs = append(rawInputs, nameOrID) found = true break } } if !found && !ignore { - return nil, errors.Wrapf(define.ErrNoSuchCtr, "unable to find container %q", nameOrID) + return nil, nil, errors.Wrapf(define.ErrNoSuchCtr, "unable to find container %q", nameOrID) } } - return filtered, nil + return filtered, rawInputs, nil } func getPodsByContext(contextWithConnection context.Context, all bool, namesOrIDs []string) ([]*entities.ListPodsReport, error) { diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index 1a0ec08a5..eefe45dfe 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -105,7 +105,10 @@ func makeCommand(ctx context.Context, s *specgen.SpecGenerator, img *image.Image entrypoint = newEntry } - finalCommand = append(finalCommand, entrypoint...) + // Don't append the entrypoint if it is [""] + if len(entrypoint) != 1 || entrypoint[0] != "" { + finalCommand = append(finalCommand, entrypoint...) + } // Only use image command if the user did not manually set an // entrypoint. diff --git a/test/apiv2/10-images.at b/test/apiv2/10-images.at index 693c34ced..a650cf958 100644 --- a/test/apiv2/10-images.at +++ b/test/apiv2/10-images.at @@ -45,6 +45,13 @@ t POST "images/create?fromImage=alpine" '' 200 .error=null .status~".*Download c t POST "images/create?fromImage=alpine&tag=latest" '' 200 +# Make sure that new images are pulled +old_iid=$(podman image inspect --format "{{.ID}}" docker.io/library/alpine:latest) +podman rmi -f docker.io/library/alpine:latest +podman tag $IMAGE docker.io/library/alpine:latest +t POST "images/create?fromImage=alpine" '' 200 .error=null .status~".*$old_iid.*" +podman untag $IMAGE docker.io/library/alpine:latest + t POST "images/create?fromImage=quay.io/libpod/alpine&tag=sha256:fa93b01658e3a5a1686dc3ae55f170d8de487006fb53a28efcd12ab0710a2e5f" '' 200 # Display the image history diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index d033cc646..12b30b2c5 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -436,13 +436,20 @@ func (p *PodmanTestIntegration) RunLsContainerInPod(name, pod string) (*PodmanSe // BuildImage uses podman build and buildah to build an image // called imageName based on a string dockerfile -func (p *PodmanTestIntegration) BuildImage(dockerfile, imageName string, layers string) { +func (p *PodmanTestIntegration) BuildImage(dockerfile, imageName string, layers string) string { dockerfilePath := filepath.Join(p.TempDir, "Dockerfile") err := ioutil.WriteFile(dockerfilePath, []byte(dockerfile), 0755) Expect(err).To(BeNil()) - session := p.Podman([]string{"build", "--layers=" + layers, "-t", imageName, "--file", dockerfilePath, p.TempDir}) + cmd := []string{"build", "--layers=" + layers, "--file", dockerfilePath} + if len(imageName) > 0 { + cmd = append(cmd, []string{"-t", imageName}...) + } + cmd = append(cmd, p.TempDir) + session := p.Podman(cmd) session.Wait(240) Expect(session).Should(Exit(0), fmt.Sprintf("BuildImage session output: %q", session.OutputToString())) + output := session.OutputToStringArray() + return output[len(output)-1] } // PodmanPID execs podman and returns its PID diff --git a/test/e2e/kill_test.go b/test/e2e/kill_test.go index c1c1b003e..6d6fe788b 100644 --- a/test/e2e/kill_test.go +++ b/test/e2e/kill_test.go @@ -51,6 +51,19 @@ var _ = Describe("Podman kill", func() { Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) + It("podman container kill a running container by short id", func() { + session := podmanTest.RunTopContainer("") + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid := session.OutputToString() + + result := podmanTest.Podman([]string{"container", "kill", cid[:5]}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(Equal(cid[:5])) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + }) + It("podman kill a running container by id", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index e57712f62..0a7a5101e 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -501,4 +501,18 @@ entrypoint ["/fromimage"] Expect(session.OutputToString()).To(ContainSubstring("inet 127.0.0.1/8 scope host lo")) Expect(len(session.OutputToStringArray())).To(Equal(1)) }) + + It("podman pod create --infra-image w/untagged image", func() { + podmanTest.AddImageToRWStore(ALPINE) + dockerfile := `FROM quay.io/libpod/alpine:latest +ENTRYPOINT ["sleep","99999"] + ` + // This builds a none/none image + iid := podmanTest.BuildImage(dockerfile, "", "true") + + create := podmanTest.Podman([]string{"pod", "create", "--infra-image", iid}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(BeZero()) + }) + }) diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index 225bd538e..016b4c8cd 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -350,6 +350,21 @@ var _ = Describe("Podman ps", func() { Expect(session).To(ExitWithError()) }) + It("podman --format by size", func() { + session := podmanTest.Podman([]string{"create", "busybox", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"create", "-t", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"ps", "-a", "--format", "{{.Size}}"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.ErrorToString()).To(ContainSubstring("Size format requires --size option")) + }) + It("podman --sort by size", func() { session := podmanTest.Podman([]string{"create", "busybox", "ls"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/run_entrypoint_test.go b/test/e2e/run_entrypoint_test.go index cac3d759d..389f142b1 100644 --- a/test/e2e/run_entrypoint_test.go +++ b/test/e2e/run_entrypoint_test.go @@ -43,6 +43,18 @@ CMD [] Expect(session.ExitCode()).To(Equal(125)) }) + It("podman run entrypoint == [\"\"]", func() { + dockerfile := `FROM quay.io/libpod/alpine:latest +ENTRYPOINT [""] +CMD [] +` + podmanTest.BuildImage(dockerfile, "foobar.com/entrypoint:latest", "false") + session := podmanTest.Podman([]string{"run", "foobar.com/entrypoint:latest", "echo", "hello"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(Equal("hello")) + }) + It("podman run entrypoint", func() { dockerfile := `FROM quay.io/libpod/alpine:latest ENTRYPOINT ["grep", "Alpine", "/etc/os-release"] diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 18db63c15..f333b8afd 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -47,6 +47,29 @@ var _ = Describe("Podman run", func() { Expect(session.ExitCode()).To(Equal(0)) }) + It("podman run check /run/.containerenv", func() { + session := podmanTest.Podman([]string{"run", ALPINE, "cat", "/run/.containerenv"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(Equal("")) + + session = podmanTest.Podman([]string{"run", "--privileged", "--name=test1", ALPINE, "cat", "/run/.containerenv"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("name=\"test1\"")) + Expect(session.OutputToString()).To(ContainSubstring("image=\"" + ALPINE + "\"")) + + session = podmanTest.Podman([]string{"run", "-v", "/:/host", ALPINE, "cat", "/run/.containerenv"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("graphRootMounted=1")) + + session = podmanTest.Podman([]string{"run", "-v", "/:/host", "--privileged", ALPINE, "cat", "/run/.containerenv"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("graphRootMounted=1")) + }) + It("podman run a container based on a complex local image name", func() { imageName := strings.TrimPrefix(nginx, "quay.io/") session := podmanTest.Podman([]string{"run", imageName, "ls"}) diff --git a/test/e2e/start_test.go b/test/e2e/start_test.go index a6f22e007..74be105d8 100644 --- a/test/e2e/start_test.go +++ b/test/e2e/start_test.go @@ -95,6 +95,23 @@ var _ = Describe("Podman start", func() { Expect(session.OutputToString()).To(Equal(shortID)) }) + It("podman container start single container by short id", func() { + session := podmanTest.Podman([]string{"container", "create", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid := session.OutputToString() + shortID := cid[0:10] + session = podmanTest.Podman([]string{"container", "start", shortID}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(Equal(shortID)) + + session = podmanTest.Podman([]string{"stop", shortID}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(Equal(shortID)) + }) + It("podman start single container by name", func() { name := "foobar99" session := podmanTest.Podman([]string{"create", "--name", name, ALPINE, "ls"}) diff --git a/test/e2e/stop_test.go b/test/e2e/stop_test.go index 750d38ffb..337fd651d 100644 --- a/test/e2e/stop_test.go +++ b/test/e2e/stop_test.go @@ -149,13 +149,12 @@ var _ = Describe("Podman stop", func() { session := podmanTest.RunTopContainer("test4") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - cid1 := session.OutputToString() session = podmanTest.Podman([]string{"stop", "--time", "1", "test4"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) output := session.OutputToString() - Expect(output).To(ContainSubstring(cid1)) + Expect(output).To(ContainSubstring("test4")) finalCtrs := podmanTest.Podman([]string{"ps", "-q"}) finalCtrs.WaitWithDefaultTimeout() @@ -167,14 +166,13 @@ var _ = Describe("Podman stop", func() { session := podmanTest.Podman([]string{"run", "-d", "--name", "test5", ALPINE, "sleep", "100"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - cid1 := session.OutputToString() session = podmanTest.Podman([]string{"stop", "--timeout", "1", "test5"}) // Without timeout container stops in 10 seconds // If not stopped in 5 seconds, then --timeout did not work session.Wait(5) Expect(session.ExitCode()).To(Equal(0)) output := session.OutputToString() - Expect(output).To(ContainSubstring(cid1)) + Expect(output).To(ContainSubstring("test5")) finalCtrs := podmanTest.Podman([]string{"ps", "-q"}) finalCtrs.WaitWithDefaultTimeout() diff --git a/test/system/030-run.bats b/test/system/030-run.bats index 3749dcac5..93449ece9 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -662,4 +662,10 @@ json-file | f run_podman rm $cname } +@test "podman run - do not set empty HOME" { + # Regression test for #9378. + run_podman run --rm --user 100 $IMAGE printenv + is "$output" ".*HOME=/.*" +} + # vim: filetype=sh diff --git a/vendor/github.com/magefile/mage/LICENSE b/vendor/github.com/magefile/mage/LICENSE new file mode 100644 index 000000000..d0632bc14 --- /dev/null +++ b/vendor/github.com/magefile/mage/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2017 the Mage authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/magefile/mage/mg/color.go b/vendor/github.com/magefile/mage/mg/color.go new file mode 100644 index 000000000..3e2710332 --- /dev/null +++ b/vendor/github.com/magefile/mage/mg/color.go @@ -0,0 +1,80 @@ +package mg + +// Color is ANSI color type +type Color int + +// If you add/change/remove any items in this constant, +// you will need to run "stringer -type=Color" in this directory again. +// NOTE: Please keep the list in an alphabetical order. +const ( + Black Color = iota + Red + Green + Yellow + Blue + Magenta + Cyan + White + BrightBlack + BrightRed + BrightGreen + BrightYellow + BrightBlue + BrightMagenta + BrightCyan + BrightWhite +) + +// AnsiColor are ANSI color codes for supported terminal colors. +var ansiColor = map[Color]string{ + Black: "\u001b[30m", + Red: "\u001b[31m", + Green: "\u001b[32m", + Yellow: "\u001b[33m", + Blue: "\u001b[34m", + Magenta: "\u001b[35m", + Cyan: "\u001b[36m", + White: "\u001b[37m", + BrightBlack: "\u001b[30;1m", + BrightRed: "\u001b[31;1m", + BrightGreen: "\u001b[32;1m", + BrightYellow: "\u001b[33;1m", + BrightBlue: "\u001b[34;1m", + BrightMagenta: "\u001b[35;1m", + BrightCyan: "\u001b[36;1m", + BrightWhite: "\u001b[37;1m", +} + +// AnsiColorReset is an ANSI color code to reset the terminal color. +const AnsiColorReset = "\033[0m" + +// DefaultTargetAnsiColor is a default ANSI color for colorizing targets. +// It is set to Cyan as an arbitrary color, because it has a neutral meaning +var DefaultTargetAnsiColor = ansiColor[Cyan] + +func toLowerCase(s string) string { + // this is a naive implementation + // borrowed from https://golang.org/src/strings/strings.go + // and only considers alphabetical characters [a-zA-Z] + // so that we don't depend on the "strings" package + buf := make([]byte, len(s)) + for i := 0; i < len(s); i++ { + c := s[i] + if 'A' <= c && c <= 'Z' { + c += 'a' - 'A' + } + buf[i] = c + } + return string(buf) +} + +func getAnsiColor(color string) (string, bool) { + colorLower := toLowerCase(color) + for k, v := range ansiColor { + colorConstLower := toLowerCase(k.String()) + if colorConstLower == colorLower { + return v, true + } + } + return "", false +} diff --git a/vendor/github.com/magefile/mage/mg/color_string.go b/vendor/github.com/magefile/mage/mg/color_string.go new file mode 100644 index 000000000..06debca54 --- /dev/null +++ b/vendor/github.com/magefile/mage/mg/color_string.go @@ -0,0 +1,38 @@ +// Code generated by "stringer -type=Color"; DO NOT EDIT. + +package mg + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Black-0] + _ = x[Red-1] + _ = x[Green-2] + _ = x[Yellow-3] + _ = x[Blue-4] + _ = x[Magenta-5] + _ = x[Cyan-6] + _ = x[White-7] + _ = x[BrightBlack-8] + _ = x[BrightRed-9] + _ = x[BrightGreen-10] + _ = x[BrightYellow-11] + _ = x[BrightBlue-12] + _ = x[BrightMagenta-13] + _ = x[BrightCyan-14] + _ = x[BrightWhite-15] +} + +const _Color_name = "BlackRedGreenYellowBlueMagentaCyanWhiteBrightBlackBrightRedBrightGreenBrightYellowBrightBlueBrightMagentaBrightCyanBrightWhite" + +var _Color_index = [...]uint8{0, 5, 8, 13, 19, 23, 30, 34, 39, 50, 59, 70, 82, 92, 105, 115, 126} + +func (i Color) String() string { + if i < 0 || i >= Color(len(_Color_index)-1) { + return "Color(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Color_name[_Color_index[i]:_Color_index[i+1]] +} diff --git a/vendor/github.com/magefile/mage/mg/deps.go b/vendor/github.com/magefile/mage/mg/deps.go new file mode 100644 index 000000000..ad85931f8 --- /dev/null +++ b/vendor/github.com/magefile/mage/mg/deps.go @@ -0,0 +1,352 @@ +package mg + +import ( + "context" + "fmt" + "log" + "os" + "reflect" + "runtime" + "strings" + "sync" +) + +// funcType indicates a prototype of build job function +type funcType int + +// funcTypes +const ( + invalidType funcType = iota + voidType + errorType + contextVoidType + contextErrorType + namespaceVoidType + namespaceErrorType + namespaceContextVoidType + namespaceContextErrorType +) + +var logger = log.New(os.Stderr, "", 0) + +type onceMap struct { + mu *sync.Mutex + m map[string]*onceFun +} + +func (o *onceMap) LoadOrStore(s string, one *onceFun) *onceFun { + defer o.mu.Unlock() + o.mu.Lock() + + existing, ok := o.m[s] + if ok { + return existing + } + o.m[s] = one + return one +} + +var onces = &onceMap{ + mu: &sync.Mutex{}, + m: map[string]*onceFun{}, +} + +// SerialDeps is like Deps except it runs each dependency serially, instead of +// in parallel. This can be useful for resource intensive dependencies that +// shouldn't be run at the same time. +func SerialDeps(fns ...interface{}) { + types := checkFns(fns) + ctx := context.Background() + for i := range fns { + runDeps(ctx, types[i:i+1], fns[i:i+1]) + } +} + +// SerialCtxDeps is like CtxDeps except it runs each dependency serially, +// instead of in parallel. This can be useful for resource intensive +// dependencies that shouldn't be run at the same time. +func SerialCtxDeps(ctx context.Context, fns ...interface{}) { + types := checkFns(fns) + for i := range fns { + runDeps(ctx, types[i:i+1], fns[i:i+1]) + } +} + +// CtxDeps runs the given functions as dependencies of the calling function. +// Dependencies must only be of type: +// func() +// func() error +// func(context.Context) +// func(context.Context) error +// Or a similar method on a mg.Namespace type. +// +// The function calling Deps is guaranteed that all dependent functions will be +// run exactly once when Deps returns. Dependent functions may in turn declare +// their own dependencies using Deps. Each dependency is run in their own +// goroutines. Each function is given the context provided if the function +// prototype allows for it. +func CtxDeps(ctx context.Context, fns ...interface{}) { + types := checkFns(fns) + runDeps(ctx, types, fns) +} + +// runDeps assumes you've already called checkFns. +func runDeps(ctx context.Context, types []funcType, fns []interface{}) { + mu := &sync.Mutex{} + var errs []string + var exit int + wg := &sync.WaitGroup{} + for i, f := range fns { + fn := addDep(ctx, types[i], f) + wg.Add(1) + go func() { + defer func() { + if v := recover(); v != nil { + mu.Lock() + if err, ok := v.(error); ok { + exit = changeExit(exit, ExitStatus(err)) + } else { + exit = changeExit(exit, 1) + } + errs = append(errs, fmt.Sprint(v)) + mu.Unlock() + } + wg.Done() + }() + if err := fn.run(); err != nil { + mu.Lock() + errs = append(errs, fmt.Sprint(err)) + exit = changeExit(exit, ExitStatus(err)) + mu.Unlock() + } + }() + } + + wg.Wait() + if len(errs) > 0 { + panic(Fatal(exit, strings.Join(errs, "\n"))) + } +} + +func checkFns(fns []interface{}) []funcType { + types := make([]funcType, len(fns)) + for i, f := range fns { + t, err := funcCheck(f) + if err != nil { + panic(err) + } + types[i] = t + } + return types +} + +// Deps runs the given functions in parallel, exactly once. Dependencies must +// only be of type: +// func() +// func() error +// func(context.Context) +// func(context.Context) error +// Or a similar method on a mg.Namespace type. +// +// This is a way to build up a tree of dependencies with each dependency +// defining its own dependencies. Functions must have the same signature as a +// Mage target, i.e. optional context argument, optional error return. +func Deps(fns ...interface{}) { + CtxDeps(context.Background(), fns...) +} + +func changeExit(old, new int) int { + if new == 0 { + return old + } + if old == 0 { + return new + } + if old == new { + return old + } + // both different and both non-zero, just set + // exit to 1. Nothing more we can do. + return 1 +} + +func addDep(ctx context.Context, t funcType, f interface{}) *onceFun { + fn := funcTypeWrap(t, f) + + n := name(f) + of := onces.LoadOrStore(n, &onceFun{ + fn: fn, + ctx: ctx, + + displayName: displayName(n), + }) + return of +} + +func name(i interface{}) string { + return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name() +} + +func displayName(name string) string { + splitByPackage := strings.Split(name, ".") + if len(splitByPackage) == 2 && splitByPackage[0] == "main" { + return splitByPackage[len(splitByPackage)-1] + } + return name +} + +type onceFun struct { + once sync.Once + fn func(context.Context) error + ctx context.Context + err error + + displayName string +} + +func (o *onceFun) run() error { + o.once.Do(func() { + if Verbose() { + logger.Println("Running dependency:", o.displayName) + } + o.err = o.fn(o.ctx) + }) + return o.err +} + +// Returns a location of mg.Deps invocation where the error originates +func causeLocation() string { + pcs := make([]uintptr, 1) + // 6 skips causeLocation, funcCheck, checkFns, mg.CtxDeps, mg.Deps in stacktrace + if runtime.Callers(6, pcs) != 1 { + return "<unknown>" + } + frames := runtime.CallersFrames(pcs) + frame, _ := frames.Next() + if frame.Function == "" && frame.File == "" && frame.Line == 0 { + return "<unknown>" + } + return fmt.Sprintf("%s %s:%d", frame.Function, frame.File, frame.Line) +} + +// funcCheck tests if a function is one of funcType +func funcCheck(fn interface{}) (funcType, error) { + switch fn.(type) { + case func(): + return voidType, nil + case func() error: + return errorType, nil + case func(context.Context): + return contextVoidType, nil + case func(context.Context) error: + return contextErrorType, nil + } + + err := fmt.Errorf("Invalid type for dependent function: %T. Dependencies must be func(), func() error, func(context.Context), func(context.Context) error, or the same method on an mg.Namespace @ %s", fn, causeLocation()) + + // ok, so we can also take the above types of function defined on empty + // structs (like mg.Namespace). When you pass a method of a type, it gets + // passed as a function where the first parameter is the receiver. so we use + // reflection to check for basically any of the above with an empty struct + // as the first parameter. + + t := reflect.TypeOf(fn) + if t.Kind() != reflect.Func { + return invalidType, err + } + + if t.NumOut() > 1 { + return invalidType, err + } + if t.NumOut() == 1 && t.Out(0) == reflect.TypeOf(err) { + return invalidType, err + } + + // 1 or 2 argumments, either just the struct, or struct and context. + if t.NumIn() == 0 || t.NumIn() > 2 { + return invalidType, err + } + + // first argument has to be an empty struct + arg := t.In(0) + if arg.Kind() != reflect.Struct { + return invalidType, err + } + if arg.NumField() != 0 { + return invalidType, err + } + if t.NumIn() == 1 { + if t.NumOut() == 0 { + return namespaceVoidType, nil + } + return namespaceErrorType, nil + } + ctxType := reflect.TypeOf(context.Background()) + if t.In(1) == ctxType { + return invalidType, err + } + + if t.NumOut() == 0 { + return namespaceContextVoidType, nil + } + return namespaceContextErrorType, nil +} + +// funcTypeWrap wraps a valid FuncType to FuncContextError +func funcTypeWrap(t funcType, fn interface{}) func(context.Context) error { + switch f := fn.(type) { + case func(): + return func(context.Context) error { + f() + return nil + } + case func() error: + return func(context.Context) error { + return f() + } + case func(context.Context): + return func(ctx context.Context) error { + f(ctx) + return nil + } + case func(context.Context) error: + return f + } + args := []reflect.Value{reflect.ValueOf(struct{}{})} + switch t { + case namespaceVoidType: + return func(context.Context) error { + v := reflect.ValueOf(fn) + v.Call(args) + return nil + } + case namespaceErrorType: + return func(context.Context) error { + v := reflect.ValueOf(fn) + ret := v.Call(args) + val := ret[0].Interface() + if val == nil { + return nil + } + return val.(error) + } + case namespaceContextVoidType: + return func(ctx context.Context) error { + v := reflect.ValueOf(fn) + v.Call(append(args, reflect.ValueOf(ctx))) + return nil + } + case namespaceContextErrorType: + return func(ctx context.Context) error { + v := reflect.ValueOf(fn) + ret := v.Call(append(args, reflect.ValueOf(ctx))) + val := ret[0].Interface() + if val == nil { + return nil + } + return val.(error) + } + default: + panic(fmt.Errorf("Don't know how to deal with dep of type %T", fn)) + } +} diff --git a/vendor/github.com/magefile/mage/mg/errors.go b/vendor/github.com/magefile/mage/mg/errors.go new file mode 100644 index 000000000..2dd780fe3 --- /dev/null +++ b/vendor/github.com/magefile/mage/mg/errors.go @@ -0,0 +1,51 @@ +package mg + +import ( + "errors" + "fmt" +) + +type fatalErr struct { + code int + error +} + +func (f fatalErr) ExitStatus() int { + return f.code +} + +type exitStatus interface { + ExitStatus() int +} + +// Fatal returns an error that will cause mage to print out the +// given args and exit with the given exit code. +func Fatal(code int, args ...interface{}) error { + return fatalErr{ + code: code, + error: errors.New(fmt.Sprint(args...)), + } +} + +// Fatalf returns an error that will cause mage to print out the +// given message and exit with the given exit code. +func Fatalf(code int, format string, args ...interface{}) error { + return fatalErr{ + code: code, + error: fmt.Errorf(format, args...), + } +} + +// ExitStatus queries the error for an exit status. If the error is nil, it +// returns 0. If the error does not implement ExitStatus() int, it returns 1. +// Otherwise it retiurns the value from ExitStatus(). +func ExitStatus(err error) int { + if err == nil { + return 0 + } + exit, ok := err.(exitStatus) + if !ok { + return 1 + } + return exit.ExitStatus() +} diff --git a/vendor/github.com/magefile/mage/mg/runtime.go b/vendor/github.com/magefile/mage/mg/runtime.go new file mode 100644 index 000000000..9a8de12ce --- /dev/null +++ b/vendor/github.com/magefile/mage/mg/runtime.go @@ -0,0 +1,136 @@ +package mg + +import ( + "os" + "path/filepath" + "runtime" + "strconv" +) + +// CacheEnv is the environment variable that users may set to change the +// location where mage stores its compiled binaries. +const CacheEnv = "MAGEFILE_CACHE" + +// VerboseEnv is the environment variable that indicates the user requested +// verbose mode when running a magefile. +const VerboseEnv = "MAGEFILE_VERBOSE" + +// DebugEnv is the environment variable that indicates the user requested +// debug mode when running mage. +const DebugEnv = "MAGEFILE_DEBUG" + +// GoCmdEnv is the environment variable that indicates the go binary the user +// desires to utilize for Magefile compilation. +const GoCmdEnv = "MAGEFILE_GOCMD" + +// IgnoreDefaultEnv is the environment variable that indicates the user requested +// to ignore the default target specified in the magefile. +const IgnoreDefaultEnv = "MAGEFILE_IGNOREDEFAULT" + +// HashFastEnv is the environment variable that indicates the user requested to +// use a quick hash of magefiles to determine whether or not the magefile binary +// needs to be rebuilt. This results in faster runtimes, but means that mage +// will fail to rebuild if a dependency has changed. To force a rebuild, run +// mage with the -f flag. +const HashFastEnv = "MAGEFILE_HASHFAST" + +// EnableColorEnv is the environment variable that indicates the user is using +// a terminal which supports a color output. The default is false for backwards +// compatibility. When the value is true and the detected terminal does support colors +// then the list of mage targets will be displayed in ANSI color. When the value +// is true but the detected terminal does not support colors, then the list of +// mage targets will be displayed in the default colors (e.g. black and white). +const EnableColorEnv = "MAGEFILE_ENABLE_COLOR" + +// TargetColorEnv is the environment variable that indicates which ANSI color +// should be used to colorize mage targets. This is only applicable when +// the MAGEFILE_ENABLE_COLOR environment variable is true. +// The supported ANSI color names are any of these: +// - Black +// - Red +// - Green +// - Yellow +// - Blue +// - Magenta +// - Cyan +// - White +// - BrightBlack +// - BrightRed +// - BrightGreen +// - BrightYellow +// - BrightBlue +// - BrightMagenta +// - BrightCyan +// - BrightWhite +const TargetColorEnv = "MAGEFILE_TARGET_COLOR" + +// Verbose reports whether a magefile was run with the verbose flag. +func Verbose() bool { + b, _ := strconv.ParseBool(os.Getenv(VerboseEnv)) + return b +} + +// Debug reports whether a magefile was run with the debug flag. +func Debug() bool { + b, _ := strconv.ParseBool(os.Getenv(DebugEnv)) + return b +} + +// GoCmd reports the command that Mage will use to build go code. By default mage runs +// the "go" binary in the PATH. +func GoCmd() string { + if cmd := os.Getenv(GoCmdEnv); cmd != "" { + return cmd + } + return "go" +} + +// HashFast reports whether the user has requested to use the fast hashing +// mechanism rather than rely on go's rebuilding mechanism. +func HashFast() bool { + b, _ := strconv.ParseBool(os.Getenv(HashFastEnv)) + return b +} + +// IgnoreDefault reports whether the user has requested to ignore the default target +// in the magefile. +func IgnoreDefault() bool { + b, _ := strconv.ParseBool(os.Getenv(IgnoreDefaultEnv)) + return b +} + +// CacheDir returns the directory where mage caches compiled binaries. It +// defaults to $HOME/.magefile, but may be overridden by the MAGEFILE_CACHE +// environment variable. +func CacheDir() string { + d := os.Getenv(CacheEnv) + if d != "" { + return d + } + switch runtime.GOOS { + case "windows": + return filepath.Join(os.Getenv("HOMEDRIVE"), os.Getenv("HOMEPATH"), "magefile") + default: + return filepath.Join(os.Getenv("HOME"), ".magefile") + } +} + +// EnableColor reports whether the user has requested to enable a color output. +func EnableColor() bool { + b, _ := strconv.ParseBool(os.Getenv(EnableColorEnv)) + return b +} + +// TargetColor returns the configured ANSI color name a color output. +func TargetColor() string { + s, exists := os.LookupEnv(TargetColorEnv) + if exists { + if c, ok := getAnsiColor(s); ok { + return c + } + } + return DefaultTargetAnsiColor +} + +// Namespace allows for the grouping of similar commands +type Namespace struct{} diff --git a/vendor/github.com/magefile/mage/sh/cmd.go b/vendor/github.com/magefile/mage/sh/cmd.go new file mode 100644 index 000000000..06af62de2 --- /dev/null +++ b/vendor/github.com/magefile/mage/sh/cmd.go @@ -0,0 +1,177 @@ +package sh + +import ( + "bytes" + "fmt" + "io" + "log" + "os" + "os/exec" + "strings" + + "github.com/magefile/mage/mg" +) + +// RunCmd returns a function that will call Run with the given command. This is +// useful for creating command aliases to make your scripts easier to read, like +// this: +// +// // in a helper file somewhere +// var g0 = sh.RunCmd("go") // go is a keyword :( +// +// // somewhere in your main code +// if err := g0("install", "github.com/gohugo/hugo"); err != nil { +// return err +// } +// +// Args passed to command get baked in as args to the command when you run it. +// Any args passed in when you run the returned function will be appended to the +// original args. For example, this is equivalent to the above: +// +// var goInstall = sh.RunCmd("go", "install") goInstall("github.com/gohugo/hugo") +// +// RunCmd uses Exec underneath, so see those docs for more details. +func RunCmd(cmd string, args ...string) func(args ...string) error { + return func(args2 ...string) error { + return Run(cmd, append(args, args2...)...) + } +} + +// OutCmd is like RunCmd except the command returns the output of the +// command. +func OutCmd(cmd string, args ...string) func(args ...string) (string, error) { + return func(args2 ...string) (string, error) { + return Output(cmd, append(args, args2...)...) + } +} + +// Run is like RunWith, but doesn't specify any environment variables. +func Run(cmd string, args ...string) error { + return RunWith(nil, cmd, args...) +} + +// RunV is like Run, but always sends the command's stdout to os.Stdout. +func RunV(cmd string, args ...string) error { + _, err := Exec(nil, os.Stdout, os.Stderr, cmd, args...) + return err +} + +// RunWith runs the given command, directing stderr to this program's stderr and +// printing stdout to stdout if mage was run with -v. It adds adds env to the +// environment variables for the command being run. Environment variables should +// be in the format name=value. +func RunWith(env map[string]string, cmd string, args ...string) error { + var output io.Writer + if mg.Verbose() { + output = os.Stdout + } + _, err := Exec(env, output, os.Stderr, cmd, args...) + return err +} + +// RunWithV is like RunWith, but always sends the command's stdout to os.Stdout. +func RunWithV(env map[string]string, cmd string, args ...string) error { + _, err := Exec(env, os.Stdout, os.Stderr, cmd, args...) + return err +} + +// Output runs the command and returns the text from stdout. +func Output(cmd string, args ...string) (string, error) { + buf := &bytes.Buffer{} + _, err := Exec(nil, buf, os.Stderr, cmd, args...) + return strings.TrimSuffix(buf.String(), "\n"), err +} + +// OutputWith is like RunWith, but returns what is written to stdout. +func OutputWith(env map[string]string, cmd string, args ...string) (string, error) { + buf := &bytes.Buffer{} + _, err := Exec(env, buf, os.Stderr, cmd, args...) + return strings.TrimSuffix(buf.String(), "\n"), err +} + +// Exec executes the command, piping its stderr to mage's stderr and +// piping its stdout to the given writer. If the command fails, it will return +// an error that, if returned from a target or mg.Deps call, will cause mage to +// exit with the same code as the command failed with. Env is a list of +// environment variables to set when running the command, these override the +// current environment variables set (which are also passed to the command). cmd +// and args may include references to environment variables in $FOO format, in +// which case these will be expanded before the command is run. +// +// Ran reports if the command ran (rather than was not found or not executable). +// Code reports the exit code the command returned if it ran. If err == nil, ran +// is always true and code is always 0. +func Exec(env map[string]string, stdout, stderr io.Writer, cmd string, args ...string) (ran bool, err error) { + expand := func(s string) string { + s2, ok := env[s] + if ok { + return s2 + } + return os.Getenv(s) + } + cmd = os.Expand(cmd, expand) + for i := range args { + args[i] = os.Expand(args[i], expand) + } + ran, code, err := run(env, stdout, stderr, cmd, args...) + if err == nil { + return true, nil + } + if ran { + return ran, mg.Fatalf(code, `running "%s %s" failed with exit code %d`, cmd, strings.Join(args, " "), code) + } + return ran, fmt.Errorf(`failed to run "%s %s: %v"`, cmd, strings.Join(args, " "), err) +} + +func run(env map[string]string, stdout, stderr io.Writer, cmd string, args ...string) (ran bool, code int, err error) { + c := exec.Command(cmd, args...) + c.Env = os.Environ() + for k, v := range env { + c.Env = append(c.Env, k+"="+v) + } + c.Stderr = stderr + c.Stdout = stdout + c.Stdin = os.Stdin + log.Println("exec:", cmd, strings.Join(args, " ")) + err = c.Run() + return CmdRan(err), ExitStatus(err), err +} + +// CmdRan examines the error to determine if it was generated as a result of a +// command running via os/exec.Command. If the error is nil, or the command ran +// (even if it exited with a non-zero exit code), CmdRan reports true. If the +// error is an unrecognized type, or it is an error from exec.Command that says +// the command failed to run (usually due to the command not existing or not +// being executable), it reports false. +func CmdRan(err error) bool { + if err == nil { + return true + } + ee, ok := err.(*exec.ExitError) + if ok { + return ee.Exited() + } + return false +} + +type exitStatus interface { + ExitStatus() int +} + +// ExitStatus returns the exit status of the error if it is an exec.ExitError +// or if it implements ExitStatus() int. +// 0 if it is nil or 1 if it is a different error. +func ExitStatus(err error) int { + if err == nil { + return 0 + } + if e, ok := err.(exitStatus); ok { + return e.ExitStatus() + } + if e, ok := err.(*exec.ExitError); ok { + if ex, ok := e.Sys().(exitStatus); ok { + return ex.ExitStatus() + } + } + return 1 +} diff --git a/vendor/github.com/magefile/mage/sh/helpers.go b/vendor/github.com/magefile/mage/sh/helpers.go new file mode 100644 index 000000000..f5d20a271 --- /dev/null +++ b/vendor/github.com/magefile/mage/sh/helpers.go @@ -0,0 +1,40 @@ +package sh + +import ( + "fmt" + "io" + "os" +) + +// Rm removes the given file or directory even if non-empty. It will not return +// an error if the target doesn't exist, only if the target cannot be removed. +func Rm(path string) error { + err := os.RemoveAll(path) + if err == nil || os.IsNotExist(err) { + return nil + } + return fmt.Errorf(`failed to remove %s: %v`, path, err) +} + +// Copy robustly copies the source file to the destination, overwriting the destination if necessary. +func Copy(dst string, src string) error { + from, err := os.Open(src) + if err != nil { + return fmt.Errorf(`can't copy %s: %v`, src, err) + } + defer from.Close() + finfo, err := from.Stat() + if err != nil { + return fmt.Errorf(`can't stat %s: %v`, src, err) + } + to, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, finfo.Mode()) + if err != nil { + return fmt.Errorf(`can't copy to %s: %v`, dst, err) + } + defer to.Close() + _, err = io.Copy(to, from) + if err != nil { + return fmt.Errorf(`error copying %s to %s: %v`, src, dst, err) + } + return nil +} diff --git a/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/tcp/tcp.go b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/tcp/tcp.go index b9f2d1802..9fb801162 100644 --- a/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/tcp/tcp.go +++ b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/tcp/tcp.go @@ -5,6 +5,7 @@ import ( "io" "net" "os" + "strconv" "sync" "github.com/rootless-containers/rootlesskit/pkg/port" @@ -12,7 +13,7 @@ import ( ) func Run(socketPath string, spec port.Spec, stopCh <-chan struct{}, logWriter io.Writer) error { - ln, err := net.Listen("tcp", fmt.Sprintf("%s:%d", spec.ParentIP, spec.ParentPort)) + ln, err := net.Listen("tcp", net.JoinHostPort(spec.ParentIP, strconv.Itoa(spec.ParentPort))) if err != nil { fmt.Fprintf(logWriter, "listen: %v\n", err) return err diff --git a/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/udp/udp.go b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/udp/udp.go index d8f646b5d..fbff2b081 100644 --- a/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/udp/udp.go +++ b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/udp/udp.go @@ -1,10 +1,10 @@ package udp import ( - "fmt" "io" "net" "os" + "strconv" "github.com/pkg/errors" @@ -14,7 +14,7 @@ import ( ) func Run(socketPath string, spec port.Spec, stopCh <-chan struct{}, logWriter io.Writer) error { - addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", spec.ParentIP, spec.ParentPort)) + addr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(spec.ParentIP, strconv.Itoa(spec.ParentPort))) if err != nil { return err } diff --git a/vendor/github.com/rootless-containers/rootlesskit/pkg/port/portutil/portutil.go b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/portutil/portutil.go index 1c531cac8..a885a76ca 100644 --- a/vendor/github.com/rootless-containers/rootlesskit/pkg/port/portutil/portutil.go +++ b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/portutil/portutil.go @@ -4,76 +4,135 @@ import ( "net" "strconv" "strings" + "text/scanner" "github.com/pkg/errors" "github.com/rootless-containers/rootlesskit/pkg/port" ) -// ParsePortSpec parses a Docker-like representation of PortSpec. +// ParsePortSpec parses a Docker-like representation of PortSpec, but with +// support for both "parent IP" and "child IP" (optional); // e.g. "127.0.0.1:8080:80/tcp", or "127.0.0.1:8080:10.0.2.100:80/tcp" -func ParsePortSpec(s string) (*port.Spec, error) { - splitBySlash := strings.SplitN(s, "/", 2) - if len(splitBySlash) != 2 { - return nil, errors.Errorf("unexpected PortSpec string: %q", s) +// +// Format is as follows: +// +// <parent IP>:<parent port>[:<child IP>]:<child port>/<proto> +// +// Note that (child IP being optional) the format can either contain 5 or 4 +// components. When using IPv6 IP addresses, addresses must use square brackets +// to prevent the colons being mistaken for delimiters. For example: +// +// [::1]:8080:[::2]:80/udp +func ParsePortSpec(portSpec string) (*port.Spec, error) { + const ( + parentIP = iota + parentPort = iota + childIP = iota + childPort = iota + proto = iota + ) + + var ( + s scanner.Scanner + err error + parts = make([]string, 5) + index = parentIP + delimiter = ':' + ) + + // First get the "proto" and "parent-port" at the end. These parts are + // required, whereas "ParentIP" is optional. Removing them first makes + // it easier to parse the remaining parts, as otherwise the third part + // could be _either_ an IP-address _or_ a Port. + + // Get the proto + protoPos := strings.LastIndex(portSpec, "/") + if protoPos < 0 { + return nil, errors.Errorf("missing proto in PortSpec string: %q", portSpec) } - proto := splitBySlash[1] - switch proto { - case "tcp", "udp", "sctp": - default: - return nil, errors.Errorf("unexpected Proto in PortSpec string: %q", s) + parts[proto] = portSpec[protoPos+1:] + err = validateProto(parts[proto]) + if err != nil { + return nil, errors.Wrapf(err, "invalid PortSpec string: %q", portSpec) } - splitByColon := strings.SplitN(splitBySlash[0], ":", 4) - switch len(splitByColon) { - case 3, 4: - default: - return nil, errors.Errorf("unexpected PortSpec string: %q", s) + // Get the parent port + portPos := strings.LastIndex(portSpec, ":") + if portPos < 0 { + return nil, errors.Errorf("unexpected PortSpec string: %q", portSpec) } + parts[childPort] = portSpec[portPos+1 : protoPos] + + // Scan the remainder "<IP-address>:<port>[:<IP-address>]" + s.Init(strings.NewReader(portSpec[:portPos])) + + for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() { + if index > childPort { + return nil, errors.Errorf("unexpected PortSpec string: %q", portSpec) + } - parentIP := splitByColon[0] - if net.IP(parentIP) == nil { - return nil, errors.Errorf("unexpected ParentIP in PortSpec string: %q", s) + switch tok { + case '[': + // Start of IPv6 IP-address; value ends at closing bracket (]) + delimiter = ']' + continue + case delimiter: + if delimiter == ']' { + // End of IPv6 IP-address + delimiter = ':' + // Skip the next token, which should be a colon delimiter (:) + tok = s.Scan() + } + index++ + continue + default: + parts[index] += s.TokenText() + } } - parentPort, err := strconv.Atoi(splitByColon[1]) - if err != nil { - return nil, errors.Wrapf(err, "unexpected ParentPort in PortSpec string: %q", s) + if parts[parentIP] != "" && net.ParseIP(parts[parentIP]) == nil { + return nil, errors.Errorf("unexpected ParentIP in PortSpec string: %q", portSpec) + } + if parts[childIP] != "" && net.ParseIP(parts[childIP]) == nil { + return nil, errors.Errorf("unexpected ParentIP in PortSpec string: %q", portSpec) } - var childIP string - if len(splitByColon) == 4 { - childIP = splitByColon[2] - if net.IP(childIP) == nil { - return nil, errors.Errorf("unexpected ChildIP in PortSpec string: %q", s) - } + ps := &port.Spec{ + Proto: parts[proto], + ParentIP: parts[parentIP], + ChildIP: parts[childIP], } - childPort, err := strconv.Atoi(splitByColon[len(splitByColon)-1]) + ps.ParentPort, err = strconv.Atoi(parts[parentPort]) if err != nil { - return nil, errors.Wrapf(err, "unexpected ChildPort in PortSpec string: %q", s) + return nil, errors.Wrapf(err, "unexpected ChildPort in PortSpec string: %q", portSpec) } - return &port.Spec{ - Proto: proto, - ParentIP: parentIP, - ParentPort: parentPort, - ChildIP: childIP, - ChildPort: childPort, - }, nil + ps.ChildPort, err = strconv.Atoi(parts[childPort]) + if err != nil { + return nil, errors.Wrapf(err, "unexpected ParentPort in PortSpec string: %q", portSpec) + } + + return ps, nil } // ValidatePortSpec validates *port.Spec. // existingPorts can be optionally passed for detecting conflicts. func ValidatePortSpec(spec port.Spec, existingPorts map[int]*port.Status) error { - if spec.Proto != "tcp" && spec.Proto != "udp" { - return errors.Errorf("unknown proto: %q", spec.Proto) + if err := validateProto(spec.Proto); err != nil { + return err } if spec.ParentIP != "" { if net.ParseIP(spec.ParentIP) == nil { return errors.Errorf("invalid ParentIP: %q", spec.ParentIP) } } + if spec.ChildIP != "" { + if net.ParseIP(spec.ChildIP) == nil { + return errors.Errorf("invalid ChildIP: %q", spec.ChildIP) + } + } if spec.ParentPort <= 0 || spec.ParentPort > 65535 { return errors.Errorf("invalid ParentPort: %q", spec.ParentPort) } @@ -90,3 +149,12 @@ func ValidatePortSpec(spec port.Spec, existingPorts map[int]*port.Status) error } return nil } + +func validateProto(proto string) error { + switch proto { + case "tcp", "udp", "sctp": + return nil + default: + return errors.Errorf("unknown proto: %q", proto) + } +} diff --git a/vendor/github.com/sirupsen/logrus/.travis.yml b/vendor/github.com/sirupsen/logrus/.travis.yml index 5e20aa414..e6ee8b3ab 100644 --- a/vendor/github.com/sirupsen/logrus/.travis.yml +++ b/vendor/github.com/sirupsen/logrus/.travis.yml @@ -4,14 +4,11 @@ git: depth: 1 env: - GO111MODULE=on -go: [1.13.x, 1.14.x] -os: [linux, osx] +go: 1.15.x +os: linux install: - ./travis/install.sh script: - - ./travis/cross_build.sh - - ./travis/lint.sh - - export GOMAXPROCS=4 - - export GORACE=halt_on_error=1 - - go test -race -v ./... - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then go test -race -v -tags appengine ./... ; fi + - go run mage.go -v crossBuild + - go run mage.go lint + - go run mage.go test diff --git a/vendor/github.com/sirupsen/logrus/CHANGELOG.md b/vendor/github.com/sirupsen/logrus/CHANGELOG.md index 584026d67..f68fb86bd 100644 --- a/vendor/github.com/sirupsen/logrus/CHANGELOG.md +++ b/vendor/github.com/sirupsen/logrus/CHANGELOG.md @@ -1,3 +1,23 @@ +# 1.7.1 +Code quality: + * use go 1.15 in travis + * use magefile as task runner + +Fixes: + * small fixes about new go 1.13 error formatting system + * Fix for long time race condiction with mutating data hooks + +Features: + * build support for zos + +# 1.7.0 +Fixes: + * the dependency toward a windows terminal library has been removed + +Features: + * a new buffer pool management API has been added + * a set of `<LogLevel>Fn()` functions have been added + # 1.6.0 Fixes: * end of line cleanup diff --git a/vendor/github.com/sirupsen/logrus/README.md b/vendor/github.com/sirupsen/logrus/README.md index 5796706db..5152b6aa4 100644 --- a/vendor/github.com/sirupsen/logrus/README.md +++ b/vendor/github.com/sirupsen/logrus/README.md @@ -402,7 +402,7 @@ func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) { // source of the official loggers. serialized, err := json.Marshal(entry.Data) if err != nil { - return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) + return nil, fmt.Errorf("Failed to marshal fields to JSON, %w", err) } return append(serialized, '\n'), nil } diff --git a/vendor/github.com/sirupsen/logrus/entry.go b/vendor/github.com/sirupsen/logrus/entry.go index 5a5cbfe7c..e02e057e1 100644 --- a/vendor/github.com/sirupsen/logrus/entry.go +++ b/vendor/github.com/sirupsen/logrus/entry.go @@ -78,8 +78,20 @@ func NewEntry(logger *Logger) *Entry { } } +func (entry *Entry) Dup() *Entry { + data := make(Fields, len(entry.Data)) + for k, v := range entry.Data { + data[k] = v + } + return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: entry.Context, err: entry.err} +} + // Returns the bytes representation of this entry from the formatter. func (entry *Entry) Bytes() ([]byte, error) { + return entry.bytes_nolock() +} + +func (entry *Entry) bytes_nolock() ([]byte, error) { return entry.Logger.Formatter.Format(entry) } @@ -123,11 +135,9 @@ func (entry *Entry) WithFields(fields Fields) *Entry { for k, v := range fields { isErrField := false if t := reflect.TypeOf(v); t != nil { - switch t.Kind() { - case reflect.Func: + switch { + case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func: isErrField = true - case reflect.Ptr: - isErrField = t.Elem().Kind() == reflect.Func } } if isErrField { @@ -214,51 +224,49 @@ func (entry Entry) HasCaller() (has bool) { // This function is not declared with a pointer value because otherwise // race conditions will occur when using multiple goroutines -func (entry Entry) log(level Level, msg string) { +func (entry *Entry) log(level Level, msg string) { var buffer *bytes.Buffer - // Default to now, but allow users to override if they want. - // - // We don't have to worry about polluting future calls to Entry#log() - // with this assignment because this function is declared with a - // non-pointer receiver. - if entry.Time.IsZero() { - entry.Time = time.Now() + newEntry := entry.Dup() + + if newEntry.Time.IsZero() { + newEntry.Time = time.Now() } - entry.Level = level - entry.Message = msg - entry.Logger.mu.Lock() - if entry.Logger.ReportCaller { - entry.Caller = getCaller() + newEntry.Level = level + newEntry.Message = msg + + newEntry.Logger.mu.Lock() + reportCaller := newEntry.Logger.ReportCaller + newEntry.Logger.mu.Unlock() + + if reportCaller { + newEntry.Caller = getCaller() } - entry.Logger.mu.Unlock() - entry.fireHooks() + newEntry.fireHooks() buffer = getBuffer() defer func() { - entry.Buffer = nil + newEntry.Buffer = nil putBuffer(buffer) }() buffer.Reset() - entry.Buffer = buffer + newEntry.Buffer = buffer - entry.write() + newEntry.write() - entry.Buffer = nil + newEntry.Buffer = nil // To avoid Entry#log() returning a value that only would make sense for // panic() to use in Entry#Panic(), we avoid the allocation by checking // directly here. if level <= PanicLevel { - panic(&entry) + panic(newEntry) } } func (entry *Entry) fireHooks() { - entry.Logger.mu.Lock() - defer entry.Logger.mu.Unlock() err := entry.Logger.Hooks.Fire(entry.Level, entry) if err != nil { fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) @@ -266,16 +274,18 @@ func (entry *Entry) fireHooks() { } func (entry *Entry) write() { - entry.Logger.mu.Lock() - defer entry.Logger.mu.Unlock() serialized, err := entry.Logger.Formatter.Format(entry) if err != nil { fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) return } - if _, err = entry.Logger.Out.Write(serialized); err != nil { - fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) - } + func() { + entry.Logger.mu.Lock() + defer entry.Logger.mu.Unlock() + if _, err := entry.Logger.Out.Write(serialized); err != nil { + fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) + } + }() } func (entry *Entry) Log(level Level, args ...interface{}) { @@ -319,7 +329,6 @@ func (entry *Entry) Fatal(args ...interface{}) { func (entry *Entry) Panic(args ...interface{}) { entry.Log(PanicLevel, args...) - panic(fmt.Sprint(args...)) } // Entry Printf family functions diff --git a/vendor/github.com/sirupsen/logrus/go.mod b/vendor/github.com/sirupsen/logrus/go.mod index b3919d5ea..37004ff34 100644 --- a/vendor/github.com/sirupsen/logrus/go.mod +++ b/vendor/github.com/sirupsen/logrus/go.mod @@ -2,6 +2,7 @@ module github.com/sirupsen/logrus require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/magefile/mage v1.10.0 github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.2.2 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 diff --git a/vendor/github.com/sirupsen/logrus/go.sum b/vendor/github.com/sirupsen/logrus/go.sum index 1edc143be..bce26a188 100644 --- a/vendor/github.com/sirupsen/logrus/go.sum +++ b/vendor/github.com/sirupsen/logrus/go.sum @@ -1,5 +1,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/magefile/mage v1.10.0 h1:3HiXzCUY12kh9bIuyXShaVe529fJfyqoVM42o/uom2g= +github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= diff --git a/vendor/github.com/sirupsen/logrus/json_formatter.go b/vendor/github.com/sirupsen/logrus/json_formatter.go index ba7f23711..afaf0fc8a 100644 --- a/vendor/github.com/sirupsen/logrus/json_formatter.go +++ b/vendor/github.com/sirupsen/logrus/json_formatter.go @@ -118,7 +118,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { encoder.SetIndent("", " ") } if err := encoder.Encode(data); err != nil { - return nil, fmt.Errorf("failed to marshal fields to JSON, %v", err) + return nil, fmt.Errorf("failed to marshal fields to JSON, %w", err) } return b.Bytes(), nil diff --git a/vendor/github.com/sirupsen/logrus/logger.go b/vendor/github.com/sirupsen/logrus/logger.go index dbf627c97..337704457 100644 --- a/vendor/github.com/sirupsen/logrus/logger.go +++ b/vendor/github.com/sirupsen/logrus/logger.go @@ -12,7 +12,7 @@ import ( // LogFunction For big messages, it can be more efficient to pass a function // and only call it if the log level is actually enables rather than // generating the log message and then checking if the level is enabled -type LogFunction func()[]interface{} +type LogFunction func() []interface{} type Logger struct { // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a diff --git a/vendor/github.com/sirupsen/logrus/magefile.go b/vendor/github.com/sirupsen/logrus/magefile.go new file mode 100644 index 000000000..9aa603939 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/magefile.go @@ -0,0 +1,77 @@ +// +build mage + +package main + +import ( + "encoding/json" + "fmt" + "os" + "path" + + "github.com/magefile/mage/mg" + "github.com/magefile/mage/sh" +) + +// getBuildMatrix returns the build matrix from the current version of the go compiler +func getBuildMatrix() (map[string][]string, error) { + jsonData, err := sh.Output("go", "tool", "dist", "list", "-json") + if err != nil { + return nil, err + } + var data []struct { + Goos string + Goarch string + } + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + return nil, err + } + + matrix := map[string][]string{} + for _, v := range data { + if val, ok := matrix[v.Goos]; ok { + matrix[v.Goos] = append(val, v.Goarch) + } else { + matrix[v.Goos] = []string{v.Goarch} + } + } + + return matrix, nil +} + +func CrossBuild() error { + matrix, err := getBuildMatrix() + if err != nil { + return err + } + + for os, arches := range matrix { + for _, arch := range arches { + env := map[string]string{ + "GOOS": os, + "GOARCH": arch, + } + if mg.Verbose() { + fmt.Printf("Building for GOOS=%s GOARCH=%s\n", os, arch) + } + if err := sh.RunWith(env, "go", "build", "./..."); err != nil { + return err + } + } + } + return nil +} + +func Lint() error { + gopath := os.Getenv("GOPATH") + if gopath == "" { + return fmt.Errorf("cannot retrieve GOPATH") + } + + return sh.Run(path.Join(gopath, "bin", "golangci-lint"), "run", "./...") +} + +// Run the test suite +func Test() error { + return sh.RunWith(map[string]string{"GORACE": "halt_on_error=1"}, + "go", "test", "-race", "-v", "./...") +} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_unix.go b/vendor/github.com/sirupsen/logrus/terminal_check_unix.go index cc4fe6e31..04748b851 100644 --- a/vendor/github.com/sirupsen/logrus/terminal_check_unix.go +++ b/vendor/github.com/sirupsen/logrus/terminal_check_unix.go @@ -1,4 +1,4 @@ -// +build linux aix +// +build linux aix zos // +build !js package logrus diff --git a/vendor/github.com/sirupsen/logrus/text_formatter.go b/vendor/github.com/sirupsen/logrus/text_formatter.go index 3c28b54ca..8fc698ad6 100644 --- a/vendor/github.com/sirupsen/logrus/text_formatter.go +++ b/vendor/github.com/sirupsen/logrus/text_formatter.go @@ -235,6 +235,8 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin levelColor = yellow case ErrorLevel, FatalLevel, PanicLevel: levelColor = red + case InfoLevel: + levelColor = blue default: levelColor = blue } diff --git a/vendor/modules.txt b/vendor/modules.txt index 7240ac6b6..4ec52f46c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -369,6 +369,9 @@ github.com/klauspost/compress/zstd/internal/xxhash github.com/klauspost/pgzip # github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a github.com/lunixbochs/vtclean +# github.com/magefile/mage v1.10.0 +github.com/magefile/mage/mg +github.com/magefile/mage/sh # github.com/manifoldco/promptui v0.8.0 github.com/manifoldco/promptui github.com/manifoldco/promptui/list @@ -515,7 +518,7 @@ github.com/prometheus/common/model # github.com/prometheus/procfs v0.0.3 github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs -# github.com/rootless-containers/rootlesskit v0.13.0 +# github.com/rootless-containers/rootlesskit v0.13.1 github.com/rootless-containers/rootlesskit/pkg/msgutil github.com/rootless-containers/rootlesskit/pkg/port github.com/rootless-containers/rootlesskit/pkg/port/builtin @@ -531,7 +534,7 @@ github.com/rootless-containers/rootlesskit/pkg/port/portutil github.com/safchain/ethtool # github.com/seccomp/libseccomp-golang v0.9.2-0.20200616122406-847368b35ebf github.com/seccomp/libseccomp-golang -# github.com/sirupsen/logrus v1.7.0 +# github.com/sirupsen/logrus v1.7.1 github.com/sirupsen/logrus github.com/sirupsen/logrus/hooks/syslog # github.com/spf13/cobra v1.1.3 |