plugin/kubernetes: Enable protobuf, Update client api package (#1114)
* vendor * code
This commit is contained in:
parent
45b0252c1a
commit
4b3a430ff2
1511 changed files with 286873 additions and 253612 deletions
123
Gopkg.lock
generated
123
Gopkg.lock
generated
|
@ -1,12 +1,6 @@
|
||||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||||
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "cloud.google.com/go"
|
|
||||||
packages = ["compute/metadata"]
|
|
||||||
revision = "0f0b8420cb699ac4ce059c63bac263f4301fe95b"
|
|
||||||
version = "v0.12.0"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/PuerkitoBio/purell"
|
name = "github.com/PuerkitoBio/purell"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
@ -31,42 +25,30 @@
|
||||||
revision = "b2a4d4ae21c789b689dd162deb819665567f481c"
|
revision = "b2a4d4ae21c789b689dd162deb819665567f481c"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/asaskevich/govalidator"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "73945b6115bfbbcc57d89b7316e28109364124e1"
|
||||||
|
version = "v7"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/beorn7/perks"
|
name = "github.com/beorn7/perks"
|
||||||
packages = ["quantile"]
|
packages = ["quantile"]
|
||||||
revision = "4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9"
|
revision = "4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/blang/semver"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "2ee87856327ba09384cabd113bc6b5d174e9ec0f"
|
|
||||||
version = "v3.5.1"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/coreos/etcd"
|
name = "github.com/coreos/etcd"
|
||||||
packages = ["client","pkg/pathutil","pkg/srv","pkg/types","version"]
|
packages = ["client","pkg/pathutil","pkg/srv","pkg/types","version"]
|
||||||
revision = "9d43462d174c664f5edf313dec0de31e1ef4ed47"
|
revision = "9d43462d174c664f5edf313dec0de31e1ef4ed47"
|
||||||
version = "v3.2.6"
|
version = "v3.2.6"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/coreos/go-oidc"
|
|
||||||
packages = ["http","jose","key","oauth2","oidc"]
|
|
||||||
revision = "a4973d9a4225417aecf5d450a9522f00c1f7130f"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/coreos/go-semver"
|
name = "github.com/coreos/go-semver"
|
||||||
packages = ["semver"]
|
packages = ["semver"]
|
||||||
revision = "8ab6407b697782a06568d4b7f1db25550ec2e4c6"
|
revision = "8ab6407b697782a06568d4b7f1db25550ec2e4c6"
|
||||||
version = "v0.2.0"
|
version = "v0.2.0"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/coreos/pkg"
|
|
||||||
packages = ["health","httputil","timeutil"]
|
|
||||||
revision = "3ac0863d7acf3bc44daf49afef8919af12f704ef"
|
|
||||||
version = "v3"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/davecgh/go-spew"
|
name = "github.com/davecgh/go-spew"
|
||||||
packages = ["spew"]
|
packages = ["spew"]
|
||||||
|
@ -105,22 +87,22 @@
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/emicklei/go-restful"
|
name = "github.com/emicklei/go-restful"
|
||||||
packages = [".","log","swagger"]
|
packages = [".","log"]
|
||||||
revision = "777bb3f19bcafe2575ffb2a3e46af92509ae9594"
|
revision = "777bb3f19bcafe2575ffb2a3e46af92509ae9594"
|
||||||
version = "v1.2"
|
version = "v1.2"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/emicklei/go-restful-swagger12"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "dcef7f55730566d41eae5db10e7d6981829720f6"
|
||||||
|
version = "1.0.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/farsightsec/golang-framestream"
|
name = "github.com/farsightsec/golang-framestream"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "dec85654e8b8cf6712870afb14ee53d1c98cd5e2"
|
revision = "dec85654e8b8cf6712870afb14ee53d1c98cd5e2"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/fsnotify/fsnotify"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "629574ca2a5df945712d3079857300b5e4da0236"
|
|
||||||
version = "v1.4.2"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/ghodss/yaml"
|
name = "github.com/ghodss/yaml"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
@ -133,6 +115,18 @@
|
||||||
revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5"
|
revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5"
|
||||||
version = "v0.3.0"
|
version = "v0.3.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/go-openapi/analysis"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "8ed83f2ea9f00f945516462951a288eaa68bf0d6"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/go-openapi/errors"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "03cfca65330da08a5a440053faf994a3c682b5bf"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/go-openapi/jsonpointer"
|
name = "github.com/go-openapi/jsonpointer"
|
||||||
|
@ -145,12 +139,24 @@
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "36d33bfe519efae5632669801b180bf1a245da3b"
|
revision = "36d33bfe519efae5632669801b180bf1a245da3b"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/go-openapi/loads"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "a80dea3052f00e5f032e860dd7355cd0cc67e24d"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/go-openapi/spec"
|
name = "github.com/go-openapi/spec"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "3faa0055dbbf2110abc1f3b4e3adbb22721e96e7"
|
revision = "3faa0055dbbf2110abc1f3b4e3adbb22721e96e7"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/go-openapi/strfmt"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "610b6cacdcde6852f4de68998bd20ce1dac85b22"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/go-openapi/swag"
|
name = "github.com/go-openapi/swag"
|
||||||
|
@ -193,6 +199,12 @@
|
||||||
packages = ["go/otgrpc"]
|
packages = ["go/otgrpc"]
|
||||||
revision = "6c130eed1e297e1aa4d415a50c90d0c81c52677e"
|
revision = "6c130eed1e297e1aa4d415a50c90d0c81c52677e"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/hashicorp/golang-lru"
|
||||||
|
packages = [".","simplelru"]
|
||||||
|
revision = "0a025b7e63adc15a622f29b0b2c4c3848243bbf6"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/howeyc/gopass"
|
name = "github.com/howeyc/gopass"
|
||||||
|
@ -205,12 +217,6 @@
|
||||||
revision = "3e95a51e0639b4cf372f2ccf74c86749d747fbdc"
|
revision = "3e95a51e0639b4cf372f2ccf74c86749d747fbdc"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/jonboulle/clockwork"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "2eee05ed794112d45db504eb05aa693efd2b8b09"
|
|
||||||
version = "v0.1.0"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/juju/ratelimit"
|
name = "github.com/juju/ratelimit"
|
||||||
|
@ -235,6 +241,12 @@
|
||||||
revision = "3247c84500bff8d9fb6d579d800f20b3e091582c"
|
revision = "3247c84500bff8d9fb6d579d800f20b3e091582c"
|
||||||
version = "v1.0.0"
|
version = "v1.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/mitchellh/mapstructure"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "d0303fe809921458f417bcf828397a65db30a7e4"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/opentracing/opentracing-go"
|
name = "github.com/opentracing/opentracing-go"
|
||||||
packages = [".","ext","log"]
|
packages = [".","ext","log"]
|
||||||
|
@ -247,12 +259,6 @@
|
||||||
revision = "d88c90182f3fb514be54a1c7adc11a04d41c7da9"
|
revision = "d88c90182f3fb514be54a1c7adc11a04d41c7da9"
|
||||||
version = "v0.2.4"
|
version = "v0.2.4"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/pborman/uuid"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "e790cca94e6cc75c7064b1332e63811d4aae1a53"
|
|
||||||
version = "v1.1"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/pierrec/lz4"
|
name = "github.com/pierrec/lz4"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
@ -313,12 +319,6 @@
|
||||||
packages = ["ssh/terminal"]
|
packages = ["ssh/terminal"]
|
||||||
revision = "81e90905daefcd6fd217b62423c0908922eadb30"
|
revision = "81e90905daefcd6fd217b62423c0908922eadb30"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "golang.org/x/oauth2"
|
|
||||||
packages = [".","google","internal","jws","jwt"]
|
|
||||||
revision = "9a379c6b3e95a790ffc43293c2a78dee0d7b6e20"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "golang.org/x/sys"
|
name = "golang.org/x/sys"
|
||||||
|
@ -331,12 +331,6 @@
|
||||||
packages = ["internal/gen","internal/triegen","internal/ucd","transform","unicode/cldr","unicode/norm","width"]
|
packages = ["internal/gen","internal/triegen","internal/ucd","transform","unicode/cldr","unicode/norm","width"]
|
||||||
revision = "ac87088df8ef557f1e32cd00ed0b6fbc3f7ddafb"
|
revision = "ac87088df8ef557f1e32cd00ed0b6fbc3f7ddafb"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "google.golang.org/appengine"
|
|
||||||
packages = [".","internal","internal/app_identity","internal/base","internal/datastore","internal/log","internal/modules","internal/remote_api","internal/urlfetch","urlfetch"]
|
|
||||||
revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a"
|
|
||||||
version = "v1.0.0"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "google.golang.org/genproto"
|
name = "google.golang.org/genproto"
|
||||||
|
@ -355,21 +349,32 @@
|
||||||
revision = "3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4"
|
revision = "3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4"
|
||||||
version = "v0.9.0"
|
version = "v0.9.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "v2"
|
||||||
|
name = "gopkg.in/mgo.v2"
|
||||||
|
packages = ["bson","internal/json"]
|
||||||
|
revision = "3f83fa5005286a7fe593b055f0d7771a7dce4655"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "v2"
|
branch = "v2"
|
||||||
name = "gopkg.in/yaml.v2"
|
name = "gopkg.in/yaml.v2"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "eb3733d160e74a9c7e442f435eb3bea458e1d19f"
|
revision = "eb3733d160e74a9c7e442f435eb3bea458e1d19f"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "k8s.io/apimachinery"
|
||||||
|
packages = ["pkg/api/equality","pkg/api/errors","pkg/api/meta","pkg/api/resource","pkg/apimachinery","pkg/apimachinery/announced","pkg/apimachinery/registered","pkg/apis/meta/v1","pkg/apis/meta/v1/unstructured","pkg/apis/meta/v1alpha1","pkg/conversion","pkg/conversion/queryparams","pkg/conversion/unstructured","pkg/fields","pkg/labels","pkg/openapi","pkg/runtime","pkg/runtime/schema","pkg/runtime/serializer","pkg/runtime/serializer/json","pkg/runtime/serializer/protobuf","pkg/runtime/serializer/recognizer","pkg/runtime/serializer/streaming","pkg/runtime/serializer/versioning","pkg/selection","pkg/types","pkg/util/cache","pkg/util/clock","pkg/util/diff","pkg/util/errors","pkg/util/framer","pkg/util/intstr","pkg/util/json","pkg/util/net","pkg/util/rand","pkg/util/runtime","pkg/util/sets","pkg/util/validation","pkg/util/validation/field","pkg/util/wait","pkg/util/yaml","pkg/version","pkg/watch","third_party/forked/golang/reflect"]
|
||||||
|
revision = "1fd2e63a9a370677308a42f24fd40c86438afddf"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "k8s.io/client-go"
|
name = "k8s.io/client-go"
|
||||||
packages = ["1.5/discovery","1.5/kubernetes","1.5/kubernetes/typed/apps/v1alpha1","1.5/kubernetes/typed/authentication/v1beta1","1.5/kubernetes/typed/authorization/v1beta1","1.5/kubernetes/typed/autoscaling/v1","1.5/kubernetes/typed/batch/v1","1.5/kubernetes/typed/certificates/v1alpha1","1.5/kubernetes/typed/core/v1","1.5/kubernetes/typed/extensions/v1beta1","1.5/kubernetes/typed/policy/v1alpha1","1.5/kubernetes/typed/rbac/v1alpha1","1.5/kubernetes/typed/storage/v1beta1","1.5/pkg/api","1.5/pkg/api/errors","1.5/pkg/api/install","1.5/pkg/api/meta","1.5/pkg/api/meta/metatypes","1.5/pkg/api/resource","1.5/pkg/api/unversioned","1.5/pkg/api/v1","1.5/pkg/api/validation/path","1.5/pkg/apimachinery","1.5/pkg/apimachinery/announced","1.5/pkg/apimachinery/registered","1.5/pkg/apis/apps","1.5/pkg/apis/apps/install","1.5/pkg/apis/apps/v1alpha1","1.5/pkg/apis/authentication","1.5/pkg/apis/authentication/install","1.5/pkg/apis/authentication/v1beta1","1.5/pkg/apis/authorization","1.5/pkg/apis/authorization/install","1.5/pkg/apis/authorization/v1beta1","1.5/pkg/apis/autoscaling","1.5/pkg/apis/autoscaling/install","1.5/pkg/apis/autoscaling/v1","1.5/pkg/apis/batch","1.5/pkg/apis/batch/install","1.5/pkg/apis/batch/v1","1.5/pkg/apis/batch/v2alpha1","1.5/pkg/apis/certificates","1.5/pkg/apis/certificates/install","1.5/pkg/apis/certificates/v1alpha1","1.5/pkg/apis/extensions","1.5/pkg/apis/extensions/install","1.5/pkg/apis/extensions/v1beta1","1.5/pkg/apis/policy","1.5/pkg/apis/policy/install","1.5/pkg/apis/policy/v1alpha1","1.5/pkg/apis/rbac","1.5/pkg/apis/rbac/install","1.5/pkg/apis/rbac/v1alpha1","1.5/pkg/apis/storage","1.5/pkg/apis/storage/install","1.5/pkg/apis/storage/v1beta1","1.5/pkg/auth/user","1.5/pkg/conversion","1.5/pkg/conversion/queryparams","1.5/pkg/fields","1.5/pkg/genericapiserver/openapi/common","1.5/pkg/labels","1.5/pkg/runtime","1.5/pkg/runtime/serializer","1.5/pkg/runtime/serializer/json","1.5/pkg/runtime/serializer/protobuf","1.5/pkg/runtime/serializer/recognizer","1.5/pkg/runtime/serializer/streaming","1.5/pkg/runtime/serializer/versioning","1.5/pkg/selection","1.5/pkg/third_party/forked/golang/reflect","1.5/pkg/types","1.5/pkg/util","1.5/pkg/util/cert","1.5/pkg/util/clock","1.5/pkg/util/errors","1.5/pkg/util/flowcontrol","1.5/pkg/util/framer","1.5/pkg/util/homedir","1.5/pkg/util/integer","1.5/pkg/util/intstr","1.5/pkg/util/json","1.5/pkg/util/labels","1.5/pkg/util/net","1.5/pkg/util/parsers","1.5/pkg/util/rand","1.5/pkg/util/runtime","1.5/pkg/util/sets","1.5/pkg/util/uuid","1.5/pkg/util/validation","1.5/pkg/util/validation/field","1.5/pkg/util/wait","1.5/pkg/util/yaml","1.5/pkg/version","1.5/pkg/watch","1.5/pkg/watch/versioned","1.5/plugin/pkg/client/auth","1.5/plugin/pkg/client/auth/gcp","1.5/plugin/pkg/client/auth/oidc","1.5/rest","1.5/tools/auth","1.5/tools/cache","1.5/tools/clientcmd","1.5/tools/clientcmd/api","1.5/tools/clientcmd/api/latest","1.5/tools/clientcmd/api/v1","1.5/tools/metrics","1.5/transport"]
|
packages = ["discovery","kubernetes","kubernetes/scheme","kubernetes/typed/admissionregistration/v1alpha1","kubernetes/typed/apps/v1beta1","kubernetes/typed/authentication/v1","kubernetes/typed/authentication/v1beta1","kubernetes/typed/authorization/v1","kubernetes/typed/authorization/v1beta1","kubernetes/typed/autoscaling/v1","kubernetes/typed/autoscaling/v2alpha1","kubernetes/typed/batch/v1","kubernetes/typed/batch/v2alpha1","kubernetes/typed/certificates/v1beta1","kubernetes/typed/core/v1","kubernetes/typed/extensions/v1beta1","kubernetes/typed/networking/v1","kubernetes/typed/policy/v1beta1","kubernetes/typed/rbac/v1alpha1","kubernetes/typed/rbac/v1beta1","kubernetes/typed/settings/v1alpha1","kubernetes/typed/storage/v1","kubernetes/typed/storage/v1beta1","pkg/api","pkg/api/v1","pkg/api/v1/ref","pkg/apis/admissionregistration","pkg/apis/admissionregistration/v1alpha1","pkg/apis/apps","pkg/apis/apps/v1beta1","pkg/apis/authentication","pkg/apis/authentication/v1","pkg/apis/authentication/v1beta1","pkg/apis/authorization","pkg/apis/authorization/v1","pkg/apis/authorization/v1beta1","pkg/apis/autoscaling","pkg/apis/autoscaling/v1","pkg/apis/autoscaling/v2alpha1","pkg/apis/batch","pkg/apis/batch/v1","pkg/apis/batch/v2alpha1","pkg/apis/certificates","pkg/apis/certificates/v1beta1","pkg/apis/extensions","pkg/apis/extensions/v1beta1","pkg/apis/networking","pkg/apis/networking/v1","pkg/apis/policy","pkg/apis/policy/v1beta1","pkg/apis/rbac","pkg/apis/rbac/v1alpha1","pkg/apis/rbac/v1beta1","pkg/apis/settings","pkg/apis/settings/v1alpha1","pkg/apis/storage","pkg/apis/storage/v1","pkg/apis/storage/v1beta1","pkg/util","pkg/util/parsers","pkg/version","rest","rest/watch","tools/auth","tools/cache","tools/clientcmd","tools/clientcmd/api","tools/clientcmd/api/latest","tools/clientcmd/api/v1","tools/metrics","transport","util/cert","util/flowcontrol","util/homedir","util/integer"]
|
||||||
revision = "1195e3a8ee1a529d53eed7c624527a68555ddf1f"
|
revision = "d92e8497f71b7b4e0494e5bd204b48d34bd6f254"
|
||||||
version = "v1.5.1"
|
version = "v4.0.0"
|
||||||
|
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "0ddaab68018c7f38bccad1d9932df961ae52544dc56e3c0060ce1c28e5da0a47"
|
inputs-digest = "916c5fe7d9ce3a19d3956068cae5daf94be1269ae86400dfed36308fbd88a539"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|
|
@ -3,7 +3,8 @@ package federation
|
||||||
import (
|
import (
|
||||||
"github.com/coredns/coredns/plugin/kubernetes"
|
"github.com/coredns/coredns/plugin/kubernetes"
|
||||||
|
|
||||||
"k8s.io/client-go/1.5/pkg/api"
|
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
api "k8s.io/client-go/pkg/api/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type APIConnFederationTest struct{}
|
type APIConnFederationTest struct{}
|
||||||
|
@ -11,23 +12,22 @@ type APIConnFederationTest struct{}
|
||||||
func (APIConnFederationTest) Run() { return }
|
func (APIConnFederationTest) Run() { return }
|
||||||
func (APIConnFederationTest) Stop() error { return nil }
|
func (APIConnFederationTest) Stop() error { return nil }
|
||||||
|
|
||||||
func (APIConnFederationTest) PodIndex(string) []interface{} {
|
func (APIConnFederationTest) PodIndex(string) []*api.Pod {
|
||||||
a := make([]interface{}, 1)
|
a := []*api.Pod{{
|
||||||
a[0] = &api.Pod{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
ObjectMeta: api.ObjectMeta{
|
|
||||||
Namespace: "podns",
|
Namespace: "podns",
|
||||||
},
|
},
|
||||||
Status: api.PodStatus{
|
Status: api.PodStatus{
|
||||||
PodIP: "10.240.0.1", // Remote IP set in test.ResponseWriter
|
PodIP: "10.240.0.1", // Remote IP set in test.ResponseWriter
|
||||||
},
|
},
|
||||||
}
|
}}
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
func (APIConnFederationTest) ServiceList() []*api.Service {
|
func (APIConnFederationTest) ServiceList() []*api.Service {
|
||||||
svcs := []*api.Service{
|
svcs := []*api.Service{
|
||||||
{
|
{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "svc1",
|
Name: "svc1",
|
||||||
Namespace: "testns",
|
Namespace: "testns",
|
||||||
},
|
},
|
||||||
|
@ -41,7 +41,7 @@ func (APIConnFederationTest) ServiceList() []*api.Service {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "hdls1",
|
Name: "hdls1",
|
||||||
Namespace: "testns",
|
Namespace: "testns",
|
||||||
},
|
},
|
||||||
|
@ -50,7 +50,7 @@ func (APIConnFederationTest) ServiceList() []*api.Service {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "external",
|
Name: "external",
|
||||||
Namespace: "testns",
|
Namespace: "testns",
|
||||||
},
|
},
|
||||||
|
@ -65,12 +65,10 @@ func (APIConnFederationTest) ServiceList() []*api.Service {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return svcs
|
return svcs
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (APIConnFederationTest) EndpointsList() api.EndpointsList {
|
func (APIConnFederationTest) EndpointsList() []*api.Endpoints {
|
||||||
return api.EndpointsList{
|
eps := []*api.Endpoints{
|
||||||
Items: []api.Endpoints{
|
|
||||||
{
|
{
|
||||||
Subsets: []api.EndpointSubset{
|
Subsets: []api.EndpointSubset{
|
||||||
{
|
{
|
||||||
|
@ -89,18 +87,18 @@ func (APIConnFederationTest) EndpointsList() api.EndpointsList {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "svc1",
|
Name: "svc1",
|
||||||
Namespace: "testns",
|
Namespace: "testns",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
return eps
|
||||||
}
|
}
|
||||||
|
|
||||||
func (APIConnFederationTest) GetNodeByName(name string) (api.Node, error) {
|
func (APIConnFederationTest) GetNodeByName(name string) (*api.Node, error) {
|
||||||
return api.Node{
|
return &api.Node{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "test.node.foo.bar",
|
Name: "test.node.foo.bar",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
kubernetes.LabelRegion: "fd-r",
|
kubernetes.LabelRegion: "fd-r",
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"github.com/coredns/coredns/plugin"
|
"github.com/coredns/coredns/plugin"
|
||||||
"github.com/coredns/coredns/request"
|
"github.com/coredns/coredns/request"
|
||||||
|
|
||||||
"k8s.io/client-go/1.5/pkg/api"
|
api "k8s.io/client-go/pkg/api/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AutoPath implements the AutoPathFunc call from the autopath plugin.
|
// AutoPath implements the AutoPathFunc call from the autopath plugin.
|
||||||
|
@ -40,14 +40,10 @@ func (k *Kubernetes) AutoPath(state request.Request) []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// podWithIP return the api.Pod for source IP ip. It returns nil if nothing can be found.
|
// podWithIP return the api.Pod for source IP ip. It returns nil if nothing can be found.
|
||||||
func (k *Kubernetes) podWithIP(ip string) (p *api.Pod) {
|
func (k *Kubernetes) podWithIP(ip string) *api.Pod {
|
||||||
objList := k.APIConn.PodIndex(ip)
|
ps := k.APIConn.PodIndex(ip)
|
||||||
for _, o := range objList {
|
if len(ps) == 0 {
|
||||||
p, ok := o.(*api.Pod)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return p
|
return ps[0]
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,45 +3,31 @@ package kubernetes
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/client-go/1.5/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/1.5/pkg/api"
|
api "k8s.io/client-go/pkg/api/v1"
|
||||||
unversionedapi "k8s.io/client-go/1.5/pkg/api/unversioned"
|
"k8s.io/client-go/tools/cache"
|
||||||
"k8s.io/client-go/1.5/pkg/api/v1"
|
|
||||||
"k8s.io/client-go/1.5/pkg/labels"
|
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/1.5/pkg/runtime"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/client-go/1.5/pkg/watch"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/client-go/1.5/tools/cache"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
namespace = api.NamespaceAll
|
namespace = api.NamespaceAll
|
||||||
)
|
)
|
||||||
|
|
||||||
// storeToNamespaceLister makes a Store that lists Namespaces.
|
|
||||||
type storeToNamespaceLister struct {
|
|
||||||
cache.Store
|
|
||||||
}
|
|
||||||
|
|
||||||
const podIPIndex = "PodIP"
|
const podIPIndex = "PodIP"
|
||||||
|
|
||||||
// List lists all Namespaces in the store.
|
|
||||||
func (s *storeToNamespaceLister) List() (ns api.NamespaceList, err error) {
|
|
||||||
for _, m := range s.Store.List() {
|
|
||||||
ns.Items = append(ns.Items, *(m.(*api.Namespace)))
|
|
||||||
}
|
|
||||||
return ns, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type dnsController interface {
|
type dnsController interface {
|
||||||
ServiceList() []*api.Service
|
ServiceList() []*api.Service
|
||||||
PodIndex(string) []interface{}
|
PodIndex(string) []*api.Pod
|
||||||
EndpointsList() api.EndpointsList
|
EndpointsList() []*api.Endpoints
|
||||||
|
|
||||||
GetNodeByName(string) (api.Node, error)
|
GetNodeByName(string) (*api.Node, error)
|
||||||
|
|
||||||
Run()
|
Run()
|
||||||
Stop() error
|
Stop() error
|
||||||
|
@ -52,15 +38,13 @@ type dnsControl struct {
|
||||||
|
|
||||||
selector *labels.Selector
|
selector *labels.Selector
|
||||||
|
|
||||||
svcController *cache.Controller
|
svcController cache.Controller
|
||||||
podController *cache.Controller
|
podController cache.Controller
|
||||||
nsController *cache.Controller
|
epController cache.Controller
|
||||||
epController *cache.Controller
|
|
||||||
|
|
||||||
svcLister cache.StoreToServiceLister
|
svcLister cache.Indexer
|
||||||
podLister cache.StoreToPodLister
|
podLister cache.Indexer
|
||||||
nsLister storeToNamespaceLister
|
epLister cache.Store
|
||||||
epLister cache.StoreToEndpointsLister
|
|
||||||
|
|
||||||
// stopLock is used to enforce only a single call to Stop is active.
|
// stopLock is used to enforce only a single call to Stop is active.
|
||||||
// Needed because we allow stopping through an http endpoint and
|
// Needed because we allow stopping through an http endpoint and
|
||||||
|
@ -74,7 +58,7 @@ type dnsControlOpts struct {
|
||||||
initPodCache bool
|
initPodCache bool
|
||||||
resyncPeriod time.Duration
|
resyncPeriod time.Duration
|
||||||
// Label handling.
|
// Label handling.
|
||||||
labelSelector *unversionedapi.LabelSelector
|
labelSelector *meta.LabelSelector
|
||||||
selector *labels.Selector
|
selector *labels.Selector
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,8 +69,7 @@ func newdnsController(kubeClient *kubernetes.Clientset, opts dnsControlOpts) *dn
|
||||||
selector: opts.selector,
|
selector: opts.selector,
|
||||||
stopCh: make(chan struct{}),
|
stopCh: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
dns.svcLister, dns.svcController = cache.NewIndexerInformer(
|
||||||
dns.svcLister.Indexer, dns.svcController = cache.NewIndexerInformer(
|
|
||||||
&cache.ListWatch{
|
&cache.ListWatch{
|
||||||
ListFunc: serviceListFunc(dns.client, namespace, dns.selector),
|
ListFunc: serviceListFunc(dns.client, namespace, dns.selector),
|
||||||
WatchFunc: serviceWatchFunc(dns.client, namespace, dns.selector),
|
WatchFunc: serviceWatchFunc(dns.client, namespace, dns.selector),
|
||||||
|
@ -97,7 +80,7 @@ func newdnsController(kubeClient *kubernetes.Clientset, opts dnsControlOpts) *dn
|
||||||
cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
|
cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
|
||||||
|
|
||||||
if opts.initPodCache {
|
if opts.initPodCache {
|
||||||
dns.podLister.Indexer, dns.podController = cache.NewIndexerInformer(
|
dns.podLister, dns.podController = cache.NewIndexerInformer(
|
||||||
&cache.ListWatch{
|
&cache.ListWatch{
|
||||||
ListFunc: podListFunc(dns.client, namespace, dns.selector),
|
ListFunc: podListFunc(dns.client, namespace, dns.selector),
|
||||||
WatchFunc: podWatchFunc(dns.client, namespace, dns.selector),
|
WatchFunc: podWatchFunc(dns.client, namespace, dns.selector),
|
||||||
|
@ -107,17 +90,7 @@ func newdnsController(kubeClient *kubernetes.Clientset, opts dnsControlOpts) *dn
|
||||||
cache.ResourceEventHandlerFuncs{},
|
cache.ResourceEventHandlerFuncs{},
|
||||||
cache.Indexers{podIPIndex: podIPIndexFunc})
|
cache.Indexers{podIPIndex: podIPIndexFunc})
|
||||||
}
|
}
|
||||||
|
dns.epLister, dns.epController = cache.NewInformer(
|
||||||
dns.nsLister.Store, dns.nsController = cache.NewInformer(
|
|
||||||
&cache.ListWatch{
|
|
||||||
ListFunc: namespaceListFunc(dns.client, dns.selector),
|
|
||||||
WatchFunc: namespaceWatchFunc(dns.client, dns.selector),
|
|
||||||
},
|
|
||||||
&api.Namespace{},
|
|
||||||
opts.resyncPeriod,
|
|
||||||
cache.ResourceEventHandlerFuncs{})
|
|
||||||
|
|
||||||
dns.epLister.Store, dns.epController = cache.NewInformer(
|
|
||||||
&cache.ListWatch{
|
&cache.ListWatch{
|
||||||
ListFunc: endpointsListFunc(dns.client, namespace, dns.selector),
|
ListFunc: endpointsListFunc(dns.client, namespace, dns.selector),
|
||||||
WatchFunc: endpointsWatchFunc(dns.client, namespace, dns.selector),
|
WatchFunc: endpointsWatchFunc(dns.client, namespace, dns.selector),
|
||||||
|
@ -137,182 +110,86 @@ func podIPIndexFunc(obj interface{}) ([]string, error) {
|
||||||
return []string{p.Status.PodIP}, nil
|
return []string{p.Status.PodIP}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func serviceListFunc(c *kubernetes.Clientset, ns string, s *labels.Selector) func(api.ListOptions) (runtime.Object, error) {
|
func serviceListFunc(c *kubernetes.Clientset, ns string, s *labels.Selector) func(meta.ListOptions) (runtime.Object, error) {
|
||||||
return func(opts api.ListOptions) (runtime.Object, error) {
|
return func(opts meta.ListOptions) (runtime.Object, error) {
|
||||||
if s != nil {
|
if s != nil {
|
||||||
opts.LabelSelector = *s
|
opts.LabelSelector = (*s).String()
|
||||||
}
|
}
|
||||||
listV1, err := c.Core().Services(ns).List(opts)
|
listV1, err := c.Services(ns).List(opts)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var listAPI api.ServiceList
|
return listV1, err
|
||||||
err = v1.Convert_v1_ServiceList_To_api_ServiceList(listV1, &listAPI, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &listAPI, err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func podListFunc(c *kubernetes.Clientset, ns string, s *labels.Selector) func(api.ListOptions) (runtime.Object, error) {
|
func podListFunc(c *kubernetes.Clientset, ns string, s *labels.Selector) func(meta.ListOptions) (runtime.Object, error) {
|
||||||
return func(opts api.ListOptions) (runtime.Object, error) {
|
return func(opts meta.ListOptions) (runtime.Object, error) {
|
||||||
if s != nil {
|
if s != nil {
|
||||||
opts.LabelSelector = *s
|
opts.LabelSelector = (*s).String()
|
||||||
}
|
}
|
||||||
listV1, err := c.Core().Pods(ns).List(opts)
|
listV1, err := c.Pods(ns).List(opts)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var listAPI api.PodList
|
return listV1, err
|
||||||
err = v1.Convert_v1_PodList_To_api_PodList(listV1, &listAPI, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &listAPI, err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func v1ToAPIFilter(in watch.Event) (out watch.Event, keep bool) {
|
func serviceWatchFunc(c *kubernetes.Clientset, ns string, s *labels.Selector) func(options meta.ListOptions) (watch.Interface, error) {
|
||||||
if in.Type == watch.Error {
|
return func(options meta.ListOptions) (watch.Interface, error) {
|
||||||
return in, true
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v1Obj := in.Object.(type) {
|
|
||||||
case *v1.Service:
|
|
||||||
var apiObj api.Service
|
|
||||||
err := v1.Convert_v1_Service_To_api_Service(v1Obj, &apiObj, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[ERROR] Could not convert v1.Service: %s", err)
|
|
||||||
return in, true
|
|
||||||
}
|
|
||||||
return watch.Event{Type: in.Type, Object: &apiObj}, true
|
|
||||||
case *v1.Pod:
|
|
||||||
var apiObj api.Pod
|
|
||||||
err := v1.Convert_v1_Pod_To_api_Pod(v1Obj, &apiObj, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[ERROR] Could not convert v1.Pod: %s", err)
|
|
||||||
return in, true
|
|
||||||
}
|
|
||||||
return watch.Event{Type: in.Type, Object: &apiObj}, true
|
|
||||||
case *v1.Namespace:
|
|
||||||
var apiObj api.Namespace
|
|
||||||
err := v1.Convert_v1_Namespace_To_api_Namespace(v1Obj, &apiObj, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[ERROR] Could not convert v1.Namespace: %s", err)
|
|
||||||
return in, true
|
|
||||||
}
|
|
||||||
return watch.Event{Type: in.Type, Object: &apiObj}, true
|
|
||||||
case *v1.Endpoints:
|
|
||||||
var apiObj api.Endpoints
|
|
||||||
err := v1.Convert_v1_Endpoints_To_api_Endpoints(v1Obj, &apiObj, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[ERROR] Could not convert v1.Endpoint: %s", err)
|
|
||||||
return in, true
|
|
||||||
}
|
|
||||||
return watch.Event{Type: in.Type, Object: &apiObj}, true
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("[WARN] Unhandled v1 type in event: %v", in)
|
|
||||||
return in, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func serviceWatchFunc(c *kubernetes.Clientset, ns string, s *labels.Selector) func(options api.ListOptions) (watch.Interface, error) {
|
|
||||||
return func(options api.ListOptions) (watch.Interface, error) {
|
|
||||||
if s != nil {
|
if s != nil {
|
||||||
options.LabelSelector = *s
|
options.LabelSelector = (*s).String()
|
||||||
}
|
}
|
||||||
w, err := c.Core().Services(ns).Watch(options)
|
w, err := c.Services(ns).Watch(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return watch.Filter(w, v1ToAPIFilter), nil
|
return w, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func podWatchFunc(c *kubernetes.Clientset, ns string, s *labels.Selector) func(options api.ListOptions) (watch.Interface, error) {
|
func podWatchFunc(c *kubernetes.Clientset, ns string, s *labels.Selector) func(options meta.ListOptions) (watch.Interface, error) {
|
||||||
return func(options api.ListOptions) (watch.Interface, error) {
|
return func(options meta.ListOptions) (watch.Interface, error) {
|
||||||
if s != nil {
|
if s != nil {
|
||||||
options.LabelSelector = *s
|
options.LabelSelector = (*s).String()
|
||||||
}
|
}
|
||||||
w, err := c.Core().Pods(ns).Watch(options)
|
w, err := c.Pods(ns).Watch(options)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return watch.Filter(w, v1ToAPIFilter), nil
|
return w, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func namespaceListFunc(c *kubernetes.Clientset, s *labels.Selector) func(api.ListOptions) (runtime.Object, error) {
|
func endpointsListFunc(c *kubernetes.Clientset, ns string, s *labels.Selector) func(meta.ListOptions) (runtime.Object, error) {
|
||||||
return func(opts api.ListOptions) (runtime.Object, error) {
|
return func(opts meta.ListOptions) (runtime.Object, error) {
|
||||||
if s != nil {
|
if s != nil {
|
||||||
opts.LabelSelector = *s
|
opts.LabelSelector = (*s).String()
|
||||||
}
|
}
|
||||||
listV1, err := c.Core().Namespaces().List(opts)
|
listV1, err := c.Endpoints(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var listAPI api.NamespaceList
|
return listV1, err
|
||||||
err = v1.Convert_v1_NamespaceList_To_api_NamespaceList(listV1, &listAPI, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &listAPI, err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func namespaceWatchFunc(c *kubernetes.Clientset, s *labels.Selector) func(options api.ListOptions) (watch.Interface, error) {
|
func endpointsWatchFunc(c *kubernetes.Clientset, ns string, s *labels.Selector) func(options meta.ListOptions) (watch.Interface, error) {
|
||||||
return func(options api.ListOptions) (watch.Interface, error) {
|
return func(options meta.ListOptions) (watch.Interface, error) {
|
||||||
if s != nil {
|
if s != nil {
|
||||||
options.LabelSelector = *s
|
options.LabelSelector = (*s).String()
|
||||||
}
|
}
|
||||||
w, err := c.Core().Namespaces().Watch(options)
|
w, err := c.Endpoints(ns).Watch(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return watch.Filter(w, v1ToAPIFilter), nil
|
return w, nil
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func endpointsListFunc(c *kubernetes.Clientset, ns string, s *labels.Selector) func(api.ListOptions) (runtime.Object, error) {
|
|
||||||
return func(opts api.ListOptions) (runtime.Object, error) {
|
|
||||||
if s != nil {
|
|
||||||
opts.LabelSelector = *s
|
|
||||||
}
|
|
||||||
listV1, err := c.Core().Endpoints(ns).List(opts)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var listAPI api.EndpointsList
|
|
||||||
err = v1.Convert_v1_EndpointsList_To_api_EndpointsList(listV1, &listAPI, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &listAPI, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func endpointsWatchFunc(c *kubernetes.Clientset, ns string, s *labels.Selector) func(options api.ListOptions) (watch.Interface, error) {
|
|
||||||
return func(options api.ListOptions) (watch.Interface, error) {
|
|
||||||
if s != nil {
|
|
||||||
options.LabelSelector = *s
|
|
||||||
}
|
|
||||||
w, err := c.Core().Endpoints(ns).Watch(options)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return watch.Filter(w, v1ToAPIFilter), nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dns *dnsControl) controllersInSync() bool {
|
func (dns *dnsControl) controllersInSync() bool {
|
||||||
hs := dns.svcController.HasSynced() &&
|
hs := dns.svcController.HasSynced() &&
|
||||||
dns.nsController.HasSynced() &&
|
|
||||||
dns.epController.HasSynced()
|
dns.epController.HasSynced()
|
||||||
|
|
||||||
if dns.podController != nil {
|
if dns.podController != nil {
|
||||||
|
@ -341,7 +218,6 @@ func (dns *dnsControl) Stop() error {
|
||||||
// Run starts the controller.
|
// Run starts the controller.
|
||||||
func (dns *dnsControl) Run() {
|
func (dns *dnsControl) Run() {
|
||||||
go dns.svcController.Run(dns.stopCh)
|
go dns.svcController.Run(dns.stopCh)
|
||||||
go dns.nsController.Run(dns.stopCh)
|
|
||||||
go dns.epController.Run(dns.stopCh)
|
go dns.epController.Run(dns.stopCh)
|
||||||
if dns.podController != nil {
|
if dns.podController != nil {
|
||||||
go dns.podController.Run(dns.stopCh)
|
go dns.podController.Run(dns.stopCh)
|
||||||
|
@ -349,54 +225,52 @@ func (dns *dnsControl) Run() {
|
||||||
<-dns.stopCh
|
<-dns.stopCh
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dns *dnsControl) NamespaceList() *api.NamespaceList {
|
func (dns *dnsControl) ServiceList() (svcs []*api.Service) {
|
||||||
nsList, err := dns.nsLister.List()
|
os := dns.svcLister.List()
|
||||||
if err != nil {
|
for _, o := range os {
|
||||||
return &api.NamespaceList{}
|
s, ok := o.(*api.Service)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
svcs = append(svcs, s)
|
||||||
return &nsList
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dns *dnsControl) ServiceList() []*api.Service {
|
|
||||||
svcs, err := dns.svcLister.List(labels.Everything())
|
|
||||||
if err != nil {
|
|
||||||
return []*api.Service{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return svcs
|
return svcs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dns *dnsControl) PodIndex(ip string) []interface{} {
|
func (dns *dnsControl) PodIndex(ip string) (pods []*api.Pod) {
|
||||||
if dns.podLister.Indexer == nil {
|
if dns.podLister == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
pods, err := dns.podLister.Indexer.ByIndex(podIPIndex, ip)
|
os, err := dns.podLister.ByIndex(podIPIndex, ip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
for _, o := range os {
|
||||||
|
p, ok := o.(*api.Pod)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pods = append(pods, p)
|
||||||
|
}
|
||||||
return pods
|
return pods
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dns *dnsControl) EndpointsList() api.EndpointsList {
|
func (dns *dnsControl) EndpointsList() (eps []*api.Endpoints) {
|
||||||
epl, err := dns.epLister.List()
|
os := dns.epLister.List()
|
||||||
if err != nil {
|
for _, o := range os {
|
||||||
return api.EndpointsList{}
|
ep, ok := o.(*api.Endpoints)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
eps = append(eps, ep)
|
||||||
return epl
|
}
|
||||||
|
return eps
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dns *dnsControl) GetNodeByName(name string) (api.Node, error) {
|
func (dns *dnsControl) GetNodeByName(name string) (*api.Node, error) {
|
||||||
v1node, err := dns.client.Core().Nodes().Get(name)
|
v1node, err := dns.client.Nodes().Get(name, meta.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return api.Node{}, err
|
return &api.Node{}, err
|
||||||
}
|
}
|
||||||
var apinode api.Node
|
return v1node, nil
|
||||||
err = v1.Convert_v1_Node_To_api_Node(v1node, &apinode, nil)
|
|
||||||
if err != nil {
|
|
||||||
return api.Node{}, err
|
|
||||||
}
|
|
||||||
return apinode, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,8 @@ import (
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"k8s.io/client-go/1.5/pkg/api"
|
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
api "k8s.io/client-go/pkg/api/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var dnsTestCases = []test.Case{
|
var dnsTestCases = []test.Case{
|
||||||
|
@ -193,23 +194,22 @@ type APIConnServeTest struct{}
|
||||||
func (APIConnServeTest) Run() { return }
|
func (APIConnServeTest) Run() { return }
|
||||||
func (APIConnServeTest) Stop() error { return nil }
|
func (APIConnServeTest) Stop() error { return nil }
|
||||||
|
|
||||||
func (APIConnServeTest) PodIndex(string) []interface{} {
|
func (APIConnServeTest) PodIndex(string) []*api.Pod {
|
||||||
a := make([]interface{}, 1)
|
a := []*api.Pod{{
|
||||||
a[0] = &api.Pod{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
ObjectMeta: api.ObjectMeta{
|
|
||||||
Namespace: "podns",
|
Namespace: "podns",
|
||||||
},
|
},
|
||||||
Status: api.PodStatus{
|
Status: api.PodStatus{
|
||||||
PodIP: "10.240.0.1", // Remote IP set in test.ResponseWriter
|
PodIP: "10.240.0.1", // Remote IP set in test.ResponseWriter
|
||||||
},
|
},
|
||||||
}
|
}}
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
func (APIConnServeTest) ServiceList() []*api.Service {
|
func (APIConnServeTest) ServiceList() []*api.Service {
|
||||||
svcs := []*api.Service{
|
svcs := []*api.Service{
|
||||||
{
|
{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "svc1",
|
Name: "svc1",
|
||||||
Namespace: "testns",
|
Namespace: "testns",
|
||||||
},
|
},
|
||||||
|
@ -223,7 +223,7 @@ func (APIConnServeTest) ServiceList() []*api.Service {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "hdls1",
|
Name: "hdls1",
|
||||||
Namespace: "testns",
|
Namespace: "testns",
|
||||||
},
|
},
|
||||||
|
@ -232,7 +232,7 @@ func (APIConnServeTest) ServiceList() []*api.Service {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "external",
|
Name: "external",
|
||||||
Namespace: "testns",
|
Namespace: "testns",
|
||||||
},
|
},
|
||||||
|
@ -247,14 +247,12 @@ func (APIConnServeTest) ServiceList() []*api.Service {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return svcs
|
return svcs
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (APIConnServeTest) EndpointsList() api.EndpointsList {
|
func (APIConnServeTest) EndpointsList() []*api.Endpoints {
|
||||||
n := "test.node.foo.bar"
|
n := "test.node.foo.bar"
|
||||||
|
|
||||||
return api.EndpointsList{
|
eps := []*api.Endpoints{
|
||||||
Items: []api.Endpoints{
|
|
||||||
{
|
{
|
||||||
Subsets: []api.EndpointSubset{
|
Subsets: []api.EndpointSubset{
|
||||||
{
|
{
|
||||||
|
@ -273,7 +271,7 @@ func (APIConnServeTest) EndpointsList() api.EndpointsList {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "svc1",
|
Name: "svc1",
|
||||||
Namespace: "testns",
|
Namespace: "testns",
|
||||||
},
|
},
|
||||||
|
@ -295,7 +293,7 @@ func (APIConnServeTest) EndpointsList() api.EndpointsList {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "hdls1",
|
Name: "hdls1",
|
||||||
Namespace: "testns",
|
Namespace: "testns",
|
||||||
},
|
},
|
||||||
|
@ -317,7 +315,7 @@ func (APIConnServeTest) EndpointsList() api.EndpointsList {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "hdls1",
|
Name: "hdls1",
|
||||||
Namespace: "testns",
|
Namespace: "testns",
|
||||||
},
|
},
|
||||||
|
@ -334,13 +332,13 @@ func (APIConnServeTest) EndpointsList() api.EndpointsList {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
return eps
|
||||||
}
|
}
|
||||||
|
|
||||||
func (APIConnServeTest) GetNodeByName(name string) (api.Node, error) {
|
func (APIConnServeTest) GetNodeByName(name string) (*api.Node, error) {
|
||||||
return api.Node{
|
return &api.Node{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "test.node.foo.bar",
|
Name: "test.node.foo.bar",
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
@ -17,13 +17,13 @@ import (
|
||||||
"github.com/coredns/coredns/request"
|
"github.com/coredns/coredns/request"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"k8s.io/client-go/1.5/kubernetes"
|
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/1.5/pkg/api"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
unversionedapi "k8s.io/client-go/1.5/pkg/api/unversioned"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/1.5/pkg/labels"
|
api "k8s.io/client-go/pkg/api/v1"
|
||||||
"k8s.io/client-go/1.5/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/1.5/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
clientcmdapi "k8s.io/client-go/1.5/tools/clientcmd/api"
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Kubernetes implements a plugin that connects to a Kubernetes cluster.
|
// Kubernetes implements a plugin that connects to a Kubernetes cluster.
|
||||||
|
@ -76,7 +76,6 @@ var (
|
||||||
errNoItems = errors.New("no items found")
|
errNoItems = errors.New("no items found")
|
||||||
errNsNotExposed = errors.New("namespace is not exposed")
|
errNsNotExposed = errors.New("namespace is not exposed")
|
||||||
errInvalidRequest = errors.New("invalid query name")
|
errInvalidRequest = errors.New("invalid query name")
|
||||||
errAPIBadPodType = errors.New("expected type *api.Pod")
|
|
||||||
errPodsDisabled = errors.New("pod records disabled")
|
errPodsDisabled = errors.New("pod records disabled")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -152,14 +151,17 @@ func (k *Kubernetes) getClientConfig() (*rest.Config, error) {
|
||||||
clusterinfo := clientcmdapi.Cluster{}
|
clusterinfo := clientcmdapi.Cluster{}
|
||||||
authinfo := clientcmdapi.AuthInfo{}
|
authinfo := clientcmdapi.AuthInfo{}
|
||||||
|
|
||||||
|
// Connect to API from in cluster
|
||||||
if len(k.APIServerList) == 0 {
|
if len(k.APIServerList) == 0 {
|
||||||
cc, err := rest.InClusterConfig()
|
cc, err := rest.InClusterConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
cc.ContentType = "application/vnd.kubernetes.protobuf"
|
||||||
return cc, err
|
return cc, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Connect to API from out of cluster
|
||||||
endpoint := k.APIServerList[0]
|
endpoint := k.APIServerList[0]
|
||||||
if len(k.APIServerList) > 1 {
|
if len(k.APIServerList) > 1 {
|
||||||
// Use a random port for api proxy, will get the value later through listener.Addr()
|
// Use a random port for api proxy, will get the value later through listener.Addr()
|
||||||
|
@ -230,7 +232,10 @@ func (k *Kubernetes) getClientConfig() (*rest.Config, error) {
|
||||||
overrides.AuthInfo = authinfo
|
overrides.AuthInfo = authinfo
|
||||||
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides)
|
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides)
|
||||||
|
|
||||||
return clientConfig.ClientConfig()
|
cc, err := clientConfig.ClientConfig()
|
||||||
|
cc.ContentType = "application/vnd.kubernetes.protobuf"
|
||||||
|
return cc, err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// initKubeCache initializes a new Kubernetes cache.
|
// initKubeCache initializes a new Kubernetes cache.
|
||||||
|
@ -248,7 +253,7 @@ func (k *Kubernetes) initKubeCache(opts dnsControlOpts) (err error) {
|
||||||
|
|
||||||
if opts.labelSelector != nil {
|
if opts.labelSelector != nil {
|
||||||
var selector labels.Selector
|
var selector labels.Selector
|
||||||
selector, err = unversionedapi.LabelSelectorAsSelector(opts.labelSelector)
|
selector, err = meta.LabelSelectorAsSelector(opts.labelSelector)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create Selector for LabelSelector '%s': %q", opts.labelSelector, err)
|
return fmt.Errorf("unable to create Selector for LabelSelector '%s': %q", opts.labelSelector, err)
|
||||||
}
|
}
|
||||||
|
@ -317,13 +322,7 @@ func (k *Kubernetes) findPods(r recordRequest, zone string) (pods []msg.Service,
|
||||||
}
|
}
|
||||||
|
|
||||||
// PodModeVerified
|
// PodModeVerified
|
||||||
objList := k.APIConn.PodIndex(ip)
|
for _, p := range k.APIConn.PodIndex(ip) {
|
||||||
|
|
||||||
for _, o := range objList {
|
|
||||||
p, ok := o.(*api.Pod)
|
|
||||||
if !ok {
|
|
||||||
return nil, errAPIBadPodType
|
|
||||||
}
|
|
||||||
// If namespace has a wildcard, filter results against Corefile namespace list.
|
// If namespace has a wildcard, filter results against Corefile namespace list.
|
||||||
if wildcard(namespace) && !k.namespaceExposed(p.Namespace) {
|
if wildcard(namespace) && !k.namespaceExposed(p.Namespace) {
|
||||||
continue
|
continue
|
||||||
|
@ -341,11 +340,11 @@ func (k *Kubernetes) findPods(r recordRequest, zone string) (pods []msg.Service,
|
||||||
|
|
||||||
// findServices returns the services matching r from the cache.
|
// findServices returns the services matching r from the cache.
|
||||||
func (k *Kubernetes) findServices(r recordRequest, zone string) (services []msg.Service, err error) {
|
func (k *Kubernetes) findServices(r recordRequest, zone string) (services []msg.Service, err error) {
|
||||||
serviceList := k.APIConn.ServiceList()
|
|
||||||
zonePath := msg.Path(zone, "coredns")
|
zonePath := msg.Path(zone, "coredns")
|
||||||
err = errNoItems // Set to errNoItems to signal really nothing found, gets reset when name is matched.
|
err = errNoItems // Set to errNoItems to signal really nothing found, gets reset when name is matched.
|
||||||
|
|
||||||
for _, svc := range serviceList {
|
for _, svc := range k.APIConn.ServiceList() {
|
||||||
|
|
||||||
if !(match(r.namespace, svc.Namespace) && match(r.service, svc.Name)) {
|
if !(match(r.namespace, svc.Namespace) && match(r.service, svc.Name)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -358,8 +357,8 @@ func (k *Kubernetes) findServices(r recordRequest, zone string) (services []msg.
|
||||||
|
|
||||||
// Endpoint query or headless service
|
// Endpoint query or headless service
|
||||||
if svc.Spec.ClusterIP == api.ClusterIPNone || r.endpoint != "" {
|
if svc.Spec.ClusterIP == api.ClusterIPNone || r.endpoint != "" {
|
||||||
endpointsList := k.APIConn.EndpointsList()
|
|
||||||
for _, ep := range endpointsList.Items {
|
for _, ep := range k.APIConn.EndpointsList() {
|
||||||
if ep.ObjectMeta.Name != svc.Name || ep.ObjectMeta.Namespace != svc.Namespace {
|
if ep.ObjectMeta.Name != svc.Name || ep.ObjectMeta.Namespace != svc.Namespace {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
var kubeApexCases = [](test.Case){
|
var kubeApexCases = []test.Case{
|
||||||
{
|
{
|
||||||
Qname: "cluster.local.", Qtype: dns.TypeSOA,
|
Qname: "cluster.local.", Qtype: dns.TypeSOA,
|
||||||
Rcode: dns.RcodeSuccess,
|
Rcode: dns.RcodeSuccess,
|
||||||
|
|
|
@ -7,7 +7,8 @@ import (
|
||||||
"github.com/coredns/coredns/request"
|
"github.com/coredns/coredns/request"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"k8s.io/client-go/1.5/pkg/api"
|
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
api "k8s.io/client-go/pkg/api/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWildcard(t *testing.T) {
|
func TestWildcard(t *testing.T) {
|
||||||
|
@ -52,12 +53,12 @@ type APIConnServiceTest struct{}
|
||||||
|
|
||||||
func (APIConnServiceTest) Run() { return }
|
func (APIConnServiceTest) Run() { return }
|
||||||
func (APIConnServiceTest) Stop() error { return nil }
|
func (APIConnServiceTest) Stop() error { return nil }
|
||||||
func (APIConnServiceTest) PodIndex(string) []interface{} { return nil }
|
func (APIConnServiceTest) PodIndex(string) []*api.Pod { return nil }
|
||||||
|
|
||||||
func (APIConnServiceTest) ServiceList() []*api.Service {
|
func (APIConnServiceTest) ServiceList() []*api.Service {
|
||||||
svcs := []*api.Service{
|
svcs := []*api.Service{
|
||||||
{
|
{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "svc1",
|
Name: "svc1",
|
||||||
Namespace: "testns",
|
Namespace: "testns",
|
||||||
},
|
},
|
||||||
|
@ -71,7 +72,7 @@ func (APIConnServiceTest) ServiceList() []*api.Service {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "hdls1",
|
Name: "hdls1",
|
||||||
Namespace: "testns",
|
Namespace: "testns",
|
||||||
},
|
},
|
||||||
|
@ -80,7 +81,7 @@ func (APIConnServiceTest) ServiceList() []*api.Service {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "external",
|
Name: "external",
|
||||||
Namespace: "testns",
|
Namespace: "testns",
|
||||||
},
|
},
|
||||||
|
@ -97,11 +98,10 @@ func (APIConnServiceTest) ServiceList() []*api.Service {
|
||||||
return svcs
|
return svcs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (APIConnServiceTest) EndpointsList() api.EndpointsList {
|
func (APIConnServiceTest) EndpointsList() []*api.Endpoints {
|
||||||
n := "test.node.foo.bar"
|
n := "test.node.foo.bar"
|
||||||
|
|
||||||
return api.EndpointsList{
|
eps := []*api.Endpoints{
|
||||||
Items: []api.Endpoints{
|
|
||||||
{
|
{
|
||||||
Subsets: []api.EndpointSubset{
|
Subsets: []api.EndpointSubset{
|
||||||
{
|
{
|
||||||
|
@ -120,7 +120,7 @@ func (APIConnServiceTest) EndpointsList() api.EndpointsList {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "svc1",
|
Name: "svc1",
|
||||||
Namespace: "testns",
|
Namespace: "testns",
|
||||||
},
|
},
|
||||||
|
@ -142,7 +142,7 @@ func (APIConnServiceTest) EndpointsList() api.EndpointsList {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "hdls1",
|
Name: "hdls1",
|
||||||
Namespace: "testns",
|
Namespace: "testns",
|
||||||
},
|
},
|
||||||
|
@ -164,7 +164,7 @@ func (APIConnServiceTest) EndpointsList() api.EndpointsList {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "hdls1",
|
Name: "hdls1",
|
||||||
Namespace: "testns",
|
Namespace: "testns",
|
||||||
},
|
},
|
||||||
|
@ -181,13 +181,13 @@ func (APIConnServiceTest) EndpointsList() api.EndpointsList {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
return eps
|
||||||
}
|
}
|
||||||
|
|
||||||
func (APIConnServiceTest) GetNodeByName(name string) (api.Node, error) {
|
func (APIConnServiceTest) GetNodeByName(name string) (*api.Node, error) {
|
||||||
return api.Node{
|
return &api.Node{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "test.node.foo.bar",
|
Name: "test.node.foo.bar",
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package kubernetes
|
package kubernetes
|
||||||
|
|
||||||
import "net"
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
func localPodIP() net.IP {
|
func localPodIP() net.IP {
|
||||||
addrs, err := net.InterfaceAddrs()
|
addrs, err := net.InterfaceAddrs()
|
||||||
|
@ -26,8 +28,7 @@ func (k *Kubernetes) localNodeName() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find endpoint matching localIP
|
// Find endpoint matching localIP
|
||||||
endpointsList := k.APIConn.EndpointsList()
|
for _, ep := range k.APIConn.EndpointsList() {
|
||||||
for _, ep := range endpointsList.Items {
|
|
||||||
for _, eps := range ep.Subsets {
|
for _, eps := range ep.Subsets {
|
||||||
for _, addr := range eps.Addresses {
|
for _, addr := range eps.Addresses {
|
||||||
if localIP.Equal(net.ParseIP(addr.IP)) {
|
if localIP.Equal(net.ParseIP(addr.IP)) {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"k8s.io/client-go/1.5/pkg/api"
|
api "k8s.io/client-go/pkg/api/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func isDefaultNS(name, zone string) bool {
|
func isDefaultNS(name, zone string) bool {
|
||||||
|
@ -20,12 +20,10 @@ func (k *Kubernetes) nsAddr() *dns.A {
|
||||||
|
|
||||||
rr := new(dns.A)
|
rr := new(dns.A)
|
||||||
localIP := k.interfaceAddrsFunc()
|
localIP := k.interfaceAddrsFunc()
|
||||||
endpointsList := k.APIConn.EndpointsList()
|
|
||||||
|
|
||||||
rr.A = localIP
|
rr.A = localIP
|
||||||
|
|
||||||
FindEndpoint:
|
FindEndpoint:
|
||||||
for _, ep := range endpointsList.Items {
|
for _, ep := range k.APIConn.EndpointsList() {
|
||||||
for _, eps := range ep.Subsets {
|
for _, eps := range ep.Subsets {
|
||||||
for _, addr := range eps.Addresses {
|
for _, addr := range eps.Addresses {
|
||||||
if localIP.Equal(net.ParseIP(addr.IP)) {
|
if localIP.Equal(net.ParseIP(addr.IP)) {
|
||||||
|
@ -42,11 +40,9 @@ FindEndpoint:
|
||||||
rr.A = localIP
|
rr.A = localIP
|
||||||
return rr
|
return rr
|
||||||
}
|
}
|
||||||
// Find service to get ClusterIP
|
|
||||||
serviceList := k.APIConn.ServiceList()
|
|
||||||
|
|
||||||
FindService:
|
FindService:
|
||||||
for _, svc := range serviceList {
|
for _, svc := range k.APIConn.ServiceList() {
|
||||||
if svcName == svc.Name && svcNamespace == svc.Namespace {
|
if svcName == svc.Name && svcNamespace == svc.Namespace {
|
||||||
if svc.Spec.ClusterIP == api.ClusterIPNone {
|
if svc.Spec.ClusterIP == api.ClusterIPNone {
|
||||||
rr.A = localIP
|
rr.A = localIP
|
||||||
|
|
|
@ -3,33 +3,33 @@ package kubernetes
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/client-go/1.5/pkg/api"
|
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
api "k8s.io/client-go/pkg/api/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type APIConnTest struct{}
|
type APIConnTest struct{}
|
||||||
|
|
||||||
func (APIConnTest) Run() { return }
|
func (APIConnTest) Run() { return }
|
||||||
func (APIConnTest) Stop() error { return nil }
|
func (APIConnTest) Stop() error { return nil }
|
||||||
func (APIConnTest) PodIndex(string) []interface{} { return nil }
|
func (APIConnTest) PodIndex(string) []*api.Pod { return nil }
|
||||||
|
|
||||||
func (APIConnTest) ServiceList() []*api.Service {
|
func (APIConnTest) ServiceList() []*api.Service {
|
||||||
svc := api.Service{
|
svcs := []*api.Service{
|
||||||
ObjectMeta: api.ObjectMeta{
|
{
|
||||||
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "dns-service",
|
Name: "dns-service",
|
||||||
Namespace: "kube-system",
|
Namespace: "kube-system",
|
||||||
},
|
},
|
||||||
Spec: api.ServiceSpec{
|
Spec: api.ServiceSpec{
|
||||||
ClusterIP: "10.0.0.111",
|
ClusterIP: "10.0.0.111",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
return svcs
|
||||||
return []*api.Service{&svc}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (APIConnTest) EndpointsList() api.EndpointsList {
|
func (APIConnTest) EndpointsList() []*api.Endpoints {
|
||||||
return api.EndpointsList{
|
eps := []*api.Endpoints{
|
||||||
Items: []api.Endpoints{
|
|
||||||
{
|
{
|
||||||
Subsets: []api.EndpointSubset{
|
Subsets: []api.EndpointSubset{
|
||||||
{
|
{
|
||||||
|
@ -40,16 +40,16 @@ func (APIConnTest) EndpointsList() api.EndpointsList {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "dns-service",
|
Name: "dns-service",
|
||||||
Namespace: "kube-system",
|
Namespace: "kube-system",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
return eps
|
||||||
}
|
}
|
||||||
|
|
||||||
func (APIConnTest) GetNodeByName(name string) (api.Node, error) { return api.Node{}, nil }
|
func (APIConnTest) GetNodeByName(name string) (*api.Node, error) { return &api.Node{}, nil }
|
||||||
|
|
||||||
func TestNsAddr(t *testing.T) {
|
func TestNsAddr(t *testing.T) {
|
||||||
|
|
||||||
|
|
|
@ -25,9 +25,7 @@ func (k *Kubernetes) Reverse(state request.Request, exact bool, opt plugin.Optio
|
||||||
// If a service cluster ip does not match, it checks all endpoints
|
// If a service cluster ip does not match, it checks all endpoints
|
||||||
func (k *Kubernetes) serviceRecordForIP(ip, name string) []msg.Service {
|
func (k *Kubernetes) serviceRecordForIP(ip, name string) []msg.Service {
|
||||||
// First check services with cluster ips
|
// First check services with cluster ips
|
||||||
svcList := k.APIConn.ServiceList()
|
for _, service := range k.APIConn.ServiceList() {
|
||||||
|
|
||||||
for _, service := range svcList {
|
|
||||||
if (len(k.Namespaces) > 0) && !k.namespaceExposed(service.Namespace) {
|
if (len(k.Namespaces) > 0) && !k.namespaceExposed(service.Namespace) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -37,8 +35,7 @@ func (k *Kubernetes) serviceRecordForIP(ip, name string) []msg.Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If no cluster ips match, search endpoints
|
// If no cluster ips match, search endpoints
|
||||||
epList := k.APIConn.EndpointsList()
|
for _, ep := range k.APIConn.EndpointsList() {
|
||||||
for _, ep := range epList.Items {
|
|
||||||
if (len(k.Namespaces) > 0) && !k.namespaceExposed(ep.ObjectMeta.Namespace) {
|
if (len(k.Namespaces) > 0) && !k.namespaceExposed(ep.ObjectMeta.Namespace) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,19 +8,20 @@ import (
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"k8s.io/client-go/1.5/pkg/api"
|
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
api "k8s.io/client-go/pkg/api/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type APIConnReverseTest struct{}
|
type APIConnReverseTest struct{}
|
||||||
|
|
||||||
func (APIConnReverseTest) Run() { return }
|
func (APIConnReverseTest) Run() { return }
|
||||||
func (APIConnReverseTest) Stop() error { return nil }
|
func (APIConnReverseTest) Stop() error { return nil }
|
||||||
func (APIConnReverseTest) PodIndex(string) []interface{} { return nil }
|
func (APIConnReverseTest) PodIndex(string) []*api.Pod { return nil }
|
||||||
|
|
||||||
func (APIConnReverseTest) ServiceList() []*api.Service {
|
func (APIConnReverseTest) ServiceList() []*api.Service {
|
||||||
svcs := []*api.Service{
|
svcs := []*api.Service{
|
||||||
{
|
{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "svc1",
|
Name: "svc1",
|
||||||
Namespace: "testns",
|
Namespace: "testns",
|
||||||
},
|
},
|
||||||
|
@ -37,9 +38,8 @@ func (APIConnReverseTest) ServiceList() []*api.Service {
|
||||||
return svcs
|
return svcs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (APIConnReverseTest) EndpointsList() api.EndpointsList {
|
func (APIConnReverseTest) EndpointsList() []*api.Endpoints {
|
||||||
return api.EndpointsList{
|
eps := []*api.Endpoints{
|
||||||
Items: []api.Endpoints{
|
|
||||||
{
|
{
|
||||||
Subsets: []api.EndpointSubset{
|
Subsets: []api.EndpointSubset{
|
||||||
{
|
{
|
||||||
|
@ -58,18 +58,18 @@ func (APIConnReverseTest) EndpointsList() api.EndpointsList {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "svc1",
|
Name: "svc1",
|
||||||
Namespace: "testns",
|
Namespace: "testns",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
return eps
|
||||||
}
|
}
|
||||||
|
|
||||||
func (APIConnReverseTest) GetNodeByName(name string) (api.Node, error) {
|
func (APIConnReverseTest) GetNodeByName(name string) (*api.Node, error) {
|
||||||
return api.Node{
|
return &api.Node{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: "test.node.foo.bar",
|
Name: "test.node.foo.bar",
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
@ -11,10 +11,10 @@ import (
|
||||||
"github.com/coredns/coredns/plugin"
|
"github.com/coredns/coredns/plugin"
|
||||||
"github.com/coredns/coredns/plugin/pkg/dnsutil"
|
"github.com/coredns/coredns/plugin/pkg/dnsutil"
|
||||||
"github.com/coredns/coredns/plugin/proxy"
|
"github.com/coredns/coredns/plugin/proxy"
|
||||||
"github.com/miekg/dns"
|
|
||||||
|
|
||||||
"github.com/mholt/caddy"
|
"github.com/mholt/caddy"
|
||||||
unversionedapi "k8s.io/client-go/1.5/pkg/api/unversioned"
|
"github.com/miekg/dns"
|
||||||
|
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -150,7 +150,7 @@ func kubernetesParse(c *caddy.Controller) (*Kubernetes, dnsControlOpts, error) {
|
||||||
args := c.RemainingArgs()
|
args := c.RemainingArgs()
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
labelSelectorString := strings.Join(args, " ")
|
labelSelectorString := strings.Join(args, " ")
|
||||||
ls, err := unversionedapi.ParseToLabelSelector(labelSelectorString)
|
ls, err := meta.ParseToLabelSelector(labelSelectorString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, opts, fmt.Errorf("unable to parse label selector value: '%v': %v", labelSelectorString, err)
|
return nil, opts, fmt.Errorf("unable to parse label selector value: '%v': %v", labelSelectorString, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mholt/caddy"
|
"github.com/mholt/caddy"
|
||||||
"k8s.io/client-go/1.5/pkg/api/unversioned"
|
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestKubernetesParse(t *testing.T) {
|
func TestKubernetesParse(t *testing.T) {
|
||||||
|
@ -431,7 +431,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
|
|
||||||
// Labels
|
// Labels
|
||||||
if opts.labelSelector != nil {
|
if opts.labelSelector != nil {
|
||||||
foundLabelSelectorString := unversioned.FormatLabelSelector(opts.labelSelector)
|
foundLabelSelectorString := meta.FormatLabelSelector(opts.labelSelector)
|
||||||
if foundLabelSelectorString != test.expectedLabelSelector {
|
if foundLabelSelectorString != test.expectedLabelSelector {
|
||||||
t.Errorf("Test %d: Expected kubernetes controller to be initialized with label selector '%s'. Instead found selector '%s' for input '%s'", i, test.expectedLabelSelector, foundLabelSelectorString, test.input)
|
t.Errorf("Test %d: Expected kubernetes controller to be initialized with label selector '%s'. Instead found selector '%s' for input '%s'", i, test.expectedLabelSelector, foundLabelSelectorString, test.input)
|
||||||
}
|
}
|
||||||
|
|
16
vendor/cloud.google.com/go/.travis.yml
generated
vendored
16
vendor/cloud.google.com/go/.travis.yml
generated
vendored
|
@ -1,16 +0,0 @@
|
||||||
sudo: false
|
|
||||||
language: go
|
|
||||||
go:
|
|
||||||
- 1.6
|
|
||||||
- 1.7
|
|
||||||
- 1.8
|
|
||||||
install:
|
|
||||||
- go get -v cloud.google.com/go/...
|
|
||||||
script:
|
|
||||||
- openssl aes-256-cbc -K $encrypted_a8b3f4fc85f4_key -iv $encrypted_a8b3f4fc85f4_iv -in key.json.enc -out key.json -d
|
|
||||||
- GCLOUD_TESTS_GOLANG_PROJECT_ID="dulcet-port-762" GCLOUD_TESTS_GOLANG_KEY="$(pwd)/key.json"
|
|
||||||
./run-tests.sh $TRAVIS_COMMIT
|
|
||||||
env:
|
|
||||||
matrix:
|
|
||||||
# The GCLOUD_TESTS_API_KEY environment variable.
|
|
||||||
secure: VdldogUOoubQ60LhuHJ+g/aJoBiujkSkWEWl79Zb8cvQorcQbxISS+JsOOp4QkUOU4WwaHAm8/3pIH1QMWOR6O78DaLmDKi5Q4RpkVdCpUXy+OAfQaZIcBsispMrjxLXnqFjo9ELnrArfjoeCTzaX0QTCfwQwVmigC8rR30JBKI=
|
|
15
vendor/cloud.google.com/go/AUTHORS
generated
vendored
15
vendor/cloud.google.com/go/AUTHORS
generated
vendored
|
@ -1,15 +0,0 @@
|
||||||
# This is the official list of cloud authors for copyright purposes.
|
|
||||||
# This file is distinct from the CONTRIBUTORS files.
|
|
||||||
# See the latter for an explanation.
|
|
||||||
|
|
||||||
# Names should be added to this file as:
|
|
||||||
# Name or Organization <email address>
|
|
||||||
# The email address is not required for organizations.
|
|
||||||
|
|
||||||
Filippo Valsorda <hi@filippo.io>
|
|
||||||
Google Inc.
|
|
||||||
Ingo Oeser <nightlyone@googlemail.com>
|
|
||||||
Palm Stone Games, Inc.
|
|
||||||
Paweł Knap <pawelknap88@gmail.com>
|
|
||||||
Péter Szilágyi <peterke@gmail.com>
|
|
||||||
Tyler Treat <ttreat31@gmail.com>
|
|
138
vendor/cloud.google.com/go/CONTRIBUTING.md
generated
vendored
138
vendor/cloud.google.com/go/CONTRIBUTING.md
generated
vendored
|
@ -1,138 +0,0 @@
|
||||||
# Contributing
|
|
||||||
|
|
||||||
1. Sign one of the contributor license agreements below.
|
|
||||||
1. `go get golang.org/x/review/git-codereview` to install the code reviewing tool.
|
|
||||||
1. You will need to ensure that your `GOBIN` directory (by default
|
|
||||||
`$GOPATH/bin`) is in your `PATH` so that git can find the command.
|
|
||||||
1. If you would like, you may want to set up aliases for git-codereview,
|
|
||||||
such that `git codereview change` becomes `git change`. See the
|
|
||||||
[godoc](https://godoc.org/golang.org/x/review/git-codereview) for details.
|
|
||||||
1. Should you run into issues with the git-codereview tool, please note
|
|
||||||
that all error messages will assume that you have set up these
|
|
||||||
aliases.
|
|
||||||
1. Get the cloud package by running `go get -d cloud.google.com/go`.
|
|
||||||
1. If you have already checked out the source, make sure that the remote git
|
|
||||||
origin is https://code.googlesource.com/gocloud:
|
|
||||||
|
|
||||||
git remote set-url origin https://code.googlesource.com/gocloud
|
|
||||||
1. Make sure your auth is configured correctly by visiting
|
|
||||||
https://code.googlesource.com, clicking "Generate Password", and following
|
|
||||||
the directions.
|
|
||||||
1. Make changes and create a change by running `git codereview change <name>`,
|
|
||||||
provide a commit message, and use `git codereview mail` to create a Gerrit CL.
|
|
||||||
1. Keep amending to the change with `git codereview change` and mail as your receive
|
|
||||||
feedback. Each new mailed amendment will create a new patch set for your change in Gerrit.
|
|
||||||
|
|
||||||
## Integration Tests
|
|
||||||
|
|
||||||
In addition to the unit tests, you may run the integration test suite.
|
|
||||||
|
|
||||||
To run the integrations tests, creating and configuration of a project in the
|
|
||||||
Google Developers Console is required.
|
|
||||||
|
|
||||||
After creating a project, you must [create a service account](https://developers.google.com/identity/protocols/OAuth2ServiceAccount#creatinganaccount).
|
|
||||||
Ensure the project-level **Owner** [IAM role](console.cloud.google.com/iam-admin/iam/project)
|
|
||||||
(or **Editor** and **Logs Configuration Writer** roles) are added to the
|
|
||||||
service account.
|
|
||||||
|
|
||||||
Once you create a project, set the following environment variables to be able to
|
|
||||||
run the against the actual APIs.
|
|
||||||
|
|
||||||
- **GCLOUD_TESTS_GOLANG_PROJECT_ID**: Developers Console project's ID (e.g. bamboo-shift-455)
|
|
||||||
- **GCLOUD_TESTS_GOLANG_KEY**: The path to the JSON key file.
|
|
||||||
- **GCLOUD_TESTS_API_KEY**: Your API key.
|
|
||||||
|
|
||||||
Install the [gcloud command-line tool][gcloudcli] to your machine and use it
|
|
||||||
to create some resources used in integration tests.
|
|
||||||
|
|
||||||
From the project's root directory:
|
|
||||||
|
|
||||||
``` sh
|
|
||||||
# Set the default project in your env.
|
|
||||||
$ gcloud config set project $GCLOUD_TESTS_GOLANG_PROJECT_ID
|
|
||||||
|
|
||||||
# Authenticate the gcloud tool with your account.
|
|
||||||
$ gcloud auth login
|
|
||||||
|
|
||||||
# Create the indexes used in the datastore integration tests.
|
|
||||||
$ gcloud preview datastore create-indexes datastore/testdata/index.yaml
|
|
||||||
|
|
||||||
# Create a Google Cloud storage bucket with the same name as your test project,
|
|
||||||
# and with the Stackdriver Logging service account as owner, for the sink
|
|
||||||
# integration tests in logging.
|
|
||||||
$ gsutil mb gs://$GCLOUD_TESTS_GOLANG_PROJECT_ID
|
|
||||||
$ gsutil acl ch -g cloud-logs@google.com:O gs://$GCLOUD_TESTS_GOLANG_PROJECT_ID
|
|
||||||
|
|
||||||
# Create a Spanner instance for the spanner integration tests.
|
|
||||||
$ gcloud beta spanner instances create go-integration-test --config regional-us-central1 --nodes 1 --description 'Instance for go client test'
|
|
||||||
# NOTE: Spanner instances are priced by the node-hour, so you may want to delete
|
|
||||||
# the instance after testing with 'gcloud beta spanner instances delete'.
|
|
||||||
```
|
|
||||||
|
|
||||||
Once you've set the environment variables, you can run the integration tests by
|
|
||||||
running:
|
|
||||||
|
|
||||||
``` sh
|
|
||||||
$ go test -v cloud.google.com/go/...
|
|
||||||
```
|
|
||||||
|
|
||||||
## Contributor License Agreements
|
|
||||||
|
|
||||||
Before we can accept your pull requests you'll need to sign a Contributor
|
|
||||||
License Agreement (CLA):
|
|
||||||
|
|
||||||
- **If you are an individual writing original source code** and **you own the
|
|
||||||
- intellectual property**, then you'll need to sign an [individual CLA][indvcla].
|
|
||||||
- **If you work for a company that wants to allow you to contribute your work**,
|
|
||||||
then you'll need to sign a [corporate CLA][corpcla].
|
|
||||||
|
|
||||||
You can sign these electronically (just scroll to the bottom). After that,
|
|
||||||
we'll be able to accept your pull requests.
|
|
||||||
|
|
||||||
## Contributor Code of Conduct
|
|
||||||
|
|
||||||
As contributors and maintainers of this project,
|
|
||||||
and in the interest of fostering an open and welcoming community,
|
|
||||||
we pledge to respect all people who contribute through reporting issues,
|
|
||||||
posting feature requests, updating documentation,
|
|
||||||
submitting pull requests or patches, and other activities.
|
|
||||||
|
|
||||||
We are committed to making participation in this project
|
|
||||||
a harassment-free experience for everyone,
|
|
||||||
regardless of level of experience, gender, gender identity and expression,
|
|
||||||
sexual orientation, disability, personal appearance,
|
|
||||||
body size, race, ethnicity, age, religion, or nationality.
|
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
|
||||||
|
|
||||||
* The use of sexualized language or imagery
|
|
||||||
* Personal attacks
|
|
||||||
* Trolling or insulting/derogatory comments
|
|
||||||
* Public or private harassment
|
|
||||||
* Publishing other's private information,
|
|
||||||
such as physical or electronic
|
|
||||||
addresses, without explicit permission
|
|
||||||
* Other unethical or unprofessional conduct.
|
|
||||||
|
|
||||||
Project maintainers have the right and responsibility to remove, edit, or reject
|
|
||||||
comments, commits, code, wiki edits, issues, and other contributions
|
|
||||||
that are not aligned to this Code of Conduct.
|
|
||||||
By adopting this Code of Conduct,
|
|
||||||
project maintainers commit themselves to fairly and consistently
|
|
||||||
applying these principles to every aspect of managing this project.
|
|
||||||
Project maintainers who do not follow or enforce the Code of Conduct
|
|
||||||
may be permanently removed from the project team.
|
|
||||||
|
|
||||||
This code of conduct applies both within project spaces and in public spaces
|
|
||||||
when an individual is representing the project or its community.
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior
|
|
||||||
may be reported by opening an issue
|
|
||||||
or contacting one or more of the project maintainers.
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0,
|
|
||||||
available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/)
|
|
||||||
|
|
||||||
[gcloudcli]: https://developers.google.com/cloud/sdk/gcloud/
|
|
||||||
[indvcla]: https://developers.google.com/open-source/cla/individual
|
|
||||||
[corpcla]: https://developers.google.com/open-source/cla/corporate
|
|
37
vendor/cloud.google.com/go/CONTRIBUTORS
generated
vendored
37
vendor/cloud.google.com/go/CONTRIBUTORS
generated
vendored
|
@ -1,37 +0,0 @@
|
||||||
# People who have agreed to one of the CLAs and can contribute patches.
|
|
||||||
# The AUTHORS file lists the copyright holders; this file
|
|
||||||
# lists people. For example, Google employees are listed here
|
|
||||||
# but not in AUTHORS, because Google holds the copyright.
|
|
||||||
#
|
|
||||||
# https://developers.google.com/open-source/cla/individual
|
|
||||||
# https://developers.google.com/open-source/cla/corporate
|
|
||||||
#
|
|
||||||
# Names should be added to this file as:
|
|
||||||
# Name <email address>
|
|
||||||
|
|
||||||
# Keep the list alphabetically sorted.
|
|
||||||
|
|
||||||
Alexis Hunt <lexer@google.com>
|
|
||||||
Andreas Litt <andreas.litt@gmail.com>
|
|
||||||
Andrew Gerrand <adg@golang.org>
|
|
||||||
Brad Fitzpatrick <bradfitz@golang.org>
|
|
||||||
Burcu Dogan <jbd@google.com>
|
|
||||||
Dave Day <djd@golang.org>
|
|
||||||
David Sansome <me@davidsansome.com>
|
|
||||||
David Symonds <dsymonds@golang.org>
|
|
||||||
Filippo Valsorda <hi@filippo.io>
|
|
||||||
Glenn Lewis <gmlewis@google.com>
|
|
||||||
Ingo Oeser <nightlyone@googlemail.com>
|
|
||||||
Johan Euphrosine <proppy@google.com>
|
|
||||||
Jonathan Amsterdam <jba@google.com>
|
|
||||||
Luna Duclos <luna.duclos@palmstonegames.com>
|
|
||||||
Magnus Hiie <magnus.hiie@gmail.com>
|
|
||||||
Michael McGreevy <mcgreevy@golang.org>
|
|
||||||
Omar Jarjur <ojarjur@google.com>
|
|
||||||
Paweł Knap <pawelknap88@gmail.com>
|
|
||||||
Péter Szilágyi <peterke@gmail.com>
|
|
||||||
Sarah Adams <shadams@google.com>
|
|
||||||
Thanatat Tamtan <acoshift@gmail.com>
|
|
||||||
Toby Burress <kurin@google.com>
|
|
||||||
Tuo Shan <shantuo@google.com>
|
|
||||||
Tyler Treat <ttreat31@gmail.com>
|
|
54
vendor/cloud.google.com/go/MIGRATION.md
generated
vendored
54
vendor/cloud.google.com/go/MIGRATION.md
generated
vendored
|
@ -1,54 +0,0 @@
|
||||||
# Code Changes
|
|
||||||
|
|
||||||
## v0.10.0
|
|
||||||
|
|
||||||
- pubsub: Replace
|
|
||||||
|
|
||||||
```
|
|
||||||
sub.ModifyPushConfig(ctx, pubsub.PushConfig{Endpoint: "https://example.com/push"})
|
|
||||||
```
|
|
||||||
|
|
||||||
with
|
|
||||||
|
|
||||||
```
|
|
||||||
sub.Update(ctx, pubsub.SubscriptionConfigToUpdate{
|
|
||||||
PushConfig: &pubsub.PushConfig{Endpoint: "https://example.com/push"},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
- trace: traceGRPCServerInterceptor will be provided from *trace.Client.
|
|
||||||
Given an initialized `*trace.Client` named `tc`, instead of
|
|
||||||
|
|
||||||
```
|
|
||||||
s := grpc.NewServer(grpc.UnaryInterceptor(trace.GRPCServerInterceptor(tc)))
|
|
||||||
```
|
|
||||||
|
|
||||||
write
|
|
||||||
|
|
||||||
```
|
|
||||||
s := grpc.NewServer(grpc.UnaryInterceptor(tc.GRPCServerInterceptor()))
|
|
||||||
```
|
|
||||||
|
|
||||||
- trace trace.GRPCClientInterceptor will also provided from *trace.Client.
|
|
||||||
Instead of
|
|
||||||
|
|
||||||
```
|
|
||||||
conn, err := grpc.Dial(srv.Addr, grpc.WithUnaryInterceptor(trace.GRPCClientInterceptor()))
|
|
||||||
```
|
|
||||||
|
|
||||||
write
|
|
||||||
|
|
||||||
```
|
|
||||||
conn, err := grpc.Dial(srv.Addr, grpc.WithUnaryInterceptor(tc.GRPCClientInterceptor()))
|
|
||||||
```
|
|
||||||
|
|
||||||
- trace: We removed the deprecated `trace.EnableGRPCTracing`. Use the gRPC
|
|
||||||
interceptor as a dial option as shown below when initializing Cloud package
|
|
||||||
clients:
|
|
||||||
|
|
||||||
```
|
|
||||||
c, err := pubsub.NewClient(ctx, "project-id", option.WithGRPCDialOption(grpc.WithUnaryInterceptor(tc.GRPCClientInterceptor())))
|
|
||||||
if err != nil {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
482
vendor/cloud.google.com/go/README.md
generated
vendored
482
vendor/cloud.google.com/go/README.md
generated
vendored
|
@ -1,482 +0,0 @@
|
||||||
# Google Cloud Client Libraries for Go
|
|
||||||
|
|
||||||
[](https://godoc.org/cloud.google.com/go)
|
|
||||||
|
|
||||||
Go packages for [Google Cloud Platform](https://cloud.google.com) services.
|
|
||||||
|
|
||||||
``` go
|
|
||||||
import "cloud.google.com/go"
|
|
||||||
```
|
|
||||||
|
|
||||||
To install the packages on your system,
|
|
||||||
|
|
||||||
```
|
|
||||||
$ go get -u cloud.google.com/go/...
|
|
||||||
```
|
|
||||||
|
|
||||||
**NOTE:** Some of these packages are under development, and may occasionally
|
|
||||||
make backwards-incompatible changes.
|
|
||||||
|
|
||||||
**NOTE:** Github repo is a mirror of [https://code.googlesource.com/gocloud](https://code.googlesource.com/gocloud).
|
|
||||||
|
|
||||||
* [News](#news)
|
|
||||||
* [Supported APIs](#supported-apis)
|
|
||||||
* [Go Versions Supported](#go-versions-supported)
|
|
||||||
* [Authorization](#authorization)
|
|
||||||
* [Cloud Datastore](#cloud-datastore-)
|
|
||||||
* [Cloud Storage](#cloud-storage-)
|
|
||||||
* [Cloud Pub/Sub](#cloud-pub-sub-)
|
|
||||||
* [Cloud BigQuery](#cloud-bigquery-)
|
|
||||||
* [Stackdriver Logging](#stackdriver-logging-)
|
|
||||||
* [Cloud Spanner](#cloud-spanner-)
|
|
||||||
|
|
||||||
|
|
||||||
## News
|
|
||||||
|
|
||||||
_August 22, 2017+
|
|
||||||
|
|
||||||
*v0.12.0*
|
|
||||||
|
|
||||||
- pubsub: Subscription.Receive now uses streaming pull.
|
|
||||||
|
|
||||||
- pubsub: add Client.TopicInProject to access topics in a different project
|
|
||||||
than the client.
|
|
||||||
|
|
||||||
- errors: renamed errorreporting. The errors package will be removed shortly.
|
|
||||||
|
|
||||||
- datastore: improved retry behavior.
|
|
||||||
|
|
||||||
- bigquery: support updates to dataset metadata, with etags.
|
|
||||||
|
|
||||||
- bigquery: add etag support to Table.Update (BREAKING: etag argument added).
|
|
||||||
|
|
||||||
- bigquery: generate all job IDs on the client.
|
|
||||||
|
|
||||||
- storage: support bucket lifecycle configurations.
|
|
||||||
|
|
||||||
|
|
||||||
_July 31, 2017_
|
|
||||||
|
|
||||||
*v0.11.0*
|
|
||||||
|
|
||||||
- Clients for spanner, pubsub and video are now in beta.
|
|
||||||
|
|
||||||
- New client for DLP.
|
|
||||||
|
|
||||||
- spanner: performance and testing improvements.
|
|
||||||
|
|
||||||
- storage: requester-pays buckets are supported.
|
|
||||||
|
|
||||||
- storage, profiler, bigtable, bigquery: bug fixes and other minor improvements.
|
|
||||||
|
|
||||||
- pubsub: bug fixes and other minor improvements
|
|
||||||
|
|
||||||
_June 17, 2017_
|
|
||||||
|
|
||||||
|
|
||||||
*v0.10.0*
|
|
||||||
|
|
||||||
- pubsub: Subscription.ModifyPushConfig replaced with Subscription.Update.
|
|
||||||
|
|
||||||
- pubsub: Subscription.Receive now runs concurrently for higher throughput.
|
|
||||||
|
|
||||||
- vision: cloud.google.com/go/vision is deprecated. Use
|
|
||||||
cloud.google.com/go/vision/apiv1 instead.
|
|
||||||
|
|
||||||
- translation: now stable.
|
|
||||||
|
|
||||||
- trace: several changes to the surface. See the link below.
|
|
||||||
|
|
||||||
[Code changes required from v0.9.0.](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/master/MIGRATION.md)
|
|
||||||
|
|
||||||
|
|
||||||
[Older news](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/master/old-news.md)
|
|
||||||
|
|
||||||
## Supported APIs
|
|
||||||
|
|
||||||
Google API | Status | Package
|
|
||||||
---------------------------------|--------------|-----------------------------------------------------------
|
|
||||||
[Datastore][cloud-datastore] | stable | [`cloud.google.com/go/datastore`][cloud-datastore-ref]
|
|
||||||
[Storage][cloud-storage] | stable | [`cloud.google.com/go/storage`][cloud-storage-ref]
|
|
||||||
[Bigtable][cloud-bigtable] | beta | [`cloud.google.com/go/bigtable`][cloud-bigtable-ref]
|
|
||||||
[BigQuery][cloud-bigquery] | beta | [`cloud.google.com/go/bigquery`][cloud-bigquery-ref]
|
|
||||||
[Logging][cloud-logging] | stable | [`cloud.google.com/go/logging`][cloud-logging-ref]
|
|
||||||
[Monitoring][cloud-monitoring] | alpha | [`cloud.google.com/go/monitoring/apiv3`][cloud-monitoring-ref]
|
|
||||||
[Pub/Sub][cloud-pubsub] | beta | [`cloud.google.com/go/pubsub`][cloud-pubsub-ref]
|
|
||||||
[Vision][cloud-vision] | beta | [`cloud.google.com/go/vision/apiv1`][cloud-vision-ref]
|
|
||||||
[Language][cloud-language] | beta | [`cloud.google.com/go/language/apiv1`][cloud-language-ref]
|
|
||||||
[Speech][cloud-speech] | beta | [`cloud.google.com/go/speech/apiv1`][cloud-speech-ref]
|
|
||||||
[Spanner][cloud-spanner] | beta | [`cloud.google.com/go/spanner`][cloud-spanner-ref]
|
|
||||||
[Translation][cloud-translation] | stable | [`cloud.google.com/go/translate`][cloud-translation-ref]
|
|
||||||
[Trace][cloud-trace] | alpha | [`cloud.google.com/go/trace`][cloud-trace-ref]
|
|
||||||
[Video Intelligence][cloud-video]| beta | [`cloud.google.com/go/videointelligence/apiv1beta1`][cloud-video-ref]
|
|
||||||
[ErrorReporting][cloud-errors] | alpha | [`cloud.google.com/go/errorreporting`][cloud-errors-ref]
|
|
||||||
|
|
||||||
|
|
||||||
> **Alpha status**: the API is still being actively developed. As a
|
|
||||||
> result, it might change in backward-incompatible ways and is not recommended
|
|
||||||
> for production use.
|
|
||||||
>
|
|
||||||
> **Beta status**: the API is largely complete, but still has outstanding
|
|
||||||
> features and bugs to be addressed. There may be minor backwards-incompatible
|
|
||||||
> changes where necessary.
|
|
||||||
>
|
|
||||||
> **Stable status**: the API is mature and ready for production use. We will
|
|
||||||
> continue addressing bugs and feature requests.
|
|
||||||
|
|
||||||
Documentation and examples are available at
|
|
||||||
https://godoc.org/cloud.google.com/go
|
|
||||||
|
|
||||||
Visit or join the
|
|
||||||
[google-api-go-announce group](https://groups.google.com/forum/#!forum/google-api-go-announce)
|
|
||||||
for updates on these packages.
|
|
||||||
|
|
||||||
## Go Versions Supported
|
|
||||||
|
|
||||||
We support the two most recent major versions of Go. If Google App Engine uses
|
|
||||||
an older version, we support that as well. You can see which versions are
|
|
||||||
currently supported by looking at the lines following `go:` in
|
|
||||||
[`.travis.yml`](.travis.yml).
|
|
||||||
|
|
||||||
## Authorization
|
|
||||||
|
|
||||||
By default, each API will use [Google Application Default Credentials][default-creds]
|
|
||||||
for authorization credentials used in calling the API endpoints. This will allow your
|
|
||||||
application to run in many environments without requiring explicit configuration.
|
|
||||||
|
|
||||||
[snip]:# (auth)
|
|
||||||
```go
|
|
||||||
client, err := storage.NewClient(ctx)
|
|
||||||
```
|
|
||||||
|
|
||||||
To authorize using a
|
|
||||||
[JSON key file](https://cloud.google.com/iam/docs/managing-service-account-keys),
|
|
||||||
pass
|
|
||||||
[`option.WithServiceAccountFile`](https://godoc.org/google.golang.org/api/option#WithServiceAccountFile)
|
|
||||||
to the `NewClient` function of the desired package. For example:
|
|
||||||
|
|
||||||
[snip]:# (auth-JSON)
|
|
||||||
```go
|
|
||||||
client, err := storage.NewClient(ctx, option.WithServiceAccountFile("path/to/keyfile.json"))
|
|
||||||
```
|
|
||||||
|
|
||||||
You can exert more control over authorization by using the
|
|
||||||
[`golang.org/x/oauth2`](https://godoc.org/golang.org/x/oauth2) package to
|
|
||||||
create an `oauth2.TokenSource`. Then pass
|
|
||||||
[`option.WithTokenSource`](https://godoc.org/google.golang.org/api/option#WithTokenSource)
|
|
||||||
to the `NewClient` function:
|
|
||||||
[snip]:# (auth-ts)
|
|
||||||
```go
|
|
||||||
tokenSource := ...
|
|
||||||
client, err := storage.NewClient(ctx, option.WithTokenSource(tokenSource))
|
|
||||||
```
|
|
||||||
|
|
||||||
## Cloud Datastore [](https://godoc.org/cloud.google.com/go/datastore)
|
|
||||||
|
|
||||||
- [About Cloud Datastore][cloud-datastore]
|
|
||||||
- [Activating the API for your project][cloud-datastore-activation]
|
|
||||||
- [API documentation][cloud-datastore-docs]
|
|
||||||
- [Go client documentation](https://godoc.org/cloud.google.com/go/datastore)
|
|
||||||
- [Complete sample program](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/datastore/tasks)
|
|
||||||
|
|
||||||
### Example Usage
|
|
||||||
|
|
||||||
First create a `datastore.Client` to use throughout your application:
|
|
||||||
|
|
||||||
[snip]:# (datastore-1)
|
|
||||||
```go
|
|
||||||
client, err := datastore.NewClient(ctx, "my-project-id")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Then use that client to interact with the API:
|
|
||||||
|
|
||||||
[snip]:# (datastore-2)
|
|
||||||
```go
|
|
||||||
type Post struct {
|
|
||||||
Title string
|
|
||||||
Body string `datastore:",noindex"`
|
|
||||||
PublishedAt time.Time
|
|
||||||
}
|
|
||||||
keys := []*datastore.Key{
|
|
||||||
datastore.NameKey("Post", "post1", nil),
|
|
||||||
datastore.NameKey("Post", "post2", nil),
|
|
||||||
}
|
|
||||||
posts := []*Post{
|
|
||||||
{Title: "Post 1", Body: "...", PublishedAt: time.Now()},
|
|
||||||
{Title: "Post 2", Body: "...", PublishedAt: time.Now()},
|
|
||||||
}
|
|
||||||
if _, err := client.PutMulti(ctx, keys, posts); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Cloud Storage [](https://godoc.org/cloud.google.com/go/storage)
|
|
||||||
|
|
||||||
- [About Cloud Storage][cloud-storage]
|
|
||||||
- [API documentation][cloud-storage-docs]
|
|
||||||
- [Go client documentation](https://godoc.org/cloud.google.com/go/storage)
|
|
||||||
- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/storage)
|
|
||||||
|
|
||||||
### Example Usage
|
|
||||||
|
|
||||||
First create a `storage.Client` to use throughout your application:
|
|
||||||
|
|
||||||
[snip]:# (storage-1)
|
|
||||||
```go
|
|
||||||
client, err := storage.NewClient(ctx)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[snip]:# (storage-2)
|
|
||||||
```go
|
|
||||||
// Read the object1 from bucket.
|
|
||||||
rc, err := client.Bucket("bucket").Object("object1").NewReader(ctx)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer rc.Close()
|
|
||||||
body, err := ioutil.ReadAll(rc)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Cloud Pub/Sub [](https://godoc.org/cloud.google.com/go/pubsub)
|
|
||||||
|
|
||||||
- [About Cloud Pubsub][cloud-pubsub]
|
|
||||||
- [API documentation][cloud-pubsub-docs]
|
|
||||||
- [Go client documentation](https://godoc.org/cloud.google.com/go/pubsub)
|
|
||||||
- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/pubsub)
|
|
||||||
|
|
||||||
### Example Usage
|
|
||||||
|
|
||||||
First create a `pubsub.Client` to use throughout your application:
|
|
||||||
|
|
||||||
[snip]:# (pubsub-1)
|
|
||||||
```go
|
|
||||||
client, err := pubsub.NewClient(ctx, "project-id")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Then use the client to publish and subscribe:
|
|
||||||
|
|
||||||
[snip]:# (pubsub-2)
|
|
||||||
```go
|
|
||||||
// Publish "hello world" on topic1.
|
|
||||||
topic := client.Topic("topic1")
|
|
||||||
res := topic.Publish(ctx, &pubsub.Message{
|
|
||||||
Data: []byte("hello world"),
|
|
||||||
})
|
|
||||||
// The publish happens asynchronously.
|
|
||||||
// Later, you can get the result from res:
|
|
||||||
...
|
|
||||||
msgID, err := res.Get(ctx)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use a callback to receive messages via subscription1.
|
|
||||||
sub := client.Subscription("subscription1")
|
|
||||||
err = sub.Receive(ctx, func(ctx context.Context, m *pubsub.Message) {
|
|
||||||
fmt.Println(m.Data)
|
|
||||||
m.Ack() // Acknowledge that we've consumed the message.
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Cloud BigQuery [](https://godoc.org/cloud.google.com/go/bigquery)
|
|
||||||
|
|
||||||
- [About Cloud BigQuery][cloud-bigquery]
|
|
||||||
- [API documentation][cloud-bigquery-docs]
|
|
||||||
- [Go client documentation][cloud-bigquery-ref]
|
|
||||||
- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/bigquery)
|
|
||||||
|
|
||||||
### Example Usage
|
|
||||||
|
|
||||||
First create a `bigquery.Client` to use throughout your application:
|
|
||||||
[snip]:# (bq-1)
|
|
||||||
```go
|
|
||||||
c, err := bigquery.NewClient(ctx, "my-project-ID")
|
|
||||||
if err != nil {
|
|
||||||
// TODO: Handle error.
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Then use that client to interact with the API:
|
|
||||||
[snip]:# (bq-2)
|
|
||||||
```go
|
|
||||||
// Construct a query.
|
|
||||||
q := c.Query(`
|
|
||||||
SELECT year, SUM(number)
|
|
||||||
FROM [bigquery-public-data:usa_names.usa_1910_2013]
|
|
||||||
WHERE name = "William"
|
|
||||||
GROUP BY year
|
|
||||||
ORDER BY year
|
|
||||||
`)
|
|
||||||
// Execute the query.
|
|
||||||
it, err := q.Read(ctx)
|
|
||||||
if err != nil {
|
|
||||||
// TODO: Handle error.
|
|
||||||
}
|
|
||||||
// Iterate through the results.
|
|
||||||
for {
|
|
||||||
var values []bigquery.Value
|
|
||||||
err := it.Next(&values)
|
|
||||||
if err == iterator.Done {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
// TODO: Handle error.
|
|
||||||
}
|
|
||||||
fmt.Println(values)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Stackdriver Logging [](https://godoc.org/cloud.google.com/go/logging)
|
|
||||||
|
|
||||||
- [About Stackdriver Logging][cloud-logging]
|
|
||||||
- [API documentation][cloud-logging-docs]
|
|
||||||
- [Go client documentation][cloud-logging-ref]
|
|
||||||
- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/logging)
|
|
||||||
|
|
||||||
### Example Usage
|
|
||||||
|
|
||||||
First create a `logging.Client` to use throughout your application:
|
|
||||||
[snip]:# (logging-1)
|
|
||||||
```go
|
|
||||||
ctx := context.Background()
|
|
||||||
client, err := logging.NewClient(ctx, "my-project")
|
|
||||||
if err != nil {
|
|
||||||
// TODO: Handle error.
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Usually, you'll want to add log entries to a buffer to be periodically flushed
|
|
||||||
(automatically and asynchronously) to the Stackdriver Logging service.
|
|
||||||
[snip]:# (logging-2)
|
|
||||||
```go
|
|
||||||
logger := client.Logger("my-log")
|
|
||||||
logger.Log(logging.Entry{Payload: "something happened!"})
|
|
||||||
```
|
|
||||||
|
|
||||||
Close your client before your program exits, to flush any buffered log entries.
|
|
||||||
[snip]:# (logging-3)
|
|
||||||
```go
|
|
||||||
err = client.Close()
|
|
||||||
if err != nil {
|
|
||||||
// TODO: Handle error.
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Cloud Spanner [](https://godoc.org/cloud.google.com/go/spanner)
|
|
||||||
|
|
||||||
- [About Cloud Spanner][cloud-spanner]
|
|
||||||
- [API documentation][cloud-spanner-docs]
|
|
||||||
- [Go client documentation](https://godoc.org/cloud.google.com/go/spanner)
|
|
||||||
|
|
||||||
### Example Usage
|
|
||||||
|
|
||||||
First create a `spanner.Client` to use throughout your application:
|
|
||||||
|
|
||||||
[snip]:# (spanner-1)
|
|
||||||
```go
|
|
||||||
client, err := spanner.NewClient(ctx, "projects/P/instances/I/databases/D")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[snip]:# (spanner-2)
|
|
||||||
```go
|
|
||||||
// Simple Reads And Writes
|
|
||||||
_, err = client.Apply(ctx, []*spanner.Mutation{
|
|
||||||
spanner.Insert("Users",
|
|
||||||
[]string{"name", "email"},
|
|
||||||
[]interface{}{"alice", "a@example.com"})})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
row, err := client.Single().ReadRow(ctx, "Users",
|
|
||||||
spanner.Key{"alice"}, []string{"email"})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Contributions are welcome. Please, see the
|
|
||||||
[CONTRIBUTING](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/master/CONTRIBUTING.md)
|
|
||||||
document for details. We're using Gerrit for our code reviews. Please don't open pull
|
|
||||||
requests against this repo, new pull requests will be automatically closed.
|
|
||||||
|
|
||||||
Please note that this project is released with a Contributor Code of Conduct.
|
|
||||||
By participating in this project you agree to abide by its terms.
|
|
||||||
See [Contributor Code of Conduct](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/master/CONTRIBUTING.md#contributor-code-of-conduct)
|
|
||||||
for more information.
|
|
||||||
|
|
||||||
[cloud-datastore]: https://cloud.google.com/datastore/
|
|
||||||
[cloud-datastore-ref]: https://godoc.org/cloud.google.com/go/datastore
|
|
||||||
[cloud-datastore-docs]: https://cloud.google.com/datastore/docs
|
|
||||||
[cloud-datastore-activation]: https://cloud.google.com/datastore/docs/activate
|
|
||||||
|
|
||||||
[cloud-pubsub]: https://cloud.google.com/pubsub/
|
|
||||||
[cloud-pubsub-ref]: https://godoc.org/cloud.google.com/go/pubsub
|
|
||||||
[cloud-pubsub-docs]: https://cloud.google.com/pubsub/docs
|
|
||||||
|
|
||||||
[cloud-storage]: https://cloud.google.com/storage/
|
|
||||||
[cloud-storage-ref]: https://godoc.org/cloud.google.com/go/storage
|
|
||||||
[cloud-storage-docs]: https://cloud.google.com/storage/docs
|
|
||||||
[cloud-storage-create-bucket]: https://cloud.google.com/storage/docs/cloud-console#_creatingbuckets
|
|
||||||
|
|
||||||
[cloud-bigtable]: https://cloud.google.com/bigtable/
|
|
||||||
[cloud-bigtable-ref]: https://godoc.org/cloud.google.com/go/bigtable
|
|
||||||
|
|
||||||
[cloud-bigquery]: https://cloud.google.com/bigquery/
|
|
||||||
[cloud-bigquery-docs]: https://cloud.google.com/bigquery/docs
|
|
||||||
[cloud-bigquery-ref]: https://godoc.org/cloud.google.com/go/bigquery
|
|
||||||
|
|
||||||
[cloud-logging]: https://cloud.google.com/logging/
|
|
||||||
[cloud-logging-docs]: https://cloud.google.com/logging/docs
|
|
||||||
[cloud-logging-ref]: https://godoc.org/cloud.google.com/go/logging
|
|
||||||
|
|
||||||
[cloud-monitoring]: https://cloud.google.com/monitoring/
|
|
||||||
[cloud-monitoring-ref]: https://godoc.org/cloud.google.com/go/monitoring/apiv3
|
|
||||||
|
|
||||||
[cloud-vision]: https://cloud.google.com/vision
|
|
||||||
[cloud-vision-ref]: https://godoc.org/cloud.google.com/go/vision/apiv1
|
|
||||||
|
|
||||||
[cloud-language]: https://cloud.google.com/natural-language
|
|
||||||
[cloud-language-ref]: https://godoc.org/cloud.google.com/go/language/apiv1
|
|
||||||
|
|
||||||
[cloud-speech]: https://cloud.google.com/speech
|
|
||||||
[cloud-speech-ref]: https://godoc.org/cloud.google.com/go/speech/apiv1
|
|
||||||
|
|
||||||
[cloud-spanner]: https://cloud.google.com/spanner/
|
|
||||||
[cloud-spanner-ref]: https://godoc.org/cloud.google.com/go/spanner
|
|
||||||
[cloud-spanner-docs]: https://cloud.google.com/spanner/docs
|
|
||||||
|
|
||||||
[cloud-translation]: https://cloud.google.com/translation
|
|
||||||
[cloud-translation-ref]: https://godoc.org/cloud.google.com/go/translation
|
|
||||||
|
|
||||||
[cloud-trace]: https://cloud.google.com/trace/
|
|
||||||
[cloud-trace-ref]: https://godoc.org/cloud.google.com/go/trace
|
|
||||||
|
|
||||||
[cloud-video]: https://cloud.google.com/video-intelligence/
|
|
||||||
[cloud-video-ref]: https://godoc.org/cloud.google.com/go/videointelligence/apiv1beta1
|
|
||||||
|
|
||||||
[cloud-errors]: https://cloud.google.com/error-reporting/
|
|
||||||
[cloud-errors-ref]: https://godoc.org/cloud.google.com/go/errorreporting
|
|
||||||
|
|
||||||
[default-creds]: https://developers.google.com/identity/protocols/application-default-credentials
|
|
32
vendor/cloud.google.com/go/appveyor.yml
generated
vendored
32
vendor/cloud.google.com/go/appveyor.yml
generated
vendored
|
@ -1,32 +0,0 @@
|
||||||
# This file configures AppVeyor (http://www.appveyor.com),
|
|
||||||
# a Windows-based CI service similar to Travis.
|
|
||||||
|
|
||||||
# Identifier for this run
|
|
||||||
version: "{build}"
|
|
||||||
|
|
||||||
# Clone the repo into this path, which conforms to the standard
|
|
||||||
# Go workspace structure.
|
|
||||||
clone_folder: c:\gopath\src\cloud.google.com\go
|
|
||||||
|
|
||||||
environment:
|
|
||||||
GOPATH: c:\gopath
|
|
||||||
GCLOUD_TESTS_GOLANG_PROJECT_ID: dulcet-port-762
|
|
||||||
GCLOUD_TESTS_GOLANG_KEY: c:\gopath\src\cloud.google.com\go\key.json
|
|
||||||
KEYFILE_CONTENTS:
|
|
||||||
secure: IvRbDAhM2PIQqzVkjzJ4FjizUvoQ+c3vG/qhJQG+HlZ/L5KEkqLu+x6WjLrExrNMyGku4znB2jmbTrUW3Ob4sGG+R5vvqeQ3YMHCVIkw5CxY+/bUDkW5RZWsVbuCnNa/vKsWmCP+/sZW6ICe29yKJ2ZOb6QaauI4s9R6j+cqBbU9pumMGYFRb0Rw3uUU7DKmVFCy+NjTENZIlDP9rmjANgAzigowJJEb2Tg9sLlQKmQeKiBSRN8lKc5Nq60a+fIzHGKvql4eIitDDDpOpyHv15/Xr1BzFw2yDoiR4X1lng0u7q0X9RgX4VIYa6gT16NXBEmQgbuX8gh7SfPMp9RhiZD9sVUaV+yogEabYpyPnmUURo0hXwkctKaBkQlEmKvjHwF5dvbg8+yqGhwtjAgFNimXG3INrwQsfQsZskkQWanutbJf9xy50GyWWFZZdi0uT4oXP/b5P7aklPXKXsvrJKBh7RjEaqBrhi86IJwOjBspvoR4l2WmcQyxb2xzQS1pjbBJFQfYJJ8+JgsstTL8PBO9d4ybJC0li1Om1qnWxkaewvPxxuoHJ9LpRKof19yRYWBmhTXb2tTASKG/zslvl4fgG4DmQBS93WC7dsiGOhAraGw2eCTgd0lYZOhk1FjWl9TS80aktXxzH/7nTvem5ohm+eDl6O0wnTL4KXjQVNSQ1PyLn4lGRJ5MNGzBTRFWIr2API2rca4Fysyfh/UdmazPGlNbY9JPGqb9+F04QzLfqm+Zz/cHy59E7lOSMBlUI4KD6d6ZNNKNRH+/g9i+fSiyiXKugTfda8KBnWGyPwprxuWGYaiQUGUYOwJY5R6x5c4mjImAB310V+Wo33UbWFJiwxEDsiCNqW1meVkBzt2er26vh4qbgCUIQ3iM3gFPfHgy+QxkmIhic7Q1HYacQElt8AAP41M7cCKWCuZidegP37MBB//mjjiNt047ZSQEvB4tqsX/OvfbByVef+cbtVw9T0yjHvmCdPW1XrhyrCCgclu6oYYdbmc5D7BBDRbjjMWGv6YvceAbfGf6ukdB5PuV+TGEN/FoQ1QTRA6Aqf+3fLMg4mS4oyTfw5xyYNbv3qoyLPrp+BnxI53WB9p0hfMg4n9FD6NntBxjDq+Q3Lk/bjC/Y4MaRWdzbMzF9a0lgGfcw9DURlK5p7uGJC9vg34feNoQprxVEZRQ01cHLeob6eGkYm4HxSRx8JY39Mh+9wzJo+k/aIvFleNC3e35NOrkXr6wb5e42n2DwBdPqdNolTLtLFRglAL1LTpp27UjvjieWJAKfoDTR5CKl01sZqt0wPdLLcvsMj6CiPFmccUIOYeZMe86kLBD61Qa5F1EwkgO3Om2qSjW96FzL4skRc+BmU5RrHlAFSldR1wpUgtkUMv9vH5Cy+UJdcvpZ8KbmhZ2PsjF7ddJ1ve9RAw3cP325AyIMwZ77Ef1mgTM0NJze6eSW1qKlEsgt1FADPyeUu1NQTA2H2dueMPGlArWTSUgyWR9AdfpqouT7eg0JWI5w+yUZZC+/rPglYbt84oLmYpwuli0z8FyEQRPIc3EtkfWIv/yYgDr2TZ0N2KvGfpi/MAUWgxI1gleC2uKgEOEtuJthd3XZjF2NoE7IBqjQOINybcJOjyeB5vRLDY1FLuxYzdg1y1etkV4XQig/vje
|
|
||||||
|
|
||||||
install:
|
|
||||||
# Info for debugging.
|
|
||||||
- echo %PATH%
|
|
||||||
- go version
|
|
||||||
- go env
|
|
||||||
- go get -v -d -t ./...
|
|
||||||
|
|
||||||
|
|
||||||
# Provide a build script, or AppVeyor will call msbuild.
|
|
||||||
build_script:
|
|
||||||
- go install -v ./...
|
|
||||||
- echo %KEYFILE_CONTENTS% > %GCLOUD_TESTS_GOLANG_KEY%
|
|
||||||
|
|
||||||
test_script:
|
|
||||||
- go test -v ./...
|
|
49
vendor/cloud.google.com/go/authexample_test.go
generated
vendored
49
vendor/cloud.google.com/go/authexample_test.go
generated
vendored
|
@ -1,49 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package cloud_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"cloud.google.com/go/datastore"
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
"google.golang.org/api/option"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Example_applicationDefaultCredentials() {
|
|
||||||
// Google Application Default Credentials is the recommended way to authorize
|
|
||||||
// and authenticate clients.
|
|
||||||
//
|
|
||||||
// See the following link on how to create and obtain Application Default Credentials:
|
|
||||||
// https://developers.google.com/identity/protocols/application-default-credentials.
|
|
||||||
client, err := datastore.NewClient(context.Background(), "project-id")
|
|
||||||
if err != nil {
|
|
||||||
// TODO: handle error.
|
|
||||||
}
|
|
||||||
_ = client // Use the client.
|
|
||||||
}
|
|
||||||
|
|
||||||
func Example_serviceAccountFile() {
|
|
||||||
// Use a JSON key file associated with a Google service account to
|
|
||||||
// authenticate and authorize. Service Account keys can be created and
|
|
||||||
// downloaded from https://console.developers.google.com/permissions/serviceaccounts.
|
|
||||||
//
|
|
||||||
// Note: This example uses the datastore client, but the same steps apply to
|
|
||||||
// the other client libraries underneath this package.
|
|
||||||
client, err := datastore.NewClient(context.Background(),
|
|
||||||
"project-id", option.WithServiceAccountFile("/path/to/service-account-key.json"))
|
|
||||||
if err != nil {
|
|
||||||
// TODO: handle error.
|
|
||||||
}
|
|
||||||
_ = client // Use the client.
|
|
||||||
}
|
|
437
vendor/cloud.google.com/go/compute/metadata/metadata.go
generated
vendored
437
vendor/cloud.google.com/go/compute/metadata/metadata.go
generated
vendored
|
@ -1,437 +0,0 @@
|
||||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// Package metadata provides access to Google Compute Engine (GCE)
|
|
||||||
// metadata and API service accounts.
|
|
||||||
//
|
|
||||||
// This package is a wrapper around the GCE metadata service,
|
|
||||||
// as documented at https://developers.google.com/compute/docs/metadata.
|
|
||||||
package metadata // import "cloud.google.com/go/compute/metadata"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
"golang.org/x/net/context/ctxhttp"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// metadataIP is the documented metadata server IP address.
|
|
||||||
metadataIP = "169.254.169.254"
|
|
||||||
|
|
||||||
// metadataHostEnv is the environment variable specifying the
|
|
||||||
// GCE metadata hostname. If empty, the default value of
|
|
||||||
// metadataIP ("169.254.169.254") is used instead.
|
|
||||||
// This is variable name is not defined by any spec, as far as
|
|
||||||
// I know; it was made up for the Go package.
|
|
||||||
metadataHostEnv = "GCE_METADATA_HOST"
|
|
||||||
|
|
||||||
userAgent = "gcloud-golang/0.1"
|
|
||||||
)
|
|
||||||
|
|
||||||
type cachedValue struct {
|
|
||||||
k string
|
|
||||||
trim bool
|
|
||||||
mu sync.Mutex
|
|
||||||
v string
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
projID = &cachedValue{k: "project/project-id", trim: true}
|
|
||||||
projNum = &cachedValue{k: "project/numeric-project-id", trim: true}
|
|
||||||
instID = &cachedValue{k: "instance/id", trim: true}
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
metaClient = &http.Client{
|
|
||||||
Transport: &http.Transport{
|
|
||||||
Dial: (&net.Dialer{
|
|
||||||
Timeout: 2 * time.Second,
|
|
||||||
KeepAlive: 30 * time.Second,
|
|
||||||
}).Dial,
|
|
||||||
ResponseHeaderTimeout: 2 * time.Second,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
subscribeClient = &http.Client{
|
|
||||||
Transport: &http.Transport{
|
|
||||||
Dial: (&net.Dialer{
|
|
||||||
Timeout: 2 * time.Second,
|
|
||||||
KeepAlive: 30 * time.Second,
|
|
||||||
}).Dial,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// NotDefinedError is returned when requested metadata is not defined.
|
|
||||||
//
|
|
||||||
// The underlying string is the suffix after "/computeMetadata/v1/".
|
|
||||||
//
|
|
||||||
// This error is not returned if the value is defined to be the empty
|
|
||||||
// string.
|
|
||||||
type NotDefinedError string
|
|
||||||
|
|
||||||
func (suffix NotDefinedError) Error() string {
|
|
||||||
return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns a value from the metadata service.
|
|
||||||
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
|
|
||||||
//
|
|
||||||
// If the GCE_METADATA_HOST environment variable is not defined, a default of
|
|
||||||
// 169.254.169.254 will be used instead.
|
|
||||||
//
|
|
||||||
// If the requested metadata is not defined, the returned error will
|
|
||||||
// be of type NotDefinedError.
|
|
||||||
func Get(suffix string) (string, error) {
|
|
||||||
val, _, err := getETag(metaClient, suffix)
|
|
||||||
return val, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// getETag returns a value from the metadata service as well as the associated
|
|
||||||
// ETag using the provided client. This func is otherwise equivalent to Get.
|
|
||||||
func getETag(client *http.Client, suffix string) (value, etag string, err error) {
|
|
||||||
// Using a fixed IP makes it very difficult to spoof the metadata service in
|
|
||||||
// a container, which is an important use-case for local testing of cloud
|
|
||||||
// deployments. To enable spoofing of the metadata service, the environment
|
|
||||||
// variable GCE_METADATA_HOST is first inspected to decide where metadata
|
|
||||||
// requests shall go.
|
|
||||||
host := os.Getenv(metadataHostEnv)
|
|
||||||
if host == "" {
|
|
||||||
// Using 169.254.169.254 instead of "metadata" here because Go
|
|
||||||
// binaries built with the "netgo" tag and without cgo won't
|
|
||||||
// know the search suffix for "metadata" is
|
|
||||||
// ".google.internal", and this IP address is documented as
|
|
||||||
// being stable anyway.
|
|
||||||
host = metadataIP
|
|
||||||
}
|
|
||||||
url := "http://" + host + "/computeMetadata/v1/" + suffix
|
|
||||||
req, _ := http.NewRequest("GET", url, nil)
|
|
||||||
req.Header.Set("Metadata-Flavor", "Google")
|
|
||||||
req.Header.Set("User-Agent", userAgent)
|
|
||||||
res, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
defer res.Body.Close()
|
|
||||||
if res.StatusCode == http.StatusNotFound {
|
|
||||||
return "", "", NotDefinedError(suffix)
|
|
||||||
}
|
|
||||||
if res.StatusCode != 200 {
|
|
||||||
return "", "", fmt.Errorf("status code %d trying to fetch %s", res.StatusCode, url)
|
|
||||||
}
|
|
||||||
all, err := ioutil.ReadAll(res.Body)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
return string(all), res.Header.Get("Etag"), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTrimmed(suffix string) (s string, err error) {
|
|
||||||
s, err = Get(suffix)
|
|
||||||
s = strings.TrimSpace(s)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cachedValue) get() (v string, err error) {
|
|
||||||
defer c.mu.Unlock()
|
|
||||||
c.mu.Lock()
|
|
||||||
if c.v != "" {
|
|
||||||
return c.v, nil
|
|
||||||
}
|
|
||||||
if c.trim {
|
|
||||||
v, err = getTrimmed(c.k)
|
|
||||||
} else {
|
|
||||||
v, err = Get(c.k)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
c.v = v
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
onGCEOnce sync.Once
|
|
||||||
onGCE bool
|
|
||||||
)
|
|
||||||
|
|
||||||
// OnGCE reports whether this process is running on Google Compute Engine.
|
|
||||||
func OnGCE() bool {
|
|
||||||
onGCEOnce.Do(initOnGCE)
|
|
||||||
return onGCE
|
|
||||||
}
|
|
||||||
|
|
||||||
func initOnGCE() {
|
|
||||||
onGCE = testOnGCE()
|
|
||||||
}
|
|
||||||
|
|
||||||
func testOnGCE() bool {
|
|
||||||
// The user explicitly said they're on GCE, so trust them.
|
|
||||||
if os.Getenv(metadataHostEnv) != "" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
resc := make(chan bool, 2)
|
|
||||||
|
|
||||||
// Try two strategies in parallel.
|
|
||||||
// See https://github.com/GoogleCloudPlatform/google-cloud-go/issues/194
|
|
||||||
go func() {
|
|
||||||
req, _ := http.NewRequest("GET", "http://"+metadataIP, nil)
|
|
||||||
req.Header.Set("User-Agent", userAgent)
|
|
||||||
res, err := ctxhttp.Do(ctx, metaClient, req)
|
|
||||||
if err != nil {
|
|
||||||
resc <- false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer res.Body.Close()
|
|
||||||
resc <- res.Header.Get("Metadata-Flavor") == "Google"
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
addrs, err := net.LookupHost("metadata.google.internal")
|
|
||||||
if err != nil || len(addrs) == 0 {
|
|
||||||
resc <- false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resc <- strsContains(addrs, metadataIP)
|
|
||||||
}()
|
|
||||||
|
|
||||||
tryHarder := systemInfoSuggestsGCE()
|
|
||||||
if tryHarder {
|
|
||||||
res := <-resc
|
|
||||||
if res {
|
|
||||||
// The first strategy succeeded, so let's use it.
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// Wait for either the DNS or metadata server probe to
|
|
||||||
// contradict the other one and say we are running on
|
|
||||||
// GCE. Give it a lot of time to do so, since the system
|
|
||||||
// info already suggests we're running on a GCE BIOS.
|
|
||||||
timer := time.NewTimer(5 * time.Second)
|
|
||||||
defer timer.Stop()
|
|
||||||
select {
|
|
||||||
case res = <-resc:
|
|
||||||
return res
|
|
||||||
case <-timer.C:
|
|
||||||
// Too slow. Who knows what this system is.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// There's no hint from the system info that we're running on
|
|
||||||
// GCE, so use the first probe's result as truth, whether it's
|
|
||||||
// true or false. The goal here is to optimize for speed for
|
|
||||||
// users who are NOT running on GCE. We can't assume that
|
|
||||||
// either a DNS lookup or an HTTP request to a blackholed IP
|
|
||||||
// address is fast. Worst case this should return when the
|
|
||||||
// metaClient's Transport.ResponseHeaderTimeout or
|
|
||||||
// Transport.Dial.Timeout fires (in two seconds).
|
|
||||||
return <-resc
|
|
||||||
}
|
|
||||||
|
|
||||||
// systemInfoSuggestsGCE reports whether the local system (without
|
|
||||||
// doing network requests) suggests that we're running on GCE. If this
|
|
||||||
// returns true, testOnGCE tries a bit harder to reach its metadata
|
|
||||||
// server.
|
|
||||||
func systemInfoSuggestsGCE() bool {
|
|
||||||
if runtime.GOOS != "linux" {
|
|
||||||
// We don't have any non-Linux clues available, at least yet.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
slurp, _ := ioutil.ReadFile("/sys/class/dmi/id/product_name")
|
|
||||||
name := strings.TrimSpace(string(slurp))
|
|
||||||
return name == "Google" || name == "Google Compute Engine"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe subscribes to a value from the metadata service.
|
|
||||||
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
|
|
||||||
// The suffix may contain query parameters.
|
|
||||||
//
|
|
||||||
// Subscribe calls fn with the latest metadata value indicated by the provided
|
|
||||||
// suffix. If the metadata value is deleted, fn is called with the empty string
|
|
||||||
// and ok false. Subscribe blocks until fn returns a non-nil error or the value
|
|
||||||
// is deleted. Subscribe returns the error value returned from the last call to
|
|
||||||
// fn, which may be nil when ok == false.
|
|
||||||
func Subscribe(suffix string, fn func(v string, ok bool) error) error {
|
|
||||||
const failedSubscribeSleep = time.Second * 5
|
|
||||||
|
|
||||||
// First check to see if the metadata value exists at all.
|
|
||||||
val, lastETag, err := getETag(subscribeClient, suffix)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := fn(val, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ok := true
|
|
||||||
if strings.ContainsRune(suffix, '?') {
|
|
||||||
suffix += "&wait_for_change=true&last_etag="
|
|
||||||
} else {
|
|
||||||
suffix += "?wait_for_change=true&last_etag="
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
val, etag, err := getETag(subscribeClient, suffix+url.QueryEscape(lastETag))
|
|
||||||
if err != nil {
|
|
||||||
if _, deleted := err.(NotDefinedError); !deleted {
|
|
||||||
time.Sleep(failedSubscribeSleep)
|
|
||||||
continue // Retry on other errors.
|
|
||||||
}
|
|
||||||
ok = false
|
|
||||||
}
|
|
||||||
lastETag = etag
|
|
||||||
|
|
||||||
if err := fn(val, ok); err != nil || !ok {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProjectID returns the current instance's project ID string.
|
|
||||||
func ProjectID() (string, error) { return projID.get() }
|
|
||||||
|
|
||||||
// NumericProjectID returns the current instance's numeric project ID.
|
|
||||||
func NumericProjectID() (string, error) { return projNum.get() }
|
|
||||||
|
|
||||||
// InternalIP returns the instance's primary internal IP address.
|
|
||||||
func InternalIP() (string, error) {
|
|
||||||
return getTrimmed("instance/network-interfaces/0/ip")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExternalIP returns the instance's primary external (public) IP address.
|
|
||||||
func ExternalIP() (string, error) {
|
|
||||||
return getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hostname returns the instance's hostname. This will be of the form
|
|
||||||
// "<instanceID>.c.<projID>.internal".
|
|
||||||
func Hostname() (string, error) {
|
|
||||||
return getTrimmed("instance/hostname")
|
|
||||||
}
|
|
||||||
|
|
||||||
// InstanceTags returns the list of user-defined instance tags,
|
|
||||||
// assigned when initially creating a GCE instance.
|
|
||||||
func InstanceTags() ([]string, error) {
|
|
||||||
var s []string
|
|
||||||
j, err := Get("instance/tags")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InstanceID returns the current VM's numeric instance ID.
|
|
||||||
func InstanceID() (string, error) {
|
|
||||||
return instID.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
// InstanceName returns the current VM's instance ID string.
|
|
||||||
func InstanceName() (string, error) {
|
|
||||||
host, err := Hostname()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return strings.Split(host, ".")[0], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zone returns the current VM's zone, such as "us-central1-b".
|
|
||||||
func Zone() (string, error) {
|
|
||||||
zone, err := getTrimmed("instance/zone")
|
|
||||||
// zone is of the form "projects/<projNum>/zones/<zoneName>".
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return zone[strings.LastIndex(zone, "/")+1:], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InstanceAttributes returns the list of user-defined attributes,
|
|
||||||
// assigned when initially creating a GCE VM instance. The value of an
|
|
||||||
// attribute can be obtained with InstanceAttributeValue.
|
|
||||||
func InstanceAttributes() ([]string, error) { return lines("instance/attributes/") }
|
|
||||||
|
|
||||||
// ProjectAttributes returns the list of user-defined attributes
|
|
||||||
// applying to the project as a whole, not just this VM. The value of
|
|
||||||
// an attribute can be obtained with ProjectAttributeValue.
|
|
||||||
func ProjectAttributes() ([]string, error) { return lines("project/attributes/") }
|
|
||||||
|
|
||||||
func lines(suffix string) ([]string, error) {
|
|
||||||
j, err := Get(suffix)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
s := strings.Split(strings.TrimSpace(j), "\n")
|
|
||||||
for i := range s {
|
|
||||||
s[i] = strings.TrimSpace(s[i])
|
|
||||||
}
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InstanceAttributeValue returns the value of the provided VM
|
|
||||||
// instance attribute.
|
|
||||||
//
|
|
||||||
// If the requested attribute is not defined, the returned error will
|
|
||||||
// be of type NotDefinedError.
|
|
||||||
//
|
|
||||||
// InstanceAttributeValue may return ("", nil) if the attribute was
|
|
||||||
// defined to be the empty string.
|
|
||||||
func InstanceAttributeValue(attr string) (string, error) {
|
|
||||||
return Get("instance/attributes/" + attr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProjectAttributeValue returns the value of the provided
|
|
||||||
// project attribute.
|
|
||||||
//
|
|
||||||
// If the requested attribute is not defined, the returned error will
|
|
||||||
// be of type NotDefinedError.
|
|
||||||
//
|
|
||||||
// ProjectAttributeValue may return ("", nil) if the attribute was
|
|
||||||
// defined to be the empty string.
|
|
||||||
func ProjectAttributeValue(attr string) (string, error) {
|
|
||||||
return Get("project/attributes/" + attr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scopes returns the service account scopes for the given account.
|
|
||||||
// The account may be empty or the string "default" to use the instance's
|
|
||||||
// main account.
|
|
||||||
func Scopes(serviceAccount string) ([]string, error) {
|
|
||||||
if serviceAccount == "" {
|
|
||||||
serviceAccount = "default"
|
|
||||||
}
|
|
||||||
return lines("instance/service-accounts/" + serviceAccount + "/scopes")
|
|
||||||
}
|
|
||||||
|
|
||||||
func strsContains(ss []string, s string) bool {
|
|
||||||
for _, v := range ss {
|
|
||||||
if v == s {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
48
vendor/cloud.google.com/go/compute/metadata/metadata_test.go
generated
vendored
48
vendor/cloud.google.com/go/compute/metadata/metadata_test.go
generated
vendored
|
@ -1,48 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package metadata
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestOnGCE_Stress(t *testing.T) {
|
|
||||||
if testing.Short() {
|
|
||||||
t.Skip("skipping in -short mode")
|
|
||||||
}
|
|
||||||
var last bool
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
onGCEOnce = sync.Once{}
|
|
||||||
|
|
||||||
now := OnGCE()
|
|
||||||
if i > 0 && now != last {
|
|
||||||
t.Errorf("%d. changed from %v to %v", i, last, now)
|
|
||||||
}
|
|
||||||
last = now
|
|
||||||
}
|
|
||||||
t.Logf("OnGCE() = %v", last)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOnGCE_Force(t *testing.T) {
|
|
||||||
onGCEOnce = sync.Once{}
|
|
||||||
old := os.Getenv(metadataHostEnv)
|
|
||||||
defer os.Setenv(metadataHostEnv, old)
|
|
||||||
os.Setenv(metadataHostEnv, "127.0.0.1")
|
|
||||||
if !OnGCE() {
|
|
||||||
t.Error("OnGCE() = false; want true")
|
|
||||||
}
|
|
||||||
}
|
|
BIN
vendor/cloud.google.com/go/key.json.enc
generated
vendored
BIN
vendor/cloud.google.com/go/key.json.enc
generated
vendored
Binary file not shown.
70
vendor/cloud.google.com/go/license_test.go
generated
vendored
70
vendor/cloud.google.com/go/license_test.go
generated
vendored
|
@ -1,70 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package cloud
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var sentinels = []string{
|
|
||||||
"Copyright",
|
|
||||||
"Google Inc",
|
|
||||||
`Licensed under the Apache License, Version 2.0 (the "License");`,
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLicense(t *testing.T) {
|
|
||||||
err := filepath.Walk(".", func(path string, fi os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ext := filepath.Ext(path); ext != ".go" && ext != ".proto" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if strings.HasSuffix(path, ".pb.go") {
|
|
||||||
// .pb.go files are generated from the proto files.
|
|
||||||
// .proto files must have license headers.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if path == "bigtable/cmd/cbt/cbtdoc.go" {
|
|
||||||
// Automatically generated.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
src, err := ioutil.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
src = src[:140] // Ensure all of the sentinel values are at the top of the file.
|
|
||||||
|
|
||||||
// Find license
|
|
||||||
for _, sentinel := range sentinels {
|
|
||||||
if !bytes.Contains(src, []byte(sentinel)) {
|
|
||||||
t.Errorf("%v: license header not present. want %q", path, sentinel)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
451
vendor/cloud.google.com/go/old-news.md
generated
vendored
451
vendor/cloud.google.com/go/old-news.md
generated
vendored
|
@ -1,451 +0,0 @@
|
||||||
_March 17, 2017_
|
|
||||||
|
|
||||||
Breaking Pubsub changes.
|
|
||||||
* Publish is now asynchronous
|
|
||||||
([announcement](https://groups.google.com/d/topic/google-api-go-announce/aaqRDIQ3rvU/discussion)).
|
|
||||||
* Subscription.Pull replaced by Subscription.Receive, which takes a callback ([announcement](https://groups.google.com/d/topic/google-api-go-announce/8pt6oetAdKc/discussion)).
|
|
||||||
* Message.Done replaced with Message.Ack and Message.Nack.
|
|
||||||
|
|
||||||
_February 14, 2017_
|
|
||||||
|
|
||||||
Release of a client library for Spanner. See
|
|
||||||
the
|
|
||||||
[blog post](https://cloudplatform.googleblog.com/2017/02/introducing-Cloud-Spanner-a-global-database-service-for-mission-critical-applications.html).
|
|
||||||
|
|
||||||
Note that although the Spanner service is beta, the Go client library is alpha.
|
|
||||||
|
|
||||||
_December 12, 2016_
|
|
||||||
|
|
||||||
Beta release of BigQuery, DataStore, Logging and Storage. See the
|
|
||||||
[blog post](https://cloudplatform.googleblog.com/2016/12/announcing-new-google-cloud-client.html).
|
|
||||||
|
|
||||||
Also, BigQuery now supports structs. Read a row directly into a struct with
|
|
||||||
`RowIterator.Next`, and upload a row directly from a struct with `Uploader.Put`.
|
|
||||||
You can also use field tags. See the [package documentation][cloud-bigquery-ref]
|
|
||||||
for details.
|
|
||||||
|
|
||||||
_December 5, 2016_
|
|
||||||
|
|
||||||
More changes to BigQuery:
|
|
||||||
|
|
||||||
* The `ValueList` type was removed. It is no longer necessary. Instead of
|
|
||||||
```go
|
|
||||||
var v ValueList
|
|
||||||
... it.Next(&v) ..
|
|
||||||
```
|
|
||||||
use
|
|
||||||
|
|
||||||
```go
|
|
||||||
var v []Value
|
|
||||||
... it.Next(&v) ...
|
|
||||||
```
|
|
||||||
|
|
||||||
* Previously, repeatedly calling `RowIterator.Next` on the same `[]Value` or
|
|
||||||
`ValueList` would append to the slice. Now each call resets the size to zero first.
|
|
||||||
|
|
||||||
* Schema inference will infer the SQL type BYTES for a struct field of
|
|
||||||
type []byte. Previously it inferred STRING.
|
|
||||||
|
|
||||||
* The types `uint`, `uint64` and `uintptr` are no longer supported in schema
|
|
||||||
inference. BigQuery's integer type is INT64, and those types may hold values
|
|
||||||
that are not correctly represented in a 64-bit signed integer.
|
|
||||||
|
|
||||||
* The SQL types DATE, TIME and DATETIME are now supported. They correspond to
|
|
||||||
the `Date`, `Time` and `DateTime` types in the new `cloud.google.com/go/civil`
|
|
||||||
package.
|
|
||||||
|
|
||||||
_November 17, 2016_
|
|
||||||
|
|
||||||
Change to BigQuery: values from INTEGER columns will now be returned as int64,
|
|
||||||
not int. This will avoid errors arising from large values on 32-bit systems.
|
|
||||||
|
|
||||||
_November 8, 2016_
|
|
||||||
|
|
||||||
New datastore feature: datastore now encodes your nested Go structs as Entity values,
|
|
||||||
instead of a flattened list of the embedded struct's fields.
|
|
||||||
This means that you may now have twice-nested slices, eg.
|
|
||||||
```go
|
|
||||||
type State struct {
|
|
||||||
Cities []struct{
|
|
||||||
Populations []int
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
See [the announcement](https://groups.google.com/forum/#!topic/google-api-go-announce/79jtrdeuJAg) for
|
|
||||||
more details.
|
|
||||||
|
|
||||||
_November 8, 2016_
|
|
||||||
|
|
||||||
Breaking changes to datastore: contexts no longer hold namespaces; instead you
|
|
||||||
must set a key's namespace explicitly. Also, key functions have been changed
|
|
||||||
and renamed.
|
|
||||||
|
|
||||||
* The WithNamespace function has been removed. To specify a namespace in a Query, use the Query.Namespace method:
|
|
||||||
```go
|
|
||||||
q := datastore.NewQuery("Kind").Namespace("ns")
|
|
||||||
```
|
|
||||||
|
|
||||||
* All the fields of Key are exported. That means you can construct any Key with a struct literal:
|
|
||||||
```go
|
|
||||||
k := &Key{Kind: "Kind", ID: 37, Namespace: "ns"}
|
|
||||||
```
|
|
||||||
|
|
||||||
* As a result of the above, the Key methods Kind, ID, d.Name, Parent, SetParent and Namespace have been removed.
|
|
||||||
|
|
||||||
* `NewIncompleteKey` has been removed, replaced by `IncompleteKey`. Replace
|
|
||||||
```go
|
|
||||||
NewIncompleteKey(ctx, kind, parent)
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```go
|
|
||||||
IncompleteKey(kind, parent)
|
|
||||||
```
|
|
||||||
and if you do use namespaces, make sure you set the namespace on the returned key.
|
|
||||||
|
|
||||||
* `NewKey` has been removed, replaced by `NameKey` and `IDKey`. Replace
|
|
||||||
```go
|
|
||||||
NewKey(ctx, kind, name, 0, parent)
|
|
||||||
NewKey(ctx, kind, "", id, parent)
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```go
|
|
||||||
NameKey(kind, name, parent)
|
|
||||||
IDKey(kind, id, parent)
|
|
||||||
```
|
|
||||||
and if you do use namespaces, make sure you set the namespace on the returned key.
|
|
||||||
|
|
||||||
* The `Done` variable has been removed. Replace `datastore.Done` with `iterator.Done`, from the package `google.golang.org/api/iterator`.
|
|
||||||
|
|
||||||
* The `Client.Close` method will have a return type of error. It will return the result of closing the underlying gRPC connection.
|
|
||||||
|
|
||||||
See [the announcement](https://groups.google.com/forum/#!topic/google-api-go-announce/hqXtM_4Ix-0) for
|
|
||||||
more details.
|
|
||||||
|
|
||||||
_October 27, 2016_
|
|
||||||
|
|
||||||
Breaking change to bigquery: `NewGCSReference` is now a function,
|
|
||||||
not a method on `Client`.
|
|
||||||
|
|
||||||
New bigquery feature: `Table.LoaderFrom` now accepts a `ReaderSource`, enabling
|
|
||||||
loading data into a table from a file or any `io.Reader`.
|
|
||||||
|
|
||||||
_October 21, 2016_
|
|
||||||
|
|
||||||
Breaking change to pubsub: removed `pubsub.Done`.
|
|
||||||
|
|
||||||
Use `iterator.Done` instead, where `iterator` is the package
|
|
||||||
`google.golang.org/api/iterator`.
|
|
||||||
|
|
||||||
_October 19, 2016_
|
|
||||||
|
|
||||||
Breaking changes to cloud.google.com/go/bigquery:
|
|
||||||
|
|
||||||
* Client.Table and Client.OpenTable have been removed.
|
|
||||||
Replace
|
|
||||||
```go
|
|
||||||
client.OpenTable("project", "dataset", "table")
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```go
|
|
||||||
client.DatasetInProject("project", "dataset").Table("table")
|
|
||||||
```
|
|
||||||
|
|
||||||
* Client.CreateTable has been removed.
|
|
||||||
Replace
|
|
||||||
```go
|
|
||||||
client.CreateTable(ctx, "project", "dataset", "table")
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```go
|
|
||||||
client.DatasetInProject("project", "dataset").Table("table").Create(ctx)
|
|
||||||
```
|
|
||||||
|
|
||||||
* Dataset.ListTables have been replaced with Dataset.Tables.
|
|
||||||
Replace
|
|
||||||
```go
|
|
||||||
tables, err := ds.ListTables(ctx)
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```go
|
|
||||||
it := ds.Tables(ctx)
|
|
||||||
for {
|
|
||||||
table, err := it.Next()
|
|
||||||
if err == iterator.Done {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
// TODO: Handle error.
|
|
||||||
}
|
|
||||||
// TODO: use table.
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
* Client.Read has been replaced with Job.Read, Table.Read and Query.Read.
|
|
||||||
Replace
|
|
||||||
```go
|
|
||||||
it, err := client.Read(ctx, job)
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```go
|
|
||||||
it, err := job.Read(ctx)
|
|
||||||
```
|
|
||||||
and similarly for reading from tables or queries.
|
|
||||||
|
|
||||||
* The iterator returned from the Read methods is now named RowIterator. Its
|
|
||||||
behavior is closer to the other iterators in these libraries. It no longer
|
|
||||||
supports the Schema method; see the next item.
|
|
||||||
Replace
|
|
||||||
```go
|
|
||||||
for it.Next(ctx) {
|
|
||||||
var vals ValueList
|
|
||||||
if err := it.Get(&vals); err != nil {
|
|
||||||
// TODO: Handle error.
|
|
||||||
}
|
|
||||||
// TODO: use vals.
|
|
||||||
}
|
|
||||||
if err := it.Err(); err != nil {
|
|
||||||
// TODO: Handle error.
|
|
||||||
}
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```
|
|
||||||
for {
|
|
||||||
var vals ValueList
|
|
||||||
err := it.Next(&vals)
|
|
||||||
if err == iterator.Done {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
// TODO: Handle error.
|
|
||||||
}
|
|
||||||
// TODO: use vals.
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Instead of the `RecordsPerRequest(n)` option, write
|
|
||||||
```go
|
|
||||||
it.PageInfo().MaxSize = n
|
|
||||||
```
|
|
||||||
Instead of the `StartIndex(i)` option, write
|
|
||||||
```go
|
|
||||||
it.StartIndex = i
|
|
||||||
```
|
|
||||||
|
|
||||||
* ValueLoader.Load now takes a Schema in addition to a slice of Values.
|
|
||||||
Replace
|
|
||||||
```go
|
|
||||||
func (vl *myValueLoader) Load(v []bigquery.Value)
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```go
|
|
||||||
func (vl *myValueLoader) Load(v []bigquery.Value, s bigquery.Schema)
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
* Table.Patch is replace by Table.Update.
|
|
||||||
Replace
|
|
||||||
```go
|
|
||||||
p := table.Patch()
|
|
||||||
p.Description("new description")
|
|
||||||
metadata, err := p.Apply(ctx)
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```go
|
|
||||||
metadata, err := table.Update(ctx, bigquery.TableMetadataToUpdate{
|
|
||||||
Description: "new description",
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
* Client.Copy is replaced by separate methods for each of its four functions.
|
|
||||||
All options have been replaced by struct fields.
|
|
||||||
|
|
||||||
* To load data from Google Cloud Storage into a table, use Table.LoaderFrom.
|
|
||||||
|
|
||||||
Replace
|
|
||||||
```go
|
|
||||||
client.Copy(ctx, table, gcsRef)
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```go
|
|
||||||
table.LoaderFrom(gcsRef).Run(ctx)
|
|
||||||
```
|
|
||||||
Instead of passing options to Copy, set fields on the Loader:
|
|
||||||
```go
|
|
||||||
loader := table.LoaderFrom(gcsRef)
|
|
||||||
loader.WriteDisposition = bigquery.WriteTruncate
|
|
||||||
```
|
|
||||||
|
|
||||||
* To extract data from a table into Google Cloud Storage, use
|
|
||||||
Table.ExtractorTo. Set fields on the returned Extractor instead of
|
|
||||||
passing options.
|
|
||||||
|
|
||||||
Replace
|
|
||||||
```go
|
|
||||||
client.Copy(ctx, gcsRef, table)
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```go
|
|
||||||
table.ExtractorTo(gcsRef).Run(ctx)
|
|
||||||
```
|
|
||||||
|
|
||||||
* To copy data into a table from one or more other tables, use
|
|
||||||
Table.CopierFrom. Set fields on the returned Copier instead of passing options.
|
|
||||||
|
|
||||||
Replace
|
|
||||||
```go
|
|
||||||
client.Copy(ctx, dstTable, srcTable)
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```go
|
|
||||||
dst.Table.CopierFrom(srcTable).Run(ctx)
|
|
||||||
```
|
|
||||||
|
|
||||||
* To start a query job, create a Query and call its Run method. Set fields
|
|
||||||
on the query instead of passing options.
|
|
||||||
|
|
||||||
Replace
|
|
||||||
```go
|
|
||||||
client.Copy(ctx, table, query)
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```go
|
|
||||||
query.Run(ctx)
|
|
||||||
```
|
|
||||||
|
|
||||||
* Table.NewUploader has been renamed to Table.Uploader. Instead of options,
|
|
||||||
configure an Uploader by setting its fields.
|
|
||||||
Replace
|
|
||||||
```go
|
|
||||||
u := table.NewUploader(bigquery.UploadIgnoreUnknownValues())
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```go
|
|
||||||
u := table.NewUploader(bigquery.UploadIgnoreUnknownValues())
|
|
||||||
u.IgnoreUnknownValues = true
|
|
||||||
```
|
|
||||||
|
|
||||||
_October 10, 2016_
|
|
||||||
|
|
||||||
Breaking changes to cloud.google.com/go/storage:
|
|
||||||
|
|
||||||
* AdminClient replaced by methods on Client.
|
|
||||||
Replace
|
|
||||||
```go
|
|
||||||
adminClient.CreateBucket(ctx, bucketName, attrs)
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```go
|
|
||||||
client.Bucket(bucketName).Create(ctx, projectID, attrs)
|
|
||||||
```
|
|
||||||
|
|
||||||
* BucketHandle.List replaced by BucketHandle.Objects.
|
|
||||||
Replace
|
|
||||||
```go
|
|
||||||
for query != nil {
|
|
||||||
objs, err := bucket.List(d.ctx, query)
|
|
||||||
if err != nil { ... }
|
|
||||||
query = objs.Next
|
|
||||||
for _, obj := range objs.Results {
|
|
||||||
fmt.Println(obj)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```go
|
|
||||||
iter := bucket.Objects(d.ctx, query)
|
|
||||||
for {
|
|
||||||
obj, err := iter.Next()
|
|
||||||
if err == iterator.Done {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil { ... }
|
|
||||||
fmt.Println(obj)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
(The `iterator` package is at `google.golang.org/api/iterator`.)
|
|
||||||
|
|
||||||
Replace `Query.Cursor` with `ObjectIterator.PageInfo().Token`.
|
|
||||||
|
|
||||||
Replace `Query.MaxResults` with `ObjectIterator.PageInfo().MaxSize`.
|
|
||||||
|
|
||||||
|
|
||||||
* ObjectHandle.CopyTo replaced by ObjectHandle.CopierFrom.
|
|
||||||
Replace
|
|
||||||
```go
|
|
||||||
attrs, err := src.CopyTo(ctx, dst, nil)
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```go
|
|
||||||
attrs, err := dst.CopierFrom(src).Run(ctx)
|
|
||||||
```
|
|
||||||
|
|
||||||
Replace
|
|
||||||
```go
|
|
||||||
attrs, err := src.CopyTo(ctx, dst, &storage.ObjectAttrs{ContextType: "text/html"})
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```go
|
|
||||||
c := dst.CopierFrom(src)
|
|
||||||
c.ContextType = "text/html"
|
|
||||||
attrs, err := c.Run(ctx)
|
|
||||||
```
|
|
||||||
|
|
||||||
* ObjectHandle.ComposeFrom replaced by ObjectHandle.ComposerFrom.
|
|
||||||
Replace
|
|
||||||
```go
|
|
||||||
attrs, err := dst.ComposeFrom(ctx, []*storage.ObjectHandle{src1, src2}, nil)
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```go
|
|
||||||
attrs, err := dst.ComposerFrom(src1, src2).Run(ctx)
|
|
||||||
```
|
|
||||||
|
|
||||||
* ObjectHandle.Update's ObjectAttrs argument replaced by ObjectAttrsToUpdate.
|
|
||||||
Replace
|
|
||||||
```go
|
|
||||||
attrs, err := obj.Update(ctx, &storage.ObjectAttrs{ContextType: "text/html"})
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```go
|
|
||||||
attrs, err := obj.Update(ctx, storage.ObjectAttrsToUpdate{ContextType: "text/html"})
|
|
||||||
```
|
|
||||||
|
|
||||||
* ObjectHandle.WithConditions replaced by ObjectHandle.If.
|
|
||||||
Replace
|
|
||||||
```go
|
|
||||||
obj.WithConditions(storage.Generation(gen), storage.IfMetaGenerationMatch(mgen))
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```go
|
|
||||||
obj.Generation(gen).If(storage.Conditions{MetagenerationMatch: mgen})
|
|
||||||
```
|
|
||||||
|
|
||||||
Replace
|
|
||||||
```go
|
|
||||||
obj.WithConditions(storage.IfGenerationMatch(0))
|
|
||||||
```
|
|
||||||
with
|
|
||||||
```go
|
|
||||||
obj.If(storage.Conditions{DoesNotExist: true})
|
|
||||||
```
|
|
||||||
|
|
||||||
* `storage.Done` replaced by `iterator.Done` (from package `google.golang.org/api/iterator`).
|
|
||||||
|
|
||||||
_October 6, 2016_
|
|
||||||
|
|
||||||
Package preview/logging deleted. Use logging instead.
|
|
||||||
|
|
||||||
_September 27, 2016_
|
|
||||||
|
|
||||||
Logging client replaced with preview version (see below).
|
|
||||||
|
|
||||||
_September 8, 2016_
|
|
||||||
|
|
||||||
* New clients for some of Google's Machine Learning APIs: Vision, Speech, and
|
|
||||||
Natural Language.
|
|
||||||
|
|
||||||
* Preview version of a new [Stackdriver Logging][cloud-logging] client in
|
|
||||||
[`cloud.google.com/go/preview/logging`](https://godoc.org/cloud.google.com/go/preview/logging).
|
|
||||||
This client uses gRPC as its transport layer, and supports log reading, sinks
|
|
||||||
and metrics. It will replace the current client at `cloud.google.com/go/logging` shortly.
|
|
||||||
|
|
86
vendor/cloud.google.com/go/run-tests.sh
generated
vendored
86
vendor/cloud.google.com/go/run-tests.sh
generated
vendored
|
@ -1,86 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Selectively run tests for this repo, based on what has changed
|
|
||||||
# in a commit. Runs short tests for the whole repo, and full tests
|
|
||||||
# for changed directories.
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
prefix=cloud.google.com/go
|
|
||||||
|
|
||||||
dryrun=false
|
|
||||||
if [[ $1 == "-n" ]]; then
|
|
||||||
dryrun=true
|
|
||||||
shift
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $1 == "" ]]; then
|
|
||||||
echo >&2 "usage: $0 [-n] COMMIT"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Files or directories that cause all tests to run if modified.
|
|
||||||
declare -A run_all
|
|
||||||
run_all=([.travis.yml]=1 [run-tests.sh]=1)
|
|
||||||
|
|
||||||
function run {
|
|
||||||
if $dryrun; then
|
|
||||||
echo $*
|
|
||||||
else
|
|
||||||
(set -x; $*)
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Find all the packages that have changed in this commit.
|
|
||||||
declare -A changed_packages
|
|
||||||
|
|
||||||
for f in $(git diff-tree --no-commit-id --name-only -r $1); do
|
|
||||||
if [[ ${run_all[$f]} == 1 ]]; then
|
|
||||||
# This change requires a full test. Do it and exit.
|
|
||||||
run go test -race -v $prefix/...
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
# Map, e.g., "spanner/client.go" to "$prefix/spanner".
|
|
||||||
d=$(dirname $f)
|
|
||||||
if [[ $d == "." ]]; then
|
|
||||||
pkg=$prefix
|
|
||||||
else
|
|
||||||
pkg=$prefix/$d
|
|
||||||
fi
|
|
||||||
changed_packages[$pkg]=1
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "changed packages: ${!changed_packages[*]}"
|
|
||||||
|
|
||||||
|
|
||||||
# Reports whether its argument, a package name, depends (recursively)
|
|
||||||
# on a changed package.
|
|
||||||
function depends_on_changed_package {
|
|
||||||
# According to go list, a package does not depend on itself, so
|
|
||||||
# we test that separately.
|
|
||||||
if [[ ${changed_packages[$1]} == 1 ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
for dep in $(go list -f '{{range .Deps}}{{.}} {{end}}' $1); do
|
|
||||||
if [[ ${changed_packages[$dep]} == 1 ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Collect the packages into two separate lists. (It is faster go test a list of
|
|
||||||
# packages than to individually go test each one.)
|
|
||||||
|
|
||||||
shorts=
|
|
||||||
fulls=
|
|
||||||
for pkg in $(go list $prefix/...); do # for each package in the repo
|
|
||||||
if depends_on_changed_package $pkg; then # if it depends on a changed package
|
|
||||||
fulls="$fulls $pkg" # run the full test
|
|
||||||
else # otherwise
|
|
||||||
shorts="$shorts $pkg" # run the short test
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
run go test -race -v -short $shorts
|
|
||||||
run go test -race -v $fulls
|
|
14
vendor/github.com/asaskevich/govalidator/.travis.yml
generated
vendored
Normal file
14
vendor/github.com/asaskevich/govalidator/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.1
|
||||||
|
- 1.2
|
||||||
|
- 1.3
|
||||||
|
- 1.4
|
||||||
|
- 1.5
|
||||||
|
- 1.6
|
||||||
|
- tip
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email:
|
||||||
|
- bwatas@gmail.com
|
21
vendor/github.com/asaskevich/govalidator/LICENSE
generated
vendored
Normal file
21
vendor/github.com/asaskevich/govalidator/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Alex Saskevich
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
423
vendor/github.com/asaskevich/govalidator/README.md
generated
vendored
Normal file
423
vendor/github.com/asaskevich/govalidator/README.md
generated
vendored
Normal file
|
@ -0,0 +1,423 @@
|
||||||
|
govalidator
|
||||||
|
===========
|
||||||
|
[](https://gitter.im/asaskevich/govalidator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [](https://godoc.org/github.com/asaskevich/govalidator) [](https://coveralls.io/r/asaskevich/govalidator?branch=master) [](https://app.wercker.com/project/bykey/1ec990b09ea86c910d5f08b0e02c6043)
|
||||||
|
[](https://travis-ci.org/asaskevich/govalidator) [](https://goreportcard.com/report/github.com/asaskevich/govalidator) [](http://go-search.org/view?id=github.com%2Fasaskevich%2Fgovalidator)
|
||||||
|
|
||||||
|
A package of validators and sanitizers for strings, structs and collections. Based on [validator.js](https://github.com/chriso/validator.js).
|
||||||
|
|
||||||
|
#### Installation
|
||||||
|
Make sure that Go is installed on your computer.
|
||||||
|
Type the following command in your terminal:
|
||||||
|
|
||||||
|
go get github.com/asaskevich/govalidator
|
||||||
|
|
||||||
|
or you can get specified release of the package with `gopkg.in`:
|
||||||
|
|
||||||
|
go get gopkg.in/asaskevich/govalidator.v4
|
||||||
|
|
||||||
|
After it the package is ready to use.
|
||||||
|
|
||||||
|
|
||||||
|
#### Import package in your project
|
||||||
|
Add following line in your `*.go` file:
|
||||||
|
```go
|
||||||
|
import "github.com/asaskevich/govalidator"
|
||||||
|
```
|
||||||
|
If you are unhappy to use long `govalidator`, you can do something like this:
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
valid "github.com/asaskevich/govalidator"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Activate behavior to require all fields have a validation tag by default
|
||||||
|
`SetFieldsRequiredByDefault` causes validation to fail when struct fields do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`). A good place to activate this is a package init function or the main() function.
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/asaskevich/govalidator"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
govalidator.SetFieldsRequiredByDefault(true)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's some code to explain it:
|
||||||
|
```go
|
||||||
|
// this struct definition will fail govalidator.ValidateStruct() (and the field values do not matter):
|
||||||
|
type exampleStruct struct {
|
||||||
|
Name string ``
|
||||||
|
Email string `valid:"email"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// this, however, will only fail when Email is empty or an invalid email address:
|
||||||
|
type exampleStruct2 struct {
|
||||||
|
Name string `valid:"-"`
|
||||||
|
Email string `valid:"email"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// lastly, this will only fail when Email is an invalid email address but not when it's empty:
|
||||||
|
type exampleStruct2 struct {
|
||||||
|
Name string `valid:"-"`
|
||||||
|
Email string `valid:"email,optional"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Recent breaking changes (see [#123](https://github.com/asaskevich/govalidator/pull/123))
|
||||||
|
##### Custom validator function signature
|
||||||
|
A context was added as the second parameter, for structs this is the object being validated – this makes dependent validation possible.
|
||||||
|
```go
|
||||||
|
import "github.com/asaskevich/govalidator"
|
||||||
|
|
||||||
|
// old signature
|
||||||
|
func(i interface{}) bool
|
||||||
|
|
||||||
|
// new signature
|
||||||
|
func(i interface{}, o interface{}) bool
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Adding a custom validator
|
||||||
|
This was changed to prevent data races when accessing custom validators.
|
||||||
|
```go
|
||||||
|
import "github.com/asaskevich/govalidator"
|
||||||
|
|
||||||
|
// before
|
||||||
|
govalidator.CustomTypeTagMap["customByteArrayValidator"] = CustomTypeValidator(func(i interface{}, o interface{}) bool {
|
||||||
|
// ...
|
||||||
|
})
|
||||||
|
|
||||||
|
// after
|
||||||
|
govalidator.CustomTypeTagMap.Set("customByteArrayValidator", CustomTypeValidator(func(i interface{}, o interface{}) bool {
|
||||||
|
// ...
|
||||||
|
}))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### List of functions:
|
||||||
|
```go
|
||||||
|
func Abs(value float64) float64
|
||||||
|
func BlackList(str, chars string) string
|
||||||
|
func ByteLength(str string, params ...string) bool
|
||||||
|
func CamelCaseToUnderscore(str string) string
|
||||||
|
func Contains(str, substring string) bool
|
||||||
|
func Count(array []interface{}, iterator ConditionIterator) int
|
||||||
|
func Each(array []interface{}, iterator Iterator)
|
||||||
|
func ErrorByField(e error, field string) string
|
||||||
|
func ErrorsByField(e error) map[string]string
|
||||||
|
func Filter(array []interface{}, iterator ConditionIterator) []interface{}
|
||||||
|
func Find(array []interface{}, iterator ConditionIterator) interface{}
|
||||||
|
func GetLine(s string, index int) (string, error)
|
||||||
|
func GetLines(s string) []string
|
||||||
|
func InRange(value, left, right float64) bool
|
||||||
|
func IsASCII(str string) bool
|
||||||
|
func IsAlpha(str string) bool
|
||||||
|
func IsAlphanumeric(str string) bool
|
||||||
|
func IsBase64(str string) bool
|
||||||
|
func IsByteLength(str string, min, max int) bool
|
||||||
|
func IsCIDR(str string) bool
|
||||||
|
func IsCreditCard(str string) bool
|
||||||
|
func IsDNSName(str string) bool
|
||||||
|
func IsDataURI(str string) bool
|
||||||
|
func IsDialString(str string) bool
|
||||||
|
func IsDivisibleBy(str, num string) bool
|
||||||
|
func IsEmail(str string) bool
|
||||||
|
func IsFilePath(str string) (bool, int)
|
||||||
|
func IsFloat(str string) bool
|
||||||
|
func IsFullWidth(str string) bool
|
||||||
|
func IsHalfWidth(str string) bool
|
||||||
|
func IsHexadecimal(str string) bool
|
||||||
|
func IsHexcolor(str string) bool
|
||||||
|
func IsHost(str string) bool
|
||||||
|
func IsIP(str string) bool
|
||||||
|
func IsIPv4(str string) bool
|
||||||
|
func IsIPv6(str string) bool
|
||||||
|
func IsISBN(str string, version int) bool
|
||||||
|
func IsISBN10(str string) bool
|
||||||
|
func IsISBN13(str string) bool
|
||||||
|
func IsISO3166Alpha2(str string) bool
|
||||||
|
func IsISO3166Alpha3(str string) bool
|
||||||
|
func IsISO693Alpha2(str string) bool
|
||||||
|
func IsISO693Alpha3b(str string) bool
|
||||||
|
func IsISO4217(str string) bool
|
||||||
|
func IsIn(str string, params ...string) bool
|
||||||
|
func IsInt(str string) bool
|
||||||
|
func IsJSON(str string) bool
|
||||||
|
func IsLatitude(str string) bool
|
||||||
|
func IsLongitude(str string) bool
|
||||||
|
func IsLowerCase(str string) bool
|
||||||
|
func IsMAC(str string) bool
|
||||||
|
func IsMongoID(str string) bool
|
||||||
|
func IsMultibyte(str string) bool
|
||||||
|
func IsNatural(value float64) bool
|
||||||
|
func IsNegative(value float64) bool
|
||||||
|
func IsNonNegative(value float64) bool
|
||||||
|
func IsNonPositive(value float64) bool
|
||||||
|
func IsNull(str string) bool
|
||||||
|
func IsNumeric(str string) bool
|
||||||
|
func IsPort(str string) bool
|
||||||
|
func IsPositive(value float64) bool
|
||||||
|
func IsPrintableASCII(str string) bool
|
||||||
|
func IsRFC3339(str string) bool
|
||||||
|
func IsRGBcolor(str string) bool
|
||||||
|
func IsRequestURI(rawurl string) bool
|
||||||
|
func IsRequestURL(rawurl string) bool
|
||||||
|
func IsSSN(str string) bool
|
||||||
|
func IsSemver(str string) bool
|
||||||
|
func IsTime(str string, format string) bool
|
||||||
|
func IsURL(str string) bool
|
||||||
|
func IsUTFDigit(str string) bool
|
||||||
|
func IsUTFLetter(str string) bool
|
||||||
|
func IsUTFLetterNumeric(str string) bool
|
||||||
|
func IsUTFNumeric(str string) bool
|
||||||
|
func IsUUID(str string) bool
|
||||||
|
func IsUUIDv3(str string) bool
|
||||||
|
func IsUUIDv4(str string) bool
|
||||||
|
func IsUUIDv5(str string) bool
|
||||||
|
func IsUpperCase(str string) bool
|
||||||
|
func IsVariableWidth(str string) bool
|
||||||
|
func IsWhole(value float64) bool
|
||||||
|
func LeftTrim(str, chars string) string
|
||||||
|
func Map(array []interface{}, iterator ResultIterator) []interface{}
|
||||||
|
func Matches(str, pattern string) bool
|
||||||
|
func NormalizeEmail(str string) (string, error)
|
||||||
|
func PadBoth(str string, padStr string, padLen int) string
|
||||||
|
func PadLeft(str string, padStr string, padLen int) string
|
||||||
|
func PadRight(str string, padStr string, padLen int) string
|
||||||
|
func Range(str string, params ...string) bool
|
||||||
|
func RemoveTags(s string) string
|
||||||
|
func ReplacePattern(str, pattern, replace string) string
|
||||||
|
func Reverse(s string) string
|
||||||
|
func RightTrim(str, chars string) string
|
||||||
|
func RuneLength(str string, params ...string) bool
|
||||||
|
func SafeFileName(str string) string
|
||||||
|
func SetFieldsRequiredByDefault(value bool)
|
||||||
|
func Sign(value float64) float64
|
||||||
|
func StringLength(str string, params ...string) bool
|
||||||
|
func StringMatches(s string, params ...string) bool
|
||||||
|
func StripLow(str string, keepNewLines bool) string
|
||||||
|
func ToBoolean(str string) (bool, error)
|
||||||
|
func ToFloat(str string) (float64, error)
|
||||||
|
func ToInt(str string) (int64, error)
|
||||||
|
func ToJSON(obj interface{}) (string, error)
|
||||||
|
func ToString(obj interface{}) string
|
||||||
|
func Trim(str, chars string) string
|
||||||
|
func Truncate(str string, length int, ending string) string
|
||||||
|
func UnderscoreToCamelCase(s string) string
|
||||||
|
func ValidateStruct(s interface{}) (bool, error)
|
||||||
|
func WhiteList(str, chars string) string
|
||||||
|
type ConditionIterator
|
||||||
|
type CustomTypeValidator
|
||||||
|
type Error
|
||||||
|
func (e Error) Error() string
|
||||||
|
type Errors
|
||||||
|
func (es Errors) Error() string
|
||||||
|
func (es Errors) Errors() []error
|
||||||
|
type ISO3166Entry
|
||||||
|
type Iterator
|
||||||
|
type ParamValidator
|
||||||
|
type ResultIterator
|
||||||
|
type UnsupportedTypeError
|
||||||
|
func (e *UnsupportedTypeError) Error() string
|
||||||
|
type Validator
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
###### IsURL
|
||||||
|
```go
|
||||||
|
println(govalidator.IsURL(`http://user@pass:domain.com/path/page`))
|
||||||
|
```
|
||||||
|
###### ToString
|
||||||
|
```go
|
||||||
|
type User struct {
|
||||||
|
FirstName string
|
||||||
|
LastName string
|
||||||
|
}
|
||||||
|
|
||||||
|
str := govalidator.ToString(&User{"John", "Juan"})
|
||||||
|
println(str)
|
||||||
|
```
|
||||||
|
###### Each, Map, Filter, Count for slices
|
||||||
|
Each iterates over the slice/array and calls Iterator for every item
|
||||||
|
```go
|
||||||
|
data := []interface{}{1, 2, 3, 4, 5}
|
||||||
|
var fn govalidator.Iterator = func(value interface{}, index int) {
|
||||||
|
println(value.(int))
|
||||||
|
}
|
||||||
|
govalidator.Each(data, fn)
|
||||||
|
```
|
||||||
|
```go
|
||||||
|
data := []interface{}{1, 2, 3, 4, 5}
|
||||||
|
var fn govalidator.ResultIterator = func(value interface{}, index int) interface{} {
|
||||||
|
return value.(int) * 3
|
||||||
|
}
|
||||||
|
_ = govalidator.Map(data, fn) // result = []interface{}{1, 6, 9, 12, 15}
|
||||||
|
```
|
||||||
|
```go
|
||||||
|
data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||||
|
var fn govalidator.ConditionIterator = func(value interface{}, index int) bool {
|
||||||
|
return value.(int)%2 == 0
|
||||||
|
}
|
||||||
|
_ = govalidator.Filter(data, fn) // result = []interface{}{2, 4, 6, 8, 10}
|
||||||
|
_ = govalidator.Count(data, fn) // result = 5
|
||||||
|
```
|
||||||
|
###### ValidateStruct [#2](https://github.com/asaskevich/govalidator/pull/2)
|
||||||
|
If you want to validate structs, you can use tag `valid` for any field in your structure. All validators used with this field in one tag are separated by comma. If you want to skip validation, place `-` in your tag. If you need a validator that is not on the list below, you can add it like this:
|
||||||
|
```go
|
||||||
|
govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool {
|
||||||
|
return str == "duck"
|
||||||
|
})
|
||||||
|
```
|
||||||
|
For completely custom validators (interface-based), see below.
|
||||||
|
|
||||||
|
Here is a list of available validators for struct fields (validator - used function):
|
||||||
|
```go
|
||||||
|
"email": IsEmail,
|
||||||
|
"url": IsURL,
|
||||||
|
"dialstring": IsDialString,
|
||||||
|
"requrl": IsRequestURL,
|
||||||
|
"requri": IsRequestURI,
|
||||||
|
"alpha": IsAlpha,
|
||||||
|
"utfletter": IsUTFLetter,
|
||||||
|
"alphanum": IsAlphanumeric,
|
||||||
|
"utfletternum": IsUTFLetterNumeric,
|
||||||
|
"numeric": IsNumeric,
|
||||||
|
"utfnumeric": IsUTFNumeric,
|
||||||
|
"utfdigit": IsUTFDigit,
|
||||||
|
"hexadecimal": IsHexadecimal,
|
||||||
|
"hexcolor": IsHexcolor,
|
||||||
|
"rgbcolor": IsRGBcolor,
|
||||||
|
"lowercase": IsLowerCase,
|
||||||
|
"uppercase": IsUpperCase,
|
||||||
|
"int": IsInt,
|
||||||
|
"float": IsFloat,
|
||||||
|
"null": IsNull,
|
||||||
|
"uuid": IsUUID,
|
||||||
|
"uuidv3": IsUUIDv3,
|
||||||
|
"uuidv4": IsUUIDv4,
|
||||||
|
"uuidv5": IsUUIDv5,
|
||||||
|
"creditcard": IsCreditCard,
|
||||||
|
"isbn10": IsISBN10,
|
||||||
|
"isbn13": IsISBN13,
|
||||||
|
"json": IsJSON,
|
||||||
|
"multibyte": IsMultibyte,
|
||||||
|
"ascii": IsASCII,
|
||||||
|
"printableascii": IsPrintableASCII,
|
||||||
|
"fullwidth": IsFullWidth,
|
||||||
|
"halfwidth": IsHalfWidth,
|
||||||
|
"variablewidth": IsVariableWidth,
|
||||||
|
"base64": IsBase64,
|
||||||
|
"datauri": IsDataURI,
|
||||||
|
"ip": IsIP,
|
||||||
|
"port": IsPort,
|
||||||
|
"ipv4": IsIPv4,
|
||||||
|
"ipv6": IsIPv6,
|
||||||
|
"dns": IsDNSName,
|
||||||
|
"host": IsHost,
|
||||||
|
"mac": IsMAC,
|
||||||
|
"latitude": IsLatitude,
|
||||||
|
"longitude": IsLongitude,
|
||||||
|
"ssn": IsSSN,
|
||||||
|
"semver": IsSemver,
|
||||||
|
"rfc3339": IsRFC3339,
|
||||||
|
"ISO3166Alpha2": IsISO3166Alpha2,
|
||||||
|
"ISO3166Alpha3": IsISO3166Alpha3,
|
||||||
|
```
|
||||||
|
Validators with parameters
|
||||||
|
|
||||||
|
```go
|
||||||
|
"range(min|max)": Range,
|
||||||
|
"length(min|max)": ByteLength,
|
||||||
|
"runelength(min|max)": RuneLength,
|
||||||
|
"matches(pattern)": StringMatches,
|
||||||
|
"in(string1|string2|...|stringN)": IsIn,
|
||||||
|
```
|
||||||
|
|
||||||
|
And here is small example of usage:
|
||||||
|
```go
|
||||||
|
type Post struct {
|
||||||
|
Title string `valid:"alphanum,required"`
|
||||||
|
Message string `valid:"duck,ascii"`
|
||||||
|
AuthorIP string `valid:"ipv4"`
|
||||||
|
Date string `valid:"-"`
|
||||||
|
}
|
||||||
|
post := &Post{
|
||||||
|
Title: "My Example Post",
|
||||||
|
Message: "duck",
|
||||||
|
AuthorIP: "123.234.54.3",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add your own struct validation tags
|
||||||
|
govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool {
|
||||||
|
return str == "duck"
|
||||||
|
})
|
||||||
|
|
||||||
|
result, err := govalidator.ValidateStruct(post)
|
||||||
|
if err != nil {
|
||||||
|
println("error: " + err.Error())
|
||||||
|
}
|
||||||
|
println(result)
|
||||||
|
```
|
||||||
|
###### WhiteList
|
||||||
|
```go
|
||||||
|
// Remove all characters from string ignoring characters between "a" and "z"
|
||||||
|
println(govalidator.WhiteList("a3a43a5a4a3a2a23a4a5a4a3a4", "a-z") == "aaaaaaaaaaaa")
|
||||||
|
```
|
||||||
|
|
||||||
|
###### Custom validation functions
|
||||||
|
Custom validation using your own domain specific validators is also available - here's an example of how to use it:
|
||||||
|
```go
|
||||||
|
import "github.com/asaskevich/govalidator"
|
||||||
|
|
||||||
|
type CustomByteArray [6]byte // custom types are supported and can be validated
|
||||||
|
|
||||||
|
type StructWithCustomByteArray struct {
|
||||||
|
ID CustomByteArray `valid:"customByteArrayValidator,customMinLengthValidator"` // multiple custom validators are possible as well and will be evaluated in sequence
|
||||||
|
Email string `valid:"email"`
|
||||||
|
CustomMinLength int `valid:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
govalidator.CustomTypeTagMap.Set("customByteArrayValidator", CustomTypeValidator(func(i interface{}, context interface{}) bool {
|
||||||
|
switch v := context.(type) { // you can type switch on the context interface being validated
|
||||||
|
case StructWithCustomByteArray:
|
||||||
|
// you can check and validate against some other field in the context,
|
||||||
|
// return early or not validate against the context at all – your choice
|
||||||
|
case SomeOtherType:
|
||||||
|
// ...
|
||||||
|
default:
|
||||||
|
// expecting some other type? Throw/panic here or continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v := i.(type) { // type switch on the struct field being validated
|
||||||
|
case CustomByteArray:
|
||||||
|
for _, e := range v { // this validator checks that the byte array is not empty, i.e. not all zeroes
|
||||||
|
if e != 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}))
|
||||||
|
govalidator.CustomTypeTagMap.Set("customMinLengthValidator", CustomTypeValidator(func(i interface{}, context interface{}) bool {
|
||||||
|
switch v := context.(type) { // this validates a field against the value in another field, i.e. dependent validation
|
||||||
|
case StructWithCustomByteArray:
|
||||||
|
return len(v.ID) >= v.CustomMinLength
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Notes
|
||||||
|
Documentation is available here: [godoc.org](https://godoc.org/github.com/asaskevich/govalidator).
|
||||||
|
Full information about code coverage is also available here: [govalidator on gocover.io](http://gocover.io/github.com/asaskevich/govalidator).
|
||||||
|
|
||||||
|
#### Support
|
||||||
|
If you do have a contribution for the package feel free to put up a Pull Request or open Issue.
|
||||||
|
|
||||||
|
#### Special thanks to [contributors](https://github.com/asaskevich/govalidator/graphs/contributors)
|
||||||
|
* [Daniel Lohse](https://github.com/annismckenzie)
|
||||||
|
* [Attila Oláh](https://github.com/attilaolah)
|
||||||
|
* [Daniel Korner](https://github.com/Dadie)
|
||||||
|
* [Steven Wilkin](https://github.com/stevenwilkin)
|
||||||
|
* [Deiwin Sarjas](https://github.com/deiwin)
|
||||||
|
* [Noah Shibley](https://github.com/slugmobile)
|
||||||
|
* [Nathan Davies](https://github.com/nathj07)
|
||||||
|
* [Matt Sanford](https://github.com/mzsanford)
|
||||||
|
* [Simon ccl1115](https://github.com/ccl1115)
|
58
vendor/github.com/asaskevich/govalidator/arrays.go
generated
vendored
Normal file
58
vendor/github.com/asaskevich/govalidator/arrays.go
generated
vendored
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package govalidator
|
||||||
|
|
||||||
|
// Iterator is the function that accepts element of slice/array and its index
|
||||||
|
type Iterator func(interface{}, int)
|
||||||
|
|
||||||
|
// ResultIterator is the function that accepts element of slice/array and its index and returns any result
|
||||||
|
type ResultIterator func(interface{}, int) interface{}
|
||||||
|
|
||||||
|
// ConditionIterator is the function that accepts element of slice/array and its index and returns boolean
|
||||||
|
type ConditionIterator func(interface{}, int) bool
|
||||||
|
|
||||||
|
// Each iterates over the slice and apply Iterator to every item
|
||||||
|
func Each(array []interface{}, iterator Iterator) {
|
||||||
|
for index, data := range array {
|
||||||
|
iterator(data, index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map iterates over the slice and apply ResultIterator to every item. Returns new slice as a result.
|
||||||
|
func Map(array []interface{}, iterator ResultIterator) []interface{} {
|
||||||
|
var result = make([]interface{}, len(array))
|
||||||
|
for index, data := range array {
|
||||||
|
result[index] = iterator(data, index)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find iterates over the slice and apply ConditionIterator to every item. Returns first item that meet ConditionIterator or nil otherwise.
|
||||||
|
func Find(array []interface{}, iterator ConditionIterator) interface{} {
|
||||||
|
for index, data := range array {
|
||||||
|
if iterator(data, index) {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter iterates over the slice and apply ConditionIterator to every item. Returns new slice.
|
||||||
|
func Filter(array []interface{}, iterator ConditionIterator) []interface{} {
|
||||||
|
var result = make([]interface{}, 0)
|
||||||
|
for index, data := range array {
|
||||||
|
if iterator(data, index) {
|
||||||
|
result = append(result, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count iterates over the slice and apply ConditionIterator to every item. Returns count of items that meets ConditionIterator.
|
||||||
|
func Count(array []interface{}, iterator ConditionIterator) int {
|
||||||
|
count := 0
|
||||||
|
for index, data := range array {
|
||||||
|
if iterator(data, index) {
|
||||||
|
count = count + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
116
vendor/github.com/asaskevich/govalidator/arrays_test.go
generated
vendored
Normal file
116
vendor/github.com/asaskevich/govalidator/arrays_test.go
generated
vendored
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
package govalidator
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestEach(t *testing.T) {
|
||||||
|
// TODO Maybe refactor?
|
||||||
|
t.Parallel()
|
||||||
|
acc := 0
|
||||||
|
data := []interface{}{1, 2, 3, 4, 5}
|
||||||
|
var fn Iterator = func(value interface{}, index int) {
|
||||||
|
acc = acc + value.(int)
|
||||||
|
}
|
||||||
|
Each(data, fn)
|
||||||
|
if acc != 15 {
|
||||||
|
t.Errorf("Expected Each(..) to be %v, got %v", 15, acc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleEach() {
|
||||||
|
data := []interface{}{1, 2, 3, 4, 5}
|
||||||
|
var fn Iterator = func(value interface{}, index int) {
|
||||||
|
println(value.(int))
|
||||||
|
}
|
||||||
|
Each(data, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMap(t *testing.T) {
|
||||||
|
// TODO Maybe refactor?
|
||||||
|
t.Parallel()
|
||||||
|
data := []interface{}{1, 2, 3, 4, 5}
|
||||||
|
var fn ResultIterator = func(value interface{}, index int) interface{} {
|
||||||
|
return value.(int) * 3
|
||||||
|
}
|
||||||
|
result := Map(data, fn)
|
||||||
|
for i, d := range result {
|
||||||
|
if d != fn(data[i], i) {
|
||||||
|
t.Errorf("Expected Map(..) to be %v, got %v", fn(data[i], i), d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleMap() {
|
||||||
|
data := []interface{}{1, 2, 3, 4, 5}
|
||||||
|
var fn ResultIterator = func(value interface{}, index int) interface{} {
|
||||||
|
return value.(int) * 3
|
||||||
|
}
|
||||||
|
_ = Map(data, fn) // result = []interface{}{1, 6, 9, 12, 15}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFind(t *testing.T) {
|
||||||
|
// TODO Maybe refactor?
|
||||||
|
t.Parallel()
|
||||||
|
findElement := 96
|
||||||
|
data := []interface{}{1, 2, 3, 4, findElement, 5}
|
||||||
|
var fn1 ConditionIterator = func(value interface{}, index int) bool {
|
||||||
|
return value.(int) == findElement
|
||||||
|
}
|
||||||
|
var fn2 ConditionIterator = func(value interface{}, index int) bool {
|
||||||
|
value, _ = value.(string)
|
||||||
|
return value == "govalidator"
|
||||||
|
}
|
||||||
|
val1 := Find(data, fn1)
|
||||||
|
val2 := Find(data, fn2)
|
||||||
|
if val1 != findElement {
|
||||||
|
t.Errorf("Expected Find(..) to be %v, got %v", findElement, val1)
|
||||||
|
}
|
||||||
|
if val2 != nil {
|
||||||
|
t.Errorf("Expected Find(..) to be %v, got %v", nil, val2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilter(t *testing.T) {
|
||||||
|
// TODO Maybe refactor?
|
||||||
|
t.Parallel()
|
||||||
|
data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||||
|
answer := []interface{}{2, 4, 6, 8, 10}
|
||||||
|
var fn ConditionIterator = func(value interface{}, index int) bool {
|
||||||
|
return value.(int)%2 == 0
|
||||||
|
}
|
||||||
|
result := Filter(data, fn)
|
||||||
|
for i := range result {
|
||||||
|
if result[i] != answer[i] {
|
||||||
|
t.Errorf("Expected Filter(..) to be %v, got %v", answer[i], result[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFilter() {
|
||||||
|
data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||||
|
var fn ConditionIterator = func(value interface{}, index int) bool {
|
||||||
|
return value.(int)%2 == 0
|
||||||
|
}
|
||||||
|
_ = Filter(data, fn) // result = []interface{}{2, 4, 6, 8, 10}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCount(t *testing.T) {
|
||||||
|
// TODO Maybe refactor?
|
||||||
|
t.Parallel()
|
||||||
|
data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||||
|
count := 5
|
||||||
|
var fn ConditionIterator = func(value interface{}, index int) bool {
|
||||||
|
return value.(int)%2 == 0
|
||||||
|
}
|
||||||
|
result := Count(data, fn)
|
||||||
|
if result != count {
|
||||||
|
t.Errorf("Expected Count(..) to be %v, got %v", count, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleCount() {
|
||||||
|
data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||||
|
var fn ConditionIterator = func(value interface{}, index int) bool {
|
||||||
|
return value.(int)%2 == 0
|
||||||
|
}
|
||||||
|
_ = Count(data, fn) // result = 5
|
||||||
|
}
|
45
vendor/github.com/asaskevich/govalidator/converter.go
generated
vendored
Normal file
45
vendor/github.com/asaskevich/govalidator/converter.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package govalidator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ToString convert the input to a string.
|
||||||
|
func ToString(obj interface{}) string {
|
||||||
|
res := fmt.Sprintf("%v", obj)
|
||||||
|
return string(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToJSON convert the input to a valid JSON string
|
||||||
|
func ToJSON(obj interface{}) (string, error) {
|
||||||
|
res, err := json.Marshal(obj)
|
||||||
|
if err != nil {
|
||||||
|
res = []byte("")
|
||||||
|
}
|
||||||
|
return string(res), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToFloat convert the input string to a float, or 0.0 if the input is not a float.
|
||||||
|
func ToFloat(str string) (float64, error) {
|
||||||
|
res, err := strconv.ParseFloat(str, 64)
|
||||||
|
if err != nil {
|
||||||
|
res = 0.0
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToInt convert the input string to an integer, or 0 if the input is not an integer.
|
||||||
|
func ToInt(str string) (int64, error) {
|
||||||
|
res, err := strconv.ParseInt(str, 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
res = 0
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToBoolean convert the input string to a boolean.
|
||||||
|
func ToBoolean(str string) (bool, error) {
|
||||||
|
return strconv.ParseBool(str)
|
||||||
|
}
|
78
vendor/github.com/asaskevich/govalidator/converter_test.go
generated
vendored
Normal file
78
vendor/github.com/asaskevich/govalidator/converter_test.go
generated
vendored
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
package govalidator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestToInt(t *testing.T) {
|
||||||
|
tests := []string{"1000", "-123", "abcdef", "100000000000000000000000000000000000000000000"}
|
||||||
|
expected := []int64{1000, -123, 0, 0}
|
||||||
|
for i := 0; i < len(tests); i++ {
|
||||||
|
result, _ := ToInt(tests[i])
|
||||||
|
if result != expected[i] {
|
||||||
|
t.Log("Case ", i, ": expected ", expected[i], " when result is ", result)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToBoolean(t *testing.T) {
|
||||||
|
tests := []string{"true", "1", "True", "false", "0", "abcdef"}
|
||||||
|
expected := []bool{true, true, true, false, false, false}
|
||||||
|
for i := 0; i < len(tests); i++ {
|
||||||
|
res, _ := ToBoolean(tests[i])
|
||||||
|
if res != expected[i] {
|
||||||
|
t.Log("Case ", i, ": expected ", expected[i], " when result is ", res)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toString(t *testing.T, test interface{}, expected string) {
|
||||||
|
res := ToString(test)
|
||||||
|
if res != expected {
|
||||||
|
t.Log("Case ToString: expected ", expected, " when result is ", res)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToString(t *testing.T) {
|
||||||
|
toString(t, "str123", "str123")
|
||||||
|
toString(t, 123, "123")
|
||||||
|
toString(t, 12.3, "12.3")
|
||||||
|
toString(t, true, "true")
|
||||||
|
toString(t, 1.5+10i, "(1.5+10i)")
|
||||||
|
// Sprintf function not guarantee that maps with equal keys always will be equal in string representation
|
||||||
|
//toString(t, struct{ Keys map[int]int }{Keys: map[int]int{1: 2, 3: 4}}, "{map[1:2 3:4]}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToFloat(t *testing.T) {
|
||||||
|
tests := []string{"", "123", "-.01", "10.", "string", "1.23e3", ".23e10"}
|
||||||
|
expected := []float64{0, 123, -0.01, 10.0, 0, 1230, 0.23e10}
|
||||||
|
for i := 0; i < len(tests); i++ {
|
||||||
|
res, _ := ToFloat(tests[i])
|
||||||
|
if res != expected[i] {
|
||||||
|
t.Log("Case ", i, ": expected ", expected[i], " when result is ", res)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToJSON(t *testing.T) {
|
||||||
|
tests := []interface{}{"test", map[string]string{"a": "b", "b": "c"}, func() error { return fmt.Errorf("Error") }}
|
||||||
|
expected := [][]string{
|
||||||
|
{"\"test\"", "<nil>"},
|
||||||
|
{"{\"a\":\"b\",\"b\":\"c\"}", "<nil>"},
|
||||||
|
{"", "json: unsupported type: func() error"},
|
||||||
|
}
|
||||||
|
for i, test := range tests {
|
||||||
|
actual, err := ToJSON(test)
|
||||||
|
if actual != expected[i][0] {
|
||||||
|
t.Errorf("Expected toJSON(%v) to return '%v', got '%v'", test, expected[i][0], actual)
|
||||||
|
}
|
||||||
|
if fmt.Sprintf("%v", err) != expected[i][1] {
|
||||||
|
t.Errorf("Expected error returned from toJSON(%v) to return '%v', got '%v'", test, expected[i][1], fmt.Sprintf("%v", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
vendor/github.com/asaskevich/govalidator/error.go
generated
vendored
Normal file
31
vendor/github.com/asaskevich/govalidator/error.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package govalidator
|
||||||
|
|
||||||
|
// Errors is an array of multiple errors and conforms to the error interface.
|
||||||
|
type Errors []error
|
||||||
|
|
||||||
|
// Errors returns itself.
|
||||||
|
func (es Errors) Errors() []error {
|
||||||
|
return es
|
||||||
|
}
|
||||||
|
|
||||||
|
func (es Errors) Error() string {
|
||||||
|
var err string
|
||||||
|
for _, e := range es {
|
||||||
|
err += e.Error() + ";"
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error encapsulates a name, an error and whether there's a custom error message or not.
|
||||||
|
type Error struct {
|
||||||
|
Name string
|
||||||
|
Err error
|
||||||
|
CustomErrorMessageExists bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e Error) Error() string {
|
||||||
|
if e.CustomErrorMessageExists {
|
||||||
|
return e.Err.Error()
|
||||||
|
}
|
||||||
|
return e.Name + ": " + e.Err.Error()
|
||||||
|
}
|
29
vendor/github.com/asaskevich/govalidator/error_test.go
generated
vendored
Normal file
29
vendor/github.com/asaskevich/govalidator/error_test.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package govalidator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestErrorsToString(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
customErr := &Error{Name: "Custom Error Name", Err: fmt.Errorf("stdlib error")}
|
||||||
|
customErrWithCustomErrorMessage := &Error{Name: "Custom Error Name 2", Err: fmt.Errorf("Bad stuff happened"), CustomErrorMessageExists: true}
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param1 Errors
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{Errors{}, ""},
|
||||||
|
{Errors{fmt.Errorf("Error 1")}, "Error 1;"},
|
||||||
|
{Errors{fmt.Errorf("Error 1"), fmt.Errorf("Error 2")}, "Error 1;Error 2;"},
|
||||||
|
{Errors{customErr, fmt.Errorf("Error 2")}, "Custom Error Name: stdlib error;Error 2;"},
|
||||||
|
{Errors{fmt.Errorf("Error 123"), customErrWithCustomErrorMessage}, "Error 123;Bad stuff happened;"},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := test.param1.Error()
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected Error() to return '%v', got '%v'", test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
vendor/github.com/asaskevich/govalidator/numerics.go
generated
vendored
Normal file
57
vendor/github.com/asaskevich/govalidator/numerics.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package govalidator
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
// Abs returns absolute value of number
|
||||||
|
func Abs(value float64) float64 {
|
||||||
|
return math.Abs(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign returns signum of number: 1 in case of value > 0, -1 in case of value < 0, 0 otherwise
|
||||||
|
func Sign(value float64) float64 {
|
||||||
|
if value > 0 {
|
||||||
|
return 1
|
||||||
|
} else if value < 0 {
|
||||||
|
return -1
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNegative returns true if value < 0
|
||||||
|
func IsNegative(value float64) bool {
|
||||||
|
return value < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPositive returns true if value > 0
|
||||||
|
func IsPositive(value float64) bool {
|
||||||
|
return value > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNonNegative returns true if value >= 0
|
||||||
|
func IsNonNegative(value float64) bool {
|
||||||
|
return value >= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNonPositive returns true if value <= 0
|
||||||
|
func IsNonPositive(value float64) bool {
|
||||||
|
return value <= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// InRange returns true if value lies between left and right border
|
||||||
|
func InRange(value, left, right float64) bool {
|
||||||
|
if left > right {
|
||||||
|
left, right = right, left
|
||||||
|
}
|
||||||
|
return value >= left && value <= right
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsWhole returns true if value is whole number
|
||||||
|
func IsWhole(value float64) bool {
|
||||||
|
return math.Remainder(value, 1) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNatural returns true if value is natural number (positive and whole)
|
||||||
|
func IsNatural(value float64) bool {
|
||||||
|
return IsWhole(value) && IsPositive(value)
|
||||||
|
}
|
204
vendor/github.com/asaskevich/govalidator/numerics_test.go
generated
vendored
Normal file
204
vendor/github.com/asaskevich/govalidator/numerics_test.go
generated
vendored
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
package govalidator
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestAbs(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param float64
|
||||||
|
expected float64
|
||||||
|
}{
|
||||||
|
{0, 0},
|
||||||
|
{-1, 1},
|
||||||
|
{10, 10},
|
||||||
|
{3.14, 3.14},
|
||||||
|
{-96, 96},
|
||||||
|
{-10e-12, 10e-12},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := Abs(test.param)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected Abs(%v) to be %v, got %v", test.param, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSign(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param float64
|
||||||
|
expected float64
|
||||||
|
}{
|
||||||
|
{0, 0},
|
||||||
|
{-1, -1},
|
||||||
|
{10, 1},
|
||||||
|
{3.14, 1},
|
||||||
|
{-96, -1},
|
||||||
|
{-10e-12, -1},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := Sign(test.param)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected Sign(%v) to be %v, got %v", test.param, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsNegative(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param float64
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{0, false},
|
||||||
|
{-1, true},
|
||||||
|
{10, false},
|
||||||
|
{3.14, false},
|
||||||
|
{-96, true},
|
||||||
|
{-10e-12, true},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := IsNegative(test.param)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected IsNegative(%v) to be %v, got %v", test.param, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsNonNegative(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param float64
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{0, true},
|
||||||
|
{-1, false},
|
||||||
|
{10, true},
|
||||||
|
{3.14, true},
|
||||||
|
{-96, false},
|
||||||
|
{-10e-12, false},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := IsNonNegative(test.param)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected IsNonNegative(%v) to be %v, got %v", test.param, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsPositive(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param float64
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{0, false},
|
||||||
|
{-1, false},
|
||||||
|
{10, true},
|
||||||
|
{3.14, true},
|
||||||
|
{-96, false},
|
||||||
|
{-10e-12, false},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := IsPositive(test.param)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected IsPositive(%v) to be %v, got %v", test.param, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsNonPositive(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param float64
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{0, true},
|
||||||
|
{-1, true},
|
||||||
|
{10, false},
|
||||||
|
{3.14, false},
|
||||||
|
{-96, true},
|
||||||
|
{-10e-12, true},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := IsNonPositive(test.param)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected IsNonPositive(%v) to be %v, got %v", test.param, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsWhole(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param float64
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{0, true},
|
||||||
|
{-1, true},
|
||||||
|
{10, true},
|
||||||
|
{3.14, false},
|
||||||
|
{-96, true},
|
||||||
|
{-10e-12, false},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := IsWhole(test.param)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected IsWhole(%v) to be %v, got %v", test.param, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsNatural(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param float64
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{0, false},
|
||||||
|
{-1, false},
|
||||||
|
{10, true},
|
||||||
|
{3.14, false},
|
||||||
|
{96, true},
|
||||||
|
{-10e-12, false},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := IsNatural(test.param)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected IsNatural(%v) to be %v, got %v", test.param, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestInRange(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param float64
|
||||||
|
left float64
|
||||||
|
right float64
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{0, 0, 0, true},
|
||||||
|
{1, 0, 0, false},
|
||||||
|
{-1, 0, 0, false},
|
||||||
|
{0, -1, 1, true},
|
||||||
|
{0, 0, 1, true},
|
||||||
|
{0, -1, 0, true},
|
||||||
|
{0, 0, -1, true},
|
||||||
|
{0, 10, 5, false},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := InRange(test.param, test.left, test.right)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected InRange(%v, %v, %v) to be %v, got %v", test.param, test.left, test.right, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
91
vendor/github.com/asaskevich/govalidator/patterns.go
generated
vendored
Normal file
91
vendor/github.com/asaskevich/govalidator/patterns.go
generated
vendored
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package govalidator
|
||||||
|
|
||||||
|
import "regexp"
|
||||||
|
|
||||||
|
// Basic regular expressions for validating strings
|
||||||
|
const (
|
||||||
|
Email string = "^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
|
||||||
|
CreditCard string = "^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11})$"
|
||||||
|
ISBN10 string = "^(?:[0-9]{9}X|[0-9]{10})$"
|
||||||
|
ISBN13 string = "^(?:[0-9]{13})$"
|
||||||
|
UUID3 string = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"
|
||||||
|
UUID4 string = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
||||||
|
UUID5 string = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
||||||
|
UUID string = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
|
||||||
|
Alpha string = "^[a-zA-Z]+$"
|
||||||
|
Alphanumeric string = "^[a-zA-Z0-9]+$"
|
||||||
|
Numeric string = "^[0-9]+$"
|
||||||
|
Int string = "^(?:[-+]?(?:0|[1-9][0-9]*))$"
|
||||||
|
Float string = "^(?:[-+]?(?:[0-9]+))?(?:\\.[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$"
|
||||||
|
Hexadecimal string = "^[0-9a-fA-F]+$"
|
||||||
|
Hexcolor string = "^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
|
||||||
|
RGBcolor string = "^rgb\\(\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*\\)$"
|
||||||
|
ASCII string = "^[\x00-\x7F]+$"
|
||||||
|
Multibyte string = "[^\x00-\x7F]"
|
||||||
|
FullWidth string = "[^\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]"
|
||||||
|
HalfWidth string = "[\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]"
|
||||||
|
Base64 string = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
|
||||||
|
PrintableASCII string = "^[\x20-\x7E]+$"
|
||||||
|
DataURI string = "^data:.+\\/(.+);base64$"
|
||||||
|
Latitude string = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
|
||||||
|
Longitude string = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
|
||||||
|
DNSName string = `^([a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*[\._]?$`
|
||||||
|
IP string = `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))`
|
||||||
|
URLSchema string = `((ftp|tcp|udp|wss?|https?):\/\/)`
|
||||||
|
URLUsername string = `(\S+(:\S*)?@)`
|
||||||
|
Hostname string = ``
|
||||||
|
URLPath string = `((\/|\?|#)[^\s]*)`
|
||||||
|
URLPort string = `(:(\d{1,5}))`
|
||||||
|
URLIP string = `([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))`
|
||||||
|
URLSubdomain string = `((www\.)|([a-zA-Z0-9]([-\.][-\._a-zA-Z0-9]+)*))`
|
||||||
|
URL string = `^` + URLSchema + `?` + URLUsername + `?` + `((` + URLIP + `|(\[` + IP + `\])|(([a-zA-Z0-9]([a-zA-Z0-9-_]+)?[a-zA-Z0-9]([-\.][a-zA-Z0-9]+)*)|(` + URLSubdomain + `?))?(([a-zA-Z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-zA-Z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-zA-Z\x{00a1}-\x{ffff}]{1,}))?))\.?` + URLPort + `?` + URLPath + `?$`
|
||||||
|
SSN string = `^\d{3}[- ]?\d{2}[- ]?\d{4}$`
|
||||||
|
WinPath string = `^[a-zA-Z]:\\(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*$`
|
||||||
|
UnixPath string = `^(/[^/\x00]*)+/?$`
|
||||||
|
Semver string = "^v?(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(-(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(\\.(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\\+[0-9a-zA-Z-]+(\\.[0-9a-zA-Z-]+)*)?$"
|
||||||
|
tagName string = "valid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Used by IsFilePath func
|
||||||
|
const (
|
||||||
|
// Unknown is unresolved OS type
|
||||||
|
Unknown = iota
|
||||||
|
// Win is Windows type
|
||||||
|
Win
|
||||||
|
// Unix is *nix OS types
|
||||||
|
Unix
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
rxEmail = regexp.MustCompile(Email)
|
||||||
|
rxCreditCard = regexp.MustCompile(CreditCard)
|
||||||
|
rxISBN10 = regexp.MustCompile(ISBN10)
|
||||||
|
rxISBN13 = regexp.MustCompile(ISBN13)
|
||||||
|
rxUUID3 = regexp.MustCompile(UUID3)
|
||||||
|
rxUUID4 = regexp.MustCompile(UUID4)
|
||||||
|
rxUUID5 = regexp.MustCompile(UUID5)
|
||||||
|
rxUUID = regexp.MustCompile(UUID)
|
||||||
|
rxAlpha = regexp.MustCompile(Alpha)
|
||||||
|
rxAlphanumeric = regexp.MustCompile(Alphanumeric)
|
||||||
|
rxNumeric = regexp.MustCompile(Numeric)
|
||||||
|
rxInt = regexp.MustCompile(Int)
|
||||||
|
rxFloat = regexp.MustCompile(Float)
|
||||||
|
rxHexadecimal = regexp.MustCompile(Hexadecimal)
|
||||||
|
rxHexcolor = regexp.MustCompile(Hexcolor)
|
||||||
|
rxRGBcolor = regexp.MustCompile(RGBcolor)
|
||||||
|
rxASCII = regexp.MustCompile(ASCII)
|
||||||
|
rxPrintableASCII = regexp.MustCompile(PrintableASCII)
|
||||||
|
rxMultibyte = regexp.MustCompile(Multibyte)
|
||||||
|
rxFullWidth = regexp.MustCompile(FullWidth)
|
||||||
|
rxHalfWidth = regexp.MustCompile(HalfWidth)
|
||||||
|
rxBase64 = regexp.MustCompile(Base64)
|
||||||
|
rxDataURI = regexp.MustCompile(DataURI)
|
||||||
|
rxLatitude = regexp.MustCompile(Latitude)
|
||||||
|
rxLongitude = regexp.MustCompile(Longitude)
|
||||||
|
rxDNSName = regexp.MustCompile(DNSName)
|
||||||
|
rxURL = regexp.MustCompile(URL)
|
||||||
|
rxSSN = regexp.MustCompile(SSN)
|
||||||
|
rxWinPath = regexp.MustCompile(WinPath)
|
||||||
|
rxUnixPath = regexp.MustCompile(UnixPath)
|
||||||
|
rxSemver = regexp.MustCompile(Semver)
|
||||||
|
)
|
613
vendor/github.com/asaskevich/govalidator/types.go
generated
vendored
Normal file
613
vendor/github.com/asaskevich/govalidator/types.go
generated
vendored
Normal file
|
@ -0,0 +1,613 @@
|
||||||
|
package govalidator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Validator is a wrapper for a validator function that returns bool and accepts string.
|
||||||
|
type Validator func(str string) bool
|
||||||
|
|
||||||
|
// CustomTypeValidator is a wrapper for validator functions that returns bool and accepts any type.
|
||||||
|
// The second parameter should be the context (in the case of validating a struct: the whole object being validated).
|
||||||
|
type CustomTypeValidator func(i interface{}, o interface{}) bool
|
||||||
|
|
||||||
|
// ParamValidator is a wrapper for validator functions that accepts additional parameters.
|
||||||
|
type ParamValidator func(str string, params ...string) bool
|
||||||
|
type tagOptionsMap map[string]string
|
||||||
|
|
||||||
|
// UnsupportedTypeError is a wrapper for reflect.Type
|
||||||
|
type UnsupportedTypeError struct {
|
||||||
|
Type reflect.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// stringValues is a slice of reflect.Value holding *reflect.StringValue.
|
||||||
|
// It implements the methods to sort by string.
|
||||||
|
type stringValues []reflect.Value
|
||||||
|
|
||||||
|
// ParamTagMap is a map of functions accept variants parameters
|
||||||
|
var ParamTagMap = map[string]ParamValidator{
|
||||||
|
"length": ByteLength,
|
||||||
|
"range": Range,
|
||||||
|
"runelength": RuneLength,
|
||||||
|
"stringlength": StringLength,
|
||||||
|
"matches": StringMatches,
|
||||||
|
"in": isInRaw,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParamTagRegexMap maps param tags to their respective regexes.
|
||||||
|
var ParamTagRegexMap = map[string]*regexp.Regexp{
|
||||||
|
"range": regexp.MustCompile("^range\\((\\d+)\\|(\\d+)\\)$"),
|
||||||
|
"length": regexp.MustCompile("^length\\((\\d+)\\|(\\d+)\\)$"),
|
||||||
|
"runelength": regexp.MustCompile("^runelength\\((\\d+)\\|(\\d+)\\)$"),
|
||||||
|
"stringlength": regexp.MustCompile("^stringlength\\((\\d+)\\|(\\d+)\\)$"),
|
||||||
|
"in": regexp.MustCompile(`^in\((.*)\)`),
|
||||||
|
"matches": regexp.MustCompile(`^matches\((.+)\)$`),
|
||||||
|
}
|
||||||
|
|
||||||
|
type customTypeTagMap struct {
|
||||||
|
validators map[string]CustomTypeValidator
|
||||||
|
|
||||||
|
sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *customTypeTagMap) Get(name string) (CustomTypeValidator, bool) {
|
||||||
|
tm.RLock()
|
||||||
|
defer tm.RUnlock()
|
||||||
|
v, ok := tm.validators[name]
|
||||||
|
return v, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *customTypeTagMap) Set(name string, ctv CustomTypeValidator) {
|
||||||
|
tm.Lock()
|
||||||
|
defer tm.Unlock()
|
||||||
|
tm.validators[name] = ctv
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomTypeTagMap is a map of functions that can be used as tags for ValidateStruct function.
|
||||||
|
// Use this to validate compound or custom types that need to be handled as a whole, e.g.
|
||||||
|
// `type UUID [16]byte` (this would be handled as an array of bytes).
|
||||||
|
var CustomTypeTagMap = &customTypeTagMap{validators: make(map[string]CustomTypeValidator)}
|
||||||
|
|
||||||
|
// TagMap is a map of functions, that can be used as tags for ValidateStruct function.
|
||||||
|
var TagMap = map[string]Validator{
|
||||||
|
"email": IsEmail,
|
||||||
|
"url": IsURL,
|
||||||
|
"dialstring": IsDialString,
|
||||||
|
"requrl": IsRequestURL,
|
||||||
|
"requri": IsRequestURI,
|
||||||
|
"alpha": IsAlpha,
|
||||||
|
"utfletter": IsUTFLetter,
|
||||||
|
"alphanum": IsAlphanumeric,
|
||||||
|
"utfletternum": IsUTFLetterNumeric,
|
||||||
|
"numeric": IsNumeric,
|
||||||
|
"utfnumeric": IsUTFNumeric,
|
||||||
|
"utfdigit": IsUTFDigit,
|
||||||
|
"hexadecimal": IsHexadecimal,
|
||||||
|
"hexcolor": IsHexcolor,
|
||||||
|
"rgbcolor": IsRGBcolor,
|
||||||
|
"lowercase": IsLowerCase,
|
||||||
|
"uppercase": IsUpperCase,
|
||||||
|
"int": IsInt,
|
||||||
|
"float": IsFloat,
|
||||||
|
"null": IsNull,
|
||||||
|
"uuid": IsUUID,
|
||||||
|
"uuidv3": IsUUIDv3,
|
||||||
|
"uuidv4": IsUUIDv4,
|
||||||
|
"uuidv5": IsUUIDv5,
|
||||||
|
"creditcard": IsCreditCard,
|
||||||
|
"isbn10": IsISBN10,
|
||||||
|
"isbn13": IsISBN13,
|
||||||
|
"json": IsJSON,
|
||||||
|
"multibyte": IsMultibyte,
|
||||||
|
"ascii": IsASCII,
|
||||||
|
"printableascii": IsPrintableASCII,
|
||||||
|
"fullwidth": IsFullWidth,
|
||||||
|
"halfwidth": IsHalfWidth,
|
||||||
|
"variablewidth": IsVariableWidth,
|
||||||
|
"base64": IsBase64,
|
||||||
|
"datauri": IsDataURI,
|
||||||
|
"ip": IsIP,
|
||||||
|
"port": IsPort,
|
||||||
|
"ipv4": IsIPv4,
|
||||||
|
"ipv6": IsIPv6,
|
||||||
|
"dns": IsDNSName,
|
||||||
|
"host": IsHost,
|
||||||
|
"mac": IsMAC,
|
||||||
|
"latitude": IsLatitude,
|
||||||
|
"longitude": IsLongitude,
|
||||||
|
"ssn": IsSSN,
|
||||||
|
"semver": IsSemver,
|
||||||
|
"rfc3339": IsRFC3339,
|
||||||
|
"ISO3166Alpha2": IsISO3166Alpha2,
|
||||||
|
"ISO3166Alpha3": IsISO3166Alpha3,
|
||||||
|
"ISO4217": IsISO4217,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ISO3166Entry stores country codes
|
||||||
|
type ISO3166Entry struct {
|
||||||
|
EnglishShortName string
|
||||||
|
FrenchShortName string
|
||||||
|
Alpha2Code string
|
||||||
|
Alpha3Code string
|
||||||
|
Numeric string
|
||||||
|
}
|
||||||
|
|
||||||
|
//ISO3166List based on https://www.iso.org/obp/ui/#search/code/ Code Type "Officially Assigned Codes"
|
||||||
|
var ISO3166List = []ISO3166Entry{
|
||||||
|
{"Afghanistan", "Afghanistan (l')", "AF", "AFG", "004"},
|
||||||
|
{"Albania", "Albanie (l')", "AL", "ALB", "008"},
|
||||||
|
{"Antarctica", "Antarctique (l')", "AQ", "ATA", "010"},
|
||||||
|
{"Algeria", "Algérie (l')", "DZ", "DZA", "012"},
|
||||||
|
{"American Samoa", "Samoa américaines (les)", "AS", "ASM", "016"},
|
||||||
|
{"Andorra", "Andorre (l')", "AD", "AND", "020"},
|
||||||
|
{"Angola", "Angola (l')", "AO", "AGO", "024"},
|
||||||
|
{"Antigua and Barbuda", "Antigua-et-Barbuda", "AG", "ATG", "028"},
|
||||||
|
{"Azerbaijan", "Azerbaïdjan (l')", "AZ", "AZE", "031"},
|
||||||
|
{"Argentina", "Argentine (l')", "AR", "ARG", "032"},
|
||||||
|
{"Australia", "Australie (l')", "AU", "AUS", "036"},
|
||||||
|
{"Austria", "Autriche (l')", "AT", "AUT", "040"},
|
||||||
|
{"Bahamas (the)", "Bahamas (les)", "BS", "BHS", "044"},
|
||||||
|
{"Bahrain", "Bahreïn", "BH", "BHR", "048"},
|
||||||
|
{"Bangladesh", "Bangladesh (le)", "BD", "BGD", "050"},
|
||||||
|
{"Armenia", "Arménie (l')", "AM", "ARM", "051"},
|
||||||
|
{"Barbados", "Barbade (la)", "BB", "BRB", "052"},
|
||||||
|
{"Belgium", "Belgique (la)", "BE", "BEL", "056"},
|
||||||
|
{"Bermuda", "Bermudes (les)", "BM", "BMU", "060"},
|
||||||
|
{"Bhutan", "Bhoutan (le)", "BT", "BTN", "064"},
|
||||||
|
{"Bolivia (Plurinational State of)", "Bolivie (État plurinational de)", "BO", "BOL", "068"},
|
||||||
|
{"Bosnia and Herzegovina", "Bosnie-Herzégovine (la)", "BA", "BIH", "070"},
|
||||||
|
{"Botswana", "Botswana (le)", "BW", "BWA", "072"},
|
||||||
|
{"Bouvet Island", "Bouvet (l'Île)", "BV", "BVT", "074"},
|
||||||
|
{"Brazil", "Brésil (le)", "BR", "BRA", "076"},
|
||||||
|
{"Belize", "Belize (le)", "BZ", "BLZ", "084"},
|
||||||
|
{"British Indian Ocean Territory (the)", "Indien (le Territoire britannique de l'océan)", "IO", "IOT", "086"},
|
||||||
|
{"Solomon Islands", "Salomon (Îles)", "SB", "SLB", "090"},
|
||||||
|
{"Virgin Islands (British)", "Vierges britanniques (les Îles)", "VG", "VGB", "092"},
|
||||||
|
{"Brunei Darussalam", "Brunéi Darussalam (le)", "BN", "BRN", "096"},
|
||||||
|
{"Bulgaria", "Bulgarie (la)", "BG", "BGR", "100"},
|
||||||
|
{"Myanmar", "Myanmar (le)", "MM", "MMR", "104"},
|
||||||
|
{"Burundi", "Burundi (le)", "BI", "BDI", "108"},
|
||||||
|
{"Belarus", "Bélarus (le)", "BY", "BLR", "112"},
|
||||||
|
{"Cambodia", "Cambodge (le)", "KH", "KHM", "116"},
|
||||||
|
{"Cameroon", "Cameroun (le)", "CM", "CMR", "120"},
|
||||||
|
{"Canada", "Canada (le)", "CA", "CAN", "124"},
|
||||||
|
{"Cabo Verde", "Cabo Verde", "CV", "CPV", "132"},
|
||||||
|
{"Cayman Islands (the)", "Caïmans (les Îles)", "KY", "CYM", "136"},
|
||||||
|
{"Central African Republic (the)", "République centrafricaine (la)", "CF", "CAF", "140"},
|
||||||
|
{"Sri Lanka", "Sri Lanka", "LK", "LKA", "144"},
|
||||||
|
{"Chad", "Tchad (le)", "TD", "TCD", "148"},
|
||||||
|
{"Chile", "Chili (le)", "CL", "CHL", "152"},
|
||||||
|
{"China", "Chine (la)", "CN", "CHN", "156"},
|
||||||
|
{"Taiwan (Province of China)", "Taïwan (Province de Chine)", "TW", "TWN", "158"},
|
||||||
|
{"Christmas Island", "Christmas (l'Île)", "CX", "CXR", "162"},
|
||||||
|
{"Cocos (Keeling) Islands (the)", "Cocos (les Îles)/ Keeling (les Îles)", "CC", "CCK", "166"},
|
||||||
|
{"Colombia", "Colombie (la)", "CO", "COL", "170"},
|
||||||
|
{"Comoros (the)", "Comores (les)", "KM", "COM", "174"},
|
||||||
|
{"Mayotte", "Mayotte", "YT", "MYT", "175"},
|
||||||
|
{"Congo (the)", "Congo (le)", "CG", "COG", "178"},
|
||||||
|
{"Congo (the Democratic Republic of the)", "Congo (la République démocratique du)", "CD", "COD", "180"},
|
||||||
|
{"Cook Islands (the)", "Cook (les Îles)", "CK", "COK", "184"},
|
||||||
|
{"Costa Rica", "Costa Rica (le)", "CR", "CRI", "188"},
|
||||||
|
{"Croatia", "Croatie (la)", "HR", "HRV", "191"},
|
||||||
|
{"Cuba", "Cuba", "CU", "CUB", "192"},
|
||||||
|
{"Cyprus", "Chypre", "CY", "CYP", "196"},
|
||||||
|
{"Czech Republic (the)", "tchèque (la République)", "CZ", "CZE", "203"},
|
||||||
|
{"Benin", "Bénin (le)", "BJ", "BEN", "204"},
|
||||||
|
{"Denmark", "Danemark (le)", "DK", "DNK", "208"},
|
||||||
|
{"Dominica", "Dominique (la)", "DM", "DMA", "212"},
|
||||||
|
{"Dominican Republic (the)", "dominicaine (la République)", "DO", "DOM", "214"},
|
||||||
|
{"Ecuador", "Équateur (l')", "EC", "ECU", "218"},
|
||||||
|
{"El Salvador", "El Salvador", "SV", "SLV", "222"},
|
||||||
|
{"Equatorial Guinea", "Guinée équatoriale (la)", "GQ", "GNQ", "226"},
|
||||||
|
{"Ethiopia", "Éthiopie (l')", "ET", "ETH", "231"},
|
||||||
|
{"Eritrea", "Érythrée (l')", "ER", "ERI", "232"},
|
||||||
|
{"Estonia", "Estonie (l')", "EE", "EST", "233"},
|
||||||
|
{"Faroe Islands (the)", "Féroé (les Îles)", "FO", "FRO", "234"},
|
||||||
|
{"Falkland Islands (the) [Malvinas]", "Falkland (les Îles)/Malouines (les Îles)", "FK", "FLK", "238"},
|
||||||
|
{"South Georgia and the South Sandwich Islands", "Géorgie du Sud-et-les Îles Sandwich du Sud (la)", "GS", "SGS", "239"},
|
||||||
|
{"Fiji", "Fidji (les)", "FJ", "FJI", "242"},
|
||||||
|
{"Finland", "Finlande (la)", "FI", "FIN", "246"},
|
||||||
|
{"Åland Islands", "Åland(les Îles)", "AX", "ALA", "248"},
|
||||||
|
{"France", "France (la)", "FR", "FRA", "250"},
|
||||||
|
{"French Guiana", "Guyane française (la )", "GF", "GUF", "254"},
|
||||||
|
{"French Polynesia", "Polynésie française (la)", "PF", "PYF", "258"},
|
||||||
|
{"French Southern Territories (the)", "Terres australes françaises (les)", "TF", "ATF", "260"},
|
||||||
|
{"Djibouti", "Djibouti", "DJ", "DJI", "262"},
|
||||||
|
{"Gabon", "Gabon (le)", "GA", "GAB", "266"},
|
||||||
|
{"Georgia", "Géorgie (la)", "GE", "GEO", "268"},
|
||||||
|
{"Gambia (the)", "Gambie (la)", "GM", "GMB", "270"},
|
||||||
|
{"Palestine, State of", "Palestine, État de", "PS", "PSE", "275"},
|
||||||
|
{"Germany", "Allemagne (l')", "DE", "DEU", "276"},
|
||||||
|
{"Ghana", "Ghana (le)", "GH", "GHA", "288"},
|
||||||
|
{"Gibraltar", "Gibraltar", "GI", "GIB", "292"},
|
||||||
|
{"Kiribati", "Kiribati", "KI", "KIR", "296"},
|
||||||
|
{"Greece", "Grèce (la)", "GR", "GRC", "300"},
|
||||||
|
{"Greenland", "Groenland (le)", "GL", "GRL", "304"},
|
||||||
|
{"Grenada", "Grenade (la)", "GD", "GRD", "308"},
|
||||||
|
{"Guadeloupe", "Guadeloupe (la)", "GP", "GLP", "312"},
|
||||||
|
{"Guam", "Guam", "GU", "GUM", "316"},
|
||||||
|
{"Guatemala", "Guatemala (le)", "GT", "GTM", "320"},
|
||||||
|
{"Guinea", "Guinée (la)", "GN", "GIN", "324"},
|
||||||
|
{"Guyana", "Guyana (le)", "GY", "GUY", "328"},
|
||||||
|
{"Haiti", "Haïti", "HT", "HTI", "332"},
|
||||||
|
{"Heard Island and McDonald Islands", "Heard-et-Îles MacDonald (l'Île)", "HM", "HMD", "334"},
|
||||||
|
{"Holy See (the)", "Saint-Siège (le)", "VA", "VAT", "336"},
|
||||||
|
{"Honduras", "Honduras (le)", "HN", "HND", "340"},
|
||||||
|
{"Hong Kong", "Hong Kong", "HK", "HKG", "344"},
|
||||||
|
{"Hungary", "Hongrie (la)", "HU", "HUN", "348"},
|
||||||
|
{"Iceland", "Islande (l')", "IS", "ISL", "352"},
|
||||||
|
{"India", "Inde (l')", "IN", "IND", "356"},
|
||||||
|
{"Indonesia", "Indonésie (l')", "ID", "IDN", "360"},
|
||||||
|
{"Iran (Islamic Republic of)", "Iran (République Islamique d')", "IR", "IRN", "364"},
|
||||||
|
{"Iraq", "Iraq (l')", "IQ", "IRQ", "368"},
|
||||||
|
{"Ireland", "Irlande (l')", "IE", "IRL", "372"},
|
||||||
|
{"Israel", "Israël", "IL", "ISR", "376"},
|
||||||
|
{"Italy", "Italie (l')", "IT", "ITA", "380"},
|
||||||
|
{"Côte d'Ivoire", "Côte d'Ivoire (la)", "CI", "CIV", "384"},
|
||||||
|
{"Jamaica", "Jamaïque (la)", "JM", "JAM", "388"},
|
||||||
|
{"Japan", "Japon (le)", "JP", "JPN", "392"},
|
||||||
|
{"Kazakhstan", "Kazakhstan (le)", "KZ", "KAZ", "398"},
|
||||||
|
{"Jordan", "Jordanie (la)", "JO", "JOR", "400"},
|
||||||
|
{"Kenya", "Kenya (le)", "KE", "KEN", "404"},
|
||||||
|
{"Korea (the Democratic People's Republic of)", "Corée (la République populaire démocratique de)", "KP", "PRK", "408"},
|
||||||
|
{"Korea (the Republic of)", "Corée (la République de)", "KR", "KOR", "410"},
|
||||||
|
{"Kuwait", "Koweït (le)", "KW", "KWT", "414"},
|
||||||
|
{"Kyrgyzstan", "Kirghizistan (le)", "KG", "KGZ", "417"},
|
||||||
|
{"Lao People's Democratic Republic (the)", "Lao, République démocratique populaire", "LA", "LAO", "418"},
|
||||||
|
{"Lebanon", "Liban (le)", "LB", "LBN", "422"},
|
||||||
|
{"Lesotho", "Lesotho (le)", "LS", "LSO", "426"},
|
||||||
|
{"Latvia", "Lettonie (la)", "LV", "LVA", "428"},
|
||||||
|
{"Liberia", "Libéria (le)", "LR", "LBR", "430"},
|
||||||
|
{"Libya", "Libye (la)", "LY", "LBY", "434"},
|
||||||
|
{"Liechtenstein", "Liechtenstein (le)", "LI", "LIE", "438"},
|
||||||
|
{"Lithuania", "Lituanie (la)", "LT", "LTU", "440"},
|
||||||
|
{"Luxembourg", "Luxembourg (le)", "LU", "LUX", "442"},
|
||||||
|
{"Macao", "Macao", "MO", "MAC", "446"},
|
||||||
|
{"Madagascar", "Madagascar", "MG", "MDG", "450"},
|
||||||
|
{"Malawi", "Malawi (le)", "MW", "MWI", "454"},
|
||||||
|
{"Malaysia", "Malaisie (la)", "MY", "MYS", "458"},
|
||||||
|
{"Maldives", "Maldives (les)", "MV", "MDV", "462"},
|
||||||
|
{"Mali", "Mali (le)", "ML", "MLI", "466"},
|
||||||
|
{"Malta", "Malte", "MT", "MLT", "470"},
|
||||||
|
{"Martinique", "Martinique (la)", "MQ", "MTQ", "474"},
|
||||||
|
{"Mauritania", "Mauritanie (la)", "MR", "MRT", "478"},
|
||||||
|
{"Mauritius", "Maurice", "MU", "MUS", "480"},
|
||||||
|
{"Mexico", "Mexique (le)", "MX", "MEX", "484"},
|
||||||
|
{"Monaco", "Monaco", "MC", "MCO", "492"},
|
||||||
|
{"Mongolia", "Mongolie (la)", "MN", "MNG", "496"},
|
||||||
|
{"Moldova (the Republic of)", "Moldova , République de", "MD", "MDA", "498"},
|
||||||
|
{"Montenegro", "Monténégro (le)", "ME", "MNE", "499"},
|
||||||
|
{"Montserrat", "Montserrat", "MS", "MSR", "500"},
|
||||||
|
{"Morocco", "Maroc (le)", "MA", "MAR", "504"},
|
||||||
|
{"Mozambique", "Mozambique (le)", "MZ", "MOZ", "508"},
|
||||||
|
{"Oman", "Oman", "OM", "OMN", "512"},
|
||||||
|
{"Namibia", "Namibie (la)", "NA", "NAM", "516"},
|
||||||
|
{"Nauru", "Nauru", "NR", "NRU", "520"},
|
||||||
|
{"Nepal", "Népal (le)", "NP", "NPL", "524"},
|
||||||
|
{"Netherlands (the)", "Pays-Bas (les)", "NL", "NLD", "528"},
|
||||||
|
{"Curaçao", "Curaçao", "CW", "CUW", "531"},
|
||||||
|
{"Aruba", "Aruba", "AW", "ABW", "533"},
|
||||||
|
{"Sint Maarten (Dutch part)", "Saint-Martin (partie néerlandaise)", "SX", "SXM", "534"},
|
||||||
|
{"Bonaire, Sint Eustatius and Saba", "Bonaire, Saint-Eustache et Saba", "BQ", "BES", "535"},
|
||||||
|
{"New Caledonia", "Nouvelle-Calédonie (la)", "NC", "NCL", "540"},
|
||||||
|
{"Vanuatu", "Vanuatu (le)", "VU", "VUT", "548"},
|
||||||
|
{"New Zealand", "Nouvelle-Zélande (la)", "NZ", "NZL", "554"},
|
||||||
|
{"Nicaragua", "Nicaragua (le)", "NI", "NIC", "558"},
|
||||||
|
{"Niger (the)", "Niger (le)", "NE", "NER", "562"},
|
||||||
|
{"Nigeria", "Nigéria (le)", "NG", "NGA", "566"},
|
||||||
|
{"Niue", "Niue", "NU", "NIU", "570"},
|
||||||
|
{"Norfolk Island", "Norfolk (l'Île)", "NF", "NFK", "574"},
|
||||||
|
{"Norway", "Norvège (la)", "NO", "NOR", "578"},
|
||||||
|
{"Northern Mariana Islands (the)", "Mariannes du Nord (les Îles)", "MP", "MNP", "580"},
|
||||||
|
{"United States Minor Outlying Islands (the)", "Îles mineures éloignées des États-Unis (les)", "UM", "UMI", "581"},
|
||||||
|
{"Micronesia (Federated States of)", "Micronésie (États fédérés de)", "FM", "FSM", "583"},
|
||||||
|
{"Marshall Islands (the)", "Marshall (Îles)", "MH", "MHL", "584"},
|
||||||
|
{"Palau", "Palaos (les)", "PW", "PLW", "585"},
|
||||||
|
{"Pakistan", "Pakistan (le)", "PK", "PAK", "586"},
|
||||||
|
{"Panama", "Panama (le)", "PA", "PAN", "591"},
|
||||||
|
{"Papua New Guinea", "Papouasie-Nouvelle-Guinée (la)", "PG", "PNG", "598"},
|
||||||
|
{"Paraguay", "Paraguay (le)", "PY", "PRY", "600"},
|
||||||
|
{"Peru", "Pérou (le)", "PE", "PER", "604"},
|
||||||
|
{"Philippines (the)", "Philippines (les)", "PH", "PHL", "608"},
|
||||||
|
{"Pitcairn", "Pitcairn", "PN", "PCN", "612"},
|
||||||
|
{"Poland", "Pologne (la)", "PL", "POL", "616"},
|
||||||
|
{"Portugal", "Portugal (le)", "PT", "PRT", "620"},
|
||||||
|
{"Guinea-Bissau", "Guinée-Bissau (la)", "GW", "GNB", "624"},
|
||||||
|
{"Timor-Leste", "Timor-Leste (le)", "TL", "TLS", "626"},
|
||||||
|
{"Puerto Rico", "Porto Rico", "PR", "PRI", "630"},
|
||||||
|
{"Qatar", "Qatar (le)", "QA", "QAT", "634"},
|
||||||
|
{"Réunion", "Réunion (La)", "RE", "REU", "638"},
|
||||||
|
{"Romania", "Roumanie (la)", "RO", "ROU", "642"},
|
||||||
|
{"Russian Federation (the)", "Russie (la Fédération de)", "RU", "RUS", "643"},
|
||||||
|
{"Rwanda", "Rwanda (le)", "RW", "RWA", "646"},
|
||||||
|
{"Saint Barthélemy", "Saint-Barthélemy", "BL", "BLM", "652"},
|
||||||
|
{"Saint Helena, Ascension and Tristan da Cunha", "Sainte-Hélène, Ascension et Tristan da Cunha", "SH", "SHN", "654"},
|
||||||
|
{"Saint Kitts and Nevis", "Saint-Kitts-et-Nevis", "KN", "KNA", "659"},
|
||||||
|
{"Anguilla", "Anguilla", "AI", "AIA", "660"},
|
||||||
|
{"Saint Lucia", "Sainte-Lucie", "LC", "LCA", "662"},
|
||||||
|
{"Saint Martin (French part)", "Saint-Martin (partie française)", "MF", "MAF", "663"},
|
||||||
|
{"Saint Pierre and Miquelon", "Saint-Pierre-et-Miquelon", "PM", "SPM", "666"},
|
||||||
|
{"Saint Vincent and the Grenadines", "Saint-Vincent-et-les Grenadines", "VC", "VCT", "670"},
|
||||||
|
{"San Marino", "Saint-Marin", "SM", "SMR", "674"},
|
||||||
|
{"Sao Tome and Principe", "Sao Tomé-et-Principe", "ST", "STP", "678"},
|
||||||
|
{"Saudi Arabia", "Arabie saoudite (l')", "SA", "SAU", "682"},
|
||||||
|
{"Senegal", "Sénégal (le)", "SN", "SEN", "686"},
|
||||||
|
{"Serbia", "Serbie (la)", "RS", "SRB", "688"},
|
||||||
|
{"Seychelles", "Seychelles (les)", "SC", "SYC", "690"},
|
||||||
|
{"Sierra Leone", "Sierra Leone (la)", "SL", "SLE", "694"},
|
||||||
|
{"Singapore", "Singapour", "SG", "SGP", "702"},
|
||||||
|
{"Slovakia", "Slovaquie (la)", "SK", "SVK", "703"},
|
||||||
|
{"Viet Nam", "Viet Nam (le)", "VN", "VNM", "704"},
|
||||||
|
{"Slovenia", "Slovénie (la)", "SI", "SVN", "705"},
|
||||||
|
{"Somalia", "Somalie (la)", "SO", "SOM", "706"},
|
||||||
|
{"South Africa", "Afrique du Sud (l')", "ZA", "ZAF", "710"},
|
||||||
|
{"Zimbabwe", "Zimbabwe (le)", "ZW", "ZWE", "716"},
|
||||||
|
{"Spain", "Espagne (l')", "ES", "ESP", "724"},
|
||||||
|
{"South Sudan", "Soudan du Sud (le)", "SS", "SSD", "728"},
|
||||||
|
{"Sudan (the)", "Soudan (le)", "SD", "SDN", "729"},
|
||||||
|
{"Western Sahara*", "Sahara occidental (le)*", "EH", "ESH", "732"},
|
||||||
|
{"Suriname", "Suriname (le)", "SR", "SUR", "740"},
|
||||||
|
{"Svalbard and Jan Mayen", "Svalbard et l'Île Jan Mayen (le)", "SJ", "SJM", "744"},
|
||||||
|
{"Swaziland", "Swaziland (le)", "SZ", "SWZ", "748"},
|
||||||
|
{"Sweden", "Suède (la)", "SE", "SWE", "752"},
|
||||||
|
{"Switzerland", "Suisse (la)", "CH", "CHE", "756"},
|
||||||
|
{"Syrian Arab Republic", "République arabe syrienne (la)", "SY", "SYR", "760"},
|
||||||
|
{"Tajikistan", "Tadjikistan (le)", "TJ", "TJK", "762"},
|
||||||
|
{"Thailand", "Thaïlande (la)", "TH", "THA", "764"},
|
||||||
|
{"Togo", "Togo (le)", "TG", "TGO", "768"},
|
||||||
|
{"Tokelau", "Tokelau (les)", "TK", "TKL", "772"},
|
||||||
|
{"Tonga", "Tonga (les)", "TO", "TON", "776"},
|
||||||
|
{"Trinidad and Tobago", "Trinité-et-Tobago (la)", "TT", "TTO", "780"},
|
||||||
|
{"United Arab Emirates (the)", "Émirats arabes unis (les)", "AE", "ARE", "784"},
|
||||||
|
{"Tunisia", "Tunisie (la)", "TN", "TUN", "788"},
|
||||||
|
{"Turkey", "Turquie (la)", "TR", "TUR", "792"},
|
||||||
|
{"Turkmenistan", "Turkménistan (le)", "TM", "TKM", "795"},
|
||||||
|
{"Turks and Caicos Islands (the)", "Turks-et-Caïcos (les Îles)", "TC", "TCA", "796"},
|
||||||
|
{"Tuvalu", "Tuvalu (les)", "TV", "TUV", "798"},
|
||||||
|
{"Uganda", "Ouganda (l')", "UG", "UGA", "800"},
|
||||||
|
{"Ukraine", "Ukraine (l')", "UA", "UKR", "804"},
|
||||||
|
{"Macedonia (the former Yugoslav Republic of)", "Macédoine (l'ex‑République yougoslave de)", "MK", "MKD", "807"},
|
||||||
|
{"Egypt", "Égypte (l')", "EG", "EGY", "818"},
|
||||||
|
{"United Kingdom of Great Britain and Northern Ireland (the)", "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord (le)", "GB", "GBR", "826"},
|
||||||
|
{"Guernsey", "Guernesey", "GG", "GGY", "831"},
|
||||||
|
{"Jersey", "Jersey", "JE", "JEY", "832"},
|
||||||
|
{"Isle of Man", "Île de Man", "IM", "IMN", "833"},
|
||||||
|
{"Tanzania, United Republic of", "Tanzanie, République-Unie de", "TZ", "TZA", "834"},
|
||||||
|
{"United States of America (the)", "États-Unis d'Amérique (les)", "US", "USA", "840"},
|
||||||
|
{"Virgin Islands (U.S.)", "Vierges des États-Unis (les Îles)", "VI", "VIR", "850"},
|
||||||
|
{"Burkina Faso", "Burkina Faso (le)", "BF", "BFA", "854"},
|
||||||
|
{"Uruguay", "Uruguay (l')", "UY", "URY", "858"},
|
||||||
|
{"Uzbekistan", "Ouzbékistan (l')", "UZ", "UZB", "860"},
|
||||||
|
{"Venezuela (Bolivarian Republic of)", "Venezuela (République bolivarienne du)", "VE", "VEN", "862"},
|
||||||
|
{"Wallis and Futuna", "Wallis-et-Futuna", "WF", "WLF", "876"},
|
||||||
|
{"Samoa", "Samoa (le)", "WS", "WSM", "882"},
|
||||||
|
{"Yemen", "Yémen (le)", "YE", "YEM", "887"},
|
||||||
|
{"Zambia", "Zambie (la)", "ZM", "ZMB", "894"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// ISO4217List is the list of ISO currency codes
|
||||||
|
var ISO4217List = []string{
|
||||||
|
"AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN",
|
||||||
|
"BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BOV", "BRL", "BSD", "BTN", "BWP", "BYN", "BZD",
|
||||||
|
"CAD", "CDF", "CHE", "CHF", "CHW", "CLF", "CLP", "CNY", "COP", "COU", "CRC", "CUC", "CUP", "CVE", "CZK",
|
||||||
|
"DJF", "DKK", "DOP", "DZD",
|
||||||
|
"EGP", "ERN", "ETB", "EUR",
|
||||||
|
"FJD", "FKP",
|
||||||
|
"GBP", "GEL", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD",
|
||||||
|
"HKD", "HNL", "HRK", "HTG", "HUF",
|
||||||
|
"IDR", "ILS", "INR", "IQD", "IRR", "ISK",
|
||||||
|
"JMD", "JOD", "JPY",
|
||||||
|
"KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT",
|
||||||
|
"LAK", "LBP", "LKR", "LRD", "LSL", "LYD",
|
||||||
|
"MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRO", "MUR", "MVR", "MWK", "MXN", "MXV", "MYR", "MZN",
|
||||||
|
"NAD", "NGN", "NIO", "NOK", "NPR", "NZD",
|
||||||
|
"OMR",
|
||||||
|
"PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG",
|
||||||
|
"QAR",
|
||||||
|
"RON", "RSD", "RUB", "RWF",
|
||||||
|
"SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLL", "SOS", "SRD", "SSP", "STD", "SVC", "SYP", "SZL",
|
||||||
|
"THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TWD", "TZS",
|
||||||
|
"UAH", "UGX", "USD", "USN", "UYI", "UYU", "UZS",
|
||||||
|
"VEF", "VND", "VUV",
|
||||||
|
"WST",
|
||||||
|
"XAF", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "XCD", "XDR", "XOF", "XPD", "XPF", "XPT", "XSU", "XTS", "XUA", "XXX",
|
||||||
|
"YER",
|
||||||
|
"ZAR", "ZMW", "ZWL",
|
||||||
|
}
|
||||||
|
|
||||||
|
// ISO693Entry stores ISO language codes
|
||||||
|
type ISO693Entry struct {
|
||||||
|
Alpha3bCode string
|
||||||
|
Alpha2Code string
|
||||||
|
English string
|
||||||
|
}
|
||||||
|
|
||||||
|
//ISO693List based on http://data.okfn.org/data/core/language-codes/r/language-codes-3b2.json
|
||||||
|
var ISO693List = []ISO693Entry{
|
||||||
|
{Alpha3bCode: "aar", Alpha2Code: "aa", English: "Afar"},
|
||||||
|
{Alpha3bCode: "abk", Alpha2Code: "ab", English: "Abkhazian"},
|
||||||
|
{Alpha3bCode: "afr", Alpha2Code: "af", English: "Afrikaans"},
|
||||||
|
{Alpha3bCode: "aka", Alpha2Code: "ak", English: "Akan"},
|
||||||
|
{Alpha3bCode: "alb", Alpha2Code: "sq", English: "Albanian"},
|
||||||
|
{Alpha3bCode: "amh", Alpha2Code: "am", English: "Amharic"},
|
||||||
|
{Alpha3bCode: "ara", Alpha2Code: "ar", English: "Arabic"},
|
||||||
|
{Alpha3bCode: "arg", Alpha2Code: "an", English: "Aragonese"},
|
||||||
|
{Alpha3bCode: "arm", Alpha2Code: "hy", English: "Armenian"},
|
||||||
|
{Alpha3bCode: "asm", Alpha2Code: "as", English: "Assamese"},
|
||||||
|
{Alpha3bCode: "ava", Alpha2Code: "av", English: "Avaric"},
|
||||||
|
{Alpha3bCode: "ave", Alpha2Code: "ae", English: "Avestan"},
|
||||||
|
{Alpha3bCode: "aym", Alpha2Code: "ay", English: "Aymara"},
|
||||||
|
{Alpha3bCode: "aze", Alpha2Code: "az", English: "Azerbaijani"},
|
||||||
|
{Alpha3bCode: "bak", Alpha2Code: "ba", English: "Bashkir"},
|
||||||
|
{Alpha3bCode: "bam", Alpha2Code: "bm", English: "Bambara"},
|
||||||
|
{Alpha3bCode: "baq", Alpha2Code: "eu", English: "Basque"},
|
||||||
|
{Alpha3bCode: "bel", Alpha2Code: "be", English: "Belarusian"},
|
||||||
|
{Alpha3bCode: "ben", Alpha2Code: "bn", English: "Bengali"},
|
||||||
|
{Alpha3bCode: "bih", Alpha2Code: "bh", English: "Bihari languages"},
|
||||||
|
{Alpha3bCode: "bis", Alpha2Code: "bi", English: "Bislama"},
|
||||||
|
{Alpha3bCode: "bos", Alpha2Code: "bs", English: "Bosnian"},
|
||||||
|
{Alpha3bCode: "bre", Alpha2Code: "br", English: "Breton"},
|
||||||
|
{Alpha3bCode: "bul", Alpha2Code: "bg", English: "Bulgarian"},
|
||||||
|
{Alpha3bCode: "bur", Alpha2Code: "my", English: "Burmese"},
|
||||||
|
{Alpha3bCode: "cat", Alpha2Code: "ca", English: "Catalan; Valencian"},
|
||||||
|
{Alpha3bCode: "cha", Alpha2Code: "ch", English: "Chamorro"},
|
||||||
|
{Alpha3bCode: "che", Alpha2Code: "ce", English: "Chechen"},
|
||||||
|
{Alpha3bCode: "chi", Alpha2Code: "zh", English: "Chinese"},
|
||||||
|
{Alpha3bCode: "chu", Alpha2Code: "cu", English: "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic"},
|
||||||
|
{Alpha3bCode: "chv", Alpha2Code: "cv", English: "Chuvash"},
|
||||||
|
{Alpha3bCode: "cor", Alpha2Code: "kw", English: "Cornish"},
|
||||||
|
{Alpha3bCode: "cos", Alpha2Code: "co", English: "Corsican"},
|
||||||
|
{Alpha3bCode: "cre", Alpha2Code: "cr", English: "Cree"},
|
||||||
|
{Alpha3bCode: "cze", Alpha2Code: "cs", English: "Czech"},
|
||||||
|
{Alpha3bCode: "dan", Alpha2Code: "da", English: "Danish"},
|
||||||
|
{Alpha3bCode: "div", Alpha2Code: "dv", English: "Divehi; Dhivehi; Maldivian"},
|
||||||
|
{Alpha3bCode: "dut", Alpha2Code: "nl", English: "Dutch; Flemish"},
|
||||||
|
{Alpha3bCode: "dzo", Alpha2Code: "dz", English: "Dzongkha"},
|
||||||
|
{Alpha3bCode: "eng", Alpha2Code: "en", English: "English"},
|
||||||
|
{Alpha3bCode: "epo", Alpha2Code: "eo", English: "Esperanto"},
|
||||||
|
{Alpha3bCode: "est", Alpha2Code: "et", English: "Estonian"},
|
||||||
|
{Alpha3bCode: "ewe", Alpha2Code: "ee", English: "Ewe"},
|
||||||
|
{Alpha3bCode: "fao", Alpha2Code: "fo", English: "Faroese"},
|
||||||
|
{Alpha3bCode: "fij", Alpha2Code: "fj", English: "Fijian"},
|
||||||
|
{Alpha3bCode: "fin", Alpha2Code: "fi", English: "Finnish"},
|
||||||
|
{Alpha3bCode: "fre", Alpha2Code: "fr", English: "French"},
|
||||||
|
{Alpha3bCode: "fry", Alpha2Code: "fy", English: "Western Frisian"},
|
||||||
|
{Alpha3bCode: "ful", Alpha2Code: "ff", English: "Fulah"},
|
||||||
|
{Alpha3bCode: "geo", Alpha2Code: "ka", English: "Georgian"},
|
||||||
|
{Alpha3bCode: "ger", Alpha2Code: "de", English: "German"},
|
||||||
|
{Alpha3bCode: "gla", Alpha2Code: "gd", English: "Gaelic; Scottish Gaelic"},
|
||||||
|
{Alpha3bCode: "gle", Alpha2Code: "ga", English: "Irish"},
|
||||||
|
{Alpha3bCode: "glg", Alpha2Code: "gl", English: "Galician"},
|
||||||
|
{Alpha3bCode: "glv", Alpha2Code: "gv", English: "Manx"},
|
||||||
|
{Alpha3bCode: "gre", Alpha2Code: "el", English: "Greek, Modern (1453-)"},
|
||||||
|
{Alpha3bCode: "grn", Alpha2Code: "gn", English: "Guarani"},
|
||||||
|
{Alpha3bCode: "guj", Alpha2Code: "gu", English: "Gujarati"},
|
||||||
|
{Alpha3bCode: "hat", Alpha2Code: "ht", English: "Haitian; Haitian Creole"},
|
||||||
|
{Alpha3bCode: "hau", Alpha2Code: "ha", English: "Hausa"},
|
||||||
|
{Alpha3bCode: "heb", Alpha2Code: "he", English: "Hebrew"},
|
||||||
|
{Alpha3bCode: "her", Alpha2Code: "hz", English: "Herero"},
|
||||||
|
{Alpha3bCode: "hin", Alpha2Code: "hi", English: "Hindi"},
|
||||||
|
{Alpha3bCode: "hmo", Alpha2Code: "ho", English: "Hiri Motu"},
|
||||||
|
{Alpha3bCode: "hrv", Alpha2Code: "hr", English: "Croatian"},
|
||||||
|
{Alpha3bCode: "hun", Alpha2Code: "hu", English: "Hungarian"},
|
||||||
|
{Alpha3bCode: "ibo", Alpha2Code: "ig", English: "Igbo"},
|
||||||
|
{Alpha3bCode: "ice", Alpha2Code: "is", English: "Icelandic"},
|
||||||
|
{Alpha3bCode: "ido", Alpha2Code: "io", English: "Ido"},
|
||||||
|
{Alpha3bCode: "iii", Alpha2Code: "ii", English: "Sichuan Yi; Nuosu"},
|
||||||
|
{Alpha3bCode: "iku", Alpha2Code: "iu", English: "Inuktitut"},
|
||||||
|
{Alpha3bCode: "ile", Alpha2Code: "ie", English: "Interlingue; Occidental"},
|
||||||
|
{Alpha3bCode: "ina", Alpha2Code: "ia", English: "Interlingua (International Auxiliary Language Association)"},
|
||||||
|
{Alpha3bCode: "ind", Alpha2Code: "id", English: "Indonesian"},
|
||||||
|
{Alpha3bCode: "ipk", Alpha2Code: "ik", English: "Inupiaq"},
|
||||||
|
{Alpha3bCode: "ita", Alpha2Code: "it", English: "Italian"},
|
||||||
|
{Alpha3bCode: "jav", Alpha2Code: "jv", English: "Javanese"},
|
||||||
|
{Alpha3bCode: "jpn", Alpha2Code: "ja", English: "Japanese"},
|
||||||
|
{Alpha3bCode: "kal", Alpha2Code: "kl", English: "Kalaallisut; Greenlandic"},
|
||||||
|
{Alpha3bCode: "kan", Alpha2Code: "kn", English: "Kannada"},
|
||||||
|
{Alpha3bCode: "kas", Alpha2Code: "ks", English: "Kashmiri"},
|
||||||
|
{Alpha3bCode: "kau", Alpha2Code: "kr", English: "Kanuri"},
|
||||||
|
{Alpha3bCode: "kaz", Alpha2Code: "kk", English: "Kazakh"},
|
||||||
|
{Alpha3bCode: "khm", Alpha2Code: "km", English: "Central Khmer"},
|
||||||
|
{Alpha3bCode: "kik", Alpha2Code: "ki", English: "Kikuyu; Gikuyu"},
|
||||||
|
{Alpha3bCode: "kin", Alpha2Code: "rw", English: "Kinyarwanda"},
|
||||||
|
{Alpha3bCode: "kir", Alpha2Code: "ky", English: "Kirghiz; Kyrgyz"},
|
||||||
|
{Alpha3bCode: "kom", Alpha2Code: "kv", English: "Komi"},
|
||||||
|
{Alpha3bCode: "kon", Alpha2Code: "kg", English: "Kongo"},
|
||||||
|
{Alpha3bCode: "kor", Alpha2Code: "ko", English: "Korean"},
|
||||||
|
{Alpha3bCode: "kua", Alpha2Code: "kj", English: "Kuanyama; Kwanyama"},
|
||||||
|
{Alpha3bCode: "kur", Alpha2Code: "ku", English: "Kurdish"},
|
||||||
|
{Alpha3bCode: "lao", Alpha2Code: "lo", English: "Lao"},
|
||||||
|
{Alpha3bCode: "lat", Alpha2Code: "la", English: "Latin"},
|
||||||
|
{Alpha3bCode: "lav", Alpha2Code: "lv", English: "Latvian"},
|
||||||
|
{Alpha3bCode: "lim", Alpha2Code: "li", English: "Limburgan; Limburger; Limburgish"},
|
||||||
|
{Alpha3bCode: "lin", Alpha2Code: "ln", English: "Lingala"},
|
||||||
|
{Alpha3bCode: "lit", Alpha2Code: "lt", English: "Lithuanian"},
|
||||||
|
{Alpha3bCode: "ltz", Alpha2Code: "lb", English: "Luxembourgish; Letzeburgesch"},
|
||||||
|
{Alpha3bCode: "lub", Alpha2Code: "lu", English: "Luba-Katanga"},
|
||||||
|
{Alpha3bCode: "lug", Alpha2Code: "lg", English: "Ganda"},
|
||||||
|
{Alpha3bCode: "mac", Alpha2Code: "mk", English: "Macedonian"},
|
||||||
|
{Alpha3bCode: "mah", Alpha2Code: "mh", English: "Marshallese"},
|
||||||
|
{Alpha3bCode: "mal", Alpha2Code: "ml", English: "Malayalam"},
|
||||||
|
{Alpha3bCode: "mao", Alpha2Code: "mi", English: "Maori"},
|
||||||
|
{Alpha3bCode: "mar", Alpha2Code: "mr", English: "Marathi"},
|
||||||
|
{Alpha3bCode: "may", Alpha2Code: "ms", English: "Malay"},
|
||||||
|
{Alpha3bCode: "mlg", Alpha2Code: "mg", English: "Malagasy"},
|
||||||
|
{Alpha3bCode: "mlt", Alpha2Code: "mt", English: "Maltese"},
|
||||||
|
{Alpha3bCode: "mon", Alpha2Code: "mn", English: "Mongolian"},
|
||||||
|
{Alpha3bCode: "nau", Alpha2Code: "na", English: "Nauru"},
|
||||||
|
{Alpha3bCode: "nav", Alpha2Code: "nv", English: "Navajo; Navaho"},
|
||||||
|
{Alpha3bCode: "nbl", Alpha2Code: "nr", English: "Ndebele, South; South Ndebele"},
|
||||||
|
{Alpha3bCode: "nde", Alpha2Code: "nd", English: "Ndebele, North; North Ndebele"},
|
||||||
|
{Alpha3bCode: "ndo", Alpha2Code: "ng", English: "Ndonga"},
|
||||||
|
{Alpha3bCode: "nep", Alpha2Code: "ne", English: "Nepali"},
|
||||||
|
{Alpha3bCode: "nno", Alpha2Code: "nn", English: "Norwegian Nynorsk; Nynorsk, Norwegian"},
|
||||||
|
{Alpha3bCode: "nob", Alpha2Code: "nb", English: "Bokmål, Norwegian; Norwegian Bokmål"},
|
||||||
|
{Alpha3bCode: "nor", Alpha2Code: "no", English: "Norwegian"},
|
||||||
|
{Alpha3bCode: "nya", Alpha2Code: "ny", English: "Chichewa; Chewa; Nyanja"},
|
||||||
|
{Alpha3bCode: "oci", Alpha2Code: "oc", English: "Occitan (post 1500); Provençal"},
|
||||||
|
{Alpha3bCode: "oji", Alpha2Code: "oj", English: "Ojibwa"},
|
||||||
|
{Alpha3bCode: "ori", Alpha2Code: "or", English: "Oriya"},
|
||||||
|
{Alpha3bCode: "orm", Alpha2Code: "om", English: "Oromo"},
|
||||||
|
{Alpha3bCode: "oss", Alpha2Code: "os", English: "Ossetian; Ossetic"},
|
||||||
|
{Alpha3bCode: "pan", Alpha2Code: "pa", English: "Panjabi; Punjabi"},
|
||||||
|
{Alpha3bCode: "per", Alpha2Code: "fa", English: "Persian"},
|
||||||
|
{Alpha3bCode: "pli", Alpha2Code: "pi", English: "Pali"},
|
||||||
|
{Alpha3bCode: "pol", Alpha2Code: "pl", English: "Polish"},
|
||||||
|
{Alpha3bCode: "por", Alpha2Code: "pt", English: "Portuguese"},
|
||||||
|
{Alpha3bCode: "pus", Alpha2Code: "ps", English: "Pushto; Pashto"},
|
||||||
|
{Alpha3bCode: "que", Alpha2Code: "qu", English: "Quechua"},
|
||||||
|
{Alpha3bCode: "roh", Alpha2Code: "rm", English: "Romansh"},
|
||||||
|
{Alpha3bCode: "rum", Alpha2Code: "ro", English: "Romanian; Moldavian; Moldovan"},
|
||||||
|
{Alpha3bCode: "run", Alpha2Code: "rn", English: "Rundi"},
|
||||||
|
{Alpha3bCode: "rus", Alpha2Code: "ru", English: "Russian"},
|
||||||
|
{Alpha3bCode: "sag", Alpha2Code: "sg", English: "Sango"},
|
||||||
|
{Alpha3bCode: "san", Alpha2Code: "sa", English: "Sanskrit"},
|
||||||
|
{Alpha3bCode: "sin", Alpha2Code: "si", English: "Sinhala; Sinhalese"},
|
||||||
|
{Alpha3bCode: "slo", Alpha2Code: "sk", English: "Slovak"},
|
||||||
|
{Alpha3bCode: "slv", Alpha2Code: "sl", English: "Slovenian"},
|
||||||
|
{Alpha3bCode: "sme", Alpha2Code: "se", English: "Northern Sami"},
|
||||||
|
{Alpha3bCode: "smo", Alpha2Code: "sm", English: "Samoan"},
|
||||||
|
{Alpha3bCode: "sna", Alpha2Code: "sn", English: "Shona"},
|
||||||
|
{Alpha3bCode: "snd", Alpha2Code: "sd", English: "Sindhi"},
|
||||||
|
{Alpha3bCode: "som", Alpha2Code: "so", English: "Somali"},
|
||||||
|
{Alpha3bCode: "sot", Alpha2Code: "st", English: "Sotho, Southern"},
|
||||||
|
{Alpha3bCode: "spa", Alpha2Code: "es", English: "Spanish; Castilian"},
|
||||||
|
{Alpha3bCode: "srd", Alpha2Code: "sc", English: "Sardinian"},
|
||||||
|
{Alpha3bCode: "srp", Alpha2Code: "sr", English: "Serbian"},
|
||||||
|
{Alpha3bCode: "ssw", Alpha2Code: "ss", English: "Swati"},
|
||||||
|
{Alpha3bCode: "sun", Alpha2Code: "su", English: "Sundanese"},
|
||||||
|
{Alpha3bCode: "swa", Alpha2Code: "sw", English: "Swahili"},
|
||||||
|
{Alpha3bCode: "swe", Alpha2Code: "sv", English: "Swedish"},
|
||||||
|
{Alpha3bCode: "tah", Alpha2Code: "ty", English: "Tahitian"},
|
||||||
|
{Alpha3bCode: "tam", Alpha2Code: "ta", English: "Tamil"},
|
||||||
|
{Alpha3bCode: "tat", Alpha2Code: "tt", English: "Tatar"},
|
||||||
|
{Alpha3bCode: "tel", Alpha2Code: "te", English: "Telugu"},
|
||||||
|
{Alpha3bCode: "tgk", Alpha2Code: "tg", English: "Tajik"},
|
||||||
|
{Alpha3bCode: "tgl", Alpha2Code: "tl", English: "Tagalog"},
|
||||||
|
{Alpha3bCode: "tha", Alpha2Code: "th", English: "Thai"},
|
||||||
|
{Alpha3bCode: "tib", Alpha2Code: "bo", English: "Tibetan"},
|
||||||
|
{Alpha3bCode: "tir", Alpha2Code: "ti", English: "Tigrinya"},
|
||||||
|
{Alpha3bCode: "ton", Alpha2Code: "to", English: "Tonga (Tonga Islands)"},
|
||||||
|
{Alpha3bCode: "tsn", Alpha2Code: "tn", English: "Tswana"},
|
||||||
|
{Alpha3bCode: "tso", Alpha2Code: "ts", English: "Tsonga"},
|
||||||
|
{Alpha3bCode: "tuk", Alpha2Code: "tk", English: "Turkmen"},
|
||||||
|
{Alpha3bCode: "tur", Alpha2Code: "tr", English: "Turkish"},
|
||||||
|
{Alpha3bCode: "twi", Alpha2Code: "tw", English: "Twi"},
|
||||||
|
{Alpha3bCode: "uig", Alpha2Code: "ug", English: "Uighur; Uyghur"},
|
||||||
|
{Alpha3bCode: "ukr", Alpha2Code: "uk", English: "Ukrainian"},
|
||||||
|
{Alpha3bCode: "urd", Alpha2Code: "ur", English: "Urdu"},
|
||||||
|
{Alpha3bCode: "uzb", Alpha2Code: "uz", English: "Uzbek"},
|
||||||
|
{Alpha3bCode: "ven", Alpha2Code: "ve", English: "Venda"},
|
||||||
|
{Alpha3bCode: "vie", Alpha2Code: "vi", English: "Vietnamese"},
|
||||||
|
{Alpha3bCode: "vol", Alpha2Code: "vo", English: "Volapük"},
|
||||||
|
{Alpha3bCode: "wel", Alpha2Code: "cy", English: "Welsh"},
|
||||||
|
{Alpha3bCode: "wln", Alpha2Code: "wa", English: "Walloon"},
|
||||||
|
{Alpha3bCode: "wol", Alpha2Code: "wo", English: "Wolof"},
|
||||||
|
{Alpha3bCode: "xho", Alpha2Code: "xh", English: "Xhosa"},
|
||||||
|
{Alpha3bCode: "yid", Alpha2Code: "yi", English: "Yiddish"},
|
||||||
|
{Alpha3bCode: "yor", Alpha2Code: "yo", English: "Yoruba"},
|
||||||
|
{Alpha3bCode: "zha", Alpha2Code: "za", English: "Zhuang; Chuang"},
|
||||||
|
{Alpha3bCode: "zul", Alpha2Code: "zu", English: "Zulu"},
|
||||||
|
}
|
262
vendor/github.com/asaskevich/govalidator/utils.go
generated
vendored
Normal file
262
vendor/github.com/asaskevich/govalidator/utils.go
generated
vendored
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
package govalidator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"html"
|
||||||
|
"math"
|
||||||
|
"path"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Contains check if the string contains the substring.
|
||||||
|
func Contains(str, substring string) bool {
|
||||||
|
return strings.Contains(str, substring)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matches check if string matches the pattern (pattern is regular expression)
|
||||||
|
// In case of error return false
|
||||||
|
func Matches(str, pattern string) bool {
|
||||||
|
match, _ := regexp.MatchString(pattern, str)
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeftTrim trim characters from the left-side of the input.
|
||||||
|
// If second argument is empty, it's will be remove leading spaces.
|
||||||
|
func LeftTrim(str, chars string) string {
|
||||||
|
if chars == "" {
|
||||||
|
return strings.TrimLeftFunc(str, unicode.IsSpace)
|
||||||
|
}
|
||||||
|
r, _ := regexp.Compile("^[" + chars + "]+")
|
||||||
|
return r.ReplaceAllString(str, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RightTrim trim characters from the right-side of the input.
|
||||||
|
// If second argument is empty, it's will be remove spaces.
|
||||||
|
func RightTrim(str, chars string) string {
|
||||||
|
if chars == "" {
|
||||||
|
return strings.TrimRightFunc(str, unicode.IsSpace)
|
||||||
|
}
|
||||||
|
r, _ := regexp.Compile("[" + chars + "]+$")
|
||||||
|
return r.ReplaceAllString(str, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim trim characters from both sides of the input.
|
||||||
|
// If second argument is empty, it's will be remove spaces.
|
||||||
|
func Trim(str, chars string) string {
|
||||||
|
return LeftTrim(RightTrim(str, chars), chars)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhiteList remove characters that do not appear in the whitelist.
|
||||||
|
func WhiteList(str, chars string) string {
|
||||||
|
pattern := "[^" + chars + "]+"
|
||||||
|
r, _ := regexp.Compile(pattern)
|
||||||
|
return r.ReplaceAllString(str, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlackList remove characters that appear in the blacklist.
|
||||||
|
func BlackList(str, chars string) string {
|
||||||
|
pattern := "[" + chars + "]+"
|
||||||
|
r, _ := regexp.Compile(pattern)
|
||||||
|
return r.ReplaceAllString(str, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// StripLow remove characters with a numerical value < 32 and 127, mostly control characters.
|
||||||
|
// If keep_new_lines is true, newline characters are preserved (\n and \r, hex 0xA and 0xD).
|
||||||
|
func StripLow(str string, keepNewLines bool) string {
|
||||||
|
chars := ""
|
||||||
|
if keepNewLines {
|
||||||
|
chars = "\x00-\x09\x0B\x0C\x0E-\x1F\x7F"
|
||||||
|
} else {
|
||||||
|
chars = "\x00-\x1F\x7F"
|
||||||
|
}
|
||||||
|
return BlackList(str, chars)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplacePattern replace regular expression pattern in string
|
||||||
|
func ReplacePattern(str, pattern, replace string) string {
|
||||||
|
r, _ := regexp.Compile(pattern)
|
||||||
|
return r.ReplaceAllString(str, replace)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escape replace <, >, & and " with HTML entities.
|
||||||
|
var Escape = html.EscapeString
|
||||||
|
|
||||||
|
func addSegment(inrune, segment []rune) []rune {
|
||||||
|
if len(segment) == 0 {
|
||||||
|
return inrune
|
||||||
|
}
|
||||||
|
if len(inrune) != 0 {
|
||||||
|
inrune = append(inrune, '_')
|
||||||
|
}
|
||||||
|
inrune = append(inrune, segment...)
|
||||||
|
return inrune
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnderscoreToCamelCase converts from underscore separated form to camel case form.
|
||||||
|
// Ex.: my_func => MyFunc
|
||||||
|
func UnderscoreToCamelCase(s string) string {
|
||||||
|
return strings.Replace(strings.Title(strings.Replace(strings.ToLower(s), "_", " ", -1)), " ", "", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CamelCaseToUnderscore converts from camel case form to underscore separated form.
|
||||||
|
// Ex.: MyFunc => my_func
|
||||||
|
func CamelCaseToUnderscore(str string) string {
|
||||||
|
var output []rune
|
||||||
|
var segment []rune
|
||||||
|
for _, r := range str {
|
||||||
|
if !unicode.IsLower(r) {
|
||||||
|
output = addSegment(output, segment)
|
||||||
|
segment = nil
|
||||||
|
}
|
||||||
|
segment = append(segment, unicode.ToLower(r))
|
||||||
|
}
|
||||||
|
output = addSegment(output, segment)
|
||||||
|
return string(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse return reversed string
|
||||||
|
func Reverse(s string) string {
|
||||||
|
r := []rune(s)
|
||||||
|
for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
r[i], r[j] = r[j], r[i]
|
||||||
|
}
|
||||||
|
return string(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLines split string by "\n" and return array of lines
|
||||||
|
func GetLines(s string) []string {
|
||||||
|
return strings.Split(s, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLine return specified line of multiline string
|
||||||
|
func GetLine(s string, index int) (string, error) {
|
||||||
|
lines := GetLines(s)
|
||||||
|
if index < 0 || index >= len(lines) {
|
||||||
|
return "", errors.New("line index out of bounds")
|
||||||
|
}
|
||||||
|
return lines[index], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveTags remove all tags from HTML string
|
||||||
|
func RemoveTags(s string) string {
|
||||||
|
return ReplacePattern(s, "<[^>]*>", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SafeFileName return safe string that can be used in file names
|
||||||
|
func SafeFileName(str string) string {
|
||||||
|
name := strings.ToLower(str)
|
||||||
|
name = path.Clean(path.Base(name))
|
||||||
|
name = strings.Trim(name, " ")
|
||||||
|
separators, err := regexp.Compile(`[ &_=+:]`)
|
||||||
|
if err == nil {
|
||||||
|
name = separators.ReplaceAllString(name, "-")
|
||||||
|
}
|
||||||
|
legal, err := regexp.Compile(`[^[:alnum:]-.]`)
|
||||||
|
if err == nil {
|
||||||
|
name = legal.ReplaceAllString(name, "")
|
||||||
|
}
|
||||||
|
for strings.Contains(name, "--") {
|
||||||
|
name = strings.Replace(name, "--", "-", -1)
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormalizeEmail canonicalize an email address.
|
||||||
|
// The local part of the email address is lowercased for all domains; the hostname is always lowercased and
|
||||||
|
// the local part of the email address is always lowercased for hosts that are known to be case-insensitive (currently only GMail).
|
||||||
|
// Normalization follows special rules for known providers: currently, GMail addresses have dots removed in the local part and
|
||||||
|
// are stripped of tags (e.g. some.one+tag@gmail.com becomes someone@gmail.com) and all @googlemail.com addresses are
|
||||||
|
// normalized to @gmail.com.
|
||||||
|
func NormalizeEmail(str string) (string, error) {
|
||||||
|
if !IsEmail(str) {
|
||||||
|
return "", fmt.Errorf("%s is not an email", str)
|
||||||
|
}
|
||||||
|
parts := strings.Split(str, "@")
|
||||||
|
parts[0] = strings.ToLower(parts[0])
|
||||||
|
parts[1] = strings.ToLower(parts[1])
|
||||||
|
if parts[1] == "gmail.com" || parts[1] == "googlemail.com" {
|
||||||
|
parts[1] = "gmail.com"
|
||||||
|
parts[0] = strings.Split(ReplacePattern(parts[0], `\.`, ""), "+")[0]
|
||||||
|
}
|
||||||
|
return strings.Join(parts, "@"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Truncate a string to the closest length without breaking words.
|
||||||
|
func Truncate(str string, length int, ending string) string {
|
||||||
|
var aftstr, befstr string
|
||||||
|
if len(str) > length {
|
||||||
|
words := strings.Fields(str)
|
||||||
|
before, present := 0, 0
|
||||||
|
for i := range words {
|
||||||
|
befstr = aftstr
|
||||||
|
before = present
|
||||||
|
aftstr = aftstr + words[i] + " "
|
||||||
|
present = len(aftstr)
|
||||||
|
if present > length && i != 0 {
|
||||||
|
if (length - before) < (present - length) {
|
||||||
|
return Trim(befstr, " /\\.,\"'#!?&@+-") + ending
|
||||||
|
}
|
||||||
|
return Trim(aftstr, " /\\.,\"'#!?&@+-") + ending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// PadLeft pad left side of string if size of string is less then indicated pad length
|
||||||
|
func PadLeft(str string, padStr string, padLen int) string {
|
||||||
|
return buildPadStr(str, padStr, padLen, true, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PadRight pad right side of string if size of string is less then indicated pad length
|
||||||
|
func PadRight(str string, padStr string, padLen int) string {
|
||||||
|
return buildPadStr(str, padStr, padLen, false, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PadBoth pad sides of string if size of string is less then indicated pad length
|
||||||
|
func PadBoth(str string, padStr string, padLen int) string {
|
||||||
|
return buildPadStr(str, padStr, padLen, true, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PadString either left, right or both sides, not the padding string can be unicode and more then one
|
||||||
|
// character
|
||||||
|
func buildPadStr(str string, padStr string, padLen int, padLeft bool, padRight bool) string {
|
||||||
|
|
||||||
|
// When padded length is less then the current string size
|
||||||
|
if padLen < utf8.RuneCountInString(str) {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
padLen -= utf8.RuneCountInString(str)
|
||||||
|
|
||||||
|
targetLen := padLen
|
||||||
|
|
||||||
|
targetLenLeft := targetLen
|
||||||
|
targetLenRight := targetLen
|
||||||
|
if padLeft && padRight {
|
||||||
|
targetLenLeft = padLen / 2
|
||||||
|
targetLenRight = padLen - targetLenLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
strToRepeatLen := utf8.RuneCountInString(padStr)
|
||||||
|
|
||||||
|
repeatTimes := int(math.Ceil(float64(targetLen) / float64(strToRepeatLen)))
|
||||||
|
repeatedString := strings.Repeat(padStr, repeatTimes)
|
||||||
|
|
||||||
|
leftSide := ""
|
||||||
|
if padLeft {
|
||||||
|
leftSide = repeatedString[0:targetLenLeft]
|
||||||
|
}
|
||||||
|
|
||||||
|
rightSide := ""
|
||||||
|
if padRight {
|
||||||
|
rightSide = repeatedString[0:targetLenRight]
|
||||||
|
}
|
||||||
|
|
||||||
|
return leftSide + str + rightSide
|
||||||
|
}
|
500
vendor/github.com/asaskevich/govalidator/utils_test.go
generated
vendored
Normal file
500
vendor/github.com/asaskevich/govalidator/utils_test.go
generated
vendored
Normal file
|
@ -0,0 +1,500 @@
|
||||||
|
package govalidator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestContains(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param1 string
|
||||||
|
param2 string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"abacada", "", true},
|
||||||
|
{"abacada", "ritir", false},
|
||||||
|
{"abacada", "a", true},
|
||||||
|
{"abacada", "aca", true},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := Contains(test.param1, test.param2)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected Contains(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMatches(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param1 string
|
||||||
|
param2 string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"123456789", "[0-9]+", true},
|
||||||
|
{"abacada", "cab$", false},
|
||||||
|
{"111222333", "((111|222|333)+)+", true},
|
||||||
|
{"abacaba", "((123+]", false},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := Matches(test.param1, test.param2)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected Matches(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLeftTrim(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param1 string
|
||||||
|
param2 string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{" \r\n\tfoo \r\n\t ", "", "foo \r\n\t "},
|
||||||
|
{"010100201000", "01", "201000"},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := LeftTrim(test.param1, test.param2)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected LeftTrim(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRightTrim(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param1 string
|
||||||
|
param2 string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{" \r\n\tfoo \r\n\t ", "", " \r\n\tfoo"},
|
||||||
|
{"010100201000", "01", "0101002"},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := RightTrim(test.param1, test.param2)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected RightTrim(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTrim(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param1 string
|
||||||
|
param2 string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{" \r\n\tfoo \r\n\t ", "", "foo"},
|
||||||
|
{"010100201000", "01", "2"},
|
||||||
|
{"1234567890987654321", "1-8", "909"},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := Trim(test.param1, test.param2)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected Trim(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This small example illustrate how to work with Trim function.
|
||||||
|
func ExampleTrim() {
|
||||||
|
// Remove from left and right spaces and "\r", "\n", "\t" characters
|
||||||
|
println(Trim(" \r\r\ntext\r \t\n", "") == "text")
|
||||||
|
// Remove from left and right characters that are between "1" and "8".
|
||||||
|
// "1-8" is like full list "12345678".
|
||||||
|
println(Trim("1234567890987654321", "1-8") == "909")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWhiteList(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param1 string
|
||||||
|
param2 string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"abcdef", "abc", "abc"},
|
||||||
|
{"aaaaaaaaaabbbbbbbbbb", "abc", "aaaaaaaaaabbbbbbbbbb"},
|
||||||
|
{"a1b2c3", "abc", "abc"},
|
||||||
|
{" ", "abc", ""},
|
||||||
|
{"a3a43a5a4a3a2a23a4a5a4a3a4", "a-z", "aaaaaaaaaaaa"},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := WhiteList(test.param1, test.param2)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected WhiteList(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This small example illustrate how to work with WhiteList function.
|
||||||
|
func ExampleWhiteList() {
|
||||||
|
// Remove all characters from string ignoring characters between "a" and "z"
|
||||||
|
println(WhiteList("a3a43a5a4a3a2a23a4a5a4a3a4", "a-z") == "aaaaaaaaaaaa")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBlackList(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param1 string
|
||||||
|
param2 string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"abcdef", "abc", "def"},
|
||||||
|
{"aaaaaaaaaabbbbbbbbbb", "abc", ""},
|
||||||
|
{"a1b2c3", "abc", "123"},
|
||||||
|
{" ", "abc", " "},
|
||||||
|
{"a3a43a5a4a3a2a23a4a5a4a3a4", "a-z", "34354322345434"},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := BlackList(test.param1, test.param2)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected BlackList(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStripLow(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param1 string
|
||||||
|
param2 bool
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"foo\x00", false, "foo"},
|
||||||
|
{"\x7Ffoo\x02", false, "foo"},
|
||||||
|
{"\x01\x09", false, ""},
|
||||||
|
{"foo\x0A\x0D", false, "foo"},
|
||||||
|
{"perch\u00e9", false, "perch\u00e9"},
|
||||||
|
{"\u20ac", false, "\u20ac"},
|
||||||
|
{"\u2206\x0A", false, "\u2206"},
|
||||||
|
{"foo\x0A\x0D", true, "foo\x0A\x0D"},
|
||||||
|
{"\x03foo\x0A\x0D", true, "foo\x0A\x0D"},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := StripLow(test.param1, test.param2)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected StripLow(%q,%t) to be %v, got %v", test.param1, test.param2, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReplacePattern(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param1 string
|
||||||
|
param2 string
|
||||||
|
param3 string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"ab123ba", "[0-9]+", "aca", "abacaba"},
|
||||||
|
{"abacaba", "[0-9]+", "aca", "abacaba"},
|
||||||
|
{"httpftp://github.comio", "(ftp|io)", "", "http://github.com"},
|
||||||
|
{"aaaaaaaaaa", "a", "", ""},
|
||||||
|
{"http123123ftp://git534543hub.comio", "(ftp|io|[0-9]+)", "", "http://github.com"},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := ReplacePattern(test.param1, test.param2, test.param3)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected ReplacePattern(%q,%q,%q) to be %v, got %v", test.param1, test.param2, test.param3, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This small example illustrate how to work with ReplacePattern function.
|
||||||
|
func ExampleReplacePattern() {
|
||||||
|
// Replace in "http123123ftp://git534543hub.comio" following (pattern "(ftp|io|[0-9]+)"):
|
||||||
|
// - Sequence "ftp".
|
||||||
|
// - Sequence "io".
|
||||||
|
// - Sequence of digits.
|
||||||
|
// with empty string.
|
||||||
|
println(ReplacePattern("http123123ftp://git534543hub.comio", "(ftp|io|[0-9]+)", "") == "http://github.com")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEscape(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{`<img alt="foo&bar">`, "<img alt="foo&bar">"},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := Escape(test.param)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected Escape(%q) to be %v, got %v", test.param, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnderscoreToCamelCase(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"a_b_c", "ABC"},
|
||||||
|
{"my_func", "MyFunc"},
|
||||||
|
{"1ab_cd", "1abCd"},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := UnderscoreToCamelCase(test.param)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected UnderscoreToCamelCase(%q) to be %v, got %v", test.param, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCamelCaseToUnderscore(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"MyFunc", "my_func"},
|
||||||
|
{"ABC", "a_b_c"},
|
||||||
|
{"1B", "1_b"},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := CamelCaseToUnderscore(test.param)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected CamelCaseToUnderscore(%q) to be %v, got %v", test.param, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReverse(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"abc", "cba"},
|
||||||
|
{"カタカナ", "ナカタカ"},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := Reverse(test.param)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected Reverse(%q) to be %v, got %v", test.param, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetLines(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param string
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{"abc", []string{"abc"}},
|
||||||
|
{"a\nb\nc", []string{"a", "b", "c"}},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := GetLines(test.param)
|
||||||
|
if !reflect.DeepEqual(actual, test.expected) {
|
||||||
|
t.Errorf("Expected GetLines(%q) to be %v, got %v", test.param, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetLine(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param1 string
|
||||||
|
param2 int
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"abc", 0, "abc"},
|
||||||
|
{"a\nb\nc", 0, "a"},
|
||||||
|
{"abc", -1, ""},
|
||||||
|
{"abacaba\n", 1, ""},
|
||||||
|
{"abc", 3, ""},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual, _ := GetLine(test.param1, test.param2)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected GetLine(%q, %d) to be %v, got %v", test.param1, test.param2, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveTags(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"abc", "abc"},
|
||||||
|
{"<!-- Test -->", ""},
|
||||||
|
{"<div><div><p><a>Text</a></p></div></div>", "Text"},
|
||||||
|
{`<a href="#">Link</a>`, "Link"},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := RemoveTags(test.param)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected RemoveTags(%q) to be %v, got %v", test.param, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSafeFileName(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"abc", "abc"},
|
||||||
|
{"123456789 '_-?ASDF@£$%£%^é.html", "123456789-asdf.html"},
|
||||||
|
{"ReadMe.md", "readme.md"},
|
||||||
|
{"file:///c:/test.go", "test.go"},
|
||||||
|
{"../../../Hello World!.txt", "hello-world.txt"},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := SafeFileName(test.param)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected SafeFileName(%q) to be %v, got %v", test.param, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNormalizeEmail(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{`test@me.com`, `test@me.com`},
|
||||||
|
{`some.name@gmail.com`, `somename@gmail.com`},
|
||||||
|
{`some.name@googlemail.com`, `somename@gmail.com`},
|
||||||
|
{`some.name+extension@gmail.com`, `somename@gmail.com`},
|
||||||
|
{`some.name+extension@googlemail.com`, `somename@gmail.com`},
|
||||||
|
{`some.name.middlename+extension@gmail.com`, `somenamemiddlename@gmail.com`},
|
||||||
|
{`some.name.middlename+extension@googlemail.com`, `somenamemiddlename@gmail.com`},
|
||||||
|
{`some.name.midd.lena.me.+extension@gmail.com`, `somenamemiddlename@gmail.com`},
|
||||||
|
{`some.name.midd.lena.me.+extension@googlemail.com`, `somenamemiddlename@gmail.com`},
|
||||||
|
{`some.name+extension@unknown.com`, `some.name+extension@unknown.com`},
|
||||||
|
{`hans@m端ller.com`, `hans@m端ller.com`},
|
||||||
|
{`hans`, ``},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual, err := NormalizeEmail(test.param)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected NormalizeEmail(%q) to be %v, got %v, err %v", test.param, test.expected, actual, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTruncate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param1 string
|
||||||
|
param2 int
|
||||||
|
param3 string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{`Lorem ipsum dolor sit amet, consectetur adipiscing elit.`, 25, `...`, `Lorem ipsum dolor sit amet...`},
|
||||||
|
{`Measuring programming progress by lines of code is like measuring aircraft building progress by weight.`, 35, ` new born babies!`, `Measuring programming progress by new born babies!`},
|
||||||
|
{`Testestestestestestestestestest testestestestestestestestest`, 7, `...`, `Testestestestestestestestestest...`},
|
||||||
|
{`Testing`, 7, `...`, `Testing`},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := Truncate(test.param1, test.param2, test.param3)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected Truncate(%q, %d, %q) to be %v, got %v", test.param1, test.param2, test.param3, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPadLeft(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param1 string
|
||||||
|
param2 string
|
||||||
|
param3 int
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"こんにちは", "xyz", 12, "xyzxyzxこんにちは"},
|
||||||
|
{"こんにちは", "xyz", 11, "xyzxyzこんにちは"},
|
||||||
|
{"abc", "x", 5, "xxabc"},
|
||||||
|
{"abc", "xyz", 5, "xyabc"},
|
||||||
|
{"abcde", "xyz", 5, "abcde"},
|
||||||
|
{"abcde", "xyz", 4, "abcde"},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := PadLeft(test.param1, test.param2, test.param3)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected PadLeft(%q,%q,%q) to be %v, got %v", test.param1, test.param2, test.param3, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPadRight(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param1 string
|
||||||
|
param2 string
|
||||||
|
param3 int
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"こんにちは", "xyz", 12, "こんにちはxyzxyzx"},
|
||||||
|
{"こんにちは", "xyz", 11, "こんにちはxyzxyz"},
|
||||||
|
{"abc", "x", 5, "abcxx"},
|
||||||
|
{"abc", "xyz", 5, "abcxy"},
|
||||||
|
{"abcde", "xyz", 5, "abcde"},
|
||||||
|
{"abcde", "xyz", 4, "abcde"},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := PadRight(test.param1, test.param2, test.param3)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected PadRight(%q,%q,%q) to be %v, got %v", test.param1, test.param2, test.param3, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPadBoth(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
param1 string
|
||||||
|
param2 string
|
||||||
|
param3 int
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"こんにちは", "xyz", 12, "xyzこんにちはxyzx"},
|
||||||
|
{"こんにちは", "xyz", 11, "xyzこんにちはxyz"},
|
||||||
|
{"abc", "x", 5, "xabcx"},
|
||||||
|
{"abc", "xyz", 5, "xabcx"},
|
||||||
|
{"abcde", "xyz", 5, "abcde"},
|
||||||
|
{"abcde", "xyz", 4, "abcde"},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := PadBoth(test.param1, test.param2, test.param3)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Expected PadBoth(%q,%q,%q) to be %v, got %v", test.param1, test.param2, test.param3, test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1044
vendor/github.com/asaskevich/govalidator/validator.go
generated
vendored
Normal file
1044
vendor/github.com/asaskevich/govalidator/validator.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
2864
vendor/github.com/asaskevich/govalidator/validator_test.go
generated
vendored
Normal file
2864
vendor/github.com/asaskevich/govalidator/validator_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
15
vendor/github.com/asaskevich/govalidator/wercker.yml
generated
vendored
Normal file
15
vendor/github.com/asaskevich/govalidator/wercker.yml
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
box: golang
|
||||||
|
build:
|
||||||
|
steps:
|
||||||
|
- setup-go-workspace
|
||||||
|
|
||||||
|
- script:
|
||||||
|
name: go get
|
||||||
|
code: |
|
||||||
|
go version
|
||||||
|
go get -t ./...
|
||||||
|
|
||||||
|
- script:
|
||||||
|
name: go test
|
||||||
|
code: |
|
||||||
|
go test -race ./...
|
21
vendor/github.com/blang/semver/.travis.yml
generated
vendored
21
vendor/github.com/blang/semver/.travis.yml
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
language: go
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- go: 1.4.3
|
|
||||||
- go: 1.5.4
|
|
||||||
- go: 1.6.3
|
|
||||||
- go: 1.7
|
|
||||||
- go: tip
|
|
||||||
allow_failures:
|
|
||||||
- go: tip
|
|
||||||
install:
|
|
||||||
- go get golang.org/x/tools/cmd/cover
|
|
||||||
- go get github.com/mattn/goveralls
|
|
||||||
script:
|
|
||||||
- echo "Test and track coverage" ; $HOME/gopath/bin/goveralls -package "." -service=travis-ci
|
|
||||||
-repotoken $COVERALLS_TOKEN
|
|
||||||
- echo "Build examples" ; cd examples && go build
|
|
||||||
- echo "Check if gofmt'd" ; diff -u <(echo -n) <(gofmt -d -s .)
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
secure: HroGEAUQpVq9zX1b1VIkraLiywhGbzvNnTZq2TMxgK7JHP8xqNplAeF1izrR2i4QLL9nsY+9WtYss4QuPvEtZcVHUobw6XnL6radF7jS1LgfYZ9Y7oF+zogZ2I5QUMRLGA7rcxQ05s7mKq3XZQfeqaNts4bms/eZRefWuaFZbkw=
|
|
194
vendor/github.com/blang/semver/README.md
generated
vendored
194
vendor/github.com/blang/semver/README.md
generated
vendored
|
@ -1,194 +0,0 @@
|
||||||
semver for golang [](https://travis-ci.org/blang/semver) [](https://godoc.org/github.com/blang/semver) [](https://coveralls.io/r/blang/semver?branch=master)
|
|
||||||
======
|
|
||||||
|
|
||||||
semver is a [Semantic Versioning](http://semver.org/) library written in golang. It fully covers spec version `2.0.0`.
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
```bash
|
|
||||||
$ go get github.com/blang/semver
|
|
||||||
```
|
|
||||||
Note: Always vendor your dependencies or fix on a specific version tag.
|
|
||||||
|
|
||||||
```go
|
|
||||||
import github.com/blang/semver
|
|
||||||
v1, err := semver.Make("1.0.0-beta")
|
|
||||||
v2, err := semver.Make("2.0.0-beta")
|
|
||||||
v1.Compare(v2)
|
|
||||||
```
|
|
||||||
|
|
||||||
Also check the [GoDocs](http://godoc.org/github.com/blang/semver).
|
|
||||||
|
|
||||||
Why should I use this lib?
|
|
||||||
-----
|
|
||||||
|
|
||||||
- Fully spec compatible
|
|
||||||
- No reflection
|
|
||||||
- No regex
|
|
||||||
- Fully tested (Coverage >99%)
|
|
||||||
- Readable parsing/validation errors
|
|
||||||
- Fast (See [Benchmarks](#benchmarks))
|
|
||||||
- Only Stdlib
|
|
||||||
- Uses values instead of pointers
|
|
||||||
- Many features, see below
|
|
||||||
|
|
||||||
|
|
||||||
Features
|
|
||||||
-----
|
|
||||||
|
|
||||||
- Parsing and validation at all levels
|
|
||||||
- Comparator-like comparisons
|
|
||||||
- Compare Helper Methods
|
|
||||||
- InPlace manipulation
|
|
||||||
- Ranges `>=1.0.0 <2.0.0 || >=3.0.0 !3.0.1-beta.1`
|
|
||||||
- Wildcards `>=1.x`, `<=2.5.x`
|
|
||||||
- Sortable (implements sort.Interface)
|
|
||||||
- database/sql compatible (sql.Scanner/Valuer)
|
|
||||||
- encoding/json compatible (json.Marshaler/Unmarshaler)
|
|
||||||
|
|
||||||
Ranges
|
|
||||||
------
|
|
||||||
|
|
||||||
A `Range` is a set of conditions which specify which versions satisfy the range.
|
|
||||||
|
|
||||||
A condition is composed of an operator and a version. The supported operators are:
|
|
||||||
|
|
||||||
- `<1.0.0` Less than `1.0.0`
|
|
||||||
- `<=1.0.0` Less than or equal to `1.0.0`
|
|
||||||
- `>1.0.0` Greater than `1.0.0`
|
|
||||||
- `>=1.0.0` Greater than or equal to `1.0.0`
|
|
||||||
- `1.0.0`, `=1.0.0`, `==1.0.0` Equal to `1.0.0`
|
|
||||||
- `!1.0.0`, `!=1.0.0` Not equal to `1.0.0`. Excludes version `1.0.0`.
|
|
||||||
|
|
||||||
Note that spaces between the operator and the version will be gracefully tolerated.
|
|
||||||
|
|
||||||
A `Range` can link multiple `Ranges` separated by space:
|
|
||||||
|
|
||||||
Ranges can be linked by logical AND:
|
|
||||||
|
|
||||||
- `>1.0.0 <2.0.0` would match between both ranges, so `1.1.1` and `1.8.7` but not `1.0.0` or `2.0.0`
|
|
||||||
- `>1.0.0 <3.0.0 !2.0.3-beta.2` would match every version between `1.0.0` and `3.0.0` except `2.0.3-beta.2`
|
|
||||||
|
|
||||||
Ranges can also be linked by logical OR:
|
|
||||||
|
|
||||||
- `<2.0.0 || >=3.0.0` would match `1.x.x` and `3.x.x` but not `2.x.x`
|
|
||||||
|
|
||||||
AND has a higher precedence than OR. It's not possible to use brackets.
|
|
||||||
|
|
||||||
Ranges can be combined by both AND and OR
|
|
||||||
|
|
||||||
- `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1`
|
|
||||||
|
|
||||||
Range usage:
|
|
||||||
|
|
||||||
```
|
|
||||||
v, err := semver.Parse("1.2.3")
|
|
||||||
range, err := semver.ParseRange(">1.0.0 <2.0.0 || >=3.0.0")
|
|
||||||
if range(v) {
|
|
||||||
//valid
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
Example
|
|
||||||
-----
|
|
||||||
|
|
||||||
Have a look at full examples in [examples/main.go](examples/main.go)
|
|
||||||
|
|
||||||
```go
|
|
||||||
import github.com/blang/semver
|
|
||||||
|
|
||||||
v, err := semver.Make("0.0.1-alpha.preview+123.github")
|
|
||||||
fmt.Printf("Major: %d\n", v.Major)
|
|
||||||
fmt.Printf("Minor: %d\n", v.Minor)
|
|
||||||
fmt.Printf("Patch: %d\n", v.Patch)
|
|
||||||
fmt.Printf("Pre: %s\n", v.Pre)
|
|
||||||
fmt.Printf("Build: %s\n", v.Build)
|
|
||||||
|
|
||||||
// Prerelease versions array
|
|
||||||
if len(v.Pre) > 0 {
|
|
||||||
fmt.Println("Prerelease versions:")
|
|
||||||
for i, pre := range v.Pre {
|
|
||||||
fmt.Printf("%d: %q\n", i, pre)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build meta data array
|
|
||||||
if len(v.Build) > 0 {
|
|
||||||
fmt.Println("Build meta data:")
|
|
||||||
for i, build := range v.Build {
|
|
||||||
fmt.Printf("%d: %q\n", i, build)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
v001, err := semver.Make("0.0.1")
|
|
||||||
// Compare using helpers: v.GT(v2), v.LT, v.GTE, v.LTE
|
|
||||||
v001.GT(v) == true
|
|
||||||
v.LT(v001) == true
|
|
||||||
v.GTE(v) == true
|
|
||||||
v.LTE(v) == true
|
|
||||||
|
|
||||||
// Or use v.Compare(v2) for comparisons (-1, 0, 1):
|
|
||||||
v001.Compare(v) == 1
|
|
||||||
v.Compare(v001) == -1
|
|
||||||
v.Compare(v) == 0
|
|
||||||
|
|
||||||
// Manipulate Version in place:
|
|
||||||
v.Pre[0], err = semver.NewPRVersion("beta")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Error parsing pre release version: %q", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("\nValidate versions:")
|
|
||||||
v.Build[0] = "?"
|
|
||||||
|
|
||||||
err = v.Validate()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Validation failed: %s\n", err)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Benchmarks
|
|
||||||
-----
|
|
||||||
|
|
||||||
BenchmarkParseSimple-4 5000000 390 ns/op 48 B/op 1 allocs/op
|
|
||||||
BenchmarkParseComplex-4 1000000 1813 ns/op 256 B/op 7 allocs/op
|
|
||||||
BenchmarkParseAverage-4 1000000 1171 ns/op 163 B/op 4 allocs/op
|
|
||||||
BenchmarkStringSimple-4 20000000 119 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkStringLarger-4 10000000 206 ns/op 32 B/op 2 allocs/op
|
|
||||||
BenchmarkStringComplex-4 5000000 324 ns/op 80 B/op 3 allocs/op
|
|
||||||
BenchmarkStringAverage-4 5000000 273 ns/op 53 B/op 2 allocs/op
|
|
||||||
BenchmarkValidateSimple-4 200000000 9.33 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkValidateComplex-4 3000000 469 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkValidateAverage-4 5000000 256 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkCompareSimple-4 100000000 11.8 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkCompareComplex-4 50000000 30.8 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkCompareAverage-4 30000000 41.5 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkSort-4 3000000 419 ns/op 256 B/op 2 allocs/op
|
|
||||||
BenchmarkRangeParseSimple-4 2000000 850 ns/op 192 B/op 5 allocs/op
|
|
||||||
BenchmarkRangeParseAverage-4 1000000 1677 ns/op 400 B/op 10 allocs/op
|
|
||||||
BenchmarkRangeParseComplex-4 300000 5214 ns/op 1440 B/op 30 allocs/op
|
|
||||||
BenchmarkRangeMatchSimple-4 50000000 25.6 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkRangeMatchAverage-4 30000000 56.4 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkRangeMatchComplex-4 10000000 153 ns/op 0 B/op 0 allocs/op
|
|
||||||
|
|
||||||
See benchmark cases at [semver_test.go](semver_test.go)
|
|
||||||
|
|
||||||
|
|
||||||
Motivation
|
|
||||||
-----
|
|
||||||
|
|
||||||
I simply couldn't find any lib supporting the full spec. Others were just wrong or used reflection and regex which i don't like.
|
|
||||||
|
|
||||||
|
|
||||||
Contribution
|
|
||||||
-----
|
|
||||||
|
|
||||||
Feel free to make a pull request. For bigger changes create a issue first to discuss about it.
|
|
||||||
|
|
||||||
|
|
||||||
License
|
|
||||||
-----
|
|
||||||
|
|
||||||
See [LICENSE](LICENSE) file.
|
|
23
vendor/github.com/blang/semver/json.go
generated
vendored
23
vendor/github.com/blang/semver/json.go
generated
vendored
|
@ -1,23 +0,0 @@
|
||||||
package semver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MarshalJSON implements the encoding/json.Marshaler interface.
|
|
||||||
func (v Version) MarshalJSON() ([]byte, error) {
|
|
||||||
return json.Marshal(v.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON implements the encoding/json.Unmarshaler interface.
|
|
||||||
func (v *Version) UnmarshalJSON(data []byte) (err error) {
|
|
||||||
var versionString string
|
|
||||||
|
|
||||||
if err = json.Unmarshal(data, &versionString); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
*v, err = Parse(versionString)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
49
vendor/github.com/blang/semver/json_test.go
generated
vendored
49
vendor/github.com/blang/semver/json_test.go
generated
vendored
|
@ -1,49 +0,0 @@
|
||||||
package semver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestJSONMarshal(t *testing.T) {
|
|
||||||
versionString := "3.1.4-alpha.1.5.9+build.2.6.5"
|
|
||||||
v, err := Parse(versionString)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
versionJSON, err := json.Marshal(v)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
quotedVersionString := strconv.Quote(versionString)
|
|
||||||
|
|
||||||
if string(versionJSON) != quotedVersionString {
|
|
||||||
t.Fatalf("JSON marshaled semantic version not equal: expected %q, got %q", quotedVersionString, string(versionJSON))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestJSONUnmarshal(t *testing.T) {
|
|
||||||
versionString := "3.1.4-alpha.1.5.9+build.2.6.5"
|
|
||||||
quotedVersionString := strconv.Quote(versionString)
|
|
||||||
|
|
||||||
var v Version
|
|
||||||
if err := json.Unmarshal([]byte(quotedVersionString), &v); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.String() != versionString {
|
|
||||||
t.Fatalf("JSON unmarshaled semantic version not equal: expected %q, got %q", versionString, v.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
badVersionString := strconv.Quote("3.1.4.1.5.9.2.6.5-other-digits-of-pi")
|
|
||||||
if err := json.Unmarshal([]byte(badVersionString), &v); err == nil {
|
|
||||||
t.Fatal("expected JSON unmarshal error, got nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := json.Unmarshal([]byte("3.1"), &v); err == nil {
|
|
||||||
t.Fatal("expected JSON unmarshal error, got nil")
|
|
||||||
}
|
|
||||||
}
|
|
17
vendor/github.com/blang/semver/package.json
generated
vendored
17
vendor/github.com/blang/semver/package.json
generated
vendored
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"author": "blang",
|
|
||||||
"bugs": {
|
|
||||||
"URL": "https://github.com/blang/semver/issues",
|
|
||||||
"url": "https://github.com/blang/semver/issues"
|
|
||||||
},
|
|
||||||
"gx": {
|
|
||||||
"dvcsimport": "github.com/blang/semver"
|
|
||||||
},
|
|
||||||
"gxVersion": "0.10.0",
|
|
||||||
"language": "go",
|
|
||||||
"license": "MIT",
|
|
||||||
"name": "semver",
|
|
||||||
"releaseCmd": "git commit -a -m \"gx publish $VERSION\"",
|
|
||||||
"version": "3.5.1"
|
|
||||||
}
|
|
||||||
|
|
416
vendor/github.com/blang/semver/range.go
generated
vendored
416
vendor/github.com/blang/semver/range.go
generated
vendored
|
@ -1,416 +0,0 @@
|
||||||
package semver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
)
|
|
||||||
|
|
||||||
type wildcardType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
noneWildcard wildcardType = iota
|
|
||||||
majorWildcard wildcardType = 1
|
|
||||||
minorWildcard wildcardType = 2
|
|
||||||
patchWildcard wildcardType = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
func wildcardTypefromInt(i int) wildcardType {
|
|
||||||
switch i {
|
|
||||||
case 1:
|
|
||||||
return majorWildcard
|
|
||||||
case 2:
|
|
||||||
return minorWildcard
|
|
||||||
case 3:
|
|
||||||
return patchWildcard
|
|
||||||
default:
|
|
||||||
return noneWildcard
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type comparator func(Version, Version) bool
|
|
||||||
|
|
||||||
var (
|
|
||||||
compEQ comparator = func(v1 Version, v2 Version) bool {
|
|
||||||
return v1.Compare(v2) == 0
|
|
||||||
}
|
|
||||||
compNE = func(v1 Version, v2 Version) bool {
|
|
||||||
return v1.Compare(v2) != 0
|
|
||||||
}
|
|
||||||
compGT = func(v1 Version, v2 Version) bool {
|
|
||||||
return v1.Compare(v2) == 1
|
|
||||||
}
|
|
||||||
compGE = func(v1 Version, v2 Version) bool {
|
|
||||||
return v1.Compare(v2) >= 0
|
|
||||||
}
|
|
||||||
compLT = func(v1 Version, v2 Version) bool {
|
|
||||||
return v1.Compare(v2) == -1
|
|
||||||
}
|
|
||||||
compLE = func(v1 Version, v2 Version) bool {
|
|
||||||
return v1.Compare(v2) <= 0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
type versionRange struct {
|
|
||||||
v Version
|
|
||||||
c comparator
|
|
||||||
}
|
|
||||||
|
|
||||||
// rangeFunc creates a Range from the given versionRange.
|
|
||||||
func (vr *versionRange) rangeFunc() Range {
|
|
||||||
return Range(func(v Version) bool {
|
|
||||||
return vr.c(v, vr.v)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Range represents a range of versions.
|
|
||||||
// A Range can be used to check if a Version satisfies it:
|
|
||||||
//
|
|
||||||
// range, err := semver.ParseRange(">1.0.0 <2.0.0")
|
|
||||||
// range(semver.MustParse("1.1.1") // returns true
|
|
||||||
type Range func(Version) bool
|
|
||||||
|
|
||||||
// OR combines the existing Range with another Range using logical OR.
|
|
||||||
func (rf Range) OR(f Range) Range {
|
|
||||||
return Range(func(v Version) bool {
|
|
||||||
return rf(v) || f(v)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// AND combines the existing Range with another Range using logical AND.
|
|
||||||
func (rf Range) AND(f Range) Range {
|
|
||||||
return Range(func(v Version) bool {
|
|
||||||
return rf(v) && f(v)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseRange parses a range and returns a Range.
|
|
||||||
// If the range could not be parsed an error is returned.
|
|
||||||
//
|
|
||||||
// Valid ranges are:
|
|
||||||
// - "<1.0.0"
|
|
||||||
// - "<=1.0.0"
|
|
||||||
// - ">1.0.0"
|
|
||||||
// - ">=1.0.0"
|
|
||||||
// - "1.0.0", "=1.0.0", "==1.0.0"
|
|
||||||
// - "!1.0.0", "!=1.0.0"
|
|
||||||
//
|
|
||||||
// A Range can consist of multiple ranges separated by space:
|
|
||||||
// Ranges can be linked by logical AND:
|
|
||||||
// - ">1.0.0 <2.0.0" would match between both ranges, so "1.1.1" and "1.8.7" but not "1.0.0" or "2.0.0"
|
|
||||||
// - ">1.0.0 <3.0.0 !2.0.3-beta.2" would match every version between 1.0.0 and 3.0.0 except 2.0.3-beta.2
|
|
||||||
//
|
|
||||||
// Ranges can also be linked by logical OR:
|
|
||||||
// - "<2.0.0 || >=3.0.0" would match "1.x.x" and "3.x.x" but not "2.x.x"
|
|
||||||
//
|
|
||||||
// AND has a higher precedence than OR. It's not possible to use brackets.
|
|
||||||
//
|
|
||||||
// Ranges can be combined by both AND and OR
|
|
||||||
//
|
|
||||||
// - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1`
|
|
||||||
func ParseRange(s string) (Range, error) {
|
|
||||||
parts := splitAndTrim(s)
|
|
||||||
orParts, err := splitORParts(parts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
expandedParts, err := expandWildcardVersion(orParts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var orFn Range
|
|
||||||
for _, p := range expandedParts {
|
|
||||||
var andFn Range
|
|
||||||
for _, ap := range p {
|
|
||||||
opStr, vStr, err := splitComparatorVersion(ap)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
vr, err := buildVersionRange(opStr, vStr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Could not parse Range %q: %s", ap, err)
|
|
||||||
}
|
|
||||||
rf := vr.rangeFunc()
|
|
||||||
|
|
||||||
// Set function
|
|
||||||
if andFn == nil {
|
|
||||||
andFn = rf
|
|
||||||
} else { // Combine with existing function
|
|
||||||
andFn = andFn.AND(rf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if orFn == nil {
|
|
||||||
orFn = andFn
|
|
||||||
} else {
|
|
||||||
orFn = orFn.OR(andFn)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return orFn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// splitORParts splits the already cleaned parts by '||'.
|
|
||||||
// Checks for invalid positions of the operator and returns an
|
|
||||||
// error if found.
|
|
||||||
func splitORParts(parts []string) ([][]string, error) {
|
|
||||||
var ORparts [][]string
|
|
||||||
last := 0
|
|
||||||
for i, p := range parts {
|
|
||||||
if p == "||" {
|
|
||||||
if i == 0 {
|
|
||||||
return nil, fmt.Errorf("First element in range is '||'")
|
|
||||||
}
|
|
||||||
ORparts = append(ORparts, parts[last:i])
|
|
||||||
last = i + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if last == len(parts) {
|
|
||||||
return nil, fmt.Errorf("Last element in range is '||'")
|
|
||||||
}
|
|
||||||
ORparts = append(ORparts, parts[last:])
|
|
||||||
return ORparts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// buildVersionRange takes a slice of 2: operator and version
|
|
||||||
// and builds a versionRange, otherwise an error.
|
|
||||||
func buildVersionRange(opStr, vStr string) (*versionRange, error) {
|
|
||||||
c := parseComparator(opStr)
|
|
||||||
if c == nil {
|
|
||||||
return nil, fmt.Errorf("Could not parse comparator %q in %q", opStr, strings.Join([]string{opStr, vStr}, ""))
|
|
||||||
}
|
|
||||||
v, err := Parse(vStr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Could not parse version %q in %q: %s", vStr, strings.Join([]string{opStr, vStr}, ""), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &versionRange{
|
|
||||||
v: v,
|
|
||||||
c: c,
|
|
||||||
}, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// inArray checks if a byte is contained in an array of bytes
|
|
||||||
func inArray(s byte, list []byte) bool {
|
|
||||||
for _, el := range list {
|
|
||||||
if el == s {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// splitAndTrim splits a range string by spaces and cleans whitespaces
|
|
||||||
func splitAndTrim(s string) (result []string) {
|
|
||||||
last := 0
|
|
||||||
var lastChar byte
|
|
||||||
excludeFromSplit := []byte{'>', '<', '='}
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
if s[i] == ' ' && !inArray(lastChar, excludeFromSplit) {
|
|
||||||
if last < i-1 {
|
|
||||||
result = append(result, s[last:i])
|
|
||||||
}
|
|
||||||
last = i + 1
|
|
||||||
} else if s[i] != ' ' {
|
|
||||||
lastChar = s[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if last < len(s)-1 {
|
|
||||||
result = append(result, s[last:])
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, v := range result {
|
|
||||||
result[i] = strings.Replace(v, " ", "", -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// parts := strings.Split(s, " ")
|
|
||||||
// for _, x := range parts {
|
|
||||||
// if s := strings.TrimSpace(x); len(s) != 0 {
|
|
||||||
// result = append(result, s)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// splitComparatorVersion splits the comparator from the version.
|
|
||||||
// Input must be free of leading or trailing spaces.
|
|
||||||
func splitComparatorVersion(s string) (string, string, error) {
|
|
||||||
i := strings.IndexFunc(s, unicode.IsDigit)
|
|
||||||
if i == -1 {
|
|
||||||
return "", "", fmt.Errorf("Could not get version from string: %q", s)
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(s[0:i]), s[i:], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getWildcardType will return the type of wildcard that the
|
|
||||||
// passed version contains
|
|
||||||
func getWildcardType(vStr string) wildcardType {
|
|
||||||
parts := strings.Split(vStr, ".")
|
|
||||||
nparts := len(parts)
|
|
||||||
wildcard := parts[nparts-1]
|
|
||||||
|
|
||||||
possibleWildcardType := wildcardTypefromInt(nparts)
|
|
||||||
if wildcard == "x" {
|
|
||||||
return possibleWildcardType
|
|
||||||
}
|
|
||||||
|
|
||||||
return noneWildcard
|
|
||||||
}
|
|
||||||
|
|
||||||
// createVersionFromWildcard will convert a wildcard version
|
|
||||||
// into a regular version, replacing 'x's with '0's, handling
|
|
||||||
// special cases like '1.x.x' and '1.x'
|
|
||||||
func createVersionFromWildcard(vStr string) string {
|
|
||||||
// handle 1.x.x
|
|
||||||
vStr2 := strings.Replace(vStr, ".x.x", ".x", 1)
|
|
||||||
vStr2 = strings.Replace(vStr2, ".x", ".0", 1)
|
|
||||||
parts := strings.Split(vStr2, ".")
|
|
||||||
|
|
||||||
// handle 1.x
|
|
||||||
if len(parts) == 2 {
|
|
||||||
return vStr2 + ".0"
|
|
||||||
}
|
|
||||||
|
|
||||||
return vStr2
|
|
||||||
}
|
|
||||||
|
|
||||||
// incrementMajorVersion will increment the major version
|
|
||||||
// of the passed version
|
|
||||||
func incrementMajorVersion(vStr string) (string, error) {
|
|
||||||
parts := strings.Split(vStr, ".")
|
|
||||||
i, err := strconv.Atoi(parts[0])
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
parts[0] = strconv.Itoa(i + 1)
|
|
||||||
|
|
||||||
return strings.Join(parts, "."), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// incrementMajorVersion will increment the minor version
|
|
||||||
// of the passed version
|
|
||||||
func incrementMinorVersion(vStr string) (string, error) {
|
|
||||||
parts := strings.Split(vStr, ".")
|
|
||||||
i, err := strconv.Atoi(parts[1])
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
parts[1] = strconv.Itoa(i + 1)
|
|
||||||
|
|
||||||
return strings.Join(parts, "."), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// expandWildcardVersion will expand wildcards inside versions
|
|
||||||
// following these rules:
|
|
||||||
//
|
|
||||||
// * when dealing with patch wildcards:
|
|
||||||
// >= 1.2.x will become >= 1.2.0
|
|
||||||
// <= 1.2.x will become < 1.3.0
|
|
||||||
// > 1.2.x will become >= 1.3.0
|
|
||||||
// < 1.2.x will become < 1.2.0
|
|
||||||
// != 1.2.x will become < 1.2.0 >= 1.3.0
|
|
||||||
//
|
|
||||||
// * when dealing with minor wildcards:
|
|
||||||
// >= 1.x will become >= 1.0.0
|
|
||||||
// <= 1.x will become < 2.0.0
|
|
||||||
// > 1.x will become >= 2.0.0
|
|
||||||
// < 1.0 will become < 1.0.0
|
|
||||||
// != 1.x will become < 1.0.0 >= 2.0.0
|
|
||||||
//
|
|
||||||
// * when dealing with wildcards without
|
|
||||||
// version operator:
|
|
||||||
// 1.2.x will become >= 1.2.0 < 1.3.0
|
|
||||||
// 1.x will become >= 1.0.0 < 2.0.0
|
|
||||||
func expandWildcardVersion(parts [][]string) ([][]string, error) {
|
|
||||||
var expandedParts [][]string
|
|
||||||
for _, p := range parts {
|
|
||||||
var newParts []string
|
|
||||||
for _, ap := range p {
|
|
||||||
if strings.Index(ap, "x") != -1 {
|
|
||||||
opStr, vStr, err := splitComparatorVersion(ap)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
versionWildcardType := getWildcardType(vStr)
|
|
||||||
flatVersion := createVersionFromWildcard(vStr)
|
|
||||||
|
|
||||||
var resultOperator string
|
|
||||||
var shouldIncrementVersion bool
|
|
||||||
switch opStr {
|
|
||||||
case ">":
|
|
||||||
resultOperator = ">="
|
|
||||||
shouldIncrementVersion = true
|
|
||||||
case ">=":
|
|
||||||
resultOperator = ">="
|
|
||||||
case "<":
|
|
||||||
resultOperator = "<"
|
|
||||||
case "<=":
|
|
||||||
resultOperator = "<"
|
|
||||||
shouldIncrementVersion = true
|
|
||||||
case "", "=", "==":
|
|
||||||
newParts = append(newParts, ">="+flatVersion)
|
|
||||||
resultOperator = "<"
|
|
||||||
shouldIncrementVersion = true
|
|
||||||
case "!=", "!":
|
|
||||||
newParts = append(newParts, "<"+flatVersion)
|
|
||||||
resultOperator = ">="
|
|
||||||
shouldIncrementVersion = true
|
|
||||||
}
|
|
||||||
|
|
||||||
var resultVersion string
|
|
||||||
if shouldIncrementVersion {
|
|
||||||
switch versionWildcardType {
|
|
||||||
case patchWildcard:
|
|
||||||
resultVersion, _ = incrementMinorVersion(flatVersion)
|
|
||||||
case minorWildcard:
|
|
||||||
resultVersion, _ = incrementMajorVersion(flatVersion)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
resultVersion = flatVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
ap = resultOperator + resultVersion
|
|
||||||
}
|
|
||||||
newParts = append(newParts, ap)
|
|
||||||
}
|
|
||||||
expandedParts = append(expandedParts, newParts)
|
|
||||||
}
|
|
||||||
|
|
||||||
return expandedParts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseComparator(s string) comparator {
|
|
||||||
switch s {
|
|
||||||
case "==":
|
|
||||||
fallthrough
|
|
||||||
case "":
|
|
||||||
fallthrough
|
|
||||||
case "=":
|
|
||||||
return compEQ
|
|
||||||
case ">":
|
|
||||||
return compGT
|
|
||||||
case ">=":
|
|
||||||
return compGE
|
|
||||||
case "<":
|
|
||||||
return compLT
|
|
||||||
case "<=":
|
|
||||||
return compLE
|
|
||||||
case "!":
|
|
||||||
fallthrough
|
|
||||||
case "!=":
|
|
||||||
return compNE
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustParseRange is like ParseRange but panics if the range cannot be parsed.
|
|
||||||
func MustParseRange(s string) Range {
|
|
||||||
r, err := ParseRange(s)
|
|
||||||
if err != nil {
|
|
||||||
panic(`semver: ParseRange(` + s + `): ` + err.Error())
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
|
581
vendor/github.com/blang/semver/range_test.go
generated
vendored
581
vendor/github.com/blang/semver/range_test.go
generated
vendored
|
@ -1,581 +0,0 @@
|
||||||
package semver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type wildcardTypeTest struct {
|
|
||||||
input string
|
|
||||||
wildcardType wildcardType
|
|
||||||
}
|
|
||||||
|
|
||||||
type comparatorTest struct {
|
|
||||||
input string
|
|
||||||
comparator func(comparator) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseComparator(t *testing.T) {
|
|
||||||
compatorTests := []comparatorTest{
|
|
||||||
{">", testGT},
|
|
||||||
{">=", testGE},
|
|
||||||
{"<", testLT},
|
|
||||||
{"<=", testLE},
|
|
||||||
{"", testEQ},
|
|
||||||
{"=", testEQ},
|
|
||||||
{"==", testEQ},
|
|
||||||
{"!=", testNE},
|
|
||||||
{"!", testNE},
|
|
||||||
{"-", nil},
|
|
||||||
{"<==", nil},
|
|
||||||
{"<<", nil},
|
|
||||||
{">>", nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range compatorTests {
|
|
||||||
if c := parseComparator(tc.input); c == nil {
|
|
||||||
if tc.comparator != nil {
|
|
||||||
t.Errorf("Comparator nil for case %q\n", tc.input)
|
|
||||||
}
|
|
||||||
} else if !tc.comparator(c) {
|
|
||||||
t.Errorf("Invalid comparator for case %q\n", tc.input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
v1 = MustParse("1.2.2")
|
|
||||||
v2 = MustParse("1.2.3")
|
|
||||||
v3 = MustParse("1.2.4")
|
|
||||||
)
|
|
||||||
|
|
||||||
func testEQ(f comparator) bool {
|
|
||||||
return f(v1, v1) && !f(v1, v2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testNE(f comparator) bool {
|
|
||||||
return !f(v1, v1) && f(v1, v2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testGT(f comparator) bool {
|
|
||||||
return f(v2, v1) && f(v3, v2) && !f(v1, v2) && !f(v1, v1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testGE(f comparator) bool {
|
|
||||||
return f(v2, v1) && f(v3, v2) && !f(v1, v2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testLT(f comparator) bool {
|
|
||||||
return f(v1, v2) && f(v2, v3) && !f(v2, v1) && !f(v1, v1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testLE(f comparator) bool {
|
|
||||||
return f(v1, v2) && f(v2, v3) && !f(v2, v1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSplitAndTrim(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
i string
|
|
||||||
s []string
|
|
||||||
}{
|
|
||||||
{"1.2.3 1.2.3", []string{"1.2.3", "1.2.3"}},
|
|
||||||
{" 1.2.3 1.2.3 ", []string{"1.2.3", "1.2.3"}}, // Spaces
|
|
||||||
{" >= 1.2.3 <= 1.2.3 ", []string{">=1.2.3", "<=1.2.3"}}, // Spaces between operator and version
|
|
||||||
{"1.2.3 || >=1.2.3 <1.2.3", []string{"1.2.3", "||", ">=1.2.3", "<1.2.3"}},
|
|
||||||
{" 1.2.3 || >=1.2.3 <1.2.3 ", []string{"1.2.3", "||", ">=1.2.3", "<1.2.3"}},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range tests {
|
|
||||||
p := splitAndTrim(tc.i)
|
|
||||||
if !reflect.DeepEqual(p, tc.s) {
|
|
||||||
t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.s, p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSplitComparatorVersion(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
i string
|
|
||||||
p []string
|
|
||||||
}{
|
|
||||||
{">1.2.3", []string{">", "1.2.3"}},
|
|
||||||
{">=1.2.3", []string{">=", "1.2.3"}},
|
|
||||||
{"<1.2.3", []string{"<", "1.2.3"}},
|
|
||||||
{"<=1.2.3", []string{"<=", "1.2.3"}},
|
|
||||||
{"1.2.3", []string{"", "1.2.3"}},
|
|
||||||
{"=1.2.3", []string{"=", "1.2.3"}},
|
|
||||||
{"==1.2.3", []string{"==", "1.2.3"}},
|
|
||||||
{"!=1.2.3", []string{"!=", "1.2.3"}},
|
|
||||||
{"!1.2.3", []string{"!", "1.2.3"}},
|
|
||||||
{"error", nil},
|
|
||||||
}
|
|
||||||
for _, tc := range tests {
|
|
||||||
if op, v, err := splitComparatorVersion(tc.i); err != nil {
|
|
||||||
if tc.p != nil {
|
|
||||||
t.Errorf("Invalid for case %q: Expected %q, got error %q", tc.i, tc.p, err)
|
|
||||||
}
|
|
||||||
} else if op != tc.p[0] {
|
|
||||||
t.Errorf("Invalid operator for case %q: Expected %q, got: %q", tc.i, tc.p[0], op)
|
|
||||||
} else if v != tc.p[1] {
|
|
||||||
t.Errorf("Invalid version for case %q: Expected %q, got: %q", tc.i, tc.p[1], v)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuildVersionRange(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
opStr string
|
|
||||||
vStr string
|
|
||||||
c func(comparator) bool
|
|
||||||
v string
|
|
||||||
}{
|
|
||||||
{">", "1.2.3", testGT, "1.2.3"},
|
|
||||||
{">=", "1.2.3", testGE, "1.2.3"},
|
|
||||||
{"<", "1.2.3", testLT, "1.2.3"},
|
|
||||||
{"<=", "1.2.3", testLE, "1.2.3"},
|
|
||||||
{"", "1.2.3", testEQ, "1.2.3"},
|
|
||||||
{"=", "1.2.3", testEQ, "1.2.3"},
|
|
||||||
{"==", "1.2.3", testEQ, "1.2.3"},
|
|
||||||
{"!=", "1.2.3", testNE, "1.2.3"},
|
|
||||||
{"!", "1.2.3", testNE, "1.2.3"},
|
|
||||||
{">>", "1.2.3", nil, ""}, // Invalid comparator
|
|
||||||
{"=", "invalid", nil, ""}, // Invalid version
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range tests {
|
|
||||||
if r, err := buildVersionRange(tc.opStr, tc.vStr); err != nil {
|
|
||||||
if tc.c != nil {
|
|
||||||
t.Errorf("Invalid for case %q: Expected %q, got error %q", strings.Join([]string{tc.opStr, tc.vStr}, ""), tc.v, err)
|
|
||||||
}
|
|
||||||
} else if r == nil {
|
|
||||||
t.Errorf("Invalid for case %q: got nil", strings.Join([]string{tc.opStr, tc.vStr}, ""))
|
|
||||||
} else {
|
|
||||||
// test version
|
|
||||||
if tv := MustParse(tc.v); !r.v.EQ(tv) {
|
|
||||||
t.Errorf("Invalid for case %q: Expected version %q, got: %q", strings.Join([]string{tc.opStr, tc.vStr}, ""), tv, r.v)
|
|
||||||
}
|
|
||||||
// test comparator
|
|
||||||
if r.c == nil {
|
|
||||||
t.Errorf("Invalid for case %q: got nil comparator", strings.Join([]string{tc.opStr, tc.vStr}, ""))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !tc.c(r.c) {
|
|
||||||
t.Errorf("Invalid comparator for case %q\n", strings.Join([]string{tc.opStr, tc.vStr}, ""))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSplitORParts(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
i []string
|
|
||||||
o [][]string
|
|
||||||
}{
|
|
||||||
{[]string{">1.2.3", "||", "<1.2.3", "||", "=1.2.3"}, [][]string{
|
|
||||||
[]string{">1.2.3"},
|
|
||||||
[]string{"<1.2.3"},
|
|
||||||
[]string{"=1.2.3"},
|
|
||||||
}},
|
|
||||||
{[]string{">1.2.3", "<1.2.3", "||", "=1.2.3"}, [][]string{
|
|
||||||
[]string{">1.2.3", "<1.2.3"},
|
|
||||||
[]string{"=1.2.3"},
|
|
||||||
}},
|
|
||||||
{[]string{">1.2.3", "||"}, nil},
|
|
||||||
{[]string{"||", ">1.2.3"}, nil},
|
|
||||||
}
|
|
||||||
for _, tc := range tests {
|
|
||||||
o, err := splitORParts(tc.i)
|
|
||||||
if err != nil && tc.o != nil {
|
|
||||||
t.Errorf("Unexpected error for case %q: %s", tc.i, err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(tc.o, o) {
|
|
||||||
t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.o, o)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetWildcardType(t *testing.T) {
|
|
||||||
wildcardTypeTests := []wildcardTypeTest{
|
|
||||||
{"x", majorWildcard},
|
|
||||||
{"1.x", minorWildcard},
|
|
||||||
{"1.2.x", patchWildcard},
|
|
||||||
{"fo.o.b.ar", noneWildcard},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range wildcardTypeTests {
|
|
||||||
o := getWildcardType(tc.input)
|
|
||||||
if o != tc.wildcardType {
|
|
||||||
t.Errorf("Invalid for case: %q: Expected %q, got: %q", tc.input, tc.wildcardType, o)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateVersionFromWildcard(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
i string
|
|
||||||
s string
|
|
||||||
}{
|
|
||||||
{"1.2.x", "1.2.0"},
|
|
||||||
{"1.x", "1.0.0"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range tests {
|
|
||||||
p := createVersionFromWildcard(tc.i)
|
|
||||||
if p != tc.s {
|
|
||||||
t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.s, p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIncrementMajorVersion(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
i string
|
|
||||||
s string
|
|
||||||
}{
|
|
||||||
{"1.2.3", "2.2.3"},
|
|
||||||
{"1.2", "2.2"},
|
|
||||||
{"foo.bar", ""},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range tests {
|
|
||||||
p, _ := incrementMajorVersion(tc.i)
|
|
||||||
if p != tc.s {
|
|
||||||
t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.s, p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIncrementMinorVersion(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
i string
|
|
||||||
s string
|
|
||||||
}{
|
|
||||||
{"1.2.3", "1.3.3"},
|
|
||||||
{"1.2", "1.3"},
|
|
||||||
{"foo.bar", ""},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range tests {
|
|
||||||
p, _ := incrementMinorVersion(tc.i)
|
|
||||||
if p != tc.s {
|
|
||||||
t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.s, p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExpandWildcardVersion(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
i [][]string
|
|
||||||
o [][]string
|
|
||||||
}{
|
|
||||||
{[][]string{[]string{"foox"}}, nil},
|
|
||||||
{[][]string{[]string{">=1.2.x"}}, [][]string{[]string{">=1.2.0"}}},
|
|
||||||
{[][]string{[]string{"<=1.2.x"}}, [][]string{[]string{"<1.3.0"}}},
|
|
||||||
{[][]string{[]string{">1.2.x"}}, [][]string{[]string{">=1.3.0"}}},
|
|
||||||
{[][]string{[]string{"<1.2.x"}}, [][]string{[]string{"<1.2.0"}}},
|
|
||||||
{[][]string{[]string{"!=1.2.x"}}, [][]string{[]string{"<1.2.0", ">=1.3.0"}}},
|
|
||||||
{[][]string{[]string{">=1.x"}}, [][]string{[]string{">=1.0.0"}}},
|
|
||||||
{[][]string{[]string{"<=1.x"}}, [][]string{[]string{"<2.0.0"}}},
|
|
||||||
{[][]string{[]string{">1.x"}}, [][]string{[]string{">=2.0.0"}}},
|
|
||||||
{[][]string{[]string{"<1.x"}}, [][]string{[]string{"<1.0.0"}}},
|
|
||||||
{[][]string{[]string{"!=1.x"}}, [][]string{[]string{"<1.0.0", ">=2.0.0"}}},
|
|
||||||
{[][]string{[]string{"1.2.x"}}, [][]string{[]string{">=1.2.0", "<1.3.0"}}},
|
|
||||||
{[][]string{[]string{"1.x"}}, [][]string{[]string{">=1.0.0", "<2.0.0"}}},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range tests {
|
|
||||||
o, _ := expandWildcardVersion(tc.i)
|
|
||||||
if !reflect.DeepEqual(tc.o, o) {
|
|
||||||
t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.o, o)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestVersionRangeToRange(t *testing.T) {
|
|
||||||
vr := versionRange{
|
|
||||||
v: MustParse("1.2.3"),
|
|
||||||
c: compLT,
|
|
||||||
}
|
|
||||||
rf := vr.rangeFunc()
|
|
||||||
if !rf(MustParse("1.2.2")) || rf(MustParse("1.2.3")) {
|
|
||||||
t.Errorf("Invalid conversion to range func")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRangeAND(t *testing.T) {
|
|
||||||
v := MustParse("1.2.2")
|
|
||||||
v1 := MustParse("1.2.1")
|
|
||||||
v2 := MustParse("1.2.3")
|
|
||||||
rf1 := Range(func(v Version) bool {
|
|
||||||
return v.GT(v1)
|
|
||||||
})
|
|
||||||
rf2 := Range(func(v Version) bool {
|
|
||||||
return v.LT(v2)
|
|
||||||
})
|
|
||||||
rf := rf1.AND(rf2)
|
|
||||||
if rf(v1) {
|
|
||||||
t.Errorf("Invalid rangefunc, accepted: %s", v1)
|
|
||||||
}
|
|
||||||
if rf(v2) {
|
|
||||||
t.Errorf("Invalid rangefunc, accepted: %s", v2)
|
|
||||||
}
|
|
||||||
if !rf(v) {
|
|
||||||
t.Errorf("Invalid rangefunc, did not accept: %s", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRangeOR(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
v Version
|
|
||||||
b bool
|
|
||||||
}{
|
|
||||||
{MustParse("1.2.0"), true},
|
|
||||||
{MustParse("1.2.2"), false},
|
|
||||||
{MustParse("1.2.4"), true},
|
|
||||||
}
|
|
||||||
v1 := MustParse("1.2.1")
|
|
||||||
v2 := MustParse("1.2.3")
|
|
||||||
rf1 := Range(func(v Version) bool {
|
|
||||||
return v.LT(v1)
|
|
||||||
})
|
|
||||||
rf2 := Range(func(v Version) bool {
|
|
||||||
return v.GT(v2)
|
|
||||||
})
|
|
||||||
rf := rf1.OR(rf2)
|
|
||||||
for _, tc := range tests {
|
|
||||||
if r := rf(tc.v); r != tc.b {
|
|
||||||
t.Errorf("Invalid for case %q: Expected %t, got %t", tc.v, tc.b, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseRange(t *testing.T) {
|
|
||||||
type tv struct {
|
|
||||||
v string
|
|
||||||
b bool
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
i string
|
|
||||||
t []tv
|
|
||||||
}{
|
|
||||||
// Simple expressions
|
|
||||||
{">1.2.3", []tv{
|
|
||||||
{"1.2.2", false},
|
|
||||||
{"1.2.3", false},
|
|
||||||
{"1.2.4", true},
|
|
||||||
}},
|
|
||||||
{">=1.2.3", []tv{
|
|
||||||
{"1.2.3", true},
|
|
||||||
{"1.2.4", true},
|
|
||||||
{"1.2.2", false},
|
|
||||||
}},
|
|
||||||
{"<1.2.3", []tv{
|
|
||||||
{"1.2.2", true},
|
|
||||||
{"1.2.3", false},
|
|
||||||
{"1.2.4", false},
|
|
||||||
}},
|
|
||||||
{"<=1.2.3", []tv{
|
|
||||||
{"1.2.2", true},
|
|
||||||
{"1.2.3", true},
|
|
||||||
{"1.2.4", false},
|
|
||||||
}},
|
|
||||||
{"1.2.3", []tv{
|
|
||||||
{"1.2.2", false},
|
|
||||||
{"1.2.3", true},
|
|
||||||
{"1.2.4", false},
|
|
||||||
}},
|
|
||||||
{"=1.2.3", []tv{
|
|
||||||
{"1.2.2", false},
|
|
||||||
{"1.2.3", true},
|
|
||||||
{"1.2.4", false},
|
|
||||||
}},
|
|
||||||
{"==1.2.3", []tv{
|
|
||||||
{"1.2.2", false},
|
|
||||||
{"1.2.3", true},
|
|
||||||
{"1.2.4", false},
|
|
||||||
}},
|
|
||||||
{"!=1.2.3", []tv{
|
|
||||||
{"1.2.2", true},
|
|
||||||
{"1.2.3", false},
|
|
||||||
{"1.2.4", true},
|
|
||||||
}},
|
|
||||||
{"!1.2.3", []tv{
|
|
||||||
{"1.2.2", true},
|
|
||||||
{"1.2.3", false},
|
|
||||||
{"1.2.4", true},
|
|
||||||
}},
|
|
||||||
// Simple Expression errors
|
|
||||||
{">>1.2.3", nil},
|
|
||||||
{"!1.2.3", nil},
|
|
||||||
{"1.0", nil},
|
|
||||||
{"string", nil},
|
|
||||||
{"", nil},
|
|
||||||
{"fo.ob.ar.x", nil},
|
|
||||||
// AND Expressions
|
|
||||||
{">1.2.2 <1.2.4", []tv{
|
|
||||||
{"1.2.2", false},
|
|
||||||
{"1.2.3", true},
|
|
||||||
{"1.2.4", false},
|
|
||||||
}},
|
|
||||||
{"<1.2.2 <1.2.4", []tv{
|
|
||||||
{"1.2.1", true},
|
|
||||||
{"1.2.2", false},
|
|
||||||
{"1.2.3", false},
|
|
||||||
{"1.2.4", false},
|
|
||||||
}},
|
|
||||||
{">1.2.2 <1.2.5 !=1.2.4", []tv{
|
|
||||||
{"1.2.2", false},
|
|
||||||
{"1.2.3", true},
|
|
||||||
{"1.2.4", false},
|
|
||||||
{"1.2.5", false},
|
|
||||||
}},
|
|
||||||
{">1.2.2 <1.2.5 !1.2.4", []tv{
|
|
||||||
{"1.2.2", false},
|
|
||||||
{"1.2.3", true},
|
|
||||||
{"1.2.4", false},
|
|
||||||
{"1.2.5", false},
|
|
||||||
}},
|
|
||||||
// OR Expressions
|
|
||||||
{">1.2.2 || <1.2.4", []tv{
|
|
||||||
{"1.2.2", true},
|
|
||||||
{"1.2.3", true},
|
|
||||||
{"1.2.4", true},
|
|
||||||
}},
|
|
||||||
{"<1.2.2 || >1.2.4", []tv{
|
|
||||||
{"1.2.2", false},
|
|
||||||
{"1.2.3", false},
|
|
||||||
{"1.2.4", false},
|
|
||||||
}},
|
|
||||||
// Wildcard expressions
|
|
||||||
{">1.x", []tv{
|
|
||||||
{"0.1.9", false},
|
|
||||||
{"1.2.6", false},
|
|
||||||
{"1.9.0", false},
|
|
||||||
{"2.0.0", true},
|
|
||||||
}},
|
|
||||||
{">1.2.x", []tv{
|
|
||||||
{"1.1.9", false},
|
|
||||||
{"1.2.6", false},
|
|
||||||
{"1.3.0", true},
|
|
||||||
}},
|
|
||||||
// Combined Expressions
|
|
||||||
{">1.2.2 <1.2.4 || >=2.0.0", []tv{
|
|
||||||
{"1.2.2", false},
|
|
||||||
{"1.2.3", true},
|
|
||||||
{"1.2.4", false},
|
|
||||||
{"2.0.0", true},
|
|
||||||
{"2.0.1", true},
|
|
||||||
}},
|
|
||||||
{"1.x || >=2.0.x <2.2.x", []tv{
|
|
||||||
{"0.9.2", false},
|
|
||||||
{"1.2.2", true},
|
|
||||||
{"2.0.0", true},
|
|
||||||
{"2.1.8", true},
|
|
||||||
{"2.2.0", false},
|
|
||||||
}},
|
|
||||||
{">1.2.2 <1.2.4 || >=2.0.0 <3.0.0", []tv{
|
|
||||||
{"1.2.2", false},
|
|
||||||
{"1.2.3", true},
|
|
||||||
{"1.2.4", false},
|
|
||||||
{"2.0.0", true},
|
|
||||||
{"2.0.1", true},
|
|
||||||
{"2.9.9", true},
|
|
||||||
{"3.0.0", false},
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range tests {
|
|
||||||
r, err := ParseRange(tc.i)
|
|
||||||
if err != nil && tc.t != nil {
|
|
||||||
t.Errorf("Error parsing range %q: %s", tc.i, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, tvc := range tc.t {
|
|
||||||
v := MustParse(tvc.v)
|
|
||||||
if res := r(v); res != tvc.b {
|
|
||||||
t.Errorf("Invalid for case %q matching %q: Expected %t, got: %t", tc.i, tvc.v, tvc.b, res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMustParseRange(t *testing.T) {
|
|
||||||
testCase := ">1.2.2 <1.2.4 || >=2.0.0 <3.0.0"
|
|
||||||
r := MustParseRange(testCase)
|
|
||||||
if !r(MustParse("1.2.3")) {
|
|
||||||
t.Errorf("Unexpected range behavior on MustParseRange")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMustParseRange_panic(t *testing.T) {
|
|
||||||
defer func() {
|
|
||||||
if recover() == nil {
|
|
||||||
t.Errorf("Should have panicked")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
_ = MustParseRange("invalid version")
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkRangeParseSimple(b *testing.B) {
|
|
||||||
const VERSION = ">1.0.0"
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
ParseRange(VERSION)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkRangeParseAverage(b *testing.B) {
|
|
||||||
const VERSION = ">=1.0.0 <2.0.0"
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
ParseRange(VERSION)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkRangeParseComplex(b *testing.B) {
|
|
||||||
const VERSION = ">=1.0.0 <2.0.0 || >=3.0.1 <4.0.0 !=3.0.3 || >=5.0.0"
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
ParseRange(VERSION)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkRangeMatchSimple(b *testing.B) {
|
|
||||||
const VERSION = ">1.0.0"
|
|
||||||
r, _ := ParseRange(VERSION)
|
|
||||||
v := MustParse("2.0.0")
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
r(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkRangeMatchAverage(b *testing.B) {
|
|
||||||
const VERSION = ">=1.0.0 <2.0.0"
|
|
||||||
r, _ := ParseRange(VERSION)
|
|
||||||
v := MustParse("1.2.3")
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
r(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkRangeMatchComplex(b *testing.B) {
|
|
||||||
const VERSION = ">=1.0.0 <2.0.0 || >=3.0.1 <4.0.0 !=3.0.3 || >=5.0.0"
|
|
||||||
r, _ := ParseRange(VERSION)
|
|
||||||
v := MustParse("5.0.1")
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
r(v)
|
|
||||||
}
|
|
||||||
}
|
|
418
vendor/github.com/blang/semver/semver.go
generated
vendored
418
vendor/github.com/blang/semver/semver.go
generated
vendored
|
@ -1,418 +0,0 @@
|
||||||
package semver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
numbers string = "0123456789"
|
|
||||||
alphas = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-"
|
|
||||||
alphanum = alphas + numbers
|
|
||||||
)
|
|
||||||
|
|
||||||
// SpecVersion is the latest fully supported spec version of semver
|
|
||||||
var SpecVersion = Version{
|
|
||||||
Major: 2,
|
|
||||||
Minor: 0,
|
|
||||||
Patch: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version represents a semver compatible version
|
|
||||||
type Version struct {
|
|
||||||
Major uint64
|
|
||||||
Minor uint64
|
|
||||||
Patch uint64
|
|
||||||
Pre []PRVersion
|
|
||||||
Build []string //No Precendence
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version to string
|
|
||||||
func (v Version) String() string {
|
|
||||||
b := make([]byte, 0, 5)
|
|
||||||
b = strconv.AppendUint(b, v.Major, 10)
|
|
||||||
b = append(b, '.')
|
|
||||||
b = strconv.AppendUint(b, v.Minor, 10)
|
|
||||||
b = append(b, '.')
|
|
||||||
b = strconv.AppendUint(b, v.Patch, 10)
|
|
||||||
|
|
||||||
if len(v.Pre) > 0 {
|
|
||||||
b = append(b, '-')
|
|
||||||
b = append(b, v.Pre[0].String()...)
|
|
||||||
|
|
||||||
for _, pre := range v.Pre[1:] {
|
|
||||||
b = append(b, '.')
|
|
||||||
b = append(b, pre.String()...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(v.Build) > 0 {
|
|
||||||
b = append(b, '+')
|
|
||||||
b = append(b, v.Build[0]...)
|
|
||||||
|
|
||||||
for _, build := range v.Build[1:] {
|
|
||||||
b = append(b, '.')
|
|
||||||
b = append(b, build...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equals checks if v is equal to o.
|
|
||||||
func (v Version) Equals(o Version) bool {
|
|
||||||
return (v.Compare(o) == 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EQ checks if v is equal to o.
|
|
||||||
func (v Version) EQ(o Version) bool {
|
|
||||||
return (v.Compare(o) == 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NE checks if v is not equal to o.
|
|
||||||
func (v Version) NE(o Version) bool {
|
|
||||||
return (v.Compare(o) != 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GT checks if v is greater than o.
|
|
||||||
func (v Version) GT(o Version) bool {
|
|
||||||
return (v.Compare(o) == 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GTE checks if v is greater than or equal to o.
|
|
||||||
func (v Version) GTE(o Version) bool {
|
|
||||||
return (v.Compare(o) >= 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GE checks if v is greater than or equal to o.
|
|
||||||
func (v Version) GE(o Version) bool {
|
|
||||||
return (v.Compare(o) >= 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LT checks if v is less than o.
|
|
||||||
func (v Version) LT(o Version) bool {
|
|
||||||
return (v.Compare(o) == -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LTE checks if v is less than or equal to o.
|
|
||||||
func (v Version) LTE(o Version) bool {
|
|
||||||
return (v.Compare(o) <= 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LE checks if v is less than or equal to o.
|
|
||||||
func (v Version) LE(o Version) bool {
|
|
||||||
return (v.Compare(o) <= 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare compares Versions v to o:
|
|
||||||
// -1 == v is less than o
|
|
||||||
// 0 == v is equal to o
|
|
||||||
// 1 == v is greater than o
|
|
||||||
func (v Version) Compare(o Version) int {
|
|
||||||
if v.Major != o.Major {
|
|
||||||
if v.Major > o.Major {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
if v.Minor != o.Minor {
|
|
||||||
if v.Minor > o.Minor {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
if v.Patch != o.Patch {
|
|
||||||
if v.Patch > o.Patch {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quick comparison if a version has no prerelease versions
|
|
||||||
if len(v.Pre) == 0 && len(o.Pre) == 0 {
|
|
||||||
return 0
|
|
||||||
} else if len(v.Pre) == 0 && len(o.Pre) > 0 {
|
|
||||||
return 1
|
|
||||||
} else if len(v.Pre) > 0 && len(o.Pre) == 0 {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
i := 0
|
|
||||||
for ; i < len(v.Pre) && i < len(o.Pre); i++ {
|
|
||||||
if comp := v.Pre[i].Compare(o.Pre[i]); comp == 0 {
|
|
||||||
continue
|
|
||||||
} else if comp == 1 {
|
|
||||||
return 1
|
|
||||||
} else {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If all pr versions are the equal but one has further prversion, this one greater
|
|
||||||
if i == len(v.Pre) && i == len(o.Pre) {
|
|
||||||
return 0
|
|
||||||
} else if i == len(v.Pre) && i < len(o.Pre) {
|
|
||||||
return -1
|
|
||||||
} else {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates v and returns error in case
|
|
||||||
func (v Version) Validate() error {
|
|
||||||
// Major, Minor, Patch already validated using uint64
|
|
||||||
|
|
||||||
for _, pre := range v.Pre {
|
|
||||||
if !pre.IsNum { //Numeric prerelease versions already uint64
|
|
||||||
if len(pre.VersionStr) == 0 {
|
|
||||||
return fmt.Errorf("Prerelease can not be empty %q", pre.VersionStr)
|
|
||||||
}
|
|
||||||
if !containsOnly(pre.VersionStr, alphanum) {
|
|
||||||
return fmt.Errorf("Invalid character(s) found in prerelease %q", pre.VersionStr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, build := range v.Build {
|
|
||||||
if len(build) == 0 {
|
|
||||||
return fmt.Errorf("Build meta data can not be empty %q", build)
|
|
||||||
}
|
|
||||||
if !containsOnly(build, alphanum) {
|
|
||||||
return fmt.Errorf("Invalid character(s) found in build meta data %q", build)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error
|
|
||||||
func New(s string) (vp *Version, err error) {
|
|
||||||
v, err := Parse(s)
|
|
||||||
vp = &v
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make is an alias for Parse, parses version string and returns a validated Version or error
|
|
||||||
func Make(s string) (Version, error) {
|
|
||||||
return Parse(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseTolerant allows for certain version specifications that do not strictly adhere to semver
|
|
||||||
// specs to be parsed by this library. It does so by normalizing versions before passing them to
|
|
||||||
// Parse(). It currently trims spaces, removes a "v" prefix, and adds a 0 patch number to versions
|
|
||||||
// with only major and minor components specified
|
|
||||||
func ParseTolerant(s string) (Version, error) {
|
|
||||||
s = strings.TrimSpace(s)
|
|
||||||
s = strings.TrimPrefix(s, "v")
|
|
||||||
|
|
||||||
// Split into major.minor.(patch+pr+meta)
|
|
||||||
parts := strings.SplitN(s, ".", 3)
|
|
||||||
if len(parts) < 3 {
|
|
||||||
if strings.ContainsAny(parts[len(parts)-1], "+-") {
|
|
||||||
return Version{}, errors.New("Short version cannot contain PreRelease/Build meta data")
|
|
||||||
}
|
|
||||||
for len(parts) < 3 {
|
|
||||||
parts = append(parts, "0")
|
|
||||||
}
|
|
||||||
s = strings.Join(parts, ".")
|
|
||||||
}
|
|
||||||
|
|
||||||
return Parse(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse parses version string and returns a validated Version or error
|
|
||||||
func Parse(s string) (Version, error) {
|
|
||||||
if len(s) == 0 {
|
|
||||||
return Version{}, errors.New("Version string empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Split into major.minor.(patch+pr+meta)
|
|
||||||
parts := strings.SplitN(s, ".", 3)
|
|
||||||
if len(parts) != 3 {
|
|
||||||
return Version{}, errors.New("No Major.Minor.Patch elements found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Major
|
|
||||||
if !containsOnly(parts[0], numbers) {
|
|
||||||
return Version{}, fmt.Errorf("Invalid character(s) found in major number %q", parts[0])
|
|
||||||
}
|
|
||||||
if hasLeadingZeroes(parts[0]) {
|
|
||||||
return Version{}, fmt.Errorf("Major number must not contain leading zeroes %q", parts[0])
|
|
||||||
}
|
|
||||||
major, err := strconv.ParseUint(parts[0], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return Version{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Minor
|
|
||||||
if !containsOnly(parts[1], numbers) {
|
|
||||||
return Version{}, fmt.Errorf("Invalid character(s) found in minor number %q", parts[1])
|
|
||||||
}
|
|
||||||
if hasLeadingZeroes(parts[1]) {
|
|
||||||
return Version{}, fmt.Errorf("Minor number must not contain leading zeroes %q", parts[1])
|
|
||||||
}
|
|
||||||
minor, err := strconv.ParseUint(parts[1], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return Version{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
v := Version{}
|
|
||||||
v.Major = major
|
|
||||||
v.Minor = minor
|
|
||||||
|
|
||||||
var build, prerelease []string
|
|
||||||
patchStr := parts[2]
|
|
||||||
|
|
||||||
if buildIndex := strings.IndexRune(patchStr, '+'); buildIndex != -1 {
|
|
||||||
build = strings.Split(patchStr[buildIndex+1:], ".")
|
|
||||||
patchStr = patchStr[:buildIndex]
|
|
||||||
}
|
|
||||||
|
|
||||||
if preIndex := strings.IndexRune(patchStr, '-'); preIndex != -1 {
|
|
||||||
prerelease = strings.Split(patchStr[preIndex+1:], ".")
|
|
||||||
patchStr = patchStr[:preIndex]
|
|
||||||
}
|
|
||||||
|
|
||||||
if !containsOnly(patchStr, numbers) {
|
|
||||||
return Version{}, fmt.Errorf("Invalid character(s) found in patch number %q", patchStr)
|
|
||||||
}
|
|
||||||
if hasLeadingZeroes(patchStr) {
|
|
||||||
return Version{}, fmt.Errorf("Patch number must not contain leading zeroes %q", patchStr)
|
|
||||||
}
|
|
||||||
patch, err := strconv.ParseUint(patchStr, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return Version{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
v.Patch = patch
|
|
||||||
|
|
||||||
// Prerelease
|
|
||||||
for _, prstr := range prerelease {
|
|
||||||
parsedPR, err := NewPRVersion(prstr)
|
|
||||||
if err != nil {
|
|
||||||
return Version{}, err
|
|
||||||
}
|
|
||||||
v.Pre = append(v.Pre, parsedPR)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build meta data
|
|
||||||
for _, str := range build {
|
|
||||||
if len(str) == 0 {
|
|
||||||
return Version{}, errors.New("Build meta data is empty")
|
|
||||||
}
|
|
||||||
if !containsOnly(str, alphanum) {
|
|
||||||
return Version{}, fmt.Errorf("Invalid character(s) found in build meta data %q", str)
|
|
||||||
}
|
|
||||||
v.Build = append(v.Build, str)
|
|
||||||
}
|
|
||||||
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustParse is like Parse but panics if the version cannot be parsed.
|
|
||||||
func MustParse(s string) Version {
|
|
||||||
v, err := Parse(s)
|
|
||||||
if err != nil {
|
|
||||||
panic(`semver: Parse(` + s + `): ` + err.Error())
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// PRVersion represents a PreRelease Version
|
|
||||||
type PRVersion struct {
|
|
||||||
VersionStr string
|
|
||||||
VersionNum uint64
|
|
||||||
IsNum bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPRVersion creates a new valid prerelease version
|
|
||||||
func NewPRVersion(s string) (PRVersion, error) {
|
|
||||||
if len(s) == 0 {
|
|
||||||
return PRVersion{}, errors.New("Prerelease is empty")
|
|
||||||
}
|
|
||||||
v := PRVersion{}
|
|
||||||
if containsOnly(s, numbers) {
|
|
||||||
if hasLeadingZeroes(s) {
|
|
||||||
return PRVersion{}, fmt.Errorf("Numeric PreRelease version must not contain leading zeroes %q", s)
|
|
||||||
}
|
|
||||||
num, err := strconv.ParseUint(s, 10, 64)
|
|
||||||
|
|
||||||
// Might never be hit, but just in case
|
|
||||||
if err != nil {
|
|
||||||
return PRVersion{}, err
|
|
||||||
}
|
|
||||||
v.VersionNum = num
|
|
||||||
v.IsNum = true
|
|
||||||
} else if containsOnly(s, alphanum) {
|
|
||||||
v.VersionStr = s
|
|
||||||
v.IsNum = false
|
|
||||||
} else {
|
|
||||||
return PRVersion{}, fmt.Errorf("Invalid character(s) found in prerelease %q", s)
|
|
||||||
}
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNumeric checks if prerelease-version is numeric
|
|
||||||
func (v PRVersion) IsNumeric() bool {
|
|
||||||
return v.IsNum
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare compares two PreRelease Versions v and o:
|
|
||||||
// -1 == v is less than o
|
|
||||||
// 0 == v is equal to o
|
|
||||||
// 1 == v is greater than o
|
|
||||||
func (v PRVersion) Compare(o PRVersion) int {
|
|
||||||
if v.IsNum && !o.IsNum {
|
|
||||||
return -1
|
|
||||||
} else if !v.IsNum && o.IsNum {
|
|
||||||
return 1
|
|
||||||
} else if v.IsNum && o.IsNum {
|
|
||||||
if v.VersionNum == o.VersionNum {
|
|
||||||
return 0
|
|
||||||
} else if v.VersionNum > o.VersionNum {
|
|
||||||
return 1
|
|
||||||
} else {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
} else { // both are Alphas
|
|
||||||
if v.VersionStr == o.VersionStr {
|
|
||||||
return 0
|
|
||||||
} else if v.VersionStr > o.VersionStr {
|
|
||||||
return 1
|
|
||||||
} else {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PreRelease version to string
|
|
||||||
func (v PRVersion) String() string {
|
|
||||||
if v.IsNum {
|
|
||||||
return strconv.FormatUint(v.VersionNum, 10)
|
|
||||||
}
|
|
||||||
return v.VersionStr
|
|
||||||
}
|
|
||||||
|
|
||||||
func containsOnly(s string, set string) bool {
|
|
||||||
return strings.IndexFunc(s, func(r rune) bool {
|
|
||||||
return !strings.ContainsRune(set, r)
|
|
||||||
}) == -1
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasLeadingZeroes(s string) bool {
|
|
||||||
return len(s) > 1 && s[0] == '0'
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBuildVersion creates a new valid build version
|
|
||||||
func NewBuildVersion(s string) (string, error) {
|
|
||||||
if len(s) == 0 {
|
|
||||||
return "", errors.New("Buildversion is empty")
|
|
||||||
}
|
|
||||||
if !containsOnly(s, alphanum) {
|
|
||||||
return "", fmt.Errorf("Invalid character(s) found in build meta data %q", s)
|
|
||||||
}
|
|
||||||
return s, nil
|
|
||||||
}
|
|
458
vendor/github.com/blang/semver/semver_test.go
generated
vendored
458
vendor/github.com/blang/semver/semver_test.go
generated
vendored
|
@ -1,458 +0,0 @@
|
||||||
package semver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func prstr(s string) PRVersion {
|
|
||||||
return PRVersion{s, 0, false}
|
|
||||||
}
|
|
||||||
|
|
||||||
func prnum(i uint64) PRVersion {
|
|
||||||
return PRVersion{"", i, true}
|
|
||||||
}
|
|
||||||
|
|
||||||
type formatTest struct {
|
|
||||||
v Version
|
|
||||||
result string
|
|
||||||
}
|
|
||||||
|
|
||||||
var formatTests = []formatTest{
|
|
||||||
{Version{1, 2, 3, nil, nil}, "1.2.3"},
|
|
||||||
{Version{0, 0, 1, nil, nil}, "0.0.1"},
|
|
||||||
{Version{0, 0, 1, []PRVersion{prstr("alpha"), prstr("preview")}, []string{"123", "456"}}, "0.0.1-alpha.preview+123.456"},
|
|
||||||
{Version{1, 2, 3, []PRVersion{prstr("alpha"), prnum(1)}, []string{"123", "456"}}, "1.2.3-alpha.1+123.456"},
|
|
||||||
{Version{1, 2, 3, []PRVersion{prstr("alpha"), prnum(1)}, nil}, "1.2.3-alpha.1"},
|
|
||||||
{Version{1, 2, 3, nil, []string{"123", "456"}}, "1.2.3+123.456"},
|
|
||||||
// Prereleases and build metadata hyphens
|
|
||||||
{Version{1, 2, 3, []PRVersion{prstr("alpha"), prstr("b-eta")}, []string{"123", "b-uild"}}, "1.2.3-alpha.b-eta+123.b-uild"},
|
|
||||||
{Version{1, 2, 3, nil, []string{"123", "b-uild"}}, "1.2.3+123.b-uild"},
|
|
||||||
{Version{1, 2, 3, []PRVersion{prstr("alpha"), prstr("b-eta")}, nil}, "1.2.3-alpha.b-eta"},
|
|
||||||
}
|
|
||||||
|
|
||||||
var tolerantFormatTests = []formatTest{
|
|
||||||
{Version{1, 2, 3, nil, nil}, "v1.2.3"},
|
|
||||||
{Version{1, 2, 3, nil, nil}, " 1.2.3 "},
|
|
||||||
{Version{1, 2, 0, nil, nil}, "1.2"},
|
|
||||||
{Version{1, 0, 0, nil, nil}, "1"},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringer(t *testing.T) {
|
|
||||||
for _, test := range formatTests {
|
|
||||||
if res := test.v.String(); res != test.result {
|
|
||||||
t.Errorf("Stringer, expected %q but got %q", test.result, res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParse(t *testing.T) {
|
|
||||||
for _, test := range formatTests {
|
|
||||||
if v, err := Parse(test.result); err != nil {
|
|
||||||
t.Errorf("Error parsing %q: %q", test.result, err)
|
|
||||||
} else if comp := v.Compare(test.v); comp != 0 {
|
|
||||||
t.Errorf("Parsing, expected %q but got %q, comp: %d ", test.v, v, comp)
|
|
||||||
} else if err := v.Validate(); err != nil {
|
|
||||||
t.Errorf("Error validating parsed version %q: %q", test.v, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseTolerant(t *testing.T) {
|
|
||||||
for _, test := range tolerantFormatTests {
|
|
||||||
if v, err := ParseTolerant(test.result); err != nil {
|
|
||||||
t.Errorf("Error parsing %q: %q", test.result, err)
|
|
||||||
} else if comp := v.Compare(test.v); comp != 0 {
|
|
||||||
t.Errorf("Parsing, expected %q but got %q, comp: %d ", test.v, v, comp)
|
|
||||||
} else if err := v.Validate(); err != nil {
|
|
||||||
t.Errorf("Error validating parsed version %q: %q", test.v, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMustParse(t *testing.T) {
|
|
||||||
_ = MustParse("32.2.1-alpha")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMustParse_panic(t *testing.T) {
|
|
||||||
defer func() {
|
|
||||||
if recover() == nil {
|
|
||||||
t.Errorf("Should have panicked")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
_ = MustParse("invalid version")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValidate(t *testing.T) {
|
|
||||||
for _, test := range formatTests {
|
|
||||||
if err := test.v.Validate(); err != nil {
|
|
||||||
t.Errorf("Error validating %q: %q", test.v, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type compareTest struct {
|
|
||||||
v1 Version
|
|
||||||
v2 Version
|
|
||||||
result int
|
|
||||||
}
|
|
||||||
|
|
||||||
var compareTests = []compareTest{
|
|
||||||
{Version{1, 0, 0, nil, nil}, Version{1, 0, 0, nil, nil}, 0},
|
|
||||||
{Version{2, 0, 0, nil, nil}, Version{1, 0, 0, nil, nil}, 1},
|
|
||||||
{Version{0, 1, 0, nil, nil}, Version{0, 1, 0, nil, nil}, 0},
|
|
||||||
{Version{0, 2, 0, nil, nil}, Version{0, 1, 0, nil, nil}, 1},
|
|
||||||
{Version{0, 0, 1, nil, nil}, Version{0, 0, 1, nil, nil}, 0},
|
|
||||||
{Version{0, 0, 2, nil, nil}, Version{0, 0, 1, nil, nil}, 1},
|
|
||||||
{Version{1, 2, 3, nil, nil}, Version{1, 2, 3, nil, nil}, 0},
|
|
||||||
{Version{2, 2, 4, nil, nil}, Version{1, 2, 4, nil, nil}, 1},
|
|
||||||
{Version{1, 3, 3, nil, nil}, Version{1, 2, 3, nil, nil}, 1},
|
|
||||||
{Version{1, 2, 4, nil, nil}, Version{1, 2, 3, nil, nil}, 1},
|
|
||||||
|
|
||||||
// Spec Examples #11
|
|
||||||
{Version{1, 0, 0, nil, nil}, Version{2, 0, 0, nil, nil}, -1},
|
|
||||||
{Version{2, 0, 0, nil, nil}, Version{2, 1, 0, nil, nil}, -1},
|
|
||||||
{Version{2, 1, 0, nil, nil}, Version{2, 1, 1, nil, nil}, -1},
|
|
||||||
|
|
||||||
// Spec Examples #9
|
|
||||||
{Version{1, 0, 0, nil, nil}, Version{1, 0, 0, []PRVersion{prstr("alpha")}, nil}, 1},
|
|
||||||
{Version{1, 0, 0, []PRVersion{prstr("alpha")}, nil}, Version{1, 0, 0, []PRVersion{prstr("alpha"), prnum(1)}, nil}, -1},
|
|
||||||
{Version{1, 0, 0, []PRVersion{prstr("alpha"), prnum(1)}, nil}, Version{1, 0, 0, []PRVersion{prstr("alpha"), prstr("beta")}, nil}, -1},
|
|
||||||
{Version{1, 0, 0, []PRVersion{prstr("alpha"), prstr("beta")}, nil}, Version{1, 0, 0, []PRVersion{prstr("beta")}, nil}, -1},
|
|
||||||
{Version{1, 0, 0, []PRVersion{prstr("beta")}, nil}, Version{1, 0, 0, []PRVersion{prstr("beta"), prnum(2)}, nil}, -1},
|
|
||||||
{Version{1, 0, 0, []PRVersion{prstr("beta"), prnum(2)}, nil}, Version{1, 0, 0, []PRVersion{prstr("beta"), prnum(11)}, nil}, -1},
|
|
||||||
{Version{1, 0, 0, []PRVersion{prstr("beta"), prnum(11)}, nil}, Version{1, 0, 0, []PRVersion{prstr("rc"), prnum(1)}, nil}, -1},
|
|
||||||
{Version{1, 0, 0, []PRVersion{prstr("rc"), prnum(1)}, nil}, Version{1, 0, 0, nil, nil}, -1},
|
|
||||||
|
|
||||||
// Ignore Build metadata
|
|
||||||
{Version{1, 0, 0, nil, []string{"1", "2", "3"}}, Version{1, 0, 0, nil, nil}, 0},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCompare(t *testing.T) {
|
|
||||||
for _, test := range compareTests {
|
|
||||||
if res := test.v1.Compare(test.v2); res != test.result {
|
|
||||||
t.Errorf("Comparing %q : %q, expected %d but got %d", test.v1, test.v2, test.result, res)
|
|
||||||
}
|
|
||||||
//Test counterpart
|
|
||||||
if res := test.v2.Compare(test.v1); res != -test.result {
|
|
||||||
t.Errorf("Comparing %q : %q, expected %d but got %d", test.v2, test.v1, -test.result, res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type wrongformatTest struct {
|
|
||||||
v *Version
|
|
||||||
str string
|
|
||||||
}
|
|
||||||
|
|
||||||
var wrongformatTests = []wrongformatTest{
|
|
||||||
{nil, ""},
|
|
||||||
{nil, "."},
|
|
||||||
{nil, "1."},
|
|
||||||
{nil, ".1"},
|
|
||||||
{nil, "a.b.c"},
|
|
||||||
{nil, "1.a.b"},
|
|
||||||
{nil, "1.1.a"},
|
|
||||||
{nil, "1.a.1"},
|
|
||||||
{nil, "a.1.1"},
|
|
||||||
{nil, ".."},
|
|
||||||
{nil, "1.."},
|
|
||||||
{nil, "1.1."},
|
|
||||||
{nil, "1..1"},
|
|
||||||
{nil, "1.1.+123"},
|
|
||||||
{nil, "1.1.-beta"},
|
|
||||||
{nil, "-1.1.1"},
|
|
||||||
{nil, "1.-1.1"},
|
|
||||||
{nil, "1.1.-1"},
|
|
||||||
// giant numbers
|
|
||||||
{nil, "20000000000000000000.1.1"},
|
|
||||||
{nil, "1.20000000000000000000.1"},
|
|
||||||
{nil, "1.1.20000000000000000000"},
|
|
||||||
{nil, "1.1.1-20000000000000000000"},
|
|
||||||
// Leading zeroes
|
|
||||||
{nil, "01.1.1"},
|
|
||||||
{nil, "001.1.1"},
|
|
||||||
{nil, "1.01.1"},
|
|
||||||
{nil, "1.001.1"},
|
|
||||||
{nil, "1.1.01"},
|
|
||||||
{nil, "1.1.001"},
|
|
||||||
{nil, "1.1.1-01"},
|
|
||||||
{nil, "1.1.1-001"},
|
|
||||||
{nil, "1.1.1-beta.01"},
|
|
||||||
{nil, "1.1.1-beta.001"},
|
|
||||||
{&Version{0, 0, 0, []PRVersion{prstr("!")}, nil}, "0.0.0-!"},
|
|
||||||
{&Version{0, 0, 0, nil, []string{"!"}}, "0.0.0+!"},
|
|
||||||
// empty prversion
|
|
||||||
{&Version{0, 0, 0, []PRVersion{prstr(""), prstr("alpha")}, nil}, "0.0.0-.alpha"},
|
|
||||||
// empty build meta data
|
|
||||||
{&Version{0, 0, 0, []PRVersion{prstr("alpha")}, []string{""}}, "0.0.0-alpha+"},
|
|
||||||
{&Version{0, 0, 0, []PRVersion{prstr("alpha")}, []string{"test", ""}}, "0.0.0-alpha+test."},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWrongFormat(t *testing.T) {
|
|
||||||
for _, test := range wrongformatTests {
|
|
||||||
|
|
||||||
if res, err := Parse(test.str); err == nil {
|
|
||||||
t.Errorf("Parsing wrong format version %q, expected error but got %q", test.str, res)
|
|
||||||
}
|
|
||||||
|
|
||||||
if test.v != nil {
|
|
||||||
if err := test.v.Validate(); err == nil {
|
|
||||||
t.Errorf("Validating wrong format version %q (%q), expected error", test.v, test.str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var wrongTolerantFormatTests = []wrongformatTest{
|
|
||||||
{nil, "1.0+abc"},
|
|
||||||
{nil, "1.0-rc.1"},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWrongTolerantFormat(t *testing.T) {
|
|
||||||
for _, test := range wrongTolerantFormatTests {
|
|
||||||
if res, err := ParseTolerant(test.str); err == nil {
|
|
||||||
t.Errorf("Parsing wrong format version %q, expected error but got %q", test.str, res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCompareHelper(t *testing.T) {
|
|
||||||
v := Version{1, 0, 0, []PRVersion{prstr("alpha")}, nil}
|
|
||||||
v1 := Version{1, 0, 0, nil, nil}
|
|
||||||
if !v.EQ(v) {
|
|
||||||
t.Errorf("%q should be equal to %q", v, v)
|
|
||||||
}
|
|
||||||
if !v.Equals(v) {
|
|
||||||
t.Errorf("%q should be equal to %q", v, v)
|
|
||||||
}
|
|
||||||
if !v1.NE(v) {
|
|
||||||
t.Errorf("%q should not be equal to %q", v1, v)
|
|
||||||
}
|
|
||||||
if !v.GTE(v) {
|
|
||||||
t.Errorf("%q should be greater than or equal to %q", v, v)
|
|
||||||
}
|
|
||||||
if !v.LTE(v) {
|
|
||||||
t.Errorf("%q should be less than or equal to %q", v, v)
|
|
||||||
}
|
|
||||||
if !v.LT(v1) {
|
|
||||||
t.Errorf("%q should be less than %q", v, v1)
|
|
||||||
}
|
|
||||||
if !v.LTE(v1) {
|
|
||||||
t.Errorf("%q should be less than or equal %q", v, v1)
|
|
||||||
}
|
|
||||||
if !v.LE(v1) {
|
|
||||||
t.Errorf("%q should be less than or equal %q", v, v1)
|
|
||||||
}
|
|
||||||
if !v1.GT(v) {
|
|
||||||
t.Errorf("%q should be greater than %q", v1, v)
|
|
||||||
}
|
|
||||||
if !v1.GTE(v) {
|
|
||||||
t.Errorf("%q should be greater than or equal %q", v1, v)
|
|
||||||
}
|
|
||||||
if !v1.GE(v) {
|
|
||||||
t.Errorf("%q should be greater than or equal %q", v1, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPreReleaseVersions(t *testing.T) {
|
|
||||||
p1, err := NewPRVersion("123")
|
|
||||||
if !p1.IsNumeric() {
|
|
||||||
t.Errorf("Expected numeric prversion, got %q", p1)
|
|
||||||
}
|
|
||||||
if p1.VersionNum != 123 {
|
|
||||||
t.Error("Wrong prversion number")
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Not expected error %q", err)
|
|
||||||
}
|
|
||||||
p2, err := NewPRVersion("alpha")
|
|
||||||
if p2.IsNumeric() {
|
|
||||||
t.Errorf("Expected non-numeric prversion, got %q", p2)
|
|
||||||
}
|
|
||||||
if p2.VersionStr != "alpha" {
|
|
||||||
t.Error("Wrong prversion string")
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Not expected error %q", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuildMetaDataVersions(t *testing.T) {
|
|
||||||
_, err := NewBuildVersion("123")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error %q", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = NewBuildVersion("build")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error %q", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = NewBuildVersion("test?")
|
|
||||||
if err == nil {
|
|
||||||
t.Error("Expected error, got none")
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = NewBuildVersion("")
|
|
||||||
if err == nil {
|
|
||||||
t.Error("Expected error, got none")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewHelper(t *testing.T) {
|
|
||||||
v, err := New("1.2.3")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error %q", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// New returns pointer
|
|
||||||
if v == nil {
|
|
||||||
t.Fatal("Version is nil")
|
|
||||||
}
|
|
||||||
if v.Compare(Version{1, 2, 3, nil, nil}) != 0 {
|
|
||||||
t.Fatal("Unexpected comparison problem")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMakeHelper(t *testing.T) {
|
|
||||||
v, err := Make("1.2.3")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error %q", err)
|
|
||||||
}
|
|
||||||
if v.Compare(Version{1, 2, 3, nil, nil}) != 0 {
|
|
||||||
t.Fatal("Unexpected comparison problem")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkParseSimple(b *testing.B) {
|
|
||||||
const VERSION = "0.0.1"
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
Parse(VERSION)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkParseComplex(b *testing.B) {
|
|
||||||
const VERSION = "0.0.1-alpha.preview+123.456"
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
Parse(VERSION)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkParseAverage(b *testing.B) {
|
|
||||||
l := len(formatTests)
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
Parse(formatTests[n%l].result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkParseTolerantAverage(b *testing.B) {
|
|
||||||
l := len(tolerantFormatTests)
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
ParseTolerant(tolerantFormatTests[n%l].result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkStringSimple(b *testing.B) {
|
|
||||||
const VERSION = "0.0.1"
|
|
||||||
v, _ := Parse(VERSION)
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
v.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkStringLarger(b *testing.B) {
|
|
||||||
const VERSION = "11.15.2012"
|
|
||||||
v, _ := Parse(VERSION)
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
v.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkStringComplex(b *testing.B) {
|
|
||||||
const VERSION = "0.0.1-alpha.preview+123.456"
|
|
||||||
v, _ := Parse(VERSION)
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
v.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkStringAverage(b *testing.B) {
|
|
||||||
l := len(formatTests)
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
formatTests[n%l].v.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkValidateSimple(b *testing.B) {
|
|
||||||
const VERSION = "0.0.1"
|
|
||||||
v, _ := Parse(VERSION)
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
v.Validate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkValidateComplex(b *testing.B) {
|
|
||||||
const VERSION = "0.0.1-alpha.preview+123.456"
|
|
||||||
v, _ := Parse(VERSION)
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
v.Validate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkValidateAverage(b *testing.B) {
|
|
||||||
l := len(formatTests)
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
formatTests[n%l].v.Validate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkCompareSimple(b *testing.B) {
|
|
||||||
const VERSION = "0.0.1"
|
|
||||||
v, _ := Parse(VERSION)
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
v.Compare(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkCompareComplex(b *testing.B) {
|
|
||||||
const VERSION = "0.0.1-alpha.preview+123.456"
|
|
||||||
v, _ := Parse(VERSION)
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
v.Compare(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkCompareAverage(b *testing.B) {
|
|
||||||
l := len(compareTests)
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
compareTests[n%l].v1.Compare((compareTests[n%l].v2))
|
|
||||||
}
|
|
||||||
}
|
|
28
vendor/github.com/blang/semver/sort.go
generated
vendored
28
vendor/github.com/blang/semver/sort.go
generated
vendored
|
@ -1,28 +0,0 @@
|
||||||
package semver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Versions represents multiple versions.
|
|
||||||
type Versions []Version
|
|
||||||
|
|
||||||
// Len returns length of version collection
|
|
||||||
func (s Versions) Len() int {
|
|
||||||
return len(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swap swaps two versions inside the collection by its indices
|
|
||||||
func (s Versions) Swap(i, j int) {
|
|
||||||
s[i], s[j] = s[j], s[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Less checks if version at index i is less than version at index j
|
|
||||||
func (s Versions) Less(i, j int) bool {
|
|
||||||
return s[i].LT(s[j])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort sorts a slice of versions
|
|
||||||
func Sort(versions []Version) {
|
|
||||||
sort.Sort(Versions(versions))
|
|
||||||
}
|
|
30
vendor/github.com/blang/semver/sort_test.go
generated
vendored
30
vendor/github.com/blang/semver/sort_test.go
generated
vendored
|
@ -1,30 +0,0 @@
|
||||||
package semver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSort(t *testing.T) {
|
|
||||||
v100, _ := Parse("1.0.0")
|
|
||||||
v010, _ := Parse("0.1.0")
|
|
||||||
v001, _ := Parse("0.0.1")
|
|
||||||
versions := []Version{v010, v100, v001}
|
|
||||||
Sort(versions)
|
|
||||||
|
|
||||||
correct := []Version{v001, v010, v100}
|
|
||||||
if !reflect.DeepEqual(versions, correct) {
|
|
||||||
t.Fatalf("Sort returned wrong order: %s", versions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkSort(b *testing.B) {
|
|
||||||
v100, _ := Parse("1.0.0")
|
|
||||||
v010, _ := Parse("0.1.0")
|
|
||||||
v001, _ := Parse("0.0.1")
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
Sort([]Version{v010, v100, v001})
|
|
||||||
}
|
|
||||||
}
|
|
30
vendor/github.com/blang/semver/sql.go
generated
vendored
30
vendor/github.com/blang/semver/sql.go
generated
vendored
|
@ -1,30 +0,0 @@
|
||||||
package semver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql/driver"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Scan implements the database/sql.Scanner interface.
|
|
||||||
func (v *Version) Scan(src interface{}) (err error) {
|
|
||||||
var str string
|
|
||||||
switch src := src.(type) {
|
|
||||||
case string:
|
|
||||||
str = src
|
|
||||||
case []byte:
|
|
||||||
str = string(src)
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("Version.Scan: cannot convert %T to string.", src)
|
|
||||||
}
|
|
||||||
|
|
||||||
if t, err := Parse(str); err == nil {
|
|
||||||
*v = t
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value implements the database/sql/driver.Valuer interface.
|
|
||||||
func (v Version) Value() (driver.Value, error) {
|
|
||||||
return v.String(), nil
|
|
||||||
}
|
|
38
vendor/github.com/blang/semver/sql_test.go
generated
vendored
38
vendor/github.com/blang/semver/sql_test.go
generated
vendored
|
@ -1,38 +0,0 @@
|
||||||
package semver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type scanTest struct {
|
|
||||||
val interface{}
|
|
||||||
shouldError bool
|
|
||||||
expected string
|
|
||||||
}
|
|
||||||
|
|
||||||
var scanTests = []scanTest{
|
|
||||||
{"1.2.3", false, "1.2.3"},
|
|
||||||
{[]byte("1.2.3"), false, "1.2.3"},
|
|
||||||
{7, true, ""},
|
|
||||||
{7e4, true, ""},
|
|
||||||
{true, true, ""},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestScanString(t *testing.T) {
|
|
||||||
for _, tc := range scanTests {
|
|
||||||
s := &Version{}
|
|
||||||
err := s.Scan(tc.val)
|
|
||||||
if tc.shouldError {
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("Scan did not return an error on %v (%T)", tc.val, tc.val)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Scan returned an unexpected error: %s (%T) on %v (%T)", tc.val, tc.val, tc.val, tc.val)
|
|
||||||
}
|
|
||||||
if val, _ := s.Value(); val != tc.expected {
|
|
||||||
t.Errorf("Wrong Value returned, expected %q, got %q", tc.expected, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
2
vendor/github.com/coreos/go-oidc/.gitignore
generated
vendored
2
vendor/github.com/coreos/go-oidc/.gitignore
generated
vendored
|
@ -1,2 +0,0 @@
|
||||||
/bin
|
|
||||||
/gopath
|
|
16
vendor/github.com/coreos/go-oidc/.travis.yml
generated
vendored
16
vendor/github.com/coreos/go-oidc/.travis.yml
generated
vendored
|
@ -1,16 +0,0 @@
|
||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.7.5
|
|
||||||
- 1.8
|
|
||||||
|
|
||||||
install:
|
|
||||||
- go get -v -t github.com/coreos/go-oidc/...
|
|
||||||
- go get golang.org/x/tools/cmd/cover
|
|
||||||
- go get github.com/golang/lint/golint
|
|
||||||
|
|
||||||
script:
|
|
||||||
- ./test
|
|
||||||
|
|
||||||
notifications:
|
|
||||||
email: false
|
|
71
vendor/github.com/coreos/go-oidc/CONTRIBUTING.md
generated
vendored
71
vendor/github.com/coreos/go-oidc/CONTRIBUTING.md
generated
vendored
|
@ -1,71 +0,0 @@
|
||||||
# How to Contribute
|
|
||||||
|
|
||||||
CoreOS projects are [Apache 2.0 licensed](LICENSE) and accept contributions via
|
|
||||||
GitHub pull requests. This document outlines some of the conventions on
|
|
||||||
development workflow, commit message formatting, contact points and other
|
|
||||||
resources to make it easier to get your contribution accepted.
|
|
||||||
|
|
||||||
# Certificate of Origin
|
|
||||||
|
|
||||||
By contributing to this project you agree to the Developer Certificate of
|
|
||||||
Origin (DCO). This document was created by the Linux Kernel community and is a
|
|
||||||
simple statement that you, as a contributor, have the legal right to make the
|
|
||||||
contribution. See the [DCO](DCO) file for details.
|
|
||||||
|
|
||||||
# Email and Chat
|
|
||||||
|
|
||||||
The project currently uses the general CoreOS email list and IRC channel:
|
|
||||||
- Email: [coreos-dev](https://groups.google.com/forum/#!forum/coreos-dev)
|
|
||||||
- IRC: #[coreos](irc://irc.freenode.org:6667/#coreos) IRC channel on freenode.org
|
|
||||||
|
|
||||||
Please avoid emailing maintainers found in the MAINTAINERS file directly. They
|
|
||||||
are very busy and read the mailing lists.
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
- Fork the repository on GitHub
|
|
||||||
- Read the [README](README.md) for build and test instructions
|
|
||||||
- Play with the project, submit bugs, submit patches!
|
|
||||||
|
|
||||||
## Contribution Flow
|
|
||||||
|
|
||||||
This is a rough outline of what a contributor's workflow looks like:
|
|
||||||
|
|
||||||
- Create a topic branch from where you want to base your work (usually master).
|
|
||||||
- Make commits of logical units.
|
|
||||||
- Make sure your commit messages are in the proper format (see below).
|
|
||||||
- Push your changes to a topic branch in your fork of the repository.
|
|
||||||
- Make sure the tests pass, and add any new tests as appropriate.
|
|
||||||
- Submit a pull request to the original repository.
|
|
||||||
|
|
||||||
Thanks for your contributions!
|
|
||||||
|
|
||||||
### Format of the Commit Message
|
|
||||||
|
|
||||||
We follow a rough convention for commit messages that is designed to answer two
|
|
||||||
questions: what changed and why. The subject line should feature the what and
|
|
||||||
the body of the commit should describe the why.
|
|
||||||
|
|
||||||
```
|
|
||||||
scripts: add the test-cluster command
|
|
||||||
|
|
||||||
this uses tmux to setup a test cluster that you can easily kill and
|
|
||||||
start for debugging.
|
|
||||||
|
|
||||||
Fixes #38
|
|
||||||
```
|
|
||||||
|
|
||||||
The format can be described more formally as follows:
|
|
||||||
|
|
||||||
```
|
|
||||||
<subsystem>: <what changed>
|
|
||||||
<BLANK LINE>
|
|
||||||
<why this change was made>
|
|
||||||
<BLANK LINE>
|
|
||||||
<footer>
|
|
||||||
```
|
|
||||||
|
|
||||||
The first line is the subject and should be no longer than 70 characters, the
|
|
||||||
second line is always blank, and other lines should be wrapped at 80 characters.
|
|
||||||
This allows the message to be easier to read on GitHub as well as in various
|
|
||||||
git tools.
|
|
36
vendor/github.com/coreos/go-oidc/DCO
generated
vendored
36
vendor/github.com/coreos/go-oidc/DCO
generated
vendored
|
@ -1,36 +0,0 @@
|
||||||
Developer Certificate of Origin
|
|
||||||
Version 1.1
|
|
||||||
|
|
||||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
|
||||||
660 York Street, Suite 102,
|
|
||||||
San Francisco, CA 94110 USA
|
|
||||||
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies of this
|
|
||||||
license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
|
|
||||||
Developer's Certificate of Origin 1.1
|
|
||||||
|
|
||||||
By making a contribution to this project, I certify that:
|
|
||||||
|
|
||||||
(a) The contribution was created in whole or in part by me and I
|
|
||||||
have the right to submit it under the open source license
|
|
||||||
indicated in the file; or
|
|
||||||
|
|
||||||
(b) The contribution is based upon previous work that, to the best
|
|
||||||
of my knowledge, is covered under an appropriate open source
|
|
||||||
license and I have the right under that license to submit that
|
|
||||||
work with modifications, whether created in whole or in part
|
|
||||||
by me, under the same open source license (unless I am
|
|
||||||
permitted to submit under a different license), as indicated
|
|
||||||
in the file; or
|
|
||||||
|
|
||||||
(c) The contribution was provided directly to me by some other
|
|
||||||
person who certified (a), (b) or (c) and I have not modified
|
|
||||||
it.
|
|
||||||
|
|
||||||
(d) I understand and agree that this project and the contribution
|
|
||||||
are public and that a record of the contribution (including all
|
|
||||||
personal information I submit with it, including my sign-off) is
|
|
||||||
maintained indefinitely and may be redistributed consistent with
|
|
||||||
this project or the open source license(s) involved.
|
|
3
vendor/github.com/coreos/go-oidc/MAINTAINERS
generated
vendored
3
vendor/github.com/coreos/go-oidc/MAINTAINERS
generated
vendored
|
@ -1,3 +0,0 @@
|
||||||
Bobby Rullo <bobby.rullo@coreos.com> (@bobbyrullo)
|
|
||||||
Ed Rooth <ed.rooth@coreos.com> (@sym3tri)
|
|
||||||
Eric Chiang <eric.chiang@coreos.com> (@ericchiang)
|
|
5
vendor/github.com/coreos/go-oidc/NOTICE
generated
vendored
5
vendor/github.com/coreos/go-oidc/NOTICE
generated
vendored
|
@ -1,5 +0,0 @@
|
||||||
CoreOS Project
|
|
||||||
Copyright 2014 CoreOS, Inc
|
|
||||||
|
|
||||||
This product includes software developed at CoreOS, Inc.
|
|
||||||
(http://www.coreos.com/).
|
|
72
vendor/github.com/coreos/go-oidc/README.md
generated
vendored
72
vendor/github.com/coreos/go-oidc/README.md
generated
vendored
|
@ -1,72 +0,0 @@
|
||||||
# go-oidc
|
|
||||||
|
|
||||||
[](https://godoc.org/github.com/coreos/go-oidc)
|
|
||||||
[](https://travis-ci.org/coreos/go-oidc)
|
|
||||||
|
|
||||||
## OpenID Connect support for Go
|
|
||||||
|
|
||||||
This package enables OpenID Connect support for the [golang.org/x/oauth2](https://godoc.org/golang.org/x/oauth2) package.
|
|
||||||
|
|
||||||
```go
|
|
||||||
provider, err := oidc.NewProvider(ctx, "https://accounts.google.com")
|
|
||||||
if err != nil {
|
|
||||||
// handle error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure an OpenID Connect aware OAuth2 client.
|
|
||||||
oauth2Config := oauth2.Config{
|
|
||||||
ClientID: clientID,
|
|
||||||
ClientSecret: clientSecret,
|
|
||||||
RedirectURL: redirectURL,
|
|
||||||
|
|
||||||
// Discovery returns the OAuth2 endpoints.
|
|
||||||
Endpoint: provider.Endpoint(),
|
|
||||||
|
|
||||||
// "openid" is a required scope for OpenID Connect flows.
|
|
||||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
OAuth2 redirects are unchanged.
|
|
||||||
|
|
||||||
```go
|
|
||||||
func handleRedirect(w http.ResponseWriter, r *http.Request) {
|
|
||||||
http.Redirect(w, r, oauth2Config.AuthCodeURL(state), http.StatusFound)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The on responses, the provider can be used to verify ID Tokens.
|
|
||||||
|
|
||||||
```go
|
|
||||||
var verifier = provider.Verifier(&oidc.Config{ClientID: clientID})
|
|
||||||
|
|
||||||
func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// Verify state and errors.
|
|
||||||
|
|
||||||
oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code"))
|
|
||||||
if err != nil {
|
|
||||||
// handle error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the ID Token from OAuth2 token.
|
|
||||||
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
|
|
||||||
if !ok {
|
|
||||||
// handle missing token
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse and verify ID Token payload.
|
|
||||||
idToken, err := verifier.Verify(ctx, rawIDToken)
|
|
||||||
if err != nil {
|
|
||||||
// handle error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract custom claims
|
|
||||||
var claims struct {
|
|
||||||
Email string `json:"email"`
|
|
||||||
Verified bool `json:"email_verified"`
|
|
||||||
}
|
|
||||||
if err := idToken.Claims(&claims); err != nil {
|
|
||||||
// handle error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
7
vendor/github.com/coreos/go-oidc/http/client.go
generated
vendored
7
vendor/github.com/coreos/go-oidc/http/client.go
generated
vendored
|
@ -1,7 +0,0 @@
|
||||||
package http
|
|
||||||
|
|
||||||
import "net/http"
|
|
||||||
|
|
||||||
type Client interface {
|
|
||||||
Do(*http.Request) (*http.Response, error)
|
|
||||||
}
|
|
2
vendor/github.com/coreos/go-oidc/http/doc.go
generated
vendored
2
vendor/github.com/coreos/go-oidc/http/doc.go
generated
vendored
|
@ -1,2 +0,0 @@
|
||||||
// Package http is DEPRECATED. Use net/http instead.
|
|
||||||
package http
|
|
161
vendor/github.com/coreos/go-oidc/http/http.go
generated
vendored
161
vendor/github.com/coreos/go-oidc/http/http.go
generated
vendored
|
@ -1,161 +0,0 @@
|
||||||
package http
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"path"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func WriteError(w http.ResponseWriter, code int, msg string) {
|
|
||||||
e := struct {
|
|
||||||
Error string `json:"error"`
|
|
||||||
}{
|
|
||||||
Error: msg,
|
|
||||||
}
|
|
||||||
b, err := json.Marshal(e)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("go-oidc: failed to marshal %#v: %v", e, err)
|
|
||||||
code = http.StatusInternalServerError
|
|
||||||
b = []byte(`{"error":"server_error"}`)
|
|
||||||
}
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(code)
|
|
||||||
w.Write(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BasicAuth parses a username and password from the request's
|
|
||||||
// Authorization header. This was pulled from golang master:
|
|
||||||
// https://codereview.appspot.com/76540043
|
|
||||||
func BasicAuth(r *http.Request) (username, password string, ok bool) {
|
|
||||||
auth := r.Header.Get("Authorization")
|
|
||||||
if auth == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasPrefix(auth, "Basic ") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(auth, "Basic "))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cs := string(c)
|
|
||||||
s := strings.IndexByte(cs, ':')
|
|
||||||
if s < 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return cs[:s], cs[s+1:], true
|
|
||||||
}
|
|
||||||
|
|
||||||
func cacheControlMaxAge(hdr string) (time.Duration, bool, error) {
|
|
||||||
for _, field := range strings.Split(hdr, ",") {
|
|
||||||
parts := strings.SplitN(strings.TrimSpace(field), "=", 2)
|
|
||||||
k := strings.ToLower(strings.TrimSpace(parts[0]))
|
|
||||||
if k != "max-age" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(parts) == 1 {
|
|
||||||
return 0, false, errors.New("max-age has no value")
|
|
||||||
}
|
|
||||||
|
|
||||||
v := strings.TrimSpace(parts[1])
|
|
||||||
if v == "" {
|
|
||||||
return 0, false, errors.New("max-age has empty value")
|
|
||||||
}
|
|
||||||
|
|
||||||
age, err := strconv.Atoi(v)
|
|
||||||
if err != nil {
|
|
||||||
return 0, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if age <= 0 {
|
|
||||||
return 0, false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return time.Duration(age) * time.Second, true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func expires(date, expires string) (time.Duration, bool, error) {
|
|
||||||
if date == "" || expires == "" {
|
|
||||||
return 0, false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var te time.Time
|
|
||||||
var err error
|
|
||||||
if expires == "0" {
|
|
||||||
return 0, false, nil
|
|
||||||
}
|
|
||||||
te, err = time.Parse(time.RFC1123, expires)
|
|
||||||
if err != nil {
|
|
||||||
return 0, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
td, err := time.Parse(time.RFC1123, date)
|
|
||||||
if err != nil {
|
|
||||||
return 0, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ttl := te.Sub(td)
|
|
||||||
|
|
||||||
// headers indicate data already expired, caller should not
|
|
||||||
// have to care about this case
|
|
||||||
if ttl <= 0 {
|
|
||||||
return 0, false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return ttl, true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Cacheable(hdr http.Header) (time.Duration, bool, error) {
|
|
||||||
ttl, ok, err := cacheControlMaxAge(hdr.Get("Cache-Control"))
|
|
||||||
if err != nil || ok {
|
|
||||||
return ttl, ok, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return expires(hdr.Get("Date"), hdr.Get("Expires"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// MergeQuery appends additional query values to an existing URL.
|
|
||||||
func MergeQuery(u url.URL, q url.Values) url.URL {
|
|
||||||
uv := u.Query()
|
|
||||||
for k, vs := range q {
|
|
||||||
for _, v := range vs {
|
|
||||||
uv.Add(k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
u.RawQuery = uv.Encode()
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewResourceLocation appends a resource id to the end of the requested URL path.
|
|
||||||
func NewResourceLocation(reqURL *url.URL, id string) string {
|
|
||||||
var u url.URL
|
|
||||||
u = *reqURL
|
|
||||||
u.Path = path.Join(u.Path, id)
|
|
||||||
u.RawQuery = ""
|
|
||||||
u.Fragment = ""
|
|
||||||
return u.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// CopyRequest returns a clone of the provided *http.Request.
|
|
||||||
// The returned object is a shallow copy of the struct and a
|
|
||||||
// deep copy of its Header field.
|
|
||||||
func CopyRequest(r *http.Request) *http.Request {
|
|
||||||
r2 := *r
|
|
||||||
r2.Header = make(http.Header)
|
|
||||||
for k, s := range r.Header {
|
|
||||||
r2.Header[k] = s
|
|
||||||
}
|
|
||||||
return &r2
|
|
||||||
}
|
|
374
vendor/github.com/coreos/go-oidc/http/http_test.go
generated
vendored
374
vendor/github.com/coreos/go-oidc/http/http_test.go
generated
vendored
|
@ -1,374 +0,0 @@
|
||||||
package http
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCacheControlMaxAgeSuccess(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
hdr string
|
|
||||||
wantAge time.Duration
|
|
||||||
wantOK bool
|
|
||||||
}{
|
|
||||||
{"max-age=12", 12 * time.Second, true},
|
|
||||||
{"max-age=-12", 0, false},
|
|
||||||
{"max-age=0", 0, false},
|
|
||||||
{"public, max-age=12", 12 * time.Second, true},
|
|
||||||
{"public, max-age=40192, must-revalidate", 40192 * time.Second, true},
|
|
||||||
{"public, not-max-age=12, must-revalidate", time.Duration(0), false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
maxAge, ok, err := cacheControlMaxAge(tt.hdr)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("case %d: err=%v", i, err)
|
|
||||||
}
|
|
||||||
if tt.wantAge != maxAge {
|
|
||||||
t.Errorf("case %d: want=%d got=%d", i, tt.wantAge, maxAge)
|
|
||||||
}
|
|
||||||
if tt.wantOK != ok {
|
|
||||||
t.Errorf("case %d: incorrect ok value: want=%t got=%t", i, tt.wantOK, ok)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCacheControlMaxAgeFail(t *testing.T) {
|
|
||||||
tests := []string{
|
|
||||||
"max-age=aasdf",
|
|
||||||
"max-age=",
|
|
||||||
"max-age",
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
_, ok, err := cacheControlMaxAge(tt)
|
|
||||||
if ok {
|
|
||||||
t.Errorf("case %d: want ok=false, got true", i)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("case %d: want non-nil err", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMergeQuery(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
u string
|
|
||||||
q url.Values
|
|
||||||
w string
|
|
||||||
}{
|
|
||||||
// No values
|
|
||||||
{
|
|
||||||
u: "http://example.com",
|
|
||||||
q: nil,
|
|
||||||
w: "http://example.com",
|
|
||||||
},
|
|
||||||
// No additional values
|
|
||||||
{
|
|
||||||
u: "http://example.com?foo=bar",
|
|
||||||
q: nil,
|
|
||||||
w: "http://example.com?foo=bar",
|
|
||||||
},
|
|
||||||
// Simple addition
|
|
||||||
{
|
|
||||||
u: "http://example.com",
|
|
||||||
q: url.Values{
|
|
||||||
"foo": []string{"bar"},
|
|
||||||
},
|
|
||||||
w: "http://example.com?foo=bar",
|
|
||||||
},
|
|
||||||
// Addition with existing values
|
|
||||||
{
|
|
||||||
u: "http://example.com?dog=boo",
|
|
||||||
q: url.Values{
|
|
||||||
"foo": []string{"bar"},
|
|
||||||
},
|
|
||||||
w: "http://example.com?dog=boo&foo=bar",
|
|
||||||
},
|
|
||||||
// Merge
|
|
||||||
{
|
|
||||||
u: "http://example.com?dog=boo",
|
|
||||||
q: url.Values{
|
|
||||||
"dog": []string{"elroy"},
|
|
||||||
},
|
|
||||||
w: "http://example.com?dog=boo&dog=elroy",
|
|
||||||
},
|
|
||||||
// Add and merge
|
|
||||||
{
|
|
||||||
u: "http://example.com?dog=boo",
|
|
||||||
q: url.Values{
|
|
||||||
"dog": []string{"elroy"},
|
|
||||||
"foo": []string{"bar"},
|
|
||||||
},
|
|
||||||
w: "http://example.com?dog=boo&dog=elroy&foo=bar",
|
|
||||||
},
|
|
||||||
// Multivalue merge
|
|
||||||
{
|
|
||||||
u: "http://example.com?dog=boo",
|
|
||||||
q: url.Values{
|
|
||||||
"dog": []string{"elroy", "penny"},
|
|
||||||
},
|
|
||||||
w: "http://example.com?dog=boo&dog=elroy&dog=penny",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
ur, err := url.Parse(tt.u)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("case %d: failed parsing test url: %v, error: %v", i, tt.u, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
got := MergeQuery(*ur, tt.q)
|
|
||||||
want, err := url.Parse(tt.w)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("case %d: failed parsing want url: %v, error: %v", i, tt.w, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(*want, got) {
|
|
||||||
t.Errorf("case %d: want: %v, got: %v", i, *want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExpiresPass(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
date string
|
|
||||||
exp string
|
|
||||||
wantTTL time.Duration
|
|
||||||
wantOK bool
|
|
||||||
}{
|
|
||||||
// Expires and Date properly set
|
|
||||||
{
|
|
||||||
date: "Thu, 01 Dec 1983 22:00:00 GMT",
|
|
||||||
exp: "Fri, 02 Dec 1983 01:00:00 GMT",
|
|
||||||
wantTTL: 10800 * time.Second,
|
|
||||||
wantOK: true,
|
|
||||||
},
|
|
||||||
// empty headers
|
|
||||||
{
|
|
||||||
date: "",
|
|
||||||
exp: "",
|
|
||||||
wantOK: false,
|
|
||||||
},
|
|
||||||
// lack of Expirs short-ciruits Date parsing
|
|
||||||
{
|
|
||||||
date: "foo",
|
|
||||||
exp: "",
|
|
||||||
wantOK: false,
|
|
||||||
},
|
|
||||||
// lack of Date short-ciruits Expires parsing
|
|
||||||
{
|
|
||||||
date: "",
|
|
||||||
exp: "foo",
|
|
||||||
wantOK: false,
|
|
||||||
},
|
|
||||||
// no Date
|
|
||||||
{
|
|
||||||
exp: "Thu, 01 Dec 1983 22:00:00 GMT",
|
|
||||||
wantTTL: 0,
|
|
||||||
wantOK: false,
|
|
||||||
},
|
|
||||||
// no Expires
|
|
||||||
{
|
|
||||||
date: "Thu, 01 Dec 1983 22:00:00 GMT",
|
|
||||||
wantTTL: 0,
|
|
||||||
wantOK: false,
|
|
||||||
},
|
|
||||||
// Expires set to false
|
|
||||||
{
|
|
||||||
date: "Thu, 01 Dec 1983 22:00:00 GMT",
|
|
||||||
exp: "0",
|
|
||||||
wantTTL: 0,
|
|
||||||
wantOK: false,
|
|
||||||
},
|
|
||||||
// Expires < Date
|
|
||||||
{
|
|
||||||
date: "Fri, 02 Dec 1983 01:00:00 GMT",
|
|
||||||
exp: "Thu, 01 Dec 1983 22:00:00 GMT",
|
|
||||||
wantTTL: 0,
|
|
||||||
wantOK: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
ttl, ok, err := expires(tt.date, tt.exp)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("case %d: err=%v", i, err)
|
|
||||||
}
|
|
||||||
if tt.wantTTL != ttl {
|
|
||||||
t.Errorf("case %d: want=%d got=%d", i, tt.wantTTL, ttl)
|
|
||||||
}
|
|
||||||
if tt.wantOK != ok {
|
|
||||||
t.Errorf("case %d: incorrect ok value: want=%t got=%t", i, tt.wantOK, ok)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExpiresFail(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
date string
|
|
||||||
exp string
|
|
||||||
}{
|
|
||||||
// malformed Date header
|
|
||||||
{
|
|
||||||
date: "foo",
|
|
||||||
exp: "Fri, 02 Dec 1983 01:00:00 GMT",
|
|
||||||
},
|
|
||||||
// malformed exp header
|
|
||||||
{
|
|
||||||
date: "Fri, 02 Dec 1983 01:00:00 GMT",
|
|
||||||
exp: "bar",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
_, _, err := expires(tt.date, tt.exp)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("case %d: expected non-nil error", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCacheablePass(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
headers http.Header
|
|
||||||
wantTTL time.Duration
|
|
||||||
wantOK bool
|
|
||||||
}{
|
|
||||||
// valid Cache-Control
|
|
||||||
{
|
|
||||||
headers: http.Header{
|
|
||||||
"Cache-Control": []string{"max-age=100"},
|
|
||||||
},
|
|
||||||
wantTTL: 100 * time.Second,
|
|
||||||
wantOK: true,
|
|
||||||
},
|
|
||||||
// valid Date/Expires
|
|
||||||
{
|
|
||||||
headers: http.Header{
|
|
||||||
"Date": []string{"Thu, 01 Dec 1983 22:00:00 GMT"},
|
|
||||||
"Expires": []string{"Fri, 02 Dec 1983 01:00:00 GMT"},
|
|
||||||
},
|
|
||||||
wantTTL: 10800 * time.Second,
|
|
||||||
wantOK: true,
|
|
||||||
},
|
|
||||||
// Cache-Control supersedes Date/Expires
|
|
||||||
{
|
|
||||||
headers: http.Header{
|
|
||||||
"Cache-Control": []string{"max-age=100"},
|
|
||||||
"Date": []string{"Thu, 01 Dec 1983 22:00:00 GMT"},
|
|
||||||
"Expires": []string{"Fri, 02 Dec 1983 01:00:00 GMT"},
|
|
||||||
},
|
|
||||||
wantTTL: 100 * time.Second,
|
|
||||||
wantOK: true,
|
|
||||||
},
|
|
||||||
// no caching headers
|
|
||||||
{
|
|
||||||
headers: http.Header{},
|
|
||||||
wantOK: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
ttl, ok, err := Cacheable(tt.headers)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("case %d: err=%v", i, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if tt.wantTTL != ttl {
|
|
||||||
t.Errorf("case %d: want=%d got=%d", i, tt.wantTTL, ttl)
|
|
||||||
}
|
|
||||||
if tt.wantOK != ok {
|
|
||||||
t.Errorf("case %d: incorrect ok value: want=%t got=%t", i, tt.wantOK, ok)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCacheableFail(t *testing.T) {
|
|
||||||
tests := []http.Header{
|
|
||||||
// invalid Cache-Control short-circuits
|
|
||||||
http.Header{
|
|
||||||
"Cache-Control": []string{"max-age"},
|
|
||||||
"Date": []string{"Thu, 01 Dec 1983 22:00:00 GMT"},
|
|
||||||
"Expires": []string{"Fri, 02 Dec 1983 01:00:00 GMT"},
|
|
||||||
},
|
|
||||||
// no Cache-Control, invalid Expires
|
|
||||||
http.Header{
|
|
||||||
"Date": []string{"Thu, 01 Dec 1983 22:00:00 GMT"},
|
|
||||||
"Expires": []string{"boo"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
_, _, err := Cacheable(tt)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("case %d: want non-nil err", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewResourceLocation(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
ru *url.URL
|
|
||||||
id string
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
ru: &url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: "example.com",
|
|
||||||
},
|
|
||||||
id: "foo",
|
|
||||||
want: "http://example.com/foo",
|
|
||||||
},
|
|
||||||
// https
|
|
||||||
{
|
|
||||||
ru: &url.URL{
|
|
||||||
Scheme: "https",
|
|
||||||
Host: "example.com",
|
|
||||||
},
|
|
||||||
id: "foo",
|
|
||||||
want: "https://example.com/foo",
|
|
||||||
},
|
|
||||||
// with path
|
|
||||||
{
|
|
||||||
ru: &url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: "example.com",
|
|
||||||
Path: "one/two/three",
|
|
||||||
},
|
|
||||||
id: "foo",
|
|
||||||
want: "http://example.com/one/two/three/foo",
|
|
||||||
},
|
|
||||||
// with fragment
|
|
||||||
{
|
|
||||||
ru: &url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: "example.com",
|
|
||||||
Fragment: "frag",
|
|
||||||
},
|
|
||||||
id: "foo",
|
|
||||||
want: "http://example.com/foo",
|
|
||||||
},
|
|
||||||
// with query
|
|
||||||
{
|
|
||||||
ru: &url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: "example.com",
|
|
||||||
RawQuery: "dog=elroy",
|
|
||||||
},
|
|
||||||
id: "foo",
|
|
||||||
want: "http://example.com/foo",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
got := NewResourceLocation(tt.ru, tt.id)
|
|
||||||
if tt.want != got {
|
|
||||||
t.Errorf("case %d: want=%s, got=%s", i, tt.want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
29
vendor/github.com/coreos/go-oidc/http/url.go
generated
vendored
29
vendor/github.com/coreos/go-oidc/http/url.go
generated
vendored
|
@ -1,29 +0,0 @@
|
||||||
package http
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"net/url"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ParseNonEmptyURL checks that a string is a parsable URL which is also not empty
|
|
||||||
// since `url.Parse("")` does not return an error. Must contian a scheme and a host.
|
|
||||||
func ParseNonEmptyURL(u string) (*url.URL, error) {
|
|
||||||
if u == "" {
|
|
||||||
return nil, errors.New("url is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
ur, err := url.Parse(u)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ur.Scheme == "" {
|
|
||||||
return nil, errors.New("url scheme is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
if ur.Host == "" {
|
|
||||||
return nil, errors.New("url host is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
return ur, nil
|
|
||||||
}
|
|
49
vendor/github.com/coreos/go-oidc/http/url_test.go
generated
vendored
49
vendor/github.com/coreos/go-oidc/http/url_test.go
generated
vendored
|
@ -1,49 +0,0 @@
|
||||||
package http
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/url"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParseNonEmptyURL(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
u string
|
|
||||||
ok bool
|
|
||||||
}{
|
|
||||||
{"", false},
|
|
||||||
{"http://", false},
|
|
||||||
{"example.com", false},
|
|
||||||
{"example", false},
|
|
||||||
{"http://example", true},
|
|
||||||
{"http://example:1234", true},
|
|
||||||
{"http://example.com", true},
|
|
||||||
{"http://example.com:1234", true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
u, err := ParseNonEmptyURL(tt.u)
|
|
||||||
if err != nil {
|
|
||||||
t.Logf("err: %v", err)
|
|
||||||
if tt.ok {
|
|
||||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
|
||||||
} else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !tt.ok {
|
|
||||||
t.Errorf("case %d: expected error but got none", i)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
uu, err := url.Parse(tt.u)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if uu.String() != u.String() {
|
|
||||||
t.Errorf("case %d: incorrect url value, want: %q, got: %q", i, uu.String(), u.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
20
vendor/github.com/coreos/go-oidc/jose.go
generated
vendored
20
vendor/github.com/coreos/go-oidc/jose.go
generated
vendored
|
@ -1,20 +0,0 @@
|
||||||
// +build !golint
|
|
||||||
|
|
||||||
// Don't lint this file. We don't want to have to add a comment to each constant.
|
|
||||||
|
|
||||||
package oidc
|
|
||||||
|
|
||||||
const (
|
|
||||||
// JOSE asymmetric signing algorithm values as defined by RFC 7518
|
|
||||||
//
|
|
||||||
// see: https://tools.ietf.org/html/rfc7518#section-3.1
|
|
||||||
RS256 = "RS256" // RSASSA-PKCS-v1.5 using SHA-256
|
|
||||||
RS384 = "RS384" // RSASSA-PKCS-v1.5 using SHA-384
|
|
||||||
RS512 = "RS512" // RSASSA-PKCS-v1.5 using SHA-512
|
|
||||||
ES256 = "ES256" // ECDSA using P-256 and SHA-256
|
|
||||||
ES384 = "ES384" // ECDSA using P-384 and SHA-384
|
|
||||||
ES512 = "ES512" // ECDSA using P-521 and SHA-512
|
|
||||||
PS256 = "PS256" // RSASSA-PSS using SHA256 and MGF1-SHA256
|
|
||||||
PS384 = "PS384" // RSASSA-PSS using SHA384 and MGF1-SHA384
|
|
||||||
PS512 = "PS512" // RSASSA-PSS using SHA512 and MGF1-SHA512
|
|
||||||
)
|
|
126
vendor/github.com/coreos/go-oidc/jose/claims.go
generated
vendored
126
vendor/github.com/coreos/go-oidc/jose/claims.go
generated
vendored
|
@ -1,126 +0,0 @@
|
||||||
package jose
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Claims map[string]interface{}
|
|
||||||
|
|
||||||
func (c Claims) Add(name string, value interface{}) {
|
|
||||||
c[name] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Claims) StringClaim(name string) (string, bool, error) {
|
|
||||||
cl, ok := c[name]
|
|
||||||
if !ok {
|
|
||||||
return "", false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
v, ok := cl.(string)
|
|
||||||
if !ok {
|
|
||||||
return "", false, fmt.Errorf("unable to parse claim as string: %v", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return v, true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Claims) StringsClaim(name string) ([]string, bool, error) {
|
|
||||||
cl, ok := c[name]
|
|
||||||
if !ok {
|
|
||||||
return nil, false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := cl.([]string); ok {
|
|
||||||
return v, true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// When unmarshaled, []string will become []interface{}.
|
|
||||||
if v, ok := cl.([]interface{}); ok {
|
|
||||||
var ret []string
|
|
||||||
for _, vv := range v {
|
|
||||||
str, ok := vv.(string)
|
|
||||||
if !ok {
|
|
||||||
return nil, false, fmt.Errorf("unable to parse claim as string array: %v", name)
|
|
||||||
}
|
|
||||||
ret = append(ret, str)
|
|
||||||
}
|
|
||||||
return ret, true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, false, fmt.Errorf("unable to parse claim as string array: %v", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Claims) Int64Claim(name string) (int64, bool, error) {
|
|
||||||
cl, ok := c[name]
|
|
||||||
if !ok {
|
|
||||||
return 0, false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
v, ok := cl.(int64)
|
|
||||||
if !ok {
|
|
||||||
vf, ok := cl.(float64)
|
|
||||||
if !ok {
|
|
||||||
return 0, false, fmt.Errorf("unable to parse claim as int64: %v", name)
|
|
||||||
}
|
|
||||||
v = int64(vf)
|
|
||||||
}
|
|
||||||
|
|
||||||
return v, true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Claims) Float64Claim(name string) (float64, bool, error) {
|
|
||||||
cl, ok := c[name]
|
|
||||||
if !ok {
|
|
||||||
return 0, false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
v, ok := cl.(float64)
|
|
||||||
if !ok {
|
|
||||||
vi, ok := cl.(int64)
|
|
||||||
if !ok {
|
|
||||||
return 0, false, fmt.Errorf("unable to parse claim as float64: %v", name)
|
|
||||||
}
|
|
||||||
v = float64(vi)
|
|
||||||
}
|
|
||||||
|
|
||||||
return v, true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Claims) TimeClaim(name string) (time.Time, bool, error) {
|
|
||||||
v, ok, err := c.Float64Claim(name)
|
|
||||||
if !ok || err != nil {
|
|
||||||
return time.Time{}, ok, err
|
|
||||||
}
|
|
||||||
|
|
||||||
s := math.Trunc(v)
|
|
||||||
ns := (v - s) * math.Pow(10, 9)
|
|
||||||
return time.Unix(int64(s), int64(ns)).UTC(), true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeClaims(payload []byte) (Claims, error) {
|
|
||||||
var c Claims
|
|
||||||
if err := json.Unmarshal(payload, &c); err != nil {
|
|
||||||
return nil, fmt.Errorf("malformed JWT claims, unable to decode: %v", err)
|
|
||||||
}
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func marshalClaims(c Claims) ([]byte, error) {
|
|
||||||
b, err := json.Marshal(c)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeClaims(c Claims) (string, error) {
|
|
||||||
b, err := marshalClaims(c)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return encodeSegment(b), nil
|
|
||||||
}
|
|
328
vendor/github.com/coreos/go-oidc/jose/claims_test.go
generated
vendored
328
vendor/github.com/coreos/go-oidc/jose/claims_test.go
generated
vendored
|
@ -1,328 +0,0 @@
|
||||||
package jose
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestString(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
cl Claims
|
|
||||||
key string
|
|
||||||
ok bool
|
|
||||||
err bool
|
|
||||||
val string
|
|
||||||
}{
|
|
||||||
// ok, no err, claim exists
|
|
||||||
{
|
|
||||||
cl: Claims{
|
|
||||||
"foo": "bar",
|
|
||||||
},
|
|
||||||
key: "foo",
|
|
||||||
val: "bar",
|
|
||||||
ok: true,
|
|
||||||
err: false,
|
|
||||||
},
|
|
||||||
// no claims
|
|
||||||
{
|
|
||||||
cl: Claims{},
|
|
||||||
key: "foo",
|
|
||||||
val: "",
|
|
||||||
ok: false,
|
|
||||||
err: false,
|
|
||||||
},
|
|
||||||
// missing claim
|
|
||||||
{
|
|
||||||
cl: Claims{
|
|
||||||
"foo": "bar",
|
|
||||||
},
|
|
||||||
key: "xxx",
|
|
||||||
val: "",
|
|
||||||
ok: false,
|
|
||||||
err: false,
|
|
||||||
},
|
|
||||||
// unparsable: type
|
|
||||||
{
|
|
||||||
cl: Claims{
|
|
||||||
"foo": struct{}{},
|
|
||||||
},
|
|
||||||
key: "foo",
|
|
||||||
val: "",
|
|
||||||
ok: false,
|
|
||||||
err: true,
|
|
||||||
},
|
|
||||||
// unparsable: nil value
|
|
||||||
{
|
|
||||||
cl: Claims{
|
|
||||||
"foo": nil,
|
|
||||||
},
|
|
||||||
key: "foo",
|
|
||||||
val: "",
|
|
||||||
ok: false,
|
|
||||||
err: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
val, ok, err := tt.cl.StringClaim(tt.key)
|
|
||||||
|
|
||||||
if tt.err && err == nil {
|
|
||||||
t.Errorf("case %d: want err=non-nil, got err=nil", i)
|
|
||||||
} else if !tt.err && err != nil {
|
|
||||||
t.Errorf("case %d: want err=nil, got err=%v", i, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tt.ok != ok {
|
|
||||||
t.Errorf("case %d: want ok=%v, got ok=%v", i, tt.ok, ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tt.val != val {
|
|
||||||
t.Errorf("case %d: want val=%v, got val=%v", i, tt.val, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInt64(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
cl Claims
|
|
||||||
key string
|
|
||||||
ok bool
|
|
||||||
err bool
|
|
||||||
val int64
|
|
||||||
}{
|
|
||||||
// ok, no err, claim exists
|
|
||||||
{
|
|
||||||
cl: Claims{
|
|
||||||
"foo": int64(100),
|
|
||||||
},
|
|
||||||
key: "foo",
|
|
||||||
val: int64(100),
|
|
||||||
ok: true,
|
|
||||||
err: false,
|
|
||||||
},
|
|
||||||
// no claims
|
|
||||||
{
|
|
||||||
cl: Claims{},
|
|
||||||
key: "foo",
|
|
||||||
val: 0,
|
|
||||||
ok: false,
|
|
||||||
err: false,
|
|
||||||
},
|
|
||||||
// missing claim
|
|
||||||
{
|
|
||||||
cl: Claims{
|
|
||||||
"foo": "bar",
|
|
||||||
},
|
|
||||||
key: "xxx",
|
|
||||||
val: 0,
|
|
||||||
ok: false,
|
|
||||||
err: false,
|
|
||||||
},
|
|
||||||
// unparsable: type
|
|
||||||
{
|
|
||||||
cl: Claims{
|
|
||||||
"foo": struct{}{},
|
|
||||||
},
|
|
||||||
key: "foo",
|
|
||||||
val: 0,
|
|
||||||
ok: false,
|
|
||||||
err: true,
|
|
||||||
},
|
|
||||||
// unparsable: nil value
|
|
||||||
{
|
|
||||||
cl: Claims{
|
|
||||||
"foo": nil,
|
|
||||||
},
|
|
||||||
key: "foo",
|
|
||||||
val: 0,
|
|
||||||
ok: false,
|
|
||||||
err: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
val, ok, err := tt.cl.Int64Claim(tt.key)
|
|
||||||
|
|
||||||
if tt.err && err == nil {
|
|
||||||
t.Errorf("case %d: want err=non-nil, got err=nil", i)
|
|
||||||
} else if !tt.err && err != nil {
|
|
||||||
t.Errorf("case %d: want err=nil, got err=%v", i, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tt.ok != ok {
|
|
||||||
t.Errorf("case %d: want ok=%v, got ok=%v", i, tt.ok, ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tt.val != val {
|
|
||||||
t.Errorf("case %d: want val=%v, got val=%v", i, tt.val, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTime(t *testing.T) {
|
|
||||||
now := time.Now().UTC()
|
|
||||||
unixNow := now.Unix()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
cl Claims
|
|
||||||
key string
|
|
||||||
ok bool
|
|
||||||
err bool
|
|
||||||
val time.Time
|
|
||||||
}{
|
|
||||||
// ok, no err, claim exists
|
|
||||||
{
|
|
||||||
cl: Claims{
|
|
||||||
"foo": unixNow,
|
|
||||||
},
|
|
||||||
key: "foo",
|
|
||||||
val: time.Unix(now.Unix(), 0).UTC(),
|
|
||||||
ok: true,
|
|
||||||
err: false,
|
|
||||||
},
|
|
||||||
// no claims
|
|
||||||
{
|
|
||||||
cl: Claims{},
|
|
||||||
key: "foo",
|
|
||||||
val: time.Time{},
|
|
||||||
ok: false,
|
|
||||||
err: false,
|
|
||||||
},
|
|
||||||
// missing claim
|
|
||||||
{
|
|
||||||
cl: Claims{
|
|
||||||
"foo": "bar",
|
|
||||||
},
|
|
||||||
key: "xxx",
|
|
||||||
val: time.Time{},
|
|
||||||
ok: false,
|
|
||||||
err: false,
|
|
||||||
},
|
|
||||||
// unparsable: type
|
|
||||||
{
|
|
||||||
cl: Claims{
|
|
||||||
"foo": struct{}{},
|
|
||||||
},
|
|
||||||
key: "foo",
|
|
||||||
val: time.Time{},
|
|
||||||
ok: false,
|
|
||||||
err: true,
|
|
||||||
},
|
|
||||||
// unparsable: nil value
|
|
||||||
{
|
|
||||||
cl: Claims{
|
|
||||||
"foo": nil,
|
|
||||||
},
|
|
||||||
key: "foo",
|
|
||||||
val: time.Time{},
|
|
||||||
ok: false,
|
|
||||||
err: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
val, ok, err := tt.cl.TimeClaim(tt.key)
|
|
||||||
|
|
||||||
if tt.err && err == nil {
|
|
||||||
t.Errorf("case %d: want err=non-nil, got err=nil", i)
|
|
||||||
} else if !tt.err && err != nil {
|
|
||||||
t.Errorf("case %d: want err=nil, got err=%v", i, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tt.ok != ok {
|
|
||||||
t.Errorf("case %d: want ok=%v, got ok=%v", i, tt.ok, ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tt.val != val {
|
|
||||||
t.Errorf("case %d: want val=%v, got val=%v", i, tt.val, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringArray(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
cl Claims
|
|
||||||
key string
|
|
||||||
ok bool
|
|
||||||
err bool
|
|
||||||
val []string
|
|
||||||
}{
|
|
||||||
// ok, no err, claim exists
|
|
||||||
{
|
|
||||||
cl: Claims{
|
|
||||||
"foo": []string{"bar", "faf"},
|
|
||||||
},
|
|
||||||
key: "foo",
|
|
||||||
val: []string{"bar", "faf"},
|
|
||||||
ok: true,
|
|
||||||
err: false,
|
|
||||||
},
|
|
||||||
// ok, no err, []interface{}
|
|
||||||
{
|
|
||||||
cl: Claims{
|
|
||||||
"foo": []interface{}{"bar", "faf"},
|
|
||||||
},
|
|
||||||
key: "foo",
|
|
||||||
val: []string{"bar", "faf"},
|
|
||||||
ok: true,
|
|
||||||
err: false,
|
|
||||||
},
|
|
||||||
// no claims
|
|
||||||
{
|
|
||||||
cl: Claims{},
|
|
||||||
key: "foo",
|
|
||||||
val: nil,
|
|
||||||
ok: false,
|
|
||||||
err: false,
|
|
||||||
},
|
|
||||||
// missing claim
|
|
||||||
{
|
|
||||||
cl: Claims{
|
|
||||||
"foo": "bar",
|
|
||||||
},
|
|
||||||
key: "xxx",
|
|
||||||
val: nil,
|
|
||||||
ok: false,
|
|
||||||
err: false,
|
|
||||||
},
|
|
||||||
// unparsable: type
|
|
||||||
{
|
|
||||||
cl: Claims{
|
|
||||||
"foo": struct{}{},
|
|
||||||
},
|
|
||||||
key: "foo",
|
|
||||||
val: nil,
|
|
||||||
ok: false,
|
|
||||||
err: true,
|
|
||||||
},
|
|
||||||
// unparsable: nil value
|
|
||||||
{
|
|
||||||
cl: Claims{
|
|
||||||
"foo": nil,
|
|
||||||
},
|
|
||||||
key: "foo",
|
|
||||||
val: nil,
|
|
||||||
ok: false,
|
|
||||||
err: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
val, ok, err := tt.cl.StringsClaim(tt.key)
|
|
||||||
|
|
||||||
if tt.err && err == nil {
|
|
||||||
t.Errorf("case %d: want err=non-nil, got err=nil", i)
|
|
||||||
} else if !tt.err && err != nil {
|
|
||||||
t.Errorf("case %d: want err=nil, got err=%v", i, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tt.ok != ok {
|
|
||||||
t.Errorf("case %d: want ok=%v, got ok=%v", i, tt.ok, ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(tt.val, val) {
|
|
||||||
t.Errorf("case %d: want val=%v, got val=%v", i, tt.val, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
2
vendor/github.com/coreos/go-oidc/jose/doc.go
generated
vendored
2
vendor/github.com/coreos/go-oidc/jose/doc.go
generated
vendored
|
@ -1,2 +0,0 @@
|
||||||
// Package jose is DEPRECATED. Use gopkg.in/square/go-jose.v2 instead.
|
|
||||||
package jose
|
|
112
vendor/github.com/coreos/go-oidc/jose/jose.go
generated
vendored
112
vendor/github.com/coreos/go-oidc/jose/jose.go
generated
vendored
|
@ -1,112 +0,0 @@
|
||||||
package jose
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
HeaderMediaType = "typ"
|
|
||||||
HeaderKeyAlgorithm = "alg"
|
|
||||||
HeaderKeyID = "kid"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Encryption Algorithm Header Parameter Values for JWS
|
|
||||||
// See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#page-6
|
|
||||||
AlgHS256 = "HS256"
|
|
||||||
AlgHS384 = "HS384"
|
|
||||||
AlgHS512 = "HS512"
|
|
||||||
AlgRS256 = "RS256"
|
|
||||||
AlgRS384 = "RS384"
|
|
||||||
AlgRS512 = "RS512"
|
|
||||||
AlgES256 = "ES256"
|
|
||||||
AlgES384 = "ES384"
|
|
||||||
AlgES512 = "ES512"
|
|
||||||
AlgPS256 = "PS256"
|
|
||||||
AlgPS384 = "PS384"
|
|
||||||
AlgPS512 = "PS512"
|
|
||||||
AlgNone = "none"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Algorithm Header Parameter Values for JWE
|
|
||||||
// See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-4.1
|
|
||||||
AlgRSA15 = "RSA1_5"
|
|
||||||
AlgRSAOAEP = "RSA-OAEP"
|
|
||||||
AlgRSAOAEP256 = "RSA-OAEP-256"
|
|
||||||
AlgA128KW = "A128KW"
|
|
||||||
AlgA192KW = "A192KW"
|
|
||||||
AlgA256KW = "A256KW"
|
|
||||||
AlgDir = "dir"
|
|
||||||
AlgECDHES = "ECDH-ES"
|
|
||||||
AlgECDHESA128KW = "ECDH-ES+A128KW"
|
|
||||||
AlgECDHESA192KW = "ECDH-ES+A192KW"
|
|
||||||
AlgECDHESA256KW = "ECDH-ES+A256KW"
|
|
||||||
AlgA128GCMKW = "A128GCMKW"
|
|
||||||
AlgA192GCMKW = "A192GCMKW"
|
|
||||||
AlgA256GCMKW = "A256GCMKW"
|
|
||||||
AlgPBES2HS256A128KW = "PBES2-HS256+A128KW"
|
|
||||||
AlgPBES2HS384A192KW = "PBES2-HS384+A192KW"
|
|
||||||
AlgPBES2HS512A256KW = "PBES2-HS512+A256KW"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Encryption Algorithm Header Parameter Values for JWE
|
|
||||||
// See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#page-22
|
|
||||||
EncA128CBCHS256 = "A128CBC-HS256"
|
|
||||||
EncA128CBCHS384 = "A128CBC-HS384"
|
|
||||||
EncA256CBCHS512 = "A256CBC-HS512"
|
|
||||||
EncA128GCM = "A128GCM"
|
|
||||||
EncA192GCM = "A192GCM"
|
|
||||||
EncA256GCM = "A256GCM"
|
|
||||||
)
|
|
||||||
|
|
||||||
type JOSEHeader map[string]string
|
|
||||||
|
|
||||||
func (j JOSEHeader) Validate() error {
|
|
||||||
if _, exists := j[HeaderKeyAlgorithm]; !exists {
|
|
||||||
return fmt.Errorf("header missing %q parameter", HeaderKeyAlgorithm)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeHeader(seg string) (JOSEHeader, error) {
|
|
||||||
b, err := decodeSegment(seg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var h JOSEHeader
|
|
||||||
err = json.Unmarshal(b, &h)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return h, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeHeader(h JOSEHeader) (string, error) {
|
|
||||||
b, err := json.Marshal(h)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return encodeSegment(b), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode JWT specific base64url encoding with padding stripped
|
|
||||||
func decodeSegment(seg string) ([]byte, error) {
|
|
||||||
if l := len(seg) % 4; l != 0 {
|
|
||||||
seg += strings.Repeat("=", 4-l)
|
|
||||||
}
|
|
||||||
return base64.URLEncoding.DecodeString(seg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode JWT specific base64url encoding with padding stripped
|
|
||||||
func encodeSegment(seg []byte) string {
|
|
||||||
return strings.TrimRight(base64.URLEncoding.EncodeToString(seg), "=")
|
|
||||||
}
|
|
135
vendor/github.com/coreos/go-oidc/jose/jwk.go
generated
vendored
135
vendor/github.com/coreos/go-oidc/jose/jwk.go
generated
vendored
|
@ -1,135 +0,0 @@
|
||||||
package jose
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/binary"
|
|
||||||
"encoding/json"
|
|
||||||
"math/big"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// JSON Web Key
|
|
||||||
// https://tools.ietf.org/html/draft-ietf-jose-json-web-key-36#page-5
|
|
||||||
type JWK struct {
|
|
||||||
ID string
|
|
||||||
Type string
|
|
||||||
Alg string
|
|
||||||
Use string
|
|
||||||
Exponent int
|
|
||||||
Modulus *big.Int
|
|
||||||
Secret []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type jwkJSON struct {
|
|
||||||
ID string `json:"kid"`
|
|
||||||
Type string `json:"kty"`
|
|
||||||
Alg string `json:"alg"`
|
|
||||||
Use string `json:"use"`
|
|
||||||
Exponent string `json:"e"`
|
|
||||||
Modulus string `json:"n"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *JWK) MarshalJSON() ([]byte, error) {
|
|
||||||
t := jwkJSON{
|
|
||||||
ID: j.ID,
|
|
||||||
Type: j.Type,
|
|
||||||
Alg: j.Alg,
|
|
||||||
Use: j.Use,
|
|
||||||
Exponent: encodeExponent(j.Exponent),
|
|
||||||
Modulus: encodeModulus(j.Modulus),
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.Marshal(&t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *JWK) UnmarshalJSON(data []byte) error {
|
|
||||||
var t jwkJSON
|
|
||||||
err := json.Unmarshal(data, &t)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
e, err := decodeExponent(t.Exponent)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err := decodeModulus(t.Modulus)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
j.ID = t.ID
|
|
||||||
j.Type = t.Type
|
|
||||||
j.Alg = t.Alg
|
|
||||||
j.Use = t.Use
|
|
||||||
j.Exponent = e
|
|
||||||
j.Modulus = n
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type JWKSet struct {
|
|
||||||
Keys []JWK `json:"keys"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeExponent(e string) (int, error) {
|
|
||||||
decE, err := decodeBase64URLPaddingOptional(e)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
var eBytes []byte
|
|
||||||
if len(decE) < 8 {
|
|
||||||
eBytes = make([]byte, 8-len(decE), 8)
|
|
||||||
eBytes = append(eBytes, decE...)
|
|
||||||
} else {
|
|
||||||
eBytes = decE
|
|
||||||
}
|
|
||||||
eReader := bytes.NewReader(eBytes)
|
|
||||||
var E uint64
|
|
||||||
err = binary.Read(eReader, binary.BigEndian, &E)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return int(E), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeExponent(e int) string {
|
|
||||||
b := make([]byte, 8)
|
|
||||||
binary.BigEndian.PutUint64(b, uint64(e))
|
|
||||||
var idx int
|
|
||||||
for ; idx < 8; idx++ {
|
|
||||||
if b[idx] != 0x0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return base64.RawURLEncoding.EncodeToString(b[idx:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turns a URL encoded modulus of a key into a big int.
|
|
||||||
func decodeModulus(n string) (*big.Int, error) {
|
|
||||||
decN, err := decodeBase64URLPaddingOptional(n)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
N := big.NewInt(0)
|
|
||||||
N.SetBytes(decN)
|
|
||||||
return N, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeModulus(n *big.Int) string {
|
|
||||||
return base64.RawURLEncoding.EncodeToString(n.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
// decodeBase64URLPaddingOptional decodes Base64 whether there is padding or not.
|
|
||||||
// The stdlib version currently doesn't handle this.
|
|
||||||
// We can get rid of this is if this bug:
|
|
||||||
// https://github.com/golang/go/issues/4237
|
|
||||||
// ever closes.
|
|
||||||
func decodeBase64URLPaddingOptional(e string) ([]byte, error) {
|
|
||||||
if m := len(e) % 4; m != 0 {
|
|
||||||
e += strings.Repeat("=", 4-m)
|
|
||||||
}
|
|
||||||
return base64.URLEncoding.DecodeString(e)
|
|
||||||
}
|
|
64
vendor/github.com/coreos/go-oidc/jose/jwk_test.go
generated
vendored
64
vendor/github.com/coreos/go-oidc/jose/jwk_test.go
generated
vendored
|
@ -1,64 +0,0 @@
|
||||||
package jose
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDecodeBase64URLPaddingOptional(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
encoded string
|
|
||||||
decoded string
|
|
||||||
err bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
// With padding
|
|
||||||
encoded: "VGVjdG9uaWM=",
|
|
||||||
decoded: "Tectonic",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Without padding
|
|
||||||
encoded: "VGVjdG9uaWM",
|
|
||||||
decoded: "Tectonic",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Even More padding
|
|
||||||
encoded: "VGVjdG9uaQ==",
|
|
||||||
decoded: "Tectoni",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// And take it away!
|
|
||||||
encoded: "VGVjdG9uaQ",
|
|
||||||
decoded: "Tectoni",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Too much padding.
|
|
||||||
encoded: "VGVjdG9uaWNh=",
|
|
||||||
decoded: "",
|
|
||||||
err: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Too much padding.
|
|
||||||
encoded: "VGVjdG9uaWNh=",
|
|
||||||
decoded: "",
|
|
||||||
err: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
got, err := decodeBase64URLPaddingOptional(tt.encoded)
|
|
||||||
if tt.err {
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("case %d: expected non-nil err", i)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("case %d: want nil err, got: %v", i, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if string(got) != tt.decoded {
|
|
||||||
t.Errorf("case %d: want=%q, got=%q", i, tt.decoded, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
51
vendor/github.com/coreos/go-oidc/jose/jws.go
generated
vendored
51
vendor/github.com/coreos/go-oidc/jose/jws.go
generated
vendored
|
@ -1,51 +0,0 @@
|
||||||
package jose
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type JWS struct {
|
|
||||||
RawHeader string
|
|
||||||
Header JOSEHeader
|
|
||||||
RawPayload string
|
|
||||||
Payload []byte
|
|
||||||
Signature []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// Given a raw encoded JWS token parses it and verifies the structure.
|
|
||||||
func ParseJWS(raw string) (JWS, error) {
|
|
||||||
parts := strings.Split(raw, ".")
|
|
||||||
if len(parts) != 3 {
|
|
||||||
return JWS{}, fmt.Errorf("malformed JWS, only %d segments", len(parts))
|
|
||||||
}
|
|
||||||
|
|
||||||
rawSig := parts[2]
|
|
||||||
jws := JWS{
|
|
||||||
RawHeader: parts[0],
|
|
||||||
RawPayload: parts[1],
|
|
||||||
}
|
|
||||||
|
|
||||||
header, err := decodeHeader(jws.RawHeader)
|
|
||||||
if err != nil {
|
|
||||||
return JWS{}, fmt.Errorf("malformed JWS, unable to decode header, %s", err)
|
|
||||||
}
|
|
||||||
if err = header.Validate(); err != nil {
|
|
||||||
return JWS{}, fmt.Errorf("malformed JWS, %s", err)
|
|
||||||
}
|
|
||||||
jws.Header = header
|
|
||||||
|
|
||||||
payload, err := decodeSegment(jws.RawPayload)
|
|
||||||
if err != nil {
|
|
||||||
return JWS{}, fmt.Errorf("malformed JWS, unable to decode payload: %s", err)
|
|
||||||
}
|
|
||||||
jws.Payload = payload
|
|
||||||
|
|
||||||
sig, err := decodeSegment(rawSig)
|
|
||||||
if err != nil {
|
|
||||||
return JWS{}, fmt.Errorf("malformed JWS, unable to decode signature: %s", err)
|
|
||||||
}
|
|
||||||
jws.Signature = sig
|
|
||||||
|
|
||||||
return jws, nil
|
|
||||||
}
|
|
74
vendor/github.com/coreos/go-oidc/jose/jws_test.go
generated
vendored
74
vendor/github.com/coreos/go-oidc/jose/jws_test.go
generated
vendored
|
@ -1,74 +0,0 @@
|
||||||
package jose
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type testCase struct{ t string }
|
|
||||||
|
|
||||||
var validInput []testCase
|
|
||||||
|
|
||||||
var invalidInput []testCase
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
validInput = []testCase{
|
|
||||||
{
|
|
||||||
"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidInput = []testCase{
|
|
||||||
// empty
|
|
||||||
{
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
// undecodeable
|
|
||||||
{
|
|
||||||
"aaa.bbb.ccc",
|
|
||||||
},
|
|
||||||
// missing parts
|
|
||||||
{
|
|
||||||
"aaa",
|
|
||||||
},
|
|
||||||
// missing parts
|
|
||||||
{
|
|
||||||
"aaa.bbb",
|
|
||||||
},
|
|
||||||
// too many parts
|
|
||||||
{
|
|
||||||
"aaa.bbb.ccc.ddd",
|
|
||||||
},
|
|
||||||
// invalid header
|
|
||||||
// EncodeHeader(map[string]string{"foo": "bar"})
|
|
||||||
{
|
|
||||||
"eyJmb28iOiJiYXIifQ.bbb.ccc",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseJWS(t *testing.T) {
|
|
||||||
for i, tt := range validInput {
|
|
||||||
jws, err := ParseJWS(tt.t)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("test: %d. expected: valid, actual: invalid", i)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedHeader := strings.Split(tt.t, ".")[0]
|
|
||||||
if jws.RawHeader != expectedHeader {
|
|
||||||
t.Errorf("test: %d. expected: %s, actual: %s", i, expectedHeader, jws.RawHeader)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedPayload := strings.Split(tt.t, ".")[1]
|
|
||||||
if jws.RawPayload != expectedPayload {
|
|
||||||
t.Errorf("test: %d. expected: %s, actual: %s", i, expectedPayload, jws.RawPayload)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range invalidInput {
|
|
||||||
_, err := ParseJWS(tt.t)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("test: %d. expected: invalid, actual: valid", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
82
vendor/github.com/coreos/go-oidc/jose/jwt.go
generated
vendored
82
vendor/github.com/coreos/go-oidc/jose/jwt.go
generated
vendored
|
@ -1,82 +0,0 @@
|
||||||
package jose
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
type JWT JWS
|
|
||||||
|
|
||||||
func ParseJWT(token string) (jwt JWT, err error) {
|
|
||||||
jws, err := ParseJWS(token)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return JWT(jws), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewJWT(header JOSEHeader, claims Claims) (jwt JWT, err error) {
|
|
||||||
jwt = JWT{}
|
|
||||||
|
|
||||||
jwt.Header = header
|
|
||||||
jwt.Header[HeaderMediaType] = "JWT"
|
|
||||||
|
|
||||||
claimBytes, err := marshalClaims(claims)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
jwt.Payload = claimBytes
|
|
||||||
|
|
||||||
eh, err := encodeHeader(header)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
jwt.RawHeader = eh
|
|
||||||
|
|
||||||
ec, err := encodeClaims(claims)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
jwt.RawPayload = ec
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *JWT) KeyID() (string, bool) {
|
|
||||||
kID, ok := j.Header[HeaderKeyID]
|
|
||||||
return kID, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *JWT) Claims() (Claims, error) {
|
|
||||||
return decodeClaims(j.Payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encoded data part of the token which may be signed.
|
|
||||||
func (j *JWT) Data() string {
|
|
||||||
return strings.Join([]string{j.RawHeader, j.RawPayload}, ".")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Full encoded JWT token string in format: header.claims.signature
|
|
||||||
func (j *JWT) Encode() string {
|
|
||||||
d := j.Data()
|
|
||||||
s := encodeSegment(j.Signature)
|
|
||||||
return strings.Join([]string{d, s}, ".")
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSignedJWT(claims Claims, s Signer) (*JWT, error) {
|
|
||||||
header := JOSEHeader{
|
|
||||||
HeaderKeyAlgorithm: s.Alg(),
|
|
||||||
HeaderKeyID: s.ID(),
|
|
||||||
}
|
|
||||||
|
|
||||||
jwt, err := NewJWT(header, claims)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sig, err := s.Sign([]byte(jwt.Data()))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
jwt.Signature = sig
|
|
||||||
|
|
||||||
return &jwt, nil
|
|
||||||
}
|
|
94
vendor/github.com/coreos/go-oidc/jose/jwt_test.go
generated
vendored
94
vendor/github.com/coreos/go-oidc/jose/jwt_test.go
generated
vendored
|
@ -1,94 +0,0 @@
|
||||||
package jose
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParseJWT(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
r string
|
|
||||||
h JOSEHeader
|
|
||||||
c Claims
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
// Example from JWT spec:
|
|
||||||
// http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#ExampleJWT
|
|
||||||
"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
|
|
||||||
JOSEHeader{
|
|
||||||
HeaderMediaType: "JWT",
|
|
||||||
HeaderKeyAlgorithm: "HS256",
|
|
||||||
},
|
|
||||||
Claims{
|
|
||||||
"iss": "joe",
|
|
||||||
// NOTE: test numbers must be floats for equality checks to work since values are converted form interface{} to float64 by default.
|
|
||||||
"exp": 1300819380.0,
|
|
||||||
"http://example.com/is_root": true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
jwt, err := ParseJWT(tt.r)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("raw token should parse. test: %d. expected: valid, actual: invalid. err=%v", i, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(tt.h, jwt.Header) {
|
|
||||||
t.Errorf("JOSE headers should match. test: %d. expected: %v, actual: %v", i, tt.h, jwt.Header)
|
|
||||||
}
|
|
||||||
|
|
||||||
claims, err := jwt.Claims()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("test: %d. expected: valid claim parsing. err=%v", i, err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(tt.c, claims) {
|
|
||||||
t.Errorf("claims should match. test: %d. expected: %v, actual: %v", i, tt.c, claims)
|
|
||||||
}
|
|
||||||
|
|
||||||
enc := jwt.Encode()
|
|
||||||
if enc != tt.r {
|
|
||||||
t.Errorf("encoded jwt should match raw jwt. test: %d. expected: %v, actual: %v", i, tt.r, enc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewJWTHeaderType(t *testing.T) {
|
|
||||||
jwt, err := NewJWT(JOSEHeader{}, Claims{})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := "JWT"
|
|
||||||
got := jwt.Header[HeaderMediaType]
|
|
||||||
if want != got {
|
|
||||||
t.Fatalf("Header %q incorrect: want=%s got=%s", HeaderMediaType, want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewJWTHeaderKeyID(t *testing.T) {
|
|
||||||
jwt, err := NewJWT(JOSEHeader{HeaderKeyID: "foo"}, Claims{})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := "foo"
|
|
||||||
got, ok := jwt.KeyID()
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("KeyID not set")
|
|
||||||
} else if want != got {
|
|
||||||
t.Fatalf("KeyID incorrect: want=%s got=%s", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewJWTHeaderKeyIDNotSet(t *testing.T) {
|
|
||||||
jwt, err := NewJWT(JOSEHeader{}, Claims{})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := jwt.KeyID(); ok {
|
|
||||||
t.Fatalf("KeyID set, but should not be")
|
|
||||||
}
|
|
||||||
}
|
|
24
vendor/github.com/coreos/go-oidc/jose/sig.go
generated
vendored
24
vendor/github.com/coreos/go-oidc/jose/sig.go
generated
vendored
|
@ -1,24 +0,0 @@
|
||||||
package jose
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Verifier interface {
|
|
||||||
ID() string
|
|
||||||
Alg() string
|
|
||||||
Verify(sig []byte, data []byte) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type Signer interface {
|
|
||||||
Verifier
|
|
||||||
Sign(data []byte) (sig []byte, err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewVerifier(jwk JWK) (Verifier, error) {
|
|
||||||
if jwk.Type != "RSA" {
|
|
||||||
return nil, fmt.Errorf("unsupported key type %q", jwk.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewVerifierRSA(jwk)
|
|
||||||
}
|
|
67
vendor/github.com/coreos/go-oidc/jose/sig_rsa.go
generated
vendored
67
vendor/github.com/coreos/go-oidc/jose/sig_rsa.go
generated
vendored
|
@ -1,67 +0,0 @@
|
||||||
package jose
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type VerifierRSA struct {
|
|
||||||
KeyID string
|
|
||||||
Hash crypto.Hash
|
|
||||||
PublicKey rsa.PublicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
type SignerRSA struct {
|
|
||||||
PrivateKey rsa.PrivateKey
|
|
||||||
VerifierRSA
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewVerifierRSA(jwk JWK) (*VerifierRSA, error) {
|
|
||||||
if jwk.Alg != "" && jwk.Alg != "RS256" {
|
|
||||||
return nil, fmt.Errorf("unsupported key algorithm %q", jwk.Alg)
|
|
||||||
}
|
|
||||||
|
|
||||||
v := VerifierRSA{
|
|
||||||
KeyID: jwk.ID,
|
|
||||||
PublicKey: rsa.PublicKey{
|
|
||||||
N: jwk.Modulus,
|
|
||||||
E: jwk.Exponent,
|
|
||||||
},
|
|
||||||
Hash: crypto.SHA256,
|
|
||||||
}
|
|
||||||
|
|
||||||
return &v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSignerRSA(kid string, key rsa.PrivateKey) *SignerRSA {
|
|
||||||
return &SignerRSA{
|
|
||||||
PrivateKey: key,
|
|
||||||
VerifierRSA: VerifierRSA{
|
|
||||||
KeyID: kid,
|
|
||||||
PublicKey: key.PublicKey,
|
|
||||||
Hash: crypto.SHA256,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *VerifierRSA) ID() string {
|
|
||||||
return v.KeyID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *VerifierRSA) Alg() string {
|
|
||||||
return "RS256"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *VerifierRSA) Verify(sig []byte, data []byte) error {
|
|
||||||
h := v.Hash.New()
|
|
||||||
h.Write(data)
|
|
||||||
return rsa.VerifyPKCS1v15(&v.PublicKey, v.Hash, h.Sum(nil), sig)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SignerRSA) Sign(data []byte) ([]byte, error) {
|
|
||||||
h := s.Hash.New()
|
|
||||||
h.Write(data)
|
|
||||||
return rsa.SignPKCS1v15(rand.Reader, &s.PrivateKey, s.Hash, h.Sum(nil))
|
|
||||||
}
|
|
206
vendor/github.com/coreos/go-oidc/jwks.go
generated
vendored
206
vendor/github.com/coreos/go-oidc/jwks.go
generated
vendored
|
@ -1,206 +0,0 @@
|
||||||
package oidc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pquerna/cachecontrol"
|
|
||||||
jose "gopkg.in/square/go-jose.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// keysExpiryDelta is the allowed clock skew between a client and the OpenID Connect
|
|
||||||
// server.
|
|
||||||
//
|
|
||||||
// When keys expire, they are valid for this amount of time after.
|
|
||||||
//
|
|
||||||
// If the keys have not expired, and an ID Token claims it was signed by a key not in
|
|
||||||
// the cache, if and only if the keys expire in this amount of time, the keys will be
|
|
||||||
// updated.
|
|
||||||
const keysExpiryDelta = 30 * time.Second
|
|
||||||
|
|
||||||
func newRemoteKeySet(ctx context.Context, jwksURL string, now func() time.Time) *remoteKeySet {
|
|
||||||
if now == nil {
|
|
||||||
now = time.Now
|
|
||||||
}
|
|
||||||
return &remoteKeySet{jwksURL: jwksURL, ctx: ctx, now: now}
|
|
||||||
}
|
|
||||||
|
|
||||||
type remoteKeySet struct {
|
|
||||||
jwksURL string
|
|
||||||
ctx context.Context
|
|
||||||
now func() time.Time
|
|
||||||
|
|
||||||
// guard all other fields
|
|
||||||
mu sync.Mutex
|
|
||||||
|
|
||||||
// inflight suppresses parallel execution of updateKeys and allows
|
|
||||||
// multiple goroutines to wait for its result.
|
|
||||||
inflight *inflight
|
|
||||||
|
|
||||||
// A set of cached keys and their expiry.
|
|
||||||
cachedKeys []jose.JSONWebKey
|
|
||||||
expiry time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// inflight is used to wait on some in-flight request from multiple goroutines.
|
|
||||||
type inflight struct {
|
|
||||||
doneCh chan struct{}
|
|
||||||
|
|
||||||
keys []jose.JSONWebKey
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func newInflight() *inflight {
|
|
||||||
return &inflight{doneCh: make(chan struct{})}
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait returns a channel that multiple goroutines can receive on. Once it returns
|
|
||||||
// a value, the inflight request is done and result() can be inspected.
|
|
||||||
func (i *inflight) wait() <-chan struct{} {
|
|
||||||
return i.doneCh
|
|
||||||
}
|
|
||||||
|
|
||||||
// done can only be called by a single goroutine. It records the result of the
|
|
||||||
// inflight request and signals other goroutines that the result is safe to
|
|
||||||
// inspect.
|
|
||||||
func (i *inflight) done(keys []jose.JSONWebKey, err error) {
|
|
||||||
i.keys = keys
|
|
||||||
i.err = err
|
|
||||||
close(i.doneCh)
|
|
||||||
}
|
|
||||||
|
|
||||||
// result cannot be called until the wait() channel has returned a value.
|
|
||||||
func (i *inflight) result() ([]jose.JSONWebKey, error) {
|
|
||||||
return i.keys, i.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *remoteKeySet) verify(ctx context.Context, jws *jose.JSONWebSignature) ([]byte, error) {
|
|
||||||
// We don't support JWTs signed with multiple signatures.
|
|
||||||
keyID := ""
|
|
||||||
for _, sig := range jws.Signatures {
|
|
||||||
keyID = sig.Header.KeyID
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
keys, expiry := r.keysFromCache()
|
|
||||||
|
|
||||||
// Don't check expiry yet. This optimizes for when the provider is unavailable.
|
|
||||||
for _, key := range keys {
|
|
||||||
if keyID == "" || key.KeyID == keyID {
|
|
||||||
if payload, err := jws.Verify(&key); err == nil {
|
|
||||||
return payload, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !r.now().Add(keysExpiryDelta).After(expiry) {
|
|
||||||
// Keys haven't expired, don't refresh.
|
|
||||||
return nil, errors.New("failed to verify id token signature")
|
|
||||||
}
|
|
||||||
|
|
||||||
keys, err := r.keysFromRemote(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("fetching keys %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, key := range keys {
|
|
||||||
if keyID == "" || key.KeyID == keyID {
|
|
||||||
if payload, err := jws.Verify(&key); err == nil {
|
|
||||||
return payload, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, errors.New("failed to verify id token signature")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *remoteKeySet) keysFromCache() (keys []jose.JSONWebKey, expiry time.Time) {
|
|
||||||
r.mu.Lock()
|
|
||||||
defer r.mu.Unlock()
|
|
||||||
return r.cachedKeys, r.expiry
|
|
||||||
}
|
|
||||||
|
|
||||||
// keysFromRemote syncs the key set from the remote set, records the values in the
|
|
||||||
// cache, and returns the key set.
|
|
||||||
func (r *remoteKeySet) keysFromRemote(ctx context.Context) ([]jose.JSONWebKey, error) {
|
|
||||||
// Need to lock to inspect the inflight request field.
|
|
||||||
r.mu.Lock()
|
|
||||||
// If there's not a current inflight request, create one.
|
|
||||||
if r.inflight == nil {
|
|
||||||
r.inflight = newInflight()
|
|
||||||
|
|
||||||
// This goroutine has exclusive ownership over the current inflight
|
|
||||||
// request. It releases the resource by nil'ing the inflight field
|
|
||||||
// once the goroutine is done.
|
|
||||||
go func() {
|
|
||||||
// Sync keys and finish inflight when that's done.
|
|
||||||
keys, expiry, err := r.updateKeys()
|
|
||||||
|
|
||||||
r.inflight.done(keys, err)
|
|
||||||
|
|
||||||
// Lock to update the keys and indicate that there is no longer an
|
|
||||||
// inflight request.
|
|
||||||
r.mu.Lock()
|
|
||||||
defer r.mu.Unlock()
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
r.cachedKeys = keys
|
|
||||||
r.expiry = expiry
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free inflight so a different request can run.
|
|
||||||
r.inflight = nil
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
inflight := r.inflight
|
|
||||||
r.mu.Unlock()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return nil, ctx.Err()
|
|
||||||
case <-inflight.wait():
|
|
||||||
return inflight.result()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *remoteKeySet) updateKeys() ([]jose.JSONWebKey, time.Time, error) {
|
|
||||||
req, err := http.NewRequest("GET", r.jwksURL, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, time.Time{}, fmt.Errorf("oidc: can't create request: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := doRequest(r.ctx, req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, time.Time{}, fmt.Errorf("oidc: get keys failed %v", err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, time.Time{}, fmt.Errorf("unable to read response body: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return nil, time.Time{}, fmt.Errorf("oidc: get keys failed: %s %s", resp.Status, body)
|
|
||||||
}
|
|
||||||
|
|
||||||
var keySet jose.JSONWebKeySet
|
|
||||||
err = unmarshalResp(resp, body, &keySet)
|
|
||||||
if err != nil {
|
|
||||||
return nil, time.Time{}, fmt.Errorf("oidc: failed to decode keys: %v %s", err, body)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the server doesn't provide cache control headers, assume the
|
|
||||||
// keys expire immediately.
|
|
||||||
expiry := r.now()
|
|
||||||
|
|
||||||
_, e, err := cachecontrol.CachableResponse(req, resp, cachecontrol.Options{})
|
|
||||||
if err == nil && e.After(expiry) {
|
|
||||||
expiry = e
|
|
||||||
}
|
|
||||||
return keySet.Keys, expiry, nil
|
|
||||||
}
|
|
250
vendor/github.com/coreos/go-oidc/jwks_test.go
generated
vendored
250
vendor/github.com/coreos/go-oidc/jwks_test.go
generated
vendored
|
@ -1,250 +0,0 @@
|
||||||
package oidc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
jose "gopkg.in/square/go-jose.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
type keyServer struct {
|
|
||||||
keys jose.JSONWebKeySet
|
|
||||||
setHeaders func(h http.Header)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *keyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if k.setHeaders != nil {
|
|
||||||
k.setHeaders(w.Header())
|
|
||||||
}
|
|
||||||
if err := json.NewEncoder(w).Encode(k.keys); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type signingKey struct {
|
|
||||||
keyID string // optional
|
|
||||||
priv interface{}
|
|
||||||
pub interface{}
|
|
||||||
alg jose.SignatureAlgorithm
|
|
||||||
}
|
|
||||||
|
|
||||||
// sign creates a JWS using the private key from the provided payload.
|
|
||||||
func (s *signingKey) sign(t *testing.T, payload []byte) string {
|
|
||||||
privKey := &jose.JSONWebKey{Key: s.priv, Algorithm: string(s.alg), KeyID: s.keyID}
|
|
||||||
|
|
||||||
signer, err := jose.NewSigner(jose.SigningKey{Algorithm: s.alg, Key: privKey}, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
jws, err := signer.Sign(payload)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := jws.CompactSerialize()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
// jwk returns the public part of the signing key.
|
|
||||||
func (s *signingKey) jwk() jose.JSONWebKey {
|
|
||||||
return jose.JSONWebKey{Key: s.pub, Use: "sig", Algorithm: string(s.alg), KeyID: s.keyID}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newRSAKey(t *testing.T) *signingKey {
|
|
||||||
priv, err := rsa.GenerateKey(rand.Reader, 1028)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
return &signingKey{"", priv, priv.Public(), jose.RS256}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newECDSAKey(t *testing.T) *signingKey {
|
|
||||||
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
return &signingKey{"", priv, priv.Public(), jose.ES256}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRSAVerify(t *testing.T) {
|
|
||||||
good := newRSAKey(t)
|
|
||||||
bad := newRSAKey(t)
|
|
||||||
|
|
||||||
testKeyVerify(t, good, bad, good)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestECDSAVerify(t *testing.T) {
|
|
||||||
good := newECDSAKey(t)
|
|
||||||
bad := newECDSAKey(t)
|
|
||||||
testKeyVerify(t, good, bad, good)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMultipleKeysVerify(t *testing.T) {
|
|
||||||
key1 := newRSAKey(t)
|
|
||||||
key2 := newRSAKey(t)
|
|
||||||
bad := newECDSAKey(t)
|
|
||||||
|
|
||||||
key1.keyID = "key1"
|
|
||||||
key2.keyID = "key2"
|
|
||||||
bad.keyID = "key3"
|
|
||||||
|
|
||||||
testKeyVerify(t, key2, bad, key1, key2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMismatchedKeyID(t *testing.T) {
|
|
||||||
key1 := newRSAKey(t)
|
|
||||||
key2 := newRSAKey(t)
|
|
||||||
|
|
||||||
// shallow copy
|
|
||||||
bad := new(signingKey)
|
|
||||||
*bad = *key1
|
|
||||||
|
|
||||||
// The bad key is a valid key this time, but has a different Key ID.
|
|
||||||
// It shouldn't match key1 because of the mismatched ID, even though
|
|
||||||
// it would confirm the signature just fine.
|
|
||||||
bad.keyID = "key3"
|
|
||||||
|
|
||||||
key1.keyID = "key1"
|
|
||||||
key2.keyID = "key2"
|
|
||||||
|
|
||||||
testKeyVerify(t, key2, bad, key1, key2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testKeyVerify(t *testing.T, good, bad *signingKey, verification ...*signingKey) {
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
keySet := jose.JSONWebKeySet{}
|
|
||||||
for _, v := range verification {
|
|
||||||
keySet.Keys = append(keySet.Keys, v.jwk())
|
|
||||||
}
|
|
||||||
|
|
||||||
payload := []byte("a secret")
|
|
||||||
|
|
||||||
jws, err := jose.ParseSigned(good.sign(t, payload))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
badJWS, err := jose.ParseSigned(bad.sign(t, payload))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
s := httptest.NewServer(&keyServer{keys: keySet})
|
|
||||||
defer s.Close()
|
|
||||||
|
|
||||||
rks := newRemoteKeySet(ctx, s.URL, nil)
|
|
||||||
|
|
||||||
// Ensure the token verifies.
|
|
||||||
gotPayload, err := rks.verify(ctx, jws)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !bytes.Equal(gotPayload, payload) {
|
|
||||||
t.Errorf("expected payload %s got %s", payload, gotPayload)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the token verifies from the cache.
|
|
||||||
gotPayload, err = rks.verify(ctx, jws)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !bytes.Equal(gotPayload, payload) {
|
|
||||||
t.Errorf("expected payload %s got %s", payload, gotPayload)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure item signed by wrong token doesn't verify.
|
|
||||||
if _, err := rks.verify(context.Background(), badJWS); err == nil {
|
|
||||||
t.Errorf("incorrectly verified signature")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCacheControl(t *testing.T) {
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
key1 := newRSAKey(t)
|
|
||||||
key2 := newRSAKey(t)
|
|
||||||
|
|
||||||
key1.keyID = "key1"
|
|
||||||
key2.keyID = "key2"
|
|
||||||
|
|
||||||
payload := []byte("a secret")
|
|
||||||
jws1, err := jose.ParseSigned(key1.sign(t, payload))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
jws2, err := jose.ParseSigned(key2.sign(t, payload))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cacheForSeconds := 1200
|
|
||||||
now := time.Now()
|
|
||||||
|
|
||||||
server := &keyServer{
|
|
||||||
keys: jose.JSONWebKeySet{
|
|
||||||
Keys: []jose.JSONWebKey{key1.jwk()},
|
|
||||||
},
|
|
||||||
setHeaders: func(h http.Header) {
|
|
||||||
h.Set("Cache-Control", "max-age="+strconv.Itoa(cacheForSeconds))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
s := httptest.NewServer(server)
|
|
||||||
defer s.Close()
|
|
||||||
|
|
||||||
rks := newRemoteKeySet(ctx, s.URL, func() time.Time { return now })
|
|
||||||
|
|
||||||
if _, err := rks.verify(ctx, jws1); err != nil {
|
|
||||||
t.Errorf("failed to verify valid signature: %v", err)
|
|
||||||
}
|
|
||||||
if _, err := rks.verify(ctx, jws2); err == nil {
|
|
||||||
t.Errorf("incorrectly verified signature")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add second key to public list.
|
|
||||||
server.keys = jose.JSONWebKeySet{
|
|
||||||
Keys: []jose.JSONWebKey{key1.jwk(), key2.jwk()},
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := rks.verify(ctx, jws1); err != nil {
|
|
||||||
t.Errorf("failed to verify valid signature: %v", err)
|
|
||||||
}
|
|
||||||
if _, err := rks.verify(ctx, jws2); err == nil {
|
|
||||||
t.Errorf("incorrectly verified signature, still within cache limit")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move time forward. Remote key set should not query the remote server.
|
|
||||||
now = now.Add(time.Duration(cacheForSeconds) * time.Second)
|
|
||||||
|
|
||||||
if _, err := rks.verify(ctx, jws1); err != nil {
|
|
||||||
t.Errorf("failed to verify valid signature: %v", err)
|
|
||||||
}
|
|
||||||
if _, err := rks.verify(ctx, jws2); err != nil {
|
|
||||||
t.Errorf("failed to verify valid signature: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kill server and move time forward again. Keys should still verify.
|
|
||||||
s.Close()
|
|
||||||
now = now.Add(time.Duration(cacheForSeconds) * time.Second)
|
|
||||||
|
|
||||||
if _, err := rks.verify(ctx, jws1); err != nil {
|
|
||||||
t.Errorf("failed to verify valid signature: %v", err)
|
|
||||||
}
|
|
||||||
if _, err := rks.verify(ctx, jws2); err != nil {
|
|
||||||
t.Errorf("failed to verify valid signature: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
2
vendor/github.com/coreos/go-oidc/key/doc.go
generated
vendored
2
vendor/github.com/coreos/go-oidc/key/doc.go
generated
vendored
|
@ -1,2 +0,0 @@
|
||||||
// Package key is DEPRECATED. Use github.com/coreos/go-oidc instead.
|
|
||||||
package key
|
|
153
vendor/github.com/coreos/go-oidc/key/key.go
generated
vendored
153
vendor/github.com/coreos/go-oidc/key/key.go
generated
vendored
|
@ -1,153 +0,0 @@
|
||||||
package key
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/coreos/go-oidc/jose"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewPublicKey(jwk jose.JWK) *PublicKey {
|
|
||||||
return &PublicKey{jwk: jwk}
|
|
||||||
}
|
|
||||||
|
|
||||||
type PublicKey struct {
|
|
||||||
jwk jose.JWK
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *PublicKey) MarshalJSON() ([]byte, error) {
|
|
||||||
return json.Marshal(&k.jwk)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *PublicKey) UnmarshalJSON(data []byte) error {
|
|
||||||
var jwk jose.JWK
|
|
||||||
if err := json.Unmarshal(data, &jwk); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
k.jwk = jwk
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *PublicKey) ID() string {
|
|
||||||
return k.jwk.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *PublicKey) Verifier() (jose.Verifier, error) {
|
|
||||||
return jose.NewVerifierRSA(k.jwk)
|
|
||||||
}
|
|
||||||
|
|
||||||
type PrivateKey struct {
|
|
||||||
KeyID string
|
|
||||||
PrivateKey *rsa.PrivateKey
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *PrivateKey) ID() string {
|
|
||||||
return k.KeyID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *PrivateKey) Signer() jose.Signer {
|
|
||||||
return jose.NewSignerRSA(k.ID(), *k.PrivateKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *PrivateKey) JWK() jose.JWK {
|
|
||||||
return jose.JWK{
|
|
||||||
ID: k.KeyID,
|
|
||||||
Type: "RSA",
|
|
||||||
Alg: "RS256",
|
|
||||||
Use: "sig",
|
|
||||||
Exponent: k.PrivateKey.PublicKey.E,
|
|
||||||
Modulus: k.PrivateKey.PublicKey.N,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type KeySet interface {
|
|
||||||
ExpiresAt() time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
type PublicKeySet struct {
|
|
||||||
keys []PublicKey
|
|
||||||
index map[string]*PublicKey
|
|
||||||
expiresAt time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPublicKeySet(jwks []jose.JWK, exp time.Time) *PublicKeySet {
|
|
||||||
keys := make([]PublicKey, len(jwks))
|
|
||||||
index := make(map[string]*PublicKey)
|
|
||||||
for i, jwk := range jwks {
|
|
||||||
keys[i] = *NewPublicKey(jwk)
|
|
||||||
index[keys[i].ID()] = &keys[i]
|
|
||||||
}
|
|
||||||
return &PublicKeySet{
|
|
||||||
keys: keys,
|
|
||||||
index: index,
|
|
||||||
expiresAt: exp,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *PublicKeySet) ExpiresAt() time.Time {
|
|
||||||
return s.expiresAt
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *PublicKeySet) Keys() []PublicKey {
|
|
||||||
return s.keys
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *PublicKeySet) Key(id string) *PublicKey {
|
|
||||||
return s.index[id]
|
|
||||||
}
|
|
||||||
|
|
||||||
type PrivateKeySet struct {
|
|
||||||
keys []*PrivateKey
|
|
||||||
ActiveKeyID string
|
|
||||||
expiresAt time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPrivateKeySet(keys []*PrivateKey, exp time.Time) *PrivateKeySet {
|
|
||||||
return &PrivateKeySet{
|
|
||||||
keys: keys,
|
|
||||||
ActiveKeyID: keys[0].ID(),
|
|
||||||
expiresAt: exp.UTC(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *PrivateKeySet) Keys() []*PrivateKey {
|
|
||||||
return s.keys
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *PrivateKeySet) ExpiresAt() time.Time {
|
|
||||||
return s.expiresAt
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *PrivateKeySet) Active() *PrivateKey {
|
|
||||||
for i, k := range s.keys {
|
|
||||||
if k.ID() == s.ActiveKeyID {
|
|
||||||
return s.keys[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type GeneratePrivateKeyFunc func() (*PrivateKey, error)
|
|
||||||
|
|
||||||
func GeneratePrivateKey() (*PrivateKey, error) {
|
|
||||||
pk, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
keyID := make([]byte, 20)
|
|
||||||
if _, err := io.ReadFull(rand.Reader, keyID); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
k := PrivateKey{
|
|
||||||
KeyID: hex.EncodeToString(keyID),
|
|
||||||
PrivateKey: pk,
|
|
||||||
}
|
|
||||||
|
|
||||||
return &k, nil
|
|
||||||
}
|
|
103
vendor/github.com/coreos/go-oidc/key/key_test.go
generated
vendored
103
vendor/github.com/coreos/go-oidc/key/key_test.go
generated
vendored
|
@ -1,103 +0,0 @@
|
||||||
package key
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rsa"
|
|
||||||
"math/big"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/coreos/go-oidc/jose"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPrivateRSAKeyJWK(t *testing.T) {
|
|
||||||
n := big.NewInt(int64(17))
|
|
||||||
if n == nil {
|
|
||||||
panic("NewInt returned nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
k := &PrivateKey{
|
|
||||||
KeyID: "foo",
|
|
||||||
PrivateKey: &rsa.PrivateKey{
|
|
||||||
PublicKey: rsa.PublicKey{N: n, E: 65537},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
want := jose.JWK{
|
|
||||||
ID: "foo",
|
|
||||||
Type: "RSA",
|
|
||||||
Alg: "RS256",
|
|
||||||
Use: "sig",
|
|
||||||
Modulus: n,
|
|
||||||
Exponent: 65537,
|
|
||||||
}
|
|
||||||
|
|
||||||
got := k.JWK()
|
|
||||||
if !reflect.DeepEqual(want, got) {
|
|
||||||
t.Fatalf("JWK mismatch: want=%#v got=%#v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPublicKeySetKey(t *testing.T) {
|
|
||||||
n := big.NewInt(int64(17))
|
|
||||||
if n == nil {
|
|
||||||
panic("NewInt returned nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
k := jose.JWK{
|
|
||||||
ID: "foo",
|
|
||||||
Type: "RSA",
|
|
||||||
Alg: "RS256",
|
|
||||||
Use: "sig",
|
|
||||||
Modulus: n,
|
|
||||||
Exponent: 65537,
|
|
||||||
}
|
|
||||||
now := time.Now().UTC()
|
|
||||||
ks := NewPublicKeySet([]jose.JWK{k}, now)
|
|
||||||
|
|
||||||
want := &PublicKey{jwk: k}
|
|
||||||
got := ks.Key("foo")
|
|
||||||
if !reflect.DeepEqual(want, got) {
|
|
||||||
t.Errorf("Unexpected response from PublicKeySet.Key: want=%#v got=%#v", want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
got = ks.Key("bar")
|
|
||||||
if got != nil {
|
|
||||||
t.Errorf("Expected nil response from PublicKeySet.Key, got %#v", got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPublicKeyMarshalJSON(t *testing.T) {
|
|
||||||
k := jose.JWK{
|
|
||||||
ID: "foo",
|
|
||||||
Type: "RSA",
|
|
||||||
Alg: "RS256",
|
|
||||||
Use: "sig",
|
|
||||||
Modulus: big.NewInt(int64(17)),
|
|
||||||
Exponent: 65537,
|
|
||||||
}
|
|
||||||
want := `{"kid":"foo","kty":"RSA","alg":"RS256","use":"sig","e":"AQAB","n":"EQ"}`
|
|
||||||
pubKey := NewPublicKey(k)
|
|
||||||
gotBytes, err := pubKey.MarshalJSON()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to marshal public key: %v", err)
|
|
||||||
}
|
|
||||||
got := string(gotBytes)
|
|
||||||
if got != want {
|
|
||||||
t.Errorf("got != want:\n%s\n%s", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGeneratePrivateKeyIDs(t *testing.T) {
|
|
||||||
key1, err := GeneratePrivateKey()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("GeneratePrivateKey(): %v", err)
|
|
||||||
}
|
|
||||||
key2, err := GeneratePrivateKey()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("GeneratePrivateKey(): %v", err)
|
|
||||||
}
|
|
||||||
if key1.KeyID == key2.KeyID {
|
|
||||||
t.Fatalf("expected different keys to have different key IDs")
|
|
||||||
}
|
|
||||||
}
|
|
99
vendor/github.com/coreos/go-oidc/key/manager.go
generated
vendored
99
vendor/github.com/coreos/go-oidc/key/manager.go
generated
vendored
|
@ -1,99 +0,0 @@
|
||||||
package key
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/jonboulle/clockwork"
|
|
||||||
|
|
||||||
"github.com/coreos/go-oidc/jose"
|
|
||||||
"github.com/coreos/pkg/health"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PrivateKeyManager interface {
|
|
||||||
ExpiresAt() time.Time
|
|
||||||
Signer() (jose.Signer, error)
|
|
||||||
JWKs() ([]jose.JWK, error)
|
|
||||||
PublicKeys() ([]PublicKey, error)
|
|
||||||
|
|
||||||
WritableKeySetRepo
|
|
||||||
health.Checkable
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPrivateKeyManager() PrivateKeyManager {
|
|
||||||
return &privateKeyManager{
|
|
||||||
clock: clockwork.NewRealClock(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type privateKeyManager struct {
|
|
||||||
keySet *PrivateKeySet
|
|
||||||
clock clockwork.Clock
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *privateKeyManager) ExpiresAt() time.Time {
|
|
||||||
if m.keySet == nil {
|
|
||||||
return m.clock.Now().UTC()
|
|
||||||
}
|
|
||||||
|
|
||||||
return m.keySet.ExpiresAt()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *privateKeyManager) Signer() (jose.Signer, error) {
|
|
||||||
if err := m.Healthy(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return m.keySet.Active().Signer(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *privateKeyManager) JWKs() ([]jose.JWK, error) {
|
|
||||||
if err := m.Healthy(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
keys := m.keySet.Keys()
|
|
||||||
jwks := make([]jose.JWK, len(keys))
|
|
||||||
for i, k := range keys {
|
|
||||||
jwks[i] = k.JWK()
|
|
||||||
}
|
|
||||||
return jwks, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *privateKeyManager) PublicKeys() ([]PublicKey, error) {
|
|
||||||
jwks, err := m.JWKs()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
keys := make([]PublicKey, len(jwks))
|
|
||||||
for i, jwk := range jwks {
|
|
||||||
keys[i] = *NewPublicKey(jwk)
|
|
||||||
}
|
|
||||||
return keys, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *privateKeyManager) Healthy() error {
|
|
||||||
if m.keySet == nil {
|
|
||||||
return errors.New("private key manager uninitialized")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(m.keySet.Keys()) == 0 {
|
|
||||||
return errors.New("private key manager zero keys")
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.keySet.ExpiresAt().Before(m.clock.Now().UTC()) {
|
|
||||||
return errors.New("private key manager keys expired")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *privateKeyManager) Set(keySet KeySet) error {
|
|
||||||
privKeySet, ok := keySet.(*PrivateKeySet)
|
|
||||||
if !ok {
|
|
||||||
return errors.New("unable to cast to PrivateKeySet")
|
|
||||||
}
|
|
||||||
|
|
||||||
m.keySet = privKeySet
|
|
||||||
return nil
|
|
||||||
}
|
|
225
vendor/github.com/coreos/go-oidc/key/manager_test.go
generated
vendored
225
vendor/github.com/coreos/go-oidc/key/manager_test.go
generated
vendored
|
@ -1,225 +0,0 @@
|
||||||
package key
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rsa"
|
|
||||||
"math/big"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/jonboulle/clockwork"
|
|
||||||
|
|
||||||
"github.com/coreos/go-oidc/jose"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
jwk1 jose.JWK
|
|
||||||
jwk2 jose.JWK
|
|
||||||
jwk3 jose.JWK
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
jwk1 = jose.JWK{
|
|
||||||
ID: "1",
|
|
||||||
Type: "RSA",
|
|
||||||
Alg: "RS256",
|
|
||||||
Use: "sig",
|
|
||||||
Modulus: big.NewInt(1),
|
|
||||||
Exponent: 65537,
|
|
||||||
}
|
|
||||||
|
|
||||||
jwk2 = jose.JWK{
|
|
||||||
ID: "2",
|
|
||||||
Type: "RSA",
|
|
||||||
Alg: "RS256",
|
|
||||||
Use: "sig",
|
|
||||||
Modulus: big.NewInt(2),
|
|
||||||
Exponent: 65537,
|
|
||||||
}
|
|
||||||
|
|
||||||
jwk3 = jose.JWK{
|
|
||||||
ID: "3",
|
|
||||||
Type: "RSA",
|
|
||||||
Alg: "RS256",
|
|
||||||
Use: "sig",
|
|
||||||
Modulus: big.NewInt(3),
|
|
||||||
Exponent: 65537,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func generatePrivateKeyStatic(t *testing.T, idAndN int) *PrivateKey {
|
|
||||||
n := big.NewInt(int64(idAndN))
|
|
||||||
if n == nil {
|
|
||||||
t.Fatalf("Call to NewInt(%d) failed", idAndN)
|
|
||||||
}
|
|
||||||
|
|
||||||
pk := &rsa.PrivateKey{
|
|
||||||
PublicKey: rsa.PublicKey{N: n, E: 65537},
|
|
||||||
}
|
|
||||||
|
|
||||||
return &PrivateKey{
|
|
||||||
KeyID: strconv.Itoa(idAndN),
|
|
||||||
PrivateKey: pk,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrivateKeyManagerJWKsRotate(t *testing.T) {
|
|
||||||
k1 := generatePrivateKeyStatic(t, 1)
|
|
||||||
k2 := generatePrivateKeyStatic(t, 2)
|
|
||||||
k3 := generatePrivateKeyStatic(t, 3)
|
|
||||||
km := NewPrivateKeyManager()
|
|
||||||
err := km.Set(&PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k1, k2, k3},
|
|
||||||
ActiveKeyID: k1.KeyID,
|
|
||||||
expiresAt: time.Now().Add(time.Minute),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []jose.JWK{jwk1, jwk2, jwk3}
|
|
||||||
got, err := km.JWKs()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(want, got) {
|
|
||||||
t.Fatalf("JWK mismatch: want=%#v got=%#v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrivateKeyManagerSigner(t *testing.T) {
|
|
||||||
k := generatePrivateKeyStatic(t, 13)
|
|
||||||
|
|
||||||
km := NewPrivateKeyManager()
|
|
||||||
err := km.Set(&PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k},
|
|
||||||
ActiveKeyID: k.KeyID,
|
|
||||||
expiresAt: time.Now().Add(time.Minute),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
signer, err := km.Signer()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
wantID := "13"
|
|
||||||
gotID := signer.ID()
|
|
||||||
if wantID != gotID {
|
|
||||||
t.Fatalf("Signer has incorrect ID: want=%s got=%s", wantID, gotID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrivateKeyManagerHealthyFail(t *testing.T) {
|
|
||||||
keyFixture := generatePrivateKeyStatic(t, 1)
|
|
||||||
tests := []*privateKeyManager{
|
|
||||||
// keySet nil
|
|
||||||
&privateKeyManager{
|
|
||||||
keySet: nil,
|
|
||||||
clock: clockwork.NewRealClock(),
|
|
||||||
},
|
|
||||||
// zero keys
|
|
||||||
&privateKeyManager{
|
|
||||||
keySet: &PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{},
|
|
||||||
expiresAt: time.Now().Add(time.Minute),
|
|
||||||
},
|
|
||||||
clock: clockwork.NewRealClock(),
|
|
||||||
},
|
|
||||||
// key set expired
|
|
||||||
&privateKeyManager{
|
|
||||||
keySet: &PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{keyFixture},
|
|
||||||
expiresAt: time.Now().Add(-1 * time.Minute),
|
|
||||||
},
|
|
||||||
clock: clockwork.NewRealClock(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
if err := tt.Healthy(); err == nil {
|
|
||||||
t.Errorf("case %d: nil error", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrivateKeyManagerHealthyFailsOtherMethods(t *testing.T) {
|
|
||||||
km := NewPrivateKeyManager()
|
|
||||||
if _, err := km.JWKs(); err == nil {
|
|
||||||
t.Fatalf("Expected non-nil error")
|
|
||||||
}
|
|
||||||
if _, err := km.Signer(); err == nil {
|
|
||||||
t.Fatalf("Expected non-nil error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrivateKeyManagerExpiresAt(t *testing.T) {
|
|
||||||
fc := clockwork.NewFakeClock()
|
|
||||||
now := fc.Now().UTC()
|
|
||||||
|
|
||||||
k := generatePrivateKeyStatic(t, 17)
|
|
||||||
km := &privateKeyManager{
|
|
||||||
clock: fc,
|
|
||||||
}
|
|
||||||
|
|
||||||
want := fc.Now().UTC()
|
|
||||||
got := km.ExpiresAt()
|
|
||||||
if want != got {
|
|
||||||
t.Fatalf("Incorrect expiration time: want=%v got=%v", want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := km.Set(&PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k},
|
|
||||||
ActiveKeyID: k.KeyID,
|
|
||||||
expiresAt: now.Add(2 * time.Minute),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want = fc.Now().UTC().Add(2 * time.Minute)
|
|
||||||
got = km.ExpiresAt()
|
|
||||||
if want != got {
|
|
||||||
t.Fatalf("Incorrect expiration time: want=%v got=%v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPublicKeys(t *testing.T) {
|
|
||||||
km := NewPrivateKeyManager()
|
|
||||||
k1 := generatePrivateKeyStatic(t, 1)
|
|
||||||
k2 := generatePrivateKeyStatic(t, 2)
|
|
||||||
k3 := generatePrivateKeyStatic(t, 3)
|
|
||||||
|
|
||||||
tests := [][]*PrivateKey{
|
|
||||||
[]*PrivateKey{k1},
|
|
||||||
[]*PrivateKey{k1, k2},
|
|
||||||
[]*PrivateKey{k1, k2, k3},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
ks := &PrivateKeySet{
|
|
||||||
keys: tt,
|
|
||||||
expiresAt: time.Now().Add(time.Hour),
|
|
||||||
}
|
|
||||||
km.Set(ks)
|
|
||||||
|
|
||||||
jwks, err := km.JWKs()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pks := NewPublicKeySet(jwks, time.Now().Add(time.Hour))
|
|
||||||
want := pks.Keys()
|
|
||||||
got, err := km.PublicKeys()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(want, got) {
|
|
||||||
t.Errorf("case %d: Invalid public keys: want=%v got=%v", i, want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
55
vendor/github.com/coreos/go-oidc/key/repo.go
generated
vendored
55
vendor/github.com/coreos/go-oidc/key/repo.go
generated
vendored
|
@ -1,55 +0,0 @@
|
||||||
package key
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
var ErrorNoKeys = errors.New("no keys found")
|
|
||||||
|
|
||||||
type WritableKeySetRepo interface {
|
|
||||||
Set(KeySet) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReadableKeySetRepo interface {
|
|
||||||
Get() (KeySet, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type PrivateKeySetRepo interface {
|
|
||||||
WritableKeySetRepo
|
|
||||||
ReadableKeySetRepo
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPrivateKeySetRepo() PrivateKeySetRepo {
|
|
||||||
return &memPrivateKeySetRepo{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type memPrivateKeySetRepo struct {
|
|
||||||
mu sync.RWMutex
|
|
||||||
pks PrivateKeySet
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *memPrivateKeySetRepo) Set(ks KeySet) error {
|
|
||||||
pks, ok := ks.(*PrivateKeySet)
|
|
||||||
if !ok {
|
|
||||||
return errors.New("unable to cast to PrivateKeySet")
|
|
||||||
} else if pks == nil {
|
|
||||||
return errors.New("nil KeySet")
|
|
||||||
}
|
|
||||||
|
|
||||||
r.mu.Lock()
|
|
||||||
defer r.mu.Unlock()
|
|
||||||
|
|
||||||
r.pks = *pks
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *memPrivateKeySetRepo) Get() (KeySet, error) {
|
|
||||||
r.mu.RLock()
|
|
||||||
defer r.mu.RUnlock()
|
|
||||||
|
|
||||||
if r.pks.keys == nil {
|
|
||||||
return nil, ErrorNoKeys
|
|
||||||
}
|
|
||||||
return KeySet(&r.pks), nil
|
|
||||||
}
|
|
159
vendor/github.com/coreos/go-oidc/key/rotate.go
generated
vendored
159
vendor/github.com/coreos/go-oidc/key/rotate.go
generated
vendored
|
@ -1,159 +0,0 @@
|
||||||
package key
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"log"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
ptime "github.com/coreos/pkg/timeutil"
|
|
||||||
"github.com/jonboulle/clockwork"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrorPrivateKeysExpired = errors.New("private keys have expired")
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewPrivateKeyRotator(repo PrivateKeySetRepo, ttl time.Duration) *PrivateKeyRotator {
|
|
||||||
return &PrivateKeyRotator{
|
|
||||||
repo: repo,
|
|
||||||
ttl: ttl,
|
|
||||||
|
|
||||||
keep: 2,
|
|
||||||
generateKey: GeneratePrivateKey,
|
|
||||||
clock: clockwork.NewRealClock(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type PrivateKeyRotator struct {
|
|
||||||
repo PrivateKeySetRepo
|
|
||||||
generateKey GeneratePrivateKeyFunc
|
|
||||||
clock clockwork.Clock
|
|
||||||
keep int
|
|
||||||
ttl time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *PrivateKeyRotator) expiresAt() time.Time {
|
|
||||||
return r.clock.Now().UTC().Add(r.ttl)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *PrivateKeyRotator) Healthy() error {
|
|
||||||
pks, err := r.privateKeySet()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.clock.Now().After(pks.ExpiresAt()) {
|
|
||||||
return ErrorPrivateKeysExpired
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *PrivateKeyRotator) privateKeySet() (*PrivateKeySet, error) {
|
|
||||||
ks, err := r.repo.Get()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
pks, ok := ks.(*PrivateKeySet)
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("unable to cast to PrivateKeySet")
|
|
||||||
}
|
|
||||||
return pks, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *PrivateKeyRotator) nextRotation() (time.Duration, error) {
|
|
||||||
pks, err := r.privateKeySet()
|
|
||||||
if err == ErrorNoKeys {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
now := r.clock.Now()
|
|
||||||
|
|
||||||
// Ideally, we want to rotate after half the TTL has elapsed.
|
|
||||||
idealRotationTime := pks.ExpiresAt().Add(-r.ttl / 2)
|
|
||||||
|
|
||||||
// If we are past the ideal rotation time, rotate immediatly.
|
|
||||||
return max(0, idealRotationTime.Sub(now)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func max(a, b time.Duration) time.Duration {
|
|
||||||
if a > b {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *PrivateKeyRotator) Run() chan struct{} {
|
|
||||||
attempt := func() {
|
|
||||||
k, err := r.generateKey()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("go-oidc: failed generating signing key: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
exp := r.expiresAt()
|
|
||||||
if err := rotatePrivateKeys(r.repo, k, r.keep, exp); err != nil {
|
|
||||||
log.Printf("go-oidc: key rotation failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stop := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
var nextRotation time.Duration
|
|
||||||
var sleep time.Duration
|
|
||||||
var err error
|
|
||||||
for {
|
|
||||||
if nextRotation, err = r.nextRotation(); err == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
sleep = ptime.ExpBackoff(sleep, time.Minute)
|
|
||||||
log.Printf("go-oidc: error getting nextRotation, retrying in %v: %v", sleep, err)
|
|
||||||
time.Sleep(sleep)
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-r.clock.After(nextRotation):
|
|
||||||
attempt()
|
|
||||||
case <-stop:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return stop
|
|
||||||
}
|
|
||||||
|
|
||||||
func rotatePrivateKeys(repo PrivateKeySetRepo, k *PrivateKey, keep int, exp time.Time) error {
|
|
||||||
ks, err := repo.Get()
|
|
||||||
if err != nil && err != ErrorNoKeys {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var keys []*PrivateKey
|
|
||||||
if ks != nil {
|
|
||||||
pks, ok := ks.(*PrivateKeySet)
|
|
||||||
if !ok {
|
|
||||||
return errors.New("unable to cast to PrivateKeySet")
|
|
||||||
}
|
|
||||||
keys = pks.Keys()
|
|
||||||
}
|
|
||||||
|
|
||||||
keys = append([]*PrivateKey{k}, keys...)
|
|
||||||
if l := len(keys); l > keep {
|
|
||||||
keys = keys[0:keep]
|
|
||||||
}
|
|
||||||
|
|
||||||
nks := PrivateKeySet{
|
|
||||||
keys: keys,
|
|
||||||
ActiveKeyID: k.ID(),
|
|
||||||
expiresAt: exp,
|
|
||||||
}
|
|
||||||
|
|
||||||
return repo.Set(KeySet(&nks))
|
|
||||||
}
|
|
311
vendor/github.com/coreos/go-oidc/key/rotate_test.go
generated
vendored
311
vendor/github.com/coreos/go-oidc/key/rotate_test.go
generated
vendored
|
@ -1,311 +0,0 @@
|
||||||
package key
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/jonboulle/clockwork"
|
|
||||||
)
|
|
||||||
|
|
||||||
func generatePrivateKeySerialFunc(t *testing.T) GeneratePrivateKeyFunc {
|
|
||||||
var n int
|
|
||||||
return func() (*PrivateKey, error) {
|
|
||||||
n++
|
|
||||||
return generatePrivateKeyStatic(t, n), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRotate(t *testing.T) {
|
|
||||||
now := time.Now()
|
|
||||||
k1 := generatePrivateKeyStatic(t, 1)
|
|
||||||
k2 := generatePrivateKeyStatic(t, 2)
|
|
||||||
k3 := generatePrivateKeyStatic(t, 3)
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
start *PrivateKeySet
|
|
||||||
key *PrivateKey
|
|
||||||
keep int
|
|
||||||
exp time.Time
|
|
||||||
want *PrivateKeySet
|
|
||||||
}{
|
|
||||||
// start with nil keys
|
|
||||||
{
|
|
||||||
start: nil,
|
|
||||||
key: k1,
|
|
||||||
keep: 2,
|
|
||||||
exp: now.Add(time.Second),
|
|
||||||
want: &PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k1},
|
|
||||||
ActiveKeyID: k1.KeyID,
|
|
||||||
expiresAt: now.Add(time.Second),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// start with zero keys
|
|
||||||
{
|
|
||||||
start: &PrivateKeySet{},
|
|
||||||
key: k1,
|
|
||||||
keep: 2,
|
|
||||||
exp: now.Add(time.Second),
|
|
||||||
want: &PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k1},
|
|
||||||
ActiveKeyID: k1.KeyID,
|
|
||||||
expiresAt: now.Add(time.Second),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// add second key
|
|
||||||
{
|
|
||||||
start: &PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k1},
|
|
||||||
ActiveKeyID: k1.KeyID,
|
|
||||||
expiresAt: now,
|
|
||||||
},
|
|
||||||
key: k2,
|
|
||||||
keep: 2,
|
|
||||||
exp: now.Add(time.Second),
|
|
||||||
want: &PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k2, k1},
|
|
||||||
ActiveKeyID: k2.KeyID,
|
|
||||||
expiresAt: now.Add(time.Second),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// rotate in third key
|
|
||||||
{
|
|
||||||
start: &PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k2, k1},
|
|
||||||
ActiveKeyID: k2.KeyID,
|
|
||||||
expiresAt: now,
|
|
||||||
},
|
|
||||||
key: k3,
|
|
||||||
keep: 2,
|
|
||||||
exp: now.Add(time.Second),
|
|
||||||
want: &PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k3, k2},
|
|
||||||
ActiveKeyID: k3.KeyID,
|
|
||||||
expiresAt: now.Add(time.Second),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
repo := NewPrivateKeySetRepo()
|
|
||||||
if tt.start != nil {
|
|
||||||
err := repo.Set(tt.start)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("case %d: unexpected error: %v", i, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rotatePrivateKeys(repo, tt.key, tt.keep, tt.exp)
|
|
||||||
got, err := repo.Get()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(tt.want, got) {
|
|
||||||
t.Errorf("case %d: unexpected result: want=%#v got=%#v", i, tt.want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrivateKeyRotatorRun(t *testing.T) {
|
|
||||||
fc := clockwork.NewFakeClock()
|
|
||||||
now := fc.Now().UTC()
|
|
||||||
|
|
||||||
k1 := generatePrivateKeyStatic(t, 1)
|
|
||||||
k2 := generatePrivateKeyStatic(t, 2)
|
|
||||||
k3 := generatePrivateKeyStatic(t, 3)
|
|
||||||
k4 := generatePrivateKeyStatic(t, 4)
|
|
||||||
|
|
||||||
kRepo := NewPrivateKeySetRepo()
|
|
||||||
krot := NewPrivateKeyRotator(kRepo, 4*time.Second)
|
|
||||||
krot.clock = fc
|
|
||||||
krot.generateKey = generatePrivateKeySerialFunc(t)
|
|
||||||
|
|
||||||
steps := []*PrivateKeySet{
|
|
||||||
&PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k1},
|
|
||||||
ActiveKeyID: k1.KeyID,
|
|
||||||
expiresAt: now.Add(4 * time.Second),
|
|
||||||
},
|
|
||||||
&PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k2, k1},
|
|
||||||
ActiveKeyID: k2.KeyID,
|
|
||||||
expiresAt: now.Add(6 * time.Second),
|
|
||||||
},
|
|
||||||
&PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k3, k2},
|
|
||||||
ActiveKeyID: k3.KeyID,
|
|
||||||
expiresAt: now.Add(8 * time.Second),
|
|
||||||
},
|
|
||||||
&PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k4, k3},
|
|
||||||
ActiveKeyID: k4.KeyID,
|
|
||||||
expiresAt: now.Add(10 * time.Second),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
stop := krot.Run()
|
|
||||||
defer close(stop)
|
|
||||||
|
|
||||||
for i, st := range steps {
|
|
||||||
// wait for the rotater to get sleepy
|
|
||||||
fc.BlockUntil(1)
|
|
||||||
|
|
||||||
got, err := kRepo.Get()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("step %d: unexpected error: %v", i, err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(st, got) {
|
|
||||||
t.Fatalf("step %d: unexpected state: want=%#v got=%#v", i, st, got)
|
|
||||||
}
|
|
||||||
fc.Advance(2 * time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrivateKeyRotatorExpiresAt(t *testing.T) {
|
|
||||||
fc := clockwork.NewFakeClock()
|
|
||||||
krot := &PrivateKeyRotator{
|
|
||||||
clock: fc,
|
|
||||||
ttl: time.Minute,
|
|
||||||
}
|
|
||||||
got := krot.expiresAt()
|
|
||||||
want := fc.Now().UTC().Add(time.Minute)
|
|
||||||
if !reflect.DeepEqual(want, got) {
|
|
||||||
t.Errorf("Incorrect expiration time: want=%v got=%v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNextRotation(t *testing.T) {
|
|
||||||
fc := clockwork.NewFakeClock()
|
|
||||||
now := fc.Now().UTC()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
expiresAt time.Time
|
|
||||||
ttl time.Duration
|
|
||||||
numKeys int
|
|
||||||
expected time.Duration
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
// closest to prod
|
|
||||||
expiresAt: now.Add(time.Hour * 24),
|
|
||||||
ttl: time.Hour * 24,
|
|
||||||
numKeys: 2,
|
|
||||||
expected: time.Hour * 12,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expiresAt: now.Add(time.Hour * 2),
|
|
||||||
ttl: time.Hour * 4,
|
|
||||||
numKeys: 2,
|
|
||||||
expected: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// No keys.
|
|
||||||
expiresAt: now.Add(time.Hour * 2),
|
|
||||||
ttl: time.Hour * 4,
|
|
||||||
numKeys: 0,
|
|
||||||
expected: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Nil keyset.
|
|
||||||
expiresAt: now.Add(time.Hour * 2),
|
|
||||||
ttl: time.Hour * 4,
|
|
||||||
numKeys: -1,
|
|
||||||
expected: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// KeySet expired.
|
|
||||||
expiresAt: now.Add(time.Hour * -2),
|
|
||||||
ttl: time.Hour * 4,
|
|
||||||
numKeys: 2,
|
|
||||||
expected: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Expiry past now + TTL
|
|
||||||
expiresAt: now.Add(time.Hour * 5),
|
|
||||||
ttl: time.Hour * 4,
|
|
||||||
numKeys: 2,
|
|
||||||
expected: 3 * time.Hour,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
kRepo := NewPrivateKeySetRepo()
|
|
||||||
krot := NewPrivateKeyRotator(kRepo, tt.ttl)
|
|
||||||
krot.clock = fc
|
|
||||||
pks := &PrivateKeySet{
|
|
||||||
expiresAt: tt.expiresAt,
|
|
||||||
}
|
|
||||||
if tt.numKeys != -1 {
|
|
||||||
for n := 0; n < tt.numKeys; n++ {
|
|
||||||
pks.keys = append(pks.keys, generatePrivateKeyStatic(t, n))
|
|
||||||
}
|
|
||||||
err := kRepo.Set(pks)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("case %d: unexpected error: %v", i, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
actual, err := krot.nextRotation()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("case %d: error calling shouldRotate(): %v", i, err)
|
|
||||||
}
|
|
||||||
if actual != tt.expected {
|
|
||||||
t.Errorf("case %d: actual == %v, want %v", i, actual, tt.expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHealthy(t *testing.T) {
|
|
||||||
fc := clockwork.NewFakeClock()
|
|
||||||
now := fc.Now().UTC()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
expiresAt time.Time
|
|
||||||
numKeys int
|
|
||||||
expected error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
expiresAt: now.Add(time.Hour),
|
|
||||||
numKeys: 2,
|
|
||||||
expected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expiresAt: now.Add(time.Hour),
|
|
||||||
numKeys: -1,
|
|
||||||
expected: ErrorNoKeys,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expiresAt: now.Add(time.Hour),
|
|
||||||
numKeys: 0,
|
|
||||||
expected: ErrorNoKeys,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expiresAt: now.Add(-time.Hour),
|
|
||||||
numKeys: 2,
|
|
||||||
expected: ErrorPrivateKeysExpired,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
kRepo := NewPrivateKeySetRepo()
|
|
||||||
krot := NewPrivateKeyRotator(kRepo, time.Hour)
|
|
||||||
krot.clock = fc
|
|
||||||
pks := &PrivateKeySet{
|
|
||||||
expiresAt: tt.expiresAt,
|
|
||||||
}
|
|
||||||
if tt.numKeys != -1 {
|
|
||||||
for n := 0; n < tt.numKeys; n++ {
|
|
||||||
pks.keys = append(pks.keys, generatePrivateKeyStatic(t, n))
|
|
||||||
}
|
|
||||||
err := kRepo.Set(pks)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("case %d: unexpected error: %v", i, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if err := krot.Healthy(); err != tt.expected {
|
|
||||||
t.Errorf("case %d: got==%q, want==%q", i, err, tt.expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
91
vendor/github.com/coreos/go-oidc/key/sync.go
generated
vendored
91
vendor/github.com/coreos/go-oidc/key/sync.go
generated
vendored
|
@ -1,91 +0,0 @@
|
||||||
package key
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"log"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/jonboulle/clockwork"
|
|
||||||
|
|
||||||
"github.com/coreos/pkg/timeutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewKeySetSyncer(r ReadableKeySetRepo, w WritableKeySetRepo) *KeySetSyncer {
|
|
||||||
return &KeySetSyncer{
|
|
||||||
readable: r,
|
|
||||||
writable: w,
|
|
||||||
clock: clockwork.NewRealClock(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type KeySetSyncer struct {
|
|
||||||
readable ReadableKeySetRepo
|
|
||||||
writable WritableKeySetRepo
|
|
||||||
clock clockwork.Clock
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *KeySetSyncer) Run() chan struct{} {
|
|
||||||
stop := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
var failing bool
|
|
||||||
var next time.Duration
|
|
||||||
for {
|
|
||||||
exp, err := syncKeySet(s.readable, s.writable, s.clock)
|
|
||||||
if err != nil || exp == 0 {
|
|
||||||
if !failing {
|
|
||||||
failing = true
|
|
||||||
next = time.Second
|
|
||||||
} else {
|
|
||||||
next = timeutil.ExpBackoff(next, time.Minute)
|
|
||||||
}
|
|
||||||
if exp == 0 {
|
|
||||||
log.Printf("Synced to already expired key set, retrying in %v: %v", next, err)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
log.Printf("Failed syncing key set, retrying in %v: %v", next, err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
failing = false
|
|
||||||
next = exp / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-s.clock.After(next):
|
|
||||||
continue
|
|
||||||
case <-stop:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return stop
|
|
||||||
}
|
|
||||||
|
|
||||||
func Sync(r ReadableKeySetRepo, w WritableKeySetRepo) (time.Duration, error) {
|
|
||||||
return syncKeySet(r, w, clockwork.NewRealClock())
|
|
||||||
}
|
|
||||||
|
|
||||||
// syncKeySet copies the keyset from r to the KeySet at w and returns the duration in which the KeySet will expire.
|
|
||||||
// If keyset has already expired, returns a zero duration.
|
|
||||||
func syncKeySet(r ReadableKeySetRepo, w WritableKeySetRepo, clock clockwork.Clock) (exp time.Duration, err error) {
|
|
||||||
var ks KeySet
|
|
||||||
ks, err = r.Get()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if ks == nil {
|
|
||||||
err = errors.New("no source KeySet")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = w.Set(ks); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
now := clock.Now()
|
|
||||||
if ks.ExpiresAt().After(now) {
|
|
||||||
exp = ks.ExpiresAt().Sub(now)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
214
vendor/github.com/coreos/go-oidc/key/sync_test.go
generated
vendored
214
vendor/github.com/coreos/go-oidc/key/sync_test.go
generated
vendored
|
@ -1,214 +0,0 @@
|
||||||
package key
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"reflect"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/jonboulle/clockwork"
|
|
||||||
)
|
|
||||||
|
|
||||||
type staticReadableKeySetRepo struct {
|
|
||||||
mu sync.RWMutex
|
|
||||||
ks KeySet
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *staticReadableKeySetRepo) Get() (KeySet, error) {
|
|
||||||
r.mu.RLock()
|
|
||||||
defer r.mu.RUnlock()
|
|
||||||
return r.ks, r.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *staticReadableKeySetRepo) set(ks KeySet, err error) {
|
|
||||||
r.mu.Lock()
|
|
||||||
defer r.mu.Unlock()
|
|
||||||
r.ks, r.err = ks, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeySyncerSync(t *testing.T) {
|
|
||||||
fc := clockwork.NewFakeClock()
|
|
||||||
now := fc.Now().UTC()
|
|
||||||
|
|
||||||
k1 := generatePrivateKeyStatic(t, 1)
|
|
||||||
k2 := generatePrivateKeyStatic(t, 2)
|
|
||||||
k3 := generatePrivateKeyStatic(t, 3)
|
|
||||||
|
|
||||||
steps := []struct {
|
|
||||||
fromKS KeySet
|
|
||||||
fromErr error
|
|
||||||
advance time.Duration
|
|
||||||
want *PrivateKeySet
|
|
||||||
}{
|
|
||||||
// on startup, first sync should trigger within a second
|
|
||||||
{
|
|
||||||
fromKS: &PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k1},
|
|
||||||
ActiveKeyID: k1.KeyID,
|
|
||||||
expiresAt: now.Add(10 * time.Second),
|
|
||||||
},
|
|
||||||
advance: time.Second,
|
|
||||||
want: &PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k1},
|
|
||||||
ActiveKeyID: k1.KeyID,
|
|
||||||
expiresAt: now.Add(10 * time.Second),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// advance halfway into TTL, triggering sync
|
|
||||||
{
|
|
||||||
fromKS: &PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k2, k1},
|
|
||||||
ActiveKeyID: k2.KeyID,
|
|
||||||
expiresAt: now.Add(15 * time.Second),
|
|
||||||
},
|
|
||||||
advance: 5 * time.Second,
|
|
||||||
want: &PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k2, k1},
|
|
||||||
ActiveKeyID: k2.KeyID,
|
|
||||||
expiresAt: now.Add(15 * time.Second),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// advance halfway into TTL, triggering sync that fails
|
|
||||||
{
|
|
||||||
fromErr: errors.New("fail!"),
|
|
||||||
advance: 10 * time.Second,
|
|
||||||
want: &PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k2, k1},
|
|
||||||
ActiveKeyID: k2.KeyID,
|
|
||||||
expiresAt: now.Add(15 * time.Second),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// sync retries quickly, and succeeds with fixed data
|
|
||||||
{
|
|
||||||
fromKS: &PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k3, k2, k1},
|
|
||||||
ActiveKeyID: k3.KeyID,
|
|
||||||
expiresAt: now.Add(25 * time.Second),
|
|
||||||
},
|
|
||||||
advance: 3 * time.Second,
|
|
||||||
want: &PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k3, k2, k1},
|
|
||||||
ActiveKeyID: k3.KeyID,
|
|
||||||
expiresAt: now.Add(25 * time.Second),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
from := &staticReadableKeySetRepo{}
|
|
||||||
to := NewPrivateKeySetRepo()
|
|
||||||
|
|
||||||
syncer := NewKeySetSyncer(from, to)
|
|
||||||
syncer.clock = fc
|
|
||||||
stop := syncer.Run()
|
|
||||||
defer close(stop)
|
|
||||||
|
|
||||||
for i, st := range steps {
|
|
||||||
from.set(st.fromKS, st.fromErr)
|
|
||||||
|
|
||||||
fc.Advance(st.advance)
|
|
||||||
fc.BlockUntil(1)
|
|
||||||
|
|
||||||
ks, err := to.Get()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("step %d: unable to get keys: %v", i, err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(st.want, ks) {
|
|
||||||
t.Fatalf("step %d: incorrect state: want=%#v got=%#v", i, st.want, ks)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSync(t *testing.T) {
|
|
||||||
fc := clockwork.NewFakeClock()
|
|
||||||
now := fc.Now().UTC()
|
|
||||||
|
|
||||||
k1 := generatePrivateKeyStatic(t, 1)
|
|
||||||
k2 := generatePrivateKeyStatic(t, 2)
|
|
||||||
k3 := generatePrivateKeyStatic(t, 3)
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
keySet *PrivateKeySet
|
|
||||||
want time.Duration
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
keySet: &PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k1},
|
|
||||||
ActiveKeyID: k1.KeyID,
|
|
||||||
expiresAt: now.Add(time.Minute),
|
|
||||||
},
|
|
||||||
want: time.Minute,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
keySet: &PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k2, k1},
|
|
||||||
ActiveKeyID: k2.KeyID,
|
|
||||||
expiresAt: now.Add(time.Minute),
|
|
||||||
},
|
|
||||||
want: time.Minute,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
keySet: &PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k3, k2, k1},
|
|
||||||
ActiveKeyID: k2.KeyID,
|
|
||||||
expiresAt: now.Add(time.Minute),
|
|
||||||
},
|
|
||||||
want: time.Minute,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
keySet: &PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k2, k1},
|
|
||||||
ActiveKeyID: k2.KeyID,
|
|
||||||
expiresAt: now.Add(time.Hour),
|
|
||||||
},
|
|
||||||
want: time.Hour,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
keySet: &PrivateKeySet{
|
|
||||||
keys: []*PrivateKey{k1},
|
|
||||||
ActiveKeyID: k1.KeyID,
|
|
||||||
expiresAt: now.Add(-time.Hour),
|
|
||||||
},
|
|
||||||
want: 0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
from := NewPrivateKeySetRepo()
|
|
||||||
to := NewPrivateKeySetRepo()
|
|
||||||
|
|
||||||
err := from.Set(tt.keySet)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
exp, err := syncKeySet(from, to, fc)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if tt.want != exp {
|
|
||||||
t.Errorf("case %d: want=%v got=%v", i, tt.want, exp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSyncFail(t *testing.T) {
|
|
||||||
tests := []error{
|
|
||||||
nil,
|
|
||||||
errors.New("fail!"),
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
from := &staticReadableKeySetRepo{ks: nil, err: tt}
|
|
||||||
to := NewPrivateKeySetRepo()
|
|
||||||
|
|
||||||
if _, err := syncKeySet(from, to, clockwork.NewFakeClock()); err == nil {
|
|
||||||
t.Errorf("case %d: expected non-nil error", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
2
vendor/github.com/coreos/go-oidc/oauth2/doc.go
generated
vendored
2
vendor/github.com/coreos/go-oidc/oauth2/doc.go
generated
vendored
|
@ -1,2 +0,0 @@
|
||||||
// Package oauth2 is DEPRECATED. Use golang.org/x/oauth instead.
|
|
||||||
package oauth2
|
|
29
vendor/github.com/coreos/go-oidc/oauth2/error.go
generated
vendored
29
vendor/github.com/coreos/go-oidc/oauth2/error.go
generated
vendored
|
@ -1,29 +0,0 @@
|
||||||
package oauth2
|
|
||||||
|
|
||||||
const (
|
|
||||||
ErrorAccessDenied = "access_denied"
|
|
||||||
ErrorInvalidClient = "invalid_client"
|
|
||||||
ErrorInvalidGrant = "invalid_grant"
|
|
||||||
ErrorInvalidRequest = "invalid_request"
|
|
||||||
ErrorServerError = "server_error"
|
|
||||||
ErrorUnauthorizedClient = "unauthorized_client"
|
|
||||||
ErrorUnsupportedGrantType = "unsupported_grant_type"
|
|
||||||
ErrorUnsupportedResponseType = "unsupported_response_type"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Error struct {
|
|
||||||
Type string `json:"error"`
|
|
||||||
Description string `json:"error_description,omitempty"`
|
|
||||||
State string `json:"state,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Error) Error() string {
|
|
||||||
if e.Description != "" {
|
|
||||||
return e.Type + ": " + e.Description
|
|
||||||
}
|
|
||||||
return e.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewError(typ string) *Error {
|
|
||||||
return &Error{Type: typ}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue