Bump github.com/containers/common from 0.35.0 to 0.35.3

Bumps [github.com/containers/common](https://github.com/containers/common) from 0.35.0 to 0.35.3.
- [Release notes](https://github.com/containers/common/releases)
- [Commits](https://github.com/containers/common/compare/v0.35.0...v0.35.3)

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot]
2021-03-19 11:02:53 +00:00
committed by Giuseppe Scrivano
parent 61e3b152fc
commit f46b34ecd2
19 changed files with 202 additions and 97 deletions

6
go.mod
View File

@ -11,9 +11,9 @@ require (
github.com/containernetworking/cni v0.8.1
github.com/containernetworking/plugins v0.9.1
github.com/containers/buildah v1.19.8
github.com/containers/common v0.35.0
github.com/containers/common v0.35.3
github.com/containers/conmon v2.0.20+incompatible
github.com/containers/image/v5 v5.10.2
github.com/containers/image/v5 v5.10.5
github.com/containers/ocicrypt v1.1.0
github.com/containers/psgo v1.5.2
github.com/containers/storage v1.28.0
@ -42,7 +42,7 @@ require (
github.com/moby/term v0.0.0-20201110203204-bea5bbe245bf
github.com/mrunalp/fileutils v0.5.0
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/onsi/ginkgo v1.15.1
github.com/onsi/ginkgo v1.15.2
github.com/onsi/gomega v1.11.0
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6

13
go.sum
View File

@ -179,13 +179,14 @@ github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRD
github.com/containers/buildah v1.19.8 h1:4TzmetfKPQF5hh6GgMwbAfrD50j+PAcsRiWDnx+gCI8=
github.com/containers/buildah v1.19.8/go.mod h1:VnyHWgNmfR1d89/zJ/F4cbwOzaQS+6sBky46W7dCo3E=
github.com/containers/common v0.33.4/go.mod h1:PhgL71XuC4jJ/1BIqeP7doke3aMFkCP90YBXwDeUr9g=
github.com/containers/common v0.35.0 h1:1OLZ2v+Tj/CN9BTQkKZ5VOriOiArJedinMMqfJRUI38=
github.com/containers/common v0.35.0/go.mod h1:gs1th7XFTOvVUl4LDPdQjOfOeNiVRDbQ7CNrZ0wS6F8=
github.com/containers/common v0.35.3 h1:6tEBSIHlJzpmt35zA1ZcjBqbtUilAHDWaa7buPvaqWY=
github.com/containers/common v0.35.3/go.mod h1:rMzxgD7nMGw++cEbsp+NZv0UJO4rgXbm7F7IbJPTwIE=
github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/image/v5 v5.10.1/go.mod h1:JlRLJZv7elVbtHaaaR6Kz8i6G3k2ttj4t7fubwxD9Hs=
github.com/containers/image/v5 v5.10.2 h1:STD9GYR9p/X0qTLmBYsyx8dEM7zQW+qZ8KHoL/64fkg=
github.com/containers/image/v5 v5.10.2/go.mod h1:JlRLJZv7elVbtHaaaR6Kz8i6G3k2ttj4t7fubwxD9Hs=
github.com/containers/image/v5 v5.10.5 h1:VK1UbsZMzjdw5Xqr3Im9h4iOqHWU0naFs+I78kavc7I=
github.com/containers/image/v5 v5.10.5/go.mod h1:SgIbWEedCNBbn2FI5cH0/jed1Ecy2s8XK5zTxvJTzII=
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE=
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc=
@ -197,7 +198,7 @@ github.com/containers/psgo v1.5.2/go.mod h1:2ubh0SsreMZjSXW1Hif58JrEcFudQyIy9EzP
github.com/containers/storage v1.23.5/go.mod h1:ha26Q6ngehFNhf3AWoXldvAvwI4jFe3ETQAf/CeZPyM=
github.com/containers/storage v1.24.5/go.mod h1:YC+2pY8SkfEAcZkwycxYbpK8EiRbx5soPPwz9dxe4IQ=
github.com/containers/storage v1.24.6/go.mod h1:YC+2pY8SkfEAcZkwycxYbpK8EiRbx5soPPwz9dxe4IQ=
github.com/containers/storage v1.25.0/go.mod h1:UxTYd5F4mPVqmDRcRL0PBS8+HP74aBn96eahnhEvPtk=
github.com/containers/storage v1.24.8/go.mod h1:YC+2pY8SkfEAcZkwycxYbpK8EiRbx5soPPwz9dxe4IQ=
github.com/containers/storage v1.28.0 h1:lA/9i9BIjfmIRxCI8GuzasYHmU4IUXVcfZZiDceD0Eg=
github.com/containers/storage v1.28.0/go.mod h1:ixAwO7Bj31cigqPEG7aCz+PYmxkDxbIFdUFioYdxbzI=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
@ -583,8 +584,8 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg=
github.com/onsi/ginkgo v1.15.1 h1:DsXNrKujDlkMS9Rsxmd+Fg7S6Kc5lhE+qX8tY6laOxc=
github.com/onsi/ginkgo v1.15.1/go.mod h1:Dd6YFfwBW84ETqqtL0CPyPXillHgY6XhQH3uuCCTr/o=
github.com/onsi/ginkgo v1.15.2 h1:l77YT15o814C2qVL47NOyjV/6RbaP7kKdrvZnxQ3Org=
github.com/onsi/ginkgo v1.15.2/go.mod h1:Dd6YFfwBW84ETqqtL0CPyPXillHgY6XhQH3uuCCTr/o=
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=

View File

@ -22,9 +22,7 @@ import (
func GetDefaultAuthFile() string {
authfile := os.Getenv("REGISTRY_AUTH_FILE")
if authfile == "" {
if authfile, ok := os.LookupEnv("DOCKER_CONFIG"); ok {
logrus.Infof("Using DOCKER_CONFIG environment variable for authfile path %s", authfile)
}
authfile = os.Getenv("DOCKER_CONFIG")
}
return authfile
}

View File

@ -16,6 +16,9 @@ var (
// Used internally and populated during init().
capabilityList []string
// Used internally and populated during init().
capsList []capability.Cap
// ErrUnknownCapability is thrown when an unknown capability is processed.
ErrUnknownCapability = errors.New("unknown capability")
@ -28,6 +31,10 @@ var (
// Useful on the CLI for `--cap-add=all` etc.
const All = "ALL"
func getCapName(c capability.Cap) string {
return "CAP_" + strings.ToUpper(c.String())
}
func init() {
last := capability.CAP_LAST_CAP
// hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap
@ -38,7 +45,8 @@ func init() {
if cap > last {
continue
}
capabilityList = append(capabilityList, "CAP_"+strings.ToUpper(cap.String()))
capsList = append(capsList, cap)
capabilityList = append(capabilityList, getCapName(cap))
}
}
@ -52,6 +60,26 @@ func stringInSlice(s string, sl []string) bool {
return false
}
// BoundingSet returns the capabilities in the current bounding set
func BoundingSet() ([]string, error) {
currentCaps, err := capability.NewPid2(0)
if err != nil {
return nil, err
}
err = currentCaps.Load()
if err != nil {
return nil, err
}
var r []string
for _, c := range capsList {
if !currentCaps.Get(capability.BOUNDING, c) {
continue
}
r = append(r, getCapName(c))
}
return r, nil
}
// AllCapabilities returns all known capabilities.
func AllCapabilities() []string {
return capabilityList

View File

@ -4,10 +4,8 @@ import (
"os"
"os/user"
"path/filepath"
"syscall"
"github.com/containers/storage/pkg/homedir"
"github.com/pkg/errors"
)
// DangerousHostPath validates if a host path is dangerous and should not be modified
@ -65,58 +63,3 @@ func DangerousHostPath(path string) (bool, error) {
return false, nil
}
// ChangeHostPathOwnership changes the uid and gid ownership of a directory or file within the host.
// This is used by the volume U flag to change source volumes ownership
func ChangeHostPathOwnership(path string, recursive bool, uid, gid int) error {
// Validate if host path can be chowned
isDangerous, err := DangerousHostPath(path)
if err != nil {
return errors.Wrapf(err, "failed to validate if host path is dangerous")
}
if isDangerous {
return errors.Errorf("chowning host path %q is not allowed. You can manually `chown -R %d:%d %s`", path, uid, gid, path)
}
// Chown host path
if recursive {
err := filepath.Walk(path, func(filePath string, f os.FileInfo, err error) error {
if err != nil {
return err
}
// Get current ownership
currentUID := int(f.Sys().(*syscall.Stat_t).Uid)
currentGID := int(f.Sys().(*syscall.Stat_t).Gid)
if uid != currentUID || gid != currentGID {
return os.Lchown(filePath, uid, gid)
}
return nil
})
if err != nil {
return errors.Wrapf(err, "failed to chown recursively host path")
}
} else {
// Get host path info
f, err := os.Lstat(path)
if err != nil {
return errors.Wrapf(err, "failed to get host path information")
}
// Get current ownership
currentUID := int(f.Sys().(*syscall.Stat_t).Uid)
currentGID := int(f.Sys().(*syscall.Stat_t).Gid)
if uid != currentUID || gid != currentGID {
if err := os.Lchown(path, uid, gid); err != nil {
return errors.Wrapf(err, "failed to chown host path")
}
}
}
return nil
}

View File

@ -0,0 +1,66 @@
// +build !windows
package chown
import (
"os"
"path/filepath"
"syscall"
"github.com/pkg/errors"
)
// ChangeHostPathOwnership changes the uid and gid ownership of a directory or file within the host.
// This is used by the volume U flag to change source volumes ownership
func ChangeHostPathOwnership(path string, recursive bool, uid, gid int) error {
// Validate if host path can be chowned
isDangerous, err := DangerousHostPath(path)
if err != nil {
return errors.Wrapf(err, "failed to validate if host path is dangerous")
}
if isDangerous {
return errors.Errorf("chowning host path %q is not allowed. You can manually `chown -R %d:%d %s`", path, uid, gid, path)
}
// Chown host path
if recursive {
err := filepath.Walk(path, func(filePath string, f os.FileInfo, err error) error {
if err != nil {
return err
}
// Get current ownership
currentUID := int(f.Sys().(*syscall.Stat_t).Uid)
currentGID := int(f.Sys().(*syscall.Stat_t).Gid)
if uid != currentUID || gid != currentGID {
return os.Lchown(filePath, uid, gid)
}
return nil
})
if err != nil {
return errors.Wrapf(err, "failed to chown recursively host path")
}
} else {
// Get host path info
f, err := os.Lstat(path)
if err != nil {
return errors.Wrapf(err, "failed to get host path information")
}
// Get current ownership
currentUID := int(f.Sys().(*syscall.Stat_t).Uid)
currentGID := int(f.Sys().(*syscall.Stat_t).Gid)
if uid != currentUID || gid != currentGID {
if err := os.Lchown(path, uid, gid); err != nil {
return errors.Wrapf(err, "failed to chown host path")
}
}
}
return nil
}

View File

@ -0,0 +1,11 @@
package chown
import (
"github.com/pkg/errors"
)
// ChangeHostPathOwnership changes the uid and gid ownership of a directory or file within the host.
// This is used by the volume U flag to change source volumes ownership
func ChangeHostPathOwnership(path string, recursive bool, uid, gid int) error {
return errors.Errorf("windows not supported")
}

View File

@ -139,3 +139,17 @@ func AutocompleteOS(cmd *cobra.Command, args []string, toComplete string) ([]str
completions := []string{"linux", "windows"}
return completions, cobra.ShellCompDirectiveNoFileComp
}
// AutocompleteJSONFormat - Autocomplete format flag option.
// -> "json"
func AutocompleteJSONFormat(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return []string{"json"}, cobra.ShellCompDirectiveNoFileComp
}
// AutocompleteOneArg - Autocomplete one random arg
func AutocompleteOneArg(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 1 {
return nil, cobra.ShellCompDirectiveDefault
}
return nil, cobra.ShellCompDirectiveNoFileComp
}

View File

@ -11,9 +11,9 @@ import (
"github.com/containers/common/pkg/apparmor"
"github.com/containers/common/pkg/cgroupv2"
"github.com/containers/storage"
"github.com/containers/storage/pkg/homedir"
"github.com/containers/storage/pkg/unshare"
"github.com/containers/storage/types"
"github.com/opencontainers/selinux/go-selinux"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@ -224,9 +224,9 @@ func defaultConfigFromMemory() (*EngineConfig, error) {
c.EventsLogFilePath = filepath.Join(c.TmpDir, "events", "events.log")
if path, ok := os.LookupEnv("CONTAINERS_STORAGE_CONF"); ok {
storage.SetDefaultConfigFilePath(path)
types.SetDefaultConfigFilePath(path)
}
storeOpts, err := storage.DefaultStoreOptions(unshare.IsRootless(), unshare.GetRootlessUID())
storeOpts, err := types.DefaultStoreOptions(unshare.IsRootless(), unshare.GetRootlessUID())
if err != nil {
return nil, err
}

View File

@ -7,13 +7,12 @@ import (
"path/filepath"
"github.com/containers/storage/pkg/unshare"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/devices"
"github.com/pkg/errors"
)
func DeviceFromPath(device string) ([]configs.Device, error) {
var devs []configs.Device
func DeviceFromPath(device string) ([]devices.Device, error) {
var devs []devices.Device
src, dst, permissions, err := Device(device)
if err != nil {
return nil, err
@ -44,7 +43,7 @@ func DeviceFromPath(device string) ([]configs.Device, error) {
}
for _, d := range srcDevices {
d.Path = filepath.Join(dst, filepath.Base(d.Path))
d.Permissions = configs.DevicePermissions(permissions)
d.Permissions = devices.Permissions(permissions)
devs = append(devs, *d)
}
return devs, nil

View File

@ -1,4 +1,4 @@
package version
// Version is the version of the build.
const Version = "0.35.0"
const Version = "0.35.3"

View File

@ -1067,6 +1067,26 @@ type diffIDResult struct {
// copyLayer copies a layer with srcInfo (with known Digest and Annotations and possibly known Size) in src to dest, perhaps (de/re/)compressing it,
// and returns a complete blobInfo of the copied layer, and a value for LayerDiffIDs if diffIDIsNeeded
func (ic *imageCopier) copyLayer(ctx context.Context, srcInfo types.BlobInfo, toEncrypt bool, pool *mpb.Progress) (types.BlobInfo, digest.Digest, error) {
// If the srcInfo doesn't contain compression information, try to compute it from the
// MediaType, which was either read from a manifest by way of LayerInfos() or constructed
// by LayerInfosForCopy(), if it was supplied at all. If we succeed in copying the blob,
// the BlobInfo we return will be passed to UpdatedImage() and then to UpdateLayerInfos(),
// which uses the compression information to compute the updated MediaType values.
// (Sadly UpdatedImage() is documented to not update MediaTypes from
// ManifestUpdateOptions.LayerInfos[].MediaType, so we are doing it indirectly.)
//
// This MIME type → compression mapping belongs in manifest-specific code in our manifest
// package (but we should preferably replace/change UpdatedImage instead of productizing
// this workaround).
if srcInfo.CompressionAlgorithm == nil {
switch srcInfo.MediaType {
case manifest.DockerV2Schema2LayerMediaType, imgspecv1.MediaTypeImageLayerGzip:
srcInfo.CompressionAlgorithm = &compression.Gzip
case imgspecv1.MediaTypeImageLayerZstd:
srcInfo.CompressionAlgorithm = &compression.Zstd
}
}
cachedDiffID := ic.c.blobInfoCache.UncompressedDigest(srcInfo.Digest) // May be ""
// Diffs are needed if we are encrypting an image or trying to decrypt an image
diffIDIsNeeded := ic.diffIDsAreNeeded && cachedDiffID == "" || toEncrypt || (isOciEncrypted(srcInfo.MediaType) && ic.c.ociDecryptConfig != nil)
@ -1095,6 +1115,19 @@ func (ic *imageCopier) copyLayer(ctx context.Context, srcInfo types.BlobInfo, to
Artifact: srcInfo,
}
}
// If the reused blob has the same digest as the one we asked for, but
// the transport didn't/couldn't supply compression info, fill it in based
// on what we know from the srcInfos we were given.
// If the srcInfos came from LayerInfosForCopy(), then UpdatedImage() will
// call UpdateLayerInfos(), which uses this information to compute the
// MediaType value for the updated layer infos, and it the transport
// didn't pass the information along from its input to its output, then
// it can derive the MediaType incorrectly.
if blobInfo.Digest == srcInfo.Digest && blobInfo.CompressionAlgorithm == nil {
blobInfo.CompressionOperation = srcInfo.CompressionOperation
blobInfo.CompressionAlgorithm = srcInfo.CompressionAlgorithm
}
return blobInfo, cachedDiffID, nil
}
}
@ -1349,8 +1382,16 @@ func (c *copier) copyBlobFromStream(ctx context.Context, srcStream io.Reader, sr
compressionOperation = types.PreserveOriginal
inputInfo = srcInfo
uploadCompressorName = srcCompressorName
// Remember if the original blob was compressed, and if so how, so that if
// LayerInfosForCopy() returned something that differs from what was in the
// source's manifest, and UpdatedImage() needs to call UpdateLayerInfos(),
// it will be able to correctly derive the MediaType for the copied blob.
if isCompressed {
uploadCompressionFormat = &compressionFormat
} else {
uploadCompressionFormat = nil
}
}
// Perform image encryption for valid mediatypes if ociEncryptConfig provided
var (

View File

@ -34,16 +34,10 @@ func shortNameAliasesConfPath(ctx *types.SystemContext) (string, error) {
}
// Rootless user
var cacheRoot string
if xdgCache := os.Getenv("XDG_CACHE_HOME"); xdgCache != "" {
cacheRoot = xdgCache
} else {
configHome, err := homedir.GetConfigHome()
cacheRoot, err := homedir.GetCacheHome()
if err != nil {
return "", err
}
cacheRoot = filepath.Join(configHome, ".cache")
}
return filepath.Join(cacheRoot, userShortNamesFile), nil
}

View File

@ -246,8 +246,7 @@ func (s *storageImageSource) LayerInfosForCopy(ctx context.Context, instanceDige
case imgspecv1.MediaTypeImageManifest:
uncompressedLayerType = imgspecv1.MediaTypeImageLayer
case manifest.DockerV2Schema1MediaType, manifest.DockerV2Schema1SignedMediaType, manifest.DockerV2Schema2MediaType:
// This is actually a compressed type, but there's no uncompressed type defined
uncompressedLayerType = manifest.DockerV2Schema2LayerMediaType
uncompressedLayerType = manifest.DockerV2SchemaLayerMediaTypeUncompressed
}
physicalBlobInfos := []types.BlobInfo{}

View File

@ -8,7 +8,7 @@ const (
// VersionMinor is for functionality in a backwards-compatible manner
VersionMinor = 10
// VersionPatch is for backwards-compatible bug fixes
VersionPatch = 2
VersionPatch = 5
// VersionDev indicates development branch. Releases will be empty string.
VersionDev = ""

View File

@ -1,3 +1,8 @@
## 1.15.2
### Fixes
- ignore blank `-focus` and `-skip` flags (#780) [e90a4a0]
## 1.15.1
### Fixes

View File

@ -62,6 +62,8 @@ Describe("the strings package", func() {
- [Completions for VSCode](https://github.com/onsi/vscode-ginkgo): just use VSCode's extension installer to install `vscode-ginkgo`.
- [Ginkgo tools for VSCode](https://marketplace.visualstudio.com/items?itemName=joselitofilho.ginkgotestexplorer): just use VSCode's extension installer to install `ginkgoTestExplorer`.
- Straightforward support for third-party testing libraries such as [Gomock](https://code.google.com/p/gomock/) and [Testify](https://github.com/stretchr/testify). Check out the [docs](https://onsi.github.io/ginkgo/#third-party-integrations) for details.
- A modular architecture that lets you easily:

View File

@ -20,7 +20,7 @@ import (
"fmt"
)
const VERSION = "1.15.1"
const VERSION = "1.15.2"
type GinkgoConfigType struct {
RandomSeed int64
@ -219,10 +219,14 @@ func BuildFlagArgs(prefix string, ginkgo GinkgoConfigType, reporter DefaultRepor
// flagFocus implements the -focus flag.
func flagFocus(arg string) {
if arg != "" {
GinkgoConfig.FocusStrings = append(GinkgoConfig.FocusStrings, arg)
}
}
// flagSkip implements the -skip flag.
func flagSkip(arg string) {
if arg != "" {
GinkgoConfig.SkipStrings = append(GinkgoConfig.SkipStrings, arg)
}
}

6
vendor/modules.txt vendored
View File

@ -93,7 +93,7 @@ github.com/containers/buildah/pkg/parse
github.com/containers/buildah/pkg/rusage
github.com/containers/buildah/pkg/supplemented
github.com/containers/buildah/util
# github.com/containers/common v0.35.0
# github.com/containers/common v0.35.3
github.com/containers/common/pkg/apparmor
github.com/containers/common/pkg/apparmor/internal/supported
github.com/containers/common/pkg/auth
@ -115,7 +115,7 @@ github.com/containers/common/pkg/umask
github.com/containers/common/version
# github.com/containers/conmon v2.0.20+incompatible
github.com/containers/conmon/runner/config
# github.com/containers/image/v5 v5.10.2
# github.com/containers/image/v5 v5.10.5
github.com/containers/image/v5/copy
github.com/containers/image/v5/directory
github.com/containers/image/v5/directory/explicitfilepath
@ -413,7 +413,7 @@ github.com/nxadm/tail/ratelimiter
github.com/nxadm/tail/util
github.com/nxadm/tail/watch
github.com/nxadm/tail/winfile
# github.com/onsi/ginkgo v1.15.1
# github.com/onsi/ginkgo v1.15.2
github.com/onsi/ginkgo
github.com/onsi/ginkgo/config
github.com/onsi/ginkgo/extensions/table