diff options
75 files changed, 607 insertions, 1526 deletions
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index 7629a8145..bc106263c 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -8,12 +8,11 @@ import ( "github.com/containers/common/pkg/config" "github.com/containers/podman/v3/cmd/podman/registry" - "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/registries" "github.com/containers/podman/v3/pkg/rootless" - systemdGen "github.com/containers/podman/v3/pkg/systemd/generate" + systemdDefine "github.com/containers/podman/v3/pkg/systemd/define" "github.com/containers/podman/v3/pkg/util" "github.com/spf13/cobra" ) @@ -732,8 +731,8 @@ func AutocompletePullOption(cmd *cobra.Command, args []string, toComplete string // AutocompleteRestartOption - Autocomplete restart options for create and run command. // -> "always", "no", "on-failure", "unless-stopped" func AutocompleteRestartOption(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - restartOptions := []string{libpod.RestartPolicyAlways, libpod.RestartPolicyNo, - libpod.RestartPolicyOnFailure, libpod.RestartPolicyUnlessStopped} + restartOptions := []string{define.RestartPolicyAlways, define.RestartPolicyNo, + define.RestartPolicyOnFailure, define.RestartPolicyUnlessStopped} return restartOptions, cobra.ShellCompDirectiveNoFileComp } @@ -908,7 +907,7 @@ func AutocompleteEventFilter(cmd *cobra.Command, args []string, toComplete strin // AutocompleteSystemdRestartOptions - Autocomplete systemd restart options. // -> "no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", "always" func AutocompleteSystemdRestartOptions(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return systemdGen.RestartPolicies, cobra.ShellCompDirectiveNoFileComp + return systemdDefine.RestartPolicies, cobra.ShellCompDirectiveNoFileComp } // AutocompleteTrustType - Autocomplete trust type options. diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go index 2fafbfac1..d1b67d963 100644 --- a/cmd/podman/common/specgen.go +++ b/cmd/podman/common/specgen.go @@ -14,7 +14,7 @@ import ( envLib "github.com/containers/podman/v3/pkg/env" ns "github.com/containers/podman/v3/pkg/namespaces" "github.com/containers/podman/v3/pkg/specgen" - systemdGen "github.com/containers/podman/v3/pkg/systemd/generate" + systemdDefine "github.com/containers/podman/v3/pkg/systemd/define" "github.com/containers/podman/v3/pkg/util" "github.com/docker/go-units" "github.com/opencontainers/runtime-spec/specs-go" @@ -342,8 +342,8 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string return errors.Wrapf(err, "unable to process labels") } - if systemdUnit, exists := osEnv[systemdGen.EnvVariable]; exists { - labels[systemdGen.EnvVariable] = systemdUnit + if systemdUnit, exists := osEnv[systemdDefine.EnvVariable]; exists { + labels[systemdDefine.EnvVariable] = systemdUnit } s.Labels = labels diff --git a/cmd/podman/networks/list.go b/cmd/podman/networks/list.go index 2181f850b..fcbcb6722 100644 --- a/cmd/podman/networks/list.go +++ b/cmd/podman/networks/list.go @@ -12,8 +12,8 @@ import ( "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" - "github.com/containers/podman/v3/libpod/network" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/network" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/pflag" diff --git a/pkg/domain/filters/helpers.go b/cmd/podman/parse/filters.go index 6a5fb68b1..8a10f2a97 100644 --- a/pkg/domain/filters/helpers.go +++ b/cmd/podman/parse/filters.go @@ -1,4 +1,4 @@ -package filters +package parse import ( "net/url" @@ -7,7 +7,7 @@ import ( "github.com/pkg/errors" ) -func ParseFilterArgumentsIntoFilters(filters []string) (url.Values, error) { +func FilterArgumentsIntoFilters(filters []string) (url.Values, error) { parsedFilters := make(url.Values) for _, f := range filters { t := strings.SplitN(f, "=", 2) diff --git a/cmd/podman/system/prune.go b/cmd/podman/system/prune.go index dcb3316f0..136c15304 100644 --- a/cmd/podman/system/prune.go +++ b/cmd/podman/system/prune.go @@ -8,11 +8,11 @@ import ( "strings" "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v3/cmd/podman/parse" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/utils" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/pkg/domain/entities" - dfilters "github.com/containers/podman/v3/pkg/domain/filters" "github.com/docker/go-units" "github.com/spf13/cobra" ) @@ -80,7 +80,7 @@ Are you sure you want to continue? [y/N] `, volumeString) } } - pruneOptions.Filters, err = dfilters.ParseFilterArgumentsIntoFilters(filters) + pruneOptions.Filters, err = parse.FilterArgumentsIntoFilters(filters) if err != nil { return err } diff --git a/cmd/podman/volumes/prune.go b/cmd/podman/volumes/prune.go index 8f78d0bae..8e190b870 100644 --- a/cmd/podman/volumes/prune.go +++ b/cmd/podman/volumes/prune.go @@ -9,11 +9,11 @@ import ( "github.com/containers/common/pkg/completion" "github.com/containers/podman/v3/cmd/podman/common" + "github.com/containers/podman/v3/cmd/podman/parse" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/utils" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/containers/podman/v3/pkg/domain/filters" "github.com/spf13/cobra" ) @@ -58,14 +58,14 @@ func prune(cmd *cobra.Command, args []string) error { if err != nil { return err } - pruneOptions.Filters, err = filters.ParseFilterArgumentsIntoFilters(filter) + pruneOptions.Filters, err = parse.FilterArgumentsIntoFilters(filter) if !force { reader := bufio.NewReader(os.Stdin) fmt.Println("WARNING! This will remove all volumes not used by at least one container. The following volumes will be removed:") if err != nil { return err } - listOptions.Filter, err = filters.ParseFilterArgumentsIntoFilters(filter) + listOptions.Filter, err = parse.FilterArgumentsIntoFilters(filter) if err != nil { return err } @@ -51,7 +51,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/pmezard/go-difflib v1.0.0 github.com/rootless-containers/rootlesskit v0.14.0-beta.0 - github.com/sirupsen/logrus v1.8.0 + github.com/sirupsen/logrus v1.8.1 github.com/spf13/cobra v1.1.3 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.7.0 @@ -537,6 +537,8 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.0 h1:nfhvjKcUMhBMVqbKHJlk5RPrrfYr/NMo3692g0dwfWU= github.com/sirupsen/logrus v1.8.0/go.mod h1:4GuYW9TZmE769R5STWrRakJc4UqQ3+QQ95fyz7ENv1A= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 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.go b/libpod/container.go index 65abbfd5e..c49d8feeb 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -84,24 +84,6 @@ func (ns LinuxNS) String() string { } } -// Valid restart policy types. -const ( - // RestartPolicyNone indicates that no restart policy has been requested - // by a container. - RestartPolicyNone = "" - // RestartPolicyNo is identical in function to RestartPolicyNone. - RestartPolicyNo = "no" - // RestartPolicyAlways unconditionally restarts the container. - RestartPolicyAlways = "always" - // RestartPolicyOnFailure restarts the container on non-0 exit code, - // with an optional maximum number of retries. - RestartPolicyOnFailure = "on-failure" - // RestartPolicyUnlessStopped unconditionally restarts unless stopped - // by the user. It is identical to Always except with respect to - // handling of system restart, which Podman does not yet support. - RestartPolicyUnlessStopped = "unless-stopped" -) - // Container is a single OCI container. // All operations on a Container that access state must begin with a call to // syncContainer(). diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 1614211fb..106e2569b 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -219,14 +219,14 @@ func (c *Container) shouldRestart() bool { // If we did not get a restart policy match, return false // Do the same if we're not a policy that restarts. if !c.state.RestartPolicyMatch || - c.config.RestartPolicy == RestartPolicyNo || - c.config.RestartPolicy == RestartPolicyNone { + c.config.RestartPolicy == define.RestartPolicyNo || + c.config.RestartPolicy == define.RestartPolicyNone { return false } // If we're RestartPolicyOnFailure, we need to check retries and exit // code. - if c.config.RestartPolicy == RestartPolicyOnFailure { + if c.config.RestartPolicy == define.RestartPolicyOnFailure { if c.state.ExitCode == 0 { return false } @@ -332,7 +332,7 @@ func (c *Container) syncContainer() error { // Only save back to DB if state changed if c.state.State != oldState { // Check for a restart policy match - if c.config.RestartPolicy != RestartPolicyNone && c.config.RestartPolicy != RestartPolicyNo && + if c.config.RestartPolicy != define.RestartPolicyNone && c.config.RestartPolicy != define.RestartPolicyNo && (oldState == define.ContainerStateRunning || oldState == define.ContainerStatePaused) && (c.state.State == define.ContainerStateStopped || c.state.State == define.ContainerStateExited) && !c.state.StoppedByUser { diff --git a/libpod/define/container.go b/libpod/define/container.go new file mode 100644 index 000000000..5a2ff026f --- /dev/null +++ b/libpod/define/container.go @@ -0,0 +1,19 @@ +package define + +// Valid restart policy types. +const ( + // RestartPolicyNone indicates that no restart policy has been requested + // by a container. + RestartPolicyNone = "" + // RestartPolicyNo is identical in function to RestartPolicyNone. + RestartPolicyNo = "no" + // RestartPolicyAlways unconditionally restarts the container. + RestartPolicyAlways = "always" + // RestartPolicyOnFailure restarts the container on non-0 exit code, + // with an optional maximum number of retries. + RestartPolicyOnFailure = "on-failure" + // RestartPolicyUnlessStopped unconditionally restarts unless stopped + // by the user. It is identical to Always except with respect to + // handling of system restart, which Podman does not yet support. + RestartPolicyUnlessStopped = "unless-stopped" +) diff --git a/libpod/kube.go b/libpod/kube.go index 6feb69fea..407c4ae00 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -83,11 +83,11 @@ func (p *Pod) GenerateForKube() (*v1.Pod, []v1.ServicePort, error) { for _, ctr := range allContainers { if !ctr.IsInfra() { switch ctr.Config().RestartPolicy { - case RestartPolicyAlways: + case define.RestartPolicyAlways: pod.Spec.RestartPolicy = v1.RestartPolicyAlways - case RestartPolicyOnFailure: + case define.RestartPolicyOnFailure: pod.Spec.RestartPolicy = v1.RestartPolicyOnFailure - case RestartPolicyNo: + case define.RestartPolicyNo: pod.Spec.RestartPolicy = v1.RestartPolicyNever default: // some pod create from cmdline, such as "", so set it to Never pod.Spec.RestartPolicy = v1.RestartPolicyNever diff --git a/libpod/network/files.go b/libpod/network/files.go index fe483e25c..d876113f9 100644 --- a/libpod/network/files.go +++ b/libpod/network/files.go @@ -11,6 +11,7 @@ import ( "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator" "github.com/containers/common/pkg/config" "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/pkg/network" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -67,7 +68,7 @@ func GetCNIConfigPathByNameOrID(config *config.Config, name string) (string, err if conf.Name == name { return confFile, nil } - if strings.HasPrefix(GetNetworkID(conf.Name), name) { + if strings.HasPrefix(network.GetNetworkID(conf.Name), name) { idMatch++ file = confFile } @@ -92,16 +93,6 @@ func ReadRawCNIConfByNameOrID(config *config.Config, name string) ([]byte, error return b, err } -// GetCNIPlugins returns a list of plugins that a given network -// has in the form of a string -func GetCNIPlugins(list *libcni.NetworkConfigList) string { - plugins := make([]string, 0, len(list.Plugins)) - for _, plug := range list.Plugins { - plugins = append(plugins, plug.Network.Type) - } - return strings.Join(plugins, ",") -} - // GetNetworkLabels returns a list of labels as a string func GetNetworkLabels(list *libcni.NetworkConfigList) NcLabels { cniJSON := make(map[string]interface{}) diff --git a/libpod/network/netconflist.go b/libpod/network/netconflist.go index 1a1583587..a45a4109a 100644 --- a/libpod/network/netconflist.go +++ b/libpod/network/netconflist.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/containernetworking/cni/libcni" + "github.com/containers/podman/v3/pkg/network" "github.com/containers/podman/v3/pkg/util" "github.com/pkg/errors" ) @@ -211,7 +212,7 @@ func IfPassesFilter(netconf *libcni.NetworkConfigList, filters map[string][]stri case "plugin": // match one plugin - plugins := GetCNIPlugins(netconf) + plugins := network.GetCNIPlugins(netconf) for _, val := range filterValues { if strings.Contains(plugins, val) { result = true @@ -243,7 +244,7 @@ func IfPassesFilter(netconf *libcni.NetworkConfigList, filters map[string][]stri case "driver": // matches only for the DefaultNetworkDriver for _, filterValue := range filterValues { - plugins := GetCNIPlugins(netconf) + plugins := network.GetCNIPlugins(netconf) if filterValue == DefaultNetworkDriver && strings.Contains(plugins, DefaultNetworkDriver) { result = true @@ -253,7 +254,7 @@ func IfPassesFilter(netconf *libcni.NetworkConfigList, filters map[string][]stri case "id": // matches part of one id for _, filterValue := range filterValues { - if strings.Contains(GetNetworkID(netconf.Name), filterValue) { + if strings.Contains(network.GetNetworkID(netconf.Name), filterValue) { result = true break } diff --git a/libpod/network/network.go b/libpod/network/network.go index f19a764ef..ed4e6388a 100644 --- a/libpod/network/network.go +++ b/libpod/network/network.go @@ -1,8 +1,6 @@ package network import ( - "crypto/sha256" - "encoding/hex" "encoding/json" "net" "os" @@ -245,13 +243,6 @@ func Exists(config *config.Config, name string) (bool, error) { return true, nil } -// GetNetworkID return the network ID for a given name. -// It is just the sha256 hash but this should be good enough. -func GetNetworkID(name string) string { - hash := sha256.Sum256([]byte(name)) - return hex.EncodeToString(hash[:]) -} - // PruneNetworks removes networks that are not being used and that is not the default // network. To keep proper fencing for imports, you must provide the used networks // to this function as a map. the key is meaningful in the map, the book is a no-op diff --git a/libpod/options.go b/libpod/options.go index 48888a2f2..85862cc17 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1364,7 +1364,7 @@ func WithRestartPolicy(policy string) CtrCreateOption { } switch policy { - case RestartPolicyNone, RestartPolicyNo, RestartPolicyOnFailure, RestartPolicyAlways, RestartPolicyUnlessStopped: + case define.RestartPolicyNone, define.RestartPolicyNo, define.RestartPolicyOnFailure, define.RestartPolicyAlways, define.RestartPolicyUnlessStopped: ctr.config.RestartPolicy = policy default: return errors.Wrapf(define.ErrInvalidArg, "%q is not a valid restart policy", policy) diff --git a/pkg/api/handlers/compat/auth.go b/pkg/api/handlers/compat/auth.go new file mode 100644 index 000000000..2c152fbc2 --- /dev/null +++ b/pkg/api/handlers/compat/auth.go @@ -0,0 +1,59 @@ +package compat + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "strings" + + DockerClient "github.com/containers/image/v5/docker" + "github.com/containers/image/v5/types" + "github.com/containers/podman/v3/pkg/api/handlers/utils" + "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/registries" + docker "github.com/docker/docker/api/types" + "github.com/pkg/errors" +) + +func stripAddressOfScheme(address string) string { + for _, s := range []string{"https", "http"} { + address = strings.TrimPrefix(address, s+"://") + } + return address +} + +func Auth(w http.ResponseWriter, r *http.Request) { + var authConfig docker.AuthConfig + err := json.NewDecoder(r.Body).Decode(&authConfig) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "failed to parse request")) + return + } + + skipTLS := types.NewOptionalBool(false) + if strings.HasPrefix(authConfig.ServerAddress, "https://localhost/") || strings.HasPrefix(authConfig.ServerAddress, "https://localhost:") || strings.HasPrefix(authConfig.ServerAddress, "localhost:") { + // support for local testing + skipTLS = types.NewOptionalBool(true) + } + + fmt.Println("Authenticating with existing credentials...") + sysCtx := types.SystemContext{ + AuthFilePath: "", + DockerCertPath: "", + DockerInsecureSkipTLSVerify: skipTLS, + SystemRegistriesConfPath: registries.SystemRegistriesConfPath(), + } + registry := stripAddressOfScheme(authConfig.ServerAddress) + if err := DockerClient.CheckAuth(context.Background(), &sysCtx, authConfig.Username, authConfig.Password, registry); err == nil { + utils.WriteResponse(w, http.StatusOK, entities.AuthReport{ + IdentityToken: "", + Status: "Login Succeeded", + }) + } else { + utils.WriteResponse(w, http.StatusBadRequest, entities.AuthReport{ + IdentityToken: "", + Status: "login attempt to " + authConfig.ServerAddress + " failed with status: " + err.Error(), + }) + } +} diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index 392f688dd..7751b91a7 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -445,6 +445,13 @@ loop: logrus.Warnf("Failed to json encode error %v", err) } flush() + for _, tag := range query.Tag { + m.Stream = fmt.Sprintf("Successfully tagged %s\n", tag) + if err := enc.Encode(m); err != nil { + logrus.Warnf("Failed to json encode error %v", err) + } + flush() + } } } break loop diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go index 28e90ac28..dfb1d7fda 100644 --- a/pkg/api/handlers/compat/networks.go +++ b/pkg/api/handlers/compat/networks.go @@ -16,6 +16,7 @@ import ( "github.com/containers/podman/v3/pkg/api/handlers/utils" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" + networkid "github.com/containers/podman/v3/pkg/network" "github.com/docker/docker/api/types" dockerNetwork "github.com/docker/docker/api/types/network" "github.com/gorilla/schema" @@ -135,7 +136,7 @@ func getNetworkResourceByNameOrID(nameOrID string, runtime *libpod.Runtime, filt report := types.NetworkResource{ Name: conf.Name, - ID: network.GetNetworkID(conf.Name), + ID: networkid.GetNetworkID(conf.Name), Created: time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)), // nolint: unconvert Scope: "local", Driver: network.DefaultNetworkDriver, diff --git a/pkg/api/server/register_auth.go b/pkg/api/server/register_auth.go index 1e5474462..56e115e30 100644 --- a/pkg/api/server/register_auth.go +++ b/pkg/api/server/register_auth.go @@ -1,13 +1,33 @@ package server import ( + "net/http" + "github.com/containers/podman/v3/pkg/api/handlers/compat" "github.com/gorilla/mux" ) func (s *APIServer) registerAuthHandlers(r *mux.Router) error { - r.Handle(VersionedPath("/auth"), s.APIHandler(compat.UnsupportedHandler)) + // swagger:operation POST /auth compat auth + // --- + // summary: Check auth configuration + // tags: + // - system (compat) + // produces: + // - application/json + // parameters: + // - in: body + // name: authConfig + // description: Authentication to check + // schema: + // $ref: "#/definitions/AuthConfig" + // responses: + // 200: + // $ref: "#/responses/SystemAuthResponse" + // 500: + // $ref: "#/responses/InternalError" + r.Handle(VersionedPath("/auth"), s.APIHandler(compat.Auth)).Methods(http.MethodPost) // Added non version path to URI to support docker non versioned paths - r.Handle("/auth", s.APIHandler(compat.UnsupportedHandler)) + r.Handle("/auth", s.APIHandler(compat.Auth)).Methods(http.MethodPost) return nil } diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index f6a8a37ca..3d86e5d38 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -652,6 +652,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // example: | // (build details...) // Successfully built 8ba084515c724cbf90d447a63600c0a6 + // Successfully tagged your_image:latest // 400: // $ref: "#/responses/BadParamError" // 500: @@ -1485,7 +1486,6 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // description: output from build process // example: | // (build details...) - // Successfully built 8ba084515c724cbf90d447a63600c0a6 // 400: // $ref: "#/responses/BadParamError" // 500: diff --git a/pkg/api/server/register_networks.go b/pkg/api/server/register_networks.go index 68a8d4ae4..c54de952f 100644 --- a/pkg/api/server/register_networks.go +++ b/pkg/api/server/register_networks.go @@ -220,28 +220,6 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error { */ r.HandleFunc(VersionedPath("/libpod/networks/{name}"), s.APIHandler(libpod.RemoveNetwork)).Methods(http.MethodDelete) - // swagger:operation GET /libpod/networks/{name}/json libpod libpodInspectNetwork - // --- - // tags: - // - networks - // summary: Inspect a network - // description: Display low level configuration for a CNI network - // parameters: - // - in: path - // name: name - // type: string - // required: true - // description: the name of the network - // produces: - // - application/json - // responses: - // 200: - // $ref: "#/responses/NetworkInspectReport" - // 404: - // $ref: "#/responses/NoSuchNetwork" - // 500: - // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/networks/{name}/json"), s.APIHandler(libpod.InspectNetwork)).Methods(http.MethodGet) // swagger:operation GET /libpod/networks/{name}/exists libpod libpodExistsNetwork // --- // tags: @@ -289,6 +267,29 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error { // 500: // $ref: "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/networks/json"), s.APIHandler(libpod.ListNetworks)).Methods(http.MethodGet) + // swagger:operation GET /libpod/networks/{name}/json libpod libpodInspectNetwork + // --- + // tags: + // - networks + // summary: Inspect a network + // description: Display low level configuration for a CNI network + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: the name of the network + // produces: + // - application/json + // responses: + // 200: + // $ref: "#/responses/NetworkInspectReport" + // 404: + // $ref: "#/responses/NoSuchNetwork" + // 500: + // $ref: "#/responses/InternalError" + r.HandleFunc(VersionedPath("/libpod/networks/{name}/json"), s.APIHandler(libpod.InspectNetwork)).Methods(http.MethodGet) + r.HandleFunc(VersionedPath("/libpod/networks/{name}"), s.APIHandler(libpod.InspectNetwork)).Methods(http.MethodGet) // swagger:operation POST /libpod/networks/create libpod libpodCreateNetwork // --- // tags: diff --git a/pkg/api/server/swagger.go b/pkg/api/server/swagger.go index 92efb8ef3..12fd083bb 100644 --- a/pkg/api/server/swagger.go +++ b/pkg/api/server/swagger.go @@ -226,3 +226,12 @@ type swagSystemPruneReport struct { entities.SystemPruneReport } } + +// Auth response +// swagger:response SystemAuthResponse +type swagSystemAuthResponse struct { + // in:body + Body struct { + entities.AuthReport + } +} diff --git a/pkg/autoupdate/autoupdate.go b/pkg/autoupdate/autoupdate.go index 53095c295..e271b9466 100644 --- a/pkg/autoupdate/autoupdate.go +++ b/pkg/autoupdate/autoupdate.go @@ -13,7 +13,7 @@ import ( "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/image" "github.com/containers/podman/v3/pkg/systemd" - systemdGen "github.com/containers/podman/v3/pkg/systemd/generate" + systemdDefine "github.com/containers/podman/v3/pkg/systemd/define" "github.com/containers/podman/v3/pkg/util" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -178,10 +178,10 @@ func AutoUpdate(runtime *libpod.Runtime, options Options) ([]string, []error) { updatedUnits := []string{} for _, ctr := range containersToRestart { labels := ctr.Labels() - unit, exists := labels[systemdGen.EnvVariable] + unit, exists := labels[systemdDefine.EnvVariable] if !exists { // Shouldn't happen but let's be sure of it. - errs = append(errs, errors.Errorf("error auto-updating container %q: no %s label found", ctr.ID(), systemdGen.EnvVariable)) + errs = append(errs, errors.Errorf("error auto-updating container %q: no %s label found", ctr.ID(), systemdDefine.EnvVariable)) continue } _, err := conn.RestartUnit(unit, "replace", nil) diff --git a/pkg/domain/entities/system.go b/pkg/domain/entities/system.go index a1cfb4481..4b8383613 100644 --- a/pkg/domain/entities/system.go +++ b/pkg/domain/entities/system.go @@ -107,3 +107,14 @@ type ComponentVersion struct { type ListRegistriesReport struct { Registries []string } + +// swagger:model AuthConfig +type AuthConfig struct { + types.AuthConfig +} + +// AuthReport describes the response for authentication check +type AuthReport struct { + IdentityToken string + Status string +} diff --git a/pkg/domain/filters/containers.go b/pkg/domain/filters/containers.go index 6f4b4e8a0..98b8f7e88 100644 --- a/pkg/domain/filters/containers.go +++ b/pkg/domain/filters/containers.go @@ -7,7 +7,7 @@ import ( "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/libpod/network" + "github.com/containers/podman/v3/pkg/network" "github.com/containers/podman/v3/pkg/timetype" "github.com/containers/podman/v3/pkg/util" "github.com/pkg/errors" diff --git a/pkg/domain/filters/pods.go b/pkg/domain/filters/pods.go index 53d10213a..0490a4848 100644 --- a/pkg/domain/filters/pods.go +++ b/pkg/domain/filters/pods.go @@ -6,7 +6,7 @@ import ( "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/libpod/network" + "github.com/containers/podman/v3/pkg/network" "github.com/containers/podman/v3/pkg/util" "github.com/pkg/errors" ) diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index b7ca69281..efc7c86e3 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -173,13 +173,13 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY var ctrRestartPolicy string switch podYAML.Spec.RestartPolicy { case v1.RestartPolicyAlways: - ctrRestartPolicy = libpod.RestartPolicyAlways + ctrRestartPolicy = define.RestartPolicyAlways case v1.RestartPolicyOnFailure: - ctrRestartPolicy = libpod.RestartPolicyOnFailure + ctrRestartPolicy = define.RestartPolicyOnFailure case v1.RestartPolicyNever: - ctrRestartPolicy = libpod.RestartPolicyNo + ctrRestartPolicy = define.RestartPolicyNo default: // Default to Always - ctrRestartPolicy = libpod.RestartPolicyAlways + ctrRestartPolicy = define.RestartPolicyAlways } configMaps := []v1.ConfigMap{} diff --git a/pkg/network/network.go b/pkg/network/network.go new file mode 100644 index 000000000..44132ca28 --- /dev/null +++ b/pkg/network/network.go @@ -0,0 +1,27 @@ +package network + +import ( + "crypto/sha256" + "encoding/hex" + "strings" + + "github.com/containernetworking/cni/libcni" +) + +// GetCNIPlugins returns a list of plugins that a given network +// has in the form of a string +func GetCNIPlugins(list *libcni.NetworkConfigList) string { + plugins := make([]string, 0, len(list.Plugins)) + for _, plug := range list.Plugins { + plugins = append(plugins, plug.Network.Type) + } + return strings.Join(plugins, ",") +} + +// GetNetworkID return the network ID for a given name. +// It is just the sha256 hash but this should be good enough. +// The caller has to make sure it is only called with the network name. +func GetNetworkID(name string) string { + hash := sha256.Sum256([]byte(name)) + return hex.EncodeToString(hash[:]) +} diff --git a/pkg/systemd/define/const.go b/pkg/systemd/define/const.go new file mode 100644 index 000000000..1b50be5db --- /dev/null +++ b/pkg/systemd/define/const.go @@ -0,0 +1,9 @@ +package define + +// EnvVariable "PODMAN_SYSTEMD_UNIT" is set in all generated systemd units and +// is set to the unit's (unique) name. +const EnvVariable = "PODMAN_SYSTEMD_UNIT" + +// RestartPolicies includes all valid restart policies to be used in a unit +// file. +var RestartPolicies = []string{"no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", "always"} diff --git a/pkg/systemd/generate/common.go b/pkg/systemd/generate/common.go index bbd1a5f92..94a6f4cb5 100644 --- a/pkg/systemd/generate/common.go +++ b/pkg/systemd/generate/common.go @@ -4,25 +4,18 @@ import ( "strconv" "strings" + "github.com/containers/podman/v3/pkg/systemd/define" "github.com/pkg/errors" ) -// EnvVariable "PODMAN_SYSTEMD_UNIT" is set in all generated systemd units and -// is set to the unit's (unique) name. -const EnvVariable = "PODMAN_SYSTEMD_UNIT" - // minTimeoutStopSec is the minimal stop timeout for generated systemd units. // Once exceeded, processes of the services are killed and the cgroup(s) are // cleaned up. const minTimeoutStopSec = 60 -// RestartPolicies includes all valid restart policies to be used in a unit -// file. -var RestartPolicies = []string{"no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", "always"} - // validateRestartPolicy checks that the user-provided policy is valid. func validateRestartPolicy(restart string) error { - for _, i := range RestartPolicies { + for _, i := range define.RestartPolicies { if i == restart { return nil } diff --git a/pkg/systemd/generate/containers.go b/pkg/systemd/generate/containers.go index 92c6d8865..9343a5067 100644 --- a/pkg/systemd/generate/containers.go +++ b/pkg/systemd/generate/containers.go @@ -11,6 +11,7 @@ import ( "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/systemd/define" "github.com/containers/podman/v3/version" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -173,7 +174,7 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst info.Executable = executable } - info.EnvVariable = EnvVariable + info.EnvVariable = define.EnvVariable info.ExecStart = "{{{{.Executable}}}} start {{{{.ContainerNameOrID}}}}" info.ExecStop = "{{{{.Executable}}}} stop {{{{if (ge .StopTimeout 0)}}}}-t {{{{.StopTimeout}}}}{{{{end}}}} {{{{.ContainerNameOrID}}}}" info.ExecStopPost = "{{{{.Executable}}}} stop {{{{if (ge .StopTimeout 0)}}}}-t {{{{.StopTimeout}}}}{{{{end}}}} {{{{.ContainerNameOrID}}}}" diff --git a/pkg/systemd/generate/containers_test.go b/pkg/systemd/generate/containers_test.go index 747c54699..ebbbdb786 100644 --- a/pkg/systemd/generate/containers_test.go +++ b/pkg/systemd/generate/containers_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/systemd/define" "github.com/stretchr/testify/assert" ) @@ -398,7 +399,7 @@ WantedBy=multi-user.target default.target PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 22, PodmanVersion: "CI", - EnvVariable: EnvVariable, + EnvVariable: define.EnvVariable, }, goodID, false, @@ -414,7 +415,7 @@ WantedBy=multi-user.target default.target PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 22, PodmanVersion: "CI", - EnvVariable: EnvVariable, + EnvVariable: define.EnvVariable, }, goodIDNoHeaderInfo, false, @@ -430,7 +431,7 @@ WantedBy=multi-user.target default.target PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", - EnvVariable: EnvVariable, + EnvVariable: define.EnvVariable, }, goodName, false, @@ -447,7 +448,7 @@ WantedBy=multi-user.target default.target StopTimeout: 10, PodmanVersion: "CI", BoundToServices: []string{"pod", "a", "b", "c"}, - EnvVariable: EnvVariable, + EnvVariable: define.EnvVariable, }, goodNameBoundTo, false, @@ -462,7 +463,7 @@ WantedBy=multi-user.target default.target PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", - EnvVariable: EnvVariable, + EnvVariable: define.EnvVariable, }, "", false, @@ -479,7 +480,7 @@ WantedBy=multi-user.target default.target StopTimeout: 10, PodmanVersion: "CI", CreateCommand: []string{"I'll get stripped", "container", "run", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN", "foo=arg \"with \" space"}, - EnvVariable: EnvVariable, + EnvVariable: define.EnvVariable, }, goodWithNameAndGeneric, true, @@ -496,7 +497,7 @@ WantedBy=multi-user.target default.target StopTimeout: 10, PodmanVersion: "CI", CreateCommand: []string{"I'll get stripped", "run", "-d", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, - EnvVariable: EnvVariable, + EnvVariable: define.EnvVariable, }, goodWithExplicitShortDetachParam, true, @@ -513,7 +514,7 @@ WantedBy=multi-user.target default.target StopTimeout: 10, PodmanVersion: "CI", CreateCommand: []string{"I'll get stripped", "run", "-d", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, - EnvVariable: EnvVariable, + EnvVariable: define.EnvVariable, Pod: &podInfo{ PodIDFile: "%t/pod-foobar.pod-id-file", }, @@ -533,7 +534,7 @@ WantedBy=multi-user.target default.target StopTimeout: 10, PodmanVersion: "CI", CreateCommand: []string{"I'll get stripped", "run", "--detach", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, - EnvVariable: EnvVariable, + EnvVariable: define.EnvVariable, }, goodNameNewDetach, true, @@ -550,7 +551,7 @@ WantedBy=multi-user.target default.target StopTimeout: 10, PodmanVersion: "CI", CreateCommand: []string{"I'll get stripped", "run", "awesome-image:latest"}, - EnvVariable: EnvVariable, + EnvVariable: define.EnvVariable, }, goodIDNew, true, @@ -567,7 +568,7 @@ WantedBy=multi-user.target default.target StopTimeout: 42, PodmanVersion: "CI", CreateCommand: []string{"I'll get stripped", "run", "--detach=true", "awesome-image:latest"}, - EnvVariable: EnvVariable, + EnvVariable: define.EnvVariable, }, genGoodNewDetach("--detach=true"), true, @@ -584,7 +585,7 @@ WantedBy=multi-user.target default.target StopTimeout: 42, PodmanVersion: "CI", CreateCommand: []string{"I'll get stripped", "run", "--detach=false", "awesome-image:latest"}, - EnvVariable: EnvVariable, + EnvVariable: define.EnvVariable, }, genGoodNewDetach("-d"), true, @@ -601,7 +602,7 @@ WantedBy=multi-user.target default.target StopTimeout: 42, PodmanVersion: "CI", CreateCommand: []string{"I'll get stripped", "run", "--name", "test", "-p", "80:80", "--detach=false", "awesome-image:latest", "somecmd", "--detach=false"}, - EnvVariable: EnvVariable, + EnvVariable: define.EnvVariable, }, goodNameNewDetachFalseWithCmd, true, @@ -618,7 +619,7 @@ WantedBy=multi-user.target default.target StopTimeout: 42, PodmanVersion: "CI", CreateCommand: []string{"I'll get stripped", "run", "--name", "test", "-p", "80:80", "--detach=false", "--detach=false", "awesome-image:latest", "somecmd", "--detach=false"}, - EnvVariable: EnvVariable, + EnvVariable: define.EnvVariable, }, goodNameNewDetachFalseWithCmd, true, @@ -635,7 +636,7 @@ WantedBy=multi-user.target default.target StopTimeout: 42, PodmanVersion: "CI", CreateCommand: []string{"I'll get stripped", "run", "-dti", "awesome-image:latest"}, - EnvVariable: EnvVariable, + EnvVariable: define.EnvVariable, }, genGoodNewDetach("-dti"), true, @@ -652,7 +653,7 @@ WantedBy=multi-user.target default.target StopTimeout: 42, PodmanVersion: "CI", CreateCommand: []string{"I'll get stripped", "run", "-tid", "awesome-image:latest"}, - EnvVariable: EnvVariable, + EnvVariable: define.EnvVariable, }, genGoodNewDetach("-tid"), true, @@ -669,7 +670,7 @@ WantedBy=multi-user.target default.target StopTimeout: 42, PodmanVersion: "CI", CreateCommand: []string{"I'll get stripped", "--events-backend", "none", "--runroot", "/root", "run", "awesome-image:latest"}, - EnvVariable: EnvVariable, + EnvVariable: define.EnvVariable, }, goodNewRootFlags, true, @@ -686,7 +687,7 @@ WantedBy=multi-user.target default.target StopTimeout: 10, PodmanVersion: "CI", CreateCommand: []string{"I'll get stripped", "container", "create", "awesome-image:latest"}, - EnvVariable: EnvVariable, + EnvVariable: define.EnvVariable, }, goodContainerCreate, true, @@ -703,7 +704,7 @@ WantedBy=multi-user.target default.target StopTimeout: 10, PodmanVersion: "CI", CreateCommand: []string{"I'll get stripped", "create", "--name", "test", "--log-driver=journald", "--log-opt=tag={{.Name}}", "awesome-image:latest"}, - EnvVariable: EnvVariable, + EnvVariable: define.EnvVariable, }, goodNewWithJournaldTag, true, @@ -720,7 +721,7 @@ WantedBy=multi-user.target default.target StopTimeout: 10, PodmanVersion: "CI", CreateCommand: []string{"I'll get stripped", "create", "--name", "test", "awesome-image:latest", "sh", "-c", "kill $$ && echo %\\"}, - EnvVariable: EnvVariable, + EnvVariable: define.EnvVariable, }, goodNewWithSpecialChars, true, diff --git a/pkg/systemd/generate/pods.go b/pkg/systemd/generate/pods.go index 8c0401278..f96058d36 100644 --- a/pkg/systemd/generate/pods.go +++ b/pkg/systemd/generate/pods.go @@ -11,6 +11,7 @@ import ( "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/systemd/define" "github.com/containers/podman/v3/version" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -237,7 +238,7 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) info.Executable = executable } - info.EnvVariable = EnvVariable + info.EnvVariable = define.EnvVariable info.ExecStart = "{{{{.Executable}}}} start {{{{.InfraNameOrID}}}}" info.ExecStop = "{{{{.Executable}}}} stop {{{{if (ge .StopTimeout 0)}}}}-t {{{{.StopTimeout}}}}{{{{end}}}} {{{{.InfraNameOrID}}}}" info.ExecStopPost = "{{{{.Executable}}}} stop {{{{if (ge .StopTimeout 0)}}}}-t {{{{.StopTimeout}}}}{{{{end}}}} {{{{.InfraNameOrID}}}}" diff --git a/test/apiv2/01-basic.at b/test/apiv2/01-basic.at index 1357e0ca6..788007069 100644 --- a/test/apiv2/01-basic.at +++ b/test/apiv2/01-basic.at @@ -30,18 +30,18 @@ done # Garbage tests - requests that should yield errors # t GET /nonesuch 404 -t POST /nonesuch '' 404 +t POST /nonesuch 404 t GET container/nonesuch/json 404 t GET libpod/containers/nonesuch/json 404 #### FIXME: maybe someday: t GET 'libpod/containers/json?a=b' 400 # Method not allowed -t POST /_ping '' 405 +t POST /_ping 405 t DELETE /_ping 405 -t POST libpod/containers/json '' 405 -t POST libpod/pods/abc '' 405 -t POST info '' 405 +t POST libpod/containers/json 405 +t POST libpod/pods/abc 405 +t POST info 405 t GET libpod/containers/create 405 # diff --git a/test/apiv2/10-images.at b/test/apiv2/10-images.at index f866422e2..4ebaeff45 100644 --- a/test/apiv2/10-images.at +++ b/test/apiv2/10-images.at @@ -41,18 +41,18 @@ t GET images/$iid/json 200 \ .Id=sha256:$iid \ .RepoTags[0]=$IMAGE -t POST "images/create?fromImage=alpine" '' 200 .error~null .status~".*Download complete.*" +t POST "images/create?fromImage=alpine" 200 .error~null .status~".*Download complete.*" -t POST "images/create?fromImage=alpine&tag=latest" '' 200 +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.*" +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 +t POST "images/create?fromImage=quay.io/libpod/alpine&tag=sha256:fa93b01658e3a5a1686dc3ae55f170d8de487006fb53a28efcd12ab0710a2e5f" 200 # Display the image history t GET libpod/images/nonesuch/history 404 diff --git a/test/apiv2/12-imagesMore.at b/test/apiv2/12-imagesMore.at index ce3049106..144b83194 100644 --- a/test/apiv2/12-imagesMore.at +++ b/test/apiv2/12-imagesMore.at @@ -17,10 +17,10 @@ t GET libpod/images/$IMAGE/tree 200 \ .Tree~^Image # Tag nonesuch image -t POST "libpod/images/nonesuch/tag?repo=myrepo&tag=mytag" '' 404 +t POST "libpod/images/nonesuch/tag?repo=myrepo&tag=mytag" 404 # Tag the image -t POST "libpod/images/$IMAGE/tag?repo=localhost:5000/myrepo&tag=mytag" '' 201 +t POST "libpod/images/$IMAGE/tag?repo=localhost:5000/myrepo&tag=mytag" 201 t GET libpod/images/$IMAGE/json 200 \ .RepoTags[1]=localhost:5000/myrepo:mytag @@ -41,13 +41,13 @@ if [ -z "${GOT_DIGEST}" ] ; then fi # Push to local registry -t POST "images/localhost:5000/myrepo/push?tlsVerify=false&tag=mytag" '' 200 +t POST "images/localhost:5000/myrepo/push?tlsVerify=false&tag=mytag" 200 # Untag the image -t POST "libpod/images/$iid/untag?repo=localhost:5000/myrepo&tag=mytag" '' 201 +t POST "libpod/images/$iid/untag?repo=localhost:5000/myrepo&tag=mytag" 201 # Try to push non-existing image -t POST "images/localhost:5000/idonotexist/push?tlsVerify=false" '' 200 +t POST "images/localhost:5000/idonotexist/push?tlsVerify=false" 200 jq -re 'select(.errorDetail)' <<<"$output" &>/dev/null || echo -e "${red}not ok: error message not found in output${nc}" 1>&2 t GET libpod/images/$IMAGE/json 200 \ diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at index 383d92ef3..478717700 100644 --- a/test/apiv2/20-containers.at +++ b/test/apiv2/20-containers.at @@ -31,12 +31,16 @@ t GET libpod/containers/json?all=true 200 \ .[0].ExitCode=0 \ .[0].IsInfra=false -# Test compat API for Network Settings +# Test compat API for Network Settings (.Network is N/A when rootless) +network_expect= +if root; then + network_expect='.[0].NetworkSettings.Networks.podman.NetworkID=podman' +fi t GET /containers/json?all=true 200 \ length=1 \ .[0].Id~[0-9a-f]\\{64\\} \ .[0].Image=$IMAGE \ - .[0].NetworkSettings.Networks.podman.NetworkID=podman + $network_expect # Make sure `limit` works. t GET libpod/containers/json?limit=1 200 \ @@ -68,9 +72,9 @@ t POST libpod/containers/create?name=test_noargs Image=${IMAGE} 201 \ .Id~[0-9a-f]\\{64\\} cid=$(jq -r '.Id' <<<"$output") # Prior to the fix in #6835, this would fail 500 "args must not be empty" -t POST libpod/containers/${cid}/start '' 204 +t POST libpod/containers/${cid}/start 204 # Container should exit almost immediately. Wait for it, confirm successful run -t POST "libpod/containers/${cid}/wait?condition=stopped&condition=exited" '' 200 '0' +t POST "libpod/containers/${cid}/wait?condition=stopped&condition=exited" 200 '0' t GET libpod/containers/${cid}/json 200 \ .Id=$cid \ .State.Status~\\\(exited\\\|stopped\\\) \ @@ -85,15 +89,15 @@ t GET libpod/containers/json?all=true 200 \ cid=$(jq -r '.[0].Id' <<<"$output") # No such container -t POST "libpod/commit?container=nonesuch" '' 404 +t POST "libpod/commit?container=nonesuch" 404 # Comment can only be used with docker format, not OCI cparam="repo=newrepo&comment=foo&author=bob" -t POST "libpod/commit?container=$CNAME&$cparam" '' 500 \ +t POST "libpod/commit?container=$CNAME&$cparam" 500 \ .cause="messages are only compatible with the docker image format (-f docker)" # Commit a new image from the container -t POST "libpod/commit?container=$CNAME" '' 200 \ +t POST "libpod/commit?container=$CNAME" 200 \ .Id~[0-9a-f]\\{64\\} iid=$(jq -r '.Id' <<<"$output") t GET libpod/images/$iid/json 200 \ @@ -103,7 +107,7 @@ t GET libpod/images/$iid/json 200 \ # Commit a new image w/o tag cparam="repo=newrepo&comment=foo&author=bob&format=docker" -t POST "libpod/commit?container=$CNAME&$cparam" '' 200 +t POST "libpod/commit?container=$CNAME&$cparam" 200 t GET libpod/images/newrepo:latest/json 200 \ .RepoTags[0]=localhost/newrepo:latest \ .Author=bob \ @@ -111,7 +115,7 @@ t GET libpod/images/newrepo:latest/json 200 \ # Commit a new image w/ specified tag and author cparam="repo=newrepo&tag=v1&author=alice" -t POST "libpod/commit?container=$cid&$cparam&pause=false" '' 200 +t POST "libpod/commit?container=$cid&$cparam&pause=false" 200 t GET libpod/images/newrepo:v1/json 200 \ .RepoTags[0]=localhost/newrepo:v1 \ .Author=alice @@ -119,7 +123,7 @@ t GET libpod/images/newrepo:v1/json 200 \ # Commit a new image w/ full parameters cparam="repo=newrepo&tag=v2&comment=bar&author=eric" cparam="$cparam&format=docker&changes=CMD=/bin/foo" -t POST "libpod/commit?container=${cid:0:12}&$cparam&pause=true" '' 200 +t POST "libpod/commit?container=${cid:0:12}&$cparam&pause=true" 200 t GET libpod/images/newrepo:v2/json 200 \ .RepoTags[0]=localhost/newrepo:v2 \ .Author=eric \ @@ -143,7 +147,7 @@ cpid_file=$(jq -r '.ConmonPidFile' <<<"$output") userdata_path=$(dirname $cpid_file) # Initializing the container -t POST libpod/containers/myctr/init '' 204 +t POST libpod/containers/myctr/init 204 # Check configuration after initializing t GET libpod/containers/myctr/json 200 \ @@ -167,7 +171,11 @@ t DELETE libpod/containers/bogus 404 # test apiv2 create container with correct entrypoint and cmd # --data '{"Image":"quay.io/libpod/alpine_labels:latest","Entrypoint":["echo"],"Cmd":["param1","param2"]}' -t POST containers/create '"Image":"'$IMAGE'","Entrypoint":["echo"],"Cmd":["param1","param2"]' 201 \ +t POST containers/create \ + Image=$IMAGE \ + Entrypoint='["echo"]' \ + Cmd='["param1","param2"]' \ + 201 \ .Id~[0-9a-f]\\{64\\} cid=$(jq -r '.Id' <<<"$output") t GET containers/$cid/json 200 \ @@ -180,7 +188,10 @@ t GET containers/$cid/json 200 \ t DELETE containers/$cid 204 # test only set the entrypoint, Cmd should be [] -t POST containers/create '"Image":"'$IMAGE'","Entrypoint":["echo","param1"]' 201 \ +t POST containers/create \ + Image=$IMAGE \ + Entrypoint='["echo","param1"]' \ + 201 \ .Id~[0-9a-f]\\{64\\} cid=$(jq -r '.Id' <<<"$output") t GET containers/$cid/json 200 \ @@ -191,14 +202,14 @@ t GET containers/$cid/json 200 \ .Args[0]="param1" # create a running container for after -t POST containers/create '"Image":"'$IMAGE'","Entrypoint":["top"]' 201 \ +t POST containers/create Image=$IMAGE Entrypoint='["top"]' 201 \ .Id~[0-9a-f]\\{64\\} cid_top=$(jq -r '.Id' <<<"$output") t GET containers/${cid_top}/json 200 \ .Config.Entrypoint[0]="top" \ .Config.Cmd='[]' \ .Path="top" -t POST containers/${cid_top}/start '' 204 +t POST containers/${cid_top}/start 204 # make sure the container is running t GET containers/${cid_top}/json 200 \ .State.Status="running" @@ -220,13 +231,17 @@ t GET containers/json?filters='{"id":["'${cid}'","'${cid_top}'"],"status":["runn length=1 \ .[0].Id=${cid_top} -t POST containers/${cid_top}/stop "" 204 +t POST containers/${cid_top}/stop 204 t DELETE containers/$cid 204 t DELETE containers/$cid_top 204 # test the WORKDIR and StopSignal -t POST containers/create '"Image":"'$ENV_WORKDIR_IMG'","WorkingDir":"/dataDir","StopSignal":"9"' 201 \ +t POST containers/create \ + Image=$ENV_WORKDIR_IMG \ + WorkingDir=/dataDir \ + StopSignal=9 \ + 201 \ .Id~[0-9a-f]\\{64\\} cid=$(jq -r '.Id' <<<"$output") t GET containers/$cid/json 200 \ @@ -247,7 +262,7 @@ t DELETE images/${MultiTagName}?force=true 200 # vim: filetype=sh # Test Volumes field adds an anonymous volume -t POST containers/create '"Image":"'$IMAGE'","Volumes":{"/test":{}}' 201 \ +t POST containers/create Image=$IMAGE Volumes='{"/test":{}}' 201 \ .Id~[0-9a-f]\\{64\\} cid=$(jq -r '.Id' <<<"$output") t GET containers/$cid/json 200 \ @@ -266,7 +281,7 @@ t GET containers/json 200 \ podman stop bar # Test CPU limit (NanoCPUs) -t POST containers/create '"Image":"'$IMAGE'","HostConfig":{"NanoCpus":500000}' 201 \ +t POST containers/create Image=$IMAGE HostConfig='{"NanoCpus":500000}' 201 \ .Id~[0-9a-f]\\{64\\} cid=$(jq -r '.Id' <<<"$output") t GET containers/$cid/json 200 \ diff --git a/test/apiv2/22-stop.at b/test/apiv2/22-stop.at index 11318ca81..91bc9937d 100644 --- a/test/apiv2/22-stop.at +++ b/test/apiv2/22-stop.at @@ -8,17 +8,17 @@ podman pull $IMAGE &>/dev/null # stop, by name podman run -dt --name mytop $IMAGE top &>/dev/null -t GET libpod/containers/mytop/json 200 .State.Status=running -t POST libpod/containers/mytop/stop "" 204 -t GET libpod/containers/mytop/json 200 .State.Status~\\\(exited\\\|stopped\\\) -t DELETE libpod/containers/mytop 204 +t GET libpod/containers/mytop/json 200 .State.Status=running +t POST libpod/containers/mytop/stop 204 +t GET libpod/containers/mytop/json 200 .State.Status~\\\(exited\\\|stopped\\\) +t DELETE libpod/containers/mytop 204 # stop, by ID # Remember that podman() hides all output; we need to get our CID via inspect podman run -dt --name mytop $IMAGE top -t GET libpod/containers/mytop/json 200 .State.Status=running +t GET libpod/containers/mytop/json 200 .State.Status=running cid=$(jq -r .Id <<<"$output") -t POST libpod/containers/$cid/stop "" 204 -t GET libpod/containers/mytop/json 200 .State.Status~\\\(exited\\\|stopped\\\) -t DELETE libpod/containers/mytop 204 +t POST libpod/containers/$cid/stop 204 +t GET libpod/containers/mytop/json 200 .State.Status~\\\(exited\\\|stopped\\\) +t DELETE libpod/containers/mytop 204 diff --git a/test/apiv2/25-containersMore.at b/test/apiv2/25-containersMore.at index b88c798eb..39bfa2e32 100644 --- a/test/apiv2/25-containersMore.at +++ b/test/apiv2/25-containersMore.at @@ -17,7 +17,7 @@ t GET libpod/containers/nonesuch/exists 404 t GET libpod/containers/foo/exists 204 # Pause the container -t POST libpod/containers/foo/pause '' 204 +t POST libpod/containers/foo/pause 204 t GET libpod/containers/foo/json 200 \ .Id~[0-9a-f]\\{64\\} \ @@ -27,7 +27,7 @@ t GET libpod/containers/foo/json 200 \ .Name=foo # Unpause the container -t POST libpod/containers/foo/unpause '' 204 +t POST libpod/containers/foo/unpause 204 t GET libpod/containers/foo/json 200 \ .Id~[0-9a-f]\\{64\\} \ @@ -44,11 +44,11 @@ t GET libpod/containers/foo/top 200 \ t GET libpod/containers/nonesuch/top 404 # Mount the container to host filesystem -t POST libpod/containers/foo/mount '' 200 +t POST libpod/containers/foo/mount 200 like "$output" ".*merged" "Check container mount" # Unmount the container -t POST libpod/containers/foo/unmount '' 204 +t POST libpod/containers/foo/unmount 204 t DELETE libpod/containers/foo?force=true 204 @@ -85,7 +85,7 @@ podman run $IMAGE true podman run $IMAGE true podman run $IMAGE true -t POST libpod/containers/prune '' 200 +t POST libpod/containers/prune 200 t GET libpod/containers/json 200 \ length=0 # vim: filetype=sh diff --git a/test/apiv2/26-containersWait.at b/test/apiv2/26-containersWait.at index 3f530c3f0..6a628e55a 100644 --- a/test/apiv2/26-containersWait.at +++ b/test/apiv2/26-containersWait.at @@ -13,15 +13,15 @@ podman rm -a -f &>/dev/null CTR="WaitTestingCtr" -t POST "containers/nonExistent/wait?condition=next-exit" '' 404 +t POST "containers/nonExistent/wait?condition=next-exit" 404 podman create --name "${CTR}" --entrypoint '["sleep", "0.5"]' "${IMAGE}" -t POST "containers/${CTR}/wait?condition=non-existent-cond" '' 400 +t POST "containers/${CTR}/wait?condition=non-existent-cond" 400 -t POST "containers/${CTR}/wait?condition=not-running" '' 200 +t POST "containers/${CTR}/wait?condition=not-running" 200 -t POST "containers/${CTR}/wait?condition=next-exit" '' 200 & +t POST "containers/${CTR}/wait?condition=next-exit" 200 & child_pid=$! podman start "${CTR}" wait "${child_pid}" @@ -37,7 +37,7 @@ if kill -2 "${child_pid}" 2> "/dev/null"; then WAIT_TEST_ERROR="1" fi -t POST "containers/${CTR}/wait?condition=removed" '' 200 & +t POST "containers/${CTR}/wait?condition=removed" 200 & child_pid=$! podman container rm "${CTR}" wait "${child_pid}" diff --git a/test/apiv2/30-volumes.at b/test/apiv2/30-volumes.at index cf4b3d3ea..c27c638bb 100644 --- a/test/apiv2/30-volumes.at +++ b/test/apiv2/30-volumes.at @@ -13,25 +13,34 @@ t POST libpod/volumes/create name=foo1 201 \ .CreatedAt~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* \ .Labels={} \ .Options={} -t POST libpod/volumes/create '' 201 +t POST libpod/volumes/create 201 t POST libpod/volumes/create \ - '"Name":"foo2","Label":{"testlabel":"testonly"},"Options":{"type":"tmpfs","o":"nodev,noexec"}}' 201 \ - .Name=foo2 \ - .Labels.testlabel=testonly \ - .Options.type=tmpfs \ - .Options.o=nodev,noexec + Name=foo2 \ + Label='{"testlabel":"testonly"}' \ + Options='{"type":"tmpfs","o":"nodev,noexec"}}' \ + 201 \ + .Name=foo2 \ + .Labels.testlabel=testonly \ + .Options.type=tmpfs \ + .Options.o=nodev,noexec t POST libpod/volumes/create \ - '"Name":"foo3","Label":{"testlabel":""},"Options":{"type":"tmpfs","o":"nodev,noexec"}}' 201 \ - .Name=foo3 \ - .Labels.testlabel="" \ - .Options.type=tmpfs \ - .Options.o=nodev,noexec + Name=foo3 \ + Label='{"testlabel":""}' \ + Options='{"type":"tmpfs","o":"nodev,noexec"}}' \ + 201 \ + .Name=foo3 \ + .Labels.testlabel="" \ + .Options.type=tmpfs \ + .Options.o=nodev,noexec t POST libpod/volumes/create \ - '"Name":"foo4","Label":{"testlabel1":"testonly"},"Options":{"type":"tmpfs","o":"nodev,noexec"}}' 201 \ - .Name=foo4 \ - .Labels.testlabel1=testonly \ - .Options.type=tmpfs \ - .Options.o=nodev,noexec + Name=foo4 \ + Label='{"testlabel1":"testonly"}' \ + Options='{"type":"tmpfs","o":"nodev,noexec"}}' \ + 201 \ + .Name=foo4 \ + .Labels.testlabel1=testonly \ + .Options.type=tmpfs \ + .Options.o=nodev,noexec # Negative test # We have created a volume named "foo1" @@ -78,15 +87,15 @@ t DELETE libpod/volumes/foo1 404 \ .response=404 ## Prune volumes with label matching 'testlabel1=testonly' -t POST libpod/volumes/prune?filters='{"label":["testlabel1=testonly"]}' "" 200 +t POST libpod/volumes/prune?filters='{"label":["testlabel1=testonly"]}' 200 t GET libpod/volumes/json?filters='{"label":["testlabel1=testonly"]}' 200 length=0 ## Prune volumes with label matching 'testlabel' -t POST libpod/volumes/prune?filters='{"label":["testlabel"]}' "" 200 +t POST libpod/volumes/prune?filters='{"label":["testlabel"]}' 200 t GET libpod/volumes/json?filters='{"label":["testlabel"]}' 200 length=0 ## Prune volumes -t POST libpod/volumes/prune "" 200 +t POST libpod/volumes/prune 200 #After prune volumes, there should be no volume existing t GET libpod/volumes/json 200 length=0 diff --git a/test/apiv2/35-networks.at b/test/apiv2/35-networks.at index 93a9c9fe4..ce7ca628a 100644 --- a/test/apiv2/35-networks.at +++ b/test/apiv2/35-networks.at @@ -6,18 +6,21 @@ t GET networks/non-existing-network 404 \ .cause='network not found' -t POST libpod/networks/create?name=network1 '' 200 \ +t POST libpod/networks/create?name=network1 200 \ .Filename~.*/network1\\.conflist # --data '{"Subnet":{"IP":"10.10.254.0","Mask":[255,255,255,0]},"Labels":{"abc":"val"}}' -t POST libpod/networks/create?name=network2 '"Subnet":{"IP":"10.10.254.0","Mask":[255,255,255,0]},"Labels":{"abc":"val"}' 200 \ +t POST libpod/networks/create?name=network2 \ + Subnet='{"IP":"10.10.254.0","Mask":[255,255,255,0]}' \ + Labels='{"abc":"val"}' \ + 200 \ .Filename~.*/network2\\.conflist # test for empty mask -t POST libpod/networks/create '"Subnet":{"IP":"10.10.1.0","Mask":[]}' 500 \ +t POST libpod/networks/create Subnet='{"IP":"10.10.1.0","Mask":[]}' 500 \ .cause~'.*cannot be empty' # test for invalid mask -t POST libpod/networks/create '"Subnet":{"IP":"10.10.1.0","Mask":[0,255,255,0]}' 500 \ +t POST libpod/networks/create Subnet='{"IP":"10.10.1.0","Mask":[0,255,255,0]}' 500 \ .cause~'.*mask is invalid' # network list @@ -59,7 +62,7 @@ t GET networks/a7662f44d65029fd4635c91feea3d720a57cef52e2a9fcc7772b69072cc1ccd1 .Scope=local # network create docker -t POST networks/create '"Name":"net3","IPAM":{"Config":[]}' 201 +t POST networks/create Name=net3\ IPAM='{"Config":[]}' 201 # network delete docker t DELETE networks/net3 204 diff --git a/test/apiv2/40-pods.at b/test/apiv2/40-pods.at index ce65105d2..f3272c41e 100644 --- a/test/apiv2/40-pods.at +++ b/test/apiv2/40-pods.at @@ -25,70 +25,70 @@ t POST "libpod/pods/create (dup pod)" name=foo 409 \ #t POST libpod/pods/create a=b 400 .cause='bad parameter' # FIXME: unimplemented -t POST libpod/pods/foo/start '' 200 \ +t POST libpod/pods/foo/start 200 \ .Errs=null \ .Id=$pod_id -t POST libpod/pods/foo/start '' 304 \ +t POST libpod/pods/foo/start 304 \ -t POST libpod/pods/fakename/start '' 404 \ +t POST libpod/pods/fakename/start 404 \ .cause="no such pod" \ .message="no pod with name or ID fakename found: no such pod" if root || have_cgroupsv2; then - t POST libpod/pods/foo/pause '' 200 + t POST libpod/pods/foo/pause 200 else # Rootless cgroupsv1 : unsupported - t POST "libpod/pods/foo/pause (rootless cgroups v1)" '' 500 \ + t POST "libpod/pods/foo/pause (rootless cgroups v1)" 500 \ .cause="this container does not have a cgroup" \ .message~".*pause pods containing rootless containers with cgroup V1" fi -t POST libpod/pods/foo/unpause '' 200 -t POST "libpod/pods/foo/unpause (2nd unpause in a row)" '' 200 -t POST "libpod/pods/fakename/unpause" '' 404\ +t POST libpod/pods/foo/unpause 200 +t POST "libpod/pods/foo/unpause (2nd unpause in a row)" 200 +t POST "libpod/pods/fakename/unpause" 404\ .cause="no such pod" \ .message="no pod with name or ID fakename found: no such pod" -t POST libpod/pods/foo/stop '' 200 \ +t POST libpod/pods/foo/stop 200 \ .Errs=null \ .Id=$pod_id -t POST "libpod/pods/foo/stop (pod is already stopped)" '' 304 -t POST "libpod/pods/fakename/stop" '' 404\ +t POST "libpod/pods/foo/stop (pod is already stopped)" 304 +t POST "libpod/pods/fakename/stop" 404\ .cause="no such pod" \ .message="no pod with name or ID fakename found: no such pod" -t POST libpod/pods/foo/restart '' 200 \ +t POST libpod/pods/foo/restart 200 \ .Errs=null \ .Id=$pod_id -t POST "libpod/pods/bar/restart (restart on nonexistent pod)" '' 404 +t POST "libpod/pods/bar/restart (restart on nonexistent pod)" 404 t POST libpod/pods/create name=bar 201 .Id~[0-9a-f]\\{64\\} pod_bar_id=$(jq -r .Id <<<"$output") -t POST libpod/pods/bar/restart '' 200 \ +t POST libpod/pods/bar/restart 200 \ .Errs=null \ .Id=$pod_bar_id -t GET libpod/pods/bar/json 200 \ +t GET libpod/pods/bar/json 200 \ .State=Running -t POST libpod/pods/bar/restart '' 200 \ +t POST libpod/pods/bar/restart 200 \ .Errs=null \ .Id=$pod_bar_id -t POST "libpod/pods/bar/stop?t=invalid" '' 400 \ +t POST "libpod/pods/bar/stop?t=invalid" 400 \ .cause="schema: error converting value for \"t\"" \ .message~"failed to parse parameters for" podman run -d --pod bar busybox sleep 999 -t POST libpod/pods/bar/stop?t=1 '' 200 \ +t POST libpod/pods/bar/stop?t=1 200 \ .Errs=null \ .Id=$pod_bar_id -t POST libpod/pods/bar/start '' 200 +t POST libpod/pods/bar/start 200 t GET libpod/pods/stats?all=true 200 is $(jq '. | length' <<<"$output") 3 "stats?all=true: number of records found" @@ -118,7 +118,7 @@ t GET libpod/pods/foo/top?ps_args=args,pid 200 \ # FIXME: I'm not sure what 'prune' is supposed to do; as of 20200224 it # just returns 200 (ok) with empty result list. -#t POST libpod/pods/prune '' 200 # FIXME: 2020-02-24 returns 200 {} +#t POST libpod/pods/prune 200 # FIXME: 2020-02-24 returns 200 {} #t POST libpod/pods/prune 'a=b' 400 # FIXME: 2020-02-24 returns 200 # Clean up; and try twice, making sure that the second time fails diff --git a/test/apiv2/44-mounts.at b/test/apiv2/44-mounts.at index 5dc560852..d54669e7d 100644 --- a/test/apiv2/44-mounts.at +++ b/test/apiv2/44-mounts.at @@ -4,7 +4,12 @@ podman pull $IMAGE &>/dev/null # Test various HostConfig options tmpfs_name="/mytmpfs" -t POST containers/create?name=hostconfig_test '"Image":"'$IMAGE'","Cmd":["df"],"HostConfig":{"Binds":["/tmp/doesnotexist:/test1"],"TmpFs":{"'$tmpfs_name'":"rw"}}' 201 \ +t POST containers/create?name=hostconfig_test \ + Image=$IMAGE \ + Cmd='["df","-P","'$tmpfs_name'"]' \ + HostConfig='{"Binds":["/tmp/doesnotexist:/test1"]' \ + TmpFs="{\"$tmpfs_name\":\"rw\"}}" \ + 201 \ .Id~[0-9a-f]\\{64\\} cid=$(jq -r '.Id' <<<"$output") @@ -13,9 +18,14 @@ t GET containers/${cid}/json 200 \ .HostConfig.Tmpfs[\"${tmpfs_name}\"]~rw, # Run the container, verify output -t POST containers/${cid}/start '' 204 -t POST containers/${cid}/wait '' 200 +t POST containers/${cid}/start 204 +t POST containers/${cid}/wait 200 t GET containers/${cid}/logs?stdout=true 200 -like "$(<$WORKDIR/curl.result.out)" ".* ${tmpfs_name}" \ +# /logs returns application/octet-stream, which our test helper saves in +# an outfile rather than returning in $output. That's why we can't test +# this directly in the /logs test above; instead, we rely on knowing the +# path to the stored results. The 'tr' is needed because there may be +# null bytes in the outfile. +like "$(tr -d \\0 <$WORKDIR/curl.result.out)" ".* ${tmpfs_name}" \ "'df' output includes tmpfs name" diff --git a/test/apiv2/45-system.at b/test/apiv2/45-system.at index ad4bdf4f7..364b87c56 100644 --- a/test/apiv2/45-system.at +++ b/test/apiv2/45-system.at @@ -27,22 +27,28 @@ t GET libpod/system/df 200 '.Volumes[0].VolumeName=foo1' # Create two more volumes to test pruneing t POST libpod/volumes/create \ - '"Name":"foo2","Label":{"testlabel1":""},"Options":{"type":"tmpfs","o":"nodev,noexec"}}' 201 \ - .Name=foo2 \ - .Driver=local \ - .Mountpoint=$volumepath/foo2/_data \ - .CreatedAt~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* \ - .Labels.testlabel1="" \ - .Options.o=nodev,noexec + Name=foo2 \ + Label='{"testlabel1":""}' \ + Options='{"type":"tmpfs","o":"nodev,noexec"}}' \ + 201 \ + .Name=foo2 \ + .Driver=local \ + .Mountpoint=$volumepath/foo2/_data \ + .CreatedAt~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* \ + .Labels.testlabel1="" \ + .Options.o=nodev,noexec t POST libpod/volumes/create \ - '"Name":"foo3","Label":{"testlabel1":"testonly"},"Options":{"type":"tmpfs","o":"nodev,noexec"}}' 201 \ - .Name=foo3 \ - .Driver=local \ - .Mountpoint=$volumepath/foo3/_data \ - .CreatedAt~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* \ - .Labels.testlabel1=testonly \ - .Options.o=nodev,noexec + Name=foo3 \ + Label='{"testlabel1":"testonly"}' \ + Options='{"type":"tmpfs","o":"nodev,noexec"}}' \ + 201 \ + .Name=foo3 \ + .Driver=local \ + .Mountpoint=$volumepath/foo3/_data \ + .CreatedAt~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* \ + .Labels.testlabel1=testonly \ + .Options.o=nodev,noexec t GET system/df 200 '.Volumes | length=3' t GET libpod/system/df 200 '.Volumes | length=3' diff --git a/test/apiv2/50-secrets.at b/test/apiv2/50-secrets.at index c4ffb5883..034ec080a 100644 --- a/test/apiv2/50-secrets.at +++ b/test/apiv2/50-secrets.at @@ -4,14 +4,14 @@ # # secret create -t POST secrets/create '"Name":"mysecret","Data":"c2VjcmV0"' 200\ +t POST secrets/create Name=mysecret Data=c2VjcmV0 200\ .ID~.* \ # secret create unsupported labels -t POST secrets/create '"Name":"mysecret","Data":"c2VjcmV0","Labels":{"fail":"fail"}' 400 +t POST secrets/create Name=mysecret Data=c2VjcmV0 Labels='{"fail":"fail"}' 400 # secret create name already in use -t POST secrets/create '"Name":"mysecret","Data":"c2VjcmV0"' 409 +t POST secrets/create Name=mysecret Data=c2VjcmV0 409 # secret inspect t GET secrets/mysecret 200 \ @@ -36,4 +36,4 @@ t DELETE secrets/mysecret 204 t DELETE secrets/bogus 404 # secret update not implemented -t POST secrets/mysecret/update "" 501 +t POST secrets/mysecret/update 501 diff --git a/test/apiv2/60-auth.at b/test/apiv2/60-auth.at index 378955cd7..cfde519c1 100644 --- a/test/apiv2/60-auth.at +++ b/test/apiv2/60-auth.at @@ -5,25 +5,19 @@ start_registry -# FIXME FIXME FIXME: remove the 'if false' for use with PR 9589 -if false; then - -# FIXME FIXME: please forgive the horrible POST params format; I have an -# upcoming PR which should fix that. - # Test with wrong password. Confirm bad status and appropriate error message -t POST /v1.40/auth "\"username\":\"${REGISTRY_USERNAME}\",\"password\":\"WrOnGPassWord\",\"serveraddress\":\"localhost:$REGISTRY_PORT/\"" \ +t POST /v1.40/auth username=$REGISTRY_USERNAME password=WrOnGPassWord serveraddress=localhost:$REGISTRY_PORT/ \ 400 \ .Status~'.* invalid username/password' -# Test with the right password. Confirm status message and reasonable token -t POST /v1.40/auth "\"username\":\"${REGISTRY_USERNAME}\",\"password\":\"${REGISTRY_PASSWORD}\",\"serveraddress\":\"localhost:$REGISTRY_PORT/\"" \ +# Test with the right password. Confirm status message +t POST /v1.40/auth username=$REGISTRY_USERNAME password=$REGISTRY_PASSWORD serveraddress=localhost:$REGISTRY_PORT/ \ 200 \ .Status="Login Succeeded" \ - .IdentityToken~[a-zA-Z0-9] - -# FIXME: now what? Try something-something using that token? -token=$(jq -r .IdentityToken <<<"$output") -# ... + .IdentityToken="" -fi # FIXME FIXME FIXME: remove when working +# Same test with url scheme provided +t POST /v1.40/auth username=$REGISTRY_USERNAME password=$REGISTRY_PASSWORD serveraddress=https://localhost:$REGISTRY_PORT/ \ + 200 \ + .Status="Login Succeeded" \ + .IdentityToken="" diff --git a/test/apiv2/README.md b/test/apiv2/README.md index 252d6454e..19727cec7 100644 --- a/test/apiv2/README.md +++ b/test/apiv2/README.md @@ -52,9 +52,14 @@ Notes: If there's no leading slash, `t` prepends `/v1.40`. This is a simple convenience for simplicity of writing tests. -* When method is POST, the argument after the endpoint must be a series -of POST arguments in the form 'key=value', separated by commas. `t` will -convert those to JSON form for passing to the server. +* When method is POST, the argument(s) after the endpoint may be a series +of POST parameters in the form 'key=value', separated by spaces: + t POST myentrypoint 200 ! no params + t POST myentrypoint id=$id 200 ! just one + t POST myentrypoint id=$id filter='{"foo":"bar"}' 200 ! two, with json + t POST myentrypoint name=$name badparam='["foo","bar"]' 500 ! etc... +`t` will convert the param list to JSON form for passing to the server. +A numeric status code terminates processing of POST parameters. * The final arguments are one or more expected string results. If an argument starts with a dot, `t` will invoke `jq` on the output to diff --git a/test/apiv2/rest_api/__init__.py b/test/apiv2/rest_api/__init__.py index b7b8a7649..0ad6b51b3 100644 --- a/test/apiv2/rest_api/__init__.py +++ b/test/apiv2/rest_api/__init__.py @@ -3,6 +3,7 @@ import json import os import shutil import subprocess +import sys import tempfile @@ -27,7 +28,9 @@ class Podman(object): self.cmd.append("--root=" + os.path.join(self.anchor_directory, "crio")) self.cmd.append("--runroot=" + os.path.join(self.anchor_directory, "crio-run")) - os.environ["CONTAINERS_REGISTRIES_CONF"] = os.path.join(self.anchor_directory, "registry.conf") + os.environ["CONTAINERS_REGISTRIES_CONF"] = os.path.join( + self.anchor_directory, "registry.conf" + ) p = configparser.ConfigParser() p.read_dict( { @@ -114,13 +117,20 @@ class Podman(object): check = kwargs.get("check", False) shell = kwargs.get("shell", False) - return subprocess.run( - cmd, - shell=shell, - check=check, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) + try: + return subprocess.run( + cmd, + shell=shell, + check=check, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + except subprocess.CalledProcessError as e: + if e.stdout: + sys.stdout.write("\nRun Stdout:\n" + e.stdout.decode("utf-8")) + if e.stderr: + sys.stderr.write("\nRun Stderr:\n" + e.stderr.decode("utf-8")) + raise def tear_down(self): shutil.rmtree(self.anchor_directory, ignore_errors=True) diff --git a/test/apiv2/rest_api/test_rest_v2_0_0.py b/test/apiv2/rest_api/test_rest_v2_0_0.py index 8a78f5185..d7910f555 100644 --- a/test/apiv2/rest_api/test_rest_v2_0_0.py +++ b/test/apiv2/rest_api/test_rest_v2_0_0.py @@ -50,23 +50,20 @@ class TestApi(unittest.TestCase): def setUp(self): super().setUp() - try: - TestApi.podman.run("run", "alpine", "/bin/ls", check=True) - except subprocess.CalledProcessError as e: - if e.stdout: - sys.stdout.write("\nRun Stdout:\n" + e.stdout.decode("utf-8")) - if e.stderr: - sys.stderr.write("\nRun Stderr:\n" + e.stderr.decode("utf-8")) - raise + TestApi.podman.run("run", "alpine", "/bin/ls", check=True) + + def tearDown(self) -> None: + super().tearDown() + + TestApi.podman.run("pod", "rm", "--all", "--force", check=True) + TestApi.podman.run("rm", "--all", "--force", check=True) @classmethod def setUpClass(cls): super().setUpClass() TestApi.podman = Podman() - TestApi.service = TestApi.podman.open( - "system", "service", "tcp:localhost:8080", "--time=0" - ) + TestApi.service = TestApi.podman.open("system", "service", "tcp:localhost:8080", "--time=0") # give the service some time to be ready... time.sleep(2) @@ -243,9 +240,7 @@ class TestApi(unittest.TestCase): def test_post_create_compat(self): """Create network and connect container during create""" - net = requests.post( - PODMAN_URL + "/v1.40/networks/create", json={"Name": "TestNetwork"} - ) + net = requests.post(PODMAN_URL + "/v1.40/networks/create", json={"Name": "TestNetwork"}) self.assertEqual(net.status_code, 201, net.text) create = requests.post( @@ -454,15 +449,11 @@ class TestApi(unittest.TestCase): self.assertIn(k, o) def test_network_compat(self): - name = "Network_" + "".join( - random.choice(string.ascii_letters) for i in range(10) - ) + name = "Network_" + "".join(random.choice(string.ascii_letters) for i in range(10)) # Cannot test for 0 existing networks because default "podman" network always exists - create = requests.post( - PODMAN_URL + "/v1.40/networks/create", json={"Name": name} - ) + create = requests.post(PODMAN_URL + "/v1.40/networks/create", json={"Name": name}) self.assertEqual(create.status_code, 201, create.content) obj = json.loads(create.content) self.assertIn(type(obj), (dict,)) @@ -492,9 +483,7 @@ class TestApi(unittest.TestCase): self.assertEqual(inspect.status_code, 404, inspect.content) # network prune - prune_name = "Network_" + "".join( - random.choice(string.ascii_letters) for i in range(10) - ) + prune_name = "Network_" + "".join(random.choice(string.ascii_letters) for i in range(10)) prune_create = requests.post( PODMAN_URL + "/v1.40/networks/create", json={"Name": prune_name} ) @@ -506,9 +495,7 @@ class TestApi(unittest.TestCase): self.assertTrue(prune_name in obj["NetworksDeleted"]) def test_volumes_compat(self): - name = "Volume_" + "".join( - random.choice(string.ascii_letters) for i in range(10) - ) + name = "Volume_" + "".join(random.choice(string.ascii_letters) for i in range(10)) ls = requests.get(PODMAN_URL + "/v1.40/volumes") self.assertEqual(ls.status_code, 200, ls.content) @@ -524,9 +511,7 @@ class TestApi(unittest.TestCase): for k in required_keys: self.assertIn(k, obj) - create = requests.post( - PODMAN_URL + "/v1.40/volumes/create", json={"Name": name} - ) + create = requests.post(PODMAN_URL + "/v1.40/volumes/create", json={"Name": name}) self.assertEqual(create.status_code, 201, create.content) # See https://docs.docker.com/engine/api/v1.40/#operation/VolumeCreate @@ -570,17 +555,6 @@ class TestApi(unittest.TestCase): self.assertIn(name, payload["VolumesDeleted"]) self.assertGreater(payload["SpaceReclaimed"], 0) - def test_auth_compat(self): - r = requests.post( - PODMAN_URL + "/v1.40/auth", - json={ - "username": "bozo", - "password": "wedontneednopasswords", - "serveraddress": "https://localhost/v1.40/", - }, - ) - self.assertEqual(r.status_code, 404, r.content) - def test_version(self): r = requests.get(PODMAN_URL + "/v1.40/version") self.assertEqual(r.status_code, 200, r.content) @@ -703,21 +677,15 @@ class TestApi(unittest.TestCase): """Verify issue #8865""" pod_name = list() - pod_name.append( - "Pod_" + "".join(random.choice(string.ascii_letters) for i in range(10)) - ) - pod_name.append( - "Pod_" + "".join(random.choice(string.ascii_letters) for i in range(10)) - ) + pod_name.append("Pod_" + "".join(random.choice(string.ascii_letters) for i in range(10))) + pod_name.append("Pod_" + "".join(random.choice(string.ascii_letters) for i in range(10))) r = requests.post( _url("/pods/create"), json={ "name": pod_name[0], "no_infra": False, - "portmappings": [ - {"host_ip": "127.0.0.1", "host_port": 8889, "container_port": 89} - ], + "portmappings": [{"host_ip": "127.0.0.1", "host_port": 8889, "container_port": 89}], }, ) self.assertEqual(r.status_code, 201, r.text) @@ -736,9 +704,7 @@ class TestApi(unittest.TestCase): json={ "name": pod_name[1], "no_infra": False, - "portmappings": [ - {"host_ip": "127.0.0.1", "host_port": 8889, "container_port": 89} - ], + "portmappings": [{"host_ip": "127.0.0.1", "host_port": 8889, "container_port": 89}], }, ) self.assertEqual(r.status_code, 201, r.text) diff --git a/test/apiv2/test-apiv2 b/test/apiv2/test-apiv2 index e32d6bc62..9f6bf257f 100755 --- a/test/apiv2/test-apiv2 +++ b/test/apiv2/test-apiv2 @@ -156,19 +156,23 @@ function _bump() { # jsonify # convert 'foo=bar,x=y' to json {"foo":"bar","x":"y"} ############# function jsonify() { - # split by comma - local -a settings_in - read -ra settings_in <<<"$1" - # convert each to double-quoted form local -a settings_out - for i in ${settings_in[*]}; do - settings_out+=$(sed -e 's/\(.*\)=\(.*\)/"\1":"\2"/' <<<$i) + for i in "$@"; do + # Each argument is of the form foo=bar. Separate into left and right. + local lhs + local rhs + IFS='=' read lhs rhs <<<"$i" + + # If right-hand side already includes double quotes, do nothing + if [[ ! $rhs =~ \" ]]; then + rhs="\"${rhs}\"" + fi + settings_out+=("\"${lhs}\":${rhs}") done - # ...and wrap inside braces. - # FIXME: handle commas - echo "{${settings_out[*]}}" + # ...and wrap inside braces, with comma separator if multiple fields + (IFS=','; echo "{${settings_out[*]}}") } ####### @@ -180,11 +184,19 @@ function t() { local curl_args local testname="$method $path" - # POST requests require an extra params arg + # POST requests may be followed by one or more key=value pairs. + # Slurp the command line until we see a 3-digit status code. if [[ $method = "POST" ]]; then - curl_args="-d $(jsonify $1)" + local -a post_args + for arg; do + case "$arg" in + *=*) post_args+=("$arg"); shift ;; + [1-9][0-9][0-9]) break;; + *) die "Internal error: invalid POST arg '$arg'" ;; + esac + done + curl_args="-d $(jsonify ${post_args[@]})" testname="$testname [$curl_args]" - shift fi # entrypoint path can include a descriptive comment; strip it off diff --git a/test/e2e/containers_conf_test.go b/test/e2e/containers_conf_test.go index 6b1a0d16e..aa2380c51 100644 --- a/test/e2e/containers_conf_test.go +++ b/test/e2e/containers_conf_test.go @@ -311,7 +311,7 @@ var _ = Describe("Podman run", func() { session = podmanTest.Podman([]string{"run", ALPINE, "date", "+'%H %Z'"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(session.OutputToString()).To(ContainSubstring("EST")) + Expect(session.OutputToString()).To(Or(ContainSubstring("EST"), ContainSubstring("EDT"))) // Umask session = podmanTest.Podman([]string{"run", "--rm", ALPINE, "sh", "-c", "umask"}) diff --git a/test/system/065-cp.bats b/test/system/065-cp.bats index 73e807843..679cdc209 100644 --- a/test/system/065-cp.bats +++ b/test/system/065-cp.bats @@ -370,6 +370,7 @@ load helpers is "${lines[0]}" "${randomcontent[0]}" "eval symlink - created container" is "${lines[1]}" "${randomcontent[1]}" "eval symlink - created container" run_podman rm -f cpcontainer + run_podman rmi $cpimage } diff --git a/test/system/070-build.bats b/test/system/070-build.bats index d413b0c10..8f6cdb46b 100644 --- a/test/system/070-build.bats +++ b/test/system/070-build.bats @@ -241,11 +241,21 @@ EOF build_arg_implicit+="=$arg_implicit_value" fi + # FIXME FIXME FIXME: 2021-03-15: workaround for #9567 (slow ubuntu 2004): + # we're seeing lots of timeouts in CI. Until/unless #9567 gets fixed, + # let's get CI passing by extending the timeout when remote on ubuntu + local localtimeout=${PODMAN_TIMEOUT} + if is_remote; then + if grep -qi ubuntu /etc/os-release; then + localtimeout=$(( 2 * $localtimeout )) + fi + fi + # cd to the dir, so we test relative paths (important for podman-remote) cd $PODMAN_TMPDIR export arg_explicit="THIS SHOULD BE OVERRIDDEN BY COMMAND LINE!" export arg_implicit=${arg_implicit_value} - run_podman ${MOUNTS_CONF} build \ + PODMAN_TIMEOUT=$localtimeout run_podman ${MOUNTS_CONF} build \ --build-arg arg_explicit=${arg_explicit_value} \ $build_arg_implicit \ --dns-search $nosuchdomain \ @@ -594,34 +604,46 @@ EOF run_podman rmi -a --force } +# Caveat lector: this test was mostly copy-pasted from buildah in #9275. +# It's not entirely clear what it's testing, or if the 'mount' section is +# necessary. @test "build with copy-from referencing the base image" { - skip_if_rootless "cannot mount as rootless" - target=busybox-derived - target_mt=busybox-mt-derived + target=derived + target_mt=derived-mt tmpdir=$PODMAN_TMPDIR/build-test mkdir -p $tmpdir + containerfile1=$tmpdir/Containerfile1 - cat >$containerfile1 <<EOF -FROM quay.io/libpod/busybox AS build -RUN rm -f /bin/paste + cat >$containerfile1 <<EOF +FROM $IMAGE AS build +RUN rm -f /etc/issue USER 1001 -COPY --from=quay.io/libpod/busybox /bin/paste /test/ +COPY --from=$IMAGE /etc/issue /test/ EOF + containerfile2=$tmpdir/Containerfile2 - cat >$containerfile2 <<EOF -FROM quay.io/libpod/busybox AS test -RUN rm -f /bin/nl + cat >$containerfile2 <<EOF +FROM $IMAGE AS test +RUN rm -f /etc/alpine-release FROM quay.io/libpod/alpine AS final -COPY --from=quay.io/libpod/busybox /bin/nl /test/ +COPY --from=$IMAGE /etc/alpine-release /test/ EOF - run_podman build -t ${target} -f ${containerfile1} ${tmpdir} - run_podman build --jobs 4 -t ${target} -f ${containerfile1} ${tmpdir} - run_podman build -t ${target} -f ${containerfile2} ${tmpdir} + # Before the build, $IMAGE's base image should not be present + local base_image=quay.io/libpod/alpine:latest + run_podman 1 image exists $base_image + + run_podman build --jobs 1 -t ${target} -f ${containerfile2} ${tmpdir} run_podman build --no-cache --jobs 4 -t ${target_mt} -f ${containerfile2} ${tmpdir} + # After the build, the base image should exist + run_podman image exists $base_image + # (can only test locally; podman-remote has no image mount command) - if ! is_remote; then + # (can also only test as root; mounting under rootless podman is too hard) + # We perform the test as a conditional, not a 'skip', because there's + # value in testing the above 'build' commands even remote & rootless. + if ! is_remote && ! is_rootless; then run_podman image mount ${target} root_single_job=$output @@ -629,8 +651,21 @@ EOF root_multi_job=$output # Check that both the version with --jobs 1 and --jobs=N have the same number of files - test $(find $root_single_job -type f | wc -l) = $(find $root_multi_job -type f | wc -l) + nfiles_single=$(find $root_single_job -type f | wc -l) + nfiles_multi=$(find $root_multi_job -type f | wc -l) + run_podman image umount ${target_mt} + run_podman image umount ${target} + + is "$nfiles_single" "$nfiles_multi" \ + "Number of files (--jobs=1) == (--jobs=4)" + + # Make sure the number is reasonable + test "$nfiles_single" -gt 50 fi + + # Clean up + run_podman rmi ${target_mt} ${target} ${base_image} + run_podman image prune -f } @test "podman build --logfile test" { diff --git a/test/system/120-load.bats b/test/system/120-load.bats index 936449bdb..95113c4a6 100644 --- a/test/system/120-load.bats +++ b/test/system/120-load.bats @@ -31,6 +31,9 @@ verify_iid_and_name() { invalid=$PODMAN_TMPDIR/invalid echo "I am an invalid file and should cause a podman-load error" > $invalid run_podman 125 load -i $invalid + # podman and podman-remote emit different messages; this is a common string + is "$output" ".*error pulling image: unable to pull .*" \ + "load -i INVALID fails with expected diagnostic" } @test "podman save to pipe and load" { diff --git a/test/system/260-sdnotify.bats b/test/system/260-sdnotify.bats index a5fa0f4e6..8bf49eb1d 100644 --- a/test/system/260-sdnotify.bats +++ b/test/system/260-sdnotify.bats @@ -42,14 +42,22 @@ function _start_socat() { _SOCAT_LOG="$PODMAN_TMPDIR/socat.log" rm -f $_SOCAT_LOG - socat unix-recvfrom:"$NOTIFY_SOCKET",fork \ - system:"(cat;echo) >> $_SOCAT_LOG" & + # Execute in subshell so we can close fd3 (which BATS uses). + # This is a superstitious ritual to try to avoid leaving processes behind, + # and thus prevent CI hangs. + (exec socat unix-recvfrom:"$NOTIFY_SOCKET",fork \ + system:"(cat;echo) >> $_SOCAT_LOG" 3>&-) & _SOCAT_PID=$! } # Stop the socat background process and clean up logs function _stop_socat() { if [[ -n "$_SOCAT_PID" ]]; then + # Kill all child processes, then the process itself. + # This is a superstitious incantation to avoid leaving processes behind. + # The '|| true' is because only f35 leaves behind socat processes; + # f33 (and perhaps others?) behave nicely. ARGH! + pkill -P $_SOCAT_PID || true kill $_SOCAT_PID fi _SOCAT_PID= @@ -57,6 +65,7 @@ function _stop_socat() { if [[ -n "$_SOCAT_LOG" ]]; then rm -f $_SOCAT_LOG fi + _SOCAT_LOG= } # Check that MAINPID=xxxxx points to a running conmon process diff --git a/test/system/700-play.bats b/test/system/700-play.bats index e7904f59f..8fa96741c 100644 --- a/test/system/700-play.bats +++ b/test/system/700-play.bats @@ -5,6 +5,20 @@ load helpers +# This is a long ugly way to clean up pods and remove the pause image +function teardown() { + run_podman pod rm -f -a + run_podman rm -f -a + run_podman image list --format '{{.ID}} {{.Repository}}' + while read id name; do + if [[ "$name" =~ /pause ]]; then + run_podman rmi $id + fi + done <<<"$output" + + basic_teardown +} + testYaml=" apiVersion: v1 kind: Pod @@ -24,7 +38,7 @@ spec: value: xterm - name: container value: podman - image: quay.io/libpod/alpine:latest + image: $IMAGE name: test resources: {} securityContext: diff --git a/vendor/github.com/magefile/mage/LICENSE b/vendor/github.com/magefile/mage/LICENSE deleted file mode 100644 index d0632bc14..000000000 --- a/vendor/github.com/magefile/mage/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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 deleted file mode 100644 index 3e2710332..000000000 --- a/vendor/github.com/magefile/mage/mg/color.go +++ /dev/null @@ -1,80 +0,0 @@ -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 deleted file mode 100644 index 06debca54..000000000 --- a/vendor/github.com/magefile/mage/mg/color_string.go +++ /dev/null @@ -1,38 +0,0 @@ -// 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 deleted file mode 100644 index ad85931f8..000000000 --- a/vendor/github.com/magefile/mage/mg/deps.go +++ /dev/null @@ -1,352 +0,0 @@ -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 deleted file mode 100644 index 2dd780fe3..000000000 --- a/vendor/github.com/magefile/mage/mg/errors.go +++ /dev/null @@ -1,51 +0,0 @@ -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 deleted file mode 100644 index 9a8de12ce..000000000 --- a/vendor/github.com/magefile/mage/mg/runtime.go +++ /dev/null @@ -1,136 +0,0 @@ -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 deleted file mode 100644 index 06af62de2..000000000 --- a/vendor/github.com/magefile/mage/sh/cmd.go +++ /dev/null @@ -1,177 +0,0 @@ -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 deleted file mode 100644 index f5d20a271..000000000 --- a/vendor/github.com/magefile/mage/sh/helpers.go +++ /dev/null @@ -1,40 +0,0 @@ -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/sirupsen/logrus/.travis.yml b/vendor/github.com/sirupsen/logrus/.travis.yml index e6ee8b3ab..c1dbd5a3a 100644 --- a/vendor/github.com/sirupsen/logrus/.travis.yml +++ b/vendor/github.com/sirupsen/logrus/.travis.yml @@ -9,6 +9,7 @@ os: linux install: - ./travis/install.sh script: - - go run mage.go -v crossBuild - - go run mage.go lint - - go run mage.go test + - cd ci + - go run mage.go -v -w ../ crossBuild + - go run mage.go -v -w ../ lint + - go run mage.go -v -w ../ test diff --git a/vendor/github.com/sirupsen/logrus/CHANGELOG.md b/vendor/github.com/sirupsen/logrus/CHANGELOG.md index 311f2c339..7567f6128 100644 --- a/vendor/github.com/sirupsen/logrus/CHANGELOG.md +++ b/vendor/github.com/sirupsen/logrus/CHANGELOG.md @@ -1,3 +1,12 @@ +# 1.8.1 +Code quality: + * move magefile in its own subdir/submodule to remove magefile dependency on logrus consumer + * improve timestamp format documentation + +Fixes: + * fix race condition on logger hooks + + # 1.8.0 Correct versioning number replacing v1.7.1. diff --git a/vendor/github.com/sirupsen/logrus/entry.go b/vendor/github.com/sirupsen/logrus/entry.go index c968f6344..07a1e5fa7 100644 --- a/vendor/github.com/sirupsen/logrus/entry.go +++ b/vendor/github.com/sirupsen/logrus/entry.go @@ -261,7 +261,15 @@ func (entry *Entry) log(level Level, msg string) { } func (entry *Entry) fireHooks() { - err := entry.Logger.Hooks.Fire(entry.Level, entry) + var tmpHooks LevelHooks + entry.Logger.mu.Lock() + tmpHooks = make(LevelHooks, len(entry.Logger.Hooks)) + for k, v := range entry.Logger.Hooks { + tmpHooks[k] = v + } + entry.Logger.mu.Unlock() + + err := tmpHooks.Fire(entry.Level, entry) if err != nil { fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) } diff --git a/vendor/github.com/sirupsen/logrus/go.mod b/vendor/github.com/sirupsen/logrus/go.mod index 37004ff34..b3919d5ea 100644 --- a/vendor/github.com/sirupsen/logrus/go.mod +++ b/vendor/github.com/sirupsen/logrus/go.mod @@ -2,7 +2,6 @@ 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 bce26a188..694c18b84 100644 --- a/vendor/github.com/sirupsen/logrus/go.sum +++ b/vendor/github.com/sirupsen/logrus/go.sum @@ -1,12 +1,8 @@ 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= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/vendor/github.com/sirupsen/logrus/json_formatter.go b/vendor/github.com/sirupsen/logrus/json_formatter.go index afaf0fc8a..c96dc5636 100644 --- a/vendor/github.com/sirupsen/logrus/json_formatter.go +++ b/vendor/github.com/sirupsen/logrus/json_formatter.go @@ -23,6 +23,9 @@ func (f FieldMap) resolve(key fieldKey) string { // JSONFormatter formats logs into parsable json type JSONFormatter struct { // TimestampFormat sets the format used for marshaling timestamps. + // The format to use is the same than for time.Format or time.Parse from the standard + // library. + // The standard Library already provides a set of predefined format. TimestampFormat string // DisableTimestamp allows disabling automatic timestamps in output diff --git a/vendor/github.com/sirupsen/logrus/magefile.go b/vendor/github.com/sirupsen/logrus/magefile.go deleted file mode 100644 index 9aa603939..000000000 --- a/vendor/github.com/sirupsen/logrus/magefile.go +++ /dev/null @@ -1,77 +0,0 @@ -// +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/text_formatter.go b/vendor/github.com/sirupsen/logrus/text_formatter.go index 8fc698ad6..be2c6efe5 100644 --- a/vendor/github.com/sirupsen/logrus/text_formatter.go +++ b/vendor/github.com/sirupsen/logrus/text_formatter.go @@ -53,7 +53,10 @@ type TextFormatter struct { // the time passed since beginning of execution. FullTimestamp bool - // TimestampFormat to use for display when a full timestamp is printed + // TimestampFormat to use for display when a full timestamp is printed. + // The format to use is the same than for time.Format or time.Parse from the standard + // library. + // The standard Library already provides a set of predefined format. TimestampFormat string // The fields are sorted by default for a consistent output. For applications diff --git a/vendor/modules.txt b/vendor/modules.txt index e5534675f..4c16ca2fd 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -369,9 +369,6 @@ 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 @@ -531,7 +528,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.8.0 +# github.com/sirupsen/logrus v1.8.1 github.com/sirupsen/logrus github.com/sirupsen/logrus/hooks/syslog # github.com/spf13/cobra v1.1.3 |