Switch from glide to govendor

This commit is contained in:
Manfred Touron 2017-12-19 13:55:52 +01:00
parent ccffd8bfe2
commit 230480afd1
1871 changed files with 302 additions and 801202 deletions

94
glide.lock generated
View File

@ -1,94 +0,0 @@
hash: 1971d053430c1e52bc18cb507ed44360e28782c9d1f5ff4c5d8d4acfb226c882
updated: 2017-10-26T17:01:26.57468+02:00
imports:
- name: github.com/aokoli/goutils
version: e57d01ace047c1a43e6a49ecf3ecc50ed2be81d1
- name: github.com/dgrijalva/jwt-go
version: c9eaceb2896dbb515dae7ec352b377a226a52721
- name: github.com/go-kit/kit
version: 9f5c614cd1e70102f80b644edbc760805ebf16d5
subpackages:
- auth/jwt
- endpoint
- log
- transport/grpc
- transport/http
- name: github.com/go-logfmt/logfmt
version: 390ab7935ee28ec6b286364bba9b4dd6410cb3d5
- name: github.com/go-stack/stack
version: 7a2f19628aabfe68f0766b59e74d6315f8347d22
- name: github.com/golang/glog
version: 23def4e6c14b4da8ac2ed8007337bc5eb5007998
- name: github.com/golang/protobuf
version: 8ee79997227bf9b34611aee7946ae64735e6fd93
subpackages:
- proto
- protoc-gen-go/descriptor
- protoc-gen-go/generator
- protoc-gen-go/plugin
- ptypes/timestamp
- name: github.com/gorilla/context
version: 1ea25387ff6f684839d82767c1733ff4d4d15d0a
- name: github.com/gorilla/handlers
version: e1b2144f2167de0e1042d1d35e5cba5119d4fb5d
- name: github.com/gorilla/mux
version: 24fca303ac6da784b9e8269f724ddeb0b2eea5e7
- name: github.com/grpc-ecosystem/grpc-gateway
version: 589b126116b5fc961939b3e156c29e4d9d58222f
subpackages:
- protoc-gen-grpc-gateway/descriptor
- protoc-gen-grpc-gateway/httprule
- utilities
- name: github.com/huandu/xstrings
version: 3959339b333561bf62a38b424fd41517c2c90f40
- name: github.com/imdario/mergo
version: 7fe0c75c13abdee74b09fcacef5ea1c6bba6a874
- name: github.com/kr/fs
version: 2788f0dbd16903de03cb8186e5c7d97b69ad387b
- name: github.com/kr/logfmt
version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0
- name: github.com/Masterminds/semver
version: 59c29afe1a994eacb71c833025ca7acf874bb1da
- name: github.com/Masterminds/sprig
version: 2f4371ac162f912989f01cc2b6af4ba6660e6a30
- name: github.com/satori/go.uuid
version: 879c5887cd475cd7864858769793b2ceb0d44feb
- name: golang.org/x/crypto
version: 0fe963104e9d1877082f8fb38f816fcd97eb1d10
subpackages:
- pbkdf2
- scrypt
- name: golang.org/x/net
version: da2b4fa28524a3baf148c1b94df4440267063c88
subpackages:
- context
- context/ctxhttp
- http2
- http2/hpack
- idna
- internal/timeseries
- lex/httplex
- trace
- name: golang.org/x/text
version: a49bea13b776691cb1b49873e5d8df96ec74831a
subpackages:
- secure/bidirule
- transform
- unicode/bidi
- unicode/norm
- name: google.golang.org/genproto
version: bb3573be0c484136831138976d444b8754777aff
subpackages:
- googleapis/api/annotations
- name: google.golang.org/grpc
version: 777daa17ff9b5daef1cfdf915088a2ada3332bf0
subpackages:
- codes
- credentials
- grpclog
- internal
- metadata
- naming
- peer
- transport
testImports: []

View File

@ -1,18 +0,0 @@
package: github.com/moul/protoc-gen-gotemplate
import:
- package: github.com/golang/protobuf
subpackages:
- proto
- protoc-gen-go/descriptor
- protoc-gen-go/generator
- protoc-gen-go/plugin
- package: github.com/kr/fs
- package: github.com/Masterminds/sprig
- package: github.com/huandu/xstrings
- package: google.golang.org/genproto
subpackages:
- googleapis/api/annotations
- package: github.com/grpc-ecosystem/grpc-gateway
version: 1.2.2
- package: github.com/gorilla/mux
version: ^1.5.0

View File

@ -1,25 +0,0 @@
language: go
go:
- 1.5
- 1.6
- 1.7
- tip
# Setting sudo access to false will let Travis CI use containers rather than
# VMs to run the tests. For more details see:
# - http://docs.travis-ci.com/user/workers/container-based-infrastructure/
# - http://docs.travis-ci.com/user/workers/standard-infrastructure/
sudo: false
script:
- GO15VENDOREXPERIMENT=1 make setup
- GO15VENDOREXPERIMENT=1 make test
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/06e3328629952dabe3e0
on_success: change # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: never # options: [always|never|change] default: always

View File

@ -1,157 +0,0 @@
package semver_test
import (
"testing"
"github.com/Masterminds/semver"
)
/* Constraint creation benchmarks */
func benchNewConstraint(c string, b *testing.B) {
for i := 0; i < b.N; i++ {
semver.NewConstraint(c)
}
}
func BenchmarkNewConstraintUnary(b *testing.B) {
benchNewConstraint("=2.0", b)
}
func BenchmarkNewConstraintTilde(b *testing.B) {
benchNewConstraint("~2.0.0", b)
}
func BenchmarkNewConstraintCaret(b *testing.B) {
benchNewConstraint("^2.0.0", b)
}
func BenchmarkNewConstraintWildcard(b *testing.B) {
benchNewConstraint("1.x", b)
}
func BenchmarkNewConstraintRange(b *testing.B) {
benchNewConstraint(">=2.1.x, <3.1.0", b)
}
func BenchmarkNewConstraintUnion(b *testing.B) {
benchNewConstraint("~2.0.0 || =3.1.0", b)
}
/* Check benchmarks */
func benchCheckVersion(c, v string, b *testing.B) {
version, _ := semver.NewVersion(v)
constraint, _ := semver.NewConstraint(c)
for i := 0; i < b.N; i++ {
constraint.Check(version)
}
}
func BenchmarkCheckVersionUnary(b *testing.B) {
benchCheckVersion("=2.0", "2.0.0", b)
}
func BenchmarkCheckVersionTilde(b *testing.B) {
benchCheckVersion("~2.0.0", "2.0.5", b)
}
func BenchmarkCheckVersionCaret(b *testing.B) {
benchCheckVersion("^2.0.0", "2.1.0", b)
}
func BenchmarkCheckVersionWildcard(b *testing.B) {
benchCheckVersion("1.x", "1.4.0", b)
}
func BenchmarkCheckVersionRange(b *testing.B) {
benchCheckVersion(">=2.1.x, <3.1.0", "2.4.5", b)
}
func BenchmarkCheckVersionUnion(b *testing.B) {
benchCheckVersion("~2.0.0 || =3.1.0", "3.1.0", b)
}
func benchValidateVersion(c, v string, b *testing.B) {
version, _ := semver.NewVersion(v)
constraint, _ := semver.NewConstraint(c)
for i := 0; i < b.N; i++ {
constraint.Validate(version)
}
}
/* Validate benchmarks, including fails */
func BenchmarkValidateVersionUnary(b *testing.B) {
benchValidateVersion("=2.0", "2.0.0", b)
}
func BenchmarkValidateVersionUnaryFail(b *testing.B) {
benchValidateVersion("=2.0", "2.0.1", b)
}
func BenchmarkValidateVersionTilde(b *testing.B) {
benchValidateVersion("~2.0.0", "2.0.5", b)
}
func BenchmarkValidateVersionTildeFail(b *testing.B) {
benchValidateVersion("~2.0.0", "1.0.5", b)
}
func BenchmarkValidateVersionCaret(b *testing.B) {
benchValidateVersion("^2.0.0", "2.1.0", b)
}
func BenchmarkValidateVersionCaretFail(b *testing.B) {
benchValidateVersion("^2.0.0", "4.1.0", b)
}
func BenchmarkValidateVersionWildcard(b *testing.B) {
benchValidateVersion("1.x", "1.4.0", b)
}
func BenchmarkValidateVersionWildcardFail(b *testing.B) {
benchValidateVersion("1.x", "2.4.0", b)
}
func BenchmarkValidateVersionRange(b *testing.B) {
benchValidateVersion(">=2.1.x, <3.1.0", "2.4.5", b)
}
func BenchmarkValidateVersionRangeFail(b *testing.B) {
benchValidateVersion(">=2.1.x, <3.1.0", "1.4.5", b)
}
func BenchmarkValidateVersionUnion(b *testing.B) {
benchValidateVersion("~2.0.0 || =3.1.0", "3.1.0", b)
}
func BenchmarkValidateVersionUnionFail(b *testing.B) {
benchValidateVersion("~2.0.0 || =3.1.0", "3.1.1", b)
}
/* Version creation benchmarks */
func benchNewVersion(v string, b *testing.B) {
for i := 0; i < b.N; i++ {
semver.NewVersion(v)
}
}
func BenchmarkNewVersionSimple(b *testing.B) {
benchNewVersion("1.0.0", b)
}
func BenchmarkNewVersionPre(b *testing.B) {
benchNewVersion("1.0.0-alpha", b)
}
func BenchmarkNewVersionMeta(b *testing.B) {
benchNewVersion("1.0.0+metadata", b)
}
func BenchmarkNewVersionMetaDash(b *testing.B) {
benchNewVersion("1.0.0+metadata-dash", b)
}

View File

@ -1,46 +0,0 @@
package semver
import (
"reflect"
"sort"
"testing"
)
func TestCollection(t *testing.T) {
raw := []string{
"1.2.3",
"1.0",
"1.3",
"2",
"0.4.2",
}
vs := make([]*Version, len(raw))
for i, r := range raw {
v, err := NewVersion(r)
if err != nil {
t.Errorf("Error parsing version: %s", err)
}
vs[i] = v
}
sort.Sort(Collection(vs))
e := []string{
"0.4.2",
"1.0.0",
"1.2.3",
"1.3.0",
"2.0.0",
}
a := make([]string, len(vs))
for i, v := range vs {
a[i] = v.String()
}
if !reflect.DeepEqual(a, e) {
t.Error("Sorting Collection failed")
}
}

View File

@ -1,452 +0,0 @@
package semver
import (
"reflect"
"testing"
)
func TestParseConstraint(t *testing.T) {
tests := []struct {
in string
f cfunc
v string
err bool
}{
{">= 1.2", constraintGreaterThanEqual, "1.2.0", false},
{"1.0", constraintTildeOrEqual, "1.0.0", false},
{"foo", nil, "", true},
{"<= 1.2", constraintLessThanEqual, "1.2.0", false},
{"=< 1.2", constraintLessThanEqual, "1.2.0", false},
{"=> 1.2", constraintGreaterThanEqual, "1.2.0", false},
{"v1.2", constraintTildeOrEqual, "1.2.0", false},
{"=1.5", constraintTildeOrEqual, "1.5.0", false},
{"> 1.3", constraintGreaterThan, "1.3.0", false},
{"< 1.4.1", constraintLessThan, "1.4.1", false},
}
for _, tc := range tests {
c, err := parseConstraint(tc.in)
if tc.err && err == nil {
t.Errorf("Expected error for %s didn't occur", tc.in)
} else if !tc.err && err != nil {
t.Errorf("Unexpected error for %s", tc.in)
}
// If an error was expected continue the loop and don't try the other
// tests as they will cause errors.
if tc.err {
continue
}
if tc.v != c.con.String() {
t.Errorf("Incorrect version found on %s", tc.in)
}
f1 := reflect.ValueOf(tc.f)
f2 := reflect.ValueOf(c.function)
if f1 != f2 {
t.Errorf("Wrong constraint found for %s", tc.in)
}
}
}
func TestConstraintCheck(t *testing.T) {
tests := []struct {
constraint string
version string
check bool
}{
{"= 2.0", "1.2.3", false},
{"= 2.0", "2.0.0", true},
{"4.1", "4.1.0", true},
{"!=4.1", "4.1.0", false},
{"!=4.1", "5.1.0", true},
{">1.1", "4.1.0", true},
{">1.1", "1.1.0", false},
{"<1.1", "0.1.0", true},
{"<1.1", "1.1.0", false},
{"<1.1", "1.1.1", false},
{">=1.1", "4.1.0", true},
{">=1.1", "1.1.0", true},
{">=1.1", "0.0.9", false},
{"<=1.1", "0.1.0", true},
{"<=1.1", "1.1.0", true},
{"<=1.1", "1.1.1", false},
{">0", "0.0.1-alpha", true},
{">=0", "0.0.1-alpha", true},
{">0", "0", false},
{">=0", "0", true},
}
for _, tc := range tests {
c, err := parseConstraint(tc.constraint)
if err != nil {
t.Errorf("err: %s", err)
continue
}
v, err := NewVersion(tc.version)
if err != nil {
t.Errorf("err: %s", err)
continue
}
a := c.check(v)
if a != tc.check {
t.Errorf("Constraint %q failing with %q", tc.constraint, tc.version)
}
}
}
func TestNewConstraint(t *testing.T) {
tests := []struct {
input string
ors int
count int
err bool
}{
{">= 1.1", 1, 1, false},
{"2.0", 1, 1, false},
{"v2.3.5-20161202202307-sha.e8fc5e5", 1, 1, false},
{">= bar", 0, 0, true},
{">= 1.2.3, < 2.0", 1, 2, false},
{">= 1.2.3, < 2.0 || => 3.0, < 4", 2, 2, false},
// The 3 - 4 should be broken into 2 by the range rewriting
{"3 - 4 || => 3.0, < 4", 2, 2, false},
}
for _, tc := range tests {
v, err := NewConstraint(tc.input)
if tc.err && err == nil {
t.Errorf("expected but did not get error for: %s", tc.input)
continue
} else if !tc.err && err != nil {
t.Errorf("unexpectederror for input %s: %s", tc.input, err)
continue
}
if tc.err {
continue
}
l := len(v.constraints)
if tc.ors != l {
t.Errorf("Expected %s to have %d ORs but got %d",
tc.input, tc.ors, l)
}
l = len(v.constraints[0])
if tc.count != l {
t.Errorf("Expected %s to have %d constraints but got %d",
tc.input, tc.count, l)
}
}
}
func TestConstraintsCheck(t *testing.T) {
tests := []struct {
constraint string
version string
check bool
}{
{"*", "1.2.3", true},
{"~0.0.0", "1.2.3", true},
{"= 2.0", "1.2.3", false},
{"= 2.0", "2.0.0", true},
{"4.1", "4.1.0", true},
{"4.1.x", "4.1.3", true},
{"1.x", "1.4", true},
{"!=4.1", "4.1.0", false},
{"!=4.1-alpha", "4.1.0-alpha", false},
{"!=4.1-alpha", "4.1.0", true},
{"!=4.1", "5.1.0", true},
{"!=4.x", "5.1.0", true},
{"!=4.x", "4.1.0", false},
{"!=4.1.x", "4.2.0", true},
{"!=4.2.x", "4.2.3", false},
{">1.1", "4.1.0", true},
{">1.1", "1.1.0", false},
{"<1.1", "0.1.0", true},
{"<1.1", "1.1.0", false},
{"<1.1", "1.1.1", false},
{"<1.x", "1.1.1", true},
{"<1.x", "2.1.1", false},
{"<1.1.x", "1.2.1", false},
{"<1.1.x", "1.1.500", true},
{"<1.2.x", "1.1.1", true},
{">=1.1", "4.1.0", true},
{">=1.1", "4.1.0-beta", false},
{">=1.1", "1.1.0", true},
{">=1.1", "0.0.9", false},
{"<=1.1", "0.1.0", true},
{"<=1.1", "0.1.0-alpha", false},
{"<=1.1-a", "0.1.0-alpha", true},
{"<=1.1", "1.1.0", true},
{"<=1.x", "1.1.0", true},
{"<=2.x", "3.1.0", false},
{"<=1.1", "1.1.1", false},
{"<=1.1.x", "1.2.500", false},
{">1.1, <2", "1.1.1", true},
{">1.1, <3", "4.3.2", false},
{">=1.1, <2, !=1.2.3", "1.2.3", false},
{">=1.1, <2, !=1.2.3 || > 3", "3.1.2", true},
{">=1.1, <2, !=1.2.3 || >= 3", "3.0.0", true},
{">=1.1, <2, !=1.2.3 || > 3", "3.0.0", false},
{">=1.1, <2, !=1.2.3 || > 3", "1.2.3", false},
{"1.1 - 2", "1.1.1", true},
{"1.1-3", "4.3.2", false},
{"^1.1", "1.1.1", true},
{"^1.1", "4.3.2", false},
{"^1.x", "1.1.1", true},
{"^2.x", "1.1.1", false},
{"^1.x", "2.1.1", false},
{"^1.x", "1.1.1-beta1", false},
{"^1.1.2-alpha", "1.2.1-beta1", true},
{"^1.2.x-alpha", "1.1.1-beta1", false},
{"~*", "2.1.1", true},
{"~1.x", "2.1.1", false},
{"~1.x", "1.3.5", true},
{"~1.x", "1.4", true},
{"~1.1", "1.1.1", true},
{"~1.1", "1.1.1-alpha", false},
{"~1.1-alpha", "1.1.1-beta", true},
{"~1.1.1-beta", "1.1.1-alpha", false},
{"~1.1.1-beta", "1.1.1", true},
{"~1.2.3", "1.2.5", true},
{"~1.2.3", "1.2.2", false},
{"~1.2.3", "1.3.2", false},
{"~1.1", "1.2.3", false},
{"~1.3", "2.4.5", false},
}
for _, tc := range tests {
c, err := NewConstraint(tc.constraint)
if err != nil {
t.Errorf("err: %s", err)
continue
}
v, err := NewVersion(tc.version)
if err != nil {
t.Errorf("err: %s", err)
continue
}
a := c.Check(v)
if a != tc.check {
t.Errorf("Constraint '%s' failing with '%s'", tc.constraint, tc.version)
}
}
}
func TestRewriteRange(t *testing.T) {
tests := []struct {
c string
nc string
}{
{"2 - 3", ">= 2, <= 3"},
{"2 - 3, 2 - 3", ">= 2, <= 3,>= 2, <= 3"},
{"2 - 3, 4.0.0 - 5.1", ">= 2, <= 3,>= 4.0.0, <= 5.1"},
}
for _, tc := range tests {
o := rewriteRange(tc.c)
if o != tc.nc {
t.Errorf("Range %s rewritten incorrectly as '%s'", tc.c, o)
}
}
}
func TestIsX(t *testing.T) {
tests := []struct {
t string
c bool
}{
{"A", false},
{"%", false},
{"X", true},
{"x", true},
{"*", true},
}
for _, tc := range tests {
a := isX(tc.t)
if a != tc.c {
t.Errorf("Function isX error on %s", tc.t)
}
}
}
func TestConstraintsValidate(t *testing.T) {
tests := []struct {
constraint string
version string
check bool
}{
{"*", "1.2.3", true},
{"~0.0.0", "1.2.3", true},
{"= 2.0", "1.2.3", false},
{"= 2.0", "2.0.0", true},
{"4.1", "4.1.0", true},
{"4.1.x", "4.1.3", true},
{"1.x", "1.4", true},
{"!=4.1", "4.1.0", false},
{"!=4.1", "5.1.0", true},
{"!=4.x", "5.1.0", true},
{"!=4.x", "4.1.0", false},
{"!=4.1.x", "4.2.0", true},
{"!=4.2.x", "4.2.3", false},
{">1.1", "4.1.0", true},
{">1.1", "1.1.0", false},
{"<1.1", "0.1.0", true},
{"<1.1", "1.1.0", false},
{"<1.1", "1.1.1", false},
{"<1.x", "1.1.1", true},
{"<1.x", "2.1.1", false},
{"<1.1.x", "1.2.1", false},
{"<1.1.x", "1.1.500", true},
{"<1.2.x", "1.1.1", true},
{">=1.1", "4.1.0", true},
{">=1.1", "1.1.0", true},
{">=1.1", "0.0.9", false},
{"<=1.1", "0.1.0", true},
{"<=1.1", "1.1.0", true},
{"<=1.x", "1.1.0", true},
{"<=2.x", "3.1.0", false},
{"<=1.1", "1.1.1", false},
{"<=1.1.x", "1.2.500", false},
{">1.1, <2", "1.1.1", true},
{">1.1, <3", "4.3.2", false},
{">=1.1, <2, !=1.2.3", "1.2.3", false},
{">=1.1, <2, !=1.2.3 || > 3", "3.1.2", true},
{">=1.1, <2, !=1.2.3 || >= 3", "3.0.0", true},
{">=1.1, <2, !=1.2.3 || > 3", "3.0.0", false},
{">=1.1, <2, !=1.2.3 || > 3", "1.2.3", false},
{"1.1 - 2", "1.1.1", true},
{"1.1-3", "4.3.2", false},
{"^1.1", "1.1.1", true},
{"^1.1", "1.1.1-alpha", false},
{"^1.1.1-alpha", "1.1.1-beta", true},
{"^1.1.1-beta", "1.1.1-alpha", false},
{"^1.1", "4.3.2", false},
{"^1.x", "1.1.1", true},
{"^2.x", "1.1.1", false},
{"^1.x", "2.1.1", false},
{"~*", "2.1.1", true},
{"~1.x", "2.1.1", false},
{"~1.x", "1.3.5", true},
{"~1.x", "1.3.5-beta", false},
{"~1.3.6-alpha", "1.3.5-beta", false},
{"~1.3.5-alpha", "1.3.5-beta", true},
{"~1.3.5-beta", "1.3.5-alpha", false},
{"~1.x", "1.4", true},
{"~1.1", "1.1.1", true},
{"~1.2.3", "1.2.5", true},
{"~1.2.3", "1.2.2", false},
{"~1.2.3", "1.3.2", false},
{"~1.1", "1.2.3", false},
{"~1.3", "2.4.5", false},
}
for _, tc := range tests {
c, err := NewConstraint(tc.constraint)
if err != nil {
t.Errorf("err: %s", err)
continue
}
v, err := NewVersion(tc.version)
if err != nil {
t.Errorf("err: %s", err)
continue
}
a, msgs := c.Validate(v)
if a != tc.check {
t.Errorf("Constraint '%s' failing with '%s'", tc.constraint, tc.version)
} else if !a && len(msgs) == 0 {
t.Errorf("%q failed with %q but no errors returned", tc.constraint, tc.version)
}
// if a == false {
// for _, m := range msgs {
// t.Errorf("%s", m)
// }
// }
}
v, err := NewVersion("1.2.3")
if err != nil {
t.Errorf("err: %s", err)
}
c, err := NewConstraint("!= 1.2.5, ^2, <= 1.1.x")
if err != nil {
t.Errorf("err: %s", err)
}
_, msgs := c.Validate(v)
if len(msgs) != 2 {
t.Error("Invalid number of validations found")
}
e := msgs[0].Error()
if e != "1.2.3 does not have same major version as 2" {
t.Error("Did not get expected message: 1.2.3 does not have same major version as 2")
}
e = msgs[1].Error()
if e != "1.2.3 is greater than 1.1.x" {
t.Error("Did not get expected message: 1.2.3 is greater than 1.1.x")
}
tests2 := []struct {
constraint, version, msg string
}{
{"= 2.0", "1.2.3", "1.2.3 is not equal to 2.0"},
{"!=4.1", "4.1.0", "4.1.0 is equal to 4.1"},
{"!=4.x", "4.1.0", "4.1.0 is equal to 4.x"},
{"!=4.2.x", "4.2.3", "4.2.3 is equal to 4.2.x"},
{">1.1", "1.1.0", "1.1.0 is less than or equal to 1.1"},
{"<1.1", "1.1.0", "1.1.0 is greater than or equal to 1.1"},
{"<1.1", "1.1.1", "1.1.1 is greater than or equal to 1.1"},
{"<1.x", "2.1.1", "2.1.1 is greater than or equal to 1.x"},
{"<1.1.x", "1.2.1", "1.2.1 is greater than or equal to 1.1.x"},
{">=1.1", "0.0.9", "0.0.9 is less than 1.1"},
{"<=2.x", "3.1.0", "3.1.0 is greater than 2.x"},
{"<=1.1", "1.1.1", "1.1.1 is greater than 1.1"},
{"<=1.1.x", "1.2.500", "1.2.500 is greater than 1.1.x"},
{">1.1, <3", "4.3.2", "4.3.2 is greater than or equal to 3"},
{">=1.1, <2, !=1.2.3", "1.2.3", "1.2.3 is equal to 1.2.3"},
{">=1.1, <2, !=1.2.3 || > 3", "3.0.0", "3.0.0 is greater than or equal to 2"},
{">=1.1, <2, !=1.2.3 || > 3", "1.2.3", "1.2.3 is equal to 1.2.3"},
{"1.1 - 3", "4.3.2", "4.3.2 is greater than 3"},
{"^1.1", "4.3.2", "4.3.2 does not have same major version as 1.1"},
{"^2.x", "1.1.1", "1.1.1 does not have same major version as 2.x"},
{"^1.x", "2.1.1", "2.1.1 does not have same major version as 1.x"},
{"~1.x", "2.1.1", "2.1.1 does not have same major and minor version as 1.x"},
{"~1.2.3", "1.2.2", "1.2.2 does not have same major and minor version as 1.2.3"},
{"~1.2.3", "1.3.2", "1.3.2 does not have same major and minor version as 1.2.3"},
{"~1.1", "1.2.3", "1.2.3 does not have same major and minor version as 1.1"},
{"~1.3", "2.4.5", "2.4.5 does not have same major and minor version as 1.3"},
}
for _, tc := range tests2 {
c, err := NewConstraint(tc.constraint)
if err != nil {
t.Errorf("err: %s", err)
continue
}
v, err := NewVersion(tc.version)
if err != nil {
t.Errorf("err: %s", err)
continue
}
_, msgs := c.Validate(v)
e := msgs[0].Error()
if e != tc.msg {
t.Errorf("Did not get expected message %q: %s", tc.msg, e)
}
}
}

View File

@ -1,450 +0,0 @@
package semver
import (
"testing"
)
func TestNewVersion(t *testing.T) {
tests := []struct {
version string
err bool
}{
{"1.2.3", false},
{"v1.2.3", false},
{"1.0", false},
{"v1.0", false},
{"1", false},
{"v1", false},
{"1.2.beta", true},
{"v1.2.beta", true},
{"foo", true},
{"1.2-5", false},
{"v1.2-5", false},
{"1.2-beta.5", false},
{"v1.2-beta.5", false},
{"\n1.2", true},
{"\nv1.2", true},
{"1.2.0-x.Y.0+metadata", false},
{"v1.2.0-x.Y.0+metadata", false},
{"1.2.0-x.Y.0+metadata-width-hypen", false},
{"v1.2.0-x.Y.0+metadata-width-hypen", false},
{"1.2.3-rc1-with-hypen", false},
{"v1.2.3-rc1-with-hypen", false},
{"1.2.3.4", true},
{"v1.2.3.4", true},
}
for _, tc := range tests {
_, err := NewVersion(tc.version)
if tc.err && err == nil {
t.Fatalf("expected error for version: %s", tc.version)
} else if !tc.err && err != nil {
t.Fatalf("error for version %s: %s", tc.version, err)
}
}
}
func TestOriginal(t *testing.T) {
tests := []string{
"1.2.3",
"v1.2.3",
"1.0",
"v1.0",
"1",
"v1",
"1.2-5",
"v1.2-5",
"1.2-beta.5",
"v1.2-beta.5",
"1.2.0-x.Y.0+metadata",
"v1.2.0-x.Y.0+metadata",
"1.2.0-x.Y.0+metadata-width-hypen",
"v1.2.0-x.Y.0+metadata-width-hypen",
"1.2.3-rc1-with-hypen",
"v1.2.3-rc1-with-hypen",
}
for _, tc := range tests {
v, err := NewVersion(tc)
if err != nil {
t.Errorf("Error parsing version %s", tc)
}
o := v.Original()
if o != tc {
t.Errorf("Error retrieving originl. Expected '%s' but got '%s'", tc, v)
}
}
}
func TestParts(t *testing.T) {
v, err := NewVersion("1.2.3-beta.1+build.123")
if err != nil {
t.Error("Error parsing version 1.2.3-beta.1+build.123")
}
if v.Major() != 1 {
t.Error("Major() returning wrong value")
}
if v.Minor() != 2 {
t.Error("Minor() returning wrong value")
}
if v.Patch() != 3 {
t.Error("Patch() returning wrong value")
}
if v.Prerelease() != "beta.1" {
t.Error("Prerelease() returning wrong value")
}
if v.Metadata() != "build.123" {
t.Error("Metadata() returning wrong value")
}
}
func TestString(t *testing.T) {
tests := []struct {
version string
expected string
}{
{"1.2.3", "1.2.3"},
{"v1.2.3", "1.2.3"},
{"1.0", "1.0.0"},
{"v1.0", "1.0.0"},
{"1", "1.0.0"},
{"v1", "1.0.0"},
{"1.2-5", "1.2.0-5"},
{"v1.2-5", "1.2.0-5"},
{"1.2-beta.5", "1.2.0-beta.5"},
{"v1.2-beta.5", "1.2.0-beta.5"},
{"1.2.0-x.Y.0+metadata", "1.2.0-x.Y.0+metadata"},
{"v1.2.0-x.Y.0+metadata", "1.2.0-x.Y.0+metadata"},
{"1.2.0-x.Y.0+metadata-width-hypen", "1.2.0-x.Y.0+metadata-width-hypen"},
{"v1.2.0-x.Y.0+metadata-width-hypen", "1.2.0-x.Y.0+metadata-width-hypen"},
{"1.2.3-rc1-with-hypen", "1.2.3-rc1-with-hypen"},
{"v1.2.3-rc1-with-hypen", "1.2.3-rc1-with-hypen"},
}
for _, tc := range tests {
v, err := NewVersion(tc.version)
if err != nil {
t.Errorf("Error parsing version %s", tc)
}
s := v.String()
if s != tc.expected {
t.Errorf("Error generating string. Expected '%s' but got '%s'", tc.expected, s)
}
}
}
func TestCompare(t *testing.T) {
tests := []struct {
v1 string
v2 string
expected int
}{
{"1.2.3", "1.5.1", -1},
{"2.2.3", "1.5.1", 1},
{"2.2.3", "2.2.2", 1},
{"3.2-beta", "3.2-beta", 0},
{"1.3", "1.1.4", 1},
{"4.2", "4.2-beta", 1},
{"4.2-beta", "4.2", -1},
{"4.2-alpha", "4.2-beta", -1},
{"4.2-alpha", "4.2-alpha", 0},
{"4.2-beta.2", "4.2-beta.1", 1},
{"4.2-beta2", "4.2-beta1", 1},
{"4.2-beta", "4.2-beta.2", -1},
{"4.2-beta", "4.2-beta.foo", 1},
{"4.2-beta.2", "4.2-beta", 1},
{"4.2-beta.foo", "4.2-beta", -1},
{"1.2+bar", "1.2+baz", 0},
}
for _, tc := range tests {
v1, err := NewVersion(tc.v1)
if err != nil {
t.Errorf("Error parsing version: %s", err)
}
v2, err := NewVersion(tc.v2)
if err != nil {
t.Errorf("Error parsing version: %s", err)
}
a := v1.Compare(v2)
e := tc.expected
if a != e {
t.Errorf(
"Comparison of '%s' and '%s' failed. Expected '%d', got '%d'",
tc.v1, tc.v2, e, a,
)
}
}
}
func TestLessThan(t *testing.T) {
tests := []struct {
v1 string
v2 string
expected bool
}{
{"1.2.3", "1.5.1", true},
{"2.2.3", "1.5.1", false},
{"3.2-beta", "3.2-beta", false},
}
for _, tc := range tests {
v1, err := NewVersion(tc.v1)
if err != nil {
t.Errorf("Error parsing version: %s", err)
}
v2, err := NewVersion(tc.v2)
if err != nil {
t.Errorf("Error parsing version: %s", err)
}
a := v1.LessThan(v2)
e := tc.expected
if a != e {
t.Errorf(
"Comparison of '%s' and '%s' failed. Expected '%t', got '%t'",
tc.v1, tc.v2, e, a,
)
}
}
}
func TestGreaterThan(t *testing.T) {
tests := []struct {
v1 string
v2 string
expected bool
}{
{"1.2.3", "1.5.1", false},
{"2.2.3", "1.5.1", true},
{"3.2-beta", "3.2-beta", false},
}
for _, tc := range tests {
v1, err := NewVersion(tc.v1)
if err != nil {
t.Errorf("Error parsing version: %s", err)
}
v2, err := NewVersion(tc.v2)
if err != nil {
t.Errorf("Error parsing version: %s", err)
}
a := v1.GreaterThan(v2)
e := tc.expected
if a != e {
t.Errorf(
"Comparison of '%s' and '%s' failed. Expected '%t', got '%t'",
tc.v1, tc.v2, e, a,
)
}
}
}
func TestEqual(t *testing.T) {
tests := []struct {
v1 string
v2 string
expected bool
}{
{"1.2.3", "1.5.1", false},
{"2.2.3", "1.5.1", false},
{"3.2-beta", "3.2-beta", true},
{"3.2-beta+foo", "3.2-beta+bar", true},
}
for _, tc := range tests {
v1, err := NewVersion(tc.v1)
if err != nil {
t.Errorf("Error parsing version: %s", err)
}
v2, err := NewVersion(tc.v2)
if err != nil {
t.Errorf("Error parsing version: %s", err)
}
a := v1.Equal(v2)
e := tc.expected
if a != e {
t.Errorf(
"Comparison of '%s' and '%s' failed. Expected '%t', got '%t'",
tc.v1, tc.v2, e, a,
)
}
}
}
func TestInc(t *testing.T) {
tests := []struct {
v1 string
expected string
how string
expectedOriginal string
}{
{"1.2.3", "1.2.4", "patch", "1.2.4"},
{"v1.2.4", "1.2.5", "patch", "v1.2.5"},
{"1.2.3", "1.3.0", "minor", "1.3.0"},
{"v1.2.4", "1.3.0", "minor", "v1.3.0"},
{"1.2.3", "2.0.0", "major", "2.0.0"},
{"v1.2.4", "2.0.0", "major", "v2.0.0"},
{"1.2.3+meta", "1.2.4", "patch", "1.2.4"},
{"1.2.3-beta+meta", "1.2.3", "patch", "1.2.3"},
{"v1.2.4-beta+meta", "1.2.4", "patch", "v1.2.4"},
{"1.2.3-beta+meta", "1.3.0", "minor", "1.3.0"},
{"v1.2.4-beta+meta", "1.3.0", "minor", "v1.3.0"},
{"1.2.3-beta+meta", "2.0.0", "major", "2.0.0"},
{"v1.2.4-beta+meta", "2.0.0", "major", "v2.0.0"},
}
for _, tc := range tests {
v1, err := NewVersion(tc.v1)
if err != nil {
t.Errorf("Error parsing version: %s", err)
}
var v2 Version
switch tc.how {
case "patch":
v2 = v1.IncPatch()
case "minor":
v2 = v1.IncMinor()
case "major":
v2 = v1.IncMajor()
}
a := v2.String()
e := tc.expected
if a != e {
t.Errorf(
"Inc %q failed. Expected %q got %q",
tc.how, e, a,
)
}
a = v2.Original()
e = tc.expectedOriginal
if a != e {
t.Errorf(
"Inc %q failed. Expected original %q got %q",
tc.how, e, a,
)
}
}
}
func TestSetPrerelease(t *testing.T) {
tests := []struct {
v1 string
prerelease string
expectedVersion string
expectedPrerelease string
expectedOriginal string
expectedErr error
}{
{"1.2.3", "**", "1.2.3", "", "1.2.3", ErrInvalidPrerelease},
{"1.2.3", "beta", "1.2.3-beta", "beta", "1.2.3-beta", nil},
{"v1.2.4", "beta", "1.2.4-beta", "beta", "v1.2.4-beta", nil},
}
for _, tc := range tests {
v1, err := NewVersion(tc.v1)
if err != nil {
t.Errorf("Error parsing version: %s", err)
}
v2, err := v1.SetPrerelease(tc.prerelease)
if err != tc.expectedErr {
t.Errorf("Expected to get err=%s, but got err=%s", tc.expectedErr, err)
}
a := v2.Prerelease()
e := tc.expectedPrerelease
if a != e {
t.Errorf("Expected prerelease value=%q, but got %q", e, a)
}
a = v2.String()
e = tc.expectedVersion
if a != e {
t.Errorf("Expected version string=%q, but got %q", e, a)
}
a = v2.Original()
e = tc.expectedOriginal
if a != e {
t.Errorf("Expected version original=%q, but got %q", e, a)
}
}
}
func TestSetMetadata(t *testing.T) {
tests := []struct {
v1 string
metadata string
expectedVersion string
expectedMetadata string
expectedOriginal string
expectedErr error
}{
{"1.2.3", "**", "1.2.3", "", "1.2.3", ErrInvalidMetadata},
{"1.2.3", "meta", "1.2.3+meta", "meta", "1.2.3+meta", nil},
{"v1.2.4", "meta", "1.2.4+meta", "meta", "v1.2.4+meta", nil},
}
for _, tc := range tests {
v1, err := NewVersion(tc.v1)
if err != nil {
t.Errorf("Error parsing version: %s", err)
}
v2, err := v1.SetMetadata(tc.metadata)
if err != tc.expectedErr {
t.Errorf("Expected to get err=%s, but got err=%s", tc.expectedErr, err)
}
a := v2.Metadata()
e := tc.expectedMetadata
if a != e {
t.Errorf("Expected metadata value=%q, but got %q", e, a)
}
a = v2.String()
e = tc.expectedVersion
if e != a {
t.Errorf("Expected version string=%q, but got %q", e, a)
}
a = v2.Original()
e = tc.expectedOriginal
if a != e {
t.Errorf("Expected version original=%q, but got %q", e, a)
}
}
}
func TestOriginalVPrefix(t *testing.T) {
tests := []struct {
version string
vprefix string
}{
{"1.2.3", ""},
{"v1.2.4", "v"},
}
for _, tc := range tests {
v1, _ := NewVersion(tc.version)
a := v1.originalVPrefix()
e := tc.vprefix
if a != e {
t.Errorf("Expected vprefix=%q, but got %q", e, a)
}
}
}

View File

@ -1,2 +0,0 @@
vendor/
/.glide

View File

@ -1,24 +0,0 @@
language: go
go:
- 1.6
- 1.7
- 1.8
- tip
# Setting sudo access to false will let Travis CI use containers rather than
# VMs to run the tests. For more details see:
# - http://docs.travis-ci.com/user/workers/container-based-infrastructure/
# - http://docs.travis-ci.com/user/workers/standard-infrastructure/
sudo: false
script:
- make setup test
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/06e3328629952dabe3e0
on_success: change # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: never # options: [always|never|change] default: always

View File

@ -1,110 +0,0 @@
package sprig
import (
"strings"
"testing"
)
func TestSha256Sum(t *testing.T) {
tpl := `{{"abc" | sha256sum}}`
if err := runt(tpl, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); err != nil {
t.Error(err)
}
}
func TestDerivePassword(t *testing.T) {
expectations := map[string]string{
`{{derivePassword 1 "long" "password" "user" "example.com"}}`: "ZedaFaxcZaso9*",
`{{derivePassword 2 "long" "password" "user" "example.com"}}`: "Fovi2@JifpTupx",
`{{derivePassword 1 "maximum" "password" "user" "example.com"}}`: "pf4zS1LjCg&LjhsZ7T2~",
`{{derivePassword 1 "medium" "password" "user" "example.com"}}`: "ZedJuz8$",
`{{derivePassword 1 "basic" "password" "user" "example.com"}}`: "pIS54PLs",
`{{derivePassword 1 "short" "password" "user" "example.com"}}`: "Zed5",
`{{derivePassword 1 "pin" "password" "user" "example.com"}}`: "6685",
}
for tpl, result := range expectations {
out, err := runRaw(tpl, nil)
if err != nil {
t.Error(err)
}
if 0 != strings.Compare(out, result) {
t.Error("Generated password does not match for", tpl)
}
}
}
// NOTE(bacongobbler): this test is really _slow_ because of how long it takes to compute
// and generate a new crypto key.
func TestGenPrivateKey(t *testing.T) {
// test that calling by default generates an RSA private key
tpl := `{{genPrivateKey ""}}`
out, err := runRaw(tpl, nil)
if err != nil {
t.Error(err)
}
if !strings.Contains(out, "RSA PRIVATE KEY") {
t.Error("Expected RSA PRIVATE KEY")
}
// test all acceptable arguments
tpl = `{{genPrivateKey "rsa"}}`
out, err = runRaw(tpl, nil)
if err != nil {
t.Error(err)
}
if !strings.Contains(out, "RSA PRIVATE KEY") {
t.Error("Expected RSA PRIVATE KEY")
}
tpl = `{{genPrivateKey "dsa"}}`
out, err = runRaw(tpl, nil)
if err != nil {
t.Error(err)
}
if !strings.Contains(out, "DSA PRIVATE KEY") {
t.Error("Expected DSA PRIVATE KEY")
}
tpl = `{{genPrivateKey "ecdsa"}}`
out, err = runRaw(tpl, nil)
if err != nil {
t.Error(err)
}
if !strings.Contains(out, "EC PRIVATE KEY") {
t.Error("Expected EC PRIVATE KEY")
}
// test bad
tpl = `{{genPrivateKey "bad"}}`
out, err = runRaw(tpl, nil)
if err != nil {
t.Error(err)
}
if out != "Unknown type bad" {
t.Error("Expected type 'bad' to be an unknown crypto algorithm")
}
// ensure that we can base64 encode the string
tpl = `{{genPrivateKey "rsa" | b64enc}}`
out, err = runRaw(tpl, nil)
if err != nil {
t.Error(err)
}
}
func TestUUIDGeneration(t *testing.T) {
tpl := `{{uuidv4}}`
out, err := runRaw(tpl, nil)
if err != nil {
t.Error(err)
}
if len(out) != 36 {
t.Error("Expected UUID of length 36")
}
out2, err := runRaw(tpl, nil)
if err != nil {
t.Error(err)
}
if out == out2 {
t.Error("Expected subsequent UUID generations to be different")
}
}

View File

@ -1,13 +0,0 @@
package sprig
import (
"testing"
)
func TestHtmlDate(t *testing.T) {
t.Skip()
tpl := `{{ htmlDate 0}}`
if err := runt(tpl, "1970-01-01"); err != nil {
t.Error(err)
}
}

View File

@ -1,107 +0,0 @@
package sprig
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestDefault(t *testing.T) {
tpl := `{{"" | default "foo"}}`
if err := runt(tpl, "foo"); err != nil {
t.Error(err)
}
tpl = `{{default "foo" 234}}`
if err := runt(tpl, "234"); err != nil {
t.Error(err)
}
tpl = `{{default "foo" 2.34}}`
if err := runt(tpl, "2.34"); err != nil {
t.Error(err)
}
tpl = `{{ .Nothing | default "123" }}`
if err := runt(tpl, "123"); err != nil {
t.Error(err)
}
tpl = `{{ default "123" }}`
if err := runt(tpl, "123"); err != nil {
t.Error(err)
}
}
func TestEmpty(t *testing.T) {
tpl := `{{if empty 1}}1{{else}}0{{end}}`
if err := runt(tpl, "0"); err != nil {
t.Error(err)
}
tpl = `{{if empty 0}}1{{else}}0{{end}}`
if err := runt(tpl, "1"); err != nil {
t.Error(err)
}
tpl = `{{if empty ""}}1{{else}}0{{end}}`
if err := runt(tpl, "1"); err != nil {
t.Error(err)
}
tpl = `{{if empty 0.0}}1{{else}}0{{end}}`
if err := runt(tpl, "1"); err != nil {
t.Error(err)
}
tpl = `{{if empty false}}1{{else}}0{{end}}`
if err := runt(tpl, "1"); err != nil {
t.Error(err)
}
dict := map[string]interface{}{"top": map[string]interface{}{}}
tpl = `{{if empty .top.NoSuchThing}}1{{else}}0{{end}}`
if err := runtv(tpl, "1", dict); err != nil {
t.Error(err)
}
tpl = `{{if empty .bottom.NoSuchThing}}1{{else}}0{{end}}`
if err := runtv(tpl, "1", dict); err != nil {
t.Error(err)
}
}
func TestCoalesce(t *testing.T) {
tests := map[string]string{
`{{ coalesce 1 }}`: "1",
`{{ coalesce "" 0 nil 2 }}`: "2",
`{{ $two := 2 }}{{ coalesce "" 0 nil $two }}`: "2",
`{{ $two := 2 }}{{ coalesce "" $two 0 0 0 }}`: "2",
`{{ $two := 2 }}{{ coalesce "" $two 3 4 5 }}`: "2",
`{{ coalesce }}`: "<no value>",
}
for tpl, expect := range tests {
assert.NoError(t, runt(tpl, expect))
}
dict := map[string]interface{}{"top": map[string]interface{}{}}
tpl := `{{ coalesce .top.NoSuchThing .bottom .bottom.dollar "airplane"}}`
if err := runtv(tpl, "airplane", dict); err != nil {
t.Error(err)
}
}
func TestToJson(t *testing.T) {
dict := map[string]interface{}{"Top": map[string]interface{}{"bool": true, "string": "test", "number": 42}}
tpl := `{{.Top | toJson}}`
expected := `{"bool":true,"number":42,"string":"test"}`
if err := runtv(tpl, expected, dict); err != nil {
t.Error(err)
}
}
func TestToPrettyJson(t *testing.T) {
dict := map[string]interface{}{"Top": map[string]interface{}{"bool": true, "string": "test", "number": 42}}
tpl := `{{.Top | toPrettyJson}}`
expected := `{
"bool": true,
"number": 42,
"string": "test"
}`
if err := runtv(tpl, expected, dict); err != nil {
t.Error(err)
}
}

View File

@ -1,174 +0,0 @@
package sprig
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestDict(t *testing.T) {
tpl := `{{$d := dict 1 2 "three" "four" 5}}{{range $k, $v := $d}}{{$k}}{{$v}}{{end}}`
out, err := runRaw(tpl, nil)
if err != nil {
t.Error(err)
}
if len(out) != 12 {
t.Errorf("Expected length 12, got %d", len(out))
}
// dict does not guarantee ordering because it is backed by a map.
if !strings.Contains(out, "12") {
t.Error("Expected grouping 12")
}
if !strings.Contains(out, "threefour") {
t.Error("Expected grouping threefour")
}
if !strings.Contains(out, "5") {
t.Error("Expected 5")
}
tpl = `{{$t := dict "I" "shot" "the" "albatross"}}{{$t.the}} {{$t.I}}`
if err := runt(tpl, "albatross shot"); err != nil {
t.Error(err)
}
}
func TestUnset(t *testing.T) {
tpl := `{{- $d := dict "one" 1 "two" 222222 -}}
{{- $_ := unset $d "two" -}}
{{- range $k, $v := $d}}{{$k}}{{$v}}{{- end -}}
`
expect := "one1"
if err := runt(tpl, expect); err != nil {
t.Error(err)
}
}
func TestHasKey(t *testing.T) {
tpl := `{{- $d := dict "one" 1 "two" 222222 -}}
{{- if hasKey $d "one" -}}1{{- end -}}
`
expect := "1"
if err := runt(tpl, expect); err != nil {
t.Error(err)
}
}
func TestPluck(t *testing.T) {
tpl := `
{{- $d := dict "one" 1 "two" 222222 -}}
{{- $d2 := dict "one" 1 "two" 33333 -}}
{{- $d3 := dict "one" 1 -}}
{{- $d4 := dict "one" 1 "two" 4444 -}}
{{- pluck "two" $d $d2 $d3 $d4 -}}
`
expect := "[222222 33333 4444]"
if err := runt(tpl, expect); err != nil {
t.Error(err)
}
}
func TestKeys(t *testing.T) {
tests := map[string]string{
`{{ dict "foo" 1 "bar" 2 | keys | sortAlpha }}`: "[bar foo]",
`{{ dict | keys }}`: "[]",
}
for tpl, expect := range tests {
if err := runt(tpl, expect); err != nil {
t.Error(err)
}
}
}
func TestPick(t *testing.T) {
tests := map[string]string{
`{{- $d := dict "one" 1 "two" 222222 }}{{ pick $d "two" | len -}}`: "1",
`{{- $d := dict "one" 1 "two" 222222 }}{{ pick $d "two" -}}`: "map[two:222222]",
`{{- $d := dict "one" 1 "two" 222222 }}{{ pick $d "one" "two" | len -}}`: "2",
`{{- $d := dict "one" 1 "two" 222222 }}{{ pick $d "one" "two" "three" | len -}}`: "2",
`{{- $d := dict }}{{ pick $d "two" | len -}}`: "0",
}
for tpl, expect := range tests {
if err := runt(tpl, expect); err != nil {
t.Error(err)
}
}
}
func TestOmit(t *testing.T) {
tests := map[string]string{
`{{- $d := dict "one" 1 "two" 222222 }}{{ omit $d "one" | len -}}`: "1",
`{{- $d := dict "one" 1 "two" 222222 }}{{ omit $d "one" -}}`: "map[two:222222]",
`{{- $d := dict "one" 1 "two" 222222 }}{{ omit $d "one" "two" | len -}}`: "0",
`{{- $d := dict "one" 1 "two" 222222 }}{{ omit $d "two" "three" | len -}}`: "1",
`{{- $d := dict }}{{ omit $d "two" | len -}}`: "0",
}
for tpl, expect := range tests {
if err := runt(tpl, expect); err != nil {
t.Error(err)
}
}
}
func TestSet(t *testing.T) {
tpl := `{{- $d := dict "one" 1 "two" 222222 -}}
{{- $_ := set $d "two" 2 -}}
{{- $_ := set $d "three" 3 -}}
{{- if hasKey $d "one" -}}{{$d.one}}{{- end -}}
{{- if hasKey $d "two" -}}{{$d.two}}{{- end -}}
{{- if hasKey $d "three" -}}{{$d.three}}{{- end -}}
`
expect := "123"
if err := runt(tpl, expect); err != nil {
t.Error(err)
}
}
func TestCompact(t *testing.T) {
tests := map[string]string{
`{{ list 1 0 "" "hello" | compact }}`: `[1 hello]`,
`{{ list "" "" | compact }}`: `[]`,
`{{ list | compact }}`: `[]`,
}
for tpl, expect := range tests {
assert.NoError(t, runt(tpl, expect))
}
}
func TestMerge(t *testing.T) {
dict := map[string]interface{}{
"src": map[string]interface{}{
"a": 1,
"b": 2,
"d": map[string]interface{}{
"e": "four",
},
"g": []int{6, 7},
},
"dst": map[string]interface{}{
"a": "one",
"c": 3,
"d": map[string]interface{}{
"f": 5,
},
"g": []int{8, 9},
},
}
tpl := `{{merge .dst .src}}`
_, err := runRaw(tpl, dict)
if err != nil {
t.Error(err)
}
expected := map[string]interface{}{
"a": "one", // key overridden
"b": 2, // merged from src
"c": 3, // merged from dst
"d": map[string]interface{}{ // deep merge
"e": "four",
"f": 5,
},
"g": []int{8, 9}, // overridden - arrays are not merged
}
assert.Equal(t, expected, dict["dst"])
}

View File

@ -1 +0,0 @@
theme: jekyll-theme-slate

View File

@ -1,25 +0,0 @@
# Type Conversion Functions
The following type conversion functions are provided by Sprig:
- `atoi`: Convert a string to an integer
- `float64`: Convert to a float64
- `int`: Convert to an `int` at the system's width.
- `int64`: Convert to an `int64`
- `toString`: Convert to a string
- `toStrings`: Convert a list, slice, or array to a list of strings.
Only `atoi` requires that the input be a specific type. The others will attempt
to convert from any type to the destination type. For example, `int64` can convert
floats to ints, and it can also convert strings to ints.
## toStrings
Given a list-like collection, produce a slice of strings.
```
list 1 2 3 | toStrings
```
The above converts `1` to `"1"`, `2` to `"2"`, and so on, and then returns
them as a list.

View File

@ -1,37 +0,0 @@
# Cryptographic and Security Functions
Sprig provides a couple of advanced cryptographic functions.
## sha256sum
The `sha256sum` function receives a string, and computes it's SHA256 digest.
```
sha256sum "Hello world!"
```
The above will compute the SHA 256 sum in an "ASCII armored" format that is
safe to print.
## derivePassword
The `derivePassword` function can be used to derive a specific password based on
some shared "master password" constraints. The algorithm for this is
[well specified](http://masterpasswordapp.com/algorithm.html).
```
derivePassword 1 "long" "password" "user" "example.com"
```
Note that it is considered insecure to store the parts directly in the template.
## generatePrivateKey
The `generatePrivateKey` function generates a new private key encoded into a PEM
block.
It takes one of the values for its first param:
- `ecdsa`: Generate an elyptical curve DSA key (P256)
- `dsa`: Generate a DSA key (L2048N256)
- `rsa`: Generate an RSA 4096 key

View File

@ -1,62 +0,0 @@
# Date Functions
## now
The current date/time. Use this in conjunction with other date functions.
## date
The `date` function formats a date.
Format the date to YEAR-MONTH-DAY:
```
now | date "2006-01-02"
```
Date formatting in Go is a [little bit different](https://pauladamsmith.com/blog/2011/05/go_time.html).
In short, take this as the base date:
```
Mon Jan 2 15:04:05 MST 2006
```
Write it in the format you want. Above, `2006-01-02` is the same date, but
in the format we want.
## dateInZone
Same as `date`, but with a timezone.
```
date "2006-01-02" (now) "UTC"
```
## dateModify
The `dateModify` takes a modification and a date and returns the timestamp.
Subtract an hour and thirty minutes from the current time:
```
now | date_modify "-1.5h"
```
## htmlDate
The `htmlDate` function formates a date for inserting into an HTML date picker
input field.
```
now | htmlDate
```
## htmlDateInZone
Same as htmlDate, but with a timezone.
```
htmlDate (now) "UTC"
```

View File

@ -1,59 +0,0 @@
# Default Functions
Sprig provides tools for setting default values for templates.
## default
To set a simple default value, use `default`:
```
default "foo" .Bar
```
In the above, if `.Bar` evaluates to a non-empty value, it will be used. But if
it is empty, `foo` will be returned instead.
The definition of "empty" depends on type:
- Numeric: 0
- String: ""
- Lists: `[]`
- Dicts: `{}`
- Boolean: `false`
- And always `nil` (aka null)
For structs, there is no definition of empty, so a struct will never return the
default.
## empty
The `empty` function returns `true` if the given value is considered empty, and
`false` otherwise. The empty values are listed in the `default` section.
```
empty .Foo
```
Note that in Go template conditionals, emptiness is calculated for you. Thus,
you rarely need `if empty .Foo`. Instead, just use `if .Foo`.
## coalesce
The `coalesce` function takes a list of values and returns the first non-empty
one.
```
coalesce 0 1 2
```
The above returns `1`.
This function is useful for scanning through multiple variables or values:
```
coalesce .name .parent.name "Matt"
```
The above will first check to see if `.name` is empty. If it is not, it will return
that value. If it _is_ empty, `coalesce` will evaluate `.parent.name` for emptiness.
Finally, if both `.name` and `.parent.name` are empty, it will return `Matt`.

View File

@ -1,124 +0,0 @@
# Dictionaries and Dict Functions
Sprig provides a key/value storage type called a `dict` (short for "dictionary",
as in Python). A `dict` is an _unorder_ type.
The key to a dictionary **must be a string**. However, the value can be any
type, even another `dict` or `list`.
Unlike `list`s, `dict`s are not immutable. The `set` and `unset` functions will
modify the contents of a dictionary.
## dict
Creating dictionaries is done by calling the `dict` function and passing it a
list of pairs.
The following creates a dictionary with three items:
```
$myDict := dict "name1" "value1" "name2" "value2" "name3" "value 3"
```
## set
Use `set` to add a new key/value pair to a dictionary.
```
$_ := set $myDict "name4" "value4"
```
Note that `set` _returns the dictionary_ (a requirement of Go template functions),
so you may need to trap the value as done above with the `$_` assignment.
## unset
Given a map and a key, delete the key from the map.
```
$_ := unset $myDict "name4"
```
As with `set`, this returns the dictionary.
Note that if the key is not found, this operation will simply return. No error
will be generated.
## hasKey
The `hasKey` function returns `true` if the given dict contains the given key.
```
hasKey $myDict "name1"
```
If the key is not found, this returns `false`.
## pluck
The `pluck` function makes it possible to give one key and multiple maps, and
get a list of all of the matches:
```
pluck "name1" $myDict $myOtherDict
```
The above will return a `list` containing every found value (`[value1 otherValue1]`).
If the give key is _not found_ in a map, that map will not have an item in the
list (and the length of the returned list will be less than the number of dicts
in the call to `pluck`.
If the key is _found_ but the value is an empty value, that value will be
inserted.
A common idiom in Sprig templates is to uses `pluck... | first` to get the first
matching key out of a collection of dictionaries.
## merge
Merge two dictionaries into one, giving precedence to the dest dictionary:
```
$newdict := merge $dest $source
```
This is a deep merge operation.
## keys
The `keys` function will return a `list` of all of the keys in a `dict`. Since
a dictionary is _unordered_, the keys will not be in a predictable order. They
can be sorted with `sortAlpha`.
```
keys $myDict | sortAlpha
```
## pick
The `pick` function selects just the given keys out of a dictionary, creating a
new `dict`.
```
$new := pick $myDict "name1" "name3"
```
The above returns `{name1: value1, name2: value2}`
## omit
The `omit` function is similar to `pick`, except it returns a new `dict` with all
the keys that _do not_ match the given keys.
```
$new := omit $myDict "name1" "name3"
```
The above returns `{name2: value2}`
## A Note on Dict Internals
A `dict` is implemented in Go as a `map[string]interface{}`. Go developers can
pass `map[string]interface{}` values into the context to make them available
to templates as `dict`s.

View File

@ -1,6 +0,0 @@
# Encoding Functions
Sprig has the following encoding and decoding functions:
- `b64enc`/`b64dec`: Encode or decode with Base64
- `b32enc`/`b32dec`: Encode or decode with Base32

View File

@ -1,11 +0,0 @@
# Flow Control Functions
## fail
Unconditionally returns an empty `string` and an `error` with the specified
text. This is useful in scenarios where other conditionals have determined that
template rendering should fail.
```
fail "Please accept the end user license agreement"
```

View File

@ -1,23 +0,0 @@
# Sprig Function Documentation
The Sprig library provides over 70 template functions for Go's template language.
- [String Functions](strings.md): `trim`, `wrap`, `randAlpha`, `plural`, etc.
- [String List Functions](string_slice.md): `splitList`, `sortAlpha`, etc.
- [Math Functions](math.md): `add`, `max`, `mul`, etc.
- [Integer Slice Functions](integer_slice.md): `until`, `untilStep`
- [Date Functions](date.md): `now`, `date`, etc.
- [Defaults Functions](defaults.md): `default`, `empty`, `coalesce`
- [Encoding Functions](encoding.md): `b64enc`, `b64dec`, etc.
- [Lists and List Functions](lists.md): `list`, `first`, `uniq`, etc.
- [Dictionaries and Dict Functions](dicts.md): `dict`, `hasKey`, `pluck`, etc.
- [Type Conversion Functions](conversion.md): `atoi`, `int64`, `toString`, etc.
- [File Path Functions](paths.md): `base`, `dir`, `ext`, `clean`, `isAbs`
- [Flow Control Functions](flow_control.md): `fail`
- Advanced Functions
- [UUID Functions](uuid.md): `uuidv4`
- [OS Functions](os.md): `env`, `expandenv`
- [Version Comparison Functions](semver.md): `semver`, `semverCompare`
- [Reflection](reflection.md): `typeOf`, `kindIs`, `typeIsLike`, etc.
- [Cryptographic and Security Functions](crypto.md): `derivePassword`, `sha256sum`, `genPrivateKey`

View File

@ -1,25 +0,0 @@
# Integer Slice Functions
## until
The `until` function builds a range of integers.
```
until 5
```
The above generates the list `[0, 1, 2, 3, 4]`.
This is useful for looping with `range $i, $e := until 5`.
## untilStep
Like `until`, `untilStep` generates a list of counting integers. But it allows
you to define a start, stop, and step:
```
untilStep 3 6 2
```
The above will produce `[3 5]` by starting with 3, and adding 2 until it is equal
or greater than 6. This is similar to Python's `range` function.

View File

@ -1,111 +0,0 @@
# Lists and List Functions
Sprig provides a simple `list` type that can contain arbitrary sequential lists
of data. This is similar to arrays or slices, but lists are designed to be used
as immutable data types.
Create a list of integers:
```
$myList := list 1 2 3 4 5
```
The above creates a list of `[1 2 3 4 5]`.
## first
To get the head item on a list, use `first`.
`first $myList` returns `1`
## rest
To get the tail of the list (everything but the first item), use `rest`.
`rest $myList` returns `[2 3 4 5]`
## last
To get the last item on a list, use `last`:
`last $myList` returns `5`. This is roughly analogous to reversing a list and
then calling `first`.
## initial
This compliments `last` by returning all _but_ the last element.
`initial $myList` returns `[1 2 3 4]`.
## append
Append a new item to an existing list, creating a new list.
```
$new = append $myList 6
```
The above would set `$new` to `[1 2 3 4 5 6]`. `$myList` would remain unaltered.
## prepend
Push an alement onto the front of a list, creating a new list.
```
prepend $myList 0
```
The above would produce `[0 1 2 3 4 5]`. `$myList` would remain unaltered.
## reverse
Produce a new list with the reversed elements of the given list.
```
reverse $myList
```
The above would generate the list `[5 4 3 2 1]`.
## uniq
Generate a list with all of the duplicates removed.
```
list 1 1 1 2 | uniq
```
The above would produce `[1 2]`
## without
The `without` function filters items out of a list.
```
without $myList 3
```
The above would produce `[1 2 4 5]`
Without can take more than one filter:
```
without $myList 1 3 5
```
That would produce `[2 4]`
## has
Test to see if a list has a particular element.
```
has $myList 4
```
The above would return `true`, while `has $myList "hello"` would return false.
## A Note on List Internals
A list is implemented in Go as a `[]interface{}`. For Go developers embedding
Sprig, you may pass `[]interface{}` items into your template context and be
able to use all of the `list` functions on those items.

View File

@ -1,46 +0,0 @@
# Math Functions
All math functions operate on `int64` values unless specified otherwise.
(In the future, these will be extended to handle floats as well)
## add
Sum numbers with `add`
## add1
To increment by 1, use `add1`
## sub
To subtract, use `sub`
## div
Perform integer division with `div`
## mod
Modulo with `mod`
## mul
Multiply with `mul`
## max
Return the largest of a series of integers:
This will return `3`:
```
max 1 2 3
```
## min
Return the smallest of a series of integers.
`min 1 2 3` will return `1`.

View File

@ -1,24 +0,0 @@
# OS Functions
_WARNING:_ These functions can lead to information leakage if not used
appropriately.
_WARNING:_ Some notable implementations of Sprig (such as
[Kubernetes Helm](http://helm.sh) _do not provide these functions for security
reasons_.
## env
The `env` function reads an environment variable:
```
env "HOME"
```
## expandenv
To substitute environment variables in a string, use `expandenv`:
```
expandenv "Your path is set to $PATH"
```

View File

@ -1,43 +0,0 @@
# File Path Functions
While Sprig does not grant access to the filesystem, it does provide functions
for working with strings that follow file path conventions.
# base
Return the last element of a path.
```
base "foo/bar/baz"
```
The above prints "baz"
# dir
Return the directory, stripping the last part of the path. So `dir "foo/bar/baz"`
returns `foo/bar`
# clean
Clean up a path.
```
clean "foo/bar/../baz"
```
The above resolves the `..` and returns `foo/baz`
# ext
Return the file extension.
```
ext "foo.bar"
```
The above returns `.bar`.
# isAbs
To check whether a file path is absolute, use `isAbs`.

View File

@ -1,38 +0,0 @@
# Reflection Functions
Sprig provides rudimentary reflection tools. These help advanced template
developers understand the underlying Go type information for a particular value.
Go has several primitive _kinds_, like `string`, `slice`, `int64`, and `bool`.
Go has an open _type_ system that allows developers to create their own types.
Sprig provides a set of functions for each.
## Kind Functions
There are two Kind functions: `kindOf` returns the kind of an object.
```
kindOf "hello"
```
The above would return `string`. For simple tests (like in `if` blocks), the
`isKind` function will let you verify that a value is a particular kind:
```
kindIs "int" 123
```
The above will return `true`
## Type Functions
Types are slightly harder to work with, so there are three different functions:
- `typeOf` returns the underlying type of a value: `typeOf $foo`
- `typeIs` is like `kindIs`, but for types: `typeIs "*io.Buffer" $myVal`
- `typeIsLike` works as `kindIs`, except that it also dereferences pointers.
**Note:** None of these can test whether or not something implements a given
interface, since doing so would require compiling the interface in ahead of time.

View File

@ -1,124 +0,0 @@
# Semantic Version Functions
Some version schemes are easily parseable and comparable. Sprig provides functions
for working with [SemVer 2](http://semver.org) versions.
## semver
The `semver` function parses a string into a Semantic Version:
```
$version := semver "1.2.3-alpha.1+123"
```
_If the parser fails, it will cause template execution to halt with an error._
At this point, `$version` is a pointer to a `Version` object with the following
properties:
- `$version.Major`: The major number (`1` above)
- `$version.Minor`: The minor number (`2` above)
- `$version.Patch`: The patch number (`3` above)
- `$version.Prerelease`: The prerelease (`alpha.1` above)
- `$version.Metadata`: The build metadata (`123` above)
- `$version.Original`: The original version as a string
Additionally, you can compare a `Version` to another `version` using the `Compare`
function:
```
semver "1.4.3" | (semver "1.2.3").Compare
```
The above will return `-1`.
The return values are:
- `-1` if the given semver is greater than the semver whose `Compare` method was called
- `1` if the version who's `Compare` function was called is greater.
- `0` if they are the same version
(Note that in SemVer, the `Metadata` field is not compared during version
comparison operations.)
## semverCompare
A more robust comparison function is provided as `semverCompare`. This version
supports version ranges:
- `semverCompare "1.2.3" "1.2.3"` checks for an exact match
- `semverCompare "^1.2.0" "1.2.3"` checks that the major and minor versions match, and that the patch
number of the second version is _greater than or equal to_ the first parameter.
The SemVer functions use the [Masterminds semver library](https://github.com/Masterminds/semver),
from the creators of Sprig.
## Basic Comparisons
There are two elements to the comparisons. First, a comparison string is a list
of comma separated and comparisons. These are then separated by || separated or
comparisons. For example, `">= 1.2, < 3.0.0 || >= 4.2.3"` is looking for a
comparison that's greater than or equal to 1.2 and less than 3.0.0 or is
greater than or equal to 4.2.3.
The basic comparisons are:
* `=`: equal (aliased to no operator)
* `!=`: not equal
* `>`: greater than
* `<`: less than
* `>=`: greater than or equal to
* `<=`: less than or equal to
_Note, according to the Semantic Version specification pre-releases may not be
API compliant with their release counterpart. It says,_
> _A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version._
_SemVer comparisons without a pre-release value will skip pre-release versions.
For example, `>1.2.3` will skip pre-releases when looking at a list of values
while `>1.2.3-alpha.1` will evaluate pre-releases._
## Hyphen Range Comparisons
There are multiple methods to handle ranges and the first is hyphens ranges.
These look like:
* `1.2 - 1.4.5` which is equivalent to `>= 1.2, <= 1.4.5`
* `2.3.4 - 4.5` which is equivalent to `>= 2.3.4, <= 4.5`
## Wildcards In Comparisons
The `x`, `X`, and `*` characters can be used as a wildcard character. This works
for all comparison operators. When used on the `=` operator it falls
back to the pack level comparison (see tilde below). For example,
* `1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
* `>= 1.2.x` is equivalent to `>= 1.2.0`
* `<= 2.x` is equivalent to `<= 3`
* `*` is equivalent to `>= 0.0.0`
## Tilde Range Comparisons (Patch)
The tilde (`~`) comparison operator is for patch level ranges when a minor
version is specified and major level changes when the minor number is missing.
For example,
* `~1.2.3` is equivalent to `>= 1.2.3, < 1.3.0`
* `~1` is equivalent to `>= 1, < 2`
* `~2.3` is equivalent to `>= 2.3, < 2.4`
* `~1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
* `~1.x` is equivalent to `>= 1, < 2`
## Caret Range Comparisons (Major)
The caret (`^`) comparison operator is for major level changes. This is useful
when comparisons of API versions as a major change is API breaking. For example,
* `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0`
* `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0`
* `^2.3` is equivalent to `>= 2.3, < 3`
* `^2.x` is equivalent to `>= 2.0.0, < 3`

View File

@ -1,55 +0,0 @@
# String Slice Functions
These function operate on or generate slices of strings. In Go, a slice is a
growable array. In Sprig, it's a special case of a `list`.
## join
Join a list of strings into a single string, with the given separator.
```
list "hello" "world" | join "_"
```
The above will produce `hello_world`
`join` will try to convert non-strings to a string value:
```
list 1 2 3 | join "+"
```
The above will produce `1+2+3`
## splitList and split
Split a string into a list of strings:
```
splitList "$" "foo$bar$baz"
```
The above will return `[foo bar baz]`
The older `split` function splits a string into a `dict`. It is designed to make
it easy to use template dot notation for accessing members:
```
$a := split "$" "foo$bar$baz"
```
The above produces a map with index keys. `{_0: foo, _1: bar, _2: baz}`
```
$a._0
```
The above produces `foo`
## sortAlpha
The `sortAlpha` function sorts a list of strings into alphabetical (lexicographical)
order.
It does _not_ sort in place, but returns a sorted copy of the list, in keeping
with the immutability of lists.

View File

@ -1,322 +0,0 @@
# String Functions
Sprig has a number of string manipulation functions.
## trim
The `trim` function removes space from either side of a string:
```
trim " hello "
```
The above produces `hello`
## trimAll
Remove given characters from the front or back of a string:
```
trimAll "$" "$5.00"
```
The above returns `5.00` (as a string).
## trimSuffix
Trim just the suffix from a string:
```
trimSuffix "-" "hello-"
```
The above returns `hello`
## upper
Convert the entire string to uppercase:
```
upper "hello"
```
The above returns `HELLO`
## lower
Convert the entire string to lowercase:
```
lower "HELLO"
```
The above returns `hello`
## title
Convert to title case:
```
title "hello world"
```
The above returns `Hello World`
## untitle
Remove title casing. `untitle "Hello World"` produces `hello world`.
## repeat
Repeat a string multiple times:
```
repeat 3 "hello"
```
The above returns `hellohellohello`
## substr
Get a substring from a string. It takes three parameters:
- start (int)
- length (int)
- string (string)
```
substr 0 5 "hello world"
```
The above returns `hello`
## nospace
Remove all whitespace from a string.
```
nospace "hello w o r l d"
```
The above returns `helloworld`
## trunc
Truncate a string (and add no suffix)
```
trunc 5 "hello world"
```
The above produces `hello`.
## abbrev
Truncate a string with ellipses (`...`)
Parameters:
- max length
- the string
```
abbrev 5 "hello world"
```
The above returns `he...`, since it counts the width of the ellipses against the
maximum length.
## abbrevboth
Abbreviate both sides:
```
abbrevboth 5 10 "1234 5678 9123"
```
the above produces `...5678...`
It takes:
- left offset
- max length
- the string
## initials
Given multiple words, take the first letter of each word and combine.
```
initials "First Try"
```
The above returns `FT`
## randAlphaNum, randAlpha, randNumeric, and randAscii
These four functions generate random strings, but with different base character
sets:
- `randAlphaNum` uses `0-9a-zA-Z`
- `randAlpha` uses `a-zA-Z`
- `randNumeric` uses `0-9`
- `randAscii` uses all printable ASCII characters
Each of them takes one parameter: the integer length of the string.
```
randNumeric 3
```
The above will produce a random string with three digits.
## wrap
Wrap text at a given column count:
```
wrap 80 $someText
```
The above will wrap the string in `$someText` at 80 columns.
## wrapWith
`wrapWith` works as `wrap`, but lets you specify the string to wrap with.
(`wrap` uses `\n`)
```
wrapWith 5 "\t" "Hello World"
```
The above produces `hello world` (where the whitespace is an ASCII tab
character)
## contains
Test to see if one string is contained inside of another:
```
contains "cat" "catch"
```
The above returns `true` because `catch` contains `cat`.
## hasPrefix and hasSuffix
The `hasPrefix` and `hasSuffix` functions test whether a string has a given
prefix or suffix:
```
hasPrefix "cat" "catch"
```
The above returns `true` because `catch` has the prefix `cat`.
## quote and squote
These functions wrap a string in double quotes (`quote`) or single quotes
(`squote`).
## cat
The `cat` function concatenates multiple strings together into one, separating
them with spaces:
```
cat "hello" "beautiful" "world"
```
The above produces `hello beautiful world`
## indent
The `indent` function indents every line in a given string to the specified
indent width. This is useful when aligning multi-line strings:
```
indent 4 $lots_of_text
```
The above will indent every line of text by 4 space characters.
## replace
Perform simple string replacement.
It takes three arguments:
- string to replace
- string to replace with
- source string
```
"I Am Henry VIII" | replace " " "-"
```
The above will produce `I-Am-Henry-VIII`
## plural
Pluralize a string.
```
len $fish | plural "one anchovy" "many anchovies"
```
In the above, if the length of the string is 1, the first argument will be
printed (`one anchovy`). Otherwise, the second argument will be printed
(`many anchovies`).
The arguments are:
- singular string
- plural string
- length integer
NOTE: Sprig does not currently support languages with more complex pluralization
rules. And `0` is considered a plural because the English language treats it
as such (`zero anchovies`). The Sprig developers are working on a solution for
better internationalization.
## snakecase
Convert string from camelCase to snake_case.
Introduced in 2.12.0.
```
snakecase "FirstName"
```
This above will produce `first_name`.
## camelcase
Convert string from snake_case to CamelCase
Introduced in 2.12.0.
```
camelcase "http_server"
```
This above will produce `HttpServer`.
## shuffle
Shuffle a string.
Introduced in 2.12.0.
```
shuffle "hello"
```
The above will randomize the letters in `hello`, perhaps producing `oelhl`.
## See Also...
The [Conversion Functions](conversion.html) contain functions for converting
strings. The [String Slice Functions](string_slice.html) contains functions
for working with an array of strings.

View File

@ -1,9 +0,0 @@
# UUID Functions
Sprig can generate UUID v4 universally unique IDs.
```
uuidv4
```
The above returns a new UUID of the v4 (randomly generated) type.

View File

@ -1,25 +0,0 @@
package sprig
import (
"fmt"
"os"
"text/template"
)
func Example() {
// Set up variables and template.
vars := map[string]interface{}{"Name": " John Jacob Jingleheimer Schmidt "}
tpl := `Hello {{.Name | trim | lower}}`
// Get the Sprig function map.
fmap := TxtFuncMap()
t := template.Must(template.New("test").Funcs(fmap).Parse(tpl))
err := t.Execute(os.Stdout, vars)
if err != nil {
fmt.Printf("Error during template execution: %s", err)
return
}
// Output:
// Hello john jacob jingleheimer schmidt
}

View File

@ -1,16 +0,0 @@
package sprig
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestFail(t *testing.T) {
const msg = "This is an error!"
tpl := fmt.Sprintf(`{{fail "%s"}}`, msg)
_, err := runRaw(tpl, nil)
assert.Error(t, err)
assert.Contains(t, err.Error(), msg)
}

View File

@ -1,108 +0,0 @@
package sprig
import (
"bytes"
"fmt"
"math/rand"
"os"
"testing"
"text/template"
"github.com/aokoli/goutils"
"github.com/stretchr/testify/assert"
)
func TestEnv(t *testing.T) {
os.Setenv("FOO", "bar")
tpl := `{{env "FOO"}}`
if err := runt(tpl, "bar"); err != nil {
t.Error(err)
}
}
func TestExpandEnv(t *testing.T) {
os.Setenv("FOO", "bar")
tpl := `{{expandenv "Hello $FOO"}}`
if err := runt(tpl, "Hello bar"); err != nil {
t.Error(err)
}
}
func TestBase(t *testing.T) {
assert.NoError(t, runt(`{{ base "foo/bar" }}`, "bar"))
}
func TestDir(t *testing.T) {
assert.NoError(t, runt(`{{ dir "foo/bar/baz" }}`, "foo/bar"))
}
func TestIsAbs(t *testing.T) {
assert.NoError(t, runt(`{{ isAbs "/foo" }}`, "true"))
assert.NoError(t, runt(`{{ isAbs "foo" }}`, "false"))
}
func TestClean(t *testing.T) {
assert.NoError(t, runt(`{{ clean "/foo/../foo/../bar" }}`, "/bar"))
}
func TestExt(t *testing.T) {
assert.NoError(t, runt(`{{ ext "/foo/bar/baz.txt" }}`, ".txt"))
}
func TestSnakeCase(t *testing.T) {
assert.NoError(t, runt(`{{ snakecase "FirstName" }}`, "first_name"))
assert.NoError(t, runt(`{{ snakecase "HTTPServer" }}`, "http_server"))
assert.NoError(t, runt(`{{ snakecase "NoHTTPS" }}`, "no_https"))
assert.NoError(t, runt(`{{ snakecase "GO_PATH" }}`, "go_path"))
assert.NoError(t, runt(`{{ snakecase "GO PATH" }}`, "go_path"))
assert.NoError(t, runt(`{{ snakecase "GO-PATH" }}`, "go_path"))
}
func TestCamelCase(t *testing.T) {
assert.NoError(t, runt(`{{ camelcase "http_server" }}`, "HttpServer"))
assert.NoError(t, runt(`{{ camelcase "_camel_case" }}`, "_CamelCase"))
assert.NoError(t, runt(`{{ camelcase "no_https" }}`, "NoHttps"))
assert.NoError(t, runt(`{{ camelcase "_complex__case_" }}`, "_Complex_Case_"))
assert.NoError(t, runt(`{{ camelcase "all" }}`, "All"))
}
func TestShuffle(t *testing.T) {
goutils.RANDOM = rand.New(rand.NewSource(1))
// Because we're using a random number generator, we need these to go in
// a predictable sequence:
assert.NoError(t, runt(`{{ shuffle "Hello World" }}`, "rldo HWlloe"))
}
// runt runs a template and checks that the output exactly matches the expected string.
func runt(tpl, expect string) error {
return runtv(tpl, expect, map[string]string{})
}
// runtv takes a template, and expected return, and values for substitution.
//
// It runs the template and verifies that the output is an exact match.
func runtv(tpl, expect string, vars interface{}) error {
fmap := TxtFuncMap()
t := template.Must(template.New("test").Funcs(fmap).Parse(tpl))
var b bytes.Buffer
err := t.Execute(&b, vars)
if err != nil {
return err
}
if expect != b.String() {
return fmt.Errorf("Expected '%s', got '%s'", expect, b.String())
}
return nil
}
// runRaw runs a template with the given variables and returns the result.
func runRaw(tpl string, vars interface{}) (string, error) {
fmap := TxtFuncMap()
t := template.Must(template.New("test").Funcs(fmap).Parse(tpl))
var b bytes.Buffer
err := t.Execute(&b, vars)
if err != nil {
return "", err
}
return b.String(), nil
}

View File

@ -1,135 +0,0 @@
package sprig
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestTuple(t *testing.T) {
tpl := `{{$t := tuple 1 "a" "foo"}}{{index $t 2}}{{index $t 0 }}{{index $t 1}}`
if err := runt(tpl, "foo1a"); err != nil {
t.Error(err)
}
}
func TestList(t *testing.T) {
tpl := `{{$t := list 1 "a" "foo"}}{{index $t 2}}{{index $t 0 }}{{index $t 1}}`
if err := runt(tpl, "foo1a"); err != nil {
t.Error(err)
}
}
func TestPush(t *testing.T) {
// Named `append` in the function map
tests := map[string]string{
`{{ $t := tuple 1 2 3 }}{{ append $t 4 | len }}`: "4",
`{{ $t := tuple 1 2 3 4 }}{{ append $t 5 | join "-" }}`: "1-2-3-4-5",
}
for tpl, expect := range tests {
assert.NoError(t, runt(tpl, expect))
}
}
func TestPrepend(t *testing.T) {
tests := map[string]string{
`{{ $t := tuple 1 2 3 }}{{ prepend $t 0 | len }}`: "4",
`{{ $t := tuple 1 2 3 4 }}{{ prepend $t 0 | join "-" }}`: "0-1-2-3-4",
}
for tpl, expect := range tests {
assert.NoError(t, runt(tpl, expect))
}
}
func TestFirst(t *testing.T) {
tests := map[string]string{
`{{ list 1 2 3 | first }}`: "1",
`{{ list | first }}`: "<no value>",
}
for tpl, expect := range tests {
assert.NoError(t, runt(tpl, expect))
}
}
func TestLast(t *testing.T) {
tests := map[string]string{
`{{ list 1 2 3 | last }}`: "3",
`{{ list | last }}`: "<no value>",
}
for tpl, expect := range tests {
assert.NoError(t, runt(tpl, expect))
}
}
func TestInitial(t *testing.T) {
tests := map[string]string{
`{{ list 1 2 3 | initial | len }}`: "2",
`{{ list 1 2 3 | initial | last }}`: "2",
`{{ list 1 2 3 | initial | first }}`: "1",
`{{ list | initial }}`: "[]",
}
for tpl, expect := range tests {
assert.NoError(t, runt(tpl, expect))
}
}
func TestRest(t *testing.T) {
tests := map[string]string{
`{{ list 1 2 3 | rest | len }}`: "2",
`{{ list 1 2 3 | rest | last }}`: "3",
`{{ list 1 2 3 | rest | first }}`: "2",
`{{ list | rest }}`: "[]",
}
for tpl, expect := range tests {
assert.NoError(t, runt(tpl, expect))
}
}
func TestReverse(t *testing.T) {
tests := map[string]string{
`{{ list 1 2 3 | reverse | first }}`: "3",
`{{ list 1 2 3 | reverse | rest | first }}`: "2",
`{{ list 1 2 3 | reverse | last }}`: "1",
`{{ list 1 2 3 4 | reverse }}`: "[4 3 2 1]",
`{{ list 1 | reverse }}`: "[1]",
`{{ list | reverse }}`: "[]",
}
for tpl, expect := range tests {
assert.NoError(t, runt(tpl, expect))
}
}
func TestUniq(t *testing.T) {
tests := map[string]string{
`{{ list 1 2 3 4 | uniq }}`: `[1 2 3 4]`,
`{{ list "a" "b" "c" "d" | uniq }}`: `[a b c d]`,
`{{ list 1 1 1 1 2 2 2 2 | uniq }}`: `[1 2]`,
`{{ list "foo" 1 1 1 1 "foo" "foo" | uniq }}`: `[foo 1]`,
`{{ list | uniq }}`: `[]`,
}
for tpl, expect := range tests {
assert.NoError(t, runt(tpl, expect))
}
}
func TestWithout(t *testing.T) {
tests := map[string]string{
`{{ without (list 1 2 3 4) 1 }}`: `[2 3 4]`,
`{{ without (list "a" "b" "c" "d") "a" }}`: `[b c d]`,
`{{ without (list 1 1 1 1 2) 1 }}`: `[2]`,
`{{ without (list) 1 }}`: `[]`,
`{{ without (list 1 2 3) }}`: `[1 2 3]`,
`{{ without list }}`: `[]`,
}
for tpl, expect := range tests {
assert.NoError(t, runt(tpl, expect))
}
}
func TestHas(t *testing.T) {
tests := map[string]string{
`{{ list 1 2 3 | has 1 }}`: `true`,
`{{ list 1 2 3 | has 4 }}`: `false`,
}
for tpl, expect := range tests {
assert.NoError(t, runt(tpl, expect))
}
}

View File

@ -1,180 +0,0 @@
package sprig
import (
"testing"
)
func TestUntil(t *testing.T) {
tests := map[string]string{
`{{range $i, $e := until 5}}{{$i}}{{$e}}{{end}}`: "0011223344",
`{{range $i, $e := until -5}}{{$i}}{{$e}} {{end}}`: "00 1-1 2-2 3-3 4-4 ",
}
for tpl, expect := range tests {
if err := runt(tpl, expect); err != nil {
t.Error(err)
}
}
}
func TestUntilStep(t *testing.T) {
tests := map[string]string{
`{{range $i, $e := untilStep 0 5 1}}{{$i}}{{$e}}{{end}}`: "0011223344",
`{{range $i, $e := untilStep 3 6 1}}{{$i}}{{$e}}{{end}}`: "031425",
`{{range $i, $e := untilStep 0 -10 -2}}{{$i}}{{$e}} {{end}}`: "00 1-2 2-4 3-6 4-8 ",
`{{range $i, $e := untilStep 3 0 1}}{{$i}}{{$e}}{{end}}`: "",
`{{range $i, $e := untilStep 3 99 0}}{{$i}}{{$e}}{{end}}`: "",
`{{range $i, $e := untilStep 3 99 -1}}{{$i}}{{$e}}{{end}}`: "",
`{{range $i, $e := untilStep 3 0 0}}{{$i}}{{$e}}{{end}}`: "",
}
for tpl, expect := range tests {
if err := runt(tpl, expect); err != nil {
t.Error(err)
}
}
}
func TestBiggest(t *testing.T) {
tpl := `{{ biggest 1 2 3 345 5 6 7}}`
if err := runt(tpl, `345`); err != nil {
t.Error(err)
}
tpl = `{{ max 345}}`
if err := runt(tpl, `345`); err != nil {
t.Error(err)
}
}
func TestMin(t *testing.T) {
tpl := `{{ min 1 2 3 345 5 6 7}}`
if err := runt(tpl, `1`); err != nil {
t.Error(err)
}
tpl = `{{ min 345}}`
if err := runt(tpl, `345`); err != nil {
t.Error(err)
}
}
func TestToFloat64(t *testing.T) {
target := float64(102)
if target != toFloat64(int8(102)) {
t.Errorf("Expected 102")
}
if target != toFloat64(int(102)) {
t.Errorf("Expected 102")
}
if target != toFloat64(int32(102)) {
t.Errorf("Expected 102")
}
if target != toFloat64(int16(102)) {
t.Errorf("Expected 102")
}
if target != toFloat64(int64(102)) {
t.Errorf("Expected 102")
}
if target != toFloat64("102") {
t.Errorf("Expected 102")
}
if 0 != toFloat64("frankie") {
t.Errorf("Expected 0")
}
if target != toFloat64(uint16(102)) {
t.Errorf("Expected 102")
}
if target != toFloat64(uint64(102)) {
t.Errorf("Expected 102")
}
if 102.1234 != toFloat64(float64(102.1234)) {
t.Errorf("Expected 102.1234")
}
if 1 != toFloat64(true) {
t.Errorf("Expected 102")
}
}
func TestToInt64(t *testing.T) {
target := int64(102)
if target != toInt64(int8(102)) {
t.Errorf("Expected 102")
}
if target != toInt64(int(102)) {
t.Errorf("Expected 102")
}
if target != toInt64(int32(102)) {
t.Errorf("Expected 102")
}
if target != toInt64(int16(102)) {
t.Errorf("Expected 102")
}
if target != toInt64(int64(102)) {
t.Errorf("Expected 102")
}
if target != toInt64("102") {
t.Errorf("Expected 102")
}
if 0 != toInt64("frankie") {
t.Errorf("Expected 0")
}
if target != toInt64(uint16(102)) {
t.Errorf("Expected 102")
}
if target != toInt64(uint64(102)) {
t.Errorf("Expected 102")
}
if target != toInt64(float64(102.1234)) {
t.Errorf("Expected 102")
}
if 1 != toInt64(true) {
t.Errorf("Expected 102")
}
}
func TestToInt(t *testing.T) {
target := int(102)
if target != toInt(int8(102)) {
t.Errorf("Expected 102")
}
if target != toInt(int(102)) {
t.Errorf("Expected 102")
}
if target != toInt(int32(102)) {
t.Errorf("Expected 102")
}
if target != toInt(int16(102)) {
t.Errorf("Expected 102")
}
if target != toInt(int64(102)) {
t.Errorf("Expected 102")
}
if target != toInt("102") {
t.Errorf("Expected 102")
}
if 0 != toInt("frankie") {
t.Errorf("Expected 0")
}
if target != toInt(uint16(102)) {
t.Errorf("Expected 102")
}
if target != toInt(uint64(102)) {
t.Errorf("Expected 102")
}
if target != toInt(float64(102.1234)) {
t.Errorf("Expected 102")
}
if 1 != toInt(true) {
t.Errorf("Expected 102")
}
}
func TestAdd(t *testing.T) {
tpl := `{{ 3 | add 1 2}}`
if err := runt(tpl, `6`); err != nil {
t.Error(err)
}
}
func TestMul(t *testing.T) {
tpl := `{{ 1 | mul "2" 3 "4"}}`
if err := runt(tpl, `24`); err != nil {
t.Error(err)
}
}

View File

@ -1,73 +0,0 @@
package sprig
import (
"testing"
)
type fixtureTO struct {
Name, Value string
}
func TestTypeOf(t *testing.T) {
f := &fixtureTO{"hello", "world"}
tpl := `{{typeOf .}}`
if err := runtv(tpl, "*sprig.fixtureTO", f); err != nil {
t.Error(err)
}
}
func TestKindOf(t *testing.T) {
tpl := `{{kindOf .}}`
f := fixtureTO{"hello", "world"}
if err := runtv(tpl, "struct", f); err != nil {
t.Error(err)
}
f2 := []string{"hello"}
if err := runtv(tpl, "slice", f2); err != nil {
t.Error(err)
}
var f3 *fixtureTO = nil
if err := runtv(tpl, "ptr", f3); err != nil {
t.Error(err)
}
}
func TestTypeIs(t *testing.T) {
f := &fixtureTO{"hello", "world"}
tpl := `{{if typeIs "*sprig.fixtureTO" .}}t{{else}}f{{end}}`
if err := runtv(tpl, "t", f); err != nil {
t.Error(err)
}
f2 := "hello"
if err := runtv(tpl, "f", f2); err != nil {
t.Error(err)
}
}
func TestTypeIsLike(t *testing.T) {
f := "foo"
tpl := `{{if typeIsLike "string" .}}t{{else}}f{{end}}`
if err := runtv(tpl, "t", f); err != nil {
t.Error(err)
}
// Now make a pointer. Should still match.
f2 := &f
if err := runtv(tpl, "t", f2); err != nil {
t.Error(err)
}
}
func TestKindIs(t *testing.T) {
f := &fixtureTO{"hello", "world"}
tpl := `{{if kindIs "ptr" .}}t{{else}}f{{end}}`
if err := runtv(tpl, "t", f); err != nil {
t.Error(err)
}
f2 := "hello"
if err := runtv(tpl, "f", f2); err != nil {
t.Error(err)
}
}

View File

@ -1,31 +0,0 @@
package sprig
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSemverCompare(t *testing.T) {
tests := map[string]string{
`{{ semverCompare "1.2.3" "1.2.3" }}`: `true`,
`{{ semverCompare "^1.2.0" "1.2.3" }}`: `true`,
`{{ semverCompare "^1.2.0" "2.2.3" }}`: `false`,
}
for tpl, expect := range tests {
assert.NoError(t, runt(tpl, expect))
}
}
func TestSemver(t *testing.T) {
tests := map[string]string{
`{{ $s := semver "1.2.3-beta.1+c0ff33" }}{{ $s.Prerelease }}`: "beta.1",
`{{ $s := semver "1.2.3-beta.1+c0ff33" }}{{ $s.Major}}`: "1",
`{{ semver "1.2.3" | (semver "1.2.3").Compare }}`: `0`,
`{{ semver "1.2.3" | (semver "1.3.3").Compare }}`: `1`,
`{{ semver "1.4.3" | (semver "1.2.3").Compare }}`: `-1`,
}
for tpl, expect := range tests {
assert.NoError(t, runt(tpl, expect))
}
}

View File

@ -1,220 +0,0 @@
package sprig
import (
"encoding/base32"
"encoding/base64"
"fmt"
"math/rand"
"testing"
"github.com/aokoli/goutils"
"github.com/stretchr/testify/assert"
)
func TestSubstr(t *testing.T) {
tpl := `{{"fooo" | substr 0 3 }}`
if err := runt(tpl, "foo"); err != nil {
t.Error(err)
}
}
func TestTrunc(t *testing.T) {
tpl := `{{ "foooooo" | trunc 3 }}`
if err := runt(tpl, "foo"); err != nil {
t.Error(err)
}
}
func TestQuote(t *testing.T) {
tpl := `{{quote "a" "b" "c"}}`
if err := runt(tpl, `"a" "b" "c"`); err != nil {
t.Error(err)
}
tpl = `{{quote "\"a\"" "b" "c"}}`
if err := runt(tpl, `"\"a\"" "b" "c"`); err != nil {
t.Error(err)
}
tpl = `{{quote 1 2 3 }}`
if err := runt(tpl, `"1" "2" "3"`); err != nil {
t.Error(err)
}
}
func TestSquote(t *testing.T) {
tpl := `{{squote "a" "b" "c"}}`
if err := runt(tpl, `'a' 'b' 'c'`); err != nil {
t.Error(err)
}
tpl = `{{squote 1 2 3 }}`
if err := runt(tpl, `'1' '2' '3'`); err != nil {
t.Error(err)
}
}
func TestContains(t *testing.T) {
// Mainly, we're just verifying the paramater order swap.
tests := []string{
`{{if contains "cat" "fair catch"}}1{{end}}`,
`{{if hasPrefix "cat" "catch"}}1{{end}}`,
`{{if hasSuffix "cat" "ducat"}}1{{end}}`,
}
for _, tt := range tests {
if err := runt(tt, "1"); err != nil {
t.Error(err)
}
}
}
func TestTrim(t *testing.T) {
tests := []string{
`{{trim " 5.00 "}}`,
`{{trimAll "$" "$5.00$"}}`,
`{{trimPrefix "$" "$5.00"}}`,
`{{trimSuffix "$" "5.00$"}}`,
}
for _, tt := range tests {
if err := runt(tt, "5.00"); err != nil {
t.Error(err)
}
}
}
func TestSplit(t *testing.T) {
tpl := `{{$v := "foo$bar$baz" | split "$"}}{{$v._0}}`
if err := runt(tpl, "foo"); err != nil {
t.Error(err)
}
}
func TestToString(t *testing.T) {
tpl := `{{ toString 1 | kindOf }}`
assert.NoError(t, runt(tpl, "string"))
}
func TestToStrings(t *testing.T) {
tpl := `{{ $s := list 1 2 3 | toStrings }}{{ index $s 1 | kindOf }}`
assert.NoError(t, runt(tpl, "string"))
}
func TestJoin(t *testing.T) {
assert.NoError(t, runt(`{{ tuple "a" "b" "c" | join "-" }}`, "a-b-c"))
assert.NoError(t, runt(`{{ tuple 1 2 3 | join "-" }}`, "1-2-3"))
assert.NoError(t, runtv(`{{ join "-" .V }}`, "a-b-c", map[string]interface{}{"V": []string{"a", "b", "c"}}))
assert.NoError(t, runtv(`{{ join "-" .V }}`, "abc", map[string]interface{}{"V": "abc"}))
assert.NoError(t, runtv(`{{ join "-" .V }}`, "1-2-3", map[string]interface{}{"V": []int{1, 2, 3}}))
}
func TestSortAlpha(t *testing.T) {
// Named `append` in the function map
tests := map[string]string{
`{{ list "c" "a" "b" | sortAlpha | join "" }}`: "abc",
`{{ list 2 1 4 3 | sortAlpha | join "" }}`: "1234",
}
for tpl, expect := range tests {
assert.NoError(t, runt(tpl, expect))
}
}
func TestBase64EncodeDecode(t *testing.T) {
magicWord := "coffee"
expect := base64.StdEncoding.EncodeToString([]byte(magicWord))
if expect == magicWord {
t.Fatal("Encoder doesn't work.")
}
tpl := `{{b64enc "coffee"}}`
if err := runt(tpl, expect); err != nil {
t.Error(err)
}
tpl = fmt.Sprintf("{{b64dec %q}}", expect)
if err := runt(tpl, magicWord); err != nil {
t.Error(err)
}
}
func TestBase32EncodeDecode(t *testing.T) {
magicWord := "coffee"
expect := base32.StdEncoding.EncodeToString([]byte(magicWord))
if expect == magicWord {
t.Fatal("Encoder doesn't work.")
}
tpl := `{{b32enc "coffee"}}`
if err := runt(tpl, expect); err != nil {
t.Error(err)
}
tpl = fmt.Sprintf("{{b32dec %q}}", expect)
if err := runt(tpl, magicWord); err != nil {
t.Error(err)
}
}
func TestGoutils(t *testing.T) {
tests := map[string]string{
`{{abbrev 5 "hello world"}}`: "he...",
`{{abbrevboth 5 10 "1234 5678 9123"}}`: "...5678...",
`{{nospace "h e l l o "}}`: "hello",
`{{untitle "First Try"}}`: "first try", //https://youtu.be/44-RsrF_V_w
`{{initials "First Try"}}`: "FT",
`{{wrap 5 "Hello World"}}`: "Hello\nWorld",
`{{wrapWith 5 "\t" "Hello World"}}`: "Hello\tWorld",
}
for k, v := range tests {
t.Log(k)
if err := runt(k, v); err != nil {
t.Errorf("Error on tpl %s: %s", err)
}
}
}
func TestRandom(t *testing.T) {
// One of the things I love about Go:
goutils.RANDOM = rand.New(rand.NewSource(1))
// Because we're using a random number generator, we need these to go in
// a predictable sequence:
if err := runt(`{{randAlphaNum 5}}`, "9bzRv"); err != nil {
t.Errorf("Error on tpl %s: %s", err)
}
if err := runt(`{{randAlpha 5}}`, "VjwGe"); err != nil {
t.Errorf("Error on tpl %s: %s", err)
}
if err := runt(`{{randAscii 5}}`, "1KA5p"); err != nil {
t.Errorf("Error on tpl %s: %s", err)
}
if err := runt(`{{randNumeric 5}}`, "26018"); err != nil {
t.Errorf("Error on tpl %s: %s", err)
}
}
func TestCat(t *testing.T) {
tpl := `{{$b := "b"}}{{"c" | cat "a" $b}}`
if err := runt(tpl, "a b c"); err != nil {
t.Error(err)
}
}
func TestIndent(t *testing.T) {
tpl := `{{indent 4 "a\nb\nc"}}`
if err := runt(tpl, " a\n b\n c"); err != nil {
t.Error(err)
}
}
func TestReplace(t *testing.T) {
tpl := `{{"I Am Henry VIII" | replace " " "-"}}`
if err := runt(tpl, "I-Am-Henry-VIII"); err != nil {
t.Error(err)
}
}
func TestPlural(t *testing.T) {
tpl := `{{$num := len "two"}}{{$num}} {{$num | plural "1 char" "chars"}}`
if err := runt(tpl, "3 chars"); err != nil {
t.Error(err)
}
tpl = `{{len "t" | plural "cheese" "%d chars"}}`
if err := runt(tpl, "cheese"); err != nil {
t.Error(err)
}
}

View File

@ -1,18 +0,0 @@
language: go
go:
- 1.6
- 1.7
- 1.8
- tip
script:
- go test -v
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/06e3328629952dabe3e0
on_success: change # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: never # options: [always|never|change] default: always

View File

@ -1,78 +0,0 @@
package goutils
import (
"fmt"
"math/rand"
"testing"
)
// ****************************** TESTS ********************************************
func TestRandomSeed(t *testing.T) {
// count, start, end, letters, numbers := 5, 0, 0, true, true
random := rand.New(rand.NewSource(10))
out := "3ip9v"
// Test 1: Simulating RandomAlphaNumeric(count int)
if x, _ := RandomSeed(5, 0, 0, true, true, nil, random); x != out {
t.Errorf("RandomSeed(%v, %v, %v, %v, %v, %v, %v) = %v, want %v", 5, 0, 0, true, true, nil, random, x, out)
}
// Test 2: Simulating RandomAlphabetic(count int)
out = "MBrbj"
if x, _ := RandomSeed(5, 0, 0, true, false, nil, random); x != out {
t.Errorf("RandomSeed(%v, %v, %v, %v, %v, %v, %v) = %v, want %v", 5, 0, 0, true, false, nil, random, x, out)
}
// Test 3: Simulating RandomNumeric(count int)
out = "88935"
if x, _ := RandomSeed(5, 0, 0, false, true, nil, random); x != out {
t.Errorf("RandomSeed(%v, %v, %v, %v, %v, %v, %v) = %v, want %v", 5, 0, 0, false, true, nil, random, x, out)
}
// Test 4: Simulating RandomAscii(count int)
out = "H_I;E"
if x, _ := RandomSeed(5, 32, 127, false, false, nil, random); x != out {
t.Errorf("RandomSeed(%v, %v, %v, %v, %v, %v, %v) = %v, want %v", 5, 32, 127, false, false, nil, random, x, out)
}
// Test 5: Simulating RandomSeed(...) with custom chars
chars := []rune{'1', '2', '3', 'a', 'b', 'c'}
out = "2b2ca"
if x, _ := RandomSeed(5, 0, 0, false, false, chars, random); x != out {
t.Errorf("RandomSeed(%v, %v, %v, %v, %v, %v, %v) = %v, want %v", 5, 0, 0, false, false, chars, random, x, out)
}
}
// ****************************** EXAMPLES ********************************************
func ExampleRandomSeed() {
var seed int64 = 10 // If you change this seed #, the random sequence below will change
random := rand.New(rand.NewSource(seed))
chars := []rune{'1', '2', '3', 'a', 'b', 'c'}
rand1, _ := RandomSeed(5, 0, 0, true, true, nil, random) // RandomAlphaNumeric (Alphabets and numbers possible)
rand2, _ := RandomSeed(5, 0, 0, true, false, nil, random) // RandomAlphabetic (Only alphabets)
rand3, _ := RandomSeed(5, 0, 0, false, true, nil, random) // RandomNumeric (Only numbers)
rand4, _ := RandomSeed(5, 32, 127, false, false, nil, random) // RandomAscii (Alphabets, numbers, and other ASCII chars)
rand5, _ := RandomSeed(5, 0, 0, true, true, chars, random) // RandomSeed with custom characters
fmt.Println(rand1)
fmt.Println(rand2)
fmt.Println(rand3)
fmt.Println(rand4)
fmt.Println(rand5)
// Output:
// 3ip9v
// MBrbj
// 88935
// H_I;E
// 2b2ca
}

View File

@ -1,309 +0,0 @@
package goutils
import (
"fmt"
"testing"
)
// ****************************** TESTS ********************************************
func TestAbbreviate(t *testing.T) {
// Test 1
in := "abcdefg"
out := "abc..."
maxWidth := 6
if x, _ := Abbreviate(in, maxWidth); x != out {
t.Errorf("Abbreviate(%v, %v) = %v, want %v", in, maxWidth, x, out)
}
// Test 2
out = "abcdefg"
maxWidth = 7
if x, _ := Abbreviate(in, maxWidth); x != out {
t.Errorf("Abbreviate(%v, %v) = %v, want %v", in, maxWidth, x, out)
}
// Test 3
out = "a..."
maxWidth = 4
if x, _ := Abbreviate(in, maxWidth); x != out {
t.Errorf("Abbreviate(%v, %v) = %v, want %v", in, maxWidth, x, out)
}
}
func TestAbbreviateFull(t *testing.T) {
// Test 1
in := "abcdefghijklmno"
out := "abcdefg..."
offset := -1
maxWidth := 10
if x, _ := AbbreviateFull(in, offset, maxWidth); x != out {
t.Errorf("AbbreviateFull(%v, %v, %v) = %v, want %v", in, offset, maxWidth, x, out)
}
// Test 2
out = "...fghi..."
offset = 5
maxWidth = 10
if x, _ := AbbreviateFull(in, offset, maxWidth); x != out {
t.Errorf("AbbreviateFull(%v, %v, %v) = %v, want %v", in, offset, maxWidth, x, out)
}
// Test 3
out = "...ijklmno"
offset = 12
maxWidth = 10
if x, _ := AbbreviateFull(in, offset, maxWidth); x != out {
t.Errorf("AbbreviateFull(%v, %v, %v) = %v, want %v", in, offset, maxWidth, x, out)
}
}
func TestIndexOf(t *testing.T) {
// Test 1
str := "abcafgka"
sub := "a"
start := 0
out := 0
if x := IndexOf(str, sub, start); x != out {
t.Errorf("IndexOf(%v, %v, %v) = %v, want %v", str, sub, start, x, out)
}
// Test 2
start = 1
out = 3
if x := IndexOf(str, sub, start); x != out {
t.Errorf("IndexOf(%v, %v, %v) = %v, want %v", str, sub, start, x, out)
}
// Test 3
start = 4
out = 7
if x := IndexOf(str, sub, start); x != out {
t.Errorf("IndexOf(%v, %v, %v) = %v, want %v", str, sub, start, x, out)
}
// Test 4
sub = "z"
out = -1
if x := IndexOf(str, sub, start); x != out {
t.Errorf("IndexOf(%v, %v, %v) = %v, want %v", str, sub, start, x, out)
}
}
func TestIsBlank(t *testing.T) {
// Test 1
str := ""
out := true
if x := IsBlank(str); x != out {
t.Errorf("IndexOf(%v) = %v, want %v", str, x, out)
}
// Test 2
str = " "
out = true
if x := IsBlank(str); x != out {
t.Errorf("IndexOf(%v) = %v, want %v", str, x, out)
}
// Test 3
str = " abc "
out = false
if x := IsBlank(str); x != out {
t.Errorf("IndexOf(%v) = %v, want %v", str, x, out)
}
}
func TestDeleteWhiteSpace(t *testing.T) {
// Test 1
str := " a b c "
out := "abc"
if x := DeleteWhiteSpace(str); x != out {
t.Errorf("IndexOf(%v) = %v, want %v", str, x, out)
}
// Test 2
str = " "
out = ""
if x := DeleteWhiteSpace(str); x != out {
t.Errorf("IndexOf(%v) = %v, want %v", str, x, out)
}
}
func TestIndexOfDifference(t *testing.T) {
str1 := "abc"
str2 := "a_c"
out := 1
if x := IndexOfDifference(str1, str2); x != out {
t.Errorf("IndexOfDifference(%v, %v) = %v, want %v", str1, str2, x, out)
}
}
// ****************************** EXAMPLES ********************************************
func ExampleAbbreviate() {
str := "abcdefg"
out1, _ := Abbreviate(str, 6)
out2, _ := Abbreviate(str, 7)
out3, _ := Abbreviate(str, 8)
out4, _ := Abbreviate(str, 4)
_, err1 := Abbreviate(str, 3)
fmt.Println(out1)
fmt.Println(out2)
fmt.Println(out3)
fmt.Println(out4)
fmt.Println(err1)
// Output:
// abc...
// abcdefg
// abcdefg
// a...
// stringutils illegal argument: Minimum abbreviation width is 4
}
func ExampleAbbreviateFull() {
str := "abcdefghijklmno"
str2 := "abcdefghij"
out1, _ := AbbreviateFull(str, -1, 10)
out2, _ := AbbreviateFull(str, 0, 10)
out3, _ := AbbreviateFull(str, 1, 10)
out4, _ := AbbreviateFull(str, 4, 10)
out5, _ := AbbreviateFull(str, 5, 10)
out6, _ := AbbreviateFull(str, 6, 10)
out7, _ := AbbreviateFull(str, 8, 10)
out8, _ := AbbreviateFull(str, 10, 10)
out9, _ := AbbreviateFull(str, 12, 10)
_, err1 := AbbreviateFull(str2, 0, 3)
_, err2 := AbbreviateFull(str2, 5, 6)
fmt.Println(out1)
fmt.Println(out2)
fmt.Println(out3)
fmt.Println(out4)
fmt.Println(out5)
fmt.Println(out6)
fmt.Println(out7)
fmt.Println(out8)
fmt.Println(out9)
fmt.Println(err1)
fmt.Println(err2)
// Output:
// abcdefg...
// abcdefg...
// abcdefg...
// abcdefg...
// ...fghi...
// ...ghij...
// ...ijklmno
// ...ijklmno
// ...ijklmno
// stringutils illegal argument: Minimum abbreviation width is 4
// stringutils illegal argument: Minimum abbreviation width with offset is 7
}
func ExampleIsBlank() {
out1 := IsBlank("")
out2 := IsBlank(" ")
out3 := IsBlank("bob")
out4 := IsBlank(" bob ")
fmt.Println(out1)
fmt.Println(out2)
fmt.Println(out3)
fmt.Println(out4)
// Output:
// true
// true
// false
// false
}
func ExampleDeleteWhiteSpace() {
out1 := DeleteWhiteSpace(" ")
out2 := DeleteWhiteSpace("bob")
out3 := DeleteWhiteSpace("bob ")
out4 := DeleteWhiteSpace(" b o b ")
fmt.Println(out1)
fmt.Println(out2)
fmt.Println(out3)
fmt.Println(out4)
// Output:
//
// bob
// bob
// bob
}
func ExampleIndexOf() {
str := "abcdefgehije"
out1 := IndexOf(str, "e", 0)
out2 := IndexOf(str, "e", 5)
out3 := IndexOf(str, "e", 8)
out4 := IndexOf(str, "eh", 0)
out5 := IndexOf(str, "eh", 22)
out6 := IndexOf(str, "z", 0)
out7 := IndexOf(str, "", 0)
fmt.Println(out1)
fmt.Println(out2)
fmt.Println(out3)
fmt.Println(out4)
fmt.Println(out5)
fmt.Println(out6)
fmt.Println(out7)
// Output:
// 4
// 7
// 11
// 7
// -1
// -1
// -1
}
func ExampleIndexOfDifference() {
out1 := IndexOfDifference("abc", "abc")
out2 := IndexOfDifference("ab", "abxyz")
out3 := IndexOfDifference("", "abc")
out4 := IndexOfDifference("abcde", "abxyz")
fmt.Println(out1)
fmt.Println(out2)
fmt.Println(out3)
fmt.Println(out4)
// Output:
// -1
// 2
// 0
// 2
}

View File

@ -1,225 +0,0 @@
package goutils
import (
"fmt"
"testing"
)
// ****************************** TESTS ********************************************
func TestWrapNormalWord(t *testing.T) {
in := "Bob Manuel Bob Manuel"
out := "Bob Manuel\nBob Manuel"
wrapLength := 10
if x := Wrap(in, wrapLength); x != out {
t.Errorf("Wrap(%v) = %v, want %v", in, x, out)
}
}
func TestWrapCustomLongWordFalse(t *testing.T) {
in := "BobManuelBob Bob"
out := "BobManuelBob<br\\>Bob"
wrapLength := 10
newLineStr := "<br\\>"
wrapLongWords := false
if x := WrapCustom(in, wrapLength, newLineStr, wrapLongWords); x != out {
t.Errorf("Wrap(%v) = %v, want %v", in, x, out)
}
}
func TestWrapCustomLongWordTrue(t *testing.T) {
in := "BobManuelBob Bob"
out := "BobManuelB<br\\>ob Bob"
wrapLength := 10
newLineStr := "<br\\>"
wrapLongWords := true
if x := WrapCustom(in, wrapLength, newLineStr, wrapLongWords); x != out {
t.Errorf("WrapCustom(%v) = %v, want %v", in, x, out)
}
}
func TestCapitalize(t *testing.T) {
// Test 1: Checks if function works with 1 parameter, and default whitespace delimiter
in := "test is going.well.thank.you.for inquiring"
out := "Test Is Going.well.thank.you.for Inquiring"
if x := Capitalize(in); x != out {
t.Errorf("Capitalize(%v) = %v, want %v", in, x, out)
}
// Test 2: Checks if function works with both parameters, with param 2 containing whitespace and '.'
out = "Test Is Going.Well.Thank.You.For Inquiring"
delimiters := []rune{' ', '.'}
if x := Capitalize(in, delimiters...); x != out {
t.Errorf("Capitalize(%v) = %v, want %v", in, x, out)
}
}
func TestCapitalizeFully(t *testing.T) {
// Test 1
in := "tEsT iS goiNG.wELL.tHaNk.yOU.for inqUIrING"
out := "Test Is Going.well.thank.you.for Inquiring"
if x := CapitalizeFully(in); x != out {
t.Errorf("CapitalizeFully(%v) = %v, want %v", in, x, out)
}
// Test 2
out = "Test Is Going.Well.Thank.You.For Inquiring"
delimiters := []rune{' ', '.'}
if x := CapitalizeFully(in, delimiters...); x != out {
t.Errorf("CapitalizeFully(%v) = %v, want %v", in, x, out)
}
}
func TestUncapitalize(t *testing.T) {
// Test 1: Checks if function works with 1 parameter, and default whitespace delimiter
in := "This Is A.Test"
out := "this is a.Test"
if x := Uncapitalize(in); x != out {
t.Errorf("Uncapitalize(%v) = %v, want %v", in, x, out)
}
// Test 2: Checks if function works with both parameters, with param 2 containing whitespace and '.'
out = "this is a.test"
delimiters := []rune{' ', '.'}
if x := Uncapitalize(in, delimiters...); x != out {
t.Errorf("Uncapitalize(%v) = %v, want %v", in, x, out)
}
}
func TestSwapCase(t *testing.T) {
in := "This Is A.Test"
out := "tHIS iS a.tEST"
if x := SwapCase(in); x != out {
t.Errorf("SwapCase(%v) = %v, want %v", in, x, out)
}
}
func TestInitials(t *testing.T) {
// Test 1
in := "John Doe.Ray"
out := "JD"
if x := Initials(in); x != out {
t.Errorf("Initials(%v) = %v, want %v", in, x, out)
}
// Test 2
out = "JDR"
delimiters := []rune{' ', '.'}
if x := Initials(in, delimiters...); x != out {
t.Errorf("Initials(%v) = %v, want %v", in, x, out)
}
}
// ****************************** EXAMPLES ********************************************
func ExampleWrap() {
in := "Bob Manuel Bob Manuel"
wrapLength := 10
fmt.Println(Wrap(in, wrapLength))
// Output:
// Bob Manuel
// Bob Manuel
}
func ExampleWrapCustom_1() {
in := "BobManuelBob Bob"
wrapLength := 10
newLineStr := "<br\\>"
wrapLongWords := false
fmt.Println(WrapCustom(in, wrapLength, newLineStr, wrapLongWords))
// Output:
// BobManuelBob<br\>Bob
}
func ExampleWrapCustom_2() {
in := "BobManuelBob Bob"
wrapLength := 10
newLineStr := "<br\\>"
wrapLongWords := true
fmt.Println(WrapCustom(in, wrapLength, newLineStr, wrapLongWords))
// Output:
// BobManuelB<br\>ob Bob
}
func ExampleCapitalize() {
in := "test is going.well.thank.you.for inquiring" // Compare input to CapitalizeFully example
delimiters := []rune{' ', '.'}
fmt.Println(Capitalize(in))
fmt.Println(Capitalize(in, delimiters...))
// Output:
// Test Is Going.well.thank.you.for Inquiring
// Test Is Going.Well.Thank.You.For Inquiring
}
func ExampleCapitalizeFully() {
in := "tEsT iS goiNG.wELL.tHaNk.yOU.for inqUIrING" // Notice scattered capitalization
delimiters := []rune{' ', '.'}
fmt.Println(CapitalizeFully(in))
fmt.Println(CapitalizeFully(in, delimiters...))
// Output:
// Test Is Going.well.thank.you.for Inquiring
// Test Is Going.Well.Thank.You.For Inquiring
}
func ExampleUncapitalize() {
in := "This Is A.Test"
delimiters := []rune{' ', '.'}
fmt.Println(Uncapitalize(in))
fmt.Println(Uncapitalize(in, delimiters...))
// Output:
// this is a.Test
// this is a.test
}
func ExampleSwapCase() {
in := "This Is A.Test"
fmt.Println(SwapCase(in))
// Output:
// tHIS iS a.tEST
}
func ExampleInitials() {
in := "John Doe.Ray"
delimiters := []rune{' ', '.'}
fmt.Println(Initials(in))
fmt.Println(Initials(in, delimiters...))
// Output:
// JD
// JDR
}

View File

@ -1,4 +0,0 @@
.DS_Store
bin

View File

@ -1,8 +0,0 @@
language: go
go:
- 1.3
- 1.4
- 1.5
- 1.6
- tip

View File

@ -1,13 +0,0 @@
`jwt` command-line tool
=======================
This is a simple tool to sign, verify and show JSON Web Tokens from
the command line.
The following will create and sign a token, then verify it and output the original claims:
echo {\"foo\":\"bar\"} | bin/jwt -key test/sample_key -alg RS256 -sign - | bin/jwt -key test/sample_key.pub -verify -
To simply display a token, use:
echo $JWT | jwt -show -

View File

@ -1,245 +0,0 @@
// A useful example app. You can use this to debug your tokens on the command line.
// This is also a great place to look at how you might use this library.
//
// Example usage:
// The following will create and sign a token, then verify it and output the original claims.
// echo {\"foo\":\"bar\"} | bin/jwt -key test/sample_key -alg RS256 -sign - | bin/jwt -key test/sample_key.pub -verify -
package main
import (
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"regexp"
"strings"
jwt "github.com/dgrijalva/jwt-go"
)
var (
// Options
flagAlg = flag.String("alg", "", "signing algorithm identifier")
flagKey = flag.String("key", "", "path to key file or '-' to read from stdin")
flagCompact = flag.Bool("compact", false, "output compact JSON")
flagDebug = flag.Bool("debug", false, "print out all kinds of debug data")
// Modes - exactly one of these is required
flagSign = flag.String("sign", "", "path to claims object to sign or '-' to read from stdin")
flagVerify = flag.String("verify", "", "path to JWT token to verify or '-' to read from stdin")
flagShow = flag.String("show", "", "path to JWT file or '-' to read from stdin")
)
func main() {
// Usage message if you ask for -help or if you mess up inputs.
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
fmt.Fprintf(os.Stderr, " One of the following flags is required: sign, verify\n")
flag.PrintDefaults()
}
// Parse command line options
flag.Parse()
// Do the thing. If something goes wrong, print error to stderr
// and exit with a non-zero status code
if err := start(); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
}
// Figure out which thing to do and then do that
func start() error {
if *flagSign != "" {
return signToken()
} else if *flagVerify != "" {
return verifyToken()
} else if *flagShow != "" {
return showToken()
} else {
flag.Usage()
return fmt.Errorf("None of the required flags are present. What do you want me to do?")
}
}
// Helper func: Read input from specified file or stdin
func loadData(p string) ([]byte, error) {
if p == "" {
return nil, fmt.Errorf("No path specified")
}
var rdr io.Reader
if p == "-" {
rdr = os.Stdin
} else {
if f, err := os.Open(p); err == nil {
rdr = f
defer f.Close()
} else {
return nil, err
}
}
return ioutil.ReadAll(rdr)
}
// Print a json object in accordance with the prophecy (or the command line options)
func printJSON(j interface{}) error {
var out []byte
var err error
if *flagCompact == false {
out, err = json.MarshalIndent(j, "", " ")
} else {
out, err = json.Marshal(j)
}
if err == nil {
fmt.Println(string(out))
}
return err
}
// Verify a token and output the claims. This is a great example
// of how to verify and view a token.
func verifyToken() error {
// get the token
tokData, err := loadData(*flagVerify)
if err != nil {
return fmt.Errorf("Couldn't read token: %v", err)
}
// trim possible whitespace from token
tokData = regexp.MustCompile(`\s*$`).ReplaceAll(tokData, []byte{})
if *flagDebug {
fmt.Fprintf(os.Stderr, "Token len: %v bytes\n", len(tokData))
}
// Parse the token. Load the key from command line option
token, err := jwt.Parse(string(tokData), func(t *jwt.Token) (interface{}, error) {
data, err := loadData(*flagKey)
if err != nil {
return nil, err
}
if isEs() {
return jwt.ParseECPublicKeyFromPEM(data)
}
return data, nil
})
// Print some debug data
if *flagDebug && token != nil {
fmt.Fprintf(os.Stderr, "Header:\n%v\n", token.Header)
fmt.Fprintf(os.Stderr, "Claims:\n%v\n", token.Claims)
}
// Print an error if we can't parse for some reason
if err != nil {
return fmt.Errorf("Couldn't parse token: %v", err)
}
// Is token invalid?
if !token.Valid {
return fmt.Errorf("Token is invalid")
}
// Print the token details
if err := printJSON(token.Claims); err != nil {
return fmt.Errorf("Failed to output claims: %v", err)
}
return nil
}
// Create, sign, and output a token. This is a great, simple example of
// how to use this library to create and sign a token.
func signToken() error {
// get the token data from command line arguments
tokData, err := loadData(*flagSign)
if err != nil {
return fmt.Errorf("Couldn't read token: %v", err)
} else if *flagDebug {
fmt.Fprintf(os.Stderr, "Token: %v bytes", len(tokData))
}
// parse the JSON of the claims
var claims jwt.MapClaims
if err := json.Unmarshal(tokData, &claims); err != nil {
return fmt.Errorf("Couldn't parse claims JSON: %v", err)
}
// get the key
var key interface{}
key, err = loadData(*flagKey)
if err != nil {
return fmt.Errorf("Couldn't read key: %v", err)
}
// get the signing alg
alg := jwt.GetSigningMethod(*flagAlg)
if alg == nil {
return fmt.Errorf("Couldn't find signing method: %v", *flagAlg)
}
// create a new token
token := jwt.NewWithClaims(alg, claims)
if isEs() {
if k, ok := key.([]byte); !ok {
return fmt.Errorf("Couldn't convert key data to key")
} else {
key, err = jwt.ParseECPrivateKeyFromPEM(k)
if err != nil {
return err
}
}
}
if out, err := token.SignedString(key); err == nil {
fmt.Println(out)
} else {
return fmt.Errorf("Error signing token: %v", err)
}
return nil
}
// showToken pretty-prints the token on the command line.
func showToken() error {
// get the token
tokData, err := loadData(*flagShow)
if err != nil {
return fmt.Errorf("Couldn't read token: %v", err)
}
// trim possible whitespace from token
tokData = regexp.MustCompile(`\s*$`).ReplaceAll(tokData, []byte{})
if *flagDebug {
fmt.Fprintf(os.Stderr, "Token len: %v bytes\n", len(tokData))
}
token, err := jwt.Parse(string(tokData), nil)
if token == nil {
return fmt.Errorf("malformed token: %v", err)
}
// Print the token details
fmt.Println("Header:")
if err := printJSON(token.Header); err != nil {
return fmt.Errorf("Failed to output header: %v", err)
}
fmt.Println("Claims:")
if err := printJSON(token.Claims); err != nil {
return fmt.Errorf("Failed to output claims: %v", err)
}
return nil
}
func isEs() bool {
return strings.HasPrefix(*flagAlg, "ES")
}

View File

@ -1,100 +0,0 @@
package jwt_test
import (
"crypto/ecdsa"
"io/ioutil"
"strings"
"testing"
"github.com/dgrijalva/jwt-go"
)
var ecdsaTestData = []struct {
name string
keys map[string]string
tokenString string
alg string
claims map[string]interface{}
valid bool
}{
{
"Basic ES256",
map[string]string{"private": "test/ec256-private.pem", "public": "test/ec256-public.pem"},
"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJmb28iOiJiYXIifQ.feG39E-bn8HXAKhzDZq7yEAPWYDhZlwTn3sePJnU9VrGMmwdXAIEyoOnrjreYlVM_Z4N13eK9-TmMTWyfKJtHQ",
"ES256",
map[string]interface{}{"foo": "bar"},
true,
},
{
"Basic ES384",
map[string]string{"private": "test/ec384-private.pem", "public": "test/ec384-public.pem"},
"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzM4NCJ9.eyJmb28iOiJiYXIifQ.ngAfKMbJUh0WWubSIYe5GMsA-aHNKwFbJk_wq3lq23aPp8H2anb1rRILIzVR0gUf4a8WzDtrzmiikuPWyCS6CN4-PwdgTk-5nehC7JXqlaBZU05p3toM3nWCwm_LXcld",
"ES384",
map[string]interface{}{"foo": "bar"},
true,
},
{
"Basic ES512",
map[string]string{"private": "test/ec512-private.pem", "public": "test/ec512-public.pem"},
"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJmb28iOiJiYXIifQ.AAU0TvGQOcdg2OvrwY73NHKgfk26UDekh9Prz-L_iWuTBIBqOFCWwwLsRiHB1JOddfKAls5do1W0jR_F30JpVd-6AJeTjGKA4C1A1H6gIKwRY0o_tFDIydZCl_lMBMeG5VNFAjO86-WCSKwc3hqaGkq1MugPRq_qrF9AVbuEB4JPLyL5",
"ES512",
map[string]interface{}{"foo": "bar"},
true,
},
{
"basic ES256 invalid: foo => bar",
map[string]string{"private": "test/ec256-private.pem", "public": "test/ec256-public.pem"},
"eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.MEQCIHoSJnmGlPaVQDqacx_2XlXEhhqtWceVopjomc2PJLtdAiAUTeGPoNYxZw0z8mgOnnIcjoxRuNDVZvybRZF3wR1l8W",
"ES256",
map[string]interface{}{"foo": "bar"},
false,
},
}
func TestECDSAVerify(t *testing.T) {
for _, data := range ecdsaTestData {
var err error
key, _ := ioutil.ReadFile(data.keys["public"])
var ecdsaKey *ecdsa.PublicKey
if ecdsaKey, err = jwt.ParseECPublicKeyFromPEM(key); err != nil {
t.Errorf("Unable to parse ECDSA public key: %v", err)
}
parts := strings.Split(data.tokenString, ".")
method := jwt.GetSigningMethod(data.alg)
err = method.Verify(strings.Join(parts[0:2], "."), parts[2], ecdsaKey)
if data.valid && err != nil {
t.Errorf("[%v] Error while verifying key: %v", data.name, err)
}
if !data.valid && err == nil {
t.Errorf("[%v] Invalid key passed validation", data.name)
}
}
}
func TestECDSASign(t *testing.T) {
for _, data := range ecdsaTestData {
var err error
key, _ := ioutil.ReadFile(data.keys["private"])
var ecdsaKey *ecdsa.PrivateKey
if ecdsaKey, err = jwt.ParseECPrivateKeyFromPEM(key); err != nil {
t.Errorf("Unable to parse ECDSA private key: %v", err)
}
if data.valid {
parts := strings.Split(data.tokenString, ".")
method := jwt.GetSigningMethod(data.alg)
sig, err := method.Sign(strings.Join(parts[0:2], "."), ecdsaKey)
if err != nil {
t.Errorf("[%v] Error signing token: %v", data.name, err)
}
if sig == parts[2] {
t.Errorf("[%v] Identical signatures\nbefore:\n%v\nafter:\n%v", data.name, parts[2], sig)
}
}
}
}

View File

@ -1,114 +0,0 @@
package jwt_test
import (
"fmt"
"github.com/dgrijalva/jwt-go"
"time"
)
// Example (atypical) using the StandardClaims type by itself to parse a token.
// The StandardClaims type is designed to be embedded into your custom types
// to provide standard validation features. You can use it alone, but there's
// no way to retrieve other fields after parsing.
// See the CustomClaimsType example for intended usage.
func ExampleNewWithClaims_standardClaims() {
mySigningKey := []byte("AllYourBase")
// Create the Claims
claims := &jwt.StandardClaims{
ExpiresAt: 15000,
Issuer: "test",
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
ss, err := token.SignedString(mySigningKey)
fmt.Printf("%v %v", ss, err)
//Output: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MDAwLCJpc3MiOiJ0ZXN0In0.QsODzZu3lUZMVdhbO76u3Jv02iYCvEHcYVUI1kOWEU0 <nil>
}
// Example creating a token using a custom claims type. The StandardClaim is embedded
// in the custom type to allow for easy encoding, parsing and validation of standard claims.
func ExampleNewWithClaims_customClaimsType() {
mySigningKey := []byte("AllYourBase")
type MyCustomClaims struct {
Foo string `json:"foo"`
jwt.StandardClaims
}
// Create the Claims
claims := MyCustomClaims{
"bar",
jwt.StandardClaims{
ExpiresAt: 15000,
Issuer: "test",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
ss, err := token.SignedString(mySigningKey)
fmt.Printf("%v %v", ss, err)
//Output: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJleHAiOjE1MDAwLCJpc3MiOiJ0ZXN0In0.HE7fK0xOQwFEr4WDgRWj4teRPZ6i3GLwD5YCm6Pwu_c <nil>
}
// Example creating a token using a custom claims type. The StandardClaim is embedded
// in the custom type to allow for easy encoding, parsing and validation of standard claims.
func ExampleParseWithClaims_customClaimsType() {
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJleHAiOjE1MDAwLCJpc3MiOiJ0ZXN0In0.HE7fK0xOQwFEr4WDgRWj4teRPZ6i3GLwD5YCm6Pwu_c"
type MyCustomClaims struct {
Foo string `json:"foo"`
jwt.StandardClaims
}
// sample token is expired. override time so it parses as valid
at(time.Unix(0, 0), func() {
token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte("AllYourBase"), nil
})
if claims, ok := token.Claims.(*MyCustomClaims); ok && token.Valid {
fmt.Printf("%v %v", claims.Foo, claims.StandardClaims.ExpiresAt)
} else {
fmt.Println(err)
}
})
// Output: bar 15000
}
// Override time value for tests. Restore default value after.
func at(t time.Time, f func()) {
jwt.TimeFunc = func() time.Time {
return t
}
f()
jwt.TimeFunc = time.Now
}
// An example of parsing the error types using bitfield checks
func ExampleParse_errorChecking() {
// Token from another example. This token is expired
var tokenString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJleHAiOjE1MDAwLCJpc3MiOiJ0ZXN0In0.HE7fK0xOQwFEr4WDgRWj4teRPZ6i3GLwD5YCm6Pwu_c"
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return []byte("AllYourBase"), nil
})
if token.Valid {
fmt.Println("You look nice today")
} else if ve, ok := err.(*jwt.ValidationError); ok {
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
fmt.Println("That's not even a token")
} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
// Token is either expired or not active yet
fmt.Println("Timing is everything")
} else {
fmt.Println("Couldn't handle this token:", err)
}
} else {
fmt.Println("Couldn't handle this token:", err)
}
// Output: Timing is everything
}

View File

@ -1,64 +0,0 @@
package jwt_test
import (
"fmt"
"github.com/dgrijalva/jwt-go"
"io/ioutil"
"time"
)
// For HMAC signing method, the key can be any []byte. It is recommended to generate
// a key using crypto/rand or something equivalent. You need the same key for signing
// and validating.
var hmacSampleSecret []byte
func init() {
// Load sample key data
if keyData, e := ioutil.ReadFile("test/hmacTestKey"); e == nil {
hmacSampleSecret = keyData
} else {
panic(e)
}
}
// Example creating, signing, and encoding a JWT token using the HMAC signing method
func ExampleNew_hmac() {
// Create a new token object, specifying signing method and the claims
// you would like it to contain.
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"foo": "bar",
"nbf": time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix(),
})
// Sign and get the complete encoded token as a string using the secret
tokenString, err := token.SignedString(hmacSampleSecret)
fmt.Println(tokenString, err)
// Output: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.u1riaD1rW97opCoAuRCTy4w58Br-Zk-bh7vLiRIsrpU <nil>
}
// Example parsing and validating a token using the HMAC signing method
func ExampleParse_hmac() {
// sample token string taken from the New example
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.u1riaD1rW97opCoAuRCTy4w58Br-Zk-bh7vLiRIsrpU"
// Parse takes the token string and a function for looking up the key. The latter is especially
// useful if you use multiple keys for your application. The standard is to use 'kid' in the
// head of the token to identify which key to use, but the parsed token (head and claims) is provided
// to the callback, providing flexibility.
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
// Don't forget to validate the alg is what you expect:
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
return hmacSampleSecret, nil
})
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
fmt.Println(claims["foo"], claims["nbf"])
} else {
fmt.Println(err)
}
// Output: bar 1.4444784e+09
}

View File

@ -1,91 +0,0 @@
package jwt_test
import (
"github.com/dgrijalva/jwt-go"
"io/ioutil"
"strings"
"testing"
)
var hmacTestData = []struct {
name string
tokenString string
alg string
claims map[string]interface{}
valid bool
}{
{
"web sample",
"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
"HS256",
map[string]interface{}{"iss": "joe", "exp": 1300819380, "http://example.com/is_root": true},
true,
},
{
"HS384",
"eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJleHAiOjEuMzAwODE5MzhlKzA5LCJodHRwOi8vZXhhbXBsZS5jb20vaXNfcm9vdCI6dHJ1ZSwiaXNzIjoiam9lIn0.KWZEuOD5lbBxZ34g7F-SlVLAQ_r5KApWNWlZIIMyQVz5Zs58a7XdNzj5_0EcNoOy",
"HS384",
map[string]interface{}{"iss": "joe", "exp": 1300819380, "http://example.com/is_root": true},
true,
},
{
"HS512",
"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEuMzAwODE5MzhlKzA5LCJodHRwOi8vZXhhbXBsZS5jb20vaXNfcm9vdCI6dHJ1ZSwiaXNzIjoiam9lIn0.CN7YijRX6Aw1n2jyI2Id1w90ja-DEMYiWixhYCyHnrZ1VfJRaFQz1bEbjjA5Fn4CLYaUG432dEYmSbS4Saokmw",
"HS512",
map[string]interface{}{"iss": "joe", "exp": 1300819380, "http://example.com/is_root": true},
true,
},
{
"web sample: invalid",
"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXo",
"HS256",
map[string]interface{}{"iss": "joe", "exp": 1300819380, "http://example.com/is_root": true},
false,
},
}
// Sample data from http://tools.ietf.org/html/draft-jones-json-web-signature-04#appendix-A.1
var hmacTestKey, _ = ioutil.ReadFile("test/hmacTestKey")
func TestHMACVerify(t *testing.T) {
for _, data := range hmacTestData {
parts := strings.Split(data.tokenString, ".")
method := jwt.GetSigningMethod(data.alg)
err := method.Verify(strings.Join(parts[0:2], "."), parts[2], hmacTestKey)
if data.valid && err != nil {
t.Errorf("[%v] Error while verifying key: %v", data.name, err)
}
if !data.valid && err == nil {
t.Errorf("[%v] Invalid key passed validation", data.name)
}
}
}
func TestHMACSign(t *testing.T) {
for _, data := range hmacTestData {
if data.valid {
parts := strings.Split(data.tokenString, ".")
method := jwt.GetSigningMethod(data.alg)
sig, err := method.Sign(strings.Join(parts[0:2], "."), hmacTestKey)
if err != nil {
t.Errorf("[%v] Error signing token: %v", data.name, err)
}
if sig != parts[2] {
t.Errorf("[%v] Incorrect signature.\nwas:\n%v\nexpecting:\n%v", data.name, sig, parts[2])
}
}
}
}
func BenchmarkHS256Signing(b *testing.B) {
benchmarkSigning(b, jwt.SigningMethodHS256, hmacTestKey)
}
func BenchmarkHS384Signing(b *testing.B) {
benchmarkSigning(b, jwt.SigningMethodHS384, hmacTestKey)
}
func BenchmarkHS512Signing(b *testing.B) {
benchmarkSigning(b, jwt.SigningMethodHS512, hmacTestKey)
}

View File

@ -1,216 +0,0 @@
package jwt_test
// Example HTTP auth using asymmetric crypto/RSA keys
// This is based on a (now outdated) example at https://gist.github.com/cryptix/45c33ecf0ae54828e63b
import (
"bytes"
"crypto/rsa"
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/dgrijalva/jwt-go/request"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"net/url"
"strings"
"time"
)
// location of the files used for signing and verification
const (
privKeyPath = "test/sample_key" // openssl genrsa -out app.rsa keysize
pubKeyPath = "test/sample_key.pub" // openssl rsa -in app.rsa -pubout > app.rsa.pub
)
var (
verifyKey *rsa.PublicKey
signKey *rsa.PrivateKey
serverPort int
// storing sample username/password pairs
// don't do this on a real server
users = map[string]string{
"test": "known",
}
)
// read the key files before starting http handlers
func init() {
signBytes, err := ioutil.ReadFile(privKeyPath)
fatal(err)
signKey, err = jwt.ParseRSAPrivateKeyFromPEM(signBytes)
fatal(err)
verifyBytes, err := ioutil.ReadFile(pubKeyPath)
fatal(err)
verifyKey, err = jwt.ParseRSAPublicKeyFromPEM(verifyBytes)
fatal(err)
http.HandleFunc("/authenticate", authHandler)
http.HandleFunc("/restricted", restrictedHandler)
// Setup listener
listener, err := net.ListenTCP("tcp", &net.TCPAddr{})
serverPort = listener.Addr().(*net.TCPAddr).Port
log.Println("Listening...")
go func() {
fatal(http.Serve(listener, nil))
}()
}
var start func()
func fatal(err error) {
if err != nil {
log.Fatal(err)
}
}
// Define some custom types were going to use within our tokens
type CustomerInfo struct {
Name string
Kind string
}
type CustomClaimsExample struct {
*jwt.StandardClaims
TokenType string
CustomerInfo
}
func Example_getTokenViaHTTP() {
// See func authHandler for an example auth handler that produces a token
res, err := http.PostForm(fmt.Sprintf("http://localhost:%v/authenticate", serverPort), url.Values{
"user": {"test"},
"pass": {"known"},
})
if err != nil {
fatal(err)
}
if res.StatusCode != 200 {
fmt.Println("Unexpected status code", res.StatusCode)
}
// Read the token out of the response body
buf := new(bytes.Buffer)
io.Copy(buf, res.Body)
res.Body.Close()
tokenString := strings.TrimSpace(buf.String())
// Parse the token
token, err := jwt.ParseWithClaims(tokenString, &CustomClaimsExample{}, func(token *jwt.Token) (interface{}, error) {
// since we only use the one private key to sign the tokens,
// we also only use its public counter part to verify
return verifyKey, nil
})
fatal(err)
claims := token.Claims.(*CustomClaimsExample)
fmt.Println(claims.CustomerInfo.Name)
//Output: test
}
func Example_useTokenViaHTTP() {
// Make a sample token
// In a real world situation, this token will have been acquired from
// some other API call (see Example_getTokenViaHTTP)
token, err := createToken("foo")
fatal(err)
// Make request. See func restrictedHandler for example request processor
req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost:%v/restricted", serverPort), nil)
fatal(err)
req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", token))
res, err := http.DefaultClient.Do(req)
fatal(err)
// Read the response body
buf := new(bytes.Buffer)
io.Copy(buf, res.Body)
res.Body.Close()
fmt.Println(buf.String())
// Output: Welcome, foo
}
func createToken(user string) (string, error) {
// create a signer for rsa 256
t := jwt.New(jwt.GetSigningMethod("RS256"))
// set our claims
t.Claims = &CustomClaimsExample{
&jwt.StandardClaims{
// set the expire time
// see http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-20#section-4.1.4
ExpiresAt: time.Now().Add(time.Minute * 1).Unix(),
},
"level1",
CustomerInfo{user, "human"},
}
// Creat token string
return t.SignedString(signKey)
}
// reads the form values, checks them and creates the token
func authHandler(w http.ResponseWriter, r *http.Request) {
// make sure its post
if r.Method != "POST" {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintln(w, "No POST", r.Method)
return
}
user := r.FormValue("user")
pass := r.FormValue("pass")
log.Printf("Authenticate: user[%s] pass[%s]\n", user, pass)
// check values
if user != "test" || pass != "known" {
w.WriteHeader(http.StatusForbidden)
fmt.Fprintln(w, "Wrong info")
return
}
tokenString, err := createToken(user)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintln(w, "Sorry, error while Signing Token!")
log.Printf("Token Signing error: %v\n", err)
return
}
w.Header().Set("Content-Type", "application/jwt")
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, tokenString)
}
// only accessible with a valid token
func restrictedHandler(w http.ResponseWriter, r *http.Request) {
// Get token from request
token, err := request.ParseFromRequestWithClaims(r, request.OAuth2Extractor, &CustomClaimsExample{}, func(token *jwt.Token) (interface{}, error) {
// since we only use the one private key to sign the tokens,
// we also only use its public counter part to verify
return verifyKey, nil
})
// If the token is missing or invalid, return error
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprintln(w, "Invalid token:", err)
return
}
// Token is valid
fmt.Fprintln(w, "Welcome,", token.Claims.(*CustomClaimsExample).Name)
return
}

View File

@ -1,72 +0,0 @@
package jwt_test
import (
"github.com/dgrijalva/jwt-go"
"strings"
"testing"
)
var noneTestData = []struct {
name string
tokenString string
alg string
key interface{}
claims map[string]interface{}
valid bool
}{
{
"Basic",
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.",
"none",
jwt.UnsafeAllowNoneSignatureType,
map[string]interface{}{"foo": "bar"},
true,
},
{
"Basic - no key",
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.",
"none",
nil,
map[string]interface{}{"foo": "bar"},
false,
},
{
"Signed",
"eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.W-jEzRfBigtCWsinvVVuldiuilzVdU5ty0MvpLaSaqK9PlAWWlDQ1VIQ_qSKzwL5IXaZkvZFJXT3yL3n7OUVu7zCNJzdwznbC8Z-b0z2lYvcklJYi2VOFRcGbJtXUqgjk2oGsiqUMUMOLP70TTefkpsgqDxbRh9CDUfpOJgW-dU7cmgaoswe3wjUAUi6B6G2YEaiuXC0XScQYSYVKIzgKXJV8Zw-7AN_DBUI4GkTpsvQ9fVVjZM9csQiEXhYekyrKu1nu_POpQonGd8yqkIyXPECNmmqH5jH4sFiF67XhD7_JpkvLziBpI-uh86evBUadmHhb9Otqw3uV3NTaXLzJw",
"none",
jwt.UnsafeAllowNoneSignatureType,
map[string]interface{}{"foo": "bar"},
false,
},
}
func TestNoneVerify(t *testing.T) {
for _, data := range noneTestData {
parts := strings.Split(data.tokenString, ".")
method := jwt.GetSigningMethod(data.alg)
err := method.Verify(strings.Join(parts[0:2], "."), parts[2], data.key)
if data.valid && err != nil {
t.Errorf("[%v] Error while verifying key: %v", data.name, err)
}
if !data.valid && err == nil {
t.Errorf("[%v] Invalid key passed validation", data.name)
}
}
}
func TestNoneSign(t *testing.T) {
for _, data := range noneTestData {
if data.valid {
parts := strings.Split(data.tokenString, ".")
method := jwt.GetSigningMethod(data.alg)
sig, err := method.Sign(strings.Join(parts[0:2], "."), data.key)
if err != nil {
t.Errorf("[%v] Error signing token: %v", data.name, err)
}
if sig != parts[2] {
t.Errorf("[%v] Incorrect signature.\nwas:\n%v\nexpecting:\n%v", data.name, sig, parts[2])
}
}
}
}

View File

@ -1,261 +0,0 @@
package jwt_test
import (
"crypto/rsa"
"encoding/json"
"fmt"
"reflect"
"testing"
"time"
"github.com/dgrijalva/jwt-go"
"github.com/dgrijalva/jwt-go/test"
)
var keyFuncError error = fmt.Errorf("error loading key")
var (
jwtTestDefaultKey *rsa.PublicKey
defaultKeyFunc jwt.Keyfunc = func(t *jwt.Token) (interface{}, error) { return jwtTestDefaultKey, nil }
emptyKeyFunc jwt.Keyfunc = func(t *jwt.Token) (interface{}, error) { return nil, nil }
errorKeyFunc jwt.Keyfunc = func(t *jwt.Token) (interface{}, error) { return nil, keyFuncError }
nilKeyFunc jwt.Keyfunc = nil
)
func init() {
jwtTestDefaultKey = test.LoadRSAPublicKeyFromDisk("test/sample_key.pub")
}
var jwtTestData = []struct {
name string
tokenString string
keyfunc jwt.Keyfunc
claims jwt.Claims
valid bool
errors uint32
parser *jwt.Parser
}{
{
"basic",
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
defaultKeyFunc,
jwt.MapClaims{"foo": "bar"},
true,
0,
nil,
},
{
"basic expired",
"", // autogen
defaultKeyFunc,
jwt.MapClaims{"foo": "bar", "exp": float64(time.Now().Unix() - 100)},
false,
jwt.ValidationErrorExpired,
nil,
},
{
"basic nbf",
"", // autogen
defaultKeyFunc,
jwt.MapClaims{"foo": "bar", "nbf": float64(time.Now().Unix() + 100)},
false,
jwt.ValidationErrorNotValidYet,
nil,
},
{
"expired and nbf",
"", // autogen
defaultKeyFunc,
jwt.MapClaims{"foo": "bar", "nbf": float64(time.Now().Unix() + 100), "exp": float64(time.Now().Unix() - 100)},
false,
jwt.ValidationErrorNotValidYet | jwt.ValidationErrorExpired,
nil,
},
{
"basic invalid",
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.EhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
defaultKeyFunc,
jwt.MapClaims{"foo": "bar"},
false,
jwt.ValidationErrorSignatureInvalid,
nil,
},
{
"basic nokeyfunc",
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
nilKeyFunc,
jwt.MapClaims{"foo": "bar"},
false,
jwt.ValidationErrorUnverifiable,
nil,
},
{
"basic nokey",
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
emptyKeyFunc,
jwt.MapClaims{"foo": "bar"},
false,
jwt.ValidationErrorSignatureInvalid,
nil,
},
{
"basic errorkey",
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
errorKeyFunc,
jwt.MapClaims{"foo": "bar"},
false,
jwt.ValidationErrorUnverifiable,
nil,
},
{
"invalid signing method",
"",
defaultKeyFunc,
jwt.MapClaims{"foo": "bar"},
false,
jwt.ValidationErrorSignatureInvalid,
&jwt.Parser{ValidMethods: []string{"HS256"}},
},
{
"valid signing method",
"",
defaultKeyFunc,
jwt.MapClaims{"foo": "bar"},
true,
0,
&jwt.Parser{ValidMethods: []string{"RS256", "HS256"}},
},
{
"JSON Number",
"",
defaultKeyFunc,
jwt.MapClaims{"foo": json.Number("123.4")},
true,
0,
&jwt.Parser{UseJSONNumber: true},
},
{
"Standard Claims",
"",
defaultKeyFunc,
&jwt.StandardClaims{
ExpiresAt: time.Now().Add(time.Second * 10).Unix(),
},
true,
0,
&jwt.Parser{UseJSONNumber: true},
},
{
"JSON Number - basic expired",
"", // autogen
defaultKeyFunc,
jwt.MapClaims{"foo": "bar", "exp": json.Number(fmt.Sprintf("%v", time.Now().Unix()-100))},
false,
jwt.ValidationErrorExpired,
&jwt.Parser{UseJSONNumber: true},
},
{
"JSON Number - basic nbf",
"", // autogen
defaultKeyFunc,
jwt.MapClaims{"foo": "bar", "nbf": json.Number(fmt.Sprintf("%v", time.Now().Unix()+100))},
false,
jwt.ValidationErrorNotValidYet,
&jwt.Parser{UseJSONNumber: true},
},
{
"JSON Number - expired and nbf",
"", // autogen
defaultKeyFunc,
jwt.MapClaims{"foo": "bar", "nbf": json.Number(fmt.Sprintf("%v", time.Now().Unix()+100)), "exp": json.Number(fmt.Sprintf("%v", time.Now().Unix()-100))},
false,
jwt.ValidationErrorNotValidYet | jwt.ValidationErrorExpired,
&jwt.Parser{UseJSONNumber: true},
},
{
"SkipClaimsValidation during token parsing",
"", // autogen
defaultKeyFunc,
jwt.MapClaims{"foo": "bar", "nbf": json.Number(fmt.Sprintf("%v", time.Now().Unix()+100))},
true,
0,
&jwt.Parser{UseJSONNumber: true, SkipClaimsValidation: true},
},
}
func TestParser_Parse(t *testing.T) {
privateKey := test.LoadRSAPrivateKeyFromDisk("test/sample_key")
// Iterate over test data set and run tests
for _, data := range jwtTestData {
// If the token string is blank, use helper function to generate string
if data.tokenString == "" {
data.tokenString = test.MakeSampleToken(data.claims, privateKey)
}
// Parse the token
var token *jwt.Token
var err error
var parser = data.parser
if parser == nil {
parser = new(jwt.Parser)
}
// Figure out correct claims type
switch data.claims.(type) {
case jwt.MapClaims:
token, err = parser.ParseWithClaims(data.tokenString, jwt.MapClaims{}, data.keyfunc)
case *jwt.StandardClaims:
token, err = parser.ParseWithClaims(data.tokenString, &jwt.StandardClaims{}, data.keyfunc)
}
// Verify result matches expectation
if !reflect.DeepEqual(data.claims, token.Claims) {
t.Errorf("[%v] Claims mismatch. Expecting: %v Got: %v", data.name, data.claims, token.Claims)
}
if data.valid && err != nil {
t.Errorf("[%v] Error while verifying token: %T:%v", data.name, err, err)
}
if !data.valid && err == nil {
t.Errorf("[%v] Invalid token passed validation", data.name)
}
if (err == nil && !token.Valid) || (err != nil && token.Valid) {
t.Errorf("[%v] Inconsistent behavior between returned error and token.Valid")
}
if data.errors != 0 {
if err == nil {
t.Errorf("[%v] Expecting error. Didn't get one.", data.name)
} else {
ve := err.(*jwt.ValidationError)
// compare the bitfield part of the error
if e := ve.Errors; e != data.errors {
t.Errorf("[%v] Errors don't match expectation. %v != %v", data.name, e, data.errors)
}
if err.Error() == keyFuncError.Error() && ve.Inner != keyFuncError {
t.Errorf("[%v] Inner error does not match expectation. %v != %v", data.name, ve.Inner, keyFuncError)
}
}
}
if data.valid && token.Signature == "" {
t.Errorf("[%v] Signature is left unpopulated after parsing", data.name)
}
}
}
// Helper method for benchmarking various methods
func benchmarkSigning(b *testing.B, method jwt.SigningMethod, key interface{}) {
t := jwt.New(method)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if _, err := t.SignedString(key); err != nil {
b.Fatal(err)
}
}
})
}

View File

@ -1,7 +0,0 @@
// Utility package for extracting JWT tokens from
// HTTP requests.
//
// The main function is ParseFromRequest and it's WithClaims variant.
// See examples for how to use the various Extractor implementations
// or roll your own.
package request

View File

@ -1,81 +0,0 @@
package request
import (
"errors"
"net/http"
)
// Errors
var (
ErrNoTokenInRequest = errors.New("no token present in request")
)
// Interface for extracting a token from an HTTP request.
// The ExtractToken method should return a token string or an error.
// If no token is present, you must return ErrNoTokenInRequest.
type Extractor interface {
ExtractToken(*http.Request) (string, error)
}
// Extractor for finding a token in a header. Looks at each specified
// header in order until there's a match
type HeaderExtractor []string
func (e HeaderExtractor) ExtractToken(req *http.Request) (string, error) {
// loop over header names and return the first one that contains data
for _, header := range e {
if ah := req.Header.Get(header); ah != "" {
return ah, nil
}
}
return "", ErrNoTokenInRequest
}
// Extract token from request arguments. This includes a POSTed form or
// GET URL arguments. Argument names are tried in order until there's a match.
// This extractor calls `ParseMultipartForm` on the request
type ArgumentExtractor []string
func (e ArgumentExtractor) ExtractToken(req *http.Request) (string, error) {
// Make sure form is parsed
req.ParseMultipartForm(10e6)
// loop over arg names and return the first one that contains data
for _, arg := range e {
if ah := req.Form.Get(arg); ah != "" {
return ah, nil
}
}
return "", ErrNoTokenInRequest
}
// Tries Extractors in order until one returns a token string or an error occurs
type MultiExtractor []Extractor
func (e MultiExtractor) ExtractToken(req *http.Request) (string, error) {
// loop over header names and return the first one that contains data
for _, extractor := range e {
if tok, err := extractor.ExtractToken(req); tok != "" {
return tok, nil
} else if err != ErrNoTokenInRequest {
return "", err
}
}
return "", ErrNoTokenInRequest
}
// Wrap an Extractor in this to post-process the value before it's handed off.
// See AuthorizationHeaderExtractor for an example
type PostExtractionFilter struct {
Extractor
Filter func(string) (string, error)
}
func (e *PostExtractionFilter) ExtractToken(req *http.Request) (string, error) {
if tok, err := e.Extractor.ExtractToken(req); tok != "" {
return e.Filter(tok)
} else {
return "", err
}
}

View File

@ -1,32 +0,0 @@
package request
import (
"fmt"
"net/url"
)
const (
exampleTokenA = "A"
)
func ExampleHeaderExtractor() {
req := makeExampleRequest("GET", "/", map[string]string{"Token": exampleTokenA}, nil)
tokenString, err := HeaderExtractor{"Token"}.ExtractToken(req)
if err == nil {
fmt.Println(tokenString)
} else {
fmt.Println(err)
}
//Output: A
}
func ExampleArgumentExtractor() {
req := makeExampleRequest("GET", "/", nil, url.Values{"token": {extractorTestTokenA}})
tokenString, err := ArgumentExtractor{"token"}.ExtractToken(req)
if err == nil {
fmt.Println(tokenString)
} else {
fmt.Println(err)
}
//Output: A
}

View File

@ -1,91 +0,0 @@
package request
import (
"fmt"
"net/http"
"net/url"
"testing"
)
var extractorTestTokenA = "A"
var extractorTestTokenB = "B"
var extractorTestData = []struct {
name string
extractor Extractor
headers map[string]string
query url.Values
token string
err error
}{
{
name: "simple header",
extractor: HeaderExtractor{"Foo"},
headers: map[string]string{"Foo": extractorTestTokenA},
query: nil,
token: extractorTestTokenA,
err: nil,
},
{
name: "simple argument",
extractor: ArgumentExtractor{"token"},
headers: map[string]string{},
query: url.Values{"token": {extractorTestTokenA}},
token: extractorTestTokenA,
err: nil,
},
{
name: "multiple extractors",
extractor: MultiExtractor{
HeaderExtractor{"Foo"},
ArgumentExtractor{"token"},
},
headers: map[string]string{"Foo": extractorTestTokenA},
query: url.Values{"token": {extractorTestTokenB}},
token: extractorTestTokenA,
err: nil,
},
{
name: "simple miss",
extractor: HeaderExtractor{"This-Header-Is-Not-Set"},
headers: map[string]string{"Foo": extractorTestTokenA},
query: nil,
token: "",
err: ErrNoTokenInRequest,
},
{
name: "filter",
extractor: AuthorizationHeaderExtractor,
headers: map[string]string{"Authorization": "Bearer " + extractorTestTokenA},
query: nil,
token: extractorTestTokenA,
err: nil,
},
}
func TestExtractor(t *testing.T) {
// Bearer token request
for _, data := range extractorTestData {
// Make request from test struct
r := makeExampleRequest("GET", "/", data.headers, data.query)
// Test extractor
token, err := data.extractor.ExtractToken(r)
if token != data.token {
t.Errorf("[%v] Expected token '%v'. Got '%v'", data.name, data.token, token)
continue
}
if err != data.err {
t.Errorf("[%v] Expected error '%v'. Got '%v'", data.name, data.err, err)
continue
}
}
}
func makeExampleRequest(method, path string, headers map[string]string, urlArgs url.Values) *http.Request {
r, _ := http.NewRequest(method, fmt.Sprintf("%v?%v", path, urlArgs.Encode()), nil)
for k, v := range headers {
r.Header.Set(k, v)
}
return r
}

View File

@ -1,28 +0,0 @@
package request
import (
"strings"
)
// Strips 'Bearer ' prefix from bearer token string
func stripBearerPrefixFromTokenString(tok string) (string, error) {
// Should be a bearer token
if len(tok) > 6 && strings.ToUpper(tok[0:7]) == "BEARER " {
return tok[7:], nil
}
return tok, nil
}
// Extract bearer token from Authorization header
// Uses PostExtractionFilter to strip "Bearer " prefix from header
var AuthorizationHeaderExtractor = &PostExtractionFilter{
HeaderExtractor{"Authorization"},
stripBearerPrefixFromTokenString,
}
// Extractor for OAuth2 access tokens. Looks in 'Authorization'
// header then 'access_token' argument for a token.
var OAuth2Extractor = &MultiExtractor{
AuthorizationHeaderExtractor,
ArgumentExtractor{"access_token"},
}

View File

@ -1,24 +0,0 @@
package request
import (
"github.com/dgrijalva/jwt-go"
"net/http"
)
// Extract and parse a JWT token from an HTTP request.
// This behaves the same as Parse, but accepts a request and an extractor
// instead of a token string. The Extractor interface allows you to define
// the logic for extracting a token. Several useful implementations are provided.
func ParseFromRequest(req *http.Request, extractor Extractor, keyFunc jwt.Keyfunc) (token *jwt.Token, err error) {
return ParseFromRequestWithClaims(req, extractor, jwt.MapClaims{}, keyFunc)
}
// ParseFromRequest but with custom Claims type
func ParseFromRequestWithClaims(req *http.Request, extractor Extractor, claims jwt.Claims, keyFunc jwt.Keyfunc) (token *jwt.Token, err error) {
// Extract token from request
if tokStr, err := extractor.ExtractToken(req); err == nil {
return jwt.ParseWithClaims(tokStr, claims, keyFunc)
} else {
return nil, err
}
}

View File

@ -1,103 +0,0 @@
package request
import (
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/dgrijalva/jwt-go/test"
"net/http"
"net/url"
"reflect"
"strings"
"testing"
)
var requestTestData = []struct {
name string
claims jwt.MapClaims
extractor Extractor
headers map[string]string
query url.Values
valid bool
}{
{
"authorization bearer token",
jwt.MapClaims{"foo": "bar"},
AuthorizationHeaderExtractor,
map[string]string{"Authorization": "Bearer %v"},
url.Values{},
true,
},
{
"oauth bearer token - header",
jwt.MapClaims{"foo": "bar"},
OAuth2Extractor,
map[string]string{"Authorization": "Bearer %v"},
url.Values{},
true,
},
{
"oauth bearer token - url",
jwt.MapClaims{"foo": "bar"},
OAuth2Extractor,
map[string]string{},
url.Values{"access_token": {"%v"}},
true,
},
{
"url token",
jwt.MapClaims{"foo": "bar"},
ArgumentExtractor{"token"},
map[string]string{},
url.Values{"token": {"%v"}},
true,
},
}
func TestParseRequest(t *testing.T) {
// load keys from disk
privateKey := test.LoadRSAPrivateKeyFromDisk("../test/sample_key")
publicKey := test.LoadRSAPublicKeyFromDisk("../test/sample_key.pub")
keyfunc := func(*jwt.Token) (interface{}, error) {
return publicKey, nil
}
// Bearer token request
for _, data := range requestTestData {
// Make token from claims
tokenString := test.MakeSampleToken(data.claims, privateKey)
// Make query string
for k, vv := range data.query {
for i, v := range vv {
if strings.Contains(v, "%v") {
data.query[k][i] = fmt.Sprintf(v, tokenString)
}
}
}
// Make request from test struct
r, _ := http.NewRequest("GET", fmt.Sprintf("/?%v", data.query.Encode()), nil)
for k, v := range data.headers {
if strings.Contains(v, "%v") {
r.Header.Set(k, fmt.Sprintf(v, tokenString))
} else {
r.Header.Set(k, tokenString)
}
}
token, err := ParseFromRequestWithClaims(r, data.extractor, jwt.MapClaims{}, keyfunc)
if token == nil {
t.Errorf("[%v] Token was not found: %v", data.name, err)
continue
}
if !reflect.DeepEqual(data.claims, token.Claims) {
t.Errorf("[%v] Claims mismatch. Expecting: %v Got: %v", data.name, data.claims, token.Claims)
}
if data.valid && err != nil {
t.Errorf("[%v] Error while verifying token: %v", data.name, err)
}
if !data.valid && err == nil {
t.Errorf("[%v] Invalid token passed validation", data.name)
}
}
}

View File

@ -1,96 +0,0 @@
// +build go1.4
package jwt_test
import (
"crypto/rsa"
"io/ioutil"
"strings"
"testing"
"github.com/dgrijalva/jwt-go"
)
var rsaPSSTestData = []struct {
name string
tokenString string
alg string
claims map[string]interface{}
valid bool
}{
{
"Basic PS256",
"eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.PPG4xyDVY8ffp4CcxofNmsTDXsrVG2npdQuibLhJbv4ClyPTUtR5giNSvuxo03kB6I8VXVr0Y9X7UxhJVEoJOmULAwRWaUsDnIewQa101cVhMa6iR8X37kfFoiZ6NkS-c7henVkkQWu2HtotkEtQvN5hFlk8IevXXPmvZlhQhwzB1sGzGYnoi1zOfuL98d3BIjUjtlwii5w6gYG2AEEzp7HnHCsb3jIwUPdq86Oe6hIFjtBwduIK90ca4UqzARpcfwxHwVLMpatKask00AgGVI0ysdk0BLMjmLutquD03XbThHScC2C2_Pp4cHWgMzvbgLU2RYYZcZRKr46QeNgz9w",
"PS256",
map[string]interface{}{"foo": "bar"},
true,
},
{
"Basic PS384",
"eyJhbGciOiJQUzM4NCIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.w7-qqgj97gK4fJsq_DCqdYQiylJjzWONvD0qWWWhqEOFk2P1eDULPnqHRnjgTXoO4HAw4YIWCsZPet7nR3Xxq4ZhMqvKW8b7KlfRTb9cH8zqFvzMmybQ4jv2hKc3bXYqVow3AoR7hN_CWXI3Dv6Kd2X5xhtxRHI6IL39oTVDUQ74LACe-9t4c3QRPuj6Pq1H4FAT2E2kW_0KOc6EQhCLWEhm2Z2__OZskDC8AiPpP8Kv4k2vB7l0IKQu8Pr4RcNBlqJdq8dA5D3hk5TLxP8V5nG1Ib80MOMMqoS3FQvSLyolFX-R_jZ3-zfq6Ebsqr0yEb0AH2CfsECF7935Pa0FKQ",
"PS384",
map[string]interface{}{"foo": "bar"},
true,
},
{
"Basic PS512",
"eyJhbGciOiJQUzUxMiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.GX1HWGzFaJevuSLavqqFYaW8_TpvcjQ8KfC5fXiSDzSiT9UD9nB_ikSmDNyDILNdtjZLSvVKfXxZJqCfefxAtiozEDDdJthZ-F0uO4SPFHlGiXszvKeodh7BuTWRI2wL9-ZO4mFa8nq3GMeQAfo9cx11i7nfN8n2YNQ9SHGovG7_T_AvaMZB_jT6jkDHpwGR9mz7x1sycckEo6teLdHRnH_ZdlHlxqknmyTu8Odr5Xh0sJFOL8BepWbbvIIn-P161rRHHiDWFv6nhlHwZnVzjx7HQrWSGb6-s2cdLie9QL_8XaMcUpjLkfOMKkDOfHo6AvpL7Jbwi83Z2ZTHjJWB-A",
"PS512",
map[string]interface{}{"foo": "bar"},
true,
},
{
"basic PS256 invalid: foo => bar",
"eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.PPG4xyDVY8ffp4CcxofNmsTDXsrVG2npdQuibLhJbv4ClyPTUtR5giNSvuxo03kB6I8VXVr0Y9X7UxhJVEoJOmULAwRWaUsDnIewQa101cVhMa6iR8X37kfFoiZ6NkS-c7henVkkQWu2HtotkEtQvN5hFlk8IevXXPmvZlhQhwzB1sGzGYnoi1zOfuL98d3BIjUjtlwii5w6gYG2AEEzp7HnHCsb3jIwUPdq86Oe6hIFjtBwduIK90ca4UqzARpcfwxHwVLMpatKask00AgGVI0ysdk0BLMjmLutquD03XbThHScC2C2_Pp4cHWgMzvbgLU2RYYZcZRKr46QeNgz9W",
"PS256",
map[string]interface{}{"foo": "bar"},
false,
},
}
func TestRSAPSSVerify(t *testing.T) {
var err error
key, _ := ioutil.ReadFile("test/sample_key.pub")
var rsaPSSKey *rsa.PublicKey
if rsaPSSKey, err = jwt.ParseRSAPublicKeyFromPEM(key); err != nil {
t.Errorf("Unable to parse RSA public key: %v", err)
}
for _, data := range rsaPSSTestData {
parts := strings.Split(data.tokenString, ".")
method := jwt.GetSigningMethod(data.alg)
err := method.Verify(strings.Join(parts[0:2], "."), parts[2], rsaPSSKey)
if data.valid && err != nil {
t.Errorf("[%v] Error while verifying key: %v", data.name, err)
}
if !data.valid && err == nil {
t.Errorf("[%v] Invalid key passed validation", data.name)
}
}
}
func TestRSAPSSSign(t *testing.T) {
var err error
key, _ := ioutil.ReadFile("test/sample_key")
var rsaPSSKey *rsa.PrivateKey
if rsaPSSKey, err = jwt.ParseRSAPrivateKeyFromPEM(key); err != nil {
t.Errorf("Unable to parse RSA private key: %v", err)
}
for _, data := range rsaPSSTestData {
if data.valid {
parts := strings.Split(data.tokenString, ".")
method := jwt.GetSigningMethod(data.alg)
sig, err := method.Sign(strings.Join(parts[0:2], "."), rsaPSSKey)
if err != nil {
t.Errorf("[%v] Error signing token: %v", data.name, err)
}
if sig == parts[2] {
t.Errorf("[%v] Signatures shouldn't match\nnew:\n%v\noriginal:\n%v", data.name, sig, parts[2])
}
}
}
}

View File

@ -1,176 +0,0 @@
package jwt_test
import (
"github.com/dgrijalva/jwt-go"
"io/ioutil"
"strings"
"testing"
)
var rsaTestData = []struct {
name string
tokenString string
alg string
claims map[string]interface{}
valid bool
}{
{
"Basic RS256",
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
"RS256",
map[string]interface{}{"foo": "bar"},
true,
},
{
"Basic RS384",
"eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.W-jEzRfBigtCWsinvVVuldiuilzVdU5ty0MvpLaSaqK9PlAWWlDQ1VIQ_qSKzwL5IXaZkvZFJXT3yL3n7OUVu7zCNJzdwznbC8Z-b0z2lYvcklJYi2VOFRcGbJtXUqgjk2oGsiqUMUMOLP70TTefkpsgqDxbRh9CDUfpOJgW-dU7cmgaoswe3wjUAUi6B6G2YEaiuXC0XScQYSYVKIzgKXJV8Zw-7AN_DBUI4GkTpsvQ9fVVjZM9csQiEXhYekyrKu1nu_POpQonGd8yqkIyXPECNmmqH5jH4sFiF67XhD7_JpkvLziBpI-uh86evBUadmHhb9Otqw3uV3NTaXLzJw",
"RS384",
map[string]interface{}{"foo": "bar"},
true,
},
{
"Basic RS512",
"eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.zBlLlmRrUxx4SJPUbV37Q1joRcI9EW13grnKduK3wtYKmDXbgDpF1cZ6B-2Jsm5RB8REmMiLpGms-EjXhgnyh2TSHE-9W2gA_jvshegLWtwRVDX40ODSkTb7OVuaWgiy9y7llvcknFBTIg-FnVPVpXMmeV_pvwQyhaz1SSwSPrDyxEmksz1hq7YONXhXPpGaNbMMeDTNP_1oj8DZaqTIL9TwV8_1wb2Odt_Fy58Ke2RVFijsOLdnyEAjt2n9Mxihu9i3PhNBkkxa2GbnXBfq3kzvZ_xxGGopLdHhJjcGWXO-NiwI9_tiu14NRv4L2xC0ItD9Yz68v2ZIZEp_DuzwRQ",
"RS512",
map[string]interface{}{"foo": "bar"},
true,
},
{
"basic invalid: foo => bar",
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.EhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
"RS256",
map[string]interface{}{"foo": "bar"},
false,
},
}
func TestRSAVerify(t *testing.T) {
keyData, _ := ioutil.ReadFile("test/sample_key.pub")
key, _ := jwt.ParseRSAPublicKeyFromPEM(keyData)
for _, data := range rsaTestData {
parts := strings.Split(data.tokenString, ".")
method := jwt.GetSigningMethod(data.alg)
err := method.Verify(strings.Join(parts[0:2], "."), parts[2], key)
if data.valid && err != nil {
t.Errorf("[%v] Error while verifying key: %v", data.name, err)
}
if !data.valid && err == nil {
t.Errorf("[%v] Invalid key passed validation", data.name)
}
}
}
func TestRSASign(t *testing.T) {
keyData, _ := ioutil.ReadFile("test/sample_key")
key, _ := jwt.ParseRSAPrivateKeyFromPEM(keyData)
for _, data := range rsaTestData {
if data.valid {
parts := strings.Split(data.tokenString, ".")
method := jwt.GetSigningMethod(data.alg)
sig, err := method.Sign(strings.Join(parts[0:2], "."), key)
if err != nil {
t.Errorf("[%v] Error signing token: %v", data.name, err)
}
if sig != parts[2] {
t.Errorf("[%v] Incorrect signature.\nwas:\n%v\nexpecting:\n%v", data.name, sig, parts[2])
}
}
}
}
func TestRSAVerifyWithPreParsedPrivateKey(t *testing.T) {
key, _ := ioutil.ReadFile("test/sample_key.pub")
parsedKey, err := jwt.ParseRSAPublicKeyFromPEM(key)
if err != nil {
t.Fatal(err)
}
testData := rsaTestData[0]
parts := strings.Split(testData.tokenString, ".")
err = jwt.SigningMethodRS256.Verify(strings.Join(parts[0:2], "."), parts[2], parsedKey)
if err != nil {
t.Errorf("[%v] Error while verifying key: %v", testData.name, err)
}
}
func TestRSAWithPreParsedPrivateKey(t *testing.T) {
key, _ := ioutil.ReadFile("test/sample_key")
parsedKey, err := jwt.ParseRSAPrivateKeyFromPEM(key)
if err != nil {
t.Fatal(err)
}
testData := rsaTestData[0]
parts := strings.Split(testData.tokenString, ".")
sig, err := jwt.SigningMethodRS256.Sign(strings.Join(parts[0:2], "."), parsedKey)
if err != nil {
t.Errorf("[%v] Error signing token: %v", testData.name, err)
}
if sig != parts[2] {
t.Errorf("[%v] Incorrect signature.\nwas:\n%v\nexpecting:\n%v", testData.name, sig, parts[2])
}
}
func TestRSAKeyParsing(t *testing.T) {
key, _ := ioutil.ReadFile("test/sample_key")
pubKey, _ := ioutil.ReadFile("test/sample_key.pub")
badKey := []byte("All your base are belong to key")
// Test parsePrivateKey
if _, e := jwt.ParseRSAPrivateKeyFromPEM(key); e != nil {
t.Errorf("Failed to parse valid private key: %v", e)
}
if k, e := jwt.ParseRSAPrivateKeyFromPEM(pubKey); e == nil {
t.Errorf("Parsed public key as valid private key: %v", k)
}
if k, e := jwt.ParseRSAPrivateKeyFromPEM(badKey); e == nil {
t.Errorf("Parsed invalid key as valid private key: %v", k)
}
// Test parsePublicKey
if _, e := jwt.ParseRSAPublicKeyFromPEM(pubKey); e != nil {
t.Errorf("Failed to parse valid public key: %v", e)
}
if k, e := jwt.ParseRSAPublicKeyFromPEM(key); e == nil {
t.Errorf("Parsed private key as valid public key: %v", k)
}
if k, e := jwt.ParseRSAPublicKeyFromPEM(badKey); e == nil {
t.Errorf("Parsed invalid key as valid private key: %v", k)
}
}
func BenchmarkRS256Signing(b *testing.B) {
key, _ := ioutil.ReadFile("test/sample_key")
parsedKey, err := jwt.ParseRSAPrivateKeyFromPEM(key)
if err != nil {
b.Fatal(err)
}
benchmarkSigning(b, jwt.SigningMethodRS256, parsedKey)
}
func BenchmarkRS384Signing(b *testing.B) {
key, _ := ioutil.ReadFile("test/sample_key")
parsedKey, err := jwt.ParseRSAPrivateKeyFromPEM(key)
if err != nil {
b.Fatal(err)
}
benchmarkSigning(b, jwt.SigningMethodRS384, parsedKey)
}
func BenchmarkRS512Signing(b *testing.B) {
key, _ := ioutil.ReadFile("test/sample_key")
parsedKey, err := jwt.ParseRSAPrivateKeyFromPEM(key)
if err != nil {
b.Fatal(err)
}
benchmarkSigning(b, jwt.SigningMethodRS512, parsedKey)
}

View File

@ -1,5 +0,0 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIAh5qA3rmqQQuu0vbKV/+zouz/y/Iy2pLpIcWUSyImSwoAoGCCqGSM49
AwEHoUQDQgAEYD54V/vp+54P9DXarYqx4MPcm+HKRIQzNasYSoRQHQ/6S6Ps8tpM
cT+KvIIC8W/e9k0W7Cm72M1P9jU7SLf/vg==
-----END EC PRIVATE KEY-----

View File

@ -1,4 +0,0 @@
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYD54V/vp+54P9DXarYqx4MPcm+HK
RIQzNasYSoRQHQ/6S6Ps8tpMcT+KvIIC8W/e9k0W7Cm72M1P9jU7SLf/vg==
-----END PUBLIC KEY-----

View File

@ -1,6 +0,0 @@
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDCaCvMHKhcG/qT7xsNLYnDT7sE/D+TtWIol1ROdaK1a564vx5pHbsRy
SEKcIxISi1igBwYFK4EEACKhZANiAATYa7rJaU7feLMqrAx6adZFNQOpaUH/Uylb
ZLriOLON5YFVwtVUpO1FfEXZUIQpptRPtc5ixIPY658yhBSb6irfIJUSP9aYTflJ
GKk/mDkK4t8mWBzhiD5B6jg9cEGhGgA=
-----END EC PRIVATE KEY-----

View File

@ -1,5 +0,0 @@
-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE2Gu6yWlO33izKqwMemnWRTUDqWlB/1Mp
W2S64jizjeWBVcLVVKTtRXxF2VCEKabUT7XOYsSD2OufMoQUm+oq3yCVEj/WmE35
SRipP5g5CuLfJlgc4Yg+Qeo4PXBBoRoA
-----END PUBLIC KEY-----

View File

@ -1,7 +0,0 @@
-----BEGIN EC PRIVATE KEY-----
MIHcAgEBBEIB0pE4uFaWRx7t03BsYlYvF1YvKaBGyvoakxnodm9ou0R9wC+sJAjH
QZZJikOg4SwNqgQ/hyrOuDK2oAVHhgVGcYmgBwYFK4EEACOhgYkDgYYABAAJXIuw
12MUzpHggia9POBFYXSxaOGKGbMjIyDI+6q7wi7LMw3HgbaOmgIqFG72o8JBQwYN
4IbXHf+f86CRY1AA2wHzbHvt6IhkCXTNxBEffa1yMUgu8n9cKKF2iLgyQKcKqW33
8fGOw/n3Rm2Yd/EB56u2rnD29qS+nOM9eGS+gy39OQ==
-----END EC PRIVATE KEY-----

View File

@ -1,6 +0,0 @@
-----BEGIN PUBLIC KEY-----
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQACVyLsNdjFM6R4IImvTzgRWF0sWjh
ihmzIyMgyPuqu8IuyzMNx4G2jpoCKhRu9qPCQUMGDeCG1x3/n/OgkWNQANsB82x7
7eiIZAl0zcQRH32tcjFILvJ/XCihdoi4MkCnCqlt9/HxjsP590ZtmHfxAeertq5w
9vakvpzjPXhkvoMt/Tk=
-----END PUBLIC KEY-----

View File

@ -1,42 +0,0 @@
package test
import (
"crypto/rsa"
"github.com/dgrijalva/jwt-go"
"io/ioutil"
)
func LoadRSAPrivateKeyFromDisk(location string) *rsa.PrivateKey {
keyData, e := ioutil.ReadFile(location)
if e != nil {
panic(e.Error())
}
key, e := jwt.ParseRSAPrivateKeyFromPEM(keyData)
if e != nil {
panic(e.Error())
}
return key
}
func LoadRSAPublicKeyFromDisk(location string) *rsa.PublicKey {
keyData, e := ioutil.ReadFile(location)
if e != nil {
panic(e.Error())
}
key, e := jwt.ParseRSAPublicKeyFromPEM(keyData)
if e != nil {
panic(e.Error())
}
return key
}
func MakeSampleToken(c jwt.Claims, key interface{}) string {
token := jwt.NewWithClaims(jwt.SigningMethodRS256, c)
s, e := token.SignedString(key)
if e != nil {
panic(e.Error())
}
return s
}

View File

@ -1 +0,0 @@
#5K+・シミew{ヲ住ウ(跼Tノ(ゥ┫メP.ソモ燾辻G<>感テwb="=.!r.Oタヘ奎gミ€

View File

@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA4f5wg5l2hKsTeNem/V41fGnJm6gOdrj8ym3rFkEU/wT8RDtn
SgFEZOQpHEgQ7JL38xUfU0Y3g6aYw9QT0hJ7mCpz9Er5qLaMXJwZxzHzAahlfA0i
cqabvJOMvQtzD6uQv6wPEyZtDTWiQi9AXwBpHssPnpYGIn20ZZuNlX2BrClciHhC
PUIIZOQn/MmqTD31jSyjoQoV7MhhMTATKJx2XrHhR+1DcKJzQBSTAGnpYVaqpsAR
ap+nwRipr3nUTuxyGohBTSmjJ2usSeQXHI3bODIRe1AuTyHceAbewn8b462yEWKA
Rdpd9AjQW5SIVPfdsz5B6GlYQ5LdYKtznTuy7wIDAQABAoIBAQCwia1k7+2oZ2d3
n6agCAbqIE1QXfCmh41ZqJHbOY3oRQG3X1wpcGH4Gk+O+zDVTV2JszdcOt7E5dAy
MaomETAhRxB7hlIOnEN7WKm+dGNrKRvV0wDU5ReFMRHg31/Lnu8c+5BvGjZX+ky9
POIhFFYJqwCRlopGSUIxmVj5rSgtzk3iWOQXr+ah1bjEXvlxDOWkHN6YfpV5ThdE
KdBIPGEVqa63r9n2h+qazKrtiRqJqGnOrHzOECYbRFYhexsNFz7YT02xdfSHn7gM
IvabDDP/Qp0PjE1jdouiMaFHYnLBbgvlnZW9yuVf/rpXTUq/njxIXMmvmEyyvSDn
FcFikB8pAoGBAPF77hK4m3/rdGT7X8a/gwvZ2R121aBcdPwEaUhvj/36dx596zvY
mEOjrWfZhF083/nYWE2kVquj2wjs+otCLfifEEgXcVPTnEOPO9Zg3uNSL0nNQghj
FuD3iGLTUBCtM66oTe0jLSslHe8gLGEQqyMzHOzYxNqibxcOZIe8Qt0NAoGBAO+U
I5+XWjWEgDmvyC3TrOSf/KCGjtu0TSv30ipv27bDLMrpvPmD/5lpptTFwcxvVhCs
2b+chCjlghFSWFbBULBrfci2FtliClOVMYrlNBdUSJhf3aYSG2Doe6Bgt1n2CpNn
/iu37Y3NfemZBJA7hNl4dYe+f+uzM87cdQ214+jrAoGAXA0XxX8ll2+ToOLJsaNT
OvNB9h9Uc5qK5X5w+7G7O998BN2PC/MWp8H+2fVqpXgNENpNXttkRm1hk1dych86
EunfdPuqsX+as44oCyJGFHVBnWpm33eWQw9YqANRI+pCJzP08I5WK3osnPiwshd+
hR54yjgfYhBFNI7B95PmEQkCgYBzFSz7h1+s34Ycr8SvxsOBWxymG5zaCsUbPsL0
4aCgLScCHb9J+E86aVbbVFdglYa5Id7DPTL61ixhl7WZjujspeXZGSbmq0Kcnckb
mDgqkLECiOJW2NHP/j0McAkDLL4tysF8TLDO8gvuvzNC+WQ6drO2ThrypLVZQ+ry
eBIPmwKBgEZxhqa0gVvHQG/7Od69KWj4eJP28kq13RhKay8JOoN0vPmspXJo1HY3
CKuHRG+AP579dncdUnOMvfXOtkdM4vk0+hWASBQzM9xzVcztCa+koAugjVaLS9A+
9uQoqEeVNTckxx0S2bYevRy7hGQmUJTyQm3j1zEUR5jpdbL83Fbq
-----END RSA PRIVATE KEY-----

View File

@ -1,9 +0,0 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4f5wg5l2hKsTeNem/V41
fGnJm6gOdrj8ym3rFkEU/wT8RDtnSgFEZOQpHEgQ7JL38xUfU0Y3g6aYw9QT0hJ7
mCpz9Er5qLaMXJwZxzHzAahlfA0icqabvJOMvQtzD6uQv6wPEyZtDTWiQi9AXwBp
HssPnpYGIn20ZZuNlX2BrClciHhCPUIIZOQn/MmqTD31jSyjoQoV7MhhMTATKJx2
XrHhR+1DcKJzQBSTAGnpYVaqpsARap+nwRipr3nUTuxyGohBTSmjJ2usSeQXHI3b
ODIRe1AuTyHceAbewn8b462yEWKARdpd9AjQW5SIVPfdsz5B6GlYQ5LdYKtznTuy
7wIDAQAB
-----END PUBLIC KEY-----

View File

@ -1,45 +0,0 @@
examples/addsvc/addsvc
examples/addsvc/client/client
examples/apigateway/apigateway
examples/profilesvc/profilesvc
examples/stringsvc1/stringsvc1
examples/stringsvc2/stringsvc2
examples/stringsvc3/stringsvc3
gover.coverprofile
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
_old*
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
# https://github.com/github/gitignore/blob/master/Global/Vim.gitignore
# swap
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags

View File

@ -1,9 +0,0 @@
language: go
script: go test -race -v ./...
go:
- 1.5.4
- 1.6.3
- 1.7.1
- tip

View File

@ -1,106 +0,0 @@
package jwt
import (
"testing"
jwt "github.com/dgrijalva/jwt-go"
"golang.org/x/net/context"
)
var (
kid = "kid"
key = []byte("test_signing_key")
method = jwt.SigningMethodHS256
invalidMethod = jwt.SigningMethodRS256
claims = Claims{"user": "go-kit"}
// Signed tokens generated at https://jwt.io/
signedKey = "eyJhbGciOiJIUzI1NiIsImtpZCI6ImtpZCIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiZ28ta2l0In0.14M2VmYyApdSlV_LZ88ajjwuaLeIFplB8JpyNy0A19E"
invalidKey = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.e30.vKVCKto-Wn6rgz3vBdaZaCBGfCBDTXOENSo_X2Gq7qA"
)
func TestSigner(t *testing.T) {
e := func(ctx context.Context, i interface{}) (interface{}, error) { return ctx, nil }
signer := NewSigner(kid, key, method, claims)(e)
ctx, err := signer(context.Background(), struct{}{})
if err != nil {
t.Fatalf("Signer returned error: %s", err)
}
token, ok := ctx.(context.Context).Value(JWTTokenContextKey).(string)
if !ok {
t.Fatal("Token did not exist in context")
}
if token != signedKey {
t.Fatalf("JWT tokens did not match: expecting %s got %s", signedKey, token)
}
}
func TestJWTParser(t *testing.T) {
e := func(ctx context.Context, i interface{}) (interface{}, error) { return ctx, nil }
keys := func(token *jwt.Token) (interface{}, error) {
return key, nil
}
parser := NewParser(keys, method)(e)
// No Token is passed into the parser
_, err := parser(context.Background(), struct{}{})
if err == nil {
t.Error("Parser should have returned an error")
}
if err != ErrTokenContextMissing {
t.Errorf("unexpected error returned, expected: %s got: %s", ErrTokenContextMissing, err)
}
// Invalid Token is passed into the parser
ctx := context.WithValue(context.Background(), JWTTokenContextKey, invalidKey)
_, err = parser(ctx, struct{}{})
if err == nil {
t.Error("Parser should have returned an error")
}
// Invalid Method is used in the parser
badParser := NewParser(keys, invalidMethod)(e)
ctx = context.WithValue(context.Background(), JWTTokenContextKey, signedKey)
_, err = badParser(ctx, struct{}{})
if err == nil {
t.Error("Parser should have returned an error")
}
if err != ErrUnexpectedSigningMethod {
t.Errorf("unexpected error returned, expected: %s got: %s", ErrUnexpectedSigningMethod, err)
}
// Invalid key is used in the parser
invalidKeys := func(token *jwt.Token) (interface{}, error) {
return []byte("bad"), nil
}
badParser = NewParser(invalidKeys, method)(e)
ctx = context.WithValue(context.Background(), JWTTokenContextKey, signedKey)
_, err = badParser(ctx, struct{}{})
if err == nil {
t.Error("Parser should have returned an error")
}
// Correct token is passed into the parser
ctx = context.WithValue(context.Background(), JWTTokenContextKey, signedKey)
ctx1, err := parser(ctx, struct{}{})
if err != nil {
t.Fatalf("Parser returned error: %s", err)
}
cl, ok := ctx1.(context.Context).Value(JWTClaimsContextKey).(Claims)
if !ok {
t.Fatal("Claims were not passed into context correctly")
}
if cl["user"] != claims["user"] {
t.Fatalf("JWT Claims.user did not match: expecting %s got %s", claims["user"], cl["user"])
}
}

View File

@ -1,126 +0,0 @@
package jwt
import (
"fmt"
"net/http"
"testing"
"google.golang.org/grpc/metadata"
"golang.org/x/net/context"
)
func TestToHTTPContext(t *testing.T) {
reqFunc := ToHTTPContext()
// When the header doesn't exist
ctx := reqFunc(context.Background(), &http.Request{})
if ctx.Value(JWTTokenContextKey) != nil {
t.Error("Context shouldn't contain the encoded JWT")
}
// Authorization header value has invalid format
header := http.Header{}
header.Set("Authorization", "no expected auth header format value")
ctx = reqFunc(context.Background(), &http.Request{Header: header})
if ctx.Value(JWTTokenContextKey) != nil {
t.Error("Context shouldn't contain the encoded JWT")
}
// Authorization header is correct
header.Set("Authorization", generateAuthHeaderFromToken(signedKey))
ctx = reqFunc(context.Background(), &http.Request{Header: header})
token := ctx.Value(JWTTokenContextKey).(string)
if token != signedKey {
t.Errorf("Context doesn't contain the expected encoded token value; expected: %s, got: %s", signedKey, token)
}
}
func TestFromHTTPContext(t *testing.T) {
reqFunc := FromHTTPContext()
// No JWT Token is passed in the context
ctx := context.Background()
r := http.Request{}
reqFunc(ctx, &r)
token := r.Header.Get("Authorization")
if token != "" {
t.Error("authorization key should not exist in metadata")
}
// Correct JWT Token is passed in the context
ctx = context.WithValue(context.Background(), JWTTokenContextKey, signedKey)
r = http.Request{Header: http.Header{}}
reqFunc(ctx, &r)
token = r.Header.Get("Authorization")
expected := generateAuthHeaderFromToken(signedKey)
if token != expected {
t.Errorf("Authorization header does not contain the expected JWT token; expected %s, got %s", expected, token)
}
}
func TestToGRPCContext(t *testing.T) {
md := metadata.MD{}
reqFunc := ToGRPCContext()
// No Authorization header is passed
ctx := reqFunc(context.Background(), &md)
token := ctx.Value(JWTTokenContextKey)
if token != nil {
t.Error("Context should not contain a JWT Token")
}
// Invalid Authorization header is passed
md["authorization"] = []string{fmt.Sprintf("%s", signedKey)}
ctx = reqFunc(context.Background(), &md)
token = ctx.Value(JWTTokenContextKey)
if token != nil {
t.Error("Context should not contain a JWT Token")
}
// Authorization header is correct
md["authorization"] = []string{fmt.Sprintf("Bearer %s", signedKey)}
ctx = reqFunc(context.Background(), &md)
token, ok := ctx.Value(JWTTokenContextKey).(string)
if !ok {
t.Fatal("JWT Token not passed to context correctly")
}
if token != signedKey {
t.Errorf("JWT tokens did not match: expecting %s got %s", signedKey, token)
}
}
func TestFromGRPCContext(t *testing.T) {
reqFunc := FromGRPCContext()
// No JWT Token is passed in the context
ctx := context.Background()
md := metadata.MD{}
reqFunc(ctx, &md)
_, ok := md["authorization"]
if ok {
t.Error("authorization key should not exist in metadata")
}
// Correct JWT Token is passed in the context
ctx = context.WithValue(context.Background(), JWTTokenContextKey, signedKey)
md = metadata.MD{}
reqFunc(ctx, &md)
token, ok := md["authorization"]
if !ok {
t.Fatal("JWT Token not passed to metadata correctly")
}
if token[0] != generateAuthHeaderFromToken(signedKey) {
t.Errorf("JWT tokens did not match: expecting %s got %s", signedKey, token[0])
}
}

View File

@ -1,10 +0,0 @@
// Package circuitbreaker implements the circuit breaker pattern.
//
// Circuit breakers prevent thundering herds, and improve resiliency against
// intermittent errors. Every client-side endpoint should be wrapped in a
// circuit breaker.
//
// We provide several implementations in this package, but if you're looking
// for guidance, Gobreaker is probably the best place to start. It has a
// simple and intuitive API, and is well-tested.
package circuitbreaker

View File

@ -1,21 +0,0 @@
package circuitbreaker
import (
"github.com/sony/gobreaker"
"golang.org/x/net/context"
"github.com/go-kit/kit/endpoint"
)
// Gobreaker returns an endpoint.Middleware that implements the circuit
// breaker pattern using the sony/gobreaker package. Only errors returned by
// the wrapped endpoint count against the circuit breaker's error count.
//
// See http://godoc.org/github.com/sony/gobreaker for more information.
func Gobreaker(cb *gobreaker.CircuitBreaker) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
return cb.Execute(func() (interface{}, error) { return next(ctx, request) })
}
}
}

View File

@ -1,19 +0,0 @@
package circuitbreaker_test
import (
"testing"
"github.com/sony/gobreaker"
"github.com/go-kit/kit/circuitbreaker"
)
func TestGobreaker(t *testing.T) {
var (
breaker = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{}))
primeWith = 100
shouldPass = func(n int) bool { return n <= 5 } // https://github.com/sony/gobreaker/blob/bfa846d/gobreaker.go#L76
circuitOpenError = "circuit breaker is open"
)
testFailingEndpoint(t, breaker, primeWith, shouldPass, 0, circuitOpenError)
}

View File

@ -1,38 +0,0 @@
package circuitbreaker
import (
"time"
"github.com/streadway/handy/breaker"
"golang.org/x/net/context"
"github.com/go-kit/kit/endpoint"
)
// HandyBreaker returns an endpoint.Middleware that implements the circuit
// breaker pattern using the streadway/handy/breaker package. Only errors
// returned by the wrapped endpoint count against the circuit breaker's error
// count.
//
// See http://godoc.org/github.com/streadway/handy/breaker for more
// information.
func HandyBreaker(cb breaker.Breaker) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
if !cb.Allow() {
return nil, breaker.ErrCircuitOpen
}
defer func(begin time.Time) {
if err == nil {
cb.Success(time.Since(begin))
} else {
cb.Failure(time.Since(begin))
}
}(time.Now())
response, err = next(ctx, request)
return
}
}
}

View File

@ -1,20 +0,0 @@
package circuitbreaker_test
import (
"testing"
handybreaker "github.com/streadway/handy/breaker"
"github.com/go-kit/kit/circuitbreaker"
)
func TestHandyBreaker(t *testing.T) {
var (
failureRatio = 0.05
breaker = circuitbreaker.HandyBreaker(handybreaker.NewBreaker(failureRatio))
primeWith = handybreaker.DefaultMinObservations * 10
shouldPass = func(n int) bool { return (float64(n) / float64(primeWith+n)) <= failureRatio }
openCircuitError = handybreaker.ErrCircuitOpen.Error()
)
testFailingEndpoint(t, breaker, primeWith, shouldPass, 0, openCircuitError)
}

View File

@ -1,30 +0,0 @@
package circuitbreaker
import (
"github.com/afex/hystrix-go/hystrix"
"golang.org/x/net/context"
"github.com/go-kit/kit/endpoint"
)
// Hystrix returns an endpoint.Middleware that implements the circuit
// breaker pattern using the afex/hystrix-go package.
//
// When using this circuit breaker, please configure your commands separately.
//
// See https://godoc.org/github.com/afex/hystrix-go/hystrix for more
// information.
func Hystrix(commandName string) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
var resp interface{}
if err := hystrix.Do(commandName, func() (err error) {
resp, err = next(ctx, request)
return err
}, nil); err != nil {
return nil, err
}
return resp, nil
}
}
}

View File

@ -1,40 +0,0 @@
package circuitbreaker_test
import (
"io/ioutil"
stdlog "log"
"testing"
"time"
"github.com/afex/hystrix-go/hystrix"
"github.com/go-kit/kit/circuitbreaker"
)
func TestHystrix(t *testing.T) {
stdlog.SetOutput(ioutil.Discard)
const (
commandName = "my-endpoint"
errorPercent = 5
maxConcurrent = 1000
)
hystrix.ConfigureCommand(commandName, hystrix.CommandConfig{
ErrorPercentThreshold: errorPercent,
MaxConcurrentRequests: maxConcurrent,
})
var (
breaker = circuitbreaker.Hystrix(commandName)
primeWith = hystrix.DefaultVolumeThreshold * 2
shouldPass = func(n int) bool { return (float64(n) / float64(primeWith+n)) <= (float64(errorPercent-1) / 100.0) }
openCircuitError = hystrix.ErrCircuitOpen.Error()
)
// hystrix-go uses buffered channels to receive reports on request success/failure,
// and so is basically impossible to test deterministically. We have to make sure
// the report buffer is emptied, by injecting a sleep between each invocation.
requestDelay := 5 * time.Millisecond
testFailingEndpoint(t, breaker, primeWith, shouldPass, requestDelay, openCircuitError)
}

View File

@ -1,76 +0,0 @@
package circuitbreaker_test
import (
"errors"
"fmt"
"path/filepath"
"runtime"
"testing"
"time"
"golang.org/x/net/context"
"github.com/go-kit/kit/endpoint"
)
func testFailingEndpoint(
t *testing.T,
breaker endpoint.Middleware,
primeWith int,
shouldPass func(int) bool,
requestDelay time.Duration,
openCircuitError string,
) {
_, file, line, _ := runtime.Caller(1)
caller := fmt.Sprintf("%s:%d", filepath.Base(file), line)
// Create a mock endpoint and wrap it with the breaker.
m := mock{}
var e endpoint.Endpoint
e = m.endpoint
e = breaker(e)
// Prime the endpoint with successful requests.
for i := 0; i < primeWith; i++ {
if _, err := e(context.Background(), struct{}{}); err != nil {
t.Fatalf("%s: during priming, got error: %v", caller, err)
}
time.Sleep(requestDelay)
}
// Switch the endpoint to start throwing errors.
m.err = errors.New("tragedy+disaster")
m.through = 0
// The first several should be allowed through and yield our error.
for i := 0; shouldPass(i); i++ {
if _, err := e(context.Background(), struct{}{}); err != m.err {
t.Fatalf("%s: want %v, have %v", caller, m.err, err)
}
time.Sleep(requestDelay)
}
through := m.through
// But the rest should be blocked by an open circuit.
for i := 0; i < 10; i++ {
if _, err := e(context.Background(), struct{}{}); err.Error() != openCircuitError {
t.Fatalf("%s: want %q, have %q", caller, openCircuitError, err.Error())
}
time.Sleep(requestDelay)
}
// Make sure none of those got through.
if want, have := through, m.through; want != have {
t.Errorf("%s: want %d, have %d", caller, want, have)
}
}
type mock struct {
through int
err error
}
func (m *mock) endpoint(context.Context, interface{}) (interface{}, error) {
m.through++
return struct{}{}, m.err
}

View File

@ -1,50 +0,0 @@
package endpoint_test
import (
"fmt"
"golang.org/x/net/context"
"github.com/go-kit/kit/endpoint"
)
func ExampleChain() {
e := endpoint.Chain(
annotate("first"),
annotate("second"),
annotate("third"),
)(myEndpoint)
if _, err := e(ctx, req); err != nil {
panic(err)
}
// Output:
// first pre
// second pre
// third pre
// my endpoint!
// third post
// second post
// first post
}
var (
ctx = context.Background()
req = struct{}{}
)
func annotate(s string) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
fmt.Println(s, "pre")
defer fmt.Println(s, "post")
return next(ctx, request)
}
}
}
func myEndpoint(context.Context, interface{}) (interface{}, error) {
fmt.Println("my endpoint!")
return struct{}{}, nil
}

View File

@ -1,5 +0,0 @@
# Examples
For more information about these examples,
including a walkthrough of the stringsvc example,
see [gokit.io/examples](https://gokit.io/examples).

View File

@ -1,75 +0,0 @@
// Package grpc provides a gRPC client for the add service.
package grpc
import (
"time"
jujuratelimit "github.com/juju/ratelimit"
stdopentracing "github.com/opentracing/opentracing-go"
"github.com/sony/gobreaker"
"google.golang.org/grpc"
"github.com/go-kit/kit/circuitbreaker"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/examples/addsvc"
"github.com/go-kit/kit/examples/addsvc/pb"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/ratelimit"
"github.com/go-kit/kit/tracing/opentracing"
grpctransport "github.com/go-kit/kit/transport/grpc"
)
// New returns an AddService backed by a gRPC client connection. It is the
// responsibility of the caller to dial, and later close, the connection.
func New(conn *grpc.ClientConn, tracer stdopentracing.Tracer, logger log.Logger) addsvc.Service {
// We construct a single ratelimiter middleware, to limit the total outgoing
// QPS from this client to all methods on the remote instance. We also
// construct per-endpoint circuitbreaker middlewares to demonstrate how
// that's done, although they could easily be combined into a single breaker
// for the entire remote instance, too.
limiter := ratelimit.NewTokenBucketLimiter(jujuratelimit.NewBucketWithRate(100, 100))
var sumEndpoint endpoint.Endpoint
{
sumEndpoint = grpctransport.NewClient(
conn,
"Add",
"Sum",
addsvc.EncodeGRPCSumRequest,
addsvc.DecodeGRPCSumResponse,
pb.SumReply{},
grpctransport.ClientBefore(opentracing.ToGRPCRequest(tracer, logger)),
).Endpoint()
sumEndpoint = opentracing.TraceClient(tracer, "Sum")(sumEndpoint)
sumEndpoint = limiter(sumEndpoint)
sumEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{
Name: "Sum",
Timeout: 30 * time.Second,
}))(sumEndpoint)
}
var concatEndpoint endpoint.Endpoint
{
concatEndpoint = grpctransport.NewClient(
conn,
"Add",
"Concat",
addsvc.EncodeGRPCConcatRequest,
addsvc.DecodeGRPCConcatResponse,
pb.ConcatReply{},
grpctransport.ClientBefore(opentracing.ToGRPCRequest(tracer, logger)),
).Endpoint()
concatEndpoint = opentracing.TraceClient(tracer, "Concat")(concatEndpoint)
concatEndpoint = limiter(concatEndpoint)
concatEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{
Name: "Concat",
Timeout: 30 * time.Second,
}))(concatEndpoint)
}
return addsvc.Endpoints{
SumEndpoint: sumEndpoint,
ConcatEndpoint: concatEndpoint,
}
}

View File

@ -1,86 +0,0 @@
// Package http provides an HTTP client for the add service.
package http
import (
"net/url"
"strings"
"time"
jujuratelimit "github.com/juju/ratelimit"
stdopentracing "github.com/opentracing/opentracing-go"
"github.com/sony/gobreaker"
"github.com/go-kit/kit/circuitbreaker"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/examples/addsvc"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/ratelimit"
"github.com/go-kit/kit/tracing/opentracing"
httptransport "github.com/go-kit/kit/transport/http"
)
// New returns an AddService backed by an HTTP server living at the remote
// instance. We expect instance to come from a service discovery system, so
// likely of the form "host:port".
func New(instance string, tracer stdopentracing.Tracer, logger log.Logger) (addsvc.Service, error) {
if !strings.HasPrefix(instance, "http") {
instance = "http://" + instance
}
u, err := url.Parse(instance)
if err != nil {
return nil, err
}
// We construct a single ratelimiter middleware, to limit the total outgoing
// QPS from this client to all methods on the remote instance. We also
// construct per-endpoint circuitbreaker middlewares to demonstrate how
// that's done, although they could easily be combined into a single breaker
// for the entire remote instance, too.
limiter := ratelimit.NewTokenBucketLimiter(jujuratelimit.NewBucketWithRate(100, 100))
var sumEndpoint endpoint.Endpoint
{
sumEndpoint = httptransport.NewClient(
"POST",
copyURL(u, "/sum"),
addsvc.EncodeHTTPGenericRequest,
addsvc.DecodeHTTPSumResponse,
httptransport.ClientBefore(opentracing.ToHTTPRequest(tracer, logger)),
).Endpoint()
sumEndpoint = opentracing.TraceClient(tracer, "Sum")(sumEndpoint)
sumEndpoint = limiter(sumEndpoint)
sumEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{
Name: "Sum",
Timeout: 30 * time.Second,
}))(sumEndpoint)
}
var concatEndpoint endpoint.Endpoint
{
concatEndpoint = httptransport.NewClient(
"POST",
copyURL(u, "/concat"),
addsvc.EncodeHTTPGenericRequest,
addsvc.DecodeHTTPConcatResponse,
httptransport.ClientBefore(opentracing.ToHTTPRequest(tracer, logger)),
).Endpoint()
concatEndpoint = opentracing.TraceClient(tracer, "Concat")(concatEndpoint)
concatEndpoint = limiter(concatEndpoint)
sumEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{
Name: "Concat",
Timeout: 30 * time.Second,
}))(sumEndpoint)
}
return addsvc.Endpoints{
SumEndpoint: sumEndpoint,
ConcatEndpoint: concatEndpoint,
}, nil
}
func copyURL(base *url.URL, path string) *url.URL {
next := *base
next.Path = path
return &next
}

View File

@ -1,55 +0,0 @@
// Package thrift provides a Thrift client for the add service.
package thrift
import (
"time"
jujuratelimit "github.com/juju/ratelimit"
"github.com/sony/gobreaker"
"github.com/go-kit/kit/circuitbreaker"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/examples/addsvc"
thriftadd "github.com/go-kit/kit/examples/addsvc/thrift/gen-go/addsvc"
"github.com/go-kit/kit/ratelimit"
)
// New returns an AddService backed by a Thrift server described by the provided
// client. The caller is responsible for constructing the client, and eventually
// closing the underlying transport.
func New(client *thriftadd.AddServiceClient) addsvc.Service {
// We construct a single ratelimiter middleware, to limit the total outgoing
// QPS from this client to all methods on the remote instance. We also
// construct per-endpoint circuitbreaker middlewares to demonstrate how
// that's done, although they could easily be combined into a single breaker
// for the entire remote instance, too.
limiter := ratelimit.NewTokenBucketLimiter(jujuratelimit.NewBucketWithRate(100, 100))
// Thrift does not currently have tracer bindings, so we skip tracing.
var sumEndpoint endpoint.Endpoint
{
sumEndpoint = addsvc.MakeThriftSumEndpoint(client)
sumEndpoint = limiter(sumEndpoint)
sumEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{
Name: "Sum",
Timeout: 30 * time.Second,
}))(sumEndpoint)
}
var concatEndpoint endpoint.Endpoint
{
concatEndpoint = addsvc.MakeThriftConcatEndpoint(client)
concatEndpoint = limiter(concatEndpoint)
concatEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{
Name: "Concat",
Timeout: 30 * time.Second,
}))(concatEndpoint)
}
return addsvc.Endpoints{
SumEndpoint: sumEndpoint,
ConcatEndpoint: concatEndpoint,
}
}

View File

@ -1,178 +0,0 @@
package main
import (
"flag"
"fmt"
"os"
"strconv"
"strings"
"time"
"github.com/apache/thrift/lib/go/thrift"
"github.com/lightstep/lightstep-tracer-go"
stdopentracing "github.com/opentracing/opentracing-go"
zipkin "github.com/openzipkin/zipkin-go-opentracing"
"golang.org/x/net/context"
"google.golang.org/grpc"
"sourcegraph.com/sourcegraph/appdash"
appdashot "sourcegraph.com/sourcegraph/appdash/opentracing"
"github.com/go-kit/kit/examples/addsvc"
grpcclient "github.com/go-kit/kit/examples/addsvc/client/grpc"
httpclient "github.com/go-kit/kit/examples/addsvc/client/http"
thriftclient "github.com/go-kit/kit/examples/addsvc/client/thrift"
thriftadd "github.com/go-kit/kit/examples/addsvc/thrift/gen-go/addsvc"
"github.com/go-kit/kit/log"
)
func main() {
// The addcli presumes no service discovery system, and expects users to
// provide the direct address of an addsvc. This presumption is reflected in
// the addcli binary and the the client packages: the -transport.addr flags
// and various client constructors both expect host:port strings. For an
// example service with a client built on top of a service discovery system,
// see profilesvc.
var (
httpAddr = flag.String("http.addr", "", "HTTP address of addsvc")
grpcAddr = flag.String("grpc.addr", "", "gRPC (HTTP) address of addsvc")
thriftAddr = flag.String("thrift.addr", "", "Thrift address of addsvc")
thriftProtocol = flag.String("thrift.protocol", "binary", "binary, compact, json, simplejson")
thriftBufferSize = flag.Int("thrift.buffer.size", 0, "0 for unbuffered")
thriftFramed = flag.Bool("thrift.framed", false, "true to enable framing")
zipkinAddr = flag.String("zipkin.addr", "", "Enable Zipkin tracing via a Kafka Collector host:port")
appdashAddr = flag.String("appdash.addr", "", "Enable Appdash tracing via an Appdash server host:port")
lightstepToken = flag.String("lightstep.token", "", "Enable LightStep tracing via a LightStep access token")
method = flag.String("method", "sum", "sum, concat")
)
flag.Parse()
if len(flag.Args()) != 2 {
fmt.Fprintf(os.Stderr, "usage: addcli [flags] <a> <b>\n")
os.Exit(1)
}
// This is a demonstration client, which supports multiple tracers.
// Your clients will probably just use one tracer.
var tracer stdopentracing.Tracer
{
if *zipkinAddr != "" {
collector, err := zipkin.NewKafkaCollector(
strings.Split(*zipkinAddr, ","),
zipkin.KafkaLogger(log.NewNopLogger()),
)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
tracer, err = zipkin.NewTracer(
zipkin.NewRecorder(collector, false, "localhost:8000", "addcli"),
)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
} else if *appdashAddr != "" {
tracer = appdashot.NewTracer(appdash.NewRemoteCollector(*appdashAddr))
} else if *lightstepToken != "" {
tracer = lightstep.NewTracer(lightstep.Options{
AccessToken: *lightstepToken,
})
defer lightstep.FlushLightStepTracer(tracer)
} else {
tracer = stdopentracing.GlobalTracer() // no-op
}
}
// This is a demonstration client, which supports multiple transports.
// Your clients will probably just define and stick with 1 transport.
var (
service addsvc.Service
err error
)
if *httpAddr != "" {
service, err = httpclient.New(*httpAddr, tracer, log.NewNopLogger())
} else if *grpcAddr != "" {
conn, err := grpc.Dial(*grpcAddr, grpc.WithInsecure(), grpc.WithTimeout(time.Second))
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v", err)
os.Exit(1)
}
defer conn.Close()
service = grpcclient.New(conn, tracer, log.NewNopLogger())
} else if *thriftAddr != "" {
// It's necessary to do all of this construction in the func main,
// because (among other reasons) we need to control the lifecycle of the
// Thrift transport, i.e. close it eventually.
var protocolFactory thrift.TProtocolFactory
switch *thriftProtocol {
case "compact":
protocolFactory = thrift.NewTCompactProtocolFactory()
case "simplejson":
protocolFactory = thrift.NewTSimpleJSONProtocolFactory()
case "json":
protocolFactory = thrift.NewTJSONProtocolFactory()
case "binary", "":
protocolFactory = thrift.NewTBinaryProtocolFactoryDefault()
default:
fmt.Fprintf(os.Stderr, "error: invalid protocol %q\n", *thriftProtocol)
os.Exit(1)
}
var transportFactory thrift.TTransportFactory
if *thriftBufferSize > 0 {
transportFactory = thrift.NewTBufferedTransportFactory(*thriftBufferSize)
} else {
transportFactory = thrift.NewTTransportFactory()
}
if *thriftFramed {
transportFactory = thrift.NewTFramedTransportFactory(transportFactory)
}
transportSocket, err := thrift.NewTSocket(*thriftAddr)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
transport := transportFactory.GetTransport(transportSocket)
if err := transport.Open(); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
defer transport.Close()
client := thriftadd.NewAddServiceClientFactory(transport, protocolFactory)
service = thriftclient.New(client)
} else {
fmt.Fprintf(os.Stderr, "error: no remote address specified\n")
os.Exit(1)
}
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
switch *method {
case "sum":
a, _ := strconv.ParseInt(flag.Args()[0], 10, 64)
b, _ := strconv.ParseInt(flag.Args()[1], 10, 64)
v, err := service.Sum(context.Background(), int(a), int(b))
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
fmt.Fprintf(os.Stdout, "%d + %d = %d\n", a, b, v)
case "concat":
a := flag.Args()[0]
b := flag.Args()[1]
v, err := service.Concat(context.Background(), a, b)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
fmt.Fprintf(os.Stdout, "%q + %q = %q\n", a, b, v)
default:
fmt.Fprintf(os.Stderr, "error: invalid method %q\n", method)
os.Exit(1)
}
}

View File

@ -1,256 +0,0 @@
package main
import (
"flag"
"fmt"
"net"
"net/http"
"net/http/pprof"
"os"
"os/signal"
"strings"
"syscall"
"github.com/apache/thrift/lib/go/thrift"
lightstep "github.com/lightstep/lightstep-tracer-go"
stdopentracing "github.com/opentracing/opentracing-go"
zipkin "github.com/openzipkin/zipkin-go-opentracing"
stdprometheus "github.com/prometheus/client_golang/prometheus"
"golang.org/x/net/context"
"google.golang.org/grpc"
"sourcegraph.com/sourcegraph/appdash"
appdashot "sourcegraph.com/sourcegraph/appdash/opentracing"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/examples/addsvc"
"github.com/go-kit/kit/examples/addsvc/pb"
thriftadd "github.com/go-kit/kit/examples/addsvc/thrift/gen-go/addsvc"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/metrics"
"github.com/go-kit/kit/metrics/prometheus"
"github.com/go-kit/kit/tracing/opentracing"
)
func main() {
var (
debugAddr = flag.String("debug.addr", ":8080", "Debug and metrics listen address")
httpAddr = flag.String("http.addr", ":8081", "HTTP listen address")
grpcAddr = flag.String("grpc.addr", ":8082", "gRPC (HTTP) listen address")
thriftAddr = flag.String("thrift.addr", ":8083", "Thrift listen address")
thriftProtocol = flag.String("thrift.protocol", "binary", "binary, compact, json, simplejson")
thriftBufferSize = flag.Int("thrift.buffer.size", 0, "0 for unbuffered")
thriftFramed = flag.Bool("thrift.framed", false, "true to enable framing")
zipkinAddr = flag.String("zipkin.addr", "", "Enable Zipkin tracing via a Kafka server host:port")
appdashAddr = flag.String("appdash.addr", "", "Enable Appdash tracing via an Appdash server host:port")
lightstepToken = flag.String("lightstep.token", "", "Enable LightStep tracing via a LightStep access token")
)
flag.Parse()
// Logging domain.
var logger log.Logger
{
logger = log.NewLogfmtLogger(os.Stdout)
logger = log.NewContext(logger).With("ts", log.DefaultTimestampUTC)
logger = log.NewContext(logger).With("caller", log.DefaultCaller)
}
logger.Log("msg", "hello")
defer logger.Log("msg", "goodbye")
// Metrics domain.
var ints, chars metrics.Counter
{
// Business level metrics.
ints = prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "addsvc",
Name: "integers_summed",
Help: "Total count of integers summed via the Sum method.",
}, []string{})
chars = prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "addsvc",
Name: "characters_concatenated",
Help: "Total count of characters concatenated via the Concat method.",
}, []string{})
}
var duration metrics.Histogram
{
// Transport level metrics.
duration = prometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "addsvc",
Name: "request_duration_ns",
Help: "Request duration in nanoseconds.",
}, []string{"method", "success"})
}
// Tracing domain.
var tracer stdopentracing.Tracer
{
if *zipkinAddr != "" {
logger := log.NewContext(logger).With("tracer", "Zipkin")
logger.Log("addr", *zipkinAddr)
collector, err := zipkin.NewKafkaCollector(
strings.Split(*zipkinAddr, ","),
zipkin.KafkaLogger(logger),
)
if err != nil {
logger.Log("err", err)
os.Exit(1)
}
tracer, err = zipkin.NewTracer(
zipkin.NewRecorder(collector, false, "localhost:80", "addsvc"),
)
if err != nil {
logger.Log("err", err)
os.Exit(1)
}
} else if *appdashAddr != "" {
logger := log.NewContext(logger).With("tracer", "Appdash")
logger.Log("addr", *appdashAddr)
tracer = appdashot.NewTracer(appdash.NewRemoteCollector(*appdashAddr))
} else if *lightstepToken != "" {
logger := log.NewContext(logger).With("tracer", "LightStep")
logger.Log() // probably don't want to print out the token :)
tracer = lightstep.NewTracer(lightstep.Options{
AccessToken: *lightstepToken,
})
defer lightstep.FlushLightStepTracer(tracer)
} else {
logger := log.NewContext(logger).With("tracer", "none")
logger.Log()
tracer = stdopentracing.GlobalTracer() // no-op
}
}
// Business domain.
var service addsvc.Service
{
service = addsvc.NewBasicService()
service = addsvc.ServiceLoggingMiddleware(logger)(service)
service = addsvc.ServiceInstrumentingMiddleware(ints, chars)(service)
}
// Endpoint domain.
var sumEndpoint endpoint.Endpoint
{
sumDuration := duration.With("method", "Sum")
sumLogger := log.NewContext(logger).With("method", "Sum")
sumEndpoint = addsvc.MakeSumEndpoint(service)
sumEndpoint = opentracing.TraceServer(tracer, "Sum")(sumEndpoint)
sumEndpoint = addsvc.EndpointInstrumentingMiddleware(sumDuration)(sumEndpoint)
sumEndpoint = addsvc.EndpointLoggingMiddleware(sumLogger)(sumEndpoint)
}
var concatEndpoint endpoint.Endpoint
{
concatDuration := duration.With("method", "Concat")
concatLogger := log.NewContext(logger).With("method", "Concat")
concatEndpoint = addsvc.MakeConcatEndpoint(service)
concatEndpoint = opentracing.TraceServer(tracer, "Concat")(concatEndpoint)
concatEndpoint = addsvc.EndpointInstrumentingMiddleware(concatDuration)(concatEndpoint)
concatEndpoint = addsvc.EndpointLoggingMiddleware(concatLogger)(concatEndpoint)
}
endpoints := addsvc.Endpoints{
SumEndpoint: sumEndpoint,
ConcatEndpoint: concatEndpoint,
}
// Mechanical domain.
errc := make(chan error)
ctx := context.Background()
// Interrupt handler.
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
errc <- fmt.Errorf("%s", <-c)
}()
// Debug listener.
go func() {
logger := log.NewContext(logger).With("transport", "debug")
m := http.NewServeMux()
m.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
m.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
m.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
m.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
m.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
m.Handle("/metrics", stdprometheus.Handler())
logger.Log("addr", *debugAddr)
errc <- http.ListenAndServe(*debugAddr, m)
}()
// HTTP transport.
go func() {
logger := log.NewContext(logger).With("transport", "HTTP")
h := addsvc.MakeHTTPHandler(ctx, endpoints, tracer, logger)
logger.Log("addr", *httpAddr)
errc <- http.ListenAndServe(*httpAddr, h)
}()
// gRPC transport.
go func() {
logger := log.NewContext(logger).With("transport", "gRPC")
ln, err := net.Listen("tcp", *grpcAddr)
if err != nil {
errc <- err
return
}
srv := addsvc.MakeGRPCServer(ctx, endpoints, tracer, logger)
s := grpc.NewServer()
pb.RegisterAddServer(s, srv)
logger.Log("addr", *grpcAddr)
errc <- s.Serve(ln)
}()
// Thrift transport.
go func() {
logger := log.NewContext(logger).With("transport", "Thrift")
var protocolFactory thrift.TProtocolFactory
switch *thriftProtocol {
case "binary":
protocolFactory = thrift.NewTBinaryProtocolFactoryDefault()
case "compact":
protocolFactory = thrift.NewTCompactProtocolFactory()
case "json":
protocolFactory = thrift.NewTJSONProtocolFactory()
case "simplejson":
protocolFactory = thrift.NewTSimpleJSONProtocolFactory()
default:
errc <- fmt.Errorf("invalid Thrift protocol %q", *thriftProtocol)
return
}
var transportFactory thrift.TTransportFactory
if *thriftBufferSize > 0 {
transportFactory = thrift.NewTBufferedTransportFactory(*thriftBufferSize)
} else {
transportFactory = thrift.NewTTransportFactory()
}
if *thriftFramed {
transportFactory = thrift.NewTFramedTransportFactory(transportFactory)
}
transport, err := thrift.NewTServerSocket(*thriftAddr)
if err != nil {
errc <- err
return
}
logger.Log("addr", *thriftAddr)
errc <- thrift.NewTSimpleServer4(
thriftadd.NewAddServiceProcessor(addsvc.MakeThriftHandler(ctx, endpoints)),
transport,
transportFactory,
protocolFactory,
).Serve()
}()
// Run!
logger.Log("exit", <-errc)
}

View File

@ -1,5 +0,0 @@
// Package addsvc is an example microservice, useful for education. It can sum
// integers and concatenate strings. A client library is available in the client
// subdirectory. A server binary is available in cmd/addsrv. An example client
// binary is available in cmd/addcli.
package addsvc

View File

@ -1,135 +0,0 @@
package addsvc
// This file contains methods to make individual endpoints from services,
// request and response types to serve those endpoints, as well as encoders and
// decoders for those types, for all of our supported transport serialization
// formats. It also includes endpoint middlewares.
import (
"fmt"
"time"
"golang.org/x/net/context"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/metrics"
)
// Endpoints collects all of the endpoints that compose an add service. It's
// meant to be used as a helper struct, to collect all of the endpoints into a
// single parameter.
//
// In a server, it's useful for functions that need to operate on a per-endpoint
// basis. For example, you might pass an Endpoints to a function that produces
// an http.Handler, with each method (endpoint) wired up to a specific path. (It
// is probably a mistake in design to invoke the Service methods on the
// Endpoints struct in a server.)
//
// In a client, it's useful to collect individually constructed endpoints into a
// single type that implements the Service interface. For example, you might
// construct individual endpoints using transport/http.NewClient, combine them
// into an Endpoints, and return it to the caller as a Service.
type Endpoints struct {
SumEndpoint endpoint.Endpoint
ConcatEndpoint endpoint.Endpoint
}
// Sum implements Service. Primarily useful in a client.
func (e Endpoints) Sum(ctx context.Context, a, b int) (int, error) {
request := sumRequest{A: a, B: b}
response, err := e.SumEndpoint(ctx, request)
if err != nil {
return 0, err
}
return response.(sumResponse).V, response.(sumResponse).Err
}
// Concat implements Service. Primarily useful in a client.
func (e Endpoints) Concat(ctx context.Context, a, b string) (string, error) {
request := concatRequest{A: a, B: b}
response, err := e.ConcatEndpoint(ctx, request)
if err != nil {
return "", err
}
return response.(concatResponse).V, response.(concatResponse).Err
}
// MakeSumEndpoint returns an endpoint that invokes Sum on the service.
// Primarily useful in a server.
func MakeSumEndpoint(s Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
sumReq := request.(sumRequest)
v, err := s.Sum(ctx, sumReq.A, sumReq.B)
if err == ErrIntOverflow {
return nil, err // special case; see comment on ErrIntOverflow
}
return sumResponse{
V: v,
Err: err,
}, nil
}
}
// MakeConcatEndpoint returns an endpoint that invokes Concat on the service.
// Primarily useful in a server.
func MakeConcatEndpoint(s Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
concatReq := request.(concatRequest)
v, err := s.Concat(ctx, concatReq.A, concatReq.B)
return concatResponse{
V: v,
Err: err,
}, nil
}
}
// EndpointInstrumentingMiddleware returns an endpoint middleware that records
// the duration of each invocation to the passed histogram. The middleware adds
// a single field: "success", which is "true" if no error is returned, and
// "false" otherwise.
func EndpointInstrumentingMiddleware(duration metrics.Histogram) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
defer func(begin time.Time) {
duration.With("success", fmt.Sprint(err == nil)).Observe(time.Since(begin).Seconds())
}(time.Now())
return next(ctx, request)
}
}
}
// EndpointLoggingMiddleware returns an endpoint middleware that logs the
// duration of each invocation, and the resulting error, if any.
func EndpointLoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
defer func(begin time.Time) {
logger.Log("error", err, "took", time.Since(begin))
}(time.Now())
return next(ctx, request)
}
}
}
// These types are unexported because they only exist to serve the endpoint
// domain, which is totally encapsulated in this package. They are otherwise
// opaque to all callers.
type sumRequest struct{ A, B int }
type sumResponse struct {
V int
Err error
}
type concatRequest struct{ A, B string }
type concatResponse struct {
V string
Err error
}

View File

@ -1,215 +0,0 @@
// Code generated by protoc-gen-go.
// source: addsvc.proto
// DO NOT EDIT!
/*
Package pb is a generated protocol buffer package.
It is generated from these files:
addsvc.proto
It has these top-level messages:
SumRequest
SumReply
ConcatRequest
ConcatReply
*/
package pb
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// The sum request contains two parameters.
type SumRequest struct {
A int64 `protobuf:"varint,1,opt,name=a" json:"a,omitempty"`
B int64 `protobuf:"varint,2,opt,name=b" json:"b,omitempty"`
}
func (m *SumRequest) Reset() { *m = SumRequest{} }
func (m *SumRequest) String() string { return proto.CompactTextString(m) }
func (*SumRequest) ProtoMessage() {}
func (*SumRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
// The sum response contains the result of the calculation.
type SumReply struct {
V int64 `protobuf:"varint,1,opt,name=v" json:"v,omitempty"`
Err string `protobuf:"bytes,2,opt,name=err" json:"err,omitempty"`
}
func (m *SumReply) Reset() { *m = SumReply{} }
func (m *SumReply) String() string { return proto.CompactTextString(m) }
func (*SumReply) ProtoMessage() {}
func (*SumReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
// The Concat request contains two parameters.
type ConcatRequest struct {
A string `protobuf:"bytes,1,opt,name=a" json:"a,omitempty"`
B string `protobuf:"bytes,2,opt,name=b" json:"b,omitempty"`
}
func (m *ConcatRequest) Reset() { *m = ConcatRequest{} }
func (m *ConcatRequest) String() string { return proto.CompactTextString(m) }
func (*ConcatRequest) ProtoMessage() {}
func (*ConcatRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
// The Concat response contains the result of the concatenation.
type ConcatReply struct {
V string `protobuf:"bytes,1,opt,name=v" json:"v,omitempty"`
Err string `protobuf:"bytes,2,opt,name=err" json:"err,omitempty"`
}
func (m *ConcatReply) Reset() { *m = ConcatReply{} }
func (m *ConcatReply) String() string { return proto.CompactTextString(m) }
func (*ConcatReply) ProtoMessage() {}
func (*ConcatReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
func init() {
proto.RegisterType((*SumRequest)(nil), "pb.SumRequest")
proto.RegisterType((*SumReply)(nil), "pb.SumReply")
proto.RegisterType((*ConcatRequest)(nil), "pb.ConcatRequest")
proto.RegisterType((*ConcatReply)(nil), "pb.ConcatReply")
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// Client API for Add service
type AddClient interface {
// Sums two integers.
Sum(ctx context.Context, in *SumRequest, opts ...grpc.CallOption) (*SumReply, error)
// Concatenates two strings
Concat(ctx context.Context, in *ConcatRequest, opts ...grpc.CallOption) (*ConcatReply, error)
}
type addClient struct {
cc *grpc.ClientConn
}
func NewAddClient(cc *grpc.ClientConn) AddClient {
return &addClient{cc}
}
func (c *addClient) Sum(ctx context.Context, in *SumRequest, opts ...grpc.CallOption) (*SumReply, error) {
out := new(SumReply)
err := grpc.Invoke(ctx, "/pb.Add/Sum", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *addClient) Concat(ctx context.Context, in *ConcatRequest, opts ...grpc.CallOption) (*ConcatReply, error) {
out := new(ConcatReply)
err := grpc.Invoke(ctx, "/pb.Add/Concat", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Add service
type AddServer interface {
// Sums two integers.
Sum(context.Context, *SumRequest) (*SumReply, error)
// Concatenates two strings
Concat(context.Context, *ConcatRequest) (*ConcatReply, error)
}
func RegisterAddServer(s *grpc.Server, srv AddServer) {
s.RegisterService(&_Add_serviceDesc, srv)
}
func _Add_Sum_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SumRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AddServer).Sum(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/pb.Add/Sum",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AddServer).Sum(ctx, req.(*SumRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Add_Concat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ConcatRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AddServer).Concat(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/pb.Add/Concat",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AddServer).Concat(ctx, req.(*ConcatRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Add_serviceDesc = grpc.ServiceDesc{
ServiceName: "pb.Add",
HandlerType: (*AddServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Sum",
Handler: _Add_Sum_Handler,
},
{
MethodName: "Concat",
Handler: _Add_Concat_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "addsvc.proto",
}
func init() { proto.RegisterFile("addsvc.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 189 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x49, 0x4c, 0x49, 0x29,
0x2e, 0x4b, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2a, 0x48, 0x52, 0xd2, 0xe0, 0xe2,
0x0a, 0x2e, 0xcd, 0x0d, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x11, 0xe2, 0xe1, 0x62, 0x4c, 0x94,
0x60, 0x54, 0x60, 0xd4, 0x60, 0x0e, 0x62, 0x4c, 0x04, 0xf1, 0x92, 0x24, 0x98, 0x20, 0xbc, 0x24,
0x25, 0x2d, 0x2e, 0x0e, 0xb0, 0xca, 0x82, 0x9c, 0x4a, 0x90, 0x4c, 0x19, 0x4c, 0x5d, 0x99, 0x90,
0x00, 0x17, 0x73, 0x6a, 0x51, 0x11, 0x58, 0x25, 0x67, 0x10, 0x88, 0xa9, 0xa4, 0xcd, 0xc5, 0xeb,
0x9c, 0x9f, 0x97, 0x9c, 0x58, 0x82, 0x61, 0x30, 0x27, 0x8a, 0xc1, 0x9c, 0x20, 0x83, 0x75, 0xb9,
0xb8, 0x61, 0x8a, 0x51, 0xcc, 0xe6, 0xc4, 0x6a, 0xb6, 0x51, 0x0c, 0x17, 0xb3, 0x63, 0x4a, 0x8a,
0x90, 0x2a, 0x17, 0x73, 0x70, 0x69, 0xae, 0x10, 0x9f, 0x5e, 0x41, 0x92, 0x1e, 0xc2, 0x07, 0x52,
0x3c, 0x70, 0x7e, 0x41, 0x4e, 0xa5, 0x12, 0x83, 0x90, 0x1e, 0x17, 0x1b, 0xc4, 0x70, 0x21, 0x41,
0x90, 0x0c, 0x8a, 0xab, 0xa4, 0xf8, 0x91, 0x85, 0xc0, 0xea, 0x93, 0xd8, 0xc0, 0x41, 0x63, 0x0c,
0x08, 0x00, 0x00, 0xff, 0xff, 0xdc, 0x37, 0x81, 0x99, 0x2a, 0x01, 0x00, 0x00,
}

View File

@ -1,36 +0,0 @@
syntax = "proto3";
package pb;
// The Add service definition.
service Add {
// Sums two integers.
rpc Sum (SumRequest) returns (SumReply) {}
// Concatenates two strings
rpc Concat (ConcatRequest) returns (ConcatReply) {}
}
// The sum request contains two parameters.
message SumRequest {
int64 a = 1;
int64 b = 2;
}
// The sum response contains the result of the calculation.
message SumReply {
int64 v = 1;
string err = 2;
}
// The Concat request contains two parameters.
message ConcatRequest {
string a = 1;
string b = 2;
}
// The Concat response contains the result of the concatenation.
message ConcatReply {
string v = 1;
string err = 2;
}

View File

@ -1,14 +0,0 @@
#!/usr/bin/env sh
# Install proto3 from source
# brew install autoconf automake libtool
# git clone https://github.com/google/protobuf
# ./autogen.sh ; ./configure ; make ; make install
#
# Update protoc Go bindings via
# go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
#
# See also
# https://github.com/grpc/grpc-go/tree/master/examples
protoc addsvc.proto --go_out=plugins=grpc:.

View File

@ -1,164 +0,0 @@
package addsvc
// This file contains the Service definition, and a basic service
// implementation. It also includes service middlewares.
import (
"errors"
"time"
"golang.org/x/net/context"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/metrics"
)
// Service describes a service that adds things together.
type Service interface {
Sum(ctx context.Context, a, b int) (int, error)
Concat(ctx context.Context, a, b string) (string, error)
}
// Business-domain errors like these may be served in two ways: returned
// directly by endpoints, or bundled into the response struct. Both methods can
// be made to work, but errors returned directly by endpoints are counted by
// middlewares that check errors, like circuit breakers.
//
// If you don't want that behavior -- and you probably don't -- then it's better
// to bundle errors into the response struct.
var (
// ErrTwoZeroes is an arbitrary business rule for the Add method.
ErrTwoZeroes = errors.New("can't sum two zeroes")
// ErrIntOverflow protects the Add method. We've decided that this error
// indicates a misbehaving service and should count against e.g. circuit
// breakers. So, we return it directly in endpoints, to illustrate the
// difference. In a real service, this probably wouldn't be the case.
ErrIntOverflow = errors.New("integer overflow")
// ErrMaxSizeExceeded protects the Concat method.
ErrMaxSizeExceeded = errors.New("result exceeds maximum size")
)
// These annoying helper functions are required to translate Go error types to
// and from strings, which is the type we use in our IDLs to represent errors.
// There is special casing to treat empty strings as nil errors.
func str2err(s string) error {
if s == "" {
return nil
}
return errors.New(s)
}
func err2str(err error) string {
if err == nil {
return ""
}
return err.Error()
}
// NewBasicService returns a naïve, stateless implementation of Service.
func NewBasicService() Service {
return basicService{}
}
type basicService struct{}
const (
intMax = 1<<31 - 1
intMin = -(intMax + 1)
maxLen = 102400
)
// Sum implements Service.
func (s basicService) Sum(_ context.Context, a, b int) (int, error) {
if a == 0 && b == 0 {
return 0, ErrTwoZeroes
}
if (b > 0 && a > (intMax-b)) || (b < 0 && a < (intMin-b)) {
return 0, ErrIntOverflow
}
return a + b, nil
}
// Concat implements Service.
func (s basicService) Concat(_ context.Context, a, b string) (string, error) {
if len(a)+len(b) > maxLen {
return "", ErrMaxSizeExceeded
}
return a + b, nil
}
// Middleware describes a service (as opposed to endpoint) middleware.
type Middleware func(Service) Service
// ServiceLoggingMiddleware returns a service middleware that logs the
// parameters and result of each method invocation.
func ServiceLoggingMiddleware(logger log.Logger) Middleware {
return func(next Service) Service {
return serviceLoggingMiddleware{
logger: logger,
next: next,
}
}
}
type serviceLoggingMiddleware struct {
logger log.Logger
next Service
}
func (mw serviceLoggingMiddleware) Sum(ctx context.Context, a, b int) (v int, err error) {
defer func(begin time.Time) {
mw.logger.Log(
"method", "Sum",
"a", a, "b", b, "result", v, "error", err,
"took", time.Since(begin),
)
}(time.Now())
return mw.next.Sum(ctx, a, b)
}
func (mw serviceLoggingMiddleware) Concat(ctx context.Context, a, b string) (v string, err error) {
defer func(begin time.Time) {
mw.logger.Log(
"method", "Concat",
"a", a, "b", b, "result", v, "error", err,
"took", time.Since(begin),
)
}(time.Now())
return mw.next.Concat(ctx, a, b)
}
// ServiceInstrumentingMiddleware returns a service middleware that instruments
// the number of integers summed and characters concatenated over the lifetime of
// the service.
func ServiceInstrumentingMiddleware(ints, chars metrics.Counter) Middleware {
return func(next Service) Service {
return serviceInstrumentingMiddleware{
ints: ints,
chars: chars,
next: next,
}
}
}
type serviceInstrumentingMiddleware struct {
ints metrics.Counter
chars metrics.Counter
next Service
}
func (mw serviceInstrumentingMiddleware) Sum(ctx context.Context, a, b int) (int, error) {
v, err := mw.next.Sum(ctx, a, b)
mw.ints.Add(float64(v))
return v, err
}
func (mw serviceInstrumentingMiddleware) Concat(ctx context.Context, a, b string) (string, error) {
v, err := mw.next.Concat(ctx, a, b)
mw.chars.Add(float64(len(v)))
return v, err
}

View File

@ -1,14 +0,0 @@
struct SumReply {
1: i64 value
2: string err
}
struct ConcatReply {
1: string value
2: string err
}
service AddService {
SumReply Sum(1: i64 a, 2: i64 b)
ConcatReply Concat(1: string a, 2: string b)
}

Some files were not shown because too many files have changed in this diff Show More