Moved to google.golang.org/genproto/googleapis/api/annotations
Fixes #52
This commit is contained in:
parent
024c5a4e4e
commit
c40779224f
@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"build-date": "2016-12-20T11:30:36.474403064+01:00",
|
"build-date": "2017-03-31T17:59:35.936239356+02:00",
|
||||||
"build-hostname": "Zoon-MacBook.local",
|
"build-hostname": "Valerios-MacBook-Pro.local",
|
||||||
"build-user": "zoon",
|
"build-user": "vgheri",
|
||||||
"go-pwd": "github.com/protoc-gen-gotemplate/examples/dummy",
|
"go-pwd": "github.com/moul/protoc-gen-gotemplate/examples/dummy",
|
||||||
"pwd": "/Users/zoon/Projects/gopath/src/github.com/protoc-gen-gotemplate/examples/dummy",
|
"pwd": "/Users/vgheri/go/src/github.com/moul/protoc-gen-gotemplate/examples/dummy",
|
||||||
"debug": false,
|
"debug": false,
|
||||||
"destination-dir": ".",
|
"destination-dir": ".",
|
||||||
"file": {
|
"file": {
|
||||||
@ -842,40 +842,27 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"environment": [
|
"environment": [
|
||||||
"TERM_PROGRAM=iTerm.app",
|
"TERM_PROGRAM=platformio-ide-terminal",
|
||||||
"ANDROID_HOME=/usr/local/opt/android-sdk",
|
|
||||||
"TERM=xterm-256color",
|
"TERM=xterm-256color",
|
||||||
"SHELL=/bin/zsh",
|
"SHELL=/bin/bash",
|
||||||
"MAKEFLAGS=",
|
"MAKEFLAGS=",
|
||||||
"TMPDIR=/var/folders/sq/wlptrlpn4v52xv7xpsgw0fc80000gn/T/",
|
"TMPDIR=/var/folders/9v/4b6h7ftd5ldfqv7gr82mbj340000gn/T/",
|
||||||
"Apple_PubSub_Socket_Render=/private/tmp/com.apple.launchd.q9vxeee00S/Render",
|
"Apple_PubSub_Socket_Render=/private/tmp/com.apple.launchd.Zt6NPL5gZZ/Render",
|
||||||
"TERM_PROGRAM_VERSION=3.0.12",
|
"USER=vgheri",
|
||||||
"TERM_SESSION_ID=w0t0p0:6D29EB08-1B96-41B8-8672-0B035605AEE5",
|
"SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.UvxCW75B4v/Listeners",
|
||||||
"ZSH=/Users/zoon/.oh-my-zsh",
|
|
||||||
"USER=zoon",
|
|
||||||
"SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.5ZcNGIwTRZ/Listeners",
|
|
||||||
"__CF_USER_TEXT_ENCODING=0x1F5:0x0:0x0",
|
"__CF_USER_TEXT_ENCODING=0x1F5:0x0:0x0",
|
||||||
"MAKELEVEL=2",
|
"MAKELEVEL=2",
|
||||||
"PAGER=less",
|
|
||||||
"MFLAGS=",
|
"MFLAGS=",
|
||||||
"LSCOLORS=Gxfxcxdxbxegedabagacad",
|
"PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/share/dotnet:/usr/local/go/bin:/usr/local/protoc/bin:/Users/vgheri/go/bin:/usr/local/protoc/bin:/Users/vgheri/go/bin",
|
||||||
"PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/zoon/Projects/gopath/bin",
|
"_=/usr/local/protoc/bin/protoc",
|
||||||
"_=/usr/local/bin/protoc",
|
"PWD=/Users/vgheri/go/src/github.com/moul/protoc-gen-gotemplate/examples/dummy",
|
||||||
"PWD=/Users/zoon/Projects/gopath/src/github.com/protoc-gen-gotemplate/examples/dummy",
|
|
||||||
"JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_112.jdk/Contents/Home",
|
|
||||||
"EDITOR=micro",
|
|
||||||
"LANG=en_US.UTF-8",
|
"LANG=en_US.UTF-8",
|
||||||
"ANDROID_SDK=/usr/local/opt/android-sdk",
|
|
||||||
"ITERM_PROFILE=Default",
|
|
||||||
"XPC_FLAGS=0x0",
|
"XPC_FLAGS=0x0",
|
||||||
"XPC_SERVICE_NAME=0",
|
"XPC_SERVICE_NAME=0",
|
||||||
"COLORFGBG=15;0",
|
"HISTCONTROL=ignoreboth:erasedups",
|
||||||
"SHLVL=3",
|
"SHLVL=15",
|
||||||
"HOME=/Users/zoon",
|
"HOME=/Users/vgheri",
|
||||||
"ITERM_SESSION_ID=w0t0p0:6D29EB08-1B96-41B8-8672-0B035605AEE5",
|
"LOGNAME=vgheri",
|
||||||
"LESS=-R",
|
"GOPATH=/Users/vgheri/go"
|
||||||
"LOGNAME=zoon",
|
|
||||||
"LC_CTYPE=UTF-8",
|
|
||||||
"GOPATH=/Users/zoon/Projects/gopath"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -55,13 +55,13 @@ export type TestMessage = {
|
|||||||
setRList?: (r: Array<number>) => void;
|
setRList?: (r: Array<number>) => void;
|
||||||
addR?: (r: number) => void;
|
addR?: (r: number) => void;
|
||||||
clearRList?: () => void;
|
clearRList?: () => void;
|
||||||
getU?: () => TestEnum;
|
getU?: () => test$TestEnum;
|
||||||
setU?: (u: TestEnum) => void;
|
setU?: (u: test$TestEnum) => void;
|
||||||
getV?: () => TestMessage$TestNestedEnum;
|
getV?: () => test$TestMessage$TestNestedEnum;
|
||||||
setV?: (v: TestMessage$TestNestedEnum) => void;
|
setV?: (v: test$TestMessage$TestNestedEnum) => void;
|
||||||
getWList?: () => Array<TestMessage$TestNestedMessage>;
|
getWList?: () => Array<test$TestMessage$TestNestedMessage>;
|
||||||
setWList?: (w: Array<TestMessage$TestNestedMessage>) => void;
|
setWList?: (w: Array<test$TestMessage$TestNestedMessage>) => void;
|
||||||
addW?: (w: TestMessage$TestNestedMessage) => void;
|
addW?: (w: test$TestMessage$TestNestedMessage) => void;
|
||||||
clearWList?: () => void;
|
clearWList?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -70,8 +70,8 @@ export type TestMessage = {
|
|||||||
|
|
||||||
|
|
||||||
export type TestNoStreamRequest = {
|
export type TestNoStreamRequest = {
|
||||||
getMessage?: () => TestMessage;
|
getMessage?: () => test$TestMessage;
|
||||||
setMessage?: (message: TestMessage) => void;
|
setMessage?: (message: test$TestMessage) => void;
|
||||||
clearMessage?: () => void;
|
clearMessage?: () => void;
|
||||||
hasMessage?: () => boolean;
|
hasMessage?: () => boolean;
|
||||||
};
|
};
|
||||||
@ -81,8 +81,8 @@ export type TestNoStreamRequest = {
|
|||||||
|
|
||||||
|
|
||||||
export type TestNoStreamReply = {
|
export type TestNoStreamReply = {
|
||||||
getMessage?: () => TestMessage;
|
getMessage?: () => test$TestMessage;
|
||||||
setMessage?: (message: TestMessage) => void;
|
setMessage?: (message: test$TestMessage) => void;
|
||||||
clearMessage?: () => void;
|
clearMessage?: () => void;
|
||||||
hasMessage?: () => boolean;
|
hasMessage?: () => boolean;
|
||||||
getErrMsg?: () => string;
|
getErrMsg?: () => string;
|
||||||
@ -94,8 +94,8 @@ export type TestNoStreamReply = {
|
|||||||
|
|
||||||
|
|
||||||
export type TestStreamRequestRequest = {
|
export type TestStreamRequestRequest = {
|
||||||
getMessage?: () => TestMessage;
|
getMessage?: () => test$TestMessage;
|
||||||
setMessage?: (message: TestMessage) => void;
|
setMessage?: (message: test$TestMessage) => void;
|
||||||
clearMessage?: () => void;
|
clearMessage?: () => void;
|
||||||
hasMessage?: () => boolean;
|
hasMessage?: () => boolean;
|
||||||
};
|
};
|
||||||
@ -105,8 +105,8 @@ export type TestStreamRequestRequest = {
|
|||||||
|
|
||||||
|
|
||||||
export type TestStreamRequestReply = {
|
export type TestStreamRequestReply = {
|
||||||
getMessage?: () => TestMessage;
|
getMessage?: () => test$TestMessage;
|
||||||
setMessage?: (message: TestMessage) => void;
|
setMessage?: (message: test$TestMessage) => void;
|
||||||
clearMessage?: () => void;
|
clearMessage?: () => void;
|
||||||
hasMessage?: () => boolean;
|
hasMessage?: () => boolean;
|
||||||
getErrMsg?: () => string;
|
getErrMsg?: () => string;
|
||||||
@ -118,8 +118,8 @@ export type TestStreamRequestReply = {
|
|||||||
|
|
||||||
|
|
||||||
export type TestStreamReplyRequest = {
|
export type TestStreamReplyRequest = {
|
||||||
getMessage?: () => TestMessage;
|
getMessage?: () => test$TestMessage;
|
||||||
setMessage?: (message: TestMessage) => void;
|
setMessage?: (message: test$TestMessage) => void;
|
||||||
clearMessage?: () => void;
|
clearMessage?: () => void;
|
||||||
hasMessage?: () => boolean;
|
hasMessage?: () => boolean;
|
||||||
};
|
};
|
||||||
@ -129,8 +129,8 @@ export type TestStreamReplyRequest = {
|
|||||||
|
|
||||||
|
|
||||||
export type TestStreamReplyReply = {
|
export type TestStreamReplyReply = {
|
||||||
getMessage?: () => TestMessage;
|
getMessage?: () => test$TestMessage;
|
||||||
setMessage?: (message: TestMessage) => void;
|
setMessage?: (message: test$TestMessage) => void;
|
||||||
clearMessage?: () => void;
|
clearMessage?: () => void;
|
||||||
hasMessage?: () => boolean;
|
hasMessage?: () => boolean;
|
||||||
getErrMsg?: () => string;
|
getErrMsg?: () => string;
|
||||||
@ -142,8 +142,8 @@ export type TestStreamReplyReply = {
|
|||||||
|
|
||||||
|
|
||||||
export type TestStreamBothRequest = {
|
export type TestStreamBothRequest = {
|
||||||
getMessage?: () => TestMessage;
|
getMessage?: () => test$TestMessage;
|
||||||
setMessage?: (message: TestMessage) => void;
|
setMessage?: (message: test$TestMessage) => void;
|
||||||
clearMessage?: () => void;
|
clearMessage?: () => void;
|
||||||
hasMessage?: () => boolean;
|
hasMessage?: () => boolean;
|
||||||
};
|
};
|
||||||
@ -153,8 +153,8 @@ export type TestStreamBothRequest = {
|
|||||||
|
|
||||||
|
|
||||||
export type TestStreamBothReply = {
|
export type TestStreamBothReply = {
|
||||||
getMessage?: () => TestMessage;
|
getMessage?: () => test$TestMessage;
|
||||||
setMessage?: (message: TestMessage) => void;
|
setMessage?: (message: test$TestMessage) => void;
|
||||||
clearMessage?: () => void;
|
clearMessage?: () => void;
|
||||||
hasMessage?: () => boolean;
|
hasMessage?: () => boolean;
|
||||||
getErrMsg?: () => string;
|
getErrMsg?: () => string;
|
||||||
|
@ -163,7 +163,7 @@ func init() { proto.RegisterFile("services/session/session.proto", fileDescripto
|
|||||||
|
|
||||||
var fileDescriptor0 = []byte{
|
var fileDescriptor0 = []byte{
|
||||||
// 188 bytes of a gzipped FileDescriptorProto
|
// 188 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2b, 0x4e, 0x2d, 0x2a,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x92, 0x2b, 0x4e, 0x2d, 0x2a,
|
||||||
0xcb, 0x4c, 0x4e, 0x2d, 0xd6, 0x2f, 0x4e, 0x2d, 0x2e, 0xce, 0xcc, 0xcf, 0x83, 0xd1, 0x7a, 0x05,
|
0xcb, 0x4c, 0x4e, 0x2d, 0xd6, 0x2f, 0x4e, 0x2d, 0x2e, 0xce, 0xcc, 0xcf, 0x83, 0xd1, 0x7a, 0x05,
|
||||||
0x45, 0xf9, 0x25, 0xf9, 0x42, 0xec, 0x50, 0xae, 0x92, 0x1b, 0x17, 0x8f, 0x4f, 0x7e, 0x7a, 0x66,
|
0x45, 0xf9, 0x25, 0xf9, 0x42, 0xec, 0x50, 0xae, 0x92, 0x1b, 0x17, 0x8f, 0x4f, 0x7e, 0x7a, 0x66,
|
||||||
0x5e, 0x50, 0x6a, 0x61, 0x69, 0x6a, 0x71, 0x89, 0x90, 0x14, 0x17, 0x47, 0x69, 0x71, 0x6a, 0x51,
|
0x5e, 0x50, 0x6a, 0x61, 0x69, 0x6a, 0x71, 0x89, 0x90, 0x14, 0x17, 0x47, 0x69, 0x71, 0x6a, 0x51,
|
||||||
|
@ -335,7 +335,7 @@ func init() { proto.RegisterFile("services/sprint/sprint.proto", fileDescriptor0
|
|||||||
|
|
||||||
var fileDescriptor0 = []byte{
|
var fileDescriptor0 = []byte{
|
||||||
// 290 bytes of a gzipped FileDescriptorProto
|
// 290 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x52, 0x4d, 0x4b, 0xc3, 0x40,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x52, 0x4d, 0x4b, 0xc3, 0x40,
|
||||||
0x10, 0x6d, 0xaa, 0x44, 0x32, 0xa5, 0xa5, 0x1d, 0x0f, 0xc6, 0xa8, 0x20, 0x8b, 0x14, 0x4f, 0x11,
|
0x10, 0x6d, 0xaa, 0x44, 0x32, 0xa5, 0xa5, 0x1d, 0x0f, 0xc6, 0xa8, 0x20, 0x8b, 0x14, 0x4f, 0x11,
|
||||||
0xea, 0x2f, 0x68, 0x3d, 0x28, 0x88, 0x97, 0xb4, 0xf7, 0x12, 0xbb, 0x43, 0x09, 0xd8, 0x24, 0xee,
|
0xea, 0x2f, 0x68, 0x3d, 0x28, 0x88, 0x97, 0xb4, 0xf7, 0x12, 0xbb, 0x43, 0x09, 0xd8, 0x24, 0xee,
|
||||||
0xac, 0xfe, 0x5f, 0xff, 0x89, 0xb0, 0xbb, 0xcd, 0x57, 0x8b, 0x27, 0x4f, 0xc9, 0xec, 0xbc, 0x7d,
|
0xac, 0xfe, 0x5f, 0xff, 0x89, 0xb0, 0xbb, 0xcd, 0x57, 0x8b, 0x27, 0x4f, 0xc9, 0xec, 0xbc, 0x7d,
|
||||||
|
@ -258,7 +258,7 @@ func init() { proto.RegisterFile("services/user/user.proto", fileDescriptor0) }
|
|||||||
|
|
||||||
var fileDescriptor0 = []byte{
|
var fileDescriptor0 = []byte{
|
||||||
// 236 bytes of a gzipped FileDescriptorProto
|
// 236 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x28, 0x4e, 0x2d, 0x2a,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x92, 0x28, 0x4e, 0x2d, 0x2a,
|
||||||
0xcb, 0x4c, 0x4e, 0x2d, 0xd6, 0x2f, 0x2d, 0x4e, 0x2d, 0x02, 0x13, 0x7a, 0x05, 0x45, 0xf9, 0x25,
|
0xcb, 0x4c, 0x4e, 0x2d, 0xd6, 0x2f, 0x2d, 0x4e, 0x2d, 0x02, 0x13, 0x7a, 0x05, 0x45, 0xf9, 0x25,
|
||||||
0xf9, 0x42, 0x2c, 0x20, 0xb6, 0x92, 0x3a, 0x97, 0xa0, 0x73, 0x51, 0x6a, 0x62, 0x49, 0x6a, 0x68,
|
0xf9, 0x42, 0x2c, 0x20, 0xb6, 0x92, 0x3a, 0x97, 0xa0, 0x73, 0x51, 0x6a, 0x62, 0x49, 0x6a, 0x68,
|
||||||
0x71, 0x6a, 0x51, 0x50, 0x6a, 0x61, 0x69, 0x6a, 0x71, 0x89, 0x90, 0x10, 0x17, 0x4b, 0x5e, 0x62,
|
0x71, 0x6a, 0x51, 0x50, 0x6a, 0x61, 0x69, 0x6a, 0x71, 0x89, 0x90, 0x10, 0x17, 0x4b, 0x5e, 0x62,
|
||||||
|
@ -4,18 +4,18 @@
|
|||||||
<loc>/posts</loc>
|
<loc>/posts</loc>
|
||||||
<priority>0.5</priority>
|
<priority>0.5</priority>
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<lastmod>2017-03-16</lastmod>
|
<lastmod>2017-03-31</lastmod>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>/authors</loc>
|
<loc>/authors</loc>
|
||||||
<priority>0.5</priority>
|
<priority>0.5</priority>
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<lastmod>2017-03-16</lastmod>
|
<lastmod>2017-03-31</lastmod>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>/comments</loc>
|
<loc>/comments</loc>
|
||||||
<priority>0.5</priority>
|
<priority>0.5</priority>
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<lastmod>2017-03-16</lastmod>
|
<lastmod>2017-03-31</lastmod>
|
||||||
</url>
|
</url>
|
||||||
</urlset>
|
</urlset>
|
||||||
|
73
glide.lock
generated
73
glide.lock
generated
@ -1,21 +1,86 @@
|
|||||||
hash: 488701437e53b39667ed5a84ed1500e727ea8d4902d804c3ea7eb50b1fc022a1
|
hash: 952872c40b8721f6bcd8bc1eab4f174aa0aebc5e8d9acb70ecdffb91068845b5
|
||||||
updated: 2016-12-21T10:24:42.512734471+01:00
|
updated: 2017-03-31T17:55:29.337081111+02:00
|
||||||
imports:
|
imports:
|
||||||
- name: github.com/aokoli/goutils
|
- name: github.com/aokoli/goutils
|
||||||
version: 9c37978a95bd5c709a15883b6242714ea6709e64
|
version: 9c37978a95bd5c709a15883b6242714ea6709e64
|
||||||
|
- name: github.com/dgrijalva/jwt-go
|
||||||
|
version: d2709f9f1f31ebcda9651b03077758c1f3a0018c
|
||||||
|
- name: github.com/go-kit/kit
|
||||||
|
version: fadad6fffe0466b19df9efd9acde5c9a52df5fa4
|
||||||
|
subpackages:
|
||||||
|
- auth/jwt
|
||||||
|
- endpoint
|
||||||
|
- log
|
||||||
|
- transport/grpc
|
||||||
|
- transport/http
|
||||||
|
- name: github.com/go-logfmt/logfmt
|
||||||
|
version: 390ab7935ee28ec6b286364bba9b4dd6410cb3d5
|
||||||
|
- name: github.com/go-stack/stack
|
||||||
|
version: 100eb0c0a9c5b306ca2fb4f165df21d80ada4b82
|
||||||
- name: github.com/golang/protobuf
|
- name: github.com/golang/protobuf
|
||||||
version: 8ee79997227bf9b34611aee7946ae64735e6fd93
|
version: 2bba0603135d7d7f5cb73b2125beeda19c09f4ef
|
||||||
subpackages:
|
subpackages:
|
||||||
- proto
|
- proto
|
||||||
- protoc-gen-go/descriptor
|
- protoc-gen-go/descriptor
|
||||||
- protoc-gen-go/generator
|
- protoc-gen-go/generator
|
||||||
- protoc-gen-go/plugin
|
- protoc-gen-go/plugin
|
||||||
|
- name: github.com/gorilla/handlers
|
||||||
|
version: 13d73096a474cac93275c679c7b8a2dc17ddba82
|
||||||
|
- name: github.com/grpc-ecosystem/grpc-gateway
|
||||||
|
version: 18d159699f2e83fc5bb9ef2f79465ca3f3122676
|
||||||
|
subpackages:
|
||||||
|
- third_party/googleapis/google/api
|
||||||
- name: github.com/huandu/xstrings
|
- name: github.com/huandu/xstrings
|
||||||
version: 3959339b333561bf62a38b424fd41517c2c90f40
|
version: 3959339b333561bf62a38b424fd41517c2c90f40
|
||||||
- name: github.com/kr/fs
|
- name: github.com/kr/fs
|
||||||
version: 2788f0dbd16903de03cb8186e5c7d97b69ad387b
|
version: 2788f0dbd16903de03cb8186e5c7d97b69ad387b
|
||||||
|
- name: github.com/kr/logfmt
|
||||||
|
version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0
|
||||||
|
- name: github.com/Masterminds/semver
|
||||||
|
version: 59c29afe1a994eacb71c833025ca7acf874bb1da
|
||||||
- name: github.com/Masterminds/sprig
|
- name: github.com/Masterminds/sprig
|
||||||
version: 69011c0cd9b4d2e0733c4d9e2c8e2a5a0d0a2f2f
|
version: cd9291e11ed2b78cdc1991bdd3e8adf51d6236bb
|
||||||
- name: github.com/satori/go.uuid
|
- name: github.com/satori/go.uuid
|
||||||
version: 879c5887cd475cd7864858769793b2ceb0d44feb
|
version: 879c5887cd475cd7864858769793b2ceb0d44feb
|
||||||
|
- name: golang.org/x/crypto
|
||||||
|
version: 3cb07270c9455e8ad27956a70891c962d121a228
|
||||||
|
subpackages:
|
||||||
|
- pbkdf2
|
||||||
|
- scrypt
|
||||||
|
- name: golang.org/x/net
|
||||||
|
version: ffcf1bedda3b04ebb15a168a59800a73d6dc0f4d
|
||||||
|
subpackages:
|
||||||
|
- context
|
||||||
|
- context/ctxhttp
|
||||||
|
- http2
|
||||||
|
- http2/hpack
|
||||||
|
- idna
|
||||||
|
- internal/timeseries
|
||||||
|
- lex/httplex
|
||||||
|
- trace
|
||||||
|
- name: golang.org/x/text
|
||||||
|
version: c3b0cbb40eb9a862fdc91f79bed8c7ac8e7a36e8
|
||||||
|
subpackages:
|
||||||
|
- secure/bidirule
|
||||||
|
- transform
|
||||||
|
- unicode/bidi
|
||||||
|
- unicode/norm
|
||||||
|
- name: google.golang.org/genproto
|
||||||
|
version: de9f5e90fe9b278809363f08c2072d2f2a429de7
|
||||||
|
subpackages:
|
||||||
|
- googleapis/api/annotations
|
||||||
|
- name: google.golang.org/grpc
|
||||||
|
version: cdee119ee21e61eef7093a41ba148fa83585e143
|
||||||
|
subpackages:
|
||||||
|
- codes
|
||||||
|
- credentials
|
||||||
|
- grpclog
|
||||||
|
- internal
|
||||||
|
- keepalive
|
||||||
|
- metadata
|
||||||
|
- naming
|
||||||
|
- peer
|
||||||
|
- stats
|
||||||
|
- tap
|
||||||
|
- transport
|
||||||
testImports: []
|
testImports: []
|
||||||
|
@ -9,3 +9,6 @@ import:
|
|||||||
- package: github.com/kr/fs
|
- package: github.com/kr/fs
|
||||||
- package: github.com/Masterminds/sprig
|
- package: github.com/Masterminds/sprig
|
||||||
- package: github.com/huandu/xstrings
|
- package: github.com/huandu/xstrings
|
||||||
|
- package: google.golang.org/genproto
|
||||||
|
subpackages:
|
||||||
|
- googleapis/api/annotations
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
"github.com/golang/protobuf/protoc-gen-go/descriptor"
|
"github.com/golang/protobuf/protoc-gen-go/descriptor"
|
||||||
options "github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api"
|
options "google.golang.org/genproto/googleapis/api/annotations"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ProtoHelpersFuncMap = template.FuncMap{
|
var ProtoHelpersFuncMap = template.FuncMap{
|
||||||
|
25
vendor/github.com/Masterminds/semver/.travis.yml
generated
vendored
Normal file
25
vendor/github.com/Masterminds/semver/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
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
|
45
vendor/github.com/Masterminds/semver/CHANGELOG.md
generated
vendored
Normal file
45
vendor/github.com/Masterminds/semver/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Release 1.2.2 (2016-12-13)
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
- #34: Fixed issue where hyphen range was not working with pre-release parsing.
|
||||||
|
|
||||||
|
# Release 1.2.1 (2016-11-28)
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
- #24: Fixed edge case issue where constraint "> 0" does not handle "0.0.1-alpha"
|
||||||
|
properly.
|
||||||
|
|
||||||
|
# Release 1.2.0 (2016-11-04)
|
||||||
|
|
||||||
|
## Added
|
||||||
|
- #20: Added MustParse function for versions (thanks @adamreese)
|
||||||
|
- #15: Added increment methods on versions (thanks @mh-cbon)
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
- Issue #21: Per the SemVer spec (section 9) a pre-release is unstable and
|
||||||
|
might not satisfy the intended compatibility. The change here ignores pre-releases
|
||||||
|
on constraint checks (e.g., ~ or ^) when a pre-release is not part of the
|
||||||
|
constraint. For example, `^1.2.3` will ignore pre-releases while
|
||||||
|
`^1.2.3-alpha` will include them.
|
||||||
|
|
||||||
|
# Release 1.1.1 (2016-06-30)
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
- Issue #9: Speed up version comparison performance (thanks @sdboyer)
|
||||||
|
- Issue #8: Added benchmarks (thanks @sdboyer)
|
||||||
|
- Updated Go Report Card URL to new location
|
||||||
|
- Updated Readme to add code snippet formatting (thanks @mh-cbon)
|
||||||
|
- Updating tagging to v[SemVer] structure for compatibility with other tools.
|
||||||
|
|
||||||
|
# Release 1.1.0 (2016-03-11)
|
||||||
|
|
||||||
|
- Issue #2: Implemented validation to provide reasons a versions failed a
|
||||||
|
constraint.
|
||||||
|
|
||||||
|
# Release 1.0.1 (2015-12-31)
|
||||||
|
|
||||||
|
- Fixed #1: * constraint failing on valid versions.
|
||||||
|
|
||||||
|
# Release 1.0.0 (2015-10-20)
|
||||||
|
|
||||||
|
- Initial release
|
20
vendor/github.com/Masterminds/semver/LICENSE.txt
generated
vendored
Normal file
20
vendor/github.com/Masterminds/semver/LICENSE.txt
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
The Masterminds
|
||||||
|
Copyright (C) 2014-2015, Matt Butcher and Matt Farina
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
36
vendor/github.com/Masterminds/semver/Makefile
generated
vendored
Normal file
36
vendor/github.com/Masterminds/semver/Makefile
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
.PHONY: setup
|
||||||
|
setup:
|
||||||
|
go get -u gopkg.in/alecthomas/gometalinter.v1
|
||||||
|
gometalinter.v1 --install
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test: validate lint
|
||||||
|
@echo "==> Running tests"
|
||||||
|
go test -v
|
||||||
|
|
||||||
|
.PHONY: validate
|
||||||
|
validate:
|
||||||
|
@echo "==> Running static validations"
|
||||||
|
@gometalinter.v1 \
|
||||||
|
--disable-all \
|
||||||
|
--enable deadcode \
|
||||||
|
--severity deadcode:error \
|
||||||
|
--enable gofmt \
|
||||||
|
--enable gosimple \
|
||||||
|
--enable ineffassign \
|
||||||
|
--enable misspell \
|
||||||
|
--enable vet \
|
||||||
|
--tests \
|
||||||
|
--vendor \
|
||||||
|
--deadline 60s \
|
||||||
|
./... || exit_code=1
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint:
|
||||||
|
@echo "==> Running linters"
|
||||||
|
@gometalinter.v1 \
|
||||||
|
--disable-all \
|
||||||
|
--enable golint \
|
||||||
|
--vendor \
|
||||||
|
--deadline 60s \
|
||||||
|
./... || :
|
163
vendor/github.com/Masterminds/semver/README.md
generated
vendored
Normal file
163
vendor/github.com/Masterminds/semver/README.md
generated
vendored
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
# SemVer
|
||||||
|
|
||||||
|
The `semver` package provides the ability to work with [Semantic Versions](http://semver.org) in Go. Specifically it provides the ability to:
|
||||||
|
|
||||||
|
* Parse semantic versions
|
||||||
|
* Sort semantic versions
|
||||||
|
* Check if a semantic version fits within a set of constraints
|
||||||
|
* Optionally work with a `v` prefix
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/Masterminds/semver.svg)](https://travis-ci.org/Masterminds/semver) [![Build status](https://ci.appveyor.com/api/projects/status/jfk66lib7hb985k8/branch/master?svg=true&passingText=windows%20build%20passing&failingText=windows%20build%20failing)](https://ci.appveyor.com/project/mattfarina/semver/branch/master) [![GoDoc](https://godoc.org/github.com/Masterminds/semver?status.png)](https://godoc.org/github.com/Masterminds/semver) [![Go Report Card](https://goreportcard.com/badge/github.com/Masterminds/semver)](https://goreportcard.com/report/github.com/Masterminds/semver)
|
||||||
|
|
||||||
|
## Parsing Semantic Versions
|
||||||
|
|
||||||
|
To parse a semantic version use the `NewVersion` function. For example,
|
||||||
|
|
||||||
|
```go
|
||||||
|
v, err := semver.NewVersion("1.2.3-beta.1+build345")
|
||||||
|
```
|
||||||
|
|
||||||
|
If there is an error the version wasn't parseable. The version object has methods
|
||||||
|
to get the parts of the version, compare it to other versions, convert the
|
||||||
|
version back into a string, and get the original string. For more details
|
||||||
|
please see the [documentation](https://godoc.org/github.com/Masterminds/semver).
|
||||||
|
|
||||||
|
## Sorting Semantic Versions
|
||||||
|
|
||||||
|
A set of versions can be sorted using the [`sort`](https://golang.org/pkg/sort/)
|
||||||
|
package from the standard library. For example,
|
||||||
|
|
||||||
|
```go
|
||||||
|
raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",}
|
||||||
|
vs := make([]*semver.Version, len(raw))
|
||||||
|
for i, r := range raw {
|
||||||
|
v, err := semver.NewVersion(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error parsing version: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vs[i] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(semver.Collection(vs))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Checking Version Constraints
|
||||||
|
|
||||||
|
Checking a version against version constraints is one of the most featureful
|
||||||
|
parts of the package.
|
||||||
|
|
||||||
|
```go
|
||||||
|
c, err := semver.NewConstraint(">= 1.2.3")
|
||||||
|
if err != nil {
|
||||||
|
// Handle constraint not being parseable.
|
||||||
|
}
|
||||||
|
|
||||||
|
v, _ := semver.NewVersion("1.3")
|
||||||
|
if err != nil {
|
||||||
|
// Handle version not being parseable.
|
||||||
|
}
|
||||||
|
// Check if the version meets the constraints. The a variable will be true.
|
||||||
|
a := c.Check(v)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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`
|
||||||
|
|
||||||
|
# Validation
|
||||||
|
|
||||||
|
In addition to testing a version against a constraint, a version can be validated
|
||||||
|
against a constraint. When validation fails a slice of errors containing why a
|
||||||
|
version didn't meet the constraint is returned. For example,
|
||||||
|
|
||||||
|
```go
|
||||||
|
c, err := semver.NewConstraint("<= 1.2.3, >= 1.4")
|
||||||
|
if err != nil {
|
||||||
|
// Handle constraint not being parseable.
|
||||||
|
}
|
||||||
|
|
||||||
|
v, _ := semver.NewVersion("1.3")
|
||||||
|
if err != nil {
|
||||||
|
// Handle version not being parseable.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate a version against a constraint.
|
||||||
|
a, msgs := c.Validate(v)
|
||||||
|
// a is false
|
||||||
|
for _, m := range msgs {
|
||||||
|
fmt.Println(m)
|
||||||
|
|
||||||
|
// Loops over the errors which would read
|
||||||
|
// "1.3 is greater than 1.2.3"
|
||||||
|
// "1.3 is less than 1.4"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# Contribute
|
||||||
|
|
||||||
|
If you find an issue or want to contribute please file an [issue](https://github.com/Masterminds/semver/issues)
|
||||||
|
or [create a pull request](https://github.com/Masterminds/semver/pulls).
|
44
vendor/github.com/Masterminds/semver/appveyor.yml
generated
vendored
Normal file
44
vendor/github.com/Masterminds/semver/appveyor.yml
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
version: build-{build}.{branch}
|
||||||
|
|
||||||
|
clone_folder: C:\gopath\src\github.com\Masterminds\semver
|
||||||
|
shallow_clone: true
|
||||||
|
|
||||||
|
environment:
|
||||||
|
GOPATH: C:\gopath
|
||||||
|
|
||||||
|
platform:
|
||||||
|
- x64
|
||||||
|
|
||||||
|
install:
|
||||||
|
- go version
|
||||||
|
- go env
|
||||||
|
- go get -u gopkg.in/alecthomas/gometalinter.v1
|
||||||
|
- set PATH=%PATH%;%GOPATH%\bin
|
||||||
|
- gometalinter.v1.exe --install
|
||||||
|
|
||||||
|
build_script:
|
||||||
|
- go install -v ./...
|
||||||
|
|
||||||
|
test_script:
|
||||||
|
- "gometalinter.v1 \
|
||||||
|
--disable-all \
|
||||||
|
--enable deadcode \
|
||||||
|
--severity deadcode:error \
|
||||||
|
--enable gofmt \
|
||||||
|
--enable gosimple \
|
||||||
|
--enable ineffassign \
|
||||||
|
--enable misspell \
|
||||||
|
--enable vet \
|
||||||
|
--tests \
|
||||||
|
--vendor \
|
||||||
|
--deadline 60s \
|
||||||
|
./... || exit_code=1"
|
||||||
|
- "gometalinter.v1 \
|
||||||
|
--disable-all \
|
||||||
|
--enable golint \
|
||||||
|
--vendor \
|
||||||
|
--deadline 60s \
|
||||||
|
./... || :"
|
||||||
|
- go test -v
|
||||||
|
|
||||||
|
deploy: off
|
157
vendor/github.com/Masterminds/semver/benchmark_test.go
generated
vendored
Normal file
157
vendor/github.com/Masterminds/semver/benchmark_test.go
generated
vendored
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
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)
|
||||||
|
}
|
24
vendor/github.com/Masterminds/semver/collection.go
generated
vendored
Normal file
24
vendor/github.com/Masterminds/semver/collection.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package semver
|
||||||
|
|
||||||
|
// Collection is a collection of Version instances and implements the sort
|
||||||
|
// interface. See the sort package for more details.
|
||||||
|
// https://golang.org/pkg/sort/
|
||||||
|
type Collection []*Version
|
||||||
|
|
||||||
|
// Len returns the length of a collection. The number of Version instances
|
||||||
|
// on the slice.
|
||||||
|
func (c Collection) Len() int {
|
||||||
|
return len(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less is needed for the sort interface to compare two Version objects on the
|
||||||
|
// slice. If checks if one is less than the other.
|
||||||
|
func (c Collection) Less(i, j int) bool {
|
||||||
|
return c[i].LessThan(c[j])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap is needed for the sort interface to replace the Version objects
|
||||||
|
// at two different positions in the slice.
|
||||||
|
func (c Collection) Swap(i, j int) {
|
||||||
|
c[i], c[j] = c[j], c[i]
|
||||||
|
}
|
46
vendor/github.com/Masterminds/semver/collection_test.go
generated
vendored
Normal file
46
vendor/github.com/Masterminds/semver/collection_test.go
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
421
vendor/github.com/Masterminds/semver/constraints.go
generated
vendored
Normal file
421
vendor/github.com/Masterminds/semver/constraints.go
generated
vendored
Normal file
@ -0,0 +1,421 @@
|
|||||||
|
package semver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Constraints is one or more constraint that a semantic version can be
|
||||||
|
// checked against.
|
||||||
|
type Constraints struct {
|
||||||
|
constraints [][]*constraint
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConstraint returns a Constraints instance that a Version instance can
|
||||||
|
// be checked against. If there is a parse error it will be returned.
|
||||||
|
func NewConstraint(c string) (*Constraints, error) {
|
||||||
|
|
||||||
|
// Rewrite - ranges into a comparison operation.
|
||||||
|
c = rewriteRange(c)
|
||||||
|
|
||||||
|
ors := strings.Split(c, "||")
|
||||||
|
or := make([][]*constraint, len(ors))
|
||||||
|
for k, v := range ors {
|
||||||
|
cs := strings.Split(v, ",")
|
||||||
|
result := make([]*constraint, len(cs))
|
||||||
|
for i, s := range cs {
|
||||||
|
pc, err := parseConstraint(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result[i] = pc
|
||||||
|
}
|
||||||
|
or[k] = result
|
||||||
|
}
|
||||||
|
|
||||||
|
o := &Constraints{constraints: or}
|
||||||
|
return o, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check tests if a version satisfies the constraints.
|
||||||
|
func (cs Constraints) Check(v *Version) bool {
|
||||||
|
// loop over the ORs and check the inner ANDs
|
||||||
|
for _, o := range cs.constraints {
|
||||||
|
joy := true
|
||||||
|
for _, c := range o {
|
||||||
|
if !c.check(v) {
|
||||||
|
joy = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if joy {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate checks if a version satisfies a constraint. If not a slice of
|
||||||
|
// reasons for the failure are returned in addition to a bool.
|
||||||
|
func (cs Constraints) Validate(v *Version) (bool, []error) {
|
||||||
|
// loop over the ORs and check the inner ANDs
|
||||||
|
var e []error
|
||||||
|
for _, o := range cs.constraints {
|
||||||
|
joy := true
|
||||||
|
for _, c := range o {
|
||||||
|
if !c.check(v) {
|
||||||
|
em := fmt.Errorf(c.msg, v, c.orig)
|
||||||
|
e = append(e, em)
|
||||||
|
joy = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if joy {
|
||||||
|
return true, []error{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, e
|
||||||
|
}
|
||||||
|
|
||||||
|
var constraintOps map[string]cfunc
|
||||||
|
var constraintMsg map[string]string
|
||||||
|
var constraintRegex *regexp.Regexp
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
constraintOps = map[string]cfunc{
|
||||||
|
"": constraintTildeOrEqual,
|
||||||
|
"=": constraintTildeOrEqual,
|
||||||
|
"!=": constraintNotEqual,
|
||||||
|
">": constraintGreaterThan,
|
||||||
|
"<": constraintLessThan,
|
||||||
|
">=": constraintGreaterThanEqual,
|
||||||
|
"=>": constraintGreaterThanEqual,
|
||||||
|
"<=": constraintLessThanEqual,
|
||||||
|
"=<": constraintLessThanEqual,
|
||||||
|
"~": constraintTilde,
|
||||||
|
"~>": constraintTilde,
|
||||||
|
"^": constraintCaret,
|
||||||
|
}
|
||||||
|
|
||||||
|
constraintMsg = map[string]string{
|
||||||
|
"": "%s is not equal to %s",
|
||||||
|
"=": "%s is not equal to %s",
|
||||||
|
"!=": "%s is equal to %s",
|
||||||
|
">": "%s is less than or equal to %s",
|
||||||
|
"<": "%s is greater than or equal to %s",
|
||||||
|
">=": "%s is less than %s",
|
||||||
|
"=>": "%s is less than %s",
|
||||||
|
"<=": "%s is greater than %s",
|
||||||
|
"=<": "%s is greater than %s",
|
||||||
|
"~": "%s does not have same major and minor version as %s",
|
||||||
|
"~>": "%s does not have same major and minor version as %s",
|
||||||
|
"^": "%s does not have same major version as %s",
|
||||||
|
}
|
||||||
|
|
||||||
|
ops := make([]string, 0, len(constraintOps))
|
||||||
|
for k := range constraintOps {
|
||||||
|
ops = append(ops, regexp.QuoteMeta(k))
|
||||||
|
}
|
||||||
|
|
||||||
|
constraintRegex = regexp.MustCompile(fmt.Sprintf(
|
||||||
|
`^\s*(%s)\s*(%s)\s*$`,
|
||||||
|
strings.Join(ops, "|"),
|
||||||
|
cvRegex))
|
||||||
|
|
||||||
|
constraintRangeRegex = regexp.MustCompile(fmt.Sprintf(
|
||||||
|
`\s*(%s)\s+-\s+(%s)\s*`,
|
||||||
|
cvRegex, cvRegex))
|
||||||
|
}
|
||||||
|
|
||||||
|
// An individual constraint
|
||||||
|
type constraint struct {
|
||||||
|
// The callback function for the restraint. It performs the logic for
|
||||||
|
// the constraint.
|
||||||
|
function cfunc
|
||||||
|
|
||||||
|
msg string
|
||||||
|
|
||||||
|
// The version used in the constraint check. For example, if a constraint
|
||||||
|
// is '<= 2.0.0' the con a version instance representing 2.0.0.
|
||||||
|
con *Version
|
||||||
|
|
||||||
|
// The original parsed version (e.g., 4.x from != 4.x)
|
||||||
|
orig string
|
||||||
|
|
||||||
|
// When an x is used as part of the version (e.g., 1.x)
|
||||||
|
minorDirty bool
|
||||||
|
dirty bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a version meets the constraint
|
||||||
|
func (c *constraint) check(v *Version) bool {
|
||||||
|
return c.function(v, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
type cfunc func(v *Version, c *constraint) bool
|
||||||
|
|
||||||
|
func parseConstraint(c string) (*constraint, error) {
|
||||||
|
m := constraintRegex.FindStringSubmatch(c)
|
||||||
|
if m == nil {
|
||||||
|
return nil, fmt.Errorf("improper constraint: %s", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
ver := m[2]
|
||||||
|
orig := ver
|
||||||
|
minorDirty := false
|
||||||
|
dirty := false
|
||||||
|
if isX(m[3]) {
|
||||||
|
ver = "0.0.0"
|
||||||
|
dirty = true
|
||||||
|
} else if isX(strings.TrimPrefix(m[4], ".")) {
|
||||||
|
minorDirty = true
|
||||||
|
dirty = true
|
||||||
|
ver = fmt.Sprintf("%s.0.0%s", m[3], m[6])
|
||||||
|
} else if isX(strings.TrimPrefix(m[5], ".")) {
|
||||||
|
dirty = true
|
||||||
|
ver = fmt.Sprintf("%s%s.0%s", m[3], m[4], m[6])
|
||||||
|
}
|
||||||
|
|
||||||
|
con, err := NewVersion(ver)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
// The constraintRegex should catch any regex parsing errors. So,
|
||||||
|
// we should never get here.
|
||||||
|
return nil, errors.New("constraint Parser Error")
|
||||||
|
}
|
||||||
|
|
||||||
|
cs := &constraint{
|
||||||
|
function: constraintOps[m[1]],
|
||||||
|
msg: constraintMsg[m[1]],
|
||||||
|
con: con,
|
||||||
|
orig: orig,
|
||||||
|
minorDirty: minorDirty,
|
||||||
|
dirty: dirty,
|
||||||
|
}
|
||||||
|
return cs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constraint functions
|
||||||
|
func constraintNotEqual(v *Version, c *constraint) bool {
|
||||||
|
if c.dirty {
|
||||||
|
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.con.Major() != v.Major() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if c.con.Minor() != v.Minor() && !c.minorDirty {
|
||||||
|
return true
|
||||||
|
} else if c.minorDirty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return !v.Equal(c.con)
|
||||||
|
}
|
||||||
|
|
||||||
|
func constraintGreaterThan(v *Version, c *constraint) bool {
|
||||||
|
|
||||||
|
// An edge case the constraint is 0.0.0 and the version is 0.0.0-someprerelease
|
||||||
|
// exists. This that case.
|
||||||
|
if !isNonZero(c.con) && isNonZero(v) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.Compare(c.con) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func constraintLessThan(v *Version, c *constraint) bool {
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.dirty {
|
||||||
|
return v.Compare(c.con) < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Major() > c.con.Major() {
|
||||||
|
return false
|
||||||
|
} else if v.Minor() > c.con.Minor() && !c.minorDirty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func constraintGreaterThanEqual(v *Version, c *constraint) bool {
|
||||||
|
// An edge case the constraint is 0.0.0 and the version is 0.0.0-someprerelease
|
||||||
|
// exists. This that case.
|
||||||
|
if !isNonZero(c.con) && isNonZero(v) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.Compare(c.con) >= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func constraintLessThanEqual(v *Version, c *constraint) bool {
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.dirty {
|
||||||
|
return v.Compare(c.con) <= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Major() > c.con.Major() {
|
||||||
|
return false
|
||||||
|
} else if v.Minor() > c.con.Minor() && !c.minorDirty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~*, ~>* --> >= 0.0.0 (any)
|
||||||
|
// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0, <3.0.0
|
||||||
|
// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0, <2.1.0
|
||||||
|
// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0, <1.3.0
|
||||||
|
// ~1.2.3, ~>1.2.3 --> >=1.2.3, <1.3.0
|
||||||
|
// ~1.2.0, ~>1.2.0 --> >=1.2.0, <1.3.0
|
||||||
|
func constraintTilde(v *Version, c *constraint) bool {
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.LessThan(c.con) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~0.0.0 is a special case where all constraints are accepted. It's
|
||||||
|
// equivalent to >= 0.0.0.
|
||||||
|
if c.con.Major() == 0 && c.con.Minor() == 0 && c.con.Patch() == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Major() != c.con.Major() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Minor() != c.con.Minor() && !c.minorDirty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// When there is a .x (dirty) status it automatically opts in to ~. Otherwise
|
||||||
|
// it's a straight =
|
||||||
|
func constraintTildeOrEqual(v *Version, c *constraint) bool {
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.dirty {
|
||||||
|
c.msg = constraintMsg["~"]
|
||||||
|
return constraintTilde(v, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.Equal(c.con)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ^* --> (any)
|
||||||
|
// ^2, ^2.x, ^2.x.x --> >=2.0.0, <3.0.0
|
||||||
|
// ^2.0, ^2.0.x --> >=2.0.0, <3.0.0
|
||||||
|
// ^1.2, ^1.2.x --> >=1.2.0, <2.0.0
|
||||||
|
// ^1.2.3 --> >=1.2.3, <2.0.0
|
||||||
|
// ^1.2.0 --> >=1.2.0, <2.0.0
|
||||||
|
func constraintCaret(v *Version, c *constraint) bool {
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.LessThan(c.con) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Major() != c.con.Major() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var constraintRangeRegex *regexp.Regexp
|
||||||
|
|
||||||
|
const cvRegex string = `v?([0-9|x|X|\*]+)(\.[0-9|x|X|\*]+)?(\.[0-9|x|X|\*]+)?` +
|
||||||
|
`(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` +
|
||||||
|
`(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?`
|
||||||
|
|
||||||
|
func isX(x string) bool {
|
||||||
|
switch x {
|
||||||
|
case "x", "*", "X":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func rewriteRange(i string) string {
|
||||||
|
m := constraintRangeRegex.FindAllStringSubmatch(i, -1)
|
||||||
|
if m == nil {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
o := i
|
||||||
|
for _, v := range m {
|
||||||
|
t := fmt.Sprintf(">= %s, <= %s", v[1], v[11])
|
||||||
|
o = strings.Replace(o, v[0], t, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect if a version is not zero (0.0.0)
|
||||||
|
func isNonZero(v *Version) bool {
|
||||||
|
if v.Major() != 0 || v.Minor() != 0 || v.Patch() != 0 || v.Prerelease() != "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
452
vendor/github.com/Masterminds/semver/constraints_test.go
generated
vendored
Normal file
452
vendor/github.com/Masterminds/semver/constraints_test.go
generated
vendored
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
115
vendor/github.com/Masterminds/semver/doc.go
generated
vendored
Normal file
115
vendor/github.com/Masterminds/semver/doc.go
generated
vendored
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
Package semver provides the ability to work with Semantic Versions (http://semver.org) in Go.
|
||||||
|
|
||||||
|
Specifically it provides the ability to:
|
||||||
|
|
||||||
|
* Parse semantic versions
|
||||||
|
* Sort semantic versions
|
||||||
|
* Check if a semantic version fits within a set of constraints
|
||||||
|
* Optionally work with a `v` prefix
|
||||||
|
|
||||||
|
Parsing Semantic Versions
|
||||||
|
|
||||||
|
To parse a semantic version use the `NewVersion` function. For example,
|
||||||
|
|
||||||
|
v, err := semver.NewVersion("1.2.3-beta.1+build345")
|
||||||
|
|
||||||
|
If there is an error the version wasn't parseable. The version object has methods
|
||||||
|
to get the parts of the version, compare it to other versions, convert the
|
||||||
|
version back into a string, and get the original string. For more details
|
||||||
|
please see the documentation at https://godoc.org/github.com/Masterminds/semver.
|
||||||
|
|
||||||
|
Sorting Semantic Versions
|
||||||
|
|
||||||
|
A set of versions can be sorted using the `sort` package from the standard library.
|
||||||
|
For example,
|
||||||
|
|
||||||
|
raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",}
|
||||||
|
vs := make([]*semver.Version, len(raw))
|
||||||
|
for i, r := range raw {
|
||||||
|
v, err := semver.NewVersion(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error parsing version: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vs[i] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(semver.Collection(vs))
|
||||||
|
|
||||||
|
Checking Version Constraints
|
||||||
|
|
||||||
|
Checking a version against version constraints is one of the most featureful
|
||||||
|
parts of the package.
|
||||||
|
|
||||||
|
c, err := semver.NewConstraint(">= 1.2.3")
|
||||||
|
if err != nil {
|
||||||
|
// Handle constraint not being parseable.
|
||||||
|
}
|
||||||
|
|
||||||
|
v, _ := semver.NewVersion("1.3")
|
||||||
|
if err != nil {
|
||||||
|
// Handle version not being parseable.
|
||||||
|
}
|
||||||
|
// Check if the version meets the constraints. The a variable will be true.
|
||||||
|
a := c.Check(v)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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`
|
||||||
|
*/
|
||||||
|
package semver
|
375
vendor/github.com/Masterminds/semver/version.go
generated
vendored
Normal file
375
vendor/github.com/Masterminds/semver/version.go
generated
vendored
Normal file
@ -0,0 +1,375 @@
|
|||||||
|
package semver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The compiled version of the regex created at init() is cached here so it
|
||||||
|
// only needs to be created once.
|
||||||
|
var versionRegex *regexp.Regexp
|
||||||
|
var validPrereleaseRegex *regexp.Regexp
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrInvalidSemVer is returned a version is found to be invalid when
|
||||||
|
// being parsed.
|
||||||
|
ErrInvalidSemVer = errors.New("Invalid Semantic Version")
|
||||||
|
|
||||||
|
// ErrInvalidMetadata is returned when the metadata is an invalid format
|
||||||
|
ErrInvalidMetadata = errors.New("Invalid Metadata string")
|
||||||
|
|
||||||
|
// ErrInvalidPrerelease is returned when the pre-release is an invalid format
|
||||||
|
ErrInvalidPrerelease = errors.New("Invalid Prerelease string")
|
||||||
|
)
|
||||||
|
|
||||||
|
// SemVerRegex is the regular expression used to parse a semantic version.
|
||||||
|
const SemVerRegex string = `v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?` +
|
||||||
|
`(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` +
|
||||||
|
`(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?`
|
||||||
|
|
||||||
|
// ValidPrerelease is the regular expression which validates
|
||||||
|
// both prerelease and metadata values.
|
||||||
|
const ValidPrerelease string = `^([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*)`
|
||||||
|
|
||||||
|
// Version represents a single semantic version.
|
||||||
|
type Version struct {
|
||||||
|
major, minor, patch int64
|
||||||
|
pre string
|
||||||
|
metadata string
|
||||||
|
original string
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
versionRegex = regexp.MustCompile("^" + SemVerRegex + "$")
|
||||||
|
validPrereleaseRegex = regexp.MustCompile(ValidPrerelease)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewVersion parses a given version and returns an instance of Version or
|
||||||
|
// an error if unable to parse the version.
|
||||||
|
func NewVersion(v string) (*Version, error) {
|
||||||
|
m := versionRegex.FindStringSubmatch(v)
|
||||||
|
if m == nil {
|
||||||
|
return nil, ErrInvalidSemVer
|
||||||
|
}
|
||||||
|
|
||||||
|
sv := &Version{
|
||||||
|
metadata: m[8],
|
||||||
|
pre: m[5],
|
||||||
|
original: v,
|
||||||
|
}
|
||||||
|
|
||||||
|
var temp int64
|
||||||
|
temp, err := strconv.ParseInt(m[1], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error parsing version segment: %s", err)
|
||||||
|
}
|
||||||
|
sv.major = temp
|
||||||
|
|
||||||
|
if m[2] != "" {
|
||||||
|
temp, err = strconv.ParseInt(strings.TrimPrefix(m[2], "."), 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error parsing version segment: %s", err)
|
||||||
|
}
|
||||||
|
sv.minor = temp
|
||||||
|
} else {
|
||||||
|
sv.minor = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if m[3] != "" {
|
||||||
|
temp, err = strconv.ParseInt(strings.TrimPrefix(m[3], "."), 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error parsing version segment: %s", err)
|
||||||
|
}
|
||||||
|
sv.patch = temp
|
||||||
|
} else {
|
||||||
|
sv.patch = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return sv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustParse parses a given version and panics on error.
|
||||||
|
func MustParse(v string) *Version {
|
||||||
|
sv, err := NewVersion(v)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return sv
|
||||||
|
}
|
||||||
|
|
||||||
|
// String converts a Version object to a string.
|
||||||
|
// Note, if the original version contained a leading v this version will not.
|
||||||
|
// See the Original() method to retrieve the original value. Semantic Versions
|
||||||
|
// don't contain a leading v per the spec. Instead it's optional on
|
||||||
|
// impelementation.
|
||||||
|
func (v *Version) String() string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
fmt.Fprintf(&buf, "%d.%d.%d", v.major, v.minor, v.patch)
|
||||||
|
if v.pre != "" {
|
||||||
|
fmt.Fprintf(&buf, "-%s", v.pre)
|
||||||
|
}
|
||||||
|
if v.metadata != "" {
|
||||||
|
fmt.Fprintf(&buf, "+%s", v.metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Original returns the original value passed in to be parsed.
|
||||||
|
func (v *Version) Original() string {
|
||||||
|
return v.original
|
||||||
|
}
|
||||||
|
|
||||||
|
// Major returns the major version.
|
||||||
|
func (v *Version) Major() int64 {
|
||||||
|
return v.major
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minor returns the minor version.
|
||||||
|
func (v *Version) Minor() int64 {
|
||||||
|
return v.minor
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch returns the patch version.
|
||||||
|
func (v *Version) Patch() int64 {
|
||||||
|
return v.patch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prerelease returns the pre-release version.
|
||||||
|
func (v *Version) Prerelease() string {
|
||||||
|
return v.pre
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata returns the metadata on the version.
|
||||||
|
func (v *Version) Metadata() string {
|
||||||
|
return v.metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
// originalVPrefix returns the original 'v' prefix if any.
|
||||||
|
func (v *Version) originalVPrefix() string {
|
||||||
|
|
||||||
|
// Note, only lowercase v is supported as a prefix by the parser.
|
||||||
|
if v.original != "" && v.original[:1] == "v" {
|
||||||
|
return v.original[:1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncPatch produces the next patch version.
|
||||||
|
// If the current version does not have prerelease/metadata information,
|
||||||
|
// it unsets metadata and prerelease values, increments patch number.
|
||||||
|
// If the current version has any of prerelease or metadata information,
|
||||||
|
// it unsets both values and keeps curent patch value
|
||||||
|
func (v Version) IncPatch() Version {
|
||||||
|
vNext := v
|
||||||
|
// according to http://semver.org/#spec-item-9
|
||||||
|
// Pre-release versions have a lower precedence than the associated normal version.
|
||||||
|
// according to http://semver.org/#spec-item-10
|
||||||
|
// Build metadata SHOULD be ignored when determining version precedence.
|
||||||
|
if v.pre != "" {
|
||||||
|
vNext.metadata = ""
|
||||||
|
vNext.pre = ""
|
||||||
|
} else {
|
||||||
|
vNext.metadata = ""
|
||||||
|
vNext.pre = ""
|
||||||
|
vNext.patch = v.patch + 1
|
||||||
|
}
|
||||||
|
vNext.original = v.originalVPrefix() + "" + vNext.String()
|
||||||
|
return vNext
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncMinor produces the next minor version.
|
||||||
|
// Sets patch to 0.
|
||||||
|
// Increments minor number.
|
||||||
|
// Unsets metadata.
|
||||||
|
// Unsets prerelease status.
|
||||||
|
func (v Version) IncMinor() Version {
|
||||||
|
vNext := v
|
||||||
|
vNext.metadata = ""
|
||||||
|
vNext.pre = ""
|
||||||
|
vNext.patch = 0
|
||||||
|
vNext.minor = v.minor + 1
|
||||||
|
vNext.original = v.originalVPrefix() + "" + vNext.String()
|
||||||
|
return vNext
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncMajor produces the next major version.
|
||||||
|
// Sets patch to 0.
|
||||||
|
// Sets minor to 0.
|
||||||
|
// Increments major number.
|
||||||
|
// Unsets metadata.
|
||||||
|
// Unsets prerelease status.
|
||||||
|
func (v Version) IncMajor() Version {
|
||||||
|
vNext := v
|
||||||
|
vNext.metadata = ""
|
||||||
|
vNext.pre = ""
|
||||||
|
vNext.patch = 0
|
||||||
|
vNext.minor = 0
|
||||||
|
vNext.major = v.major + 1
|
||||||
|
vNext.original = v.originalVPrefix() + "" + vNext.String()
|
||||||
|
return vNext
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPrerelease defines the prerelease value.
|
||||||
|
// Value must not include the required 'hypen' prefix.
|
||||||
|
func (v Version) SetPrerelease(prerelease string) (Version, error) {
|
||||||
|
vNext := v
|
||||||
|
if len(prerelease) > 0 && !validPrereleaseRegex.MatchString(prerelease) {
|
||||||
|
return vNext, ErrInvalidPrerelease
|
||||||
|
}
|
||||||
|
vNext.pre = prerelease
|
||||||
|
vNext.original = v.originalVPrefix() + "" + vNext.String()
|
||||||
|
return vNext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMetadata defines metadata value.
|
||||||
|
// Value must not include the required 'plus' prefix.
|
||||||
|
func (v Version) SetMetadata(metadata string) (Version, error) {
|
||||||
|
vNext := v
|
||||||
|
if len(metadata) > 0 && !validPrereleaseRegex.MatchString(metadata) {
|
||||||
|
return vNext, ErrInvalidMetadata
|
||||||
|
}
|
||||||
|
vNext.metadata = metadata
|
||||||
|
vNext.original = v.originalVPrefix() + "" + vNext.String()
|
||||||
|
return vNext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LessThan tests if one version is less than another one.
|
||||||
|
func (v *Version) LessThan(o *Version) bool {
|
||||||
|
return v.Compare(o) < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GreaterThan tests if one version is greater than another one.
|
||||||
|
func (v *Version) GreaterThan(o *Version) bool {
|
||||||
|
return v.Compare(o) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal tests if two versions are equal to each other.
|
||||||
|
// Note, versions can be equal with different metadata since metadata
|
||||||
|
// is not considered part of the comparable version.
|
||||||
|
func (v *Version) Equal(o *Version) bool {
|
||||||
|
return v.Compare(o) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare compares this version to another one. It returns -1, 0, or 1 if
|
||||||
|
// the version smaller, equal, or larger than the other version.
|
||||||
|
//
|
||||||
|
// Versions are compared by X.Y.Z. Build metadata is ignored. Prerelease is
|
||||||
|
// lower than the version without a prerelease.
|
||||||
|
func (v *Version) Compare(o *Version) int {
|
||||||
|
// Compare the major, minor, and patch version for differences. If a
|
||||||
|
// difference is found return the comparison.
|
||||||
|
if d := compareSegment(v.Major(), o.Major()); d != 0 {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
if d := compareSegment(v.Minor(), o.Minor()); d != 0 {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
if d := compareSegment(v.Patch(), o.Patch()); d != 0 {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point the major, minor, and patch versions are the same.
|
||||||
|
ps := v.pre
|
||||||
|
po := o.Prerelease()
|
||||||
|
|
||||||
|
if ps == "" && po == "" {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if ps == "" {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if po == "" {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return comparePrerelease(ps, po)
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareSegment(v, o int64) int {
|
||||||
|
if v < o {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if v > o {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func comparePrerelease(v, o string) int {
|
||||||
|
|
||||||
|
// split the prelease versions by their part. The separator, per the spec,
|
||||||
|
// is a .
|
||||||
|
sparts := strings.Split(v, ".")
|
||||||
|
oparts := strings.Split(o, ".")
|
||||||
|
|
||||||
|
// Find the longer length of the parts to know how many loop iterations to
|
||||||
|
// go through.
|
||||||
|
slen := len(sparts)
|
||||||
|
olen := len(oparts)
|
||||||
|
|
||||||
|
l := slen
|
||||||
|
if olen > slen {
|
||||||
|
l = olen
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over each part of the prereleases to compare the differences.
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
// Since the lentgh of the parts can be different we need to create
|
||||||
|
// a placeholder. This is to avoid out of bounds issues.
|
||||||
|
stemp := ""
|
||||||
|
if i < slen {
|
||||||
|
stemp = sparts[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
otemp := ""
|
||||||
|
if i < olen {
|
||||||
|
otemp = oparts[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
d := comparePrePart(stemp, otemp)
|
||||||
|
if d != 0 {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reaching here means two versions are of equal value but have different
|
||||||
|
// metadata (the part following a +). They are not identical in string form
|
||||||
|
// but the version comparison finds them to be equal.
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func comparePrePart(s, o string) int {
|
||||||
|
// Fastpath if they are equal
|
||||||
|
if s == o {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// When s or o are empty we can use the other in an attempt to determine
|
||||||
|
// the response.
|
||||||
|
if o == "" {
|
||||||
|
_, n := strconv.ParseInt(s, 10, 64)
|
||||||
|
if n != nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if s == "" {
|
||||||
|
_, n := strconv.ParseInt(o, 10, 64)
|
||||||
|
if n != nil {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if s > o {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
450
vendor/github.com/Masterminds/semver/version_test.go
generated
vendored
Normal file
450
vendor/github.com/Masterminds/semver/version_test.go
generated
vendored
Normal file
@ -0,0 +1,450 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
vendor/github.com/Masterminds/sprig/.gitignore
generated
vendored
1
vendor/github.com/Masterminds/sprig/.gitignore
generated
vendored
@ -1 +1,2 @@
|
|||||||
vendor/
|
vendor/
|
||||||
|
/.glide
|
||||||
|
9
vendor/github.com/Masterminds/sprig/.travis.yml
generated
vendored
9
vendor/github.com/Masterminds/sprig/.travis.yml
generated
vendored
@ -1,9 +1,9 @@
|
|||||||
language: go
|
language: go
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.3
|
- 1.6
|
||||||
- 1.4
|
- 1.7
|
||||||
- 1.5
|
- 1.8
|
||||||
- tip
|
- tip
|
||||||
|
|
||||||
# Setting sudo access to false will let Travis CI use containers rather than
|
# Setting sudo access to false will let Travis CI use containers rather than
|
||||||
@ -12,6 +12,9 @@ go:
|
|||||||
# - http://docs.travis-ci.com/user/workers/standard-infrastructure/
|
# - http://docs.travis-ci.com/user/workers/standard-infrastructure/
|
||||||
sudo: false
|
sudo: false
|
||||||
|
|
||||||
|
script:
|
||||||
|
- make setup test
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
webhooks:
|
webhooks:
|
||||||
urls:
|
urls:
|
||||||
|
13
vendor/github.com/Masterminds/sprig/Makefile
generated
vendored
Normal file
13
vendor/github.com/Masterminds/sprig/Makefile
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
HAS_GLIDE := $(shell command -v glide;)
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
go test -v .
|
||||||
|
|
||||||
|
.PHONY: setup
|
||||||
|
setup:
|
||||||
|
ifndef HAS_GLIDE
|
||||||
|
go get -u github.com/Masterminds/glide
|
||||||
|
endif
|
||||||
|
glide install
|
91
vendor/github.com/Masterminds/sprig/README.md
generated
vendored
91
vendor/github.com/Masterminds/sprig/README.md
generated
vendored
@ -108,6 +108,7 @@ parse, it returns the time unaltered. See `time.ParseDuration` for info on durat
|
|||||||
"one anchovy" "many anchovies"`
|
"one anchovy" "many anchovies"`
|
||||||
- uuidv4: Generate a UUID v4 string
|
- uuidv4: Generate a UUID v4 string
|
||||||
- sha256sum: Generate a hex encoded sha256 hash of the input
|
- sha256sum: Generate a hex encoded sha256 hash of the input
|
||||||
|
- toString: Convert something to a string
|
||||||
|
|
||||||
### String Slice Functions:
|
### String Slice Functions:
|
||||||
|
|
||||||
@ -115,6 +116,10 @@ parse, it returns the time unaltered. See `time.ParseDuration` for info on durat
|
|||||||
- split: strings.Split, but as `split SEP STRING`. The results are returned
|
- split: strings.Split, but as `split SEP STRING`. The results are returned
|
||||||
as a map with the indexes set to _N, where N is an integer starting from 0.
|
as a map with the indexes set to _N, where N is an integer starting from 0.
|
||||||
Use it like this: `{{$v := "foo/bar/baz" | split "/"}}{{$v._0}}` (Prints `foo`)
|
Use it like this: `{{$v := "foo/bar/baz" | split "/"}}{{$v._0}}` (Prints `foo`)
|
||||||
|
- splitList: strings.Split, but as `split SEP STRING`. The results are returned
|
||||||
|
as an array.
|
||||||
|
- toStrings: convert a list to a list of strings. 'list 1 2 3 | toStrings' produces '["1" "2" "3"]'
|
||||||
|
- sortAlpha: sort a list lexicographically.
|
||||||
|
|
||||||
### Integer Slice Functions:
|
### Integer Slice Functions:
|
||||||
|
|
||||||
@ -141,12 +146,26 @@ parse, it returns the time unaltered. See `time.ParseDuration` for info on durat
|
|||||||
no clear empty condition). For everything else, nil value triggers a default.
|
no clear empty condition). For everything else, nil value triggers a default.
|
||||||
- empty: Returns true if the given value is the zero value for that
|
- empty: Returns true if the given value is the zero value for that
|
||||||
type. Structs are always non-empty.
|
type. Structs are always non-empty.
|
||||||
|
- coalesce: Given a list of items, return the first non-empty one.
|
||||||
|
This follows the same rules as 'empty'. `{{ coalesce .someVal 0 "hello" }}`
|
||||||
|
will return `.someVal` if set, or else return "hello". The 0 is skipped
|
||||||
|
because it is an empty value.
|
||||||
|
- compact: Return a copy of a list with all of the empty values removed.
|
||||||
|
`list 0 1 2 "" | compact` will return `[1 2]`
|
||||||
|
|
||||||
### OS:
|
### OS:
|
||||||
|
|
||||||
- env: Read an environment variable.
|
- env: Read an environment variable.
|
||||||
- expandenv: Expand all environment variables in a string.
|
- expandenv: Expand all environment variables in a string.
|
||||||
|
|
||||||
|
### File Paths:
|
||||||
|
- base: Return the last element of a path. https://golang.org/pkg/path#Base
|
||||||
|
- dir: Remove the last element of a path. https://golang.org/pkg/path#Dir
|
||||||
|
- clean: Clean a path to the shortest equivalent name. (e.g. remove "foo/.."
|
||||||
|
from "foo/../bar.html") https://golang.org/pkg/path#Clean
|
||||||
|
- ext: Get the extension for a file path: https://golang.org/pkg/path#Ext
|
||||||
|
- isAbs: Returns true if a path is absolute: https://golang.org/pkg/path#IsAbs
|
||||||
|
|
||||||
### Encoding:
|
### Encoding:
|
||||||
|
|
||||||
- b32enc: Encode a string into a Base32 string
|
- b32enc: Encode a string into a Base32 string
|
||||||
@ -156,8 +175,11 @@ parse, it returns the time unaltered. See `time.ParseDuration` for info on durat
|
|||||||
|
|
||||||
### Data Structures:
|
### Data Structures:
|
||||||
|
|
||||||
- tuple: A sequence of related objects. It is implemented as a
|
- tuple: Takes an arbitrary list of items and returns a slice of items. Its
|
||||||
`[]interface{}`, where each item can be accessed using `index`.
|
tuple-ish properties are mainly gained through the template idiom, and not
|
||||||
|
through an API provided here. WARNING: The implementation of tuple will
|
||||||
|
change in the future.
|
||||||
|
- list: An arbitrary ordered list of items. (This is prefered over tuple.)
|
||||||
- dict: Takes a list of name/values and returns a map[string]interface{}.
|
- dict: Takes a list of name/values and returns a map[string]interface{}.
|
||||||
The first parameter is converted to a string and stored as a key, the
|
The first parameter is converted to a string and stored as a key, the
|
||||||
second parameter is treated as the value. And so on, with odds as keys and
|
second parameter is treated as the value. And so on, with odds as keys and
|
||||||
@ -165,7 +187,43 @@ parse, it returns the time unaltered. See `time.ParseDuration` for info on durat
|
|||||||
be assigned the empty string. Non-string keys are converted to strings as
|
be assigned the empty string. Non-string keys are converted to strings as
|
||||||
follows: []byte are converted, fmt.Stringers will have String() called.
|
follows: []byte are converted, fmt.Stringers will have String() called.
|
||||||
errors will have Error() called. All others will be passed through
|
errors will have Error() called. All others will be passed through
|
||||||
fmt.Sprtinf("%v").
|
fmt.Sprtinf("%v"). _dicts are unordered_.
|
||||||
|
|
||||||
|
List:
|
||||||
|
|
||||||
|
```
|
||||||
|
{{$t := list 1 "a" "foo"}}
|
||||||
|
{{index $t 2}}{{index $t 0 }}{{index $t 1}}
|
||||||
|
{{/* Prints foo1a *}}
|
||||||
|
```
|
||||||
|
|
||||||
|
Dict:
|
||||||
|
```
|
||||||
|
{{ $t := map "key1" "value1" "key2" "value2" }}
|
||||||
|
{{ $t.key2 }}
|
||||||
|
{{ /* Prints value2 *}}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Lists Functions:
|
||||||
|
|
||||||
|
These are used to manipulate lists: `{{ list 1 2 3 | reverse | first }}`
|
||||||
|
|
||||||
|
- first: Get the first item in a 'list'. 'list 1 2 3 | first' prints '1'
|
||||||
|
- last: Get the last item in a 'list': 'list 1 2 3 | last ' prints '3'
|
||||||
|
- rest: Get all but the first item in a list: 'list 1 2 3 | rest' returns '[2 3]'
|
||||||
|
- initial: Get all but the last item in a list: 'list 1 2 3 | initial' returns '[1 2]'
|
||||||
|
- append: Add an item to the end of a list: 'append $list 4' adds '4' to the end of '$list'
|
||||||
|
- prepend: Add an item to the beginning of a list: 'prepend $list 4' puts 4 at the beginning of the list.
|
||||||
|
- reverse: Reverse the items in a list.
|
||||||
|
- uniq: Remove duplicates from a list.
|
||||||
|
- without: Return a list with the given values removed: 'without (list 1 2 3) 1' would return '[2 3]'
|
||||||
|
- has: Return 'tru' if the item is found in the list: 'has "foo" $list' will return 'true' if the list contains "foo"
|
||||||
|
|
||||||
|
### Dict Functions:
|
||||||
|
|
||||||
|
These are used to manipulate dicts.
|
||||||
|
|
||||||
- set: Takes a dict, a key, and a value, and sets that key/value pair in
|
- set: Takes a dict, a key, and a value, and sets that key/value pair in
|
||||||
the dict. `set $dict $key $value`. For convenience, it returns the dict,
|
the dict. `set $dict $key $value`. For convenience, it returns the dict,
|
||||||
even though the dict was modified in place.
|
even though the dict was modified in place.
|
||||||
@ -173,12 +231,10 @@ parse, it returns the time unaltered. See `time.ParseDuration` for info on durat
|
|||||||
dict. `unset $dict $key`. This returns the dict for convenience.
|
dict. `unset $dict $key`. This returns the dict for convenience.
|
||||||
- hasKey: Takes a dict and a key, and returns boolean true if the key is in
|
- hasKey: Takes a dict and a key, and returns boolean true if the key is in
|
||||||
the dict.
|
the dict.
|
||||||
|
- pluck: Given a key and one or more maps, get all of the values for that key.
|
||||||
```
|
- keys: Get an array of all of the keys in a dict. Order is not guaranteed.
|
||||||
{{$t := tuple 1 "a" "foo"}}
|
- pick: Select just the given keys out of the dict, and return a new dict.
|
||||||
{{index $t 2}}{{index $t 0 }}{{index $t 1}}
|
- omit: Return a dict without the given keys.
|
||||||
{{/* Prints foo1a *}}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Reflection:
|
### Reflection:
|
||||||
|
|
||||||
@ -215,6 +271,23 @@ string is passed in, functions will attempt to conver with
|
|||||||
- min: Return the smallest of a series of integers. `min 1 2 3` returns
|
- min: Return the smallest of a series of integers. `min 1 2 3` returns
|
||||||
`1`.
|
`1`.
|
||||||
|
|
||||||
|
### Cryptographic Functions:
|
||||||
|
|
||||||
|
- derivePassword: Derive a password from the given parameters according to the "Master Password" algorithm (http://masterpasswordapp.com/algorithm.html)
|
||||||
|
Given parameters (in order) are:
|
||||||
|
`counter` (starting with 1), `password_type` (maximum, long, medium, short, basic, or pin), `password`,
|
||||||
|
`user`, and `site`. The following line generates a long password for the user "user" and with a master-password "password" on the site "example.com":
|
||||||
|
```
|
||||||
|
{{ derivePassword 1 "long" "password" "user" "example.com" }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## SemVer Functions:
|
||||||
|
|
||||||
|
These functions provide version parsing and comparisons for SemVer 2 version
|
||||||
|
strings.
|
||||||
|
|
||||||
|
- semver: Parse a semantic version and return a Version object.
|
||||||
|
- semverCompare: Compare a SemVer range to a particular version.
|
||||||
|
|
||||||
## Principles:
|
## Principles:
|
||||||
|
|
||||||
|
148
vendor/github.com/Masterminds/sprig/crypto.go
generated
vendored
Normal file
148
vendor/github.com/Masterminds/sprig/crypto.go
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
package sprig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/dsa"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/asn1"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
uuid "github.com/satori/go.uuid"
|
||||||
|
"golang.org/x/crypto/scrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func sha256sum(input string) string {
|
||||||
|
hash := sha256.Sum256([]byte(input))
|
||||||
|
return hex.EncodeToString(hash[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// uuidv4 provides a safe and secure UUID v4 implementation
|
||||||
|
func uuidv4() string {
|
||||||
|
return fmt.Sprintf("%s", uuid.NewV4())
|
||||||
|
}
|
||||||
|
|
||||||
|
var master_password_seed = "com.lyndir.masterpassword"
|
||||||
|
|
||||||
|
var password_type_templates = map[string][][]byte{
|
||||||
|
"maximum": {[]byte("anoxxxxxxxxxxxxxxxxx"), []byte("axxxxxxxxxxxxxxxxxno")},
|
||||||
|
"long": {[]byte("CvcvnoCvcvCvcv"), []byte("CvcvCvcvnoCvcv"), []byte("CvcvCvcvCvcvno"), []byte("CvccnoCvcvCvcv"), []byte("CvccCvcvnoCvcv"),
|
||||||
|
[]byte("CvccCvcvCvcvno"), []byte("CvcvnoCvccCvcv"), []byte("CvcvCvccnoCvcv"), []byte("CvcvCvccCvcvno"), []byte("CvcvnoCvcvCvcc"),
|
||||||
|
[]byte("CvcvCvcvnoCvcc"), []byte("CvcvCvcvCvccno"), []byte("CvccnoCvccCvcv"), []byte("CvccCvccnoCvcv"), []byte("CvccCvccCvcvno"),
|
||||||
|
[]byte("CvcvnoCvccCvcc"), []byte("CvcvCvccnoCvcc"), []byte("CvcvCvccCvccno"), []byte("CvccnoCvcvCvcc"), []byte("CvccCvcvnoCvcc"),
|
||||||
|
[]byte("CvccCvcvCvccno")},
|
||||||
|
"medium": {[]byte("CvcnoCvc"), []byte("CvcCvcno")},
|
||||||
|
"short": {[]byte("Cvcn")},
|
||||||
|
"basic": {[]byte("aaanaaan"), []byte("aannaaan"), []byte("aaannaaa")},
|
||||||
|
"pin": {[]byte("nnnn")},
|
||||||
|
}
|
||||||
|
|
||||||
|
var template_characters = map[byte]string{
|
||||||
|
'V': "AEIOU",
|
||||||
|
'C': "BCDFGHJKLMNPQRSTVWXYZ",
|
||||||
|
'v': "aeiou",
|
||||||
|
'c': "bcdfghjklmnpqrstvwxyz",
|
||||||
|
'A': "AEIOUBCDFGHJKLMNPQRSTVWXYZ",
|
||||||
|
'a': "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz",
|
||||||
|
'n': "0123456789",
|
||||||
|
'o': "@&%?,=[]_:-+*$#!'^~;()/.",
|
||||||
|
'x': "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()",
|
||||||
|
}
|
||||||
|
|
||||||
|
func derivePassword(counter uint32, password_type, password, user, site string) string {
|
||||||
|
var templates = password_type_templates[password_type]
|
||||||
|
if templates == nil {
|
||||||
|
return fmt.Sprintf("cannot find password template %s", password_type)
|
||||||
|
}
|
||||||
|
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
buffer.WriteString(master_password_seed)
|
||||||
|
binary.Write(&buffer, binary.BigEndian, uint32(len(user)))
|
||||||
|
buffer.WriteString(user)
|
||||||
|
|
||||||
|
salt := buffer.Bytes()
|
||||||
|
key, err := scrypt.Key([]byte(password), salt, 32768, 8, 2, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Sprintf("failed to derive password: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.Truncate(len(master_password_seed))
|
||||||
|
binary.Write(&buffer, binary.BigEndian, uint32(len(site)))
|
||||||
|
buffer.WriteString(site)
|
||||||
|
binary.Write(&buffer, binary.BigEndian, counter)
|
||||||
|
|
||||||
|
var hmacv = hmac.New(sha256.New, key)
|
||||||
|
hmacv.Write(buffer.Bytes())
|
||||||
|
var seed = hmacv.Sum(nil)
|
||||||
|
var temp = templates[int(seed[0])%len(templates)]
|
||||||
|
|
||||||
|
buffer.Truncate(0)
|
||||||
|
for i, element := range temp {
|
||||||
|
pass_chars := template_characters[element]
|
||||||
|
pass_char := pass_chars[int(seed[i+1])%len(pass_chars)]
|
||||||
|
buffer.WriteByte(pass_char)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func generatePrivateKey(typ string) string {
|
||||||
|
var priv interface{}
|
||||||
|
var err error
|
||||||
|
switch typ {
|
||||||
|
case "", "rsa":
|
||||||
|
// good enough for government work
|
||||||
|
priv, err = rsa.GenerateKey(rand.Reader, 4096)
|
||||||
|
case "dsa":
|
||||||
|
key := new(dsa.PrivateKey)
|
||||||
|
// again, good enough for government work
|
||||||
|
if err = dsa.GenerateParameters(&key.Parameters, rand.Reader, dsa.L2048N256); err != nil {
|
||||||
|
return fmt.Sprintf("failed to generate dsa params: %s", err)
|
||||||
|
}
|
||||||
|
err = dsa.GenerateKey(key, rand.Reader)
|
||||||
|
priv = key
|
||||||
|
case "ecdsa":
|
||||||
|
// again, good enough for government work
|
||||||
|
priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
default:
|
||||||
|
return "Unknown type " + typ
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Sprintf("failed to generate private key: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pem.EncodeToMemory(pemBlockForKey(priv)))
|
||||||
|
}
|
||||||
|
|
||||||
|
type DSAKeyFormat struct {
|
||||||
|
Version int
|
||||||
|
P, Q, G, Y, X *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
func pemBlockForKey(priv interface{}) *pem.Block {
|
||||||
|
switch k := priv.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
|
||||||
|
case *dsa.PrivateKey:
|
||||||
|
val := DSAKeyFormat{
|
||||||
|
P: k.P, Q: k.Q, G: k.G,
|
||||||
|
Y: k.Y, X: k.X,
|
||||||
|
}
|
||||||
|
bytes, _ := asn1.Marshal(val)
|
||||||
|
return &pem.Block{Type: "DSA PRIVATE KEY", Bytes: bytes}
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
b, _ := x509.MarshalECPrivateKey(k)
|
||||||
|
return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
110
vendor/github.com/Masterminds/sprig/crypto_test.go
generated
vendored
Normal file
110
vendor/github.com/Masterminds/sprig/crypto_test.go
generated
vendored
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
53
vendor/github.com/Masterminds/sprig/date.go
generated
vendored
Normal file
53
vendor/github.com/Masterminds/sprig/date.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package sprig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Given a format and a date, format the date string.
|
||||||
|
//
|
||||||
|
// Date can be a `time.Time` or an `int, int32, int64`.
|
||||||
|
// In the later case, it is treated as seconds since UNIX
|
||||||
|
// epoch.
|
||||||
|
func date(fmt string, date interface{}) string {
|
||||||
|
return dateInZone(fmt, date, "Local")
|
||||||
|
}
|
||||||
|
|
||||||
|
func htmlDate(date interface{}) string {
|
||||||
|
return dateInZone("2006-01-02", date, "Local")
|
||||||
|
}
|
||||||
|
|
||||||
|
func htmlDateInZone(date interface{}, zone string) string {
|
||||||
|
return dateInZone("2006-01-02", date, zone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dateInZone(fmt string, date interface{}, zone string) string {
|
||||||
|
var t time.Time
|
||||||
|
switch date := date.(type) {
|
||||||
|
default:
|
||||||
|
t = time.Now()
|
||||||
|
case time.Time:
|
||||||
|
t = date
|
||||||
|
case int64:
|
||||||
|
t = time.Unix(date, 0)
|
||||||
|
case int:
|
||||||
|
t = time.Unix(int64(date), 0)
|
||||||
|
case int32:
|
||||||
|
t = time.Unix(int64(date), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
loc, err := time.LoadLocation(zone)
|
||||||
|
if err != nil {
|
||||||
|
loc, _ = time.LoadLocation("UTC")
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.In(loc).Format(fmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dateModify(fmt string, date time.Time) time.Time {
|
||||||
|
d, err := time.ParseDuration(fmt)
|
||||||
|
if err != nil {
|
||||||
|
return date
|
||||||
|
}
|
||||||
|
return date.Add(d)
|
||||||
|
}
|
13
vendor/github.com/Masterminds/sprig/date_test.go
generated
vendored
Normal file
13
vendor/github.com/Masterminds/sprig/date_test.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
62
vendor/github.com/Masterminds/sprig/defaults.go
generated
vendored
Normal file
62
vendor/github.com/Masterminds/sprig/defaults.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package sprig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// dfault checks whether `given` is set, and returns default if not set.
|
||||||
|
//
|
||||||
|
// This returns `d` if `given` appears not to be set, and `given` otherwise.
|
||||||
|
//
|
||||||
|
// For numeric types 0 is unset.
|
||||||
|
// For strings, maps, arrays, and slices, len() = 0 is considered unset.
|
||||||
|
// For bool, false is unset.
|
||||||
|
// Structs are never considered unset.
|
||||||
|
//
|
||||||
|
// For everything else, including pointers, a nil value is unset.
|
||||||
|
func dfault(d interface{}, given ...interface{}) interface{} {
|
||||||
|
|
||||||
|
if empty(given) || empty(given[0]) {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
return given[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// empty returns true if the given value has the zero value for its type.
|
||||||
|
func empty(given interface{}) bool {
|
||||||
|
g := reflect.ValueOf(given)
|
||||||
|
if !g.IsValid() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basically adapted from text/template.isTrue
|
||||||
|
switch g.Kind() {
|
||||||
|
default:
|
||||||
|
return g.IsNil()
|
||||||
|
case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
|
||||||
|
return g.Len() == 0
|
||||||
|
case reflect.Bool:
|
||||||
|
return g.Bool() == false
|
||||||
|
case reflect.Complex64, reflect.Complex128:
|
||||||
|
return g.Complex() == 0
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return g.Int() == 0
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
return g.Uint() == 0
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return g.Float() == 0
|
||||||
|
case reflect.Struct:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// coalesce returns the first non-empty value.
|
||||||
|
func coalesce(v ...interface{}) interface{} {
|
||||||
|
for _, val := range v {
|
||||||
|
if !empty(val) {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
84
vendor/github.com/Masterminds/sprig/defaults_test.go
generated
vendored
Normal file
84
vendor/github.com/Masterminds/sprig/defaults_test.go
generated
vendored
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
74
vendor/github.com/Masterminds/sprig/dict.go
generated
vendored
Normal file
74
vendor/github.com/Masterminds/sprig/dict.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package sprig
|
||||||
|
|
||||||
|
func set(d map[string]interface{}, key string, value interface{}) map[string]interface{} {
|
||||||
|
d[key] = value
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func unset(d map[string]interface{}, key string) map[string]interface{} {
|
||||||
|
delete(d, key)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasKey(d map[string]interface{}, key string) bool {
|
||||||
|
_, ok := d[key]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func pluck(key string, d ...map[string]interface{}) []interface{} {
|
||||||
|
res := []interface{}{}
|
||||||
|
for _, dict := range d {
|
||||||
|
if val, ok := dict[key]; ok {
|
||||||
|
res = append(res, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func keys(dict map[string]interface{}) []string {
|
||||||
|
k := []string{}
|
||||||
|
for key := range dict {
|
||||||
|
k = append(k, key)
|
||||||
|
}
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
func pick(dict map[string]interface{}, keys ...string) map[string]interface{} {
|
||||||
|
res := map[string]interface{}{}
|
||||||
|
for _, k := range keys {
|
||||||
|
if v, ok := dict[k]; ok {
|
||||||
|
res[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func omit(dict map[string]interface{}, keys ...string) map[string]interface{} {
|
||||||
|
res := map[string]interface{}{}
|
||||||
|
|
||||||
|
omit := make(map[string]bool, len(keys))
|
||||||
|
for _, k := range keys {
|
||||||
|
omit[k] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range dict {
|
||||||
|
if _, ok := omit[k]; !ok {
|
||||||
|
res[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func dict(v ...interface{}) map[string]interface{} {
|
||||||
|
dict := map[string]interface{}{}
|
||||||
|
lenv := len(v)
|
||||||
|
for i := 0; i < lenv; i += 2 {
|
||||||
|
key := strval(v[i])
|
||||||
|
if i+1 >= lenv {
|
||||||
|
dict[key] = ""
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dict[key] = v[i+1]
|
||||||
|
}
|
||||||
|
return dict
|
||||||
|
}
|
137
vendor/github.com/Masterminds/sprig/dict_test.go
generated
vendored
Normal file
137
vendor/github.com/Masterminds/sprig/dict_test.go
generated
vendored
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
225
vendor/github.com/Masterminds/sprig/doc.go
generated
vendored
Normal file
225
vendor/github.com/Masterminds/sprig/doc.go
generated
vendored
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
/*
|
||||||
|
Sprig: Template functions for Go.
|
||||||
|
|
||||||
|
This package contains a number of utility functions for working with data
|
||||||
|
inside of Go `html/template` and `text/template` files.
|
||||||
|
|
||||||
|
To add these functions, use the `template.Funcs()` method:
|
||||||
|
|
||||||
|
t := templates.New("foo").Funcs(sprig.FuncMap())
|
||||||
|
|
||||||
|
Note that you should add the function map before you parse any template files.
|
||||||
|
|
||||||
|
In several cases, Sprig reverses the order of arguments from the way they
|
||||||
|
appear in the standard library. This is to make it easier to pipe
|
||||||
|
arguments into functions.
|
||||||
|
|
||||||
|
Date Functions
|
||||||
|
|
||||||
|
- date FORMAT TIME: Format a date, where a date is an integer type or a time.Time type, and
|
||||||
|
format is a time.Format formatting string.
|
||||||
|
- dateModify: Given a date, modify it with a duration: `date_modify "-1.5h" now`. If the duration doesn't
|
||||||
|
parse, it returns the time unaltered. See `time.ParseDuration` for info on duration strings.
|
||||||
|
- now: Current time.Time, for feeding into date-related functions.
|
||||||
|
- htmlDate TIME: Format a date for use in the value field of an HTML "date" form element.
|
||||||
|
- dateInZone FORMAT TIME TZ: Like date, but takes three arguments: format, timestamp,
|
||||||
|
timezone.
|
||||||
|
- htmlDateInZone TIME TZ: Like htmlDate, but takes two arguments: timestamp,
|
||||||
|
timezone.
|
||||||
|
|
||||||
|
String Functions
|
||||||
|
|
||||||
|
- abbrev: Truncate a string with ellipses. `abbrev 5 "hello world"` yields "he..."
|
||||||
|
- abbrevboth: Abbreviate from both sides, yielding "...lo wo..."
|
||||||
|
- trunc: Truncate a string (no suffix). `trunc 5 "Hello World"` yields "hello".
|
||||||
|
- trim: strings.TrimSpace
|
||||||
|
- trimAll: strings.Trim, but with the argument order reversed `trimAll "$" "$5.00"` or `"$5.00 | trimAll "$"`
|
||||||
|
- trimSuffix: strings.TrimSuffix, but with the argument order reversed: `trimSuffix "-" "ends-with-"`
|
||||||
|
- trimPrefix: strings.TrimPrefix, but with the argument order reversed `trimPrefix "$" "$5"`
|
||||||
|
- upper: strings.ToUpper
|
||||||
|
- lower: strings.ToLower
|
||||||
|
- nospace: Remove all space characters from a string. `nospace "h e l l o"` becomes "hello"
|
||||||
|
- title: strings.Title
|
||||||
|
- untitle: Remove title casing
|
||||||
|
- repeat: strings.Repeat, but with the arguments switched: `repeat count str`. (This simplifies common pipelines)
|
||||||
|
- substr: Given string, start, and length, return a substr.
|
||||||
|
- initials: Given a multi-word string, return the initials. `initials "Matt Butcher"` returns "MB"
|
||||||
|
- randAlphaNum: Given a length, generate a random alphanumeric sequence
|
||||||
|
- randAlpha: Given a length, generate an alphabetic string
|
||||||
|
- randAscii: Given a length, generate a random ASCII string (symbols included)
|
||||||
|
- randNumeric: Given a length, generate a string of digits.
|
||||||
|
- wrap: Force a line wrap at the given width. `wrap 80 "imagine a longer string"`
|
||||||
|
- wrapWith: Wrap a line at the given length, but using 'sep' instead of a newline. `wrapWith 50, "<br>", $html`
|
||||||
|
- contains: strings.Contains, but with the arguments switched: `contains substr str`. (This simplifies common pipelines)
|
||||||
|
- hasPrefix: strings.hasPrefix, but with the arguments switched
|
||||||
|
- hasSuffix: strings.hasSuffix, but with the arguments switched
|
||||||
|
- quote: Wrap string(s) in double quotation marks, escape the contents by adding '\' before '"'.
|
||||||
|
- squote: Wrap string(s) in double quotation marks, does not escape content.
|
||||||
|
- cat: Concatenate strings, separating them by spaces. `cat $a $b $c`.
|
||||||
|
- indent: Indent a string using space characters. `indent 4 "foo\nbar"` produces " foo\n bar"
|
||||||
|
- replace: Replace an old with a new in a string: `$name | replace " " "-"`
|
||||||
|
- plural: Choose singular or plural based on length: `len $fish | plural "one anchovy" "many anchovies"`
|
||||||
|
- sha256sum: Generate a hex encoded sha256 hash of the input
|
||||||
|
- toString: Convert something to a string
|
||||||
|
|
||||||
|
String Slice Functions:
|
||||||
|
|
||||||
|
- join: strings.Join, but as `join SEP SLICE`
|
||||||
|
- split: strings.Split, but as `split SEP STRING`. The results are returned
|
||||||
|
as a map with the indexes set to _N, where N is an integer starting from 0.
|
||||||
|
Use it like this: `{{$v := "foo/bar/baz" | split "/"}}{{$v._0}}` (Prints `foo`)
|
||||||
|
- splitList: strings.Split, but as `split SEP STRING`. The results are returned
|
||||||
|
as an array.
|
||||||
|
- toStrings: convert a list to a list of strings. 'list 1 2 3 | toStrings' produces '["1" "2" "3"]'
|
||||||
|
- sortAlpha: sort a list lexicographically.
|
||||||
|
|
||||||
|
Integer Slice Functions:
|
||||||
|
|
||||||
|
- until: Given an integer, returns a slice of counting integers from 0 to one
|
||||||
|
less than the given integer: `range $i, $e := until 5`
|
||||||
|
- untilStep: Given start, stop, and step, return an integer slice starting at
|
||||||
|
'start', stopping at `stop`, and incrementing by 'step. This is the same
|
||||||
|
as Python's long-form of 'range'.
|
||||||
|
|
||||||
|
Conversions:
|
||||||
|
|
||||||
|
- atoi: Convert a string to an integer. 0 if the integer could not be parsed.
|
||||||
|
- in64: Convert a string or another numeric type to an int64.
|
||||||
|
- int: Convert a string or another numeric type to an int.
|
||||||
|
- float64: Convert a string or another numeric type to a float64.
|
||||||
|
|
||||||
|
Defaults:
|
||||||
|
|
||||||
|
- default: Give a default value. Used like this: trim " "| default "empty".
|
||||||
|
Since trim produces an empty string, the default value is returned. For
|
||||||
|
things with a length (strings, slices, maps), len(0) will trigger the default.
|
||||||
|
For numbers, the value 0 will trigger the default. For booleans, false will
|
||||||
|
trigger the default. For structs, the default is never returned (there is
|
||||||
|
no clear empty condition). For everything else, nil value triggers a default.
|
||||||
|
- empty: Return true if the given value is the zero value for its type.
|
||||||
|
Caveats: structs are always non-empty. This should match the behavior of
|
||||||
|
{{if pipeline}}, but can be used inside of a pipeline.
|
||||||
|
- coalesce: Given a list of items, return the first non-empty one.
|
||||||
|
This follows the same rules as 'empty'. '{{ coalesce .someVal 0 "hello" }}`
|
||||||
|
will return `.someVal` if set, or else return "hello". The 0 is skipped
|
||||||
|
because it is an empty value.
|
||||||
|
- compact: Return a copy of a list with all of the empty values removed.
|
||||||
|
'list 0 1 2 "" | compact' will return '[1 2]'
|
||||||
|
|
||||||
|
OS:
|
||||||
|
- env: Resolve an environment variable
|
||||||
|
- expandenv: Expand a string through the environment
|
||||||
|
|
||||||
|
File Paths:
|
||||||
|
- base: Return the last element of a path. https://golang.org/pkg/path#Base
|
||||||
|
- dir: Remove the last element of a path. https://golang.org/pkg/path#Dir
|
||||||
|
- clean: Clean a path to the shortest equivalent name. (e.g. remove "foo/.."
|
||||||
|
from "foo/../bar.html") https://golang.org/pkg/path#Clean
|
||||||
|
- ext: https://golang.org/pkg/path#Ext
|
||||||
|
- isAbs: https://golang.org/pkg/path#IsAbs
|
||||||
|
|
||||||
|
Encoding:
|
||||||
|
- b64enc: Base 64 encode a string.
|
||||||
|
- b64dec: Base 64 decode a string.
|
||||||
|
|
||||||
|
Reflection:
|
||||||
|
|
||||||
|
- typeOf: Takes an interface and returns a string representation of the type.
|
||||||
|
For pointers, this will return a type prefixed with an asterisk(`*`). So
|
||||||
|
a pointer to type `Foo` will be `*Foo`.
|
||||||
|
- typeIs: Compares an interface with a string name, and returns true if they match.
|
||||||
|
Note that a pointer will not match a reference. For example `*Foo` will not
|
||||||
|
match `Foo`.
|
||||||
|
- typeIsLike: Compares an interface with a string name and returns true if
|
||||||
|
the interface is that `name` or that `*name`. In other words, if the given
|
||||||
|
value matches the given type or is a pointer to the given type, this returns
|
||||||
|
true.
|
||||||
|
- kindOf: Takes an interface and returns a string representation of its kind.
|
||||||
|
- kindIs: Returns true if the given string matches the kind of the given interface.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Data Structures:
|
||||||
|
|
||||||
|
- tuple: Takes an arbitrary list of items and returns a slice of items. Its
|
||||||
|
tuple-ish properties are mainly gained through the template idiom, and not
|
||||||
|
through an API provided here. WARNING: The implementation of tuple will
|
||||||
|
change in the future.
|
||||||
|
- list: An arbitrary ordered list of items. (This is prefered over tuple.)
|
||||||
|
- dict: Takes a list of name/values and returns a map[string]interface{}.
|
||||||
|
The first parameter is converted to a string and stored as a key, the
|
||||||
|
second parameter is treated as the value. And so on, with odds as keys and
|
||||||
|
evens as values. If the function call ends with an odd, the last key will
|
||||||
|
be assigned the empty string. Non-string keys are converted to strings as
|
||||||
|
follows: []byte are converted, fmt.Stringers will have String() called.
|
||||||
|
errors will have Error() called. All others will be passed through
|
||||||
|
fmt.Sprtinf("%v").
|
||||||
|
|
||||||
|
Lists Functions:
|
||||||
|
|
||||||
|
These are used to manipulate lists: '{{ list 1 2 3 | reverse | first }}'
|
||||||
|
|
||||||
|
- first: Get the first item in a 'list'. 'list 1 2 3 | first' prints '1'
|
||||||
|
- last: Get the last item in a 'list': 'list 1 2 3 | last ' prints '3'
|
||||||
|
- rest: Get all but the first item in a list: 'list 1 2 3 | rest' returns '[2 3]'
|
||||||
|
- initial: Get all but the last item in a list: 'list 1 2 3 | initial' returns '[1 2]'
|
||||||
|
- append: Add an item to the end of a list: 'append $list 4' adds '4' to the end of '$list'
|
||||||
|
- prepend: Add an item to the beginning of a list: 'prepend $list 4' puts 4 at the beginning of the list.
|
||||||
|
- reverse: Reverse the items in a list.
|
||||||
|
- uniq: Remove duplicates from a list.
|
||||||
|
- without: Return a list with the given values removed: 'without (list 1 2 3) 1' would return '[2 3]'
|
||||||
|
- has: Return 'true' if the item is found in the list: 'has "foo" $list' will return 'true' if the list contains "foo"
|
||||||
|
|
||||||
|
Dict Functions:
|
||||||
|
|
||||||
|
These are used to manipulate dicts.
|
||||||
|
|
||||||
|
- set: Takes a dict, a key, and a value, and sets that key/value pair in
|
||||||
|
the dict. `set $dict $key $value`. For convenience, it returns the dict,
|
||||||
|
even though the dict was modified in place.
|
||||||
|
- unset: Takes a dict and a key, and deletes that key/value pair from the
|
||||||
|
dict. `unset $dict $key`. This returns the dict for convenience.
|
||||||
|
- hasKey: Takes a dict and a key, and returns boolean true if the key is in
|
||||||
|
the dict.
|
||||||
|
- pluck: Given a key and one or more maps, get all of the values for that key.
|
||||||
|
- keys: Get an array of all of the keys in a dict.
|
||||||
|
- pick: Select just the given keys out of the dict, and return a new dict.
|
||||||
|
- omit: Return a dict without the given keys.
|
||||||
|
|
||||||
|
Math Functions:
|
||||||
|
|
||||||
|
Integer functions will convert integers of any width to `int64`. If a
|
||||||
|
string is passed in, functions will attempt to convert with
|
||||||
|
`strconv.ParseInt(s, 1064)`. If this fails, the value will be treated as 0.
|
||||||
|
|
||||||
|
- add1: Increment an integer by 1
|
||||||
|
- add: Sum an arbitrary number of integers
|
||||||
|
- sub: Subtract the second integer from the first
|
||||||
|
- div: Divide the first integer by the second
|
||||||
|
- mod: Module of first integer divided by second
|
||||||
|
- mul: Multiply integers
|
||||||
|
- max: Return the biggest of a series of one or more integers
|
||||||
|
- min: Return the smallest of a series of one or more integers
|
||||||
|
- biggest: DEPRECATED. Return the biggest of a series of one or more integers
|
||||||
|
|
||||||
|
Crypto Functions:
|
||||||
|
|
||||||
|
- genPrivateKey: Generate a private key for the given cryptosystem. If no
|
||||||
|
argument is supplied, by default it will generate a private key using
|
||||||
|
the RSA algorithm. Accepted values are `rsa`, `dsa`, and `ecdsa`.
|
||||||
|
- derivePassword: Derive a password from the given parameters according to the ["Master Password" algorithm](http://masterpasswordapp.com/algorithm.html)
|
||||||
|
Given parameters (in order) are:
|
||||||
|
`counter` (starting with 1), `password_type` (maximum, long, medium, short, basic, or pin), `password`,
|
||||||
|
`user`, and `site`
|
||||||
|
|
||||||
|
SemVer Functions:
|
||||||
|
|
||||||
|
These functions provide version parsing and comparisons for SemVer 2 version
|
||||||
|
strings.
|
||||||
|
|
||||||
|
- semver: Parse a semantic version and return a Version object.
|
||||||
|
- semverCompare: Compare a SemVer range to a particular version.
|
||||||
|
*/
|
||||||
|
package sprig
|
1
vendor/github.com/Masterminds/sprig/docs/_config.yml
generated
vendored
Normal file
1
vendor/github.com/Masterminds/sprig/docs/_config.yml
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
theme: jekyll-theme-slate
|
25
vendor/github.com/Masterminds/sprig/docs/conversion.md
generated
vendored
Normal file
25
vendor/github.com/Masterminds/sprig/docs/conversion.md
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# 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.
|
37
vendor/github.com/Masterminds/sprig/docs/crypto.md
generated
vendored
Normal file
37
vendor/github.com/Masterminds/sprig/docs/crypto.md
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# 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
|
62
vendor/github.com/Masterminds/sprig/docs/date.md
generated
vendored
Normal file
62
vendor/github.com/Masterminds/sprig/docs/date.md
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# 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"
|
||||||
|
```
|
||||||
|
|
59
vendor/github.com/Masterminds/sprig/docs/defaults.md
generated
vendored
Normal file
59
vendor/github.com/Masterminds/sprig/docs/defaults.md
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# 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`.
|
115
vendor/github.com/Masterminds/sprig/docs/dicts.md
generated
vendored
Normal file
115
vendor/github.com/Masterminds/sprig/docs/dicts.md
generated
vendored
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
|
||||||
|
## 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.
|
6
vendor/github.com/Masterminds/sprig/docs/encoding.md
generated
vendored
Normal file
6
vendor/github.com/Masterminds/sprig/docs/encoding.md
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# Encoding Functions
|
||||||
|
|
||||||
|
Sprig has the following encoding and decoding functions:
|
||||||
|
|
||||||
|
- `b64enc`/`b64dec`: Encode or decode with Base64
|
||||||
|
- `b32enc`/`b32dec`: Encode or decode with Base32
|
22
vendor/github.com/Masterminds/sprig/docs/index.md
generated
vendored
Normal file
22
vendor/github.com/Masterminds/sprig/docs/index.md
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# Sprig Function Documentation
|
||||||
|
|
||||||
|
The Sprig library provides over 70 template functions for Go's template language.
|
||||||
|
|
||||||
|
- [String Functions](strings.html): `trim`, `wrap`, `randAlpha`, `plural`, etc.
|
||||||
|
- [String List Functions](string_slice.html): `splitList`, `sortAlpha`, etc.
|
||||||
|
- [Math Functions](math.html): `add`, `max`, `mul`, etc.
|
||||||
|
- [Integer Slice Functions](integer_slice.html): `until`, `untilStep`
|
||||||
|
- [Date Functions](date.html): `now`, `date`, etc.
|
||||||
|
- [Defaults Functions](defaults.html): `default`, `empty`, `coalesce`
|
||||||
|
- [Encoding Functions](encoding.html): `b64enc`, `b64dec`, etc.
|
||||||
|
- [Lists and List Functions](lists.html): `list`, `first`, `uniq`, etc.
|
||||||
|
- [Dictionaries and Dict Functions](dicts.html): `dict`, `hasKey`, `pluck`, etc.
|
||||||
|
- [Type Conversion Functions](conversion.html): `atoi`, `int64`, `toString`, etc.
|
||||||
|
- [File Path Functions](paths.html): `base`, `dir`, `ext`, `clean`, `isAbs`
|
||||||
|
- Advanced Functions
|
||||||
|
- [UUID Functions](uuid.html): `uuidv4`
|
||||||
|
- [OS Functions](os.html): `env`, `expandenv`
|
||||||
|
- [Version Comparison Functions](semver.html): `semver`, `semverCompare`
|
||||||
|
- [Reflection](reflection.html): `typeOf`, `kindIs`, `typeIsLike`, etc.
|
||||||
|
- [Cryptographic and Security Functions](crypto.html): `derivePassword`, `sha256sum`, `genPrivateKey`
|
||||||
|
|
25
vendor/github.com/Masterminds/sprig/docs/integer_slice.md
generated
vendored
Normal file
25
vendor/github.com/Masterminds/sprig/docs/integer_slice.md
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# 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.
|
111
vendor/github.com/Masterminds/sprig/docs/lists.md
generated
vendored
Normal file
111
vendor/github.com/Masterminds/sprig/docs/lists.md
generated
vendored
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
# 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.
|
46
vendor/github.com/Masterminds/sprig/docs/math.md
generated
vendored
Normal file
46
vendor/github.com/Masterminds/sprig/docs/math.md
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# 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`.
|
||||||
|
|
24
vendor/github.com/Masterminds/sprig/docs/os.md
generated
vendored
Normal file
24
vendor/github.com/Masterminds/sprig/docs/os.md
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# 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"
|
||||||
|
```
|
43
vendor/github.com/Masterminds/sprig/docs/paths.md
generated
vendored
Normal file
43
vendor/github.com/Masterminds/sprig/docs/paths.md
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# 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`.
|
38
vendor/github.com/Masterminds/sprig/docs/reflection.md
generated
vendored
Normal file
38
vendor/github.com/Masterminds/sprig/docs/reflection.md
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# 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.
|
124
vendor/github.com/Masterminds/sprig/docs/semver.md
generated
vendored
Normal file
124
vendor/github.com/Masterminds/sprig/docs/semver.md
generated
vendored
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
# 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`
|
||||||
|
|
55
vendor/github.com/Masterminds/sprig/docs/string_slice.md
generated
vendored
Normal file
55
vendor/github.com/Masterminds/sprig/docs/string_slice.md
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# 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.
|
285
vendor/github.com/Masterminds/sprig/docs/strings.md
generated
vendored
Normal file
285
vendor/github.com/Masterminds/sprig/docs/strings.md
generated
vendored
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
## 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.
|
9
vendor/github.com/Masterminds/sprig/docs/uuid.md
generated
vendored
Normal file
9
vendor/github.com/Masterminds/sprig/docs/uuid.md
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# UUID Functions
|
||||||
|
|
||||||
|
Sprig can generate UUID v4 universally unique IDs.
|
||||||
|
|
||||||
|
```
|
||||||
|
uuidv4
|
||||||
|
```
|
||||||
|
|
||||||
|
The above returns a new UUID of the v4 (randomly generated) type.
|
735
vendor/github.com/Masterminds/sprig/functions.go
generated
vendored
735
vendor/github.com/Masterminds/sprig/functions.go
generated
vendored
@ -1,215 +1,22 @@
|
|||||||
/*
|
|
||||||
Sprig: Template functions for Go.
|
|
||||||
|
|
||||||
This package contains a number of utility functions for working with data
|
|
||||||
inside of Go `html/template` and `text/template` files.
|
|
||||||
|
|
||||||
To add these functions, use the `template.Funcs()` method:
|
|
||||||
|
|
||||||
t := templates.New("foo").Funcs(sprig.FuncMap())
|
|
||||||
|
|
||||||
Note that you should add the function map before you parse any template files.
|
|
||||||
|
|
||||||
In several cases, Sprig reverses the order of arguments from the way they
|
|
||||||
appear in the standard library. This is to make it easier to pipe
|
|
||||||
arguments into functions.
|
|
||||||
|
|
||||||
Date Functions
|
|
||||||
|
|
||||||
- date FORMAT TIME: Format a date, where a date is an integer type or a time.Time type, and
|
|
||||||
format is a time.Format formatting string.
|
|
||||||
- dateModify: Given a date, modify it with a duration: `date_modify "-1.5h" now`. If the duration doesn't
|
|
||||||
parse, it returns the time unaltered. See `time.ParseDuration` for info on duration strings.
|
|
||||||
- now: Current time.Time, for feeding into date-related functions.
|
|
||||||
- htmlDate TIME: Format a date for use in the value field of an HTML "date" form element.
|
|
||||||
- dateInZone FORMAT TIME TZ: Like date, but takes three arguments: format, timestamp,
|
|
||||||
timezone.
|
|
||||||
- htmlDateInZone TIME TZ: Like htmlDate, but takes two arguments: timestamp,
|
|
||||||
timezone.
|
|
||||||
|
|
||||||
String Functions
|
|
||||||
|
|
||||||
- abbrev: Truncate a string with ellipses. `abbrev 5 "hello world"` yields "he..."
|
|
||||||
- abbrevboth: Abbreviate from both sides, yielding "...lo wo..."
|
|
||||||
- trunc: Truncate a string (no suffix). `trunc 5 "Hello World"` yields "hello".
|
|
||||||
- trim: strings.TrimSpace
|
|
||||||
- trimAll: strings.Trim, but with the argument order reversed `trimAll "$" "$5.00"` or `"$5.00 | trimAll "$"`
|
|
||||||
- trimSuffix: strings.TrimSuffix, but with the argument order reversed: `trimSuffix "-" "ends-with-"`
|
|
||||||
- trimPrefix: strings.TrimPrefix, but with the argument order reversed `trimPrefix "$" "$5"`
|
|
||||||
- upper: strings.ToUpper
|
|
||||||
- lower: strings.ToLower
|
|
||||||
- nospace: Remove all space characters from a string. `nospace "h e l l o"` becomes "hello"
|
|
||||||
- title: strings.Title
|
|
||||||
- untitle: Remove title casing
|
|
||||||
- repeat: strings.Repeat, but with the arguments switched: `repeat count str`. (This simplifies common pipelines)
|
|
||||||
- substr: Given string, start, and length, return a substr.
|
|
||||||
- initials: Given a multi-word string, return the initials. `initials "Matt Butcher"` returns "MB"
|
|
||||||
- randAlphaNum: Given a length, generate a random alphanumeric sequence
|
|
||||||
- randAlpha: Given a length, generate an alphabetic string
|
|
||||||
- randAscii: Given a length, generate a random ASCII string (symbols included)
|
|
||||||
- randNumeric: Given a length, generate a string of digits.
|
|
||||||
- wrap: Force a line wrap at the given width. `wrap 80 "imagine a longer string"`
|
|
||||||
- wrapWith: Wrap a line at the given length, but using 'sep' instead of a newline. `wrapWith 50, "<br>", $html`
|
|
||||||
- contains: strings.Contains, but with the arguments switched: `contains substr str`. (This simplifies common pipelines)
|
|
||||||
- hasPrefix: strings.hasPrefix, but with the arguments switched
|
|
||||||
- hasSuffix: strings.hasSuffix, but with the arguments switched
|
|
||||||
- quote: Wrap string(s) in double quotation marks, escape the contents by adding '\' before '"'.
|
|
||||||
- squote: Wrap string(s) in double quotation marks, does not escape content.
|
|
||||||
- cat: Concatenate strings, separating them by spaces. `cat $a $b $c`.
|
|
||||||
- indent: Indent a string using space characters. `indent 4 "foo\nbar"` produces " foo\n bar"
|
|
||||||
- replace: Replace an old with a new in a string: `$name | replace " " "-"`
|
|
||||||
- plural: Choose singular or plural based on length: `len $fish | plural "one anchovy" "many anchovies"`
|
|
||||||
- sha256sum: Generate a hex encoded sha256 hash of the input
|
|
||||||
|
|
||||||
String Slice Functions:
|
|
||||||
|
|
||||||
- join: strings.Join, but as `join SEP SLICE`
|
|
||||||
- split: strings.Split, but as `split SEP STRING`. The results are returned
|
|
||||||
as a map with the indexes set to _N, where N is an integer starting from 0.
|
|
||||||
Use it like this: `{{$v := "foo/bar/baz" | split "/"}}{{$v._0}}` (Prints `foo`)
|
|
||||||
|
|
||||||
Integer Slice Functions:
|
|
||||||
|
|
||||||
- until: Given an integer, returns a slice of counting integers from 0 to one
|
|
||||||
less than the given integer: `range $i, $e := until 5`
|
|
||||||
- untilStep: Given start, stop, and step, return an integer slice starting at
|
|
||||||
'start', stopping at `stop`, and incrementing by 'step. This is the same
|
|
||||||
as Python's long-form of 'range'.
|
|
||||||
|
|
||||||
Conversions:
|
|
||||||
|
|
||||||
- atoi: Convert a string to an integer. 0 if the integer could not be parsed.
|
|
||||||
- in64: Convert a string or another numeric type to an int64.
|
|
||||||
- int: Convert a string or another numeric type to an int.
|
|
||||||
- float64: Convert a string or another numeric type to a float64.
|
|
||||||
|
|
||||||
Defaults:
|
|
||||||
|
|
||||||
- default: Give a default value. Used like this: trim " "| default "empty".
|
|
||||||
Since trim produces an empty string, the default value is returned. For
|
|
||||||
things with a length (strings, slices, maps), len(0) will trigger the default.
|
|
||||||
For numbers, the value 0 will trigger the default. For booleans, false will
|
|
||||||
trigger the default. For structs, the default is never returned (there is
|
|
||||||
no clear empty condition). For everything else, nil value triggers a default.
|
|
||||||
- empty: Return true if the given value is the zero value for its type.
|
|
||||||
Caveats: structs are always non-empty. This should match the behavior of
|
|
||||||
{{if pipeline}}, but can be used inside of a pipeline.
|
|
||||||
|
|
||||||
OS:
|
|
||||||
- env: Resolve an environment variable
|
|
||||||
- expandenv: Expand a string through the environment
|
|
||||||
|
|
||||||
File Paths:
|
|
||||||
- base: Return the last element of a path. https://golang.org/pkg/path#Base
|
|
||||||
- dir: Remove the last element of a path. https://golang.org/pkg/path#Dir
|
|
||||||
- clean: Clean a path to the shortest equivalent name. (e.g. remove "foo/.."
|
|
||||||
from "foo/../bar.html") https://golang.org/pkg/path#Clean
|
|
||||||
- ext: https://golang.org/pkg/path#Ext
|
|
||||||
- isAbs: https://golang.org/pkg/path#IsAbs
|
|
||||||
|
|
||||||
Encoding:
|
|
||||||
- b64enc: Base 64 encode a string.
|
|
||||||
- b64dec: Base 64 decode a string.
|
|
||||||
|
|
||||||
Reflection:
|
|
||||||
|
|
||||||
- typeOf: Takes an interface and returns a string representation of the type.
|
|
||||||
For pointers, this will return a type prefixed with an asterisk(`*`). So
|
|
||||||
a pointer to type `Foo` will be `*Foo`.
|
|
||||||
- typeIs: Compares an interface with a string name, and returns true if they match.
|
|
||||||
Note that a pointer will not match a reference. For example `*Foo` will not
|
|
||||||
match `Foo`.
|
|
||||||
- typeIsLike: Compares an interface with a string name and returns true if
|
|
||||||
the interface is that `name` or that `*name`. In other words, if the given
|
|
||||||
value matches the given type or is a pointer to the given type, this returns
|
|
||||||
true.
|
|
||||||
- kindOf: Takes an interface and returns a string representation of its kind.
|
|
||||||
- kindIs: Returns true if the given string matches the kind of the given interface.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Data Structures:
|
|
||||||
|
|
||||||
- tuple: Takes an arbitrary list of items and returns a slice of items. Its
|
|
||||||
tuple-ish properties are mainly gained through the template idiom, and not
|
|
||||||
through an API provided here.
|
|
||||||
- dict: Takes a list of name/values and returns a map[string]interface{}.
|
|
||||||
The first parameter is converted to a string and stored as a key, the
|
|
||||||
second parameter is treated as the value. And so on, with odds as keys and
|
|
||||||
evens as values. If the function call ends with an odd, the last key will
|
|
||||||
be assigned the empty string. Non-string keys are converted to strings as
|
|
||||||
follows: []byte are converted, fmt.Stringers will have String() called.
|
|
||||||
errors will have Error() called. All others will be passed through
|
|
||||||
fmt.Sprtinf("%v").
|
|
||||||
- set: Takes a dict, a key, and a value, and sets that key/value pair in
|
|
||||||
the dict. `set $dict $key $value`. For convenience, it returns the dict,
|
|
||||||
even though the dict was modified in place.
|
|
||||||
- unset: Takes a dict and a key, and deletes that key/value pair from the
|
|
||||||
dict. `unset $dict $key`. This returns the dict for convenience.
|
|
||||||
- hasKey: Takes a dict and a key, and returns boolean true if the key is in
|
|
||||||
the dict.
|
|
||||||
|
|
||||||
Math Functions:
|
|
||||||
|
|
||||||
Integer functions will convert integers of any width to `int64`. If a
|
|
||||||
string is passed in, functions will attempt to convert with
|
|
||||||
`strconv.ParseInt(s, 1064)`. If this fails, the value will be treated as 0.
|
|
||||||
|
|
||||||
- add1: Increment an integer by 1
|
|
||||||
- add: Sum an arbitrary number of integers
|
|
||||||
- sub: Subtract the second integer from the first
|
|
||||||
- div: Divide the first integer by the second
|
|
||||||
- mod: Module of first integer divided by second
|
|
||||||
- mul: Multiply integers
|
|
||||||
- max: Return the biggest of a series of one or more integers
|
|
||||||
- min: Return the smallest of a series of one or more integers
|
|
||||||
- biggest: DEPRECATED. Return the biggest of a series of one or more integers
|
|
||||||
|
|
||||||
Crypto Functions:
|
|
||||||
|
|
||||||
- genPrivateKey: Generate a private key for the given cryptosystem. If no
|
|
||||||
argument is supplied, by default it will generate a private key using
|
|
||||||
the RSA algorithm. Accepted values are `rsa`, `dsa`, and `ecdsa`.
|
|
||||||
|
|
||||||
*/
|
|
||||||
package sprig
|
package sprig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/dsa"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/asn1"
|
|
||||||
"encoding/base32"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/pem"
|
|
||||||
"fmt"
|
|
||||||
"html/template"
|
"html/template"
|
||||||
"math"
|
|
||||||
"math/big"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"reflect"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
ttemplate "text/template"
|
ttemplate "text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
util "github.com/aokoli/goutils"
|
util "github.com/aokoli/goutils"
|
||||||
uuid "github.com/satori/go.uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Produce the function map.
|
// Produce the function map.
|
||||||
//
|
//
|
||||||
// Use this to pass the functions into the template engine:
|
// Use this to pass the functions into the template engine:
|
||||||
//
|
//
|
||||||
// tpl := template.New("foo").Funcs(sprig.FuncMap))
|
// tpl := template.New("foo").Funcs(sprig.FuncMap()))
|
||||||
//
|
//
|
||||||
func FuncMap() template.FuncMap {
|
func FuncMap() template.FuncMap {
|
||||||
return HtmlFuncMap()
|
return HtmlFuncMap()
|
||||||
@ -235,12 +42,21 @@ func HermeticHtmlFuncMap() template.FuncMap {
|
|||||||
|
|
||||||
// TextFuncMap returns a 'text/template'.FuncMap
|
// TextFuncMap returns a 'text/template'.FuncMap
|
||||||
func TxtFuncMap() ttemplate.FuncMap {
|
func TxtFuncMap() ttemplate.FuncMap {
|
||||||
return ttemplate.FuncMap(genericMap)
|
return ttemplate.FuncMap(GenericFuncMap())
|
||||||
}
|
}
|
||||||
|
|
||||||
// HtmlFuncMap returns an 'html/template'.Funcmap
|
// HtmlFuncMap returns an 'html/template'.Funcmap
|
||||||
func HtmlFuncMap() template.FuncMap {
|
func HtmlFuncMap() template.FuncMap {
|
||||||
return template.FuncMap(genericMap)
|
return template.FuncMap(GenericFuncMap())
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenericFuncMap returns a copy of the basic function map as a map[string]interface{}.
|
||||||
|
func GenericFuncMap() map[string]interface{} {
|
||||||
|
gfm := make(map[string]interface{}, len(genericMap))
|
||||||
|
for k, v := range genericMap {
|
||||||
|
gfm[k] = v
|
||||||
|
}
|
||||||
|
return gfm
|
||||||
}
|
}
|
||||||
|
|
||||||
// These functions are not guaranteed to evaluate to the same result for given input, because they
|
// These functions are not guaranteed to evaluate to the same result for given input, because they
|
||||||
@ -319,6 +135,7 @@ var genericMap = map[string]interface{}{
|
|||||||
"replace": replace,
|
"replace": replace,
|
||||||
"plural": plural,
|
"plural": plural,
|
||||||
"sha256sum": sha256sum,
|
"sha256sum": sha256sum,
|
||||||
|
"toString": strval,
|
||||||
|
|
||||||
// Wrap Atoi to stop errors.
|
// Wrap Atoi to stop errors.
|
||||||
"atoi": func(a string) int { i, _ := strconv.Atoi(a); return i },
|
"atoi": func(a string) int { i, _ := strconv.Atoi(a); return i },
|
||||||
@ -332,7 +149,9 @@ var genericMap = map[string]interface{}{
|
|||||||
//"lte": func(a, b int) bool {return a <= b},
|
//"lte": func(a, b int) bool {return a <= b},
|
||||||
|
|
||||||
// split "/" foo/bar returns map[int]string{0: foo, 1: bar}
|
// split "/" foo/bar returns map[int]string{0: foo, 1: bar}
|
||||||
"split": split,
|
"split": split,
|
||||||
|
"splitList": func(sep, orig string) []string { return strings.Split(orig, sep) },
|
||||||
|
"toStrings": strslice,
|
||||||
|
|
||||||
"until": until,
|
"until": until,
|
||||||
"untilStep": untilStep,
|
"untilStep": untilStep,
|
||||||
@ -362,11 +181,14 @@ var genericMap = map[string]interface{}{
|
|||||||
|
|
||||||
// string slices. Note that we reverse the order b/c that's better
|
// string slices. Note that we reverse the order b/c that's better
|
||||||
// for template processing.
|
// for template processing.
|
||||||
"join": func(sep string, ss []string) string { return strings.Join(ss, sep) },
|
"join": join,
|
||||||
|
"sortAlpha": sortAlpha,
|
||||||
|
|
||||||
// Defaults
|
// Defaults
|
||||||
"default": dfault,
|
"default": dfault,
|
||||||
"empty": empty,
|
"empty": empty,
|
||||||
|
"coalesce": coalesce,
|
||||||
|
"compact": compact,
|
||||||
|
|
||||||
// Reflection
|
// Reflection
|
||||||
"typeOf": typeOf,
|
"typeOf": typeOf,
|
||||||
@ -393,503 +215,36 @@ var genericMap = map[string]interface{}{
|
|||||||
"b32dec": base32decode,
|
"b32dec": base32decode,
|
||||||
|
|
||||||
// Data Structures:
|
// Data Structures:
|
||||||
"tuple": tuple,
|
"tuple": list, // FIXME: with the addition of append/prepend these are no longer immutable.
|
||||||
|
"list": list,
|
||||||
"dict": dict,
|
"dict": dict,
|
||||||
"set": set,
|
"set": set,
|
||||||
"unset": unset,
|
"unset": unset,
|
||||||
"hasKey": hasKey,
|
"hasKey": hasKey,
|
||||||
|
"pluck": pluck,
|
||||||
|
"keys": keys,
|
||||||
|
"pick": pick,
|
||||||
|
"omit": omit,
|
||||||
|
|
||||||
|
"append": push, "push": push,
|
||||||
|
"prepend": prepend,
|
||||||
|
"first": first,
|
||||||
|
"rest": rest,
|
||||||
|
"last": last,
|
||||||
|
"initial": initial,
|
||||||
|
"reverse": reverse,
|
||||||
|
"uniq": uniq,
|
||||||
|
"without": without,
|
||||||
|
"has": func(needle interface{}, haystack []interface{}) bool { return inList(haystack, needle) },
|
||||||
|
|
||||||
// Crypto:
|
// Crypto:
|
||||||
"genPrivateKey": generatePrivateKey,
|
"genPrivateKey": generatePrivateKey,
|
||||||
|
"derivePassword": derivePassword,
|
||||||
|
|
||||||
// UUIDs:
|
// UUIDs:
|
||||||
"uuidv4": uuidv4,
|
"uuidv4": uuidv4,
|
||||||
}
|
|
||||||
|
// SemVer:
|
||||||
func split(sep, orig string) map[string]string {
|
"semver": semver,
|
||||||
parts := strings.Split(orig, sep)
|
"semverCompare": semverCompare,
|
||||||
res := make(map[string]string, len(parts))
|
|
||||||
for i, v := range parts {
|
|
||||||
res["_"+strconv.Itoa(i)] = v
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
// substring creates a substring of the given string.
|
|
||||||
//
|
|
||||||
// If start is < 0, this calls string[:length].
|
|
||||||
//
|
|
||||||
// If start is >= 0 and length < 0, this calls string[start:]
|
|
||||||
//
|
|
||||||
// Otherwise, this calls string[start, length].
|
|
||||||
func substring(start, length int, s string) string {
|
|
||||||
if start < 0 {
|
|
||||||
return s[:length]
|
|
||||||
}
|
|
||||||
if length < 0 {
|
|
||||||
return s[start:]
|
|
||||||
}
|
|
||||||
return s[start:length]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Given a format and a date, format the date string.
|
|
||||||
//
|
|
||||||
// Date can be a `time.Time` or an `int, int32, int64`.
|
|
||||||
// In the later case, it is treated as seconds since UNIX
|
|
||||||
// epoch.
|
|
||||||
func date(fmt string, date interface{}) string {
|
|
||||||
return dateInZone(fmt, date, "Local")
|
|
||||||
}
|
|
||||||
|
|
||||||
func htmlDate(date interface{}) string {
|
|
||||||
return dateInZone("2006-01-02", date, "Local")
|
|
||||||
}
|
|
||||||
|
|
||||||
func htmlDateInZone(date interface{}, zone string) string {
|
|
||||||
return dateInZone("2006-01-02", date, zone)
|
|
||||||
}
|
|
||||||
|
|
||||||
func dateInZone(fmt string, date interface{}, zone string) string {
|
|
||||||
var t time.Time
|
|
||||||
switch date := date.(type) {
|
|
||||||
default:
|
|
||||||
t = time.Now()
|
|
||||||
case time.Time:
|
|
||||||
t = date
|
|
||||||
case int64:
|
|
||||||
t = time.Unix(date, 0)
|
|
||||||
case int:
|
|
||||||
t = time.Unix(int64(date), 0)
|
|
||||||
case int32:
|
|
||||||
t = time.Unix(int64(date), 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
loc, err := time.LoadLocation(zone)
|
|
||||||
if err != nil {
|
|
||||||
loc, _ = time.LoadLocation("UTC")
|
|
||||||
}
|
|
||||||
|
|
||||||
return t.In(loc).Format(fmt)
|
|
||||||
}
|
|
||||||
|
|
||||||
func dateModify(fmt string, date time.Time) time.Time {
|
|
||||||
d, err := time.ParseDuration(fmt)
|
|
||||||
if err != nil {
|
|
||||||
return date
|
|
||||||
}
|
|
||||||
return date.Add(d)
|
|
||||||
}
|
|
||||||
|
|
||||||
func max(a interface{}, i ...interface{}) int64 {
|
|
||||||
aa := toInt64(a)
|
|
||||||
for _, b := range i {
|
|
||||||
bb := toInt64(b)
|
|
||||||
if bb > aa {
|
|
||||||
aa = bb
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return aa
|
|
||||||
}
|
|
||||||
|
|
||||||
func min(a interface{}, i ...interface{}) int64 {
|
|
||||||
aa := toInt64(a)
|
|
||||||
for _, b := range i {
|
|
||||||
bb := toInt64(b)
|
|
||||||
if bb < aa {
|
|
||||||
aa = bb
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return aa
|
|
||||||
}
|
|
||||||
|
|
||||||
// dfault checks whether `given` is set, and returns default if not set.
|
|
||||||
//
|
|
||||||
// This returns `d` if `given` appears not to be set, and `given` otherwise.
|
|
||||||
//
|
|
||||||
// For numeric types 0 is unset.
|
|
||||||
// For strings, maps, arrays, and slices, len() = 0 is considered unset.
|
|
||||||
// For bool, false is unset.
|
|
||||||
// Structs are never considered unset.
|
|
||||||
//
|
|
||||||
// For everything else, including pointers, a nil value is unset.
|
|
||||||
func dfault(d interface{}, given ...interface{}) interface{} {
|
|
||||||
|
|
||||||
if empty(given) || empty(given[0]) {
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
return given[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// empty returns true if the given value has the zero value for its type.
|
|
||||||
func empty(given interface{}) bool {
|
|
||||||
g := reflect.ValueOf(given)
|
|
||||||
if !g.IsValid() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Basically adapted from text/template.isTrue
|
|
||||||
switch g.Kind() {
|
|
||||||
default:
|
|
||||||
return g.IsNil()
|
|
||||||
case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
|
|
||||||
return g.Len() == 0
|
|
||||||
case reflect.Bool:
|
|
||||||
return g.Bool() == false
|
|
||||||
case reflect.Complex64, reflect.Complex128:
|
|
||||||
return g.Complex() == 0
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
return g.Int() == 0
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
||||||
return g.Uint() == 0
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return g.Float() == 0
|
|
||||||
case reflect.Struct:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// typeIs returns true if the src is the type named in target.
|
|
||||||
func typeIs(target string, src interface{}) bool {
|
|
||||||
return target == typeOf(src)
|
|
||||||
}
|
|
||||||
|
|
||||||
func typeIsLike(target string, src interface{}) bool {
|
|
||||||
t := typeOf(src)
|
|
||||||
return target == t || "*"+target == t
|
|
||||||
}
|
|
||||||
|
|
||||||
func typeOf(src interface{}) string {
|
|
||||||
return fmt.Sprintf("%T", src)
|
|
||||||
}
|
|
||||||
|
|
||||||
func kindIs(target string, src interface{}) bool {
|
|
||||||
return target == kindOf(src)
|
|
||||||
}
|
|
||||||
|
|
||||||
func kindOf(src interface{}) string {
|
|
||||||
return reflect.ValueOf(src).Kind().String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func base64encode(v string) string {
|
|
||||||
return base64.StdEncoding.EncodeToString([]byte(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func base64decode(v string) string {
|
|
||||||
data, err := base64.StdEncoding.DecodeString(v)
|
|
||||||
if err != nil {
|
|
||||||
return err.Error()
|
|
||||||
}
|
|
||||||
return string(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func base32encode(v string) string {
|
|
||||||
return base32.StdEncoding.EncodeToString([]byte(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func base32decode(v string) string {
|
|
||||||
data, err := base32.StdEncoding.DecodeString(v)
|
|
||||||
if err != nil {
|
|
||||||
return err.Error()
|
|
||||||
}
|
|
||||||
return string(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func abbrev(width int, s string) string {
|
|
||||||
if width < 4 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
r, _ := util.Abbreviate(s, width)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func abbrevboth(left, right int, s string) string {
|
|
||||||
if right < 4 || left > 0 && right < 7 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
r, _ := util.AbbreviateFull(s, left, right)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
func initials(s string) string {
|
|
||||||
// Wrap this just to eliminate the var args, which templates don't do well.
|
|
||||||
return util.Initials(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func randAlphaNumeric(count int) string {
|
|
||||||
// It is not possible, it appears, to actually generate an error here.
|
|
||||||
r, _ := util.RandomAlphaNumeric(count)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func randAlpha(count int) string {
|
|
||||||
r, _ := util.RandomAlphabetic(count)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func randAscii(count int) string {
|
|
||||||
r, _ := util.RandomAscii(count)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func randNumeric(count int) string {
|
|
||||||
r, _ := util.RandomNumeric(count)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func untitle(str string) string {
|
|
||||||
return util.Uncapitalize(str)
|
|
||||||
}
|
|
||||||
|
|
||||||
func quote(str ...interface{}) string {
|
|
||||||
out := make([]string, len(str))
|
|
||||||
for i, s := range str {
|
|
||||||
out[i] = fmt.Sprintf("%q", strval(s))
|
|
||||||
}
|
|
||||||
return strings.Join(out, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
func squote(str ...interface{}) string {
|
|
||||||
out := make([]string, len(str))
|
|
||||||
for i, s := range str {
|
|
||||||
out[i] = fmt.Sprintf("'%v'", s)
|
|
||||||
}
|
|
||||||
return strings.Join(out, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
func tuple(v ...interface{}) []interface{} {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func set(d map[string]interface{}, key string, value interface{}) map[string]interface{} {
|
|
||||||
d[key] = value
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
func unset(d map[string]interface{}, key string) map[string]interface{} {
|
|
||||||
delete(d, key)
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasKey(d map[string]interface{}, key string) bool {
|
|
||||||
_, ok := d[key]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func dict(v ...interface{}) map[string]interface{} {
|
|
||||||
dict := map[string]interface{}{}
|
|
||||||
lenv := len(v)
|
|
||||||
for i := 0; i < lenv; i += 2 {
|
|
||||||
key := strval(v[i])
|
|
||||||
if i+1 >= lenv {
|
|
||||||
dict[key] = ""
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dict[key] = v[i+1]
|
|
||||||
}
|
|
||||||
return dict
|
|
||||||
}
|
|
||||||
|
|
||||||
func strval(v interface{}) string {
|
|
||||||
switch v := v.(type) {
|
|
||||||
case string:
|
|
||||||
return v
|
|
||||||
case []byte:
|
|
||||||
return string(v)
|
|
||||||
case error:
|
|
||||||
return v.Error()
|
|
||||||
case fmt.Stringer:
|
|
||||||
return v.String()
|
|
||||||
default:
|
|
||||||
return fmt.Sprintf("%v", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// toFloat64 converts 64-bit floats
|
|
||||||
func toFloat64(v interface{}) float64 {
|
|
||||||
if str, ok := v.(string); ok {
|
|
||||||
iv, err := strconv.ParseFloat(str, 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return iv
|
|
||||||
}
|
|
||||||
|
|
||||||
val := reflect.Indirect(reflect.ValueOf(v))
|
|
||||||
switch val.Kind() {
|
|
||||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
|
||||||
return float64(val.Int())
|
|
||||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
|
||||||
return float64(val.Uint())
|
|
||||||
case reflect.Uint, reflect.Uint64:
|
|
||||||
return float64(val.Uint())
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return val.Float()
|
|
||||||
case reflect.Bool:
|
|
||||||
if val.Bool() == true {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
default:
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func toInt(v interface{}) int {
|
|
||||||
//It's not optimal. Bud I don't want duplicate toInt64 code.
|
|
||||||
return int(toInt64(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
// toInt64 converts integer types to 64-bit integers
|
|
||||||
func toInt64(v interface{}) int64 {
|
|
||||||
if str, ok := v.(string); ok {
|
|
||||||
iv, err := strconv.ParseInt(str, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return iv
|
|
||||||
}
|
|
||||||
|
|
||||||
val := reflect.Indirect(reflect.ValueOf(v))
|
|
||||||
switch val.Kind() {
|
|
||||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
|
||||||
return val.Int()
|
|
||||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
|
||||||
return int64(val.Uint())
|
|
||||||
case reflect.Uint, reflect.Uint64:
|
|
||||||
tv := val.Uint()
|
|
||||||
if tv <= math.MaxInt64 {
|
|
||||||
return int64(tv)
|
|
||||||
}
|
|
||||||
// TODO: What is the sensible thing to do here?
|
|
||||||
return math.MaxInt64
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return int64(val.Float())
|
|
||||||
case reflect.Bool:
|
|
||||||
if val.Bool() == true {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
default:
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func generatePrivateKey(typ string) string {
|
|
||||||
var priv interface{}
|
|
||||||
var err error
|
|
||||||
switch typ {
|
|
||||||
case "", "rsa":
|
|
||||||
// good enough for government work
|
|
||||||
priv, err = rsa.GenerateKey(rand.Reader, 4096)
|
|
||||||
case "dsa":
|
|
||||||
key := new(dsa.PrivateKey)
|
|
||||||
// again, good enough for government work
|
|
||||||
if err = dsa.GenerateParameters(&key.Parameters, rand.Reader, dsa.L2048N256); err != nil {
|
|
||||||
return fmt.Sprintf("failed to generate dsa params: %s", err)
|
|
||||||
}
|
|
||||||
err = dsa.GenerateKey(key, rand.Reader)
|
|
||||||
priv = key
|
|
||||||
case "ecdsa":
|
|
||||||
// again, good enough for government work
|
|
||||||
priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
||||||
default:
|
|
||||||
return "Unknown type " + typ
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Sprintf("failed to generate private key: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(pem.EncodeToMemory(pemBlockForKey(priv)))
|
|
||||||
}
|
|
||||||
|
|
||||||
type DSAKeyFormat struct {
|
|
||||||
Version int
|
|
||||||
P, Q, G, Y, X *big.Int
|
|
||||||
}
|
|
||||||
|
|
||||||
func pemBlockForKey(priv interface{}) *pem.Block {
|
|
||||||
switch k := priv.(type) {
|
|
||||||
case *rsa.PrivateKey:
|
|
||||||
return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
|
|
||||||
case *dsa.PrivateKey:
|
|
||||||
val := DSAKeyFormat{
|
|
||||||
P: k.P, Q: k.Q, G: k.G,
|
|
||||||
Y: k.Y, X: k.X,
|
|
||||||
}
|
|
||||||
bytes, _ := asn1.Marshal(val)
|
|
||||||
return &pem.Block{Type: "DSA PRIVATE KEY", Bytes: bytes}
|
|
||||||
case *ecdsa.PrivateKey:
|
|
||||||
b, _ := x509.MarshalECPrivateKey(k)
|
|
||||||
return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func trunc(c int, s string) string {
|
|
||||||
if len(s) <= c {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return s[0:c]
|
|
||||||
}
|
|
||||||
|
|
||||||
func cat(v ...interface{}) string {
|
|
||||||
r := strings.TrimSpace(strings.Repeat("%v ", len(v)))
|
|
||||||
return fmt.Sprintf(r, v...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func indent(spaces int, v string) string {
|
|
||||||
pad := strings.Repeat(" ", spaces)
|
|
||||||
return pad + strings.Replace(v, "\n", "\n"+pad, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func replace(old, new, src string) string {
|
|
||||||
return strings.Replace(src, old, new, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func plural(one, many string, count int) string {
|
|
||||||
if count == 1 {
|
|
||||||
return one
|
|
||||||
}
|
|
||||||
return many
|
|
||||||
}
|
|
||||||
|
|
||||||
func sha256sum(input string) string {
|
|
||||||
hash := sha256.Sum256([]byte(input))
|
|
||||||
return hex.EncodeToString(hash[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func until(count int) []int {
|
|
||||||
step := 1
|
|
||||||
if count < 0 {
|
|
||||||
step = -1
|
|
||||||
}
|
|
||||||
return untilStep(0, count, step)
|
|
||||||
}
|
|
||||||
|
|
||||||
func untilStep(start, stop, step int) []int {
|
|
||||||
v := []int{}
|
|
||||||
|
|
||||||
if stop < start {
|
|
||||||
if step >= 0 {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
for i := start; i > stop; i += step {
|
|
||||||
v = append(v, i)
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
if step <= 0 {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
for i := start; i < stop; i += step {
|
|
||||||
v = append(v, i)
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// uuidv4 provides a safe and secure UUID v4 implementation
|
|
||||||
func uuidv4() string {
|
|
||||||
return fmt.Sprintf("%s", uuid.NewV4())
|
|
||||||
}
|
}
|
||||||
|
663
vendor/github.com/Masterminds/sprig/functions_test.go
generated
vendored
663
vendor/github.com/Masterminds/sprig/functions_test.go
generated
vendored
@ -2,376 +2,14 @@ package sprig
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base32"
|
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"github.com/aokoli/goutils"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is woefully incomplete. Please help.
|
|
||||||
|
|
||||||
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 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHtmlDate(t *testing.T) {
|
|
||||||
t.Skip()
|
|
||||||
tpl := `{{ htmlDate 0}}`
|
|
||||||
if err := runt(tpl, "1970-01-01"); 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 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 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 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 TestSplit(t *testing.T) {
|
|
||||||
tpl := `{{$v := "foo$bar$baz" | split "$"}}{{$v._0}}`
|
|
||||||
if err := runt(tpl, "foo"); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEnv(t *testing.T) {
|
func TestEnv(t *testing.T) {
|
||||||
os.Setenv("FOO", "bar")
|
os.Setenv("FOO", "bar")
|
||||||
tpl := `{{env "FOO"}}`
|
tpl := `{{env "FOO"}}`
|
||||||
@ -388,217 +26,6 @@ func TestExpandEnv(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSha256Sum(t *testing.T) {
|
|
||||||
tpl := `{{"abc" | sha256sum}}`
|
|
||||||
if err := runt(tpl, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 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 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 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 TestBase(t *testing.T) {
|
func TestBase(t *testing.T) {
|
||||||
assert.NoError(t, runt(`{{ base "foo/bar" }}`, "bar"))
|
assert.NoError(t, runt(`{{ base "foo/bar" }}`, "bar"))
|
||||||
}
|
}
|
||||||
@ -620,92 +47,14 @@ func TestExt(t *testing.T) {
|
|||||||
assert.NoError(t, runt(`{{ ext "/foo/bar/baz.txt" }}`, ".txt"))
|
assert.NoError(t, runt(`{{ ext "/foo/bar/baz.txt" }}`, ".txt"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDelete(t *testing.T) {
|
// runt runs a template and checks that the output exactly matches the expected string.
|
||||||
fmap := TxtFuncMap()
|
|
||||||
delete(fmap, "split")
|
|
||||||
if _, ok := fmap["split"]; ok {
|
|
||||||
t.Error("Failed to delete split from map")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runt(tpl, expect string) error {
|
func runt(tpl, expect string) error {
|
||||||
return runtv(tpl, expect, map[string]string{})
|
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 {
|
func runtv(tpl, expect string, vars interface{}) error {
|
||||||
fmap := TxtFuncMap()
|
fmap := TxtFuncMap()
|
||||||
t := template.Must(template.New("test").Funcs(fmap).Parse(tpl))
|
t := template.Must(template.New("test").Funcs(fmap).Parse(tpl))
|
||||||
@ -719,6 +68,8 @@ func runtv(tpl, expect string, vars interface{}) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// runRaw runs a template with the given variables and returns the result.
|
||||||
func runRaw(tpl string, vars interface{}) (string, error) {
|
func runRaw(tpl string, vars interface{}) (string, error) {
|
||||||
fmap := TxtFuncMap()
|
fmap := TxtFuncMap()
|
||||||
t := template.Must(template.New("test").Funcs(fmap).Parse(tpl))
|
t := template.Must(template.New("test").Funcs(fmap).Parse(tpl))
|
||||||
|
25
vendor/github.com/Masterminds/sprig/glide.lock
generated
vendored
25
vendor/github.com/Masterminds/sprig/glide.lock
generated
vendored
@ -1,8 +1,27 @@
|
|||||||
hash: a8ed42a70698b4d199b5de7fa33e7c48251651e6ccf97d007f546cb72a5d0f8f
|
hash: c2d7cb87ff32a0aba767b90bff630c3e4c1ca9904fc72568414d20d79a41e70f
|
||||||
updated: 2016-09-30T12:23:39.512939213-06:00
|
updated: 2017-03-13T18:38:30.597881175-06:00
|
||||||
imports:
|
imports:
|
||||||
- name: github.com/aokoli/goutils
|
- name: github.com/aokoli/goutils
|
||||||
version: 9c37978a95bd5c709a15883b6242714ea6709e64
|
version: 9c37978a95bd5c709a15883b6242714ea6709e64
|
||||||
|
- name: github.com/Masterminds/semver
|
||||||
|
version: 59c29afe1a994eacb71c833025ca7acf874bb1da
|
||||||
- name: github.com/satori/go.uuid
|
- name: github.com/satori/go.uuid
|
||||||
version: 879c5887cd475cd7864858769793b2ceb0d44feb
|
version: 879c5887cd475cd7864858769793b2ceb0d44feb
|
||||||
testImports: []
|
- name: golang.org/x/crypto
|
||||||
|
version: 1f22c0103821b9390939b6776727195525381532
|
||||||
|
subpackages:
|
||||||
|
- pbkdf2
|
||||||
|
- scrypt
|
||||||
|
testImports:
|
||||||
|
- name: github.com/davecgh/go-spew
|
||||||
|
version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
|
||||||
|
subpackages:
|
||||||
|
- spew
|
||||||
|
- name: github.com/pmezard/go-difflib
|
||||||
|
version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
|
||||||
|
subpackages:
|
||||||
|
- difflib
|
||||||
|
- name: github.com/stretchr/testify
|
||||||
|
version: e3a8ff8ce36581f87a15341206f205b1da467059
|
||||||
|
subpackages:
|
||||||
|
- assert
|
||||||
|
5
vendor/github.com/Masterminds/sprig/glide.yaml
generated
vendored
5
vendor/github.com/Masterminds/sprig/glide.yaml
generated
vendored
@ -3,3 +3,8 @@ import:
|
|||||||
- package: github.com/aokoli/goutils
|
- package: github.com/aokoli/goutils
|
||||||
- package: github.com/satori/go.uuid
|
- package: github.com/satori/go.uuid
|
||||||
version: ^1.1.0
|
version: ^1.1.0
|
||||||
|
- package: golang.org/x/crypto
|
||||||
|
subpackages:
|
||||||
|
- scrypt
|
||||||
|
- package: github.com/Masterminds/semver
|
||||||
|
version: v1.2.2
|
||||||
|
109
vendor/github.com/Masterminds/sprig/list.go
generated
vendored
Normal file
109
vendor/github.com/Masterminds/sprig/list.go
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package sprig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
func list(v ...interface{}) []interface{} {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func push(list []interface{}, v interface{}) []interface{} {
|
||||||
|
return append(list, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepend(list []interface{}, v interface{}) []interface{} {
|
||||||
|
return append([]interface{}{v}, list...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func last(list []interface{}) interface{} {
|
||||||
|
l := len(list)
|
||||||
|
if l == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return list[l-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func first(list []interface{}) interface{} {
|
||||||
|
if len(list) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return list[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func rest(list []interface{}) []interface{} {
|
||||||
|
if len(list) == 0 {
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
return list[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func initial(list []interface{}) []interface{} {
|
||||||
|
l := len(list)
|
||||||
|
if l == 0 {
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
return list[:l-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func sortAlpha(list interface{}) []string {
|
||||||
|
k := reflect.Indirect(reflect.ValueOf(list)).Kind()
|
||||||
|
switch k {
|
||||||
|
case reflect.Slice, reflect.Array:
|
||||||
|
a := strslice(list)
|
||||||
|
s := sort.StringSlice(a)
|
||||||
|
s.Sort()
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return []string{strval(list)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func reverse(v []interface{}) []interface{} {
|
||||||
|
// We do not sort in place because the incomming array should not be altered.
|
||||||
|
l := len(v)
|
||||||
|
c := make([]interface{}, l)
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
c[l-i-1] = v[i]
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func compact(list []interface{}) []interface{} {
|
||||||
|
res := []interface{}{}
|
||||||
|
for _, item := range list {
|
||||||
|
if !empty(item) {
|
||||||
|
res = append(res, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func uniq(list []interface{}) []interface{} {
|
||||||
|
dest := []interface{}{}
|
||||||
|
for _, item := range list {
|
||||||
|
if !inList(dest, item) {
|
||||||
|
dest = append(dest, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dest
|
||||||
|
}
|
||||||
|
|
||||||
|
func inList(haystack []interface{}, needle interface{}) bool {
|
||||||
|
for _, h := range haystack {
|
||||||
|
if reflect.DeepEqual(needle, h) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func without(list []interface{}, omit ...interface{}) []interface{} {
|
||||||
|
res := []interface{}{}
|
||||||
|
for _, i := range list {
|
||||||
|
if !inList(omit, i) {
|
||||||
|
res = append(res, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
135
vendor/github.com/Masterminds/sprig/list_test.go
generated
vendored
Normal file
135
vendor/github.com/Masterminds/sprig/list_test.go
generated
vendored
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
129
vendor/github.com/Masterminds/sprig/numeric.go
generated
vendored
Normal file
129
vendor/github.com/Masterminds/sprig/numeric.go
generated
vendored
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
package sprig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// toFloat64 converts 64-bit floats
|
||||||
|
func toFloat64(v interface{}) float64 {
|
||||||
|
if str, ok := v.(string); ok {
|
||||||
|
iv, err := strconv.ParseFloat(str, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return iv
|
||||||
|
}
|
||||||
|
|
||||||
|
val := reflect.Indirect(reflect.ValueOf(v))
|
||||||
|
switch val.Kind() {
|
||||||
|
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||||
|
return float64(val.Int())
|
||||||
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
||||||
|
return float64(val.Uint())
|
||||||
|
case reflect.Uint, reflect.Uint64:
|
||||||
|
return float64(val.Uint())
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return val.Float()
|
||||||
|
case reflect.Bool:
|
||||||
|
if val.Bool() == true {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toInt(v interface{}) int {
|
||||||
|
//It's not optimal. Bud I don't want duplicate toInt64 code.
|
||||||
|
return int(toInt64(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// toInt64 converts integer types to 64-bit integers
|
||||||
|
func toInt64(v interface{}) int64 {
|
||||||
|
if str, ok := v.(string); ok {
|
||||||
|
iv, err := strconv.ParseInt(str, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return iv
|
||||||
|
}
|
||||||
|
|
||||||
|
val := reflect.Indirect(reflect.ValueOf(v))
|
||||||
|
switch val.Kind() {
|
||||||
|
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||||
|
return val.Int()
|
||||||
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
||||||
|
return int64(val.Uint())
|
||||||
|
case reflect.Uint, reflect.Uint64:
|
||||||
|
tv := val.Uint()
|
||||||
|
if tv <= math.MaxInt64 {
|
||||||
|
return int64(tv)
|
||||||
|
}
|
||||||
|
// TODO: What is the sensible thing to do here?
|
||||||
|
return math.MaxInt64
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return int64(val.Float())
|
||||||
|
case reflect.Bool:
|
||||||
|
if val.Bool() == true {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func max(a interface{}, i ...interface{}) int64 {
|
||||||
|
aa := toInt64(a)
|
||||||
|
for _, b := range i {
|
||||||
|
bb := toInt64(b)
|
||||||
|
if bb > aa {
|
||||||
|
aa = bb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return aa
|
||||||
|
}
|
||||||
|
|
||||||
|
func min(a interface{}, i ...interface{}) int64 {
|
||||||
|
aa := toInt64(a)
|
||||||
|
for _, b := range i {
|
||||||
|
bb := toInt64(b)
|
||||||
|
if bb < aa {
|
||||||
|
aa = bb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return aa
|
||||||
|
}
|
||||||
|
|
||||||
|
func until(count int) []int {
|
||||||
|
step := 1
|
||||||
|
if count < 0 {
|
||||||
|
step = -1
|
||||||
|
}
|
||||||
|
return untilStep(0, count, step)
|
||||||
|
}
|
||||||
|
|
||||||
|
func untilStep(start, stop, step int) []int {
|
||||||
|
v := []int{}
|
||||||
|
|
||||||
|
if stop < start {
|
||||||
|
if step >= 0 {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
for i := start; i > stop; i += step {
|
||||||
|
v = append(v, i)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
if step <= 0 {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
for i := start; i < stop; i += step {
|
||||||
|
v = append(v, i)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
180
vendor/github.com/Masterminds/sprig/numeric_test.go
generated
vendored
Normal file
180
vendor/github.com/Masterminds/sprig/numeric_test.go
generated
vendored
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
28
vendor/github.com/Masterminds/sprig/reflect.go
generated
vendored
Normal file
28
vendor/github.com/Masterminds/sprig/reflect.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package sprig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// typeIs returns true if the src is the type named in target.
|
||||||
|
func typeIs(target string, src interface{}) bool {
|
||||||
|
return target == typeOf(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func typeIsLike(target string, src interface{}) bool {
|
||||||
|
t := typeOf(src)
|
||||||
|
return target == t || "*"+target == t
|
||||||
|
}
|
||||||
|
|
||||||
|
func typeOf(src interface{}) string {
|
||||||
|
return fmt.Sprintf("%T", src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func kindIs(target string, src interface{}) bool {
|
||||||
|
return target == kindOf(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func kindOf(src interface{}) string {
|
||||||
|
return reflect.ValueOf(src).Kind().String()
|
||||||
|
}
|
73
vendor/github.com/Masterminds/sprig/reflect_test.go
generated
vendored
Normal file
73
vendor/github.com/Masterminds/sprig/reflect_test.go
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
23
vendor/github.com/Masterminds/sprig/semver.go
generated
vendored
Normal file
23
vendor/github.com/Masterminds/sprig/semver.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package sprig
|
||||||
|
|
||||||
|
import (
|
||||||
|
sv2 "github.com/Masterminds/semver"
|
||||||
|
)
|
||||||
|
|
||||||
|
func semverCompare(constraint, version string) (bool, error) {
|
||||||
|
c, err := sv2.NewConstraint(constraint)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := sv2.NewVersion(version)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Check(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func semver(version string) (*sv2.Version, error) {
|
||||||
|
return sv2.NewVersion(version)
|
||||||
|
}
|
31
vendor/github.com/Masterminds/sprig/semver_test.go
generated
vendored
Normal file
31
vendor/github.com/Masterminds/sprig/semver_test.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
197
vendor/github.com/Masterminds/sprig/strings.go
generated
vendored
Normal file
197
vendor/github.com/Masterminds/sprig/strings.go
generated
vendored
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
package sprig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base32"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
util "github.com/aokoli/goutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func base64encode(v string) string {
|
||||||
|
return base64.StdEncoding.EncodeToString([]byte(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func base64decode(v string) string {
|
||||||
|
data, err := base64.StdEncoding.DecodeString(v)
|
||||||
|
if err != nil {
|
||||||
|
return err.Error()
|
||||||
|
}
|
||||||
|
return string(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func base32encode(v string) string {
|
||||||
|
return base32.StdEncoding.EncodeToString([]byte(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func base32decode(v string) string {
|
||||||
|
data, err := base32.StdEncoding.DecodeString(v)
|
||||||
|
if err != nil {
|
||||||
|
return err.Error()
|
||||||
|
}
|
||||||
|
return string(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func abbrev(width int, s string) string {
|
||||||
|
if width < 4 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
r, _ := util.Abbreviate(s, width)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func abbrevboth(left, right int, s string) string {
|
||||||
|
if right < 4 || left > 0 && right < 7 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
r, _ := util.AbbreviateFull(s, left, right)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
func initials(s string) string {
|
||||||
|
// Wrap this just to eliminate the var args, which templates don't do well.
|
||||||
|
return util.Initials(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func randAlphaNumeric(count int) string {
|
||||||
|
// It is not possible, it appears, to actually generate an error here.
|
||||||
|
r, _ := util.RandomAlphaNumeric(count)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func randAlpha(count int) string {
|
||||||
|
r, _ := util.RandomAlphabetic(count)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func randAscii(count int) string {
|
||||||
|
r, _ := util.RandomAscii(count)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func randNumeric(count int) string {
|
||||||
|
r, _ := util.RandomNumeric(count)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func untitle(str string) string {
|
||||||
|
return util.Uncapitalize(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func quote(str ...interface{}) string {
|
||||||
|
out := make([]string, len(str))
|
||||||
|
for i, s := range str {
|
||||||
|
out[i] = fmt.Sprintf("%q", strval(s))
|
||||||
|
}
|
||||||
|
return strings.Join(out, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func squote(str ...interface{}) string {
|
||||||
|
out := make([]string, len(str))
|
||||||
|
for i, s := range str {
|
||||||
|
out[i] = fmt.Sprintf("'%v'", s)
|
||||||
|
}
|
||||||
|
return strings.Join(out, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func cat(v ...interface{}) string {
|
||||||
|
r := strings.TrimSpace(strings.Repeat("%v ", len(v)))
|
||||||
|
return fmt.Sprintf(r, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func indent(spaces int, v string) string {
|
||||||
|
pad := strings.Repeat(" ", spaces)
|
||||||
|
return pad + strings.Replace(v, "\n", "\n"+pad, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func replace(old, new, src string) string {
|
||||||
|
return strings.Replace(src, old, new, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func plural(one, many string, count int) string {
|
||||||
|
if count == 1 {
|
||||||
|
return one
|
||||||
|
}
|
||||||
|
return many
|
||||||
|
}
|
||||||
|
|
||||||
|
func strslice(v interface{}) []string {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case []string:
|
||||||
|
return v
|
||||||
|
case []interface{}:
|
||||||
|
l := len(v)
|
||||||
|
b := make([]string, l)
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
b[i] = strval(v[i])
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
default:
|
||||||
|
val := reflect.ValueOf(v)
|
||||||
|
switch val.Kind() {
|
||||||
|
case reflect.Array, reflect.Slice:
|
||||||
|
l := val.Len()
|
||||||
|
b := make([]string, l)
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
b[i] = strval(val.Index(i).Interface())
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
default:
|
||||||
|
return []string{strval(v)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func strval(v interface{}) string {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case string:
|
||||||
|
return v
|
||||||
|
case []byte:
|
||||||
|
return string(v)
|
||||||
|
case error:
|
||||||
|
return v.Error()
|
||||||
|
case fmt.Stringer:
|
||||||
|
return v.String()
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("%v", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func trunc(c int, s string) string {
|
||||||
|
if len(s) <= c {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return s[0:c]
|
||||||
|
}
|
||||||
|
|
||||||
|
func join(sep string, v interface{}) string {
|
||||||
|
return strings.Join(strslice(v), sep)
|
||||||
|
}
|
||||||
|
|
||||||
|
func split(sep, orig string) map[string]string {
|
||||||
|
parts := strings.Split(orig, sep)
|
||||||
|
res := make(map[string]string, len(parts))
|
||||||
|
for i, v := range parts {
|
||||||
|
res["_"+strconv.Itoa(i)] = v
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// substring creates a substring of the given string.
|
||||||
|
//
|
||||||
|
// If start is < 0, this calls string[:length].
|
||||||
|
//
|
||||||
|
// If start is >= 0 and length < 0, this calls string[start:]
|
||||||
|
//
|
||||||
|
// Otherwise, this calls string[start, length].
|
||||||
|
func substring(start, length int, s string) string {
|
||||||
|
if start < 0 {
|
||||||
|
return s[:length]
|
||||||
|
}
|
||||||
|
if length < 0 {
|
||||||
|
return s[start:]
|
||||||
|
}
|
||||||
|
return s[start:length]
|
||||||
|
}
|
220
vendor/github.com/Masterminds/sprig/strings_test.go
generated
vendored
Normal file
220
vendor/github.com/Masterminds/sprig/strings_test.go
generated
vendored
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
4
vendor/github.com/dgrijalva/jwt-go/.gitignore
generated
vendored
Normal file
4
vendor/github.com/dgrijalva/jwt-go/.gitignore
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
.DS_Store
|
||||||
|
bin
|
||||||
|
|
||||||
|
|
8
vendor/github.com/dgrijalva/jwt-go/.travis.yml
generated
vendored
Normal file
8
vendor/github.com/dgrijalva/jwt-go/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.3
|
||||||
|
- 1.4
|
||||||
|
- 1.5
|
||||||
|
- 1.6
|
||||||
|
- tip
|
8
vendor/github.com/dgrijalva/jwt-go/LICENSE
generated
vendored
Normal file
8
vendor/github.com/dgrijalva/jwt-go/LICENSE
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
Copyright (c) 2012 Dave Grijalva
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
96
vendor/github.com/dgrijalva/jwt-go/MIGRATION_GUIDE.md
generated
vendored
Normal file
96
vendor/github.com/dgrijalva/jwt-go/MIGRATION_GUIDE.md
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
## Migration Guide from v2 -> v3
|
||||||
|
|
||||||
|
Version 3 adds several new, frequently requested features. To do so, it introduces a few breaking changes. We've worked to keep these as minimal as possible. This guide explains the breaking changes and how you can quickly update your code.
|
||||||
|
|
||||||
|
### `Token.Claims` is now an interface type
|
||||||
|
|
||||||
|
The most requested feature from the 2.0 verison of this library was the ability to provide a custom type to the JSON parser for claims. This was implemented by introducing a new interface, `Claims`, to replace `map[string]interface{}`. We also included two concrete implementations of `Claims`: `MapClaims` and `StandardClaims`.
|
||||||
|
|
||||||
|
`MapClaims` is an alias for `map[string]interface{}` with built in validation behavior. It is the default claims type when using `Parse`. The usage is unchanged except you must type cast the claims property.
|
||||||
|
|
||||||
|
The old example for parsing a token looked like this..
|
||||||
|
|
||||||
|
```go
|
||||||
|
if token, err := jwt.Parse(tokenString, keyLookupFunc); err == nil {
|
||||||
|
fmt.Printf("Token for user %v expires %v", token.Claims["user"], token.Claims["exp"])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
is now directly mapped to...
|
||||||
|
|
||||||
|
```go
|
||||||
|
if token, err := jwt.Parse(tokenString, keyLookupFunc); err == nil {
|
||||||
|
claims := token.Claims.(jwt.MapClaims)
|
||||||
|
fmt.Printf("Token for user %v expires %v", claims["user"], claims["exp"])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`StandardClaims` is designed to be embedded in your custom type. You can supply a custom claims type with the new `ParseWithClaims` function. Here's an example of using a custom claims type.
|
||||||
|
|
||||||
|
```go
|
||||||
|
type MyCustomClaims struct {
|
||||||
|
User string
|
||||||
|
*StandardClaims
|
||||||
|
}
|
||||||
|
|
||||||
|
if token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, keyLookupFunc); err == nil {
|
||||||
|
claims := token.Claims.(*MyCustomClaims)
|
||||||
|
fmt.Printf("Token for user %v expires %v", claims.User, claims.StandardClaims.ExpiresAt)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `ParseFromRequest` has been moved
|
||||||
|
|
||||||
|
To keep this library focused on the tokens without becoming overburdened with complex request processing logic, `ParseFromRequest` and its new companion `ParseFromRequestWithClaims` have been moved to a subpackage, `request`. The method signatues have also been augmented to receive a new argument: `Extractor`.
|
||||||
|
|
||||||
|
`Extractors` do the work of picking the token string out of a request. The interface is simple and composable.
|
||||||
|
|
||||||
|
This simple parsing example:
|
||||||
|
|
||||||
|
```go
|
||||||
|
if token, err := jwt.ParseFromRequest(tokenString, req, keyLookupFunc); err == nil {
|
||||||
|
fmt.Printf("Token for user %v expires %v", token.Claims["user"], token.Claims["exp"])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
is directly mapped to:
|
||||||
|
|
||||||
|
```go
|
||||||
|
if token, err := request.ParseFromRequest(tokenString, request.OAuth2Extractor, req, keyLookupFunc); err == nil {
|
||||||
|
fmt.Printf("Token for user %v expires %v", token.Claims["user"], token.Claims["exp"])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
There are several concrete `Extractor` types provided for your convenience:
|
||||||
|
|
||||||
|
* `HeaderExtractor` will search a list of headers until one contains content.
|
||||||
|
* `ArgumentExtractor` will search a list of keys in request query and form arguments until one contains content.
|
||||||
|
* `MultiExtractor` will try a list of `Extractors` in order until one returns content.
|
||||||
|
* `AuthorizationHeaderExtractor` will look in the `Authorization` header for a `Bearer` token.
|
||||||
|
* `OAuth2Extractor` searches the places an OAuth2 token would be specified (per the spec): `Authorization` header and `access_token` argument
|
||||||
|
* `PostExtractionFilter` wraps an `Extractor`, allowing you to process the content before it's parsed. A simple example is stripping the `Bearer ` text from a header
|
||||||
|
|
||||||
|
|
||||||
|
### RSA signing methods no longer accept `[]byte` keys
|
||||||
|
|
||||||
|
Due to a [critical vulnerability](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/), we've decided the convenience of accepting `[]byte` instead of `rsa.PublicKey` or `rsa.PrivateKey` isn't worth the risk of misuse.
|
||||||
|
|
||||||
|
To replace this behavior, we've added two helper methods: `ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error)` and `ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error)`. These are just simple helpers for unpacking PEM encoded PKCS1 and PKCS8 keys. If your keys are encoded any other way, all you need to do is convert them to the `crypto/rsa` package's types.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func keyLookupFunc(*Token) (interface{}, error) {
|
||||||
|
// Don't forget to validate the alg is what you expect:
|
||||||
|
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
|
||||||
|
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look up key
|
||||||
|
key, err := lookupPublicKey(token.Header["kid"])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unpack key from PEM encoded PKCS8
|
||||||
|
return jwt.ParseRSAPublicKeyFromPEM(key)
|
||||||
|
}
|
||||||
|
```
|
85
vendor/github.com/dgrijalva/jwt-go/README.md
generated
vendored
Normal file
85
vendor/github.com/dgrijalva/jwt-go/README.md
generated
vendored
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
A [go](http://www.golang.org) (or 'golang' for search engine friendliness) implementation of [JSON Web Tokens](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html)
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/dgrijalva/jwt-go.svg?branch=master)](https://travis-ci.org/dgrijalva/jwt-go)
|
||||||
|
|
||||||
|
**BREAKING CHANGES:*** Version 3.0.0 is here. It includes _a lot_ of changes including a few that break the API. We've tried to break as few things as possible, so there should just be a few type signature changes. A full list of breaking changes is available in `VERSION_HISTORY.md`. See `MIGRATION_GUIDE.md` for more information on updating your code.
|
||||||
|
|
||||||
|
**NOTICE:** A vulnerability in JWT was [recently published](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/). As this library doesn't force users to validate the `alg` is what they expected, it's possible your usage is effected. There will be an update soon to remedy this, and it will likey require backwards-incompatible changes to the API. In the short term, please make sure your implementation verifies the `alg` is what you expect.
|
||||||
|
|
||||||
|
|
||||||
|
## What the heck is a JWT?
|
||||||
|
|
||||||
|
JWT.io has [a great introduction](https://jwt.io/introduction) to JSON Web Tokens.
|
||||||
|
|
||||||
|
In short, it's a signed JSON object that does something useful (for example, authentication). It's commonly used for `Bearer` tokens in Oauth 2. A token is made of three parts, separated by `.`'s. The first two parts are JSON objects, that have been [base64url](http://tools.ietf.org/html/rfc4648) encoded. The last part is the signature, encoded the same way.
|
||||||
|
|
||||||
|
The first part is called the header. It contains the necessary information for verifying the last part, the signature. For example, which encryption method was used for signing and what key was used.
|
||||||
|
|
||||||
|
The part in the middle is the interesting bit. It's called the Claims and contains the actual stuff you care about. Refer to [the RFC](http://self-issued.info/docs/draft-jones-json-web-token.html) for information about reserved keys and the proper way to add your own.
|
||||||
|
|
||||||
|
## What's in the box?
|
||||||
|
|
||||||
|
This library supports the parsing and verification as well as the generation and signing of JWTs. Current supported signing algorithms are HMAC SHA, RSA, RSA-PSS, and ECDSA, though hooks are present for adding your own.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
See [the project documentation](https://godoc.org/github.com/dgrijalva/jwt-go) for examples of usage:
|
||||||
|
|
||||||
|
* [Simple example of parsing and validating a token](https://godoc.org/github.com/dgrijalva/jwt-go#example_Parse_hmac)
|
||||||
|
* [Simple example of building and signing a token](https://godoc.org/github.com/dgrijalva/jwt-go#example_New_hmac)
|
||||||
|
* [Directory of Examples](https://godoc.org/github.com/dgrijalva/jwt-go#pkg-examples)
|
||||||
|
|
||||||
|
## Extensions
|
||||||
|
|
||||||
|
This library publishes all the necessary components for adding your own signing methods. Simply implement the `SigningMethod` interface and register a factory method using `RegisterSigningMethod`.
|
||||||
|
|
||||||
|
Here's an example of an extension that integrates with the Google App Engine signing tools: https://github.com/someone1/gcp-jwt-go
|
||||||
|
|
||||||
|
## Compliance
|
||||||
|
|
||||||
|
This library was last reviewed to comply with [RTF 7519](http://www.rfc-editor.org/info/rfc7519) dated May 2015 with a few notable differences:
|
||||||
|
|
||||||
|
* In order to protect against accidental use of [Unsecured JWTs](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#UnsecuredJWT), tokens using `alg=none` will only be accepted if the constant `jwt.UnsafeAllowNoneSignatureType` is provided as the key.
|
||||||
|
|
||||||
|
## Project Status & Versioning
|
||||||
|
|
||||||
|
This library is considered production ready. Feedback and feature requests are appreciated. The API should be considered stable. There should be very few backwards-incompatible changes outside of major version updates (and only with good reason).
|
||||||
|
|
||||||
|
This project uses [Semantic Versioning 2.0.0](http://semver.org). Accepted pull requests will land on `master`. Periodically, versions will be tagged from `master`. You can find all the releases on [the project releases page](https://github.com/dgrijalva/jwt-go/releases).
|
||||||
|
|
||||||
|
While we try to make it obvious when we make breaking changes, there isn't a great mechanism for pushing announcements out to users. You may want to use this alternative package include: `gopkg.in/dgrijalva/jwt-go.v2`. It will do the right thing WRT semantic versioning.
|
||||||
|
|
||||||
|
## Usage Tips
|
||||||
|
|
||||||
|
### Signing vs Encryption
|
||||||
|
|
||||||
|
A token is simply a JSON object that is signed by its author. this tells you exactly two things about the data:
|
||||||
|
|
||||||
|
* The author of the token was in the possession of the signing secret
|
||||||
|
* The data has not been modified since it was signed
|
||||||
|
|
||||||
|
It's important to know that JWT does not provide encryption, which means anyone who has access to the token can read its contents. If you need to protect (encrypt) the data, there is a companion spec, `JWE`, that provides this functionality. JWE is currently outside the scope of this library.
|
||||||
|
|
||||||
|
### Choosing a Signing Method
|
||||||
|
|
||||||
|
There are several signing methods available, and you should probably take the time to learn about the various options before choosing one. The principal design decision is most likely going to be symmetric vs asymmetric.
|
||||||
|
|
||||||
|
Symmetric signing methods, such as HSA, use only a single secret. This is probably the simplest signing method to use since any `[]byte` can be used as a valid secret. They are also slightly computationally faster to use, though this rarely is enough to matter. Symmetric signing methods work the best when both producers and consumers of tokens are trusted, or even the same system. Since the same secret is used to both sign and validate tokens, you can't easily distribute the key for validation.
|
||||||
|
|
||||||
|
Asymmetric signing methods, such as RSA, use different keys for signing and verifying tokens. This makes it possible to produce tokens with a private key, and allow any consumer to access the public key for verification.
|
||||||
|
|
||||||
|
### JWT and OAuth
|
||||||
|
|
||||||
|
It's worth mentioning that OAuth and JWT are not the same thing. A JWT token is simply a signed JSON object. It can be used anywhere such a thing is useful. There is some confusion, though, as JWT is the most common type of bearer token used in OAuth2 authentication.
|
||||||
|
|
||||||
|
Without going too far down the rabbit hole, here's a description of the interaction of these technologies:
|
||||||
|
|
||||||
|
* OAuth is a protocol for allowing an identity provider to be separate from the service a user is logging in to. For example, whenever you use Facebook to log into a different service (Yelp, Spotify, etc), you are using OAuth.
|
||||||
|
* OAuth defines several options for passing around authentication data. One popular method is called a "bearer token". A bearer token is simply a string that _should_ only be held by an authenticated user. Thus, simply presenting this token proves your identity. You can probably derive from here why a JWT might make a good bearer token.
|
||||||
|
* Because bearer tokens are used for authentication, it's important they're kept secret. This is why transactions that use bearer tokens typically happen over SSL.
|
||||||
|
|
||||||
|
## More
|
||||||
|
|
||||||
|
Documentation can be found [on godoc.org](http://godoc.org/github.com/dgrijalva/jwt-go).
|
||||||
|
|
||||||
|
The command line utility included in this project (cmd/jwt) provides a straightforward example of token creation and parsing as well as a useful tool for debugging your own integration. You'll also find several implementation examples in to documentation.
|
105
vendor/github.com/dgrijalva/jwt-go/VERSION_HISTORY.md
generated
vendored
Normal file
105
vendor/github.com/dgrijalva/jwt-go/VERSION_HISTORY.md
generated
vendored
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
## `jwt-go` Version History
|
||||||
|
|
||||||
|
#### 3.0.0
|
||||||
|
|
||||||
|
* **Compatibility Breaking Changes**: See MIGRATION_GUIDE.md for tips on updating your code
|
||||||
|
* Dropped support for `[]byte` keys when using RSA signing methods. This convenience feature could contribute to security vulnerabilities involving mismatched key types with signing methods.
|
||||||
|
* `ParseFromRequest` has been moved to `request` subpackage and usage has changed
|
||||||
|
* The `Claims` property on `Token` is now type `Claims` instead of `map[string]interface{}`. The default value is type `MapClaims`, which is an alias to `map[string]interface{}`. This makes it possible to use a custom type when decoding claims.
|
||||||
|
* Other Additions and Changes
|
||||||
|
* Added `Claims` interface type to allow users to decode the claims into a custom type
|
||||||
|
* Added `ParseWithClaims`, which takes a third argument of type `Claims`. Use this function instead of `Parse` if you have a custom type you'd like to decode into.
|
||||||
|
* Dramatically improved the functionality and flexibility of `ParseFromRequest`, which is now in the `request` subpackage
|
||||||
|
* Added `ParseFromRequestWithClaims` which is the `FromRequest` equivalent of `ParseWithClaims`
|
||||||
|
* Added new interface type `Extractor`, which is used for extracting JWT strings from http requests. Used with `ParseFromRequest` and `ParseFromRequestWithClaims`.
|
||||||
|
* Added several new, more specific, validation errors to error type bitmask
|
||||||
|
* Moved examples from README to executable example files
|
||||||
|
* Signing method registry is now thread safe
|
||||||
|
* Added new property to `ValidationError`, which contains the raw error returned by calls made by parse/verify (such as those returned by keyfunc or json parser)
|
||||||
|
|
||||||
|
#### 2.7.0
|
||||||
|
|
||||||
|
This will likely be the last backwards compatible release before 3.0.0, excluding essential bug fixes.
|
||||||
|
|
||||||
|
* Added new option `-show` to the `jwt` command that will just output the decoded token without verifying
|
||||||
|
* Error text for expired tokens includes how long it's been expired
|
||||||
|
* Fixed incorrect error returned from `ParseRSAPublicKeyFromPEM`
|
||||||
|
* Documentation updates
|
||||||
|
|
||||||
|
#### 2.6.0
|
||||||
|
|
||||||
|
* Exposed inner error within ValidationError
|
||||||
|
* Fixed validation errors when using UseJSONNumber flag
|
||||||
|
* Added several unit tests
|
||||||
|
|
||||||
|
#### 2.5.0
|
||||||
|
|
||||||
|
* Added support for signing method none. You shouldn't use this. The API tries to make this clear.
|
||||||
|
* Updated/fixed some documentation
|
||||||
|
* Added more helpful error message when trying to parse tokens that begin with `BEARER `
|
||||||
|
|
||||||
|
#### 2.4.0
|
||||||
|
|
||||||
|
* Added new type, Parser, to allow for configuration of various parsing parameters
|
||||||
|
* You can now specify a list of valid signing methods. Anything outside this set will be rejected.
|
||||||
|
* You can now opt to use the `json.Number` type instead of `float64` when parsing token JSON
|
||||||
|
* Added support for [Travis CI](https://travis-ci.org/dgrijalva/jwt-go)
|
||||||
|
* Fixed some bugs with ECDSA parsing
|
||||||
|
|
||||||
|
#### 2.3.0
|
||||||
|
|
||||||
|
* Added support for ECDSA signing methods
|
||||||
|
* Added support for RSA PSS signing methods (requires go v1.4)
|
||||||
|
|
||||||
|
#### 2.2.0
|
||||||
|
|
||||||
|
* Gracefully handle a `nil` `Keyfunc` being passed to `Parse`. Result will now be the parsed token and an error, instead of a panic.
|
||||||
|
|
||||||
|
#### 2.1.0
|
||||||
|
|
||||||
|
Backwards compatible API change that was missed in 2.0.0.
|
||||||
|
|
||||||
|
* The `SignedString` method on `Token` now takes `interface{}` instead of `[]byte`
|
||||||
|
|
||||||
|
#### 2.0.0
|
||||||
|
|
||||||
|
There were two major reasons for breaking backwards compatibility with this update. The first was a refactor required to expand the width of the RSA and HMAC-SHA signing implementations. There will likely be no required code changes to support this change.
|
||||||
|
|
||||||
|
The second update, while unfortunately requiring a small change in integration, is required to open up this library to other signing methods. Not all keys used for all signing methods have a single standard on-disk representation. Requiring `[]byte` as the type for all keys proved too limiting. Additionally, this implementation allows for pre-parsed tokens to be reused, which might matter in an application that parses a high volume of tokens with a small set of keys. Backwards compatibilty has been maintained for passing `[]byte` to the RSA signing methods, but they will also accept `*rsa.PublicKey` and `*rsa.PrivateKey`.
|
||||||
|
|
||||||
|
It is likely the only integration change required here will be to change `func(t *jwt.Token) ([]byte, error)` to `func(t *jwt.Token) (interface{}, error)` when calling `Parse`.
|
||||||
|
|
||||||
|
* **Compatibility Breaking Changes**
|
||||||
|
* `SigningMethodHS256` is now `*SigningMethodHMAC` instead of `type struct`
|
||||||
|
* `SigningMethodRS256` is now `*SigningMethodRSA` instead of `type struct`
|
||||||
|
* `KeyFunc` now returns `interface{}` instead of `[]byte`
|
||||||
|
* `SigningMethod.Sign` now takes `interface{}` instead of `[]byte` for the key
|
||||||
|
* `SigningMethod.Verify` now takes `interface{}` instead of `[]byte` for the key
|
||||||
|
* Renamed type `SigningMethodHS256` to `SigningMethodHMAC`. Specific sizes are now just instances of this type.
|
||||||
|
* Added public package global `SigningMethodHS256`
|
||||||
|
* Added public package global `SigningMethodHS384`
|
||||||
|
* Added public package global `SigningMethodHS512`
|
||||||
|
* Renamed type `SigningMethodRS256` to `SigningMethodRSA`. Specific sizes are now just instances of this type.
|
||||||
|
* Added public package global `SigningMethodRS256`
|
||||||
|
* Added public package global `SigningMethodRS384`
|
||||||
|
* Added public package global `SigningMethodRS512`
|
||||||
|
* Moved sample private key for HMAC tests from an inline value to a file on disk. Value is unchanged.
|
||||||
|
* Refactored the RSA implementation to be easier to read
|
||||||
|
* Exposed helper methods `ParseRSAPrivateKeyFromPEM` and `ParseRSAPublicKeyFromPEM`
|
||||||
|
|
||||||
|
#### 1.0.2
|
||||||
|
|
||||||
|
* Fixed bug in parsing public keys from certificates
|
||||||
|
* Added more tests around the parsing of keys for RS256
|
||||||
|
* Code refactoring in RS256 implementation. No functional changes
|
||||||
|
|
||||||
|
#### 1.0.1
|
||||||
|
|
||||||
|
* Fixed panic if RS256 signing method was passed an invalid key
|
||||||
|
|
||||||
|
#### 1.0.0
|
||||||
|
|
||||||
|
* First versioned release
|
||||||
|
* API stabilized
|
||||||
|
* Supports creating, signing, parsing, and validating JWT tokens
|
||||||
|
* Supports RS256 and HS256 signing methods
|
134
vendor/github.com/dgrijalva/jwt-go/claims.go
generated
vendored
Normal file
134
vendor/github.com/dgrijalva/jwt-go/claims.go
generated
vendored
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
package jwt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/subtle"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// For a type to be a Claims object, it must just have a Valid method that determines
|
||||||
|
// if the token is invalid for any supported reason
|
||||||
|
type Claims interface {
|
||||||
|
Valid() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Structured version of Claims Section, as referenced at
|
||||||
|
// https://tools.ietf.org/html/rfc7519#section-4.1
|
||||||
|
// See examples for how to use this with your own claim types
|
||||||
|
type StandardClaims struct {
|
||||||
|
Audience string `json:"aud,omitempty"`
|
||||||
|
ExpiresAt int64 `json:"exp,omitempty"`
|
||||||
|
Id string `json:"jti,omitempty"`
|
||||||
|
IssuedAt int64 `json:"iat,omitempty"`
|
||||||
|
Issuer string `json:"iss,omitempty"`
|
||||||
|
NotBefore int64 `json:"nbf,omitempty"`
|
||||||
|
Subject string `json:"sub,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validates time based claims "exp, iat, nbf".
|
||||||
|
// There is no accounting for clock skew.
|
||||||
|
// As well, if any of the above claims are not in the token, it will still
|
||||||
|
// be considered a valid claim.
|
||||||
|
func (c StandardClaims) Valid() error {
|
||||||
|
vErr := new(ValidationError)
|
||||||
|
now := TimeFunc().Unix()
|
||||||
|
|
||||||
|
// The claims below are optional, by default, so if they are set to the
|
||||||
|
// default value in Go, let's not fail the verification for them.
|
||||||
|
if c.VerifyExpiresAt(now, false) == false {
|
||||||
|
delta := time.Unix(now, 0).Sub(time.Unix(c.ExpiresAt, 0))
|
||||||
|
vErr.Inner = fmt.Errorf("token is expired by %v", delta)
|
||||||
|
vErr.Errors |= ValidationErrorExpired
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.VerifyIssuedAt(now, false) == false {
|
||||||
|
vErr.Inner = fmt.Errorf("Token used before issued")
|
||||||
|
vErr.Errors |= ValidationErrorIssuedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.VerifyNotBefore(now, false) == false {
|
||||||
|
vErr.Inner = fmt.Errorf("token is not valid yet")
|
||||||
|
vErr.Errors |= ValidationErrorNotValidYet
|
||||||
|
}
|
||||||
|
|
||||||
|
if vErr.valid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return vErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compares the aud claim against cmp.
|
||||||
|
// If required is false, this method will return true if the value matches or is unset
|
||||||
|
func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool {
|
||||||
|
return verifyAud(c.Audience, cmp, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compares the exp claim against cmp.
|
||||||
|
// If required is false, this method will return true if the value matches or is unset
|
||||||
|
func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool) bool {
|
||||||
|
return verifyExp(c.ExpiresAt, cmp, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compares the iat claim against cmp.
|
||||||
|
// If required is false, this method will return true if the value matches or is unset
|
||||||
|
func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool {
|
||||||
|
return verifyIat(c.IssuedAt, cmp, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compares the iss claim against cmp.
|
||||||
|
// If required is false, this method will return true if the value matches or is unset
|
||||||
|
func (c *StandardClaims) VerifyIssuer(cmp string, req bool) bool {
|
||||||
|
return verifyIss(c.Issuer, cmp, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compares the nbf claim against cmp.
|
||||||
|
// If required is false, this method will return true if the value matches or is unset
|
||||||
|
func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool {
|
||||||
|
return verifyNbf(c.NotBefore, cmp, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- helpers
|
||||||
|
|
||||||
|
func verifyAud(aud string, cmp string, required bool) bool {
|
||||||
|
if aud == "" {
|
||||||
|
return !required
|
||||||
|
}
|
||||||
|
if subtle.ConstantTimeCompare([]byte(aud), []byte(cmp)) != 0 {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyExp(exp int64, now int64, required bool) bool {
|
||||||
|
if exp == 0 {
|
||||||
|
return !required
|
||||||
|
}
|
||||||
|
return now <= exp
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyIat(iat int64, now int64, required bool) bool {
|
||||||
|
if iat == 0 {
|
||||||
|
return !required
|
||||||
|
}
|
||||||
|
return now >= iat
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyIss(iss string, cmp string, required bool) bool {
|
||||||
|
if iss == "" {
|
||||||
|
return !required
|
||||||
|
}
|
||||||
|
if subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0 {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyNbf(nbf int64, now int64, required bool) bool {
|
||||||
|
if nbf == 0 {
|
||||||
|
return !required
|
||||||
|
}
|
||||||
|
return now >= nbf
|
||||||
|
}
|
13
vendor/github.com/dgrijalva/jwt-go/cmd/jwt/README.md
generated
vendored
Normal file
13
vendor/github.com/dgrijalva/jwt-go/cmd/jwt/README.md
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
`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 -
|
245
vendor/github.com/dgrijalva/jwt-go/cmd/jwt/app.go
generated
vendored
Normal file
245
vendor/github.com/dgrijalva/jwt-go/cmd/jwt/app.go
generated
vendored
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
// 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")
|
||||||
|
}
|
4
vendor/github.com/dgrijalva/jwt-go/doc.go
generated
vendored
Normal file
4
vendor/github.com/dgrijalva/jwt-go/doc.go
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
// Package jwt is a Go implementation of JSON Web Tokens: http://self-issued.info/docs/draft-jones-json-web-token.html
|
||||||
|
//
|
||||||
|
// See README.md for more info.
|
||||||
|
package jwt
|
147
vendor/github.com/dgrijalva/jwt-go/ecdsa.go
generated
vendored
Normal file
147
vendor/github.com/dgrijalva/jwt-go/ecdsa.go
generated
vendored
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
package jwt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rand"
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Sadly this is missing from crypto/ecdsa compared to crypto/rsa
|
||||||
|
ErrECDSAVerification = errors.New("crypto/ecdsa: verification error")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Implements the ECDSA family of signing methods signing methods
|
||||||
|
type SigningMethodECDSA struct {
|
||||||
|
Name string
|
||||||
|
Hash crypto.Hash
|
||||||
|
KeySize int
|
||||||
|
CurveBits int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specific instances for EC256 and company
|
||||||
|
var (
|
||||||
|
SigningMethodES256 *SigningMethodECDSA
|
||||||
|
SigningMethodES384 *SigningMethodECDSA
|
||||||
|
SigningMethodES512 *SigningMethodECDSA
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// ES256
|
||||||
|
SigningMethodES256 = &SigningMethodECDSA{"ES256", crypto.SHA256, 32, 256}
|
||||||
|
RegisterSigningMethod(SigningMethodES256.Alg(), func() SigningMethod {
|
||||||
|
return SigningMethodES256
|
||||||
|
})
|
||||||
|
|
||||||
|
// ES384
|
||||||
|
SigningMethodES384 = &SigningMethodECDSA{"ES384", crypto.SHA384, 48, 384}
|
||||||
|
RegisterSigningMethod(SigningMethodES384.Alg(), func() SigningMethod {
|
||||||
|
return SigningMethodES384
|
||||||
|
})
|
||||||
|
|
||||||
|
// ES512
|
||||||
|
SigningMethodES512 = &SigningMethodECDSA{"ES512", crypto.SHA512, 66, 521}
|
||||||
|
RegisterSigningMethod(SigningMethodES512.Alg(), func() SigningMethod {
|
||||||
|
return SigningMethodES512
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SigningMethodECDSA) Alg() string {
|
||||||
|
return m.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements the Verify method from SigningMethod
|
||||||
|
// For this verify method, key must be an ecdsa.PublicKey struct
|
||||||
|
func (m *SigningMethodECDSA) Verify(signingString, signature string, key interface{}) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Decode the signature
|
||||||
|
var sig []byte
|
||||||
|
if sig, err = DecodeSegment(signature); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the key
|
||||||
|
var ecdsaKey *ecdsa.PublicKey
|
||||||
|
switch k := key.(type) {
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
ecdsaKey = k
|
||||||
|
default:
|
||||||
|
return ErrInvalidKeyType
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sig) != 2*m.KeySize {
|
||||||
|
return ErrECDSAVerification
|
||||||
|
}
|
||||||
|
|
||||||
|
r := big.NewInt(0).SetBytes(sig[:m.KeySize])
|
||||||
|
s := big.NewInt(0).SetBytes(sig[m.KeySize:])
|
||||||
|
|
||||||
|
// Create hasher
|
||||||
|
if !m.Hash.Available() {
|
||||||
|
return ErrHashUnavailable
|
||||||
|
}
|
||||||
|
hasher := m.Hash.New()
|
||||||
|
hasher.Write([]byte(signingString))
|
||||||
|
|
||||||
|
// Verify the signature
|
||||||
|
if verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), r, s); verifystatus == true {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return ErrECDSAVerification
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements the Sign method from SigningMethod
|
||||||
|
// For this signing method, key must be an ecdsa.PrivateKey struct
|
||||||
|
func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) (string, error) {
|
||||||
|
// Get the key
|
||||||
|
var ecdsaKey *ecdsa.PrivateKey
|
||||||
|
switch k := key.(type) {
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
ecdsaKey = k
|
||||||
|
default:
|
||||||
|
return "", ErrInvalidKeyType
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the hasher
|
||||||
|
if !m.Hash.Available() {
|
||||||
|
return "", ErrHashUnavailable
|
||||||
|
}
|
||||||
|
|
||||||
|
hasher := m.Hash.New()
|
||||||
|
hasher.Write([]byte(signingString))
|
||||||
|
|
||||||
|
// Sign the string and return r, s
|
||||||
|
if r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, hasher.Sum(nil)); err == nil {
|
||||||
|
curveBits := ecdsaKey.Curve.Params().BitSize
|
||||||
|
|
||||||
|
if m.CurveBits != curveBits {
|
||||||
|
return "", ErrInvalidKey
|
||||||
|
}
|
||||||
|
|
||||||
|
keyBytes := curveBits / 8
|
||||||
|
if curveBits%8 > 0 {
|
||||||
|
keyBytes += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// We serialize the outpus (r and s) into big-endian byte arrays and pad
|
||||||
|
// them with zeros on the left to make sure the sizes work out. Both arrays
|
||||||
|
// must be keyBytes long, and the output must be 2*keyBytes long.
|
||||||
|
rBytes := r.Bytes()
|
||||||
|
rBytesPadded := make([]byte, keyBytes)
|
||||||
|
copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)
|
||||||
|
|
||||||
|
sBytes := s.Bytes()
|
||||||
|
sBytesPadded := make([]byte, keyBytes)
|
||||||
|
copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)
|
||||||
|
|
||||||
|
out := append(rBytesPadded, sBytesPadded...)
|
||||||
|
|
||||||
|
return EncodeSegment(out), nil
|
||||||
|
} else {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
100
vendor/github.com/dgrijalva/jwt-go/ecdsa_test.go
generated
vendored
Normal file
100
vendor/github.com/dgrijalva/jwt-go/ecdsa_test.go
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
vendor/github.com/dgrijalva/jwt-go/ecdsa_utils.go
generated
vendored
Normal file
67
vendor/github.com/dgrijalva/jwt-go/ecdsa_utils.go
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package jwt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNotECPublicKey = errors.New("Key is not a valid ECDSA public key")
|
||||||
|
ErrNotECPrivateKey = errors.New("Key is not a valid ECDSA private key")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parse PEM encoded Elliptic Curve Private Key Structure
|
||||||
|
func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Parse PEM block
|
||||||
|
var block *pem.Block
|
||||||
|
if block, _ = pem.Decode(key); block == nil {
|
||||||
|
return nil, ErrKeyMustBePEMEncoded
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the key
|
||||||
|
var parsedKey interface{}
|
||||||
|
if parsedKey, err = x509.ParseECPrivateKey(block.Bytes); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pkey *ecdsa.PrivateKey
|
||||||
|
var ok bool
|
||||||
|
if pkey, ok = parsedKey.(*ecdsa.PrivateKey); !ok {
|
||||||
|
return nil, ErrNotECPrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse PEM encoded PKCS1 or PKCS8 public key
|
||||||
|
func ParseECPublicKeyFromPEM(key []byte) (*ecdsa.PublicKey, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Parse PEM block
|
||||||
|
var block *pem.Block
|
||||||
|
if block, _ = pem.Decode(key); block == nil {
|
||||||
|
return nil, ErrKeyMustBePEMEncoded
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the key
|
||||||
|
var parsedKey interface{}
|
||||||
|
if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {
|
||||||
|
if cert, err := x509.ParseCertificate(block.Bytes); err == nil {
|
||||||
|
parsedKey = cert.PublicKey
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pkey *ecdsa.PublicKey
|
||||||
|
var ok bool
|
||||||
|
if pkey, ok = parsedKey.(*ecdsa.PublicKey); !ok {
|
||||||
|
return nil, ErrNotECPublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkey, nil
|
||||||
|
}
|
63
vendor/github.com/dgrijalva/jwt-go/errors.go
generated
vendored
Normal file
63
vendor/github.com/dgrijalva/jwt-go/errors.go
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package jwt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error constants
|
||||||
|
var (
|
||||||
|
ErrInvalidKey = errors.New("key is invalid")
|
||||||
|
ErrInvalidKeyType = errors.New("key is of invalid type")
|
||||||
|
ErrHashUnavailable = errors.New("the requested hash function is unavailable")
|
||||||
|
)
|
||||||
|
|
||||||
|
// The errors that might occur when parsing and validating a token
|
||||||
|
const (
|
||||||
|
ValidationErrorMalformed uint32 = 1 << iota // Token is malformed
|
||||||
|
ValidationErrorUnverifiable // Token could not be verified because of signing problems
|
||||||
|
ValidationErrorSignatureInvalid // Signature validation failed
|
||||||
|
|
||||||
|
// Standard Claim validation errors
|
||||||
|
ValidationErrorAudience // AUD validation failed
|
||||||
|
ValidationErrorExpired // EXP validation failed
|
||||||
|
ValidationErrorIssuedAt // IAT validation failed
|
||||||
|
ValidationErrorIssuer // ISS validation failed
|
||||||
|
ValidationErrorNotValidYet // NBF validation failed
|
||||||
|
ValidationErrorId // JTI validation failed
|
||||||
|
ValidationErrorClaimsInvalid // Generic claims validation error
|
||||||
|
)
|
||||||
|
|
||||||
|
// Helper for constructing a ValidationError with a string error message
|
||||||
|
func NewValidationError(errorText string, errorFlags uint32) *ValidationError {
|
||||||
|
return &ValidationError{
|
||||||
|
text: errorText,
|
||||||
|
Errors: errorFlags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The error from Parse if token is not valid
|
||||||
|
type ValidationError struct {
|
||||||
|
Inner error // stores the error returned by external dependencies, i.e.: KeyFunc
|
||||||
|
Errors uint32 // bitfield. see ValidationError... constants
|
||||||
|
text string // errors that do not have a valid error just have text
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validation error is an error type
|
||||||
|
func (e ValidationError) Error() string {
|
||||||
|
if e.Inner != nil {
|
||||||
|
return e.Inner.Error()
|
||||||
|
} else if e.text != "" {
|
||||||
|
return e.text
|
||||||
|
} else {
|
||||||
|
return "token is invalid"
|
||||||
|
}
|
||||||
|
return e.Inner.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// No errors
|
||||||
|
func (e *ValidationError) valid() bool {
|
||||||
|
if e.Errors > 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
114
vendor/github.com/dgrijalva/jwt-go/example_test.go
generated
vendored
Normal file
114
vendor/github.com/dgrijalva/jwt-go/example_test.go
generated
vendored
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
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
|
||||||
|
}
|
94
vendor/github.com/dgrijalva/jwt-go/hmac.go
generated
vendored
Normal file
94
vendor/github.com/dgrijalva/jwt-go/hmac.go
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
package jwt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/hmac"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Implements the HMAC-SHA family of signing methods signing methods
|
||||||
|
type SigningMethodHMAC struct {
|
||||||
|
Name string
|
||||||
|
Hash crypto.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specific instances for HS256 and company
|
||||||
|
var (
|
||||||
|
SigningMethodHS256 *SigningMethodHMAC
|
||||||
|
SigningMethodHS384 *SigningMethodHMAC
|
||||||
|
SigningMethodHS512 *SigningMethodHMAC
|
||||||
|
ErrSignatureInvalid = errors.New("signature is invalid")
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// HS256
|
||||||
|
SigningMethodHS256 = &SigningMethodHMAC{"HS256", crypto.SHA256}
|
||||||
|
RegisterSigningMethod(SigningMethodHS256.Alg(), func() SigningMethod {
|
||||||
|
return SigningMethodHS256
|
||||||
|
})
|
||||||
|
|
||||||
|
// HS384
|
||||||
|
SigningMethodHS384 = &SigningMethodHMAC{"HS384", crypto.SHA384}
|
||||||
|
RegisterSigningMethod(SigningMethodHS384.Alg(), func() SigningMethod {
|
||||||
|
return SigningMethodHS384
|
||||||
|
})
|
||||||
|
|
||||||
|
// HS512
|
||||||
|
SigningMethodHS512 = &SigningMethodHMAC{"HS512", crypto.SHA512}
|
||||||
|
RegisterSigningMethod(SigningMethodHS512.Alg(), func() SigningMethod {
|
||||||
|
return SigningMethodHS512
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SigningMethodHMAC) Alg() string {
|
||||||
|
return m.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the signature of HSXXX tokens. Returns nil if the signature is valid.
|
||||||
|
func (m *SigningMethodHMAC) Verify(signingString, signature string, key interface{}) error {
|
||||||
|
// Verify the key is the right type
|
||||||
|
keyBytes, ok := key.([]byte)
|
||||||
|
if !ok {
|
||||||
|
return ErrInvalidKeyType
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode signature, for comparison
|
||||||
|
sig, err := DecodeSegment(signature)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can we use the specified hashing method?
|
||||||
|
if !m.Hash.Available() {
|
||||||
|
return ErrHashUnavailable
|
||||||
|
}
|
||||||
|
|
||||||
|
// This signing method is symmetric, so we validate the signature
|
||||||
|
// by reproducing the signature from the signing string and key, then
|
||||||
|
// comparing that against the provided signature.
|
||||||
|
hasher := hmac.New(m.Hash.New, keyBytes)
|
||||||
|
hasher.Write([]byte(signingString))
|
||||||
|
if !hmac.Equal(sig, hasher.Sum(nil)) {
|
||||||
|
return ErrSignatureInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
// No validation errors. Signature is good.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements the Sign method from SigningMethod for this signing method.
|
||||||
|
// Key must be []byte
|
||||||
|
func (m *SigningMethodHMAC) Sign(signingString string, key interface{}) (string, error) {
|
||||||
|
if keyBytes, ok := key.([]byte); ok {
|
||||||
|
if !m.Hash.Available() {
|
||||||
|
return "", ErrHashUnavailable
|
||||||
|
}
|
||||||
|
|
||||||
|
hasher := hmac.New(m.Hash.New, keyBytes)
|
||||||
|
hasher.Write([]byte(signingString))
|
||||||
|
|
||||||
|
return EncodeSegment(hasher.Sum(nil)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", ErrInvalidKey
|
||||||
|
}
|
64
vendor/github.com/dgrijalva/jwt-go/hmac_example_test.go
generated
vendored
Normal file
64
vendor/github.com/dgrijalva/jwt-go/hmac_example_test.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
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
|
||||||
|
}
|
91
vendor/github.com/dgrijalva/jwt-go/hmac_test.go
generated
vendored
Normal file
91
vendor/github.com/dgrijalva/jwt-go/hmac_test.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
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)
|
||||||
|
}
|
216
vendor/github.com/dgrijalva/jwt-go/http_example_test.go
generated
vendored
Normal file
216
vendor/github.com/dgrijalva/jwt-go/http_example_test.go
generated
vendored
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
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
|
||||||
|
}
|
94
vendor/github.com/dgrijalva/jwt-go/map_claims.go
generated
vendored
Normal file
94
vendor/github.com/dgrijalva/jwt-go/map_claims.go
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
package jwt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
// "fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Claims type that uses the map[string]interface{} for JSON decoding
|
||||||
|
// This is the default claims type if you don't supply one
|
||||||
|
type MapClaims map[string]interface{}
|
||||||
|
|
||||||
|
// Compares the aud claim against cmp.
|
||||||
|
// If required is false, this method will return true if the value matches or is unset
|
||||||
|
func (m MapClaims) VerifyAudience(cmp string, req bool) bool {
|
||||||
|
aud, _ := m["aud"].(string)
|
||||||
|
return verifyAud(aud, cmp, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compares the exp claim against cmp.
|
||||||
|
// If required is false, this method will return true if the value matches or is unset
|
||||||
|
func (m MapClaims) VerifyExpiresAt(cmp int64, req bool) bool {
|
||||||
|
switch exp := m["exp"].(type) {
|
||||||
|
case float64:
|
||||||
|
return verifyExp(int64(exp), cmp, req)
|
||||||
|
case json.Number:
|
||||||
|
v, _ := exp.Int64()
|
||||||
|
return verifyExp(v, cmp, req)
|
||||||
|
}
|
||||||
|
return req == false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compares the iat claim against cmp.
|
||||||
|
// If required is false, this method will return true if the value matches or is unset
|
||||||
|
func (m MapClaims) VerifyIssuedAt(cmp int64, req bool) bool {
|
||||||
|
switch iat := m["iat"].(type) {
|
||||||
|
case float64:
|
||||||
|
return verifyIat(int64(iat), cmp, req)
|
||||||
|
case json.Number:
|
||||||
|
v, _ := iat.Int64()
|
||||||
|
return verifyIat(v, cmp, req)
|
||||||
|
}
|
||||||
|
return req == false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compares the iss claim against cmp.
|
||||||
|
// If required is false, this method will return true if the value matches or is unset
|
||||||
|
func (m MapClaims) VerifyIssuer(cmp string, req bool) bool {
|
||||||
|
iss, _ := m["iss"].(string)
|
||||||
|
return verifyIss(iss, cmp, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compares the nbf claim against cmp.
|
||||||
|
// If required is false, this method will return true if the value matches or is unset
|
||||||
|
func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool {
|
||||||
|
switch nbf := m["nbf"].(type) {
|
||||||
|
case float64:
|
||||||
|
return verifyNbf(int64(nbf), cmp, req)
|
||||||
|
case json.Number:
|
||||||
|
v, _ := nbf.Int64()
|
||||||
|
return verifyNbf(v, cmp, req)
|
||||||
|
}
|
||||||
|
return req == false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validates time based claims "exp, iat, nbf".
|
||||||
|
// There is no accounting for clock skew.
|
||||||
|
// As well, if any of the above claims are not in the token, it will still
|
||||||
|
// be considered a valid claim.
|
||||||
|
func (m MapClaims) Valid() error {
|
||||||
|
vErr := new(ValidationError)
|
||||||
|
now := TimeFunc().Unix()
|
||||||
|
|
||||||
|
if m.VerifyExpiresAt(now, false) == false {
|
||||||
|
vErr.Inner = errors.New("Token is expired")
|
||||||
|
vErr.Errors |= ValidationErrorExpired
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.VerifyIssuedAt(now, false) == false {
|
||||||
|
vErr.Inner = errors.New("Token used before issued")
|
||||||
|
vErr.Errors |= ValidationErrorIssuedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.VerifyNotBefore(now, false) == false {
|
||||||
|
vErr.Inner = errors.New("Token is not valid yet")
|
||||||
|
vErr.Errors |= ValidationErrorNotValidYet
|
||||||
|
}
|
||||||
|
|
||||||
|
if vErr.valid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return vErr
|
||||||
|
}
|
52
vendor/github.com/dgrijalva/jwt-go/none.go
generated
vendored
Normal file
52
vendor/github.com/dgrijalva/jwt-go/none.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package jwt
|
||||||
|
|
||||||
|
// Implements the none signing method. This is required by the spec
|
||||||
|
// but you probably should never use it.
|
||||||
|
var SigningMethodNone *signingMethodNone
|
||||||
|
|
||||||
|
const UnsafeAllowNoneSignatureType unsafeNoneMagicConstant = "none signing method allowed"
|
||||||
|
|
||||||
|
var NoneSignatureTypeDisallowedError error
|
||||||
|
|
||||||
|
type signingMethodNone struct{}
|
||||||
|
type unsafeNoneMagicConstant string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SigningMethodNone = &signingMethodNone{}
|
||||||
|
NoneSignatureTypeDisallowedError = NewValidationError("'none' signature type is not allowed", ValidationErrorSignatureInvalid)
|
||||||
|
|
||||||
|
RegisterSigningMethod(SigningMethodNone.Alg(), func() SigningMethod {
|
||||||
|
return SigningMethodNone
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *signingMethodNone) Alg() string {
|
||||||
|
return "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only allow 'none' alg type if UnsafeAllowNoneSignatureType is specified as the key
|
||||||
|
func (m *signingMethodNone) Verify(signingString, signature string, key interface{}) (err error) {
|
||||||
|
// Key must be UnsafeAllowNoneSignatureType to prevent accidentally
|
||||||
|
// accepting 'none' signing method
|
||||||
|
if _, ok := key.(unsafeNoneMagicConstant); !ok {
|
||||||
|
return NoneSignatureTypeDisallowedError
|
||||||
|
}
|
||||||
|
// If signing method is none, signature must be an empty string
|
||||||
|
if signature != "" {
|
||||||
|
return NewValidationError(
|
||||||
|
"'none' signing method with non-empty signature",
|
||||||
|
ValidationErrorSignatureInvalid,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept 'none' signing method.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only allow 'none' signing if UnsafeAllowNoneSignatureType is specified as the key
|
||||||
|
func (m *signingMethodNone) Sign(signingString string, key interface{}) (string, error) {
|
||||||
|
if _, ok := key.(unsafeNoneMagicConstant); ok {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return "", NoneSignatureTypeDisallowedError
|
||||||
|
}
|
72
vendor/github.com/dgrijalva/jwt-go/none_test.go
generated
vendored
Normal file
72
vendor/github.com/dgrijalva/jwt-go/none_test.go
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
128
vendor/github.com/dgrijalva/jwt-go/parser.go
generated
vendored
Normal file
128
vendor/github.com/dgrijalva/jwt-go/parser.go
generated
vendored
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package jwt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Parser struct {
|
||||||
|
ValidMethods []string // If populated, only these methods will be considered valid
|
||||||
|
UseJSONNumber bool // Use JSON Number format in JSON decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse, validate, and return a token.
|
||||||
|
// keyFunc will receive the parsed token and should return the key for validating.
|
||||||
|
// If everything is kosher, err will be nil
|
||||||
|
func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) {
|
||||||
|
return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) {
|
||||||
|
parts := strings.Split(tokenString, ".")
|
||||||
|
if len(parts) != 3 {
|
||||||
|
return nil, NewValidationError("token contains an invalid number of segments", ValidationErrorMalformed)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
token := &Token{Raw: tokenString}
|
||||||
|
|
||||||
|
// parse Header
|
||||||
|
var headerBytes []byte
|
||||||
|
if headerBytes, err = DecodeSegment(parts[0]); err != nil {
|
||||||
|
if strings.HasPrefix(strings.ToLower(tokenString), "bearer ") {
|
||||||
|
return token, NewValidationError("tokenstring should not contain 'bearer '", ValidationErrorMalformed)
|
||||||
|
}
|
||||||
|
return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
|
||||||
|
}
|
||||||
|
if err = json.Unmarshal(headerBytes, &token.Header); err != nil {
|
||||||
|
return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse Claims
|
||||||
|
var claimBytes []byte
|
||||||
|
token.Claims = claims
|
||||||
|
|
||||||
|
if claimBytes, err = DecodeSegment(parts[1]); err != nil {
|
||||||
|
return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
|
||||||
|
}
|
||||||
|
dec := json.NewDecoder(bytes.NewBuffer(claimBytes))
|
||||||
|
if p.UseJSONNumber {
|
||||||
|
dec.UseNumber()
|
||||||
|
}
|
||||||
|
// JSON Decode. Special case for map type to avoid weird pointer behavior
|
||||||
|
if c, ok := token.Claims.(MapClaims); ok {
|
||||||
|
err = dec.Decode(&c)
|
||||||
|
} else {
|
||||||
|
err = dec.Decode(&claims)
|
||||||
|
}
|
||||||
|
// Handle decode error
|
||||||
|
if err != nil {
|
||||||
|
return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup signature method
|
||||||
|
if method, ok := token.Header["alg"].(string); ok {
|
||||||
|
if token.Method = GetSigningMethod(method); token.Method == nil {
|
||||||
|
return token, NewValidationError("signing method (alg) is unavailable.", ValidationErrorUnverifiable)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return token, NewValidationError("signing method (alg) is unspecified.", ValidationErrorUnverifiable)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify signing method is in the required set
|
||||||
|
if p.ValidMethods != nil {
|
||||||
|
var signingMethodValid = false
|
||||||
|
var alg = token.Method.Alg()
|
||||||
|
for _, m := range p.ValidMethods {
|
||||||
|
if m == alg {
|
||||||
|
signingMethodValid = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !signingMethodValid {
|
||||||
|
// signing method is not in the listed set
|
||||||
|
return token, NewValidationError(fmt.Sprintf("signing method %v is invalid", alg), ValidationErrorSignatureInvalid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup key
|
||||||
|
var key interface{}
|
||||||
|
if keyFunc == nil {
|
||||||
|
// keyFunc was not provided. short circuiting validation
|
||||||
|
return token, NewValidationError("no Keyfunc was provided.", ValidationErrorUnverifiable)
|
||||||
|
}
|
||||||
|
if key, err = keyFunc(token); err != nil {
|
||||||
|
// keyFunc returned an error
|
||||||
|
return token, &ValidationError{Inner: err, Errors: ValidationErrorUnverifiable}
|
||||||
|
}
|
||||||
|
|
||||||
|
vErr := &ValidationError{}
|
||||||
|
|
||||||
|
// Validate Claims
|
||||||
|
if err := token.Claims.Valid(); err != nil {
|
||||||
|
|
||||||
|
// If the Claims Valid returned an error, check if it is a validation error,
|
||||||
|
// If it was another error type, create a ValidationError with a generic ClaimsInvalid flag set
|
||||||
|
if e, ok := err.(*ValidationError); !ok {
|
||||||
|
vErr = &ValidationError{Inner: err, Errors: ValidationErrorClaimsInvalid}
|
||||||
|
} else {
|
||||||
|
vErr = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform validation
|
||||||
|
token.Signature = parts[2]
|
||||||
|
if err = token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, key); err != nil {
|
||||||
|
vErr.Inner = err
|
||||||
|
vErr.Errors |= ValidationErrorSignatureInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
if vErr.valid() {
|
||||||
|
token.Valid = true
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return token, vErr
|
||||||
|
}
|
252
vendor/github.com/dgrijalva/jwt-go/parser_test.go
generated
vendored
Normal file
252
vendor/github.com/dgrijalva/jwt-go/parser_test.go
generated
vendored
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
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},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
7
vendor/github.com/dgrijalva/jwt-go/request/doc.go
generated
vendored
Normal file
7
vendor/github.com/dgrijalva/jwt-go/request/doc.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// 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
|
81
vendor/github.com/dgrijalva/jwt-go/request/extractor.go
generated
vendored
Normal file
81
vendor/github.com/dgrijalva/jwt-go/request/extractor.go
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
32
vendor/github.com/dgrijalva/jwt-go/request/extractor_example_test.go
generated
vendored
Normal file
32
vendor/github.com/dgrijalva/jwt-go/request/extractor_example_test.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
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
|
||||||
|
}
|
91
vendor/github.com/dgrijalva/jwt-go/request/extractor_test.go
generated
vendored
Normal file
91
vendor/github.com/dgrijalva/jwt-go/request/extractor_test.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
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
|
||||||
|
}
|
28
vendor/github.com/dgrijalva/jwt-go/request/oauth2.go
generated
vendored
Normal file
28
vendor/github.com/dgrijalva/jwt-go/request/oauth2.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
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"},
|
||||||
|
}
|
24
vendor/github.com/dgrijalva/jwt-go/request/request.go
generated
vendored
Normal file
24
vendor/github.com/dgrijalva/jwt-go/request/request.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
103
vendor/github.com/dgrijalva/jwt-go/request/request_test.go
generated
vendored
Normal file
103
vendor/github.com/dgrijalva/jwt-go/request/request_test.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
100
vendor/github.com/dgrijalva/jwt-go/rsa.go
generated
vendored
Normal file
100
vendor/github.com/dgrijalva/jwt-go/rsa.go
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package jwt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Implements the RSA family of signing methods signing methods
|
||||||
|
type SigningMethodRSA struct {
|
||||||
|
Name string
|
||||||
|
Hash crypto.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specific instances for RS256 and company
|
||||||
|
var (
|
||||||
|
SigningMethodRS256 *SigningMethodRSA
|
||||||
|
SigningMethodRS384 *SigningMethodRSA
|
||||||
|
SigningMethodRS512 *SigningMethodRSA
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// RS256
|
||||||
|
SigningMethodRS256 = &SigningMethodRSA{"RS256", crypto.SHA256}
|
||||||
|
RegisterSigningMethod(SigningMethodRS256.Alg(), func() SigningMethod {
|
||||||
|
return SigningMethodRS256
|
||||||
|
})
|
||||||
|
|
||||||
|
// RS384
|
||||||
|
SigningMethodRS384 = &SigningMethodRSA{"RS384", crypto.SHA384}
|
||||||
|
RegisterSigningMethod(SigningMethodRS384.Alg(), func() SigningMethod {
|
||||||
|
return SigningMethodRS384
|
||||||
|
})
|
||||||
|
|
||||||
|
// RS512
|
||||||
|
SigningMethodRS512 = &SigningMethodRSA{"RS512", crypto.SHA512}
|
||||||
|
RegisterSigningMethod(SigningMethodRS512.Alg(), func() SigningMethod {
|
||||||
|
return SigningMethodRS512
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SigningMethodRSA) Alg() string {
|
||||||
|
return m.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements the Verify method from SigningMethod
|
||||||
|
// For this signing method, must be an rsa.PublicKey structure.
|
||||||
|
func (m *SigningMethodRSA) Verify(signingString, signature string, key interface{}) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Decode the signature
|
||||||
|
var sig []byte
|
||||||
|
if sig, err = DecodeSegment(signature); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var rsaKey *rsa.PublicKey
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
if rsaKey, ok = key.(*rsa.PublicKey); !ok {
|
||||||
|
return ErrInvalidKeyType
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create hasher
|
||||||
|
if !m.Hash.Available() {
|
||||||
|
return ErrHashUnavailable
|
||||||
|
}
|
||||||
|
hasher := m.Hash.New()
|
||||||
|
hasher.Write([]byte(signingString))
|
||||||
|
|
||||||
|
// Verify the signature
|
||||||
|
return rsa.VerifyPKCS1v15(rsaKey, m.Hash, hasher.Sum(nil), sig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements the Sign method from SigningMethod
|
||||||
|
// For this signing method, must be an rsa.PrivateKey structure.
|
||||||
|
func (m *SigningMethodRSA) Sign(signingString string, key interface{}) (string, error) {
|
||||||
|
var rsaKey *rsa.PrivateKey
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
// Validate type of key
|
||||||
|
if rsaKey, ok = key.(*rsa.PrivateKey); !ok {
|
||||||
|
return "", ErrInvalidKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the hasher
|
||||||
|
if !m.Hash.Available() {
|
||||||
|
return "", ErrHashUnavailable
|
||||||
|
}
|
||||||
|
|
||||||
|
hasher := m.Hash.New()
|
||||||
|
hasher.Write([]byte(signingString))
|
||||||
|
|
||||||
|
// Sign the string and return the encoded bytes
|
||||||
|
if sigBytes, err := rsa.SignPKCS1v15(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil)); err == nil {
|
||||||
|
return EncodeSegment(sigBytes), nil
|
||||||
|
} else {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user