From d59a570c3d9e91ab5e8e7ddc4282c2087d390f96 Mon Sep 17 00:00:00 2001
From: Milos Gajdos
Date: Tue, 19 Dec 2023 14:34:01 +0000
Subject: [PATCH 01/98] update: set User-Agent header in GCS storage driver
Signed-off-by: Milos Gajdos
---
registry/storage/driver/gcs/gcs.go | 24 +++++++++++++-----------
1 file changed, 13 insertions(+), 11 deletions(-)
diff --git a/registry/storage/driver/gcs/gcs.go b/registry/storage/driver/gcs/gcs.go
index beb9261e..d2cdb596 100644
--- a/registry/storage/driver/gcs/gcs.go
+++ b/registry/storage/driver/gcs/gcs.go
@@ -155,6 +155,7 @@ func FromParameters(ctx context.Context, parameters map[string]interface{}) (sto
jwtConf := new(jwt.Config)
var err error
var gcs *storage.Client
+ var options []option.ClientOption
if keyfile, ok := parameters["keyfile"]; ok {
jsonKey, err := os.ReadFile(fmt.Sprint(keyfile))
if err != nil {
@@ -165,10 +166,7 @@ func FromParameters(ctx context.Context, parameters map[string]interface{}) (sto
return nil, err
}
ts = jwtConf.TokenSource(ctx)
- gcs, err = storage.NewClient(ctx, option.WithCredentialsFile(fmt.Sprint(keyfile)))
- if err != nil {
- return nil, err
- }
+ options = append(options, option.WithCredentialsFile(fmt.Sprint(keyfile)))
} else if credentials, ok := parameters["credentials"]; ok {
credentialMap, ok := credentials.(map[interface{}]interface{})
if !ok {
@@ -194,10 +192,7 @@ func FromParameters(ctx context.Context, parameters map[string]interface{}) (sto
return nil, err
}
ts = jwtConf.TokenSource(ctx)
- gcs, err = storage.NewClient(ctx, option.WithCredentialsJSON(data))
- if err != nil {
- return nil, err
- }
+ options = append(options, option.WithCredentialsJSON(data))
} else {
var err error
// DefaultTokenSource is a convenience method. It first calls FindDefaultCredentials,
@@ -207,12 +202,19 @@ func FromParameters(ctx context.Context, parameters map[string]interface{}) (sto
if err != nil {
return nil, err
}
- gcs, err = storage.NewClient(ctx)
- if err != nil {
- return nil, err
+ }
+
+ if userAgent, ok := parameters["useragent"]; ok {
+ if ua, ok := userAgent.(string); ok && ua != "" {
+ options = append(options, option.WithUserAgent(ua))
}
}
+ gcs, err = storage.NewClient(ctx, options...)
+ if err != nil {
+ return nil, err
+ }
+
maxConcurrency, err := base.GetLimitFromParameter(parameters["maxconcurrency"], minConcurrency, defaultMaxConcurrency)
if err != nil {
return nil, fmt.Errorf("maxconcurrency config error: %s", err)
From ab27c9d5f18cb080aff37770d1c55f2554c6b307 Mon Sep 17 00:00:00 2001
From: Cory Snider
Date: Tue, 19 Dec 2023 12:56:46 -0500
Subject: [PATCH 02/98] version: use go list -m
It appears that the value of Package is intended to be what is nowadays
called the module path, not the path to the version package. This also
fixes the issue of the version file being regenerated incorrectly under
shell redirection as the go list command no longer attempts to parse .go
files under the version package.
$ ./version.sh > version.go
version.go:1:1: expected 'package', found 'EOF'
Signed-off-by: Cory Snider
---
version/version.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version/version.sh b/version/version.sh
index 75dca784..bef4e74d 100755
--- a/version/version.sh
+++ b/version/version.sh
@@ -12,7 +12,7 @@ package version
// Package is the overall, canonical project import path under which the
// package was built.
-var Package = "$(go list)"
+var Package = "$(go list -m)"
// Version indicates which version of the binary is running. This is set to
// the latest release tag by hand, always suffixed by "+unknown". During
From a74cacff04e76ec91230ed407a10d50f64c028ef Mon Sep 17 00:00:00 2001
From: Cory Snider
Date: Tue, 19 Dec 2023 13:02:44 -0500
Subject: [PATCH 03/98] version: export getter functions
Future-proof the version package's exported interface by only making the
data available through getter functions. This affords us the flexibility
to e.g. implement them in terms of "runtime/debug".ReadBuildInfo() in
the future.
Signed-off-by: Cory Snider
---
Makefile | 2 +-
registry/handlers/app.go | 2 +-
registry/registry.go | 2 +-
tracing/tracing.go | 2 +-
version/{print.go => get.go} | 20 +++++++++++++++++++-
version/version.go | 12 ++++++------
version/version.sh | 12 ++++++------
7 files changed, 35 insertions(+), 17 deletions(-)
rename version/{print.go => get.go} (53%)
diff --git a/Makefile b/Makefile
index 2723a2af..23567898 100644
--- a/Makefile
+++ b/Makefile
@@ -37,7 +37,7 @@ WHALE = "+"
TESTFLAGS_RACE=
GOFILES=$(shell find . -type f -name '*.go')
GO_TAGS=$(if $(BUILDTAGS),-tags "$(BUILDTAGS)",)
-GO_LDFLAGS=-ldflags '-extldflags "-Wl,-z,now" -s -w -X $(PKG)/version.Version=$(VERSION) -X $(PKG)/version.Revision=$(REVISION) -X $(PKG)/version.Package=$(PKG) $(EXTRA_LDFLAGS)'
+GO_LDFLAGS=-ldflags '-extldflags "-Wl,-z,now" -s -w -X $(PKG)/version.version=$(VERSION) -X $(PKG)/version.revision=$(REVISION) -X $(PKG)/version.mainpkg=$(PKG) $(EXTRA_LDFLAGS)'
BINARIES=$(addprefix bin/,$(COMMANDS))
diff --git a/registry/handlers/app.go b/registry/handlers/app.go
index 64e49e41..2983176b 100644
--- a/registry/handlers/app.go
+++ b/registry/handlers/app.go
@@ -114,7 +114,7 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
storageParams = make(configuration.Parameters)
}
if storageParams["useragent"] == "" {
- storageParams["useragent"] = fmt.Sprintf("distribution/%s %s", version.Version, runtime.Version())
+ storageParams["useragent"] = fmt.Sprintf("distribution/%s %s", version.Version(), runtime.Version())
}
var err error
diff --git a/registry/registry.go b/registry/registry.go
index 6fb79f18..875e776a 100644
--- a/registry/registry.go
+++ b/registry/registry.go
@@ -99,7 +99,7 @@ var ServeCmd = &cobra.Command{
Long: "`serve` stores and distributes Docker images.",
Run: func(cmd *cobra.Command, args []string) {
// setup context
- ctx := dcontext.WithVersion(dcontext.Background(), version.Version)
+ ctx := dcontext.WithVersion(dcontext.Background(), version.Version())
config, err := resolveConfiguration(args)
if err != nil {
diff --git a/tracing/tracing.go b/tracing/tracing.go
index 8a8559e7..c75acf81 100644
--- a/tracing/tracing.go
+++ b/tracing/tracing.go
@@ -26,7 +26,7 @@ func InitOpenTelemetry(ctx context.Context) error {
res := resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String(serviceName),
- semconv.ServiceVersionKey.String(version.Version),
+ semconv.ServiceVersionKey.String(version.Version()),
)
exp, err := autoexport.NewSpanExporter(ctx)
diff --git a/version/print.go b/version/get.go
similarity index 53%
rename from version/print.go
rename to version/get.go
index 5529da85..db68e6d4 100644
--- a/version/print.go
+++ b/version/get.go
@@ -6,6 +6,24 @@ import (
"os"
)
+// Package returns the overall, canonical project import path under
+// which the package was built.
+func Package() string {
+ return mainpkg
+}
+
+// Version returns returns the module version the running binary was
+// built from.
+func Version() string {
+ return version
+}
+
+// Revision returns the VCS (e.g. git) revision being used to build
+// the program at linking time.
+func Revision() string {
+ return revision
+}
+
// FprintVersion outputs the version string to the writer, in the following
// format, followed by a newline:
//
@@ -16,7 +34,7 @@ import (
//
// registry github.com/distribution/distribution v2.0
func FprintVersion(w io.Writer) {
- fmt.Fprintln(w, os.Args[0], Package, Version)
+ fmt.Fprintln(w, os.Args[0], Package(), Version())
}
// PrintVersion outputs the version information, from Fprint, to stdout.
diff --git a/version/version.go b/version/version.go
index a99b7759..ceaa82f2 100644
--- a/version/version.go
+++ b/version/version.go
@@ -1,15 +1,15 @@
package version
-// Package is the overall, canonical project import path under which the
+// mainpkg is the overall, canonical project import path under which the
// package was built.
-var Package = "github.com/distribution/distribution/v3"
+var mainpkg = "github.com/distribution/distribution/v3"
-// Version indicates which version of the binary is running. This is set to
+// version indicates which version of the binary is running. This is set to
// the latest release tag by hand, always suffixed by "+unknown". During
// build, it will be replaced by the actual version. The value here will be
// used if the registry is run after a go get based install.
-var Version = "v3.0.0-alpha.1"
+var version = "v3.0.0-alpha.1.m+unknown"
-// Revision is filled with the VCS (e.g. git) revision being used to build
+// revision is filled with the VCS (e.g. git) revision being used to build
// the program at linking time.
-var Revision = ""
+var revision = ""
diff --git a/version/version.sh b/version/version.sh
index bef4e74d..176a370a 100755
--- a/version/version.sh
+++ b/version/version.sh
@@ -10,17 +10,17 @@ set -e
cat <
Date: Wed, 20 Dec 2023 14:24:15 +0000
Subject: [PATCH 04/98] feat: add GH issue template
Signed-off-by: Milos Gajdos
---
.github/ISSUE_TEMPLATE/bug_report.yml | 48 ++++++++++++++++++++++
.github/ISSUE_TEMPLATE/config.yml | 8 ++++
.github/ISSUE_TEMPLATE/feature_request.yml | 12 ++++++
3 files changed, 68 insertions(+)
create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml
create mode 100644 .github/ISSUE_TEMPLATE/config.yml
create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 00000000..d1d091bb
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,48 @@
+name: Bug report
+description: Create a report to help us improve
+labels:
+ - kind/bug
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thank you for taking the time to report a bug!
+ If this is a security issue please report it to the [Distributions Security Mailing List](mailto:cncf-distribution-security@lists.cncf.io).
+ - type: textarea
+ id: description
+ attributes:
+ label: Description
+ description: Please give a clear and concise description of the bug
+ validations:
+ required: true
+ - type: textarea
+ id: repro
+ attributes:
+ label: Reproduce
+ description: Steps to reproduce the bug
+ placeholder: |
+ 1. start registry version X ...
+ 2. `docker push image:tag` ...
+ validations:
+ required: true
+ - type: textarea
+ id: expected
+ attributes:
+ label: Expected behavior
+ description: What is the expected behavior?
+ placeholder: |
+ E.g. "registry returns an incorrect API error"
+ - type: textarea
+ id: version
+ attributes:
+ label: registry version
+ description: Output of `registry --version`. Alternatively tell us the docker image tag.
+ validations:
+ required: true
+ - type: textarea
+ id: additional
+ attributes:
+ label: Additional Info
+ description: Additional info you want to provide such as logs, system info, environment, etc.
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 00000000..8965f208
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,8 @@
+blank_issues_enabled: false
+contact_links:
+ - name: Security and Vulnerabilities
+ url: https://github.com/distribution/distribution/blob/main/SECURITY.md
+ about: Please report any security issues or vulnerabilities responsibly to the distribution maintainers team. Please do not use the public issue tracker.
+ - name: Questions and Discussions
+ url: https://github.com/distribution/distribution/discussions/new/choose
+ about: Use Github Discussions to ask questions and/or open discussion topics.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 00000000..08ea159f
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,12 @@
+name: Feature request
+description: Missing functionality? Come tell us about it!
+labels:
+ - kind/feature
+body:
+ - type: textarea
+ id: description
+ attributes:
+ label: Description
+ description: What is the feature you want to see?
+ validations:
+ required: true
From 599290318264d3aa6e9b7f4ec24f9a56462bd168 Mon Sep 17 00:00:00 2001
From: Milos Gajdos
Date: Wed, 20 Dec 2023 15:34:13 +0000
Subject: [PATCH 05/98] fix: build status badge
At some point we renamed the build workflow from CI to build but forgot
to update the build status badge link in the readme. This fixes it.
Signed-off-by: Milos Gajdos
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 01563cde..af73fb61 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
-[![Build Status](https://github.com/distribution/distribution/workflows/CI/badge.svg?branch=main&event=push)](https://github.com/distribution/distribution/actions?query=workflow%3ACI)
+[![Build Status](https://github.com/distribution/distribution/workflows/build/badge.svg?branch=main&event=push)](https://github.com/distribution/distribution/actions/workflows/build.yml?query=workflow%3Abuild)
[![GoDoc](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/distribution/distribution)
[![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](LICENSE)
[![codecov](https://codecov.io/gh/distribution/distribution/branch/main/graph/badge.svg)](https://codecov.io/gh/distribution/distribution)
From 0e0d74b03763eabf1c2b6bf30d2d2203901f4195 Mon Sep 17 00:00:00 2001
From: Steven Kalt
Date: Thu, 21 Dec 2023 08:00:21 -0500
Subject: [PATCH 06/98] docs: remove legacy kramdown options from link
I was reading https://distribution.github.io/distribution/recipes/mirror/#gotcha when I noticed some unexpected annotations after the "fair use policy" link. According to [Stack Overflow](https://stackoverflow.com/a/4705645/6571327), these are kramdown options that the current hugo documentation site isn't respecting. I searched the hugo docs and couldn't find an easy way to preserve `rel="noopener" target="_blank"` behavior, so I removed the annotation.
Signed-off-by: Steven Kalt
---
docs/content/recipes/mirror.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/content/recipes/mirror.md b/docs/content/recipes/mirror.md
index 524ef1dd..f11f9cba 100644
--- a/docs/content/recipes/mirror.md
+++ b/docs/content/recipes/mirror.md
@@ -38,7 +38,7 @@ The following table shows examples of allowed and disallowed mirror URLs.
> **Note**
>
-> Mirrors of Docker Hub are still subject to Docker's [fair usage policy](https://www.docker.com/pricing/resource-consumption-updates){: target="blank" rel="noopener" class=“”}.
+> Mirrors of Docker Hub are still subject to Docker's [fair usage policy](https://www.docker.com/pricing/resource-consumption-updates).
### Solution
From e29a5c8e68353705ea282d1e13f26937da199944 Mon Sep 17 00:00:00 2001
From: Milos Gajdos
Date: Thu, 21 Dec 2023 08:43:24 +0000
Subject: [PATCH 07/98] update: readme cleanup and fxes
Signed-off-by: Milos Gajdos
---
README.md | 2 +-
docs/dockerhub.md | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index af73fb61..76f837a3 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,7 @@ This repository contains the following components:
|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **registry** | An implementation of the [OCI Distribution Specification](https://github.com/opencontainers/distribution-spec). |
| **libraries** | A rich set of libraries for interacting with distribution components. Please see [godoc](https://pkg.go.dev/github.com/distribution/distribution) for details. **Note**: The interfaces for these libraries are **unstable**. |
-| **documentation** | Docker's full documentation set is available at [docs.docker.com](https://docs.docker.com). This repository [contains the subset](docs/) related just to the registry. |
+| **documentation** | Full documentation is available at [https://distribution.github.io/distribution](https://distribution.github.io/distribution/).
### How does this integrate with Docker, containerd, and other OCI client?
diff --git a/docs/dockerhub.md b/docs/dockerhub.md
index 55bb8aab..7db49e9b 100644
--- a/docs/dockerhub.md
+++ b/docs/dockerhub.md
@@ -5,7 +5,7 @@ This repository provides container images for the Open Source Registry implement
-[![Build Status](https://github.com/distribution/distribution/workflows/CI/badge.svg?branch=main&event=push)](https://github.com/distribution/distribution/actions?query=workflow%3ACI)
+[![Build Status](https://github.com/distribution/distribution/workflows/build/badge.svg?branch=main&event=push)](https://github.com/distribution/distribution/actions/workflows/build.yml?query=workflow%3Abuild)
[![OCI Conformance](https://github.com/distribution/distribution/workflows/conformance/badge.svg)](https://github.com/distribution/distribution/actions?query=workflow%3Aconformance)
[![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](LICENSE)
From 5f397b877dc7dab2de3854fb9ba2f746c7a9bea7 Mon Sep 17 00:00:00 2001
From: Sebastiaan van Stijn
Date: Fri, 22 Dec 2023 10:06:51 +0100
Subject: [PATCH 08/98] update to alpine 3.19
Signed-off-by: Sebastiaan van Stijn
---
Dockerfile | 2 +-
dockerfiles/docs.Dockerfile | 2 +-
dockerfiles/git.Dockerfile | 2 +-
dockerfiles/lint.Dockerfile | 2 +-
dockerfiles/vendor.Dockerfile | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index 7c88b1ce..950b00b6 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.21.5
-ARG ALPINE_VERSION=3.18
+ARG ALPINE_VERSION=3.19
ARG XX_VERSION=1.2.1
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
diff --git a/dockerfiles/docs.Dockerfile b/dockerfiles/docs.Dockerfile
index f54afdc7..1660e0cc 100644
--- a/dockerfiles/docs.Dockerfile
+++ b/dockerfiles/docs.Dockerfile
@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.21.5
-ARG ALPINE_VERSION=3.18
+ARG ALPINE_VERSION=3.19
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base
RUN apk add --no-cache git
diff --git a/dockerfiles/git.Dockerfile b/dockerfiles/git.Dockerfile
index 499c02a5..daae6cb4 100644
--- a/dockerfiles/git.Dockerfile
+++ b/dockerfiles/git.Dockerfile
@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.20.12
-ARG ALPINE_VERSION=3.18
+ARG ALPINE_VERSION=3.19
FROM alpine:${ALPINE_VERSION} AS base
RUN apk add --no-cache git gpg
diff --git a/dockerfiles/lint.Dockerfile b/dockerfiles/lint.Dockerfile
index b059e297..a94795f3 100644
--- a/dockerfiles/lint.Dockerfile
+++ b/dockerfiles/lint.Dockerfile
@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.20.12
-ARG ALPINE_VERSION=3.18
+ARG ALPINE_VERSION=3.19
ARG GOLANGCI_LINT_VERSION=v1.55.2
ARG BUILDTAGS=""
diff --git a/dockerfiles/vendor.Dockerfile b/dockerfiles/vendor.Dockerfile
index c325839a..13187f51 100644
--- a/dockerfiles/vendor.Dockerfile
+++ b/dockerfiles/vendor.Dockerfile
@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.20.12
-ARG ALPINE_VERSION=3.18
+ARG ALPINE_VERSION=3.19
ARG MODOUTDATED_VERSION=v0.8.0
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base
From e96fce1703986d12e365f823205ab2c4cd11ed7f Mon Sep 17 00:00:00 2001
From: Milos Gajdos
Date: Wed, 20 Dec 2023 14:09:19 +0000
Subject: [PATCH 09/98] feat: add PR labeler
This is an initial commit to kickstart a conversation about how we want
the new PRs to be labeled. TBC.
Signed-off-by: Milos Gajdos
---
.github/labeler.yml | 50 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
create mode 100644 .github/labeler.yml
diff --git a/.github/labeler.yml b/.github/labeler.yml
new file mode 100644
index 00000000..0b329cdd
--- /dev/null
+++ b/.github/labeler.yml
@@ -0,0 +1,50 @@
+area/ci:
+ - .github/**/*
+
+area/config:
+ - charts/registry/config/**/*
+
+area/docs:
+ - README.md
+ - docs/**/*.md
+
+area/build:
+ - Makefile
+ - Dockerfile
+ - docker-bake.hcl
+ - dockerfiles/**/*
+
+area/ci:
+ - .github/**/*
+ - tests
+ - testutil
+
+dependencies:
+ - vendor/**/*
+ - go.mod
+ - go.sum
+
+area/storage:
+ - registry/storage
+
+area/storage/s3:
+ - registry/storage/s3-aws
+
+area/storage/gcs:
+ - registry/storage/gcs
+
+area/storage/azure:
+ - registry/storage/azure
+
+area/auth:
+ - registry/auth
+
+area/proxy:
+ - registry/proxy
+
+area/config:
+ - configuration
+
+area/api:
+ - registry/api
+ - registry/handlers
From 4f9fe183c3a1ebc762c274c564eb6abed4e06997 Mon Sep 17 00:00:00 2001
From: Sebastiaan van Stijn
Date: Fri, 22 Dec 2023 10:23:09 +0100
Subject: [PATCH 10/98] vendor: github.com/gorilla/handlers v1.5.2
full diff: https://github.com/gorilla/handlers/compare/v1.5.1...v1.5.2
Signed-off-by: Sebastiaan van Stijn
---
go.mod | 2 +-
go.sum | 5 +--
.../github.com/gorilla/handlers/.editorconfig | 20 +++++++++
vendor/github.com/gorilla/handlers/.gitignore | 2 +
vendor/github.com/gorilla/handlers/LICENSE | 39 ++++++++++--------
vendor/github.com/gorilla/handlers/Makefile | 34 +++++++++++++++
vendor/github.com/gorilla/handlers/README.md | 10 ++---
.../github.com/gorilla/handlers/canonical.go | 9 ++--
.../github.com/gorilla/handlers/compress.go | 8 ++--
vendor/github.com/gorilla/handlers/cors.go | 41 +++++++++----------
.../github.com/gorilla/handlers/handlers.go | 15 ++++---
vendor/github.com/gorilla/handlers/logging.go | 32 ++++++++-------
.../gorilla/handlers/proxy_headers.go | 16 ++++----
.../github.com/gorilla/handlers/recovery.go | 22 +++++-----
vendor/modules.txt | 4 +-
15 files changed, 161 insertions(+), 98 deletions(-)
create mode 100644 vendor/github.com/gorilla/handlers/.editorconfig
create mode 100644 vendor/github.com/gorilla/handlers/.gitignore
create mode 100644 vendor/github.com/gorilla/handlers/Makefile
diff --git a/go.mod b/go.mod
index 86c8141a..3498ef9c 100644
--- a/go.mod
+++ b/go.mod
@@ -16,7 +16,7 @@ require (
github.com/docker/go-metrics v0.0.1
github.com/go-jose/go-jose/v3 v3.0.1
github.com/google/uuid v1.3.1
- github.com/gorilla/handlers v1.5.1
+ github.com/gorilla/handlers v1.5.2
github.com/gorilla/mux v1.8.1
github.com/hashicorp/golang-lru/arc/v2 v2.0.5
github.com/klauspost/compress v1.17.4
diff --git a/go.sum b/go.sum
index 02d7ce70..7f27d0d9 100644
--- a/go.sum
+++ b/go.sum
@@ -76,7 +76,6 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@@ -136,8 +135,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4=
github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI=
-github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
-github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
+github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
+github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
diff --git a/vendor/github.com/gorilla/handlers/.editorconfig b/vendor/github.com/gorilla/handlers/.editorconfig
new file mode 100644
index 00000000..c6b74c3e
--- /dev/null
+++ b/vendor/github.com/gorilla/handlers/.editorconfig
@@ -0,0 +1,20 @@
+; https://editorconfig.org/
+
+root = true
+
+[*]
+insert_final_newline = true
+charset = utf-8
+trim_trailing_whitespace = true
+indent_style = space
+indent_size = 2
+
+[{Makefile,go.mod,go.sum,*.go,.gitmodules}]
+indent_style = tab
+indent_size = 4
+
+[*.md]
+indent_size = 4
+trim_trailing_whitespace = false
+
+eclint_indent_style = unset
\ No newline at end of file
diff --git a/vendor/github.com/gorilla/handlers/.gitignore b/vendor/github.com/gorilla/handlers/.gitignore
new file mode 100644
index 00000000..577a89e8
--- /dev/null
+++ b/vendor/github.com/gorilla/handlers/.gitignore
@@ -0,0 +1,2 @@
+# Output of the go test coverage tool
+coverage.coverprofile
diff --git a/vendor/github.com/gorilla/handlers/LICENSE b/vendor/github.com/gorilla/handlers/LICENSE
index 66ea3c8a..bb9d80bc 100644
--- a/vendor/github.com/gorilla/handlers/LICENSE
+++ b/vendor/github.com/gorilla/handlers/LICENSE
@@ -1,22 +1,27 @@
-Copyright (c) 2013 The Gorilla Handlers Authors. All rights reserved.
+Copyright (c) 2023 The Gorilla Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
+modification, are permitted provided that the following conditions are
+met:
- Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
- Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/gorilla/handlers/Makefile b/vendor/github.com/gorilla/handlers/Makefile
new file mode 100644
index 00000000..003b784f
--- /dev/null
+++ b/vendor/github.com/gorilla/handlers/Makefile
@@ -0,0 +1,34 @@
+GO_LINT=$(shell which golangci-lint 2> /dev/null || echo '')
+GO_LINT_URI=github.com/golangci/golangci-lint/cmd/golangci-lint@latest
+
+GO_SEC=$(shell which gosec 2> /dev/null || echo '')
+GO_SEC_URI=github.com/securego/gosec/v2/cmd/gosec@latest
+
+GO_VULNCHECK=$(shell which govulncheck 2> /dev/null || echo '')
+GO_VULNCHECK_URI=golang.org/x/vuln/cmd/govulncheck@latest
+
+.PHONY: verify
+verify: sec govulncheck lint test
+
+.PHONY: lint
+lint:
+ $(if $(GO_LINT), ,go install $(GO_LINT_URI))
+ @echo "##### Running golangci-lint #####"
+ golangci-lint run -v
+
+.PHONY: sec
+sec:
+ $(if $(GO_SEC), ,go install $(GO_SEC_URI))
+ @echo "##### Running gosec #####"
+ gosec ./...
+
+.PHONY: govulncheck
+govulncheck:
+ $(if $(GO_VULNCHECK), ,go install $(GO_VULNCHECK_URI))
+ @echo "##### Running govulncheck #####"
+ govulncheck ./...
+
+.PHONY: test
+test:
+ @echo "##### Running tests #####"
+ go test -race -cover -coverprofile=coverage.coverprofile -covermode=atomic -v ./...
diff --git a/vendor/github.com/gorilla/handlers/README.md b/vendor/github.com/gorilla/handlers/README.md
index 6eba66bf..02555b26 100644
--- a/vendor/github.com/gorilla/handlers/README.md
+++ b/vendor/github.com/gorilla/handlers/README.md
@@ -1,9 +1,9 @@
-gorilla/handlers
-================
-[![GoDoc](https://godoc.org/github.com/gorilla/handlers?status.svg)](https://godoc.org/github.com/gorilla/handlers)
-[![CircleCI](https://circleci.com/gh/gorilla/handlers.svg?style=svg)](https://circleci.com/gh/gorilla/handlers)
-[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/handlers/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/handlers?badge)
+# gorilla/handlers
+![Testing](https://github.com/gorilla/handlers/actions/workflows/test.yml/badge.svg)
+[![Codecov](https://codecov.io/github/gorilla/handlers/branch/main/graph/badge.svg)](https://codecov.io/github/gorilla/handlers)
+[![GoDoc](https://godoc.org/github.com/gorilla/handlers?status.svg)](https://godoc.org/github.com/gorilla/handlers)
+[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/handlers/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/handlers?badge)
Package handlers is a collection of handlers (aka "HTTP middleware") for use
with Go's `net/http` package (or any framework supporting `http.Handler`), including:
diff --git a/vendor/github.com/gorilla/handlers/canonical.go b/vendor/github.com/gorilla/handlers/canonical.go
index 8437fefc..7121f530 100644
--- a/vendor/github.com/gorilla/handlers/canonical.go
+++ b/vendor/github.com/gorilla/handlers/canonical.go
@@ -21,12 +21,11 @@ type canonical struct {
//
// Example:
//
-// r := mux.NewRouter()
-// canonical := handlers.CanonicalHost("http://www.gorillatoolkit.org", 302)
-// r.HandleFunc("/route", YourHandler)
-//
-// log.Fatal(http.ListenAndServe(":7000", canonical(r)))
+// r := mux.NewRouter()
+// canonical := handlers.CanonicalHost("http://www.gorillatoolkit.org", 302)
+// r.HandleFunc("/route", YourHandler)
//
+// log.Fatal(http.ListenAndServe(":7000", canonical(r)))
func CanonicalHost(domain string, code int) func(h http.Handler) http.Handler {
fn := func(h http.Handler) http.Handler {
return canonical{h, domain, code}
diff --git a/vendor/github.com/gorilla/handlers/compress.go b/vendor/github.com/gorilla/handlers/compress.go
index 1e95f1cc..d6f58950 100644
--- a/vendor/github.com/gorilla/handlers/compress.go
+++ b/vendor/github.com/gorilla/handlers/compress.go
@@ -44,13 +44,13 @@ type flusher interface {
Flush() error
}
-func (w *compressResponseWriter) Flush() {
+func (cw *compressResponseWriter) Flush() {
// Flush compressed data if compressor supports it.
- if f, ok := w.compressor.(flusher); ok {
- f.Flush()
+ if f, ok := cw.compressor.(flusher); ok {
+ _ = f.Flush()
}
// Flush HTTP response.
- if f, ok := w.w.(http.Flusher); ok {
+ if f, ok := cw.w.(http.Flusher); ok {
f.Flush()
}
}
diff --git a/vendor/github.com/gorilla/handlers/cors.go b/vendor/github.com/gorilla/handlers/cors.go
index 0dcdffb3..8af9c096 100644
--- a/vendor/github.com/gorilla/handlers/cors.go
+++ b/vendor/github.com/gorilla/handlers/cors.go
@@ -26,14 +26,14 @@ type cors struct {
type OriginValidator func(string) bool
var (
- defaultCorsOptionStatusCode = 200
- defaultCorsMethods = []string{"GET", "HEAD", "POST"}
+ defaultCorsOptionStatusCode = http.StatusOK
+ defaultCorsMethods = []string{http.MethodGet, http.MethodHead, http.MethodPost}
defaultCorsHeaders = []string{"Accept", "Accept-Language", "Content-Language", "Origin"}
- // (WebKit/Safari v9 sends the Origin header by default in AJAX requests)
+ // (WebKit/Safari v9 sends the Origin header by default in AJAX requests).
)
const (
- corsOptionMethod string = "OPTIONS"
+ corsOptionMethod string = http.MethodOptions
corsAllowOriginHeader string = "Access-Control-Allow-Origin"
corsExposeHeadersHeader string = "Access-Control-Expose-Headers"
corsMaxAgeHeader string = "Access-Control-Max-Age"
@@ -101,10 +101,8 @@ func (ch *cors) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !ch.isMatch(method, defaultCorsMethods) {
w.Header().Set(corsAllowMethodsHeader, method)
}
- } else {
- if len(ch.exposedHeaders) > 0 {
- w.Header().Set(corsExposeHeadersHeader, strings.Join(ch.exposedHeaders, ","))
- }
+ } else if len(ch.exposedHeaders) > 0 {
+ w.Header().Set(corsExposeHeadersHeader, strings.Join(ch.exposedHeaders, ","))
}
if ch.allowCredentials {
@@ -141,22 +139,21 @@ func (ch *cors) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// CORS provides Cross-Origin Resource Sharing middleware.
// Example:
//
-// import (
-// "net/http"
+// import (
+// "net/http"
//
-// "github.com/gorilla/handlers"
-// "github.com/gorilla/mux"
-// )
+// "github.com/gorilla/handlers"
+// "github.com/gorilla/mux"
+// )
//
-// func main() {
-// r := mux.NewRouter()
-// r.HandleFunc("/users", UserEndpoint)
-// r.HandleFunc("/projects", ProjectEndpoint)
-//
-// // Apply the CORS middleware to our top-level router, with the defaults.
-// http.ListenAndServe(":8000", handlers.CORS()(r))
-// }
+// func main() {
+// r := mux.NewRouter()
+// r.HandleFunc("/users", UserEndpoint)
+// r.HandleFunc("/projects", ProjectEndpoint)
//
+// // Apply the CORS middleware to our top-level router, with the defaults.
+// http.ListenAndServe(":8000", handlers.CORS()(r))
+// }
func CORS(opts ...CORSOption) func(http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
ch := parseCORSOptions(opts...)
@@ -174,7 +171,7 @@ func parseCORSOptions(opts ...CORSOption) *cors {
}
for _, option := range opts {
- option(ch)
+ _ = option(ch) //TODO: @bharat-rajani, return error to caller if not nil?
}
return ch
diff --git a/vendor/github.com/gorilla/handlers/handlers.go b/vendor/github.com/gorilla/handlers/handlers.go
index 0509482a..9b92fce3 100644
--- a/vendor/github.com/gorilla/handlers/handlers.go
+++ b/vendor/github.com/gorilla/handlers/handlers.go
@@ -35,7 +35,7 @@ func (h MethodHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
sort.Strings(allow)
w.Header().Set("Allow", strings.Join(allow, ", "))
- if req.Method == "OPTIONS" {
+ if req.Method == http.MethodOptions {
w.WriteHeader(http.StatusOK)
} else {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
@@ -44,7 +44,7 @@ func (h MethodHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
// responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP
-// status code and body size
+// status code and body size.
type responseLogger struct {
w http.ResponseWriter
status int
@@ -97,7 +97,7 @@ func isContentType(h http.Header, contentType string) bool {
// Only PUT, POST, and PATCH requests are considered.
func ContentTypeHandler(h http.Handler, contentTypes ...string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if !(r.Method == "PUT" || r.Method == "POST" || r.Method == "PATCH") {
+ if !(r.Method == http.MethodPut || r.Method == http.MethodPost || r.Method == http.MethodPatch) {
h.ServeHTTP(w, r)
return
}
@@ -108,7 +108,10 @@ func ContentTypeHandler(h http.Handler, contentTypes ...string) http.Handler {
return
}
}
- http.Error(w, fmt.Sprintf("Unsupported content type %q; expected one of %q", r.Header.Get("Content-Type"), contentTypes), http.StatusUnsupportedMediaType)
+ http.Error(w, fmt.Sprintf("Unsupported content type %q; expected one of %q",
+ r.Header.Get("Content-Type"),
+ contentTypes),
+ http.StatusUnsupportedMediaType)
})
}
@@ -133,12 +136,12 @@ const (
// Form method takes precedence over header method.
func HTTPMethodOverrideHandler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.Method == "POST" {
+ if r.Method == http.MethodPost {
om := r.FormValue(HTTPMethodOverrideFormKey)
if om == "" {
om = r.Header.Get(HTTPMethodOverrideHeader)
}
- if om == "PUT" || om == "PATCH" || om == "DELETE" {
+ if om == http.MethodPut || om == http.MethodPatch || om == http.MethodDelete {
r.Method = om
}
}
diff --git a/vendor/github.com/gorilla/handlers/logging.go b/vendor/github.com/gorilla/handlers/logging.go
index 228465eb..2badb6fb 100644
--- a/vendor/github.com/gorilla/handlers/logging.go
+++ b/vendor/github.com/gorilla/handlers/logging.go
@@ -18,7 +18,7 @@ import (
// Logging
-// LogFormatterParams is the structure any formatter will be handed when time to log comes
+// LogFormatterParams is the structure any formatter will be handed when time to log comes.
type LogFormatterParams struct {
Request *http.Request
URL url.URL
@@ -27,7 +27,7 @@ type LogFormatterParams struct {
Size int
}
-// LogFormatter gives the signature of the formatter function passed to CustomLoggingHandler
+// LogFormatter gives the signature of the formatter function passed to CustomLoggingHandler.
type LogFormatter func(writer io.Writer, params LogFormatterParams)
// loggingHandler is the http.Handler implementation for LoggingHandlerTo and its
@@ -46,7 +46,10 @@ func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
h.handler.ServeHTTP(w, req)
if req.MultipartForm != nil {
- req.MultipartForm.RemoveAll()
+ err := req.MultipartForm.RemoveAll()
+ if err != nil {
+ return
+ }
}
params := LogFormatterParams{
@@ -76,7 +79,7 @@ const lowerhex = "0123456789abcdef"
func appendQuoted(buf []byte, s string) []byte {
var runeTmp [utf8.UTFMax]byte
- for width := 0; len(s) > 0; s = s[width:] {
+ for width := 0; len(s) > 0; s = s[width:] { //nolint: wastedassign //TODO: why width starts from 0and reassigned as 1
r := rune(s[0])
width = 1
if r >= utf8.RuneSelf {
@@ -191,7 +194,7 @@ func buildCommonLogLine(req *http.Request, url url.URL, ts time.Time, status int
func writeLog(writer io.Writer, params LogFormatterParams) {
buf := buildCommonLogLine(params.Request, params.URL, params.TimeStamp, params.StatusCode, params.Size)
buf = append(buf, '\n')
- writer.Write(buf)
+ _, _ = writer.Write(buf)
}
// writeCombinedLog writes a log entry for req to w in Apache Combined Log Format.
@@ -204,7 +207,7 @@ func writeCombinedLog(writer io.Writer, params LogFormatterParams) {
buf = append(buf, `" "`...)
buf = appendQuoted(buf, params.Request.UserAgent())
buf = append(buf, '"', '\n')
- writer.Write(buf)
+ _, _ = writer.Write(buf)
}
// CombinedLoggingHandler return a http.Handler that wraps h and logs requests to out in
@@ -212,7 +215,7 @@ func writeCombinedLog(writer io.Writer, params LogFormatterParams) {
//
// See http://httpd.apache.org/docs/2.2/logs.html#combined for a description of this format.
//
-// LoggingHandler always sets the ident field of the log to -
+// LoggingHandler always sets the ident field of the log to -.
func CombinedLoggingHandler(out io.Writer, h http.Handler) http.Handler {
return loggingHandler{out, h, writeCombinedLog}
}
@@ -226,19 +229,18 @@ func CombinedLoggingHandler(out io.Writer, h http.Handler) http.Handler {
//
// Example:
//
-// r := mux.NewRouter()
-// r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-// w.Write([]byte("This is a catch-all route"))
-// })
-// loggedRouter := handlers.LoggingHandler(os.Stdout, r)
-// http.ListenAndServe(":1123", loggedRouter)
-//
+// r := mux.NewRouter()
+// r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+// w.Write([]byte("This is a catch-all route"))
+// })
+// loggedRouter := handlers.LoggingHandler(os.Stdout, r)
+// http.ListenAndServe(":1123", loggedRouter)
func LoggingHandler(out io.Writer, h http.Handler) http.Handler {
return loggingHandler{out, h, writeLog}
}
// CustomLoggingHandler provides a way to supply a custom log formatter
-// while taking advantage of the mechanisms in this package
+// while taking advantage of the mechanisms in this package.
func CustomLoggingHandler(out io.Writer, h http.Handler, f LogFormatter) http.Handler {
return loggingHandler{out, h, f}
}
diff --git a/vendor/github.com/gorilla/handlers/proxy_headers.go b/vendor/github.com/gorilla/handlers/proxy_headers.go
index ed939dce..281d753e 100644
--- a/vendor/github.com/gorilla/handlers/proxy_headers.go
+++ b/vendor/github.com/gorilla/handlers/proxy_headers.go
@@ -18,7 +18,7 @@ var (
var (
// RFC7239 defines a new "Forwarded: " header designed to replace the
// existing use of X-Forwarded-* headers.
- // e.g. Forwarded: for=192.0.2.60;proto=https;by=203.0.113.43
+ // e.g. Forwarded: for=192.0.2.60;proto=https;by=203.0.113.43.
forwarded = http.CanonicalHeaderKey("Forwarded")
// Allows for a sub-match of the first value after 'for=' to the next
// comma, semi-colon or space. The match is case-insensitive.
@@ -67,7 +67,9 @@ func ProxyHeaders(h http.Handler) http.Handler {
func getIP(r *http.Request) string {
var addr string
- if fwd := r.Header.Get(xForwardedFor); fwd != "" {
+ switch {
+ case r.Header.Get(xForwardedFor) != "":
+ fwd := r.Header.Get(xForwardedFor)
// Only grab the first (client) address. Note that '192.168.0.1,
// 10.1.1.1' is a valid key for X-Forwarded-For where addresses after
// the first may represent forwarding proxies earlier in the chain.
@@ -76,17 +78,15 @@ func getIP(r *http.Request) string {
s = len(fwd)
}
addr = fwd[:s]
- } else if fwd := r.Header.Get(xRealIP); fwd != "" {
- // X-Real-IP should only contain one IP address (the client making the
- // request).
- addr = fwd
- } else if fwd := r.Header.Get(forwarded); fwd != "" {
+ case r.Header.Get(xRealIP) != "":
+ addr = r.Header.Get(xRealIP)
+ case r.Header.Get(forwarded) != "":
// match should contain at least two elements if the protocol was
// specified in the Forwarded header. The first element will always be
// the 'for=' capture, which we ignore. In the case of multiple IP
// addresses (for=8.8.8.8, 8.8.4.4,172.16.1.20 is valid) we only
// extract the first, which should be the client IP.
- if match := forRegex.FindStringSubmatch(fwd); len(match) > 1 {
+ if match := forRegex.FindStringSubmatch(r.Header.Get(forwarded)); len(match) > 1 {
// IPv6 addresses in Forwarded headers are quoted-strings. We strip
// these quotes.
addr = strings.Trim(match[1], `"`)
diff --git a/vendor/github.com/gorilla/handlers/recovery.go b/vendor/github.com/gorilla/handlers/recovery.go
index 4c4c1d9c..0d4f955e 100644
--- a/vendor/github.com/gorilla/handlers/recovery.go
+++ b/vendor/github.com/gorilla/handlers/recovery.go
@@ -36,12 +36,12 @@ func parseRecoveryOptions(h http.Handler, opts ...RecoveryOption) http.Handler {
//
// Example:
//
-// r := mux.NewRouter()
-// r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-// panic("Unexpected error!")
-// })
+// r := mux.NewRouter()
+// r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+// panic("Unexpected error!")
+// })
//
-// http.ListenAndServe(":1123", handlers.RecoveryHandler()(r))
+// http.ListenAndServe(":1123", handlers.RecoveryHandler()(r))
func RecoveryHandler(opts ...RecoveryOption) func(h http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
r := &recoveryHandler{handler: h}
@@ -50,20 +50,22 @@ func RecoveryHandler(opts ...RecoveryOption) func(h http.Handler) http.Handler {
}
// RecoveryLogger is a functional option to override
-// the default logger
+// the default logger.
func RecoveryLogger(logger RecoveryHandlerLogger) RecoveryOption {
return func(h http.Handler) {
- r := h.(*recoveryHandler)
+ r := h.(*recoveryHandler) //nolint:errcheck //TODO:
+ // @bharat-rajani should return type-assertion error but would break the API?
r.logger = logger
}
}
// PrintRecoveryStack is a functional option to enable
// or disable printing stack traces on panic.
-func PrintRecoveryStack(print bool) RecoveryOption {
+func PrintRecoveryStack(shouldPrint bool) RecoveryOption {
return func(h http.Handler) {
- r := h.(*recoveryHandler)
- r.printStack = print
+ r := h.(*recoveryHandler) //nolint:errcheck //TODO:
+ // @bharat-rajani should return type-assertion error but would break the API?
+ r.printStack = shouldPrint
}
}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index f9e6318d..1ae601a0 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -243,8 +243,8 @@ github.com/googleapis/gax-go/v2
github.com/googleapis/gax-go/v2/apierror
github.com/googleapis/gax-go/v2/apierror/internal/proto
github.com/googleapis/gax-go/v2/internal
-# github.com/gorilla/handlers v1.5.1
-## explicit; go 1.14
+# github.com/gorilla/handlers v1.5.2
+## explicit; go 1.20
github.com/gorilla/handlers
# github.com/gorilla/mux v1.8.1
## explicit; go 1.20
From 5bd45551b40abfc5fec37c7510ced32eb19996ad Mon Sep 17 00:00:00 2001
From: Milos Gajdos
Date: Fri, 22 Dec 2023 09:36:48 +0000
Subject: [PATCH 11/98] fix: update Dockerfile version output
Signed-off-by: Milos Gajdos
---
Dockerfile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Dockerfile b/Dockerfile
index 7c88b1ce..24ed3df9 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -16,7 +16,7 @@ FROM base AS version
ARG PKG=github.com/distribution/distribution/v3
RUN --mount=target=. \
VERSION=$(git describe --match 'v[0-9]*' --dirty='.m' --always --tags) REVISION=$(git rev-parse HEAD)$(if ! git diff --no-ext-diff --quiet --exit-code; then echo .m; fi); \
- echo "-X ${PKG}/version.Version=${VERSION#v} -X ${PKG}/version.Revision=${REVISION} -X ${PKG}/version.Package=${PKG}" | tee /tmp/.ldflags; \
+ echo "-X ${PKG}/version.version=${VERSION#v} -X ${PKG}/version.revision=${REVISION} -X ${PKG}/version.mainpkg=${PKG}" | tee /tmp/.ldflags; \
echo -n "${VERSION}" | tee /tmp/.version;
FROM base AS build
From 55e91b39e407bde9b6137b8555ede1850e91dac4 Mon Sep 17 00:00:00 2001
From: CrazyMax <1951866+crazy-max@users.noreply.github.com>
Date: Sat, 23 Dec 2023 14:41:23 +0100
Subject: [PATCH 12/98] chore: use no-cache-filter for outdated stage
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
---
docker-bake.hcl | 6 +-----
dockerfiles/vendor.Dockerfile | 1 -
2 files changed, 1 insertion(+), 6 deletions(-)
diff --git a/docker-bake.hcl b/docker-bake.hcl
index 0587572a..e5710acd 100644
--- a/docker-bake.hcl
+++ b/docker-bake.hcl
@@ -39,11 +39,7 @@ target "update-vendor" {
target "mod-outdated" {
dockerfile = "./dockerfiles/vendor.Dockerfile"
target = "outdated"
- args = {
- // used to invalidate cache for outdated run stage
- // can be dropped when https://github.com/moby/buildkit/issues/1213 fixed
- _RANDOM = uuidv4()
- }
+ no-cache-filter = ["outdated"]
output = ["type=cacheonly"]
}
diff --git a/dockerfiles/vendor.Dockerfile b/dockerfiles/vendor.Dockerfile
index c325839a..a3fe9cd2 100644
--- a/dockerfiles/vendor.Dockerfile
+++ b/dockerfiles/vendor.Dockerfile
@@ -40,7 +40,6 @@ EOT
FROM psampaz/go-mod-outdated:${MODOUTDATED_VERSION} AS go-mod-outdated
FROM base AS outdated
-ARG _RANDOM
RUN --mount=target=.,ro \
--mount=target=/go/pkg/mod,type=cache \
--mount=from=go-mod-outdated,source=/home/go-mod-outdated,target=/usr/bin/go-mod-outdated \
From 7838a369a367aad88d6468f778114557b6807b4f Mon Sep 17 00:00:00 2001
From: CrazyMax <1951866+crazy-max@users.noreply.github.com>
Date: Sat, 23 Dec 2023 15:10:52 +0100
Subject: [PATCH 13/98] chore: dependabot to keep gha up to date
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
---
.github/dependabot.yml | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 .github/dependabot.yml
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..078c4204
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,8 @@
+version: 2
+updates:
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "daily"
+ labels:
+ - "dependencies"
From ea02d9c42e90c6e479aad82b745bfcd7feded963 Mon Sep 17 00:00:00 2001
From: Milos Gajdos
Date: Fri, 22 Dec 2023 09:44:00 +0000
Subject: [PATCH 14/98] fix: add labeler action
Whilst we had added labeles to GHA config, we forgot to add the actual
action doing the labeling.
Co-authored-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
Signed-off-by: Milos Gajdos
---
.github/labeler.yml | 3 +++
.github/workflows/label.yaml | 19 +++++++++++++++++++
2 files changed, 22 insertions(+)
create mode 100644 .github/workflows/label.yaml
diff --git a/.github/labeler.yml b/.github/labeler.yml
index 0b329cdd..b1f1f45e 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -36,6 +36,9 @@ area/storage/gcs:
area/storage/azure:
- registry/storage/azure
+area/cache:
+ - registry/storage/cache
+
area/auth:
- registry/auth
diff --git a/.github/workflows/label.yaml b/.github/workflows/label.yaml
new file mode 100644
index 00000000..6dda19bc
--- /dev/null
+++ b/.github/workflows/label.yaml
@@ -0,0 +1,19 @@
+name: Pull Request Labeler
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+on:
+ pull_request_target:
+
+jobs:
+ labeler:
+ permissions:
+ contents: read
+ pull-requests: write
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/labeler@v5
+ with:
+ dot: true
From b2bd724b52147b909267d08a1739263b3e0101f5 Mon Sep 17 00:00:00 2001
From: CrazyMax <1951866+crazy-max@users.noreply.github.com>
Date: Sun, 24 Dec 2023 11:44:42 +0100
Subject: [PATCH 15/98] chore: sort and fix mailmap
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
---
.mailmap | 68 ++++++++++++++++++++++++++++++--------------------------
1 file changed, 36 insertions(+), 32 deletions(-)
diff --git a/.mailmap b/.mailmap
index 0f48321d..8d4f7837 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1,32 +1,36 @@
-Stephen J Day Stephen Day
-Stephen J Day Stephen Day
-Olivier Gambier Olivier Gambier
-Brian Bland Brian Bland
-Brian Bland Brian Bland
-Josh Hawn Josh Hawn
-Richard Scothern Richard
-Richard Scothern Richard Scothern
-Andrew Meredith Andrew Meredith
-harche harche
-Jessie Frazelle
-Sharif Nassar Sharif Nassar
-Sven Dowideit Sven Dowideit
-Vincent Giersch Vincent Giersch
-davidli davidli
-Omer Cohen Omer Cohen
-Eric Yang Eric Yang
-Nikita Tarasov Nikita
-Yu Wang yuwaMSFT2
-Yu Wang Yu Wang (UC)
-Olivier Gambier dmp
-Olivier Gambier Olivier
-Olivier Gambier Olivier
-Elsan Li 李楠 elsanli(李楠)
-Rui Cao ruicao
-Gwendolynne Barr gbarr01
-Haibing Zhou 周海兵 zhouhaibing089
-Feng Honglin tifayuki
-Helen Xie Helen-xie
-Mike Brown Mike Brown
-Manish Tomar Manish Tomar
-Sakeven Jiang sakeven
+Andrew Meredith
+Andrew Meredith
+Brian Bland
+Brian Bland
+Brian Bland
+davidli
+davidli
+Eric Yang
+Eric Yang
+harche
+harche
+Jessie Frazelle
+Jessie Frazelle
+Josh Hawn
+Josh Hawn
+Manish Tomar
+Manish Tomar
+Mike Brown
+Mike Brown
+Nikita Tarasov
+Nikita Tarasov
+Olivier Gambier
+Olivier Gambier
+Omer Cohen
+Omer Cohen
+Richard Scothern
+Richard Scothern
+Sharif Nassar
+Sharif Nassar
+Stephen J Day
+Stephen J Day
+Stephen J Day
+Sven Dowideit
+Sven Dowideit
+Vincent Giersch
+Vincent Giersch
From 6908e0d5facd31ed32046df03a09040c964be0b3 Mon Sep 17 00:00:00 2001
From: Paul Meyer <49727155+katexochen@users.noreply.github.com>
Date: Tue, 26 Dec 2023 13:55:18 +0100
Subject: [PATCH 16/98] fix: add missing skip in s3 driver test
Signed-off-by: Paul Meyer <49727155+katexochen@users.noreply.github.com>
---
registry/storage/driver/s3-aws/s3_test.go | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/registry/storage/driver/s3-aws/s3_test.go b/registry/storage/driver/s3-aws/s3_test.go
index d7989801..e00b5771 100644
--- a/registry/storage/driver/s3-aws/s3_test.go
+++ b/registry/storage/driver/s3-aws/s3_test.go
@@ -203,6 +203,8 @@ func TestEmptyRootList(t *testing.T) {
}
func TestClientTransport(t *testing.T) {
+ skipCheck(t)
+
testCases := []struct {
skipverify bool
}{
@@ -243,7 +245,7 @@ func TestClientTransport(t *testing.T) {
return
}
// if tc.skipverify is false we do not override the driver
- // HTTP clien transport and leave it to the AWS SDK.
+ // HTTP client transport and leave it to the AWS SDK.
if s3drv.S3.Client.Config.HTTPClient.Transport != nil {
t.Errorf("unexpected S3 driver client transport")
}
From 5bd7f2588057e58009c13db38db9168337c9d379 Mon Sep 17 00:00:00 2001
From: Paul Meyer <49727155+katexochen@users.noreply.github.com>
Date: Tue, 26 Dec 2023 12:54:14 +0100
Subject: [PATCH 17/98] fix: load gcs credentials and client inside
DriverConstructor
Signed-off-by: Paul Meyer <49727155+katexochen@users.noreply.github.com>
---
registry/storage/driver/gcs/gcs_test.go | 66 ++++++++++++-------------
1 file changed, 33 insertions(+), 33 deletions(-)
diff --git a/registry/storage/driver/gcs/gcs_test.go b/registry/storage/driver/gcs/gcs_test.go
index e04230bd..b8e54b80 100644
--- a/registry/storage/driver/gcs/gcs_test.go
+++ b/registry/storage/driver/gcs/gcs_test.go
@@ -34,40 +34,40 @@ func init() {
}
}
- jsonKey, err := os.ReadFile(credentials)
- if err != nil {
- panic(fmt.Sprintf("Error reading JSON key : %v", err))
- }
-
- var ts oauth2.TokenSource
- var email string
- var privateKey []byte
-
- ts, err = google.DefaultTokenSource(dcontext.Background(), storage.ScopeFullControl)
- if err != nil {
- // Assume that the file contents are within the environment variable since it exists
- // but does not contain a valid file path
- jwtConfig, err := google.JWTConfigFromJSON(jsonKey, storage.ScopeFullControl)
- if err != nil {
- panic(fmt.Sprintf("Error reading JWT config : %s", err))
- }
- email = jwtConfig.Email
- privateKey = jwtConfig.PrivateKey
- if len(privateKey) == 0 {
- panic("Error reading JWT config : missing private_key property")
- }
- if email == "" {
- panic("Error reading JWT config : missing client_email property")
- }
- ts = jwtConfig.TokenSource(dcontext.Background())
- }
-
- gcs, err := storage.NewClient(dcontext.Background(), option.WithCredentialsJSON(jsonKey))
- if err != nil {
- panic(fmt.Sprintf("Error initializing gcs client : %v", err))
- }
-
gcsDriverConstructor = func(rootDirectory string) (storagedriver.StorageDriver, error) {
+ jsonKey, err := os.ReadFile(credentials)
+ if err != nil {
+ panic(fmt.Sprintf("Error reading JSON key : %v", err))
+ }
+
+ var ts oauth2.TokenSource
+ var email string
+ var privateKey []byte
+
+ ts, err = google.DefaultTokenSource(dcontext.Background(), storage.ScopeFullControl)
+ if err != nil {
+ // Assume that the file contents are within the environment variable since it exists
+ // but does not contain a valid file path
+ jwtConfig, err := google.JWTConfigFromJSON(jsonKey, storage.ScopeFullControl)
+ if err != nil {
+ panic(fmt.Sprintf("Error reading JWT config : %s", err))
+ }
+ email = jwtConfig.Email
+ privateKey = jwtConfig.PrivateKey
+ if len(privateKey) == 0 {
+ panic("Error reading JWT config : missing private_key property")
+ }
+ if email == "" {
+ panic("Error reading JWT config : missing client_email property")
+ }
+ ts = jwtConfig.TokenSource(dcontext.Background())
+ }
+
+ gcs, err := storage.NewClient(dcontext.Background(), option.WithCredentialsJSON(jsonKey))
+ if err != nil {
+ panic(fmt.Sprintf("Error initializing gcs client : %v", err))
+ }
+
parameters := driverParameters{
bucket: bucket,
rootDirectory: rootDirectory,
From bdfa8324a088bb45938f158d45aa6eafd5655275 Mon Sep 17 00:00:00 2001
From: Sebastiaan van Stijn
Date: Wed, 27 Dec 2023 12:28:10 +0100
Subject: [PATCH 18/98] vendor: github.com/mitchellh/mapstructure v1.5.0
note that this repository will be sunset, and the "endorsed" fork will be
maintened by "go-viper". Updating the dependency to the latest version in
preparation.
full diff: https://github.com/mitchellh/mapstructure/compare/v1.1.2...v1.5.0
Signed-off-by: Sebastiaan van Stijn
---
go.mod | 2 +-
go.sum | 4 +-
.../mitchellh/mapstructure/.travis.yml | 8 -
.../mitchellh/mapstructure/CHANGELOG.md | 75 +++
.../mitchellh/mapstructure/decode_hooks.go | 94 ++-
.../mitchellh/mapstructure/mapstructure.go | 563 +++++++++++++++---
vendor/modules.txt | 4 +-
7 files changed, 635 insertions(+), 115 deletions(-)
delete mode 100644 vendor/github.com/mitchellh/mapstructure/.travis.yml
diff --git a/go.mod b/go.mod
index 86c8141a..92042102 100644
--- a/go.mod
+++ b/go.mod
@@ -20,7 +20,7 @@ require (
github.com/gorilla/mux v1.8.1
github.com/hashicorp/golang-lru/arc/v2 v2.0.5
github.com/klauspost/compress v1.17.4
- github.com/mitchellh/mapstructure v1.1.2
+ github.com/mitchellh/mapstructure v1.5.0
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.2
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5
diff --git a/go.sum b/go.sum
index 02d7ce70..25aa3ae7 100644
--- a/go.sum
+++ b/go.sum
@@ -171,8 +171,8 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
-github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
-github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
diff --git a/vendor/github.com/mitchellh/mapstructure/.travis.yml b/vendor/github.com/mitchellh/mapstructure/.travis.yml
deleted file mode 100644
index 1689c7d7..00000000
--- a/vendor/github.com/mitchellh/mapstructure/.travis.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-language: go
-
-go:
- - "1.11.x"
- - tip
-
-script:
- - go test
diff --git a/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md b/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md
index 3b3cb723..c7582349 100644
--- a/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md
+++ b/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md
@@ -1,3 +1,78 @@
+## 1.5.0
+
+* New option `IgnoreUntaggedFields` to ignore decoding to any fields
+ without `mapstructure` (or the configured tag name) set [GH-277]
+* New option `ErrorUnset` which makes it an error if any fields
+ in a target struct are not set by the decoding process. [GH-225]
+* New function `OrComposeDecodeHookFunc` to help compose decode hooks. [GH-240]
+* Decoding to slice from array no longer crashes [GH-265]
+* Decode nested struct pointers to map [GH-271]
+* Fix issue where `,squash` was ignored if `Squash` option was set. [GH-280]
+* Fix issue where fields with `,omitempty` would sometimes decode
+ into a map with an empty string key [GH-281]
+
+## 1.4.3
+
+* Fix cases where `json.Number` didn't decode properly [GH-261]
+
+## 1.4.2
+
+* Custom name matchers to support any sort of casing, formatting, etc. for
+ field names. [GH-250]
+* Fix possible panic in ComposeDecodeHookFunc [GH-251]
+
+## 1.4.1
+
+* Fix regression where `*time.Time` value would be set to empty and not be sent
+ to decode hooks properly [GH-232]
+
+## 1.4.0
+
+* A new decode hook type `DecodeHookFuncValue` has been added that has
+ access to the full values. [GH-183]
+* Squash is now supported with embedded fields that are struct pointers [GH-205]
+* Empty strings will convert to 0 for all numeric types when weakly decoding [GH-206]
+
+## 1.3.3
+
+* Decoding maps from maps creates a settable value for decode hooks [GH-203]
+
+## 1.3.2
+
+* Decode into interface type with a struct value is supported [GH-187]
+
+## 1.3.1
+
+* Squash should only squash embedded structs. [GH-194]
+
+## 1.3.0
+
+* Added `",omitempty"` support. This will ignore zero values in the source
+ structure when encoding. [GH-145]
+
+## 1.2.3
+
+* Fix duplicate entries in Keys list with pointer values. [GH-185]
+
+## 1.2.2
+
+* Do not add unsettable (unexported) values to the unused metadata key
+ or "remain" value. [GH-150]
+
+## 1.2.1
+
+* Go modules checksum mismatch fix
+
+## 1.2.0
+
+* Added support to capture unused values in a field using the `",remain"` value
+ in the mapstructure tag. There is an example to showcase usage.
+* Added `DecoderConfig` option to always squash embedded structs
+* `json.Number` can decode into `uint` types
+* Empty slices are preserved and not replaced with nil slices
+* Fix panic that can occur in when decoding a map into a nil slice of structs
+* Improved package documentation for godoc
+
## 1.1.2
* Fix error when decode hook decodes interface implementation into interface
diff --git a/vendor/github.com/mitchellh/mapstructure/decode_hooks.go b/vendor/github.com/mitchellh/mapstructure/decode_hooks.go
index 1f0abc65..3a754ca7 100644
--- a/vendor/github.com/mitchellh/mapstructure/decode_hooks.go
+++ b/vendor/github.com/mitchellh/mapstructure/decode_hooks.go
@@ -1,6 +1,7 @@
package mapstructure
import (
+ "encoding"
"errors"
"fmt"
"net"
@@ -16,10 +17,11 @@ func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
// Create variables here so we can reference them with the reflect pkg
var f1 DecodeHookFuncType
var f2 DecodeHookFuncKind
+ var f3 DecodeHookFuncValue
// Fill in the variables into this interface and the rest is done
// automatically using the reflect package.
- potential := []interface{}{f1, f2}
+ potential := []interface{}{f1, f2, f3}
v := reflect.ValueOf(h)
vt := v.Type()
@@ -38,13 +40,15 @@ func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
// that took reflect.Kind instead of reflect.Type.
func DecodeHookExec(
raw DecodeHookFunc,
- from reflect.Type, to reflect.Type,
- data interface{}) (interface{}, error) {
+ from reflect.Value, to reflect.Value) (interface{}, error) {
+
switch f := typedDecodeHook(raw).(type) {
case DecodeHookFuncType:
- return f(from, to, data)
+ return f(from.Type(), to.Type(), from.Interface())
case DecodeHookFuncKind:
- return f(from.Kind(), to.Kind(), data)
+ return f(from.Kind(), to.Kind(), from.Interface())
+ case DecodeHookFuncValue:
+ return f(from, to)
default:
return nil, errors.New("invalid decode hook signature")
}
@@ -56,28 +60,45 @@ func DecodeHookExec(
// The composed funcs are called in order, with the result of the
// previous transformation.
func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
- return func(
- f reflect.Type,
- t reflect.Type,
- data interface{}) (interface{}, error) {
+ return func(f reflect.Value, t reflect.Value) (interface{}, error) {
var err error
+ data := f.Interface()
+
+ newFrom := f
for _, f1 := range fs {
- data, err = DecodeHookExec(f1, f, t, data)
+ data, err = DecodeHookExec(f1, newFrom, t)
if err != nil {
return nil, err
}
-
- // Modify the from kind to be correct with the new data
- f = nil
- if val := reflect.ValueOf(data); val.IsValid() {
- f = val.Type()
- }
+ newFrom = reflect.ValueOf(data)
}
return data, nil
}
}
+// OrComposeDecodeHookFunc executes all input hook functions until one of them returns no error. In that case its value is returned.
+// If all hooks return an error, OrComposeDecodeHookFunc returns an error concatenating all error messages.
+func OrComposeDecodeHookFunc(ff ...DecodeHookFunc) DecodeHookFunc {
+ return func(a, b reflect.Value) (interface{}, error) {
+ var allErrs string
+ var out interface{}
+ var err error
+
+ for _, f := range ff {
+ out, err = DecodeHookExec(f, a, b)
+ if err != nil {
+ allErrs += err.Error() + "\n"
+ continue
+ }
+
+ return out, nil
+ }
+
+ return nil, errors.New(allErrs)
+ }
+}
+
// StringToSliceHookFunc returns a DecodeHookFunc that converts
// string to []string by splitting on the given sep.
func StringToSliceHookFunc(sep string) DecodeHookFunc {
@@ -215,3 +236,44 @@ func WeaklyTypedHook(
return data, nil
}
+
+func RecursiveStructToMapHookFunc() DecodeHookFunc {
+ return func(f reflect.Value, t reflect.Value) (interface{}, error) {
+ if f.Kind() != reflect.Struct {
+ return f.Interface(), nil
+ }
+
+ var i interface{} = struct{}{}
+ if t.Type() != reflect.TypeOf(&i).Elem() {
+ return f.Interface(), nil
+ }
+
+ m := make(map[string]interface{})
+ t.Set(reflect.ValueOf(m))
+
+ return f.Interface(), nil
+ }
+}
+
+// TextUnmarshallerHookFunc returns a DecodeHookFunc that applies
+// strings to the UnmarshalText function, when the target type
+// implements the encoding.TextUnmarshaler interface
+func TextUnmarshallerHookFunc() DecodeHookFuncType {
+ return func(
+ f reflect.Type,
+ t reflect.Type,
+ data interface{}) (interface{}, error) {
+ if f.Kind() != reflect.String {
+ return data, nil
+ }
+ result := reflect.New(t).Interface()
+ unmarshaller, ok := result.(encoding.TextUnmarshaler)
+ if !ok {
+ return data, nil
+ }
+ if err := unmarshaller.UnmarshalText([]byte(data.(string))); err != nil {
+ return nil, err
+ }
+ return result, nil
+ }
+}
diff --git a/vendor/github.com/mitchellh/mapstructure/mapstructure.go b/vendor/github.com/mitchellh/mapstructure/mapstructure.go
index 256ee63f..1efb22ac 100644
--- a/vendor/github.com/mitchellh/mapstructure/mapstructure.go
+++ b/vendor/github.com/mitchellh/mapstructure/mapstructure.go
@@ -1,10 +1,161 @@
-// Package mapstructure exposes functionality to convert an arbitrary
-// map[string]interface{} into a native Go structure.
+// Package mapstructure exposes functionality to convert one arbitrary
+// Go type into another, typically to convert a map[string]interface{}
+// into a native Go structure.
//
// The Go structure can be arbitrarily complex, containing slices,
// other structs, etc. and the decoder will properly decode nested
// maps and so on into the proper structures in the native Go struct.
// See the examples to see what the decoder is capable of.
+//
+// The simplest function to start with is Decode.
+//
+// Field Tags
+//
+// When decoding to a struct, mapstructure will use the field name by
+// default to perform the mapping. For example, if a struct has a field
+// "Username" then mapstructure will look for a key in the source value
+// of "username" (case insensitive).
+//
+// type User struct {
+// Username string
+// }
+//
+// You can change the behavior of mapstructure by using struct tags.
+// The default struct tag that mapstructure looks for is "mapstructure"
+// but you can customize it using DecoderConfig.
+//
+// Renaming Fields
+//
+// To rename the key that mapstructure looks for, use the "mapstructure"
+// tag and set a value directly. For example, to change the "username" example
+// above to "user":
+//
+// type User struct {
+// Username string `mapstructure:"user"`
+// }
+//
+// Embedded Structs and Squashing
+//
+// Embedded structs are treated as if they're another field with that name.
+// By default, the two structs below are equivalent when decoding with
+// mapstructure:
+//
+// type Person struct {
+// Name string
+// }
+//
+// type Friend struct {
+// Person
+// }
+//
+// type Friend struct {
+// Person Person
+// }
+//
+// This would require an input that looks like below:
+//
+// map[string]interface{}{
+// "person": map[string]interface{}{"name": "alice"},
+// }
+//
+// If your "person" value is NOT nested, then you can append ",squash" to
+// your tag value and mapstructure will treat it as if the embedded struct
+// were part of the struct directly. Example:
+//
+// type Friend struct {
+// Person `mapstructure:",squash"`
+// }
+//
+// Now the following input would be accepted:
+//
+// map[string]interface{}{
+// "name": "alice",
+// }
+//
+// When decoding from a struct to a map, the squash tag squashes the struct
+// fields into a single map. Using the example structs from above:
+//
+// Friend{Person: Person{Name: "alice"}}
+//
+// Will be decoded into a map:
+//
+// map[string]interface{}{
+// "name": "alice",
+// }
+//
+// DecoderConfig has a field that changes the behavior of mapstructure
+// to always squash embedded structs.
+//
+// Remainder Values
+//
+// If there are any unmapped keys in the source value, mapstructure by
+// default will silently ignore them. You can error by setting ErrorUnused
+// in DecoderConfig. If you're using Metadata you can also maintain a slice
+// of the unused keys.
+//
+// You can also use the ",remain" suffix on your tag to collect all unused
+// values in a map. The field with this tag MUST be a map type and should
+// probably be a "map[string]interface{}" or "map[interface{}]interface{}".
+// See example below:
+//
+// type Friend struct {
+// Name string
+// Other map[string]interface{} `mapstructure:",remain"`
+// }
+//
+// Given the input below, Other would be populated with the other
+// values that weren't used (everything but "name"):
+//
+// map[string]interface{}{
+// "name": "bob",
+// "address": "123 Maple St.",
+// }
+//
+// Omit Empty Values
+//
+// When decoding from a struct to any other value, you may use the
+// ",omitempty" suffix on your tag to omit that value if it equates to
+// the zero value. The zero value of all types is specified in the Go
+// specification.
+//
+// For example, the zero type of a numeric type is zero ("0"). If the struct
+// field value is zero and a numeric type, the field is empty, and it won't
+// be encoded into the destination type.
+//
+// type Source struct {
+// Age int `mapstructure:",omitempty"`
+// }
+//
+// Unexported fields
+//
+// Since unexported (private) struct fields cannot be set outside the package
+// where they are defined, the decoder will simply skip them.
+//
+// For this output type definition:
+//
+// type Exported struct {
+// private string // this unexported field will be skipped
+// Public string
+// }
+//
+// Using this map as input:
+//
+// map[string]interface{}{
+// "private": "I will be ignored",
+// "Public": "I made it through!",
+// }
+//
+// The following struct will be decoded:
+//
+// type Exported struct {
+// private: "" // field is left with an empty string (zero value)
+// Public: "I made it through!"
+// }
+//
+// Other Configuration
+//
+// mapstructure is highly configurable. See the DecoderConfig struct
+// for other features and options that are supported.
package mapstructure
import (
@@ -21,10 +172,11 @@ import (
// data transformations. See "DecodeHook" in the DecoderConfig
// struct.
//
-// The type should be DecodeHookFuncType or DecodeHookFuncKind.
-// Either is accepted. Types are a superset of Kinds (Types can return
-// Kinds) and are generally a richer thing to use, but Kinds are simpler
-// if you only need those.
+// The type must be one of DecodeHookFuncType, DecodeHookFuncKind, or
+// DecodeHookFuncValue.
+// Values are a superset of Types (Values can return types), and Types are a
+// superset of Kinds (Types can return Kinds) and are generally a richer thing
+// to use, but Kinds are simpler if you only need those.
//
// The reason DecodeHookFunc is multi-typed is for backwards compatibility:
// we started with Kinds and then realized Types were the better solution,
@@ -40,15 +192,22 @@ type DecodeHookFuncType func(reflect.Type, reflect.Type, interface{}) (interface
// source and target types.
type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error)
+// DecodeHookFuncValue is a DecodeHookFunc which has complete access to both the source and target
+// values.
+type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (interface{}, error)
+
// DecoderConfig is the configuration that is used to create a new decoder
// and allows customization of various aspects of decoding.
type DecoderConfig struct {
// DecodeHook, if set, will be called before any decoding and any
// type conversion (if WeaklyTypedInput is on). This lets you modify
- // the values before they're set down onto the resulting struct.
+ // the values before they're set down onto the resulting struct. The
+ // DecodeHook is called for every map and value in the input. This means
+ // that if a struct has embedded fields with squash tags the decode hook
+ // is called only once with all of the input data, not once for each
+ // embedded struct.
//
- // If an error is returned, the entire decode will fail with that
- // error.
+ // If an error is returned, the entire decode will fail with that error.
DecodeHook DecodeHookFunc
// If ErrorUnused is true, then it is an error for there to exist
@@ -56,6 +215,12 @@ type DecoderConfig struct {
// (extra keys).
ErrorUnused bool
+ // If ErrorUnset is true, then it is an error for there to exist
+ // fields in the result that were not set in the decoding process
+ // (extra fields). This only applies to decoding to a struct. This
+ // will affect all nested structs as well.
+ ErrorUnset bool
+
// ZeroFields, if set to true, will zero fields before writing them.
// For example, a map will be emptied before decoded values are put in
// it. If this is false, a map will be merged.
@@ -80,6 +245,14 @@ type DecoderConfig struct {
//
WeaklyTypedInput bool
+ // Squash will squash embedded structs. A squash tag may also be
+ // added to an individual struct field using a tag. For example:
+ //
+ // type Parent struct {
+ // Child `mapstructure:",squash"`
+ // }
+ Squash bool
+
// Metadata is the struct that will contain extra metadata about
// the decoding. If this is nil, then no metadata will be tracked.
Metadata *Metadata
@@ -91,6 +264,15 @@ type DecoderConfig struct {
// The tag name that mapstructure reads for field names. This
// defaults to "mapstructure"
TagName string
+
+ // IgnoreUntaggedFields ignores all struct fields without explicit
+ // TagName, comparable to `mapstructure:"-"` as default behaviour.
+ IgnoreUntaggedFields bool
+
+ // MatchName is the function used to match the map key to the struct
+ // field name or tag. Defaults to `strings.EqualFold`. This can be used
+ // to implement case-sensitive tag values, support snake casing, etc.
+ MatchName func(mapKey, fieldName string) bool
}
// A Decoder takes a raw interface value and turns it into structured
@@ -112,6 +294,11 @@ type Metadata struct {
// Unused is a slice of keys that were found in the raw value but
// weren't decoded since there was no matching field in the result interface
Unused []string
+
+ // Unset is a slice of field names that were found in the result interface
+ // but weren't set in the decoding process since there was no matching value
+ // in the input
+ Unset []string
}
// Decode takes an input structure and uses reflection to translate it to
@@ -203,12 +390,20 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) {
if config.Metadata.Unused == nil {
config.Metadata.Unused = make([]string, 0)
}
+
+ if config.Metadata.Unset == nil {
+ config.Metadata.Unset = make([]string, 0)
+ }
}
if config.TagName == "" {
config.TagName = "mapstructure"
}
+ if config.MatchName == nil {
+ config.MatchName = strings.EqualFold
+ }
+
result := &Decoder{
config: config,
}
@@ -261,9 +456,7 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
if d.config.DecodeHook != nil {
// We have a DecodeHook, so let's pre-process the input.
var err error
- input, err = DecodeHookExec(
- d.config.DecodeHook,
- inputVal.Type(), outVal.Type(), input)
+ input, err = DecodeHookExec(d.config.DecodeHook, inputVal, outVal)
if err != nil {
return fmt.Errorf("error decoding '%s': %s", name, err)
}
@@ -271,6 +464,7 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
var err error
outputKind := getKind(outVal)
+ addMetaKey := true
switch outputKind {
case reflect.Bool:
err = d.decodeBool(name, input, outVal)
@@ -289,7 +483,7 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
case reflect.Map:
err = d.decodeMap(name, input, outVal)
case reflect.Ptr:
- err = d.decodePtr(name, input, outVal)
+ addMetaKey, err = d.decodePtr(name, input, outVal)
case reflect.Slice:
err = d.decodeSlice(name, input, outVal)
case reflect.Array:
@@ -303,7 +497,7 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
// If we reached here, then we successfully decoded SOMETHING, so
// mark the key as used if we're tracking metainput.
- if d.config.Metadata != nil && name != "" {
+ if addMetaKey && d.config.Metadata != nil && name != "" {
d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)
}
@@ -314,7 +508,34 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
// value to "data" of that type.
func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) error {
if val.IsValid() && val.Elem().IsValid() {
- return d.decode(name, data, val.Elem())
+ elem := val.Elem()
+
+ // If we can't address this element, then its not writable. Instead,
+ // we make a copy of the value (which is a pointer and therefore
+ // writable), decode into that, and replace the whole value.
+ copied := false
+ if !elem.CanAddr() {
+ copied = true
+
+ // Make *T
+ copy := reflect.New(elem.Type())
+
+ // *T = elem
+ copy.Elem().Set(elem)
+
+ // Set elem so we decode into it
+ elem = copy
+ }
+
+ // Decode. If we have an error then return. We also return right
+ // away if we're not a copy because that means we decoded directly.
+ if err := d.decode(name, data, elem); err != nil || !copied {
+ return err
+ }
+
+ // If we're a copy, we need to set te final result
+ val.Set(elem.Elem())
+ return nil
}
dataVal := reflect.ValueOf(data)
@@ -386,8 +607,8 @@ func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value)
if !converted {
return fmt.Errorf(
- "'%s' expected type '%s', got unconvertible type '%s'",
- name, val.Type(), dataVal.Type())
+ "'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
+ name, val.Type(), dataVal.Type(), data)
}
return nil
@@ -412,7 +633,12 @@ func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) er
val.SetInt(0)
}
case dataKind == reflect.String && d.config.WeaklyTypedInput:
- i, err := strconv.ParseInt(dataVal.String(), 0, val.Type().Bits())
+ str := dataVal.String()
+ if str == "" {
+ str = "0"
+ }
+
+ i, err := strconv.ParseInt(str, 0, val.Type().Bits())
if err == nil {
val.SetInt(i)
} else {
@@ -428,8 +654,8 @@ func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) er
val.SetInt(i)
default:
return fmt.Errorf(
- "'%s' expected type '%s', got unconvertible type '%s'",
- name, val.Type(), dataVal.Type())
+ "'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
+ name, val.Type(), dataVal.Type(), data)
}
return nil
@@ -438,6 +664,7 @@ func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) er
func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) error {
dataVal := reflect.Indirect(reflect.ValueOf(data))
dataKind := getKind(dataVal)
+ dataType := dataVal.Type()
switch {
case dataKind == reflect.Int:
@@ -463,16 +690,29 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e
val.SetUint(0)
}
case dataKind == reflect.String && d.config.WeaklyTypedInput:
- i, err := strconv.ParseUint(dataVal.String(), 0, val.Type().Bits())
+ str := dataVal.String()
+ if str == "" {
+ str = "0"
+ }
+
+ i, err := strconv.ParseUint(str, 0, val.Type().Bits())
if err == nil {
val.SetUint(i)
} else {
return fmt.Errorf("cannot parse '%s' as uint: %s", name, err)
}
+ case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":
+ jn := data.(json.Number)
+ i, err := strconv.ParseUint(string(jn), 0, 64)
+ if err != nil {
+ return fmt.Errorf(
+ "error decoding json.Number into %s: %s", name, err)
+ }
+ val.SetUint(i)
default:
return fmt.Errorf(
- "'%s' expected type '%s', got unconvertible type '%s'",
- name, val.Type(), dataVal.Type())
+ "'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
+ name, val.Type(), dataVal.Type(), data)
}
return nil
@@ -502,8 +742,8 @@ func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) e
}
default:
return fmt.Errorf(
- "'%s' expected type '%s', got unconvertible type '%s'",
- name, val.Type(), dataVal.Type())
+ "'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
+ name, val.Type(), dataVal.Type(), data)
}
return nil
@@ -528,7 +768,12 @@ func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value)
val.SetFloat(0)
}
case dataKind == reflect.String && d.config.WeaklyTypedInput:
- f, err := strconv.ParseFloat(dataVal.String(), val.Type().Bits())
+ str := dataVal.String()
+ if str == "" {
+ str = "0"
+ }
+
+ f, err := strconv.ParseFloat(str, val.Type().Bits())
if err == nil {
val.SetFloat(f)
} else {
@@ -544,8 +789,8 @@ func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value)
val.SetFloat(i)
default:
return fmt.Errorf(
- "'%s' expected type '%s', got unconvertible type '%s'",
- name, val.Type(), dataVal.Type())
+ "'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
+ name, val.Type(), dataVal.Type(), data)
}
return nil
@@ -596,7 +841,7 @@ func (d *Decoder) decodeMapFromSlice(name string, dataVal reflect.Value, val ref
for i := 0; i < dataVal.Len(); i++ {
err := d.decode(
- fmt.Sprintf("%s[%d]", name, i),
+ name+"["+strconv.Itoa(i)+"]",
dataVal.Index(i).Interface(), val)
if err != nil {
return err
@@ -629,7 +874,7 @@ func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val refle
}
for _, k := range dataVal.MapKeys() {
- fieldName := fmt.Sprintf("%s[%s]", name, k)
+ fieldName := name + "[" + k.String() + "]"
// First decode the key into the proper type
currentKey := reflect.Indirect(reflect.New(valKeyType))
@@ -678,27 +923,48 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
}
tagValue := f.Tag.Get(d.config.TagName)
- tagParts := strings.Split(tagValue, ",")
+ keyName := f.Name
+
+ if tagValue == "" && d.config.IgnoreUntaggedFields {
+ continue
+ }
+
+ // If Squash is set in the config, we squash the field down.
+ squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous
+
+ v = dereferencePtrToStructIfNeeded(v, d.config.TagName)
// Determine the name of the key in the map
- keyName := f.Name
- if tagParts[0] != "" {
- if tagParts[0] == "-" {
+ if index := strings.Index(tagValue, ","); index != -1 {
+ if tagValue[:index] == "-" {
continue
}
- keyName = tagParts[0]
- }
-
- // If "squash" is specified in the tag, we squash the field down.
- squash := false
- for _, tag := range tagParts[1:] {
- if tag == "squash" {
- squash = true
- break
+ // If "omitempty" is specified in the tag, it ignores empty values.
+ if strings.Index(tagValue[index+1:], "omitempty") != -1 && isEmptyValue(v) {
+ continue
}
- }
- if squash && v.Kind() != reflect.Struct {
- return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
+
+ // If "squash" is specified in the tag, we squash the field down.
+ squash = squash || strings.Index(tagValue[index+1:], "squash") != -1
+ if squash {
+ // When squashing, the embedded type can be a pointer to a struct.
+ if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct {
+ v = v.Elem()
+ }
+
+ // The final type must be a struct
+ if v.Kind() != reflect.Struct {
+ return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
+ }
+ }
+ if keyNameTagValue := tagValue[:index]; keyNameTagValue != "" {
+ keyName = keyNameTagValue
+ }
+ } else if len(tagValue) > 0 {
+ if tagValue == "-" {
+ continue
+ }
+ keyName = tagValue
}
switch v.Kind() {
@@ -713,11 +979,22 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
mType := reflect.MapOf(vKeyType, vElemType)
vMap := reflect.MakeMap(mType)
- err := d.decode(keyName, x.Interface(), vMap)
+ // Creating a pointer to a map so that other methods can completely
+ // overwrite the map if need be (looking at you decodeMapFromMap). The
+ // indirection allows the underlying map to be settable (CanSet() == true)
+ // where as reflect.MakeMap returns an unsettable map.
+ addrVal := reflect.New(vMap.Type())
+ reflect.Indirect(addrVal).Set(vMap)
+
+ err := d.decode(keyName, x.Interface(), reflect.Indirect(addrVal))
if err != nil {
return err
}
+ // the underlying map may have been completely overwritten so pull
+ // it indirectly out of the enclosing value.
+ vMap = reflect.Indirect(addrVal)
+
if squash {
for _, k := range vMap.MapKeys() {
valMap.SetMapIndex(k, vMap.MapIndex(k))
@@ -738,7 +1015,7 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
return nil
}
-func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) error {
+func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) (bool, error) {
// If the input data is nil, then we want to just set the output
// pointer to be nil as well.
isNil := data == nil
@@ -759,7 +1036,7 @@ func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) er
val.Set(nilValue)
}
- return nil
+ return true, nil
}
// Create an element of the concrete (non pointer) type and decode
@@ -773,16 +1050,16 @@ func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) er
}
if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil {
- return err
+ return false, err
}
val.Set(realVal)
} else {
if err := d.decode(name, data, reflect.Indirect(val)); err != nil {
- return err
+ return false, err
}
}
- return nil
+ return false, nil
}
func (d *Decoder) decodeFunc(name string, data interface{}, val reflect.Value) error {
@@ -791,8 +1068,8 @@ func (d *Decoder) decodeFunc(name string, data interface{}, val reflect.Value) e
dataVal := reflect.Indirect(reflect.ValueOf(data))
if val.Type() != dataVal.Type() {
return fmt.Errorf(
- "'%s' expected type '%s', got unconvertible type '%s'",
- name, val.Type(), dataVal.Type())
+ "'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
+ name, val.Type(), dataVal.Type(), data)
}
val.Set(dataVal)
return nil
@@ -805,8 +1082,8 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
valElemType := valType.Elem()
sliceType := reflect.SliceOf(valElemType)
- valSlice := val
- if valSlice.IsNil() || d.config.ZeroFields {
+ // If we have a non array/slice type then we first attempt to convert.
+ if dataValKind != reflect.Array && dataValKind != reflect.Slice {
if d.config.WeaklyTypedInput {
switch {
// Slice and array we use the normal logic
@@ -833,18 +1110,17 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
}
}
- // Check input type
- if dataValKind != reflect.Array && dataValKind != reflect.Slice {
- return fmt.Errorf(
- "'%s': source data must be an array or slice, got %s", name, dataValKind)
+ return fmt.Errorf(
+ "'%s': source data must be an array or slice, got %s", name, dataValKind)
+ }
- }
-
- // If the input value is empty, then don't allocate since non-nil != nil
- if dataVal.Len() == 0 {
- return nil
- }
+ // If the input value is nil, then don't allocate since empty != nil
+ if dataValKind != reflect.Array && dataVal.IsNil() {
+ return nil
+ }
+ valSlice := val
+ if valSlice.IsNil() || d.config.ZeroFields {
// Make a new slice to hold our result, same size as the original data.
valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len())
}
@@ -859,7 +1135,7 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
}
currentField := valSlice.Index(i)
- fieldName := fmt.Sprintf("%s[%d]", name, i)
+ fieldName := name + "[" + strconv.Itoa(i) + "]"
if err := d.decode(fieldName, currentData, currentField); err != nil {
errors = appendErrors(errors, err)
}
@@ -926,7 +1202,7 @@ func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value)
currentData := dataVal.Index(i).Interface()
currentField := valArray.Index(i)
- fieldName := fmt.Sprintf("%s[%d]", name, i)
+ fieldName := name + "[" + strconv.Itoa(i) + "]"
if err := d.decode(fieldName, currentData, currentField); err != nil {
errors = appendErrors(errors, err)
}
@@ -962,13 +1238,23 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value)
// Not the most efficient way to do this but we can optimize later if
// we want to. To convert from struct to struct we go to map first
// as an intermediary.
- m := make(map[string]interface{})
- mval := reflect.Indirect(reflect.ValueOf(&m))
- if err := d.decodeMapFromStruct(name, dataVal, mval, mval); err != nil {
+
+ // Make a new map to hold our result
+ mapType := reflect.TypeOf((map[string]interface{})(nil))
+ mval := reflect.MakeMap(mapType)
+
+ // Creating a pointer to a map so that other methods can completely
+ // overwrite the map if need be (looking at you decodeMapFromMap). The
+ // indirection allows the underlying map to be settable (CanSet() == true)
+ // where as reflect.MakeMap returns an unsettable map.
+ addrVal := reflect.New(mval.Type())
+
+ reflect.Indirect(addrVal).Set(mval)
+ if err := d.decodeMapFromStruct(name, dataVal, reflect.Indirect(addrVal), mval); err != nil {
return err
}
- result := d.decodeStructFromMap(name, mval, val)
+ result := d.decodeStructFromMap(name, reflect.Indirect(addrVal), val)
return result
default:
@@ -991,6 +1277,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
dataValKeysUnused[dataValKey.Interface()] = struct{}{}
}
+ targetValKeysUnused := make(map[interface{}]struct{})
errors := make([]string, 0)
// This slice will keep track of all the structs we'll be decoding.
@@ -1005,6 +1292,11 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
field reflect.StructField
val reflect.Value
}
+
+ // remainField is set to a valid field set with the "remain" tag if
+ // we are keeping track of remaining values.
+ var remainField *field
+
fields := []field{}
for len(structs) > 0 {
structVal := structs[0]
@@ -1014,30 +1306,47 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
for i := 0; i < structType.NumField(); i++ {
fieldType := structType.Field(i)
- fieldKind := fieldType.Type.Kind()
+ fieldVal := structVal.Field(i)
+ if fieldVal.Kind() == reflect.Ptr && fieldVal.Elem().Kind() == reflect.Struct {
+ // Handle embedded struct pointers as embedded structs.
+ fieldVal = fieldVal.Elem()
+ }
// If "squash" is specified in the tag, we squash the field down.
- squash := false
+ squash := d.config.Squash && fieldVal.Kind() == reflect.Struct && fieldType.Anonymous
+ remain := false
+
+ // We always parse the tags cause we're looking for other tags too
tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",")
for _, tag := range tagParts[1:] {
if tag == "squash" {
squash = true
break
}
+
+ if tag == "remain" {
+ remain = true
+ break
+ }
}
if squash {
- if fieldKind != reflect.Struct {
+ if fieldVal.Kind() != reflect.Struct {
errors = appendErrors(errors,
- fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldKind))
+ fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldVal.Kind()))
} else {
- structs = append(structs, structVal.FieldByName(fieldType.Name))
+ structs = append(structs, fieldVal)
}
continue
}
- // Normal struct field, store it away
- fields = append(fields, field{fieldType, structVal.Field(i)})
+ // Build our field
+ if remain {
+ remainField = &field{fieldType, fieldVal}
+ } else {
+ // Normal struct field, store it away
+ fields = append(fields, field{fieldType, fieldVal})
+ }
}
}
@@ -1064,7 +1373,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
continue
}
- if strings.EqualFold(mK, fieldName) {
+ if d.config.MatchName(mK, fieldName) {
rawMapKey = dataValKey
rawMapVal = dataVal.MapIndex(dataValKey)
break
@@ -1073,14 +1382,12 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
if !rawMapVal.IsValid() {
// There was no matching key in the map for the value in
- // the struct. Just ignore.
+ // the struct. Remember it for potential errors and metadata.
+ targetValKeysUnused[fieldName] = struct{}{}
continue
}
}
- // Delete the key we're using from the unused map so we stop tracking
- delete(dataValKeysUnused, rawMapKey.Interface())
-
if !fieldValue.IsValid() {
// This should never happen
panic("field is not valid")
@@ -1092,10 +1399,13 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
continue
}
+ // Delete the key we're using from the unused map so we stop tracking
+ delete(dataValKeysUnused, rawMapKey.Interface())
+
// If the name is empty string, then we're at the root, and we
// don't dot-join the fields.
if name != "" {
- fieldName = fmt.Sprintf("%s.%s", name, fieldName)
+ fieldName = name + "." + fieldName
}
if err := d.decode(fieldName, rawMapVal.Interface(), fieldValue); err != nil {
@@ -1103,6 +1413,25 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
}
}
+ // If we have a "remain"-tagged field and we have unused keys then
+ // we put the unused keys directly into the remain field.
+ if remainField != nil && len(dataValKeysUnused) > 0 {
+ // Build a map of only the unused values
+ remain := map[interface{}]interface{}{}
+ for key := range dataValKeysUnused {
+ remain[key] = dataVal.MapIndex(reflect.ValueOf(key)).Interface()
+ }
+
+ // Decode it as-if we were just decoding this map onto our map.
+ if err := d.decodeMap(name, remain, remainField.val); err != nil {
+ errors = appendErrors(errors, err)
+ }
+
+ // Set the map to nil so we have none so that the next check will
+ // not error (ErrorUnused)
+ dataValKeysUnused = nil
+ }
+
if d.config.ErrorUnused && len(dataValKeysUnused) > 0 {
keys := make([]string, 0, len(dataValKeysUnused))
for rawKey := range dataValKeysUnused {
@@ -1114,6 +1443,17 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
errors = appendErrors(errors, err)
}
+ if d.config.ErrorUnset && len(targetValKeysUnused) > 0 {
+ keys := make([]string, 0, len(targetValKeysUnused))
+ for rawKey := range targetValKeysUnused {
+ keys = append(keys, rawKey.(string))
+ }
+ sort.Strings(keys)
+
+ err := fmt.Errorf("'%s' has unset fields: %s", name, strings.Join(keys, ", "))
+ errors = appendErrors(errors, err)
+ }
+
if len(errors) > 0 {
return &Error{errors}
}
@@ -1123,16 +1463,42 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
for rawKey := range dataValKeysUnused {
key := rawKey.(string)
if name != "" {
- key = fmt.Sprintf("%s.%s", name, key)
+ key = name + "." + key
}
d.config.Metadata.Unused = append(d.config.Metadata.Unused, key)
}
+ for rawKey := range targetValKeysUnused {
+ key := rawKey.(string)
+ if name != "" {
+ key = name + "." + key
+ }
+
+ d.config.Metadata.Unset = append(d.config.Metadata.Unset, key)
+ }
}
return nil
}
+func isEmptyValue(v reflect.Value) bool {
+ switch getKind(v) {
+ case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
+ return v.Len() == 0
+ case reflect.Bool:
+ return !v.Bool()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return v.Int() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return v.Uint() == 0
+ case reflect.Float32, reflect.Float64:
+ return v.Float() == 0
+ case reflect.Interface, reflect.Ptr:
+ return v.IsNil()
+ }
+ return false
+}
+
func getKind(val reflect.Value) reflect.Kind {
kind := val.Kind()
@@ -1147,3 +1513,28 @@ func getKind(val reflect.Value) reflect.Kind {
return kind
}
}
+
+func isStructTypeConvertibleToMap(typ reflect.Type, checkMapstructureTags bool, tagName string) bool {
+ for i := 0; i < typ.NumField(); i++ {
+ f := typ.Field(i)
+ if f.PkgPath == "" && !checkMapstructureTags { // check for unexported fields
+ return true
+ }
+ if checkMapstructureTags && f.Tag.Get(tagName) != "" { // check for mapstructure tags inside
+ return true
+ }
+ }
+ return false
+}
+
+func dereferencePtrToStructIfNeeded(v reflect.Value, tagName string) reflect.Value {
+ if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
+ return v
+ }
+ deref := v.Elem()
+ derefT := deref.Type()
+ if isStructTypeConvertibleToMap(derefT, true, tagName) {
+ return deref
+ }
+ return v
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index f9e6318d..5b16607e 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -283,8 +283,8 @@ github.com/kylelemons/godebug/pretty
# github.com/matttproud/golang_protobuf_extensions v1.0.4
## explicit; go 1.9
github.com/matttproud/golang_protobuf_extensions/pbutil
-# github.com/mitchellh/mapstructure v1.1.2
-## explicit
+# github.com/mitchellh/mapstructure v1.5.0
+## explicit; go 1.14
github.com/mitchellh/mapstructure
# github.com/opencontainers/go-digest v1.0.0
## explicit; go 1.13
From befbaa680ca097c4b584684b686186419bf19330 Mon Sep 17 00:00:00 2001
From: CrazyMax <1951866+crazy-max@users.noreply.github.com>
Date: Sun, 24 Dec 2023 11:46:08 +0100
Subject: [PATCH 19/98] chore: update mailmap
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
---
.mailmap | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 132 insertions(+), 7 deletions(-)
diff --git a/.mailmap b/.mailmap
index 8d4f7837..1c711f9f 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1,36 +1,161 @@
+Aaron Lehmann
+Aaron Lehmann
+Akihiro Suda
+Akihiro Suda
+Akihiro Suda
+Alexander Morozov
+Alexander Morozov
Andrew Meredith
Andrew Meredith
+Andrii Soldatenko
+Andrii Soldatenko
+Antonio Murdaca
+Antonio Murdaca
+Antonio Murdaca
+Antonio Murdaca
+Antonio Murdaca
+Antonio Murdaca
+baojiangnan
+baojiangnan
Brian Bland
Brian Bland
Brian Bland
-davidli
-davidli
+CrazyMax
+CrazyMax <1951866+crazy-max@users.noreply.github.com>
+CrazyMax
+Daniel Nephin
+Daniel Nephin
+David Karlsson
+David Karlsson <35727626+dvdksn@users.noreply.github.com>
+David Wu
+David Wu
+Derek McGowan
+Derek McGowan
+Doug Davis
+Doug Davis
Eric Yang
+Eric Yang
Eric Yang
+Erica Windisch
+Erica Windisch
+Guillaume J. Charmes
+Guillaume J. Charmes
+Guillaume J. Charmes
+Guillaume J. Charmes
+Guillaume J. Charmes
harche
harche
-Jessie Frazelle
-Jessie Frazelle
+Jessica Frazelle
+Jessica Frazelle
+Jessica Frazelle
+Jessica Frazelle
+Jessica Frazelle
+Jessica Frazelle
+Jessica Frazelle
+Jessica Frazelle
+Jessica Frazelle
+Jessica Frazelle
+Joao Fernandes
+Joao Fernandes
+Johan Euphrosine
+Johan Euphrosine
+John Howard
+John Howard
Josh Hawn
Josh Hawn
+Joyce Brum
+Joyce Brum
+Justin Cormack
+Justin Cormack
+Justin Cormack