arm64 support and build update (#190)
This commit is contained in:
Родитель
e99c79aa4d
Коммит
6ff686b62f
|
@ -1,121 +0,0 @@
|
|||
{
|
||||
"ImportPath": "github.com/Azure/custom-script-extension-linux",
|
||||
"GoVersion": "go1.6",
|
||||
"GodepVersion": "v74",
|
||||
"Packages": [
|
||||
"./..."
|
||||
],
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "github.com/Azure/azure-docker-extension/pkg/vmextension",
|
||||
"Comment": "1.1.1606092330-20-g0dd2f19",
|
||||
"Rev": "0dd2f199467db78e91d28c30d8595d1bcaf776de"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/Azure/azure-docker-extension/pkg/vmextension/status",
|
||||
"Comment": "1.1.1606092330-20-g0dd2f19",
|
||||
"Rev": "0dd2f199467db78e91d28c30d8595d1bcaf776de"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/Azure/azure-sdk-for-go/storage",
|
||||
"Comment": "v3.1.0-beta-18-g87de771",
|
||||
"Rev": "87de771fcdf5ec4341f3842f74b5bb98652b10f7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ahmetalpbalkan/go-httpbin",
|
||||
"Rev": "8817b883dae18c2cf82e5af7b4d4f4674de24b09"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/davecgh/go-spew/spew",
|
||||
"Rev": "5215b55f46b2b919f50a1df0eaa5886afe4e3b3d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/go-kit/kit/log",
|
||||
"Comment": "v0.1.0-64-gb076b44",
|
||||
"Rev": "b076b44dbec2aa943b52c9efaa5e8b46fd52b061"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/go-logfmt/logfmt",
|
||||
"Comment": "v0.2.0-9-gd432719",
|
||||
"Rev": "d4327190ff838312623b09bfeb50d7c93c8d9c1d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/go-stack/stack",
|
||||
"Comment": "v1.5.2",
|
||||
"Rev": "100eb0c0a9c5b306ca2fb4f165df21d80ada4b82"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gorilla/context",
|
||||
"Comment": "v1.1-4-gaed02d1",
|
||||
"Rev": "aed02d124ae4a0e94fea4541c8effd05bf0c8296"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gorilla/mux",
|
||||
"Comment": "v1.1-13-g9fa818a",
|
||||
"Rev": "9fa818a44c2bf1396a17f9d5a3c0f6dd39d2ff8e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/kr/logfmt",
|
||||
"Rev": "b84e30acd515aadc4b783ad4ff83aff3299bdfe0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/pkg/errors",
|
||||
"Comment": "v0.7.0-5-ga2d6902",
|
||||
"Rev": "a2d6902c6d2a2f194eb3fb474981ab7867c81505"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/pmezard/go-difflib/difflib",
|
||||
"Rev": "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/stretchr/testify/assert",
|
||||
"Comment": "v1.1.3-19-gd77da35",
|
||||
"Rev": "d77da356e56a7428ad25149ca77381849a6a5232"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/stretchr/testify/require",
|
||||
"Comment": "v1.1.3-19-gd77da35",
|
||||
"Rev": "d77da356e56a7428ad25149ca77381849a6a5232"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/xeipuuv/gojsonpointer",
|
||||
"Rev": "e0fe6f68307607d540ed8eac07a342c33fa1b54a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/xeipuuv/gojsonreference",
|
||||
"Rev": "e02fc20de94c78484cd5ffb007f8af96be030a45"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/xeipuuv/gojsonschema",
|
||||
"Rev": "c539bca196be50ccdd1f0bcd9076de95f9081831"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/encoding",
|
||||
"Rev": "ce78b075c2fbd48520f4995b173eb9fe18b56ef3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/encoding/internal",
|
||||
"Rev": "ce78b075c2fbd48520f4995b173eb9fe18b56ef3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/encoding/internal/identifier",
|
||||
"Rev": "ce78b075c2fbd48520f4995b173eb9fe18b56ef3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/encoding/unicode",
|
||||
"Rev": "ce78b075c2fbd48520f4995b173eb9fe18b56ef3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/internal/utf8internal",
|
||||
"Rev": "ce78b075c2fbd48520f4995b173eb9fe18b56ef3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/runes",
|
||||
"Rev": "ce78b075c2fbd48520f4995b173eb9fe18b56ef3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/transform",
|
||||
"Rev": "ce78b075c2fbd48520f4995b173eb9fe18b56ef3"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
This directory tree is generated automatically by godep.
|
||||
|
||||
Please do not edit.
|
||||
|
||||
See https://github.com/tools/godep for more information.
|
8
Makefile
8
Makefile
|
@ -1,12 +1,12 @@
|
|||
BINDIR=bin
|
||||
BIN=custom-script-extension
|
||||
BIN_ARM64=custom-script-extension-arm64
|
||||
BUNDLEDIR=bundle
|
||||
BUNDLE=custom-script-extension.zip
|
||||
|
||||
bundle: clean binary
|
||||
@mkdir -p $(BUNDLEDIR)
|
||||
zip ./$(BUNDLEDIR)/$(BUNDLE) ./$(BINDIR)/$(BIN)
|
||||
zip ./$(BUNDLEDIR)/$(BUNDLE) ./$(BINDIR)/custom-script-shim
|
||||
zip -r ./$(BUNDLEDIR)/$(BUNDLE) ./$(BINDIR)
|
||||
zip -j ./$(BUNDLEDIR)/$(BUNDLE) ./misc/HandlerManifest.json
|
||||
zip -j ./$(BUNDLEDIR)/$(BUNDLE) ./misc/manifest.xml
|
||||
|
||||
|
@ -15,10 +15,12 @@ binary: clean
|
|||
echo "GOPATH is not set"; \
|
||||
exit 1; \
|
||||
fi
|
||||
go get -d -u -f github.com/Azure/azure-extension-foundation/...
|
||||
GOOS=linux GOARCH=amd64 govvv build -v \
|
||||
-ldflags "-X main.Version=`grep -E -m 1 -o '<Version>(.*)</Version>' misc/manifest.xml | awk -F">" '{print $$2}' | awk -F"<" '{print $$1}'`" \
|
||||
-o $(BINDIR)/$(BIN) ./main
|
||||
GOOS=linux GOARCH=arm64 govvv build -v \
|
||||
-ldflags "-X main.Version=`grep -E -m 1 -o '<Version>(.*)</Version>' misc/manifest.xml | awk -F">" '{print $$2}' | awk -F"<" '{print $$1}'`" \
|
||||
-o $(BINDIR)/$(BIN_ARM64) ./main
|
||||
cp ./misc/custom-script-shim ./$(BINDIR)
|
||||
clean:
|
||||
rm -rf "$(BINDIR)" "$(BUNDLEDIR)"
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
module github.com/Azure/custom-script-extension-linux
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-extension-foundation v0.0.0-20190726000431-02f4f599e64a
|
||||
github.com/Azure/azure-sdk-for-go v3.1.0-beta.0.20160802173609-87de771fcdf5+incompatible
|
||||
github.com/ahmetalpbalkan/go-httpbin v0.0.0-20160706084156-8817b883dae1
|
||||
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2 // indirect
|
||||
github.com/go-kit/kit v0.1.1-0.20160721083846-b076b44dbec2
|
||||
github.com/go-logfmt/logfmt v0.2.1-0.20160601130801-d4327190ff83 // indirect
|
||||
github.com/go-stack/stack v1.5.2 // indirect
|
||||
github.com/gorilla/context v0.0.0-20160525203319-aed02d124ae4 // indirect
|
||||
github.com/gorilla/mux v0.0.0-20160605233521-9fa818a44c2b // indirect
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.1.4-0.20160615092844-d77da356e56a
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20151027082146-e0fe6f683076 // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20150808065054-e02fc20de94c // indirect
|
||||
github.com/xeipuuv/gojsonschema v0.0.0-20160623135812-c539bca196be
|
||||
golang.org/x/text v0.3.7
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
)
|
|
@ -0,0 +1,42 @@
|
|||
github.com/Azure/azure-extension-foundation v0.0.0-20190726000431-02f4f599e64a h1:dU4O2MKIUGQ9JeJf6yyBLXwA1wso5zu1wLcz0pQggbc=
|
||||
github.com/Azure/azure-extension-foundation v0.0.0-20190726000431-02f4f599e64a/go.mod h1:a0BFq9UoWBHvBS7iagvjFqBjYfxtBsmqvCLWIHRq9b0=
|
||||
github.com/Azure/azure-sdk-for-go v3.1.0-beta.0.20160802173609-87de771fcdf5+incompatible h1:7CctKV2SGUVFq3a+WNHypGRKQzaPCNVEAMMAlEJXrWc=
|
||||
github.com/Azure/azure-sdk-for-go v3.1.0-beta.0.20160802173609-87de771fcdf5+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/ahmetalpbalkan/go-httpbin v0.0.0-20160706084156-8817b883dae1 h1:/sPElPBMLSi6+bV0o0fPN4U24qQCNHs1i/BjnO+GqLc=
|
||||
github.com/ahmetalpbalkan/go-httpbin v0.0.0-20160706084156-8817b883dae1/go.mod h1:Rg55S63lgqSBCawY/oTm7jdFSySp6jwIqgHMB2IeHK8=
|
||||
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2 h1:5zdDAMuB3gvbHB1m2BZT9+t9w+xaBmK3ehb7skDXcwM=
|
||||
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-kit/kit v0.1.1-0.20160721083846-b076b44dbec2 h1:awXynDTA1TiAp1SA/o/xoU6oRHE3xKCokck9l4/poMc=
|
||||
github.com/go-kit/kit v0.1.1-0.20160721083846-b076b44dbec2/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.2.1-0.20160601130801-d4327190ff83 h1:WEFlTYvIQSd2ofUwgM9nOR+KTzdG/pLZzdUlDp6mciM=
|
||||
github.com/go-logfmt/logfmt v0.2.1-0.20160601130801-d4327190ff83/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-stack/stack v1.5.2 h1:5sTB/0oZM2O31k/N1IRwxxVXzLIt5NF2Aqx/2gWI9OY=
|
||||
github.com/go-stack/stack v1.5.2/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gorilla/context v0.0.0-20160525203319-aed02d124ae4 h1:3nOfQt8sRPYbXORD5tJ8YyQ3HlL2Jt3LJ2U17CbNh6I=
|
||||
github.com/gorilla/context v0.0.0-20160525203319-aed02d124ae4/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v0.0.0-20160605233521-9fa818a44c2b h1:OFvZV3a+25cGJH9dETHw0nk0wV6hLZI7IJijOkXEFS0=
|
||||
github.com/gorilla/mux v0.0.0-20160605233521-9fa818a44c2b/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.1.4-0.20160615092844-d77da356e56a h1:UWu0XgfW9PCuyeZYNe2eGGkDZjooQKjVQqY/+d/jYmc=
|
||||
github.com/stretchr/testify v1.1.4-0.20160615092844-d77da356e56a/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20151027082146-e0fe6f683076 h1:KM4T3G70MiR+JtqplcYkNVoNz7pDwYaBxWBXQK804So=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20151027082146-e0fe6f683076/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20150808065054-e02fc20de94c h1:XZWnr3bsDQWAZg4Ne+cPoXRPILrNlPNQfxBuwLl43is=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20150808065054-e02fc20de94c/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v0.0.0-20160623135812-c539bca196be h1:sRGd3e18izj1hQgF1hSvDOA8RPPnA2t4p8YeLZ/GdBU=
|
||||
github.com/xeipuuv/gojsonschema v0.0.0-20160623135812-c539bca196be/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
17
main/cmds.go
17
main/cmds.go
|
@ -11,7 +11,6 @@ import (
|
|||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-docker-extension/pkg/vmextension"
|
||||
"github.com/Azure/custom-script-extension-linux/pkg/seqnum"
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -21,7 +20,7 @@ const (
|
|||
maxScriptSize = 256 * 1024
|
||||
)
|
||||
|
||||
type cmdFunc func(ctx *log.Context, hEnv vmextension.HandlerEnvironment, seqNum int) (msg string, err error)
|
||||
type cmdFunc func(ctx *log.Context, hEnv HandlerEnvironment, seqNum int) (msg string, err error)
|
||||
type preFunc func(ctx *log.Context, seqNum int) error
|
||||
|
||||
type cmd struct {
|
||||
|
@ -54,12 +53,12 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
func noop(ctx *log.Context, h vmextension.HandlerEnvironment, seqNum int) (string, error) {
|
||||
func noop(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, error) {
|
||||
ctx.Log("event", "noop")
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func install(ctx *log.Context, h vmextension.HandlerEnvironment, seqNum int) (string, error) {
|
||||
func install(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, error) {
|
||||
if err := os.MkdirAll(dataDir, 0755); err != nil {
|
||||
return "", errors.Wrap(err, "failed to create data dir")
|
||||
}
|
||||
|
@ -79,7 +78,7 @@ func install(ctx *log.Context, h vmextension.HandlerEnvironment, seqNum int) (st
|
|||
return "", nil
|
||||
}
|
||||
|
||||
func migrateToMostRecentSequence(ctx *log.Context, h vmextension.HandlerEnvironment, seqNum int) {
|
||||
func migrateToMostRecentSequence(ctx *log.Context, h HandlerEnvironment, seqNum int) {
|
||||
// The status folder is used instead of the settings because the settings file is written
|
||||
// by the agent before install is called. As a result, the extension cannot determine if this
|
||||
// is a new install or an upgrade.
|
||||
|
@ -93,7 +92,7 @@ func migrateToMostRecentSequence(ctx *log.Context, h vmextension.HandlerEnvironm
|
|||
// do not have invent another method. The CustomScript extension should have been using this
|
||||
// from the beginning, but it was not.
|
||||
//
|
||||
computedSeqNum, err := vmextension.FindSeqNumStatus(h.HandlerEnvironment.StatusFolder)
|
||||
computedSeqNum, err := FindSeqNumStatus(h.HandlerEnvironment.StatusFolder)
|
||||
if err != nil {
|
||||
// If there was an error, the sequence number is zero.
|
||||
ctx.Log("event", "migrate to mrseq", "error", err)
|
||||
|
@ -111,7 +110,7 @@ func migrateToMostRecentSequence(ctx *log.Context, h vmextension.HandlerEnvironm
|
|||
fout.WriteString(fmt.Sprintf("%v", computedSeqNum))
|
||||
}
|
||||
|
||||
func uninstall(ctx *log.Context, h vmextension.HandlerEnvironment, seqNum int) (string, error) {
|
||||
func uninstall(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, error) {
|
||||
{ // a new context scope with path
|
||||
ctx = ctx.With("path", dataDir)
|
||||
ctx.Log("event", "removing data dir", "path", dataDir)
|
||||
|
@ -143,9 +142,9 @@ func min(a, b int) int {
|
|||
return b
|
||||
}
|
||||
|
||||
func enable(ctx *log.Context, h vmextension.HandlerEnvironment, seqNum int) (string, error) {
|
||||
func enable(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, error) {
|
||||
// parse the extension handler settings (not available prior to 'enable')
|
||||
cfg, err := parseAndValidateSettings(ctx, h.HandlerEnvironment.ConfigFolder)
|
||||
cfg, err := parseAndValidateSettings(ctx, h.HandlerEnvironment.ConfigFolder, seqNum)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to get configuration")
|
||||
}
|
||||
|
|
|
@ -7,13 +7,14 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"os"
|
||||
|
||||
"github.com/Azure/custom-script-extension-linux/pkg/blobutil"
|
||||
"github.com/Azure/custom-script-extension-linux/pkg/download"
|
||||
"github.com/Azure/custom-script-extension-linux/pkg/preprocess"
|
||||
"github.com/Azure/custom-script-extension-linux/pkg/urlutil"
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/pkg/errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
// downloadAndProcessURL downloads using the specified downloader and saves it to the
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package vmextension
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
|
@ -2,8 +2,9 @@ package main
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Azure/azure-docker-extension/pkg/vmextension"
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
@ -20,8 +21,8 @@ var (
|
|||
|
||||
// handlerSettings holds the configuration of the extension handler.
|
||||
type handlerSettings struct {
|
||||
publicSettings
|
||||
protectedSettings
|
||||
publicSettings `json:"publicSettings"`
|
||||
protectedSettings `json:"protectedSettings"`
|
||||
}
|
||||
|
||||
func (s *handlerSettings) commandToExecute() string {
|
||||
|
@ -112,9 +113,9 @@ func (self *clientOrObjectId) isEmpty() bool {
|
|||
|
||||
// parseAndValidateSettings reads configuration from configFolder, decrypts it,
|
||||
// runs JSON-schema and logical validation on it and returns it back.
|
||||
func parseAndValidateSettings(ctx *log.Context, configFolder string) (h handlerSettings, _ error) {
|
||||
func parseAndValidateSettings(ctx *log.Context, configFolder string, seqNum int) (h handlerSettings, _ error) {
|
||||
ctx.Log("event", "reading configuration")
|
||||
pubJSON, protJSON, err := readSettings(configFolder)
|
||||
pubJSON, protJSON, err := readSettings(configFolder, seqNum)
|
||||
if err != nil {
|
||||
return h, err
|
||||
}
|
||||
|
@ -127,7 +128,7 @@ func parseAndValidateSettings(ctx *log.Context, configFolder string) (h handlerS
|
|||
ctx.Log("event", "json schema valid")
|
||||
|
||||
ctx.Log("event", "parsing configuration json")
|
||||
if err := vmextension.UnmarshalHandlerSettings(pubJSON, protJSON, &h.publicSettings, &h.protectedSettings); err != nil {
|
||||
if err := UnmarshalHandlerSettings(pubJSON, protJSON, &h.publicSettings, &h.protectedSettings); err != nil {
|
||||
return h, errors.Wrap(err, "json parsing error")
|
||||
}
|
||||
ctx.Log("event", "parsed configuration json")
|
||||
|
@ -143,8 +144,9 @@ func parseAndValidateSettings(ctx *log.Context, configFolder string) (h handlerS
|
|||
// readSettings uses specified configFolder (comes from HandlerEnvironment) to
|
||||
// decrypt and parse the public/protected settings of the extension handler into
|
||||
// JSON objects.
|
||||
func readSettings(configFolder string) (pubSettingsJSON, protSettingsJSON map[string]interface{}, err error) {
|
||||
pubSettingsJSON, protSettingsJSON, err = vmextension.ReadSettings(configFolder)
|
||||
func readSettings(configFolder string, seqNum int) (pubSettingsJSON, protSettingsJSON map[string]interface{}, err error) {
|
||||
cf := filepath.Join(configFolder, fmt.Sprintf("%d%s", seqNum, ".settings"))
|
||||
pubSettingsJSON, protSettingsJSON, err = ReadSettings(cf)
|
||||
err = errors.Wrapf(err, "error reading extension configuration")
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package vmextension
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -7,9 +7,8 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -18,11 +17,11 @@ const (
|
|||
|
||||
type handlerSettingsFile struct {
|
||||
RuntimeSettings []struct {
|
||||
HandlerSettings handlerSettings `json:"handlerSettings"`
|
||||
HandlerSettings handlerSettingsCommon `json:"handlerSettings"`
|
||||
} `json:"runtimeSettings"`
|
||||
}
|
||||
|
||||
type handlerSettings struct {
|
||||
type handlerSettingsCommon struct {
|
||||
PublicSettings map[string]interface{} `json:"publicSettings"`
|
||||
ProtectedSettingsBase64 string `json:"protectedSettings"`
|
||||
SettingsCertThumbprint string `json:"protectedSettingsCertThumbprint"`
|
||||
|
@ -41,17 +40,14 @@ func settingsPath(configFolder string) (string, error) {
|
|||
// ReadSettings locates the .settings file and returns public settings
|
||||
// JSON, and protected settings JSON (by decrypting it with the keys in
|
||||
// configFolder).
|
||||
func ReadSettings(configFolder string) (public, protected map[string]interface{}, _ error) {
|
||||
cf, err := settingsPath(configFolder)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("canot locate settings file: %v", err)
|
||||
}
|
||||
hs, err := parseHandlerSettingsFile(cf)
|
||||
func ReadSettings(configFilePath string) (public, protected map[string]interface{}, _ error) {
|
||||
hs, err := parseHandlerSettingsFile(configFilePath)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error parsing settings file: %v", err)
|
||||
}
|
||||
|
||||
public = hs.PublicSettings
|
||||
configFolder := filepath.Dir(configFilePath)
|
||||
if err := unmarshalProtectedSettings(configFolder, hs, &protected); err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to parse protected settings: %v", err)
|
||||
}
|
||||
|
@ -86,7 +82,7 @@ func unmarshalSettings(in interface{}, v interface{}) error {
|
|||
|
||||
// parseHandlerSettings parses a handler settings file (e.g. 0.settings) and
|
||||
// returns it as a structured object.
|
||||
func parseHandlerSettingsFile(path string) (h handlerSettings, _ error) {
|
||||
func parseHandlerSettingsFile(path string) (h handlerSettingsCommon, _ error) {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return h, fmt.Errorf("Error reading %s: %v", path, err)
|
||||
|
@ -108,7 +104,7 @@ func parseHandlerSettingsFile(path string) (h handlerSettings, _ error) {
|
|||
// unmarshalProtectedSettings decodes the protected settings from handler
|
||||
// runtime settings JSON file, decrypts it using the certificates and unmarshals
|
||||
// into the given struct v.
|
||||
func unmarshalProtectedSettings(configFolder string, hs handlerSettings, v interface{}) error {
|
||||
func unmarshalProtectedSettings(configFolder string, hs handlerSettingsCommon, v interface{}) error {
|
||||
if hs.ProtectedSettingsBase64 == "" {
|
||||
return nil
|
||||
}
|
36
main/main.go
36
main/main.go
|
@ -3,10 +3,9 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/azure-docker-extension/pkg/vmextension"
|
||||
"github.com/Azure/azure-docker-extension/pkg/vmextension/status"
|
||||
"github.com/go-kit/kit/log"
|
||||
)
|
||||
|
||||
|
@ -27,6 +26,9 @@ var (
|
|||
// downloadDir is where we store the downloaded files in the "{downloadDir}/{seqnum}/file"
|
||||
// format and the logs as "{downloadDir}/{seqnum}/std(out|err)". Stored under dataDir
|
||||
downloadDir = "download"
|
||||
|
||||
// configSequenceNumber environment variable should be set by VMAgent to sequence number
|
||||
configSequenceNumber = "ConfigSequenceNumber"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -38,14 +40,30 @@ func main() {
|
|||
ctx = ctx.With("operation", strings.ToLower(cmd.name))
|
||||
|
||||
// parse extension environment
|
||||
hEnv, err := vmextension.GetHandlerEnv()
|
||||
hEnv, err := GetHandlerEnv()
|
||||
if err != nil {
|
||||
ctx.Log("message", "failed to parse handlerenv", "error", err)
|
||||
os.Exit(cmd.failExitCode)
|
||||
}
|
||||
seqNum, err := vmextension.FindSeqNumConfig(hEnv.HandlerEnvironment.ConfigFolder)
|
||||
if err != nil {
|
||||
ctx.Log("messsage", "failed to find sequence number", "error", err)
|
||||
|
||||
seqNum := -1
|
||||
// Agent should set env variable sequence number
|
||||
seqNumVariable := os.Getenv(configSequenceNumber)
|
||||
if seqNumVariable != "" {
|
||||
seqNum, err = strconv.Atoi(seqNumVariable)
|
||||
if err != nil {
|
||||
ctx.Log("message", "failed to parse env variable ConfigSequenceNumber:"+seqNumVariable, "error", err)
|
||||
os.Exit(cmd.failExitCode)
|
||||
}
|
||||
}
|
||||
|
||||
// Read the seqNum from latest config file in case VMAgent did not set it as env variable (old agent versions)
|
||||
if seqNum == -1 {
|
||||
seqNum, err = FindSeqNumConfig(hEnv.HandlerEnvironment.ConfigFolder)
|
||||
if err != nil {
|
||||
ctx.Log("messsage", "failed to find sequence number", "error", err)
|
||||
os.Exit(cmd.failExitCode)
|
||||
}
|
||||
}
|
||||
ctx = ctx.With("seq", seqNum)
|
||||
|
||||
|
@ -59,14 +77,14 @@ func main() {
|
|||
}
|
||||
}
|
||||
// execute the subcommand
|
||||
reportStatus(ctx, hEnv, seqNum, status.StatusTransitioning, cmd, "")
|
||||
reportStatus(ctx, hEnv, seqNum, StatusTransitioning, cmd, "")
|
||||
msg, err := cmd.f(ctx, hEnv, seqNum)
|
||||
if err != nil {
|
||||
ctx.Log("event", "failed to handle", "error", err)
|
||||
reportStatus(ctx, hEnv, seqNum, status.StatusError, cmd, err.Error()+msg)
|
||||
reportStatus(ctx, hEnv, seqNum, StatusError, cmd, err.Error()+msg)
|
||||
os.Exit(cmd.failExitCode)
|
||||
}
|
||||
reportStatus(ctx, hEnv, seqNum, status.StatusSuccess, cmd, msg)
|
||||
reportStatus(ctx, hEnv, seqNum, StatusSuccess, cmd, msg)
|
||||
ctx.Log("event", "end")
|
||||
}
|
||||
|
||||
|
|
|
@ -1,22 +1,30 @@
|
|||
package vmextension
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// chmod is used to set the mode bits for new seqnum files.
|
||||
chmod = os.FileMode(0600)
|
||||
)
|
||||
|
||||
// FindSeqNumConfig gets the laster seq no from config files
|
||||
func FindSeqNumConfig(path string) (int, error) {
|
||||
return FindSeqNum(path, ".settings")
|
||||
}
|
||||
|
||||
// FindSeqNumStatus gets the laster seq no from status files
|
||||
func FindSeqNumStatus(path string) (int, error) {
|
||||
return FindSeqNum(path, ".status")
|
||||
}
|
||||
|
||||
// FindSeqnum finds the file with the highest number under configFolder
|
||||
// FindSeqNum finds the file with the highest number under configFolder
|
||||
// named like 0.settings, 1.settings so on.
|
||||
func FindSeqNum(path, ext string) (int, error) {
|
||||
g, err := filepath.Glob(filepath.Join(path, fmt.Sprintf("*%s", ext)))
|
||||
|
@ -33,9 +41,8 @@ func FindSeqNum(path, ext string) (int, error) {
|
|||
seqs = append(seqs, i)
|
||||
}
|
||||
if len(seqs) == 0 {
|
||||
return 0, fmt.Errorf("Can't find out seqnum from %s, not enough files.", path)
|
||||
return 0, fmt.Errorf("Can't find out seqnum from %s, not enough files", path)
|
||||
}
|
||||
sort.Sort(sort.Reverse(sort.IntSlice(seqs)))
|
||||
return seqs[0], nil
|
||||
}
|
||||
|
113
main/status.go
113
main/status.go
|
@ -1,23 +1,100 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/Azure/azure-docker-extension/pkg/vmextension"
|
||||
"github.com/Azure/azure-docker-extension/pkg/vmextension/status"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type StatusReport []StatusItem
|
||||
|
||||
type StatusItem struct {
|
||||
Version float64 `json:"version"`
|
||||
TimestampUTC string `json:"timestampUTC"`
|
||||
Status Status `json:"status"`
|
||||
}
|
||||
|
||||
type Type string
|
||||
|
||||
const (
|
||||
StatusTransitioning Type = "transitioning"
|
||||
StatusError Type = "error"
|
||||
StatusSuccess Type = "success"
|
||||
)
|
||||
|
||||
type Status struct {
|
||||
Operation string `json:"operation"`
|
||||
Status Type `json:"status"`
|
||||
FormattedMessage FormattedMessage `json:"formattedMessage"`
|
||||
}
|
||||
type FormattedMessage struct {
|
||||
Lang string `json:"lang"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func NewStatus(t Type, operation, message string) StatusReport {
|
||||
return []StatusItem{
|
||||
{
|
||||
Version: 1.0,
|
||||
TimestampUTC: time.Now().UTC().Format(time.RFC3339),
|
||||
Status: Status{
|
||||
Operation: operation,
|
||||
Status: t,
|
||||
FormattedMessage: FormattedMessage{
|
||||
Lang: "en",
|
||||
Message: message},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r StatusReport) marshal() ([]byte, error) {
|
||||
return json.MarshalIndent(r, "", "\t")
|
||||
}
|
||||
|
||||
// Save persists the status message to the specified status folder using the
|
||||
// sequence number. The operation consists of writing to a temporary file in the
|
||||
// same folder and moving it to the final destination for atomicity.
|
||||
func (r StatusReport) Save(statusFolder string, seqNum int) error {
|
||||
fn := fmt.Sprintf("%d.status", seqNum)
|
||||
path := filepath.Join(statusFolder, fn)
|
||||
tmpFile, err := ioutil.TempFile(statusFolder, fn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("status: failed to create temporary file: %v", err)
|
||||
}
|
||||
tmpFile.Close()
|
||||
|
||||
b, err := r.marshal()
|
||||
if err != nil {
|
||||
return fmt.Errorf("status: failed to marshal into json: %v", err)
|
||||
}
|
||||
if err := ioutil.WriteFile(tmpFile.Name(), b, 0644); err != nil {
|
||||
return fmt.Errorf("status: failed to path=%s error=%v", tmpFile.Name(), err)
|
||||
}
|
||||
|
||||
if err := os.Rename(tmpFile.Name(), path); err != nil {
|
||||
return fmt.Errorf("status: failed to move to path=%s error=%v", path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// reportStatus saves operation status to the status file for the extension
|
||||
// handler with the optional given message, if the given cmd requires reporting
|
||||
// status.
|
||||
//
|
||||
// If an error occurs reporting the status, it will be logged and returned.
|
||||
func reportStatus(ctx *log.Context, hEnv vmextension.HandlerEnvironment, seqNum int, t status.Type, c cmd, msg string) error {
|
||||
func reportStatus(ctx *log.Context, hEnv HandlerEnvironment, seqNum int, t Type, c cmd, msg string) error {
|
||||
if !c.shouldReportStatus {
|
||||
ctx.Log("status", "not reported for operation (by design)")
|
||||
return nil
|
||||
}
|
||||
s := status.NewStatus(t, c.name, statusMsg(c, t, msg))
|
||||
s := NewStatus(t, c.name, statusMsg(c, t, msg))
|
||||
if err := s.Save(hEnv.HandlerEnvironment.StatusFolder, seqNum); err != nil {
|
||||
ctx.Log("event", "failed to save handler status", "error", err)
|
||||
return errors.Wrap(err, "failed to save handler status")
|
||||
|
@ -25,19 +102,39 @@ func reportStatus(ctx *log.Context, hEnv vmextension.HandlerEnvironment, seqNum
|
|||
return nil
|
||||
}
|
||||
|
||||
// readStatus loads current status file in StatusReport
|
||||
func readStatus(ctx *log.Context, hEnv HandlerEnvironment, seqNum int) (Type, error) {
|
||||
fileName := fmt.Sprintf("%d.status", seqNum)
|
||||
path := filepath.Join(hEnv.HandlerEnvironment.StatusFolder, fileName)
|
||||
buffer, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error reading status file %s: %v", path, err)
|
||||
}
|
||||
|
||||
var statusReport StatusReport
|
||||
if err := json.Unmarshal(buffer, &statusReport); err != nil {
|
||||
return "", fmt.Errorf("error parsing json: %v", err)
|
||||
}
|
||||
|
||||
if len(statusReport) != 1 {
|
||||
return "", fmt.Errorf("wrong statusReport count. expected:1, got:%d", len(statusReport))
|
||||
}
|
||||
return statusReport[0].Status.Status, nil
|
||||
}
|
||||
|
||||
// statusMsg creates the reported status message based on the provided operation
|
||||
// type and the given message string.
|
||||
//
|
||||
// A message will be generated for empty string. For error status, pass the
|
||||
// error message.
|
||||
func statusMsg(c cmd, t status.Type, msg string) string {
|
||||
func statusMsg(c cmd, t Type, msg string) string {
|
||||
s := c.name
|
||||
switch t {
|
||||
case status.StatusSuccess:
|
||||
case StatusSuccess:
|
||||
s += " succeeded"
|
||||
case status.StatusTransitioning:
|
||||
case StatusTransitioning:
|
||||
s += " in progress"
|
||||
case status.StatusError:
|
||||
case StatusError:
|
||||
s += " failed"
|
||||
}
|
||||
|
||||
|
|
|
@ -6,28 +6,26 @@ import (
|
|||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/azure-docker-extension/pkg/vmextension"
|
||||
"github.com/Azure/azure-docker-extension/pkg/vmextension/status"
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_statusMsg(t *testing.T) {
|
||||
require.Equal(t, "Enable succeeded", statusMsg(cmdEnable, status.StatusSuccess, ""))
|
||||
require.Equal(t, "Enable succeeded: msg", statusMsg(cmdEnable, status.StatusSuccess, "msg"))
|
||||
require.Equal(t, "Enable succeeded", statusMsg(cmdEnable, StatusSuccess, ""))
|
||||
require.Equal(t, "Enable succeeded: msg", statusMsg(cmdEnable, StatusSuccess, "msg"))
|
||||
|
||||
require.Equal(t, "Enable failed", statusMsg(cmdEnable, status.StatusError, ""))
|
||||
require.Equal(t, "Enable failed: msg", statusMsg(cmdEnable, status.StatusError, "msg"))
|
||||
require.Equal(t, "Enable failed", statusMsg(cmdEnable, StatusError, ""))
|
||||
require.Equal(t, "Enable failed: msg", statusMsg(cmdEnable, StatusError, "msg"))
|
||||
|
||||
require.Equal(t, "Enable in progress", statusMsg(cmdEnable, status.StatusTransitioning, ""))
|
||||
require.Equal(t, "Enable in progress: msg", statusMsg(cmdEnable, status.StatusTransitioning, "msg"))
|
||||
require.Equal(t, "Enable in progress", statusMsg(cmdEnable, StatusTransitioning, ""))
|
||||
require.Equal(t, "Enable in progress: msg", statusMsg(cmdEnable, StatusTransitioning, "msg"))
|
||||
}
|
||||
|
||||
func Test_reportStatus_fails(t *testing.T) {
|
||||
fakeEnv := vmextension.HandlerEnvironment{}
|
||||
fakeEnv := HandlerEnvironment{}
|
||||
fakeEnv.HandlerEnvironment.StatusFolder = "/non-existing/dir/"
|
||||
|
||||
err := reportStatus(log.NewContext(log.NewNopLogger()), fakeEnv, 1, status.StatusSuccess, cmdEnable, "")
|
||||
err := reportStatus(log.NewContext(log.NewNopLogger()), fakeEnv, 1, StatusSuccess, cmdEnable, "")
|
||||
require.NotNil(t, err)
|
||||
require.Contains(t, err.Error(), "failed to save handler status")
|
||||
}
|
||||
|
@ -37,10 +35,10 @@ func Test_reportStatus_fileExists(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
fakeEnv := vmextension.HandlerEnvironment{}
|
||||
fakeEnv := HandlerEnvironment{}
|
||||
fakeEnv.HandlerEnvironment.StatusFolder = tmpDir
|
||||
|
||||
require.Nil(t, reportStatus(log.NewContext(log.NewNopLogger()), fakeEnv, 1, status.StatusError, cmdEnable, "FOO ERROR"))
|
||||
require.Nil(t, reportStatus(log.NewContext(log.NewNopLogger()), fakeEnv, 1, StatusError, cmdEnable, "FOO ERROR"))
|
||||
|
||||
path := filepath.Join(tmpDir, "1.status")
|
||||
b, err := ioutil.ReadFile(path)
|
||||
|
@ -54,9 +52,9 @@ func Test_reportStatus_checksIfShouldBeReported(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
fakeEnv := vmextension.HandlerEnvironment{}
|
||||
fakeEnv := HandlerEnvironment{}
|
||||
fakeEnv.HandlerEnvironment.StatusFolder = tmpDir
|
||||
require.Nil(t, reportStatus(log.NewContext(log.NewNopLogger()), fakeEnv, 2, status.StatusSuccess, c, ""))
|
||||
require.Nil(t, reportStatus(log.NewContext(log.NewNopLogger()), fakeEnv, 2, StatusSuccess, c, ""))
|
||||
|
||||
fp := filepath.Join(tmpDir, "2.status")
|
||||
_, err = os.Stat(fp) // check if the .status file is there
|
||||
|
|
|
@ -4,8 +4,11 @@ set -euo pipefail
|
|||
readonly SCRIPT_DIR=$(dirname "$0")
|
||||
readonly LOG_DIR="/var/log/azure/custom-script"
|
||||
readonly LOG_FILE=handler.log
|
||||
readonly HANDLER_BIN="custom-script-extension"
|
||||
|
||||
readonly ARCHITECTURE=$(uname -p)
|
||||
HANDLER_BIN="custom-script-extension"
|
||||
if [ $ARCHITECTURE == "arm64" ] || [ $ARCHITECTURE == "aarch64" ]; then
|
||||
HANDLER_BIN="custom-script-extension-arm64";
|
||||
fi
|
||||
|
||||
# status_file returns the .status file path we are supposed to write
|
||||
# by determining the highest sequence number from ./config/*.settings files.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<ExtensionImage xmlns="http://schemas.microsoft.com/windowsazure">
|
||||
<ProviderNameSpace>Microsoft.Azure.Extensions</ProviderNameSpace>
|
||||
<Type>CustomScript</Type>
|
||||
<Version>2.1.5</Version>
|
||||
<Version>2.1.6</Version>
|
||||
<Label>Microsoft Azure Custom Script Extension for Linux Virtual Machines</Label>
|
||||
<HostingResources>VmRole</HostingResources>
|
||||
<MediaLink></MediaLink>
|
||||
|
|
|
@ -8,11 +8,11 @@ import (
|
|||
|
||||
"path/filepath"
|
||||
|
||||
"fmt"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
import "fmt"
|
||||
|
||||
const testDataDir = "testdata"
|
||||
|
||||
// BOM test files and their checksums in case of loss of BOM bytes
|
||||
|
|
|
@ -1,202 +0,0 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2016 Microsoft Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
82
vendor/github.com/Azure/azure-docker-extension/pkg/vmextension/status/status.go
сгенерированный
поставляемый
82
vendor/github.com/Azure/azure-docker-extension/pkg/vmextension/status/status.go
сгенерированный
поставляемый
|
@ -1,82 +0,0 @@
|
|||
package status
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
type StatusReport []StatusItem
|
||||
|
||||
type StatusItem struct {
|
||||
Version float64 `json:"version"`
|
||||
TimestampUTC string `json:"timestampUTC"`
|
||||
Status Status `json:"status"`
|
||||
}
|
||||
|
||||
type Type string
|
||||
|
||||
const (
|
||||
StatusTransitioning Type = "transitioning"
|
||||
StatusError Type = "error"
|
||||
StatusSuccess Type = "success"
|
||||
)
|
||||
|
||||
type Status struct {
|
||||
Operation string `json:"operation"`
|
||||
Status Type `json:"status"`
|
||||
FormattedMessage FormattedMessage `json:"formattedMessage"`
|
||||
}
|
||||
type FormattedMessage struct {
|
||||
Lang string `json:"lang"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func NewStatus(t Type, operation, message string) StatusReport {
|
||||
return []StatusItem{
|
||||
{
|
||||
Version: 1.0,
|
||||
TimestampUTC: time.Now().UTC().Format(time.RFC3339),
|
||||
Status: Status{
|
||||
Operation: operation,
|
||||
Status: t,
|
||||
FormattedMessage: FormattedMessage{
|
||||
Lang: "en",
|
||||
Message: message},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r StatusReport) marshal() ([]byte, error) {
|
||||
return json.MarshalIndent(r, "", "\t")
|
||||
}
|
||||
|
||||
// Save persists the status message to the specified status folder using the
|
||||
// sequence number. The operation consists of writing to a temporary file in the
|
||||
// same folder and moving it to the final destination for atomicity.
|
||||
func (r StatusReport) Save(statusFolder string, seqNum int) error {
|
||||
fn := fmt.Sprintf("%d.status", seqNum)
|
||||
path := filepath.Join(statusFolder, fn)
|
||||
tmpFile, err := ioutil.TempFile(statusFolder, fn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("status: failed to create temporary file: %v", err)
|
||||
}
|
||||
tmpFile.Close()
|
||||
|
||||
b, err := r.marshal()
|
||||
if err != nil {
|
||||
return fmt.Errorf("status: failed to marshal into json: %v", err)
|
||||
}
|
||||
if err := ioutil.WriteFile(tmpFile.Name(), b, 0644); err != nil {
|
||||
return fmt.Errorf("status: failed to path=%s error=%v", tmpFile.Name(), err)
|
||||
}
|
||||
|
||||
if err := os.Rename(tmpFile.Name(), path); err != nil {
|
||||
return fmt.Errorf("status: failed to move to path=%s error=%v", path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
21
vendor/github.com/Azure/azure-extension-foundation/LICENSE
сгенерированный
поставляемый
Normal file
21
vendor/github.com/Azure/azure-extension-foundation/LICENSE
сгенерированный
поставляемый
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
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
|
19
vendor/github.com/Azure/azure-extension-foundation/errorhelper/errorhelper.go
сгенерированный
поставляемый
Normal file
19
vendor/github.com/Azure/azure-extension-foundation/errorhelper/errorhelper.go
сгенерированный
поставляемый
Normal file
|
@ -0,0 +1,19 @@
|
|||
package errorhelper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
func AddStackToError(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
stackString := string(debug.Stack())
|
||||
return fmt.Errorf("%+v\nCallStack: %s", err, stackString)
|
||||
}
|
||||
|
||||
func NewErrorWithStack(errString string) error {
|
||||
stackString := string(debug.Stack())
|
||||
return fmt.Errorf("%s\nCallStack: %s", errString, stackString)
|
||||
}
|
214
vendor/github.com/Azure/azure-extension-foundation/httputil/httputil.go
сгенерированный
поставляемый
Normal file
214
vendor/github.com/Azure/azure-extension-foundation/httputil/httputil.go
сгенерированный
поставляемый
Normal file
|
@ -0,0 +1,214 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package httputil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"github.com/Azure/azure-extension-foundation/errorhelper"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
OperationGet = "GET"
|
||||
OperationPost = "POST"
|
||||
OperationDelete = "DELETE"
|
||||
OperationPut = "PUT"
|
||||
)
|
||||
|
||||
type HttpClient interface {
|
||||
Get(url string, headers map[string]string) (responseCode int, body []byte, err error)
|
||||
Post(url string, headers map[string]string, payload []byte) (responseCode int, body []byte, err error)
|
||||
Put(url string, headers map[string]string, payload []byte) (responseCode int, body []byte, err error)
|
||||
Delete(url string, headers map[string]string, payload []byte) (responseCode int, body []byte, err error)
|
||||
}
|
||||
|
||||
// for testing
|
||||
type httpClientInterface interface {
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
httpClient httpClientInterface
|
||||
retryBehavior RetryBehavior
|
||||
}
|
||||
|
||||
type RetryBehavior = func(statusCode int, i int) bool
|
||||
|
||||
// return false to end retries
|
||||
// i starts from 1 keeps getting incremented while function returns true
|
||||
|
||||
var NoRetry RetryBehavior = func(statusCode int, i int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
var LinearRetryThrice RetryBehavior = func(statusCode int, i int) bool {
|
||||
if !isTransientHttpStatusCode(statusCode) {
|
||||
return false
|
||||
}
|
||||
time.Sleep(time.Second * 3)
|
||||
if i < 3 {
|
||||
return true // retry if count < 3
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// The default retry behavior is 5 retries with exponential back-off with a maximum wait time of 60 seconds
|
||||
var DefaultRetryBehavior RetryBehavior = func(statusCode int, i int) bool {
|
||||
if !isTransientHttpStatusCode(statusCode) {
|
||||
return false
|
||||
}
|
||||
delay := time.Second * time.Duration(2^(i))
|
||||
const maxDelay time.Duration = 60 * time.Second
|
||||
|
||||
if delay > maxDelay {
|
||||
delay = maxDelay
|
||||
}
|
||||
time.Sleep(delay)
|
||||
if i < 5 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isTransientHttpStatusCode(statusCode int) bool {
|
||||
switch statusCode {
|
||||
case
|
||||
http.StatusRequestTimeout, // 408
|
||||
http.StatusTooManyRequests, // 429
|
||||
http.StatusInternalServerError, // 500
|
||||
http.StatusBadGateway, // 502
|
||||
http.StatusServiceUnavailable, // 503
|
||||
http.StatusGatewayTimeout: // 504
|
||||
return true // timeout and too many requests
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func IsSuccessStatusCode(statusCode int) bool {
|
||||
switch statusCode {
|
||||
case 200, 201:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func NewSecureHttpClient(retryBehavior RetryBehavior) HttpClient {
|
||||
if retryBehavior == nil {
|
||||
panic("Retry policy must be specified")
|
||||
}
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
Renegotiation: tls.RenegotiateFreelyAsClient,
|
||||
}
|
||||
|
||||
transport := &http.Transport{TLSClientConfig: tlsConfig}
|
||||
httpClient := &http.Client{Transport: transport}
|
||||
return &Client{httpClient, retryBehavior}
|
||||
}
|
||||
|
||||
func NewSecureHttpClientWithCertificates(certificate string, key string, retryBehavior RetryBehavior) HttpClient {
|
||||
if retryBehavior == nil {
|
||||
panic("Retry policy must be specified")
|
||||
}
|
||||
|
||||
cert, err := tls.LoadX509KeyPair(certificate, key)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
Renegotiation: tls.RenegotiateFreelyAsClient,
|
||||
}
|
||||
|
||||
transport := &http.Transport{TLSClientConfig: tlsConfig}
|
||||
httpClient := &http.Client{Transport: transport}
|
||||
return &Client{httpClient, retryBehavior}
|
||||
}
|
||||
|
||||
func NewInsecureHttpClientWithCertificates(certificate string, key string, retryBehavior RetryBehavior) HttpClient {
|
||||
if retryBehavior == nil {
|
||||
panic("Retry policy must be specified")
|
||||
}
|
||||
|
||||
cert, err := tls.LoadX509KeyPair(certificate, key)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
InsecureSkipVerify: true,
|
||||
Renegotiation: tls.RenegotiateFreelyAsClient,
|
||||
}
|
||||
|
||||
transport := &http.Transport{TLSClientConfig: tlsConfig}
|
||||
httpClient := &http.Client{Transport: transport}
|
||||
|
||||
return &Client{httpClient, retryBehavior}
|
||||
}
|
||||
|
||||
// Get issues a get request
|
||||
func (client *Client) Get(url string, headers map[string]string) (responseCode int, body []byte, err error) {
|
||||
return client.issueRequest(OperationGet, url, headers, nil)
|
||||
}
|
||||
|
||||
// Post issues a post request
|
||||
func (client *Client) Post(url string, headers map[string]string, payload []byte) (responseCode int, body []byte, err error) {
|
||||
return client.issueRequest(OperationPost, url, headers, bytes.NewBuffer(payload))
|
||||
}
|
||||
|
||||
// Put issues a put request
|
||||
func (client *Client) Put(url string, headers map[string]string, payload []byte) (responseCode int, body []byte, err error) {
|
||||
return client.issueRequest(OperationPut, url, headers, bytes.NewBuffer(payload))
|
||||
}
|
||||
|
||||
// Delete issues a delete request
|
||||
func (client *Client) Delete(url string, headers map[string]string, payload []byte) (responseCode int, body []byte, err error) {
|
||||
return client.issueRequest(OperationDelete, url, headers, bytes.NewBuffer(payload))
|
||||
}
|
||||
|
||||
func (client *Client) issueRequest(operation string, url string, headers map[string]string, payload *bytes.Buffer) (int, []byte, error) {
|
||||
request, err := http.NewRequest(operation, url, nil)
|
||||
if payload != nil && payload.Len() != 0 {
|
||||
request, err = http.NewRequest(operation, url, payload)
|
||||
}
|
||||
|
||||
for key, value := range headers {
|
||||
request.Header.Add(key, value)
|
||||
}
|
||||
|
||||
res, err := client.httpClient.Do(request)
|
||||
|
||||
if err == nil && IsSuccessStatusCode(res.StatusCode) {
|
||||
// no need to retry
|
||||
} else if err == nil && res != nil {
|
||||
// there was no error, so look at the status code to retry
|
||||
for i := 1; client.retryBehavior(res.StatusCode, i); i++ {
|
||||
res, err = client.httpClient.Do(request)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return -1, nil, errorhelper.AddStackToError(err)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
res.Body.Close()
|
||||
code := res.StatusCode
|
||||
if err != nil {
|
||||
return -1, nil, errorhelper.AddStackToError(err)
|
||||
}
|
||||
|
||||
return code, body, nil
|
||||
}
|
26
vendor/github.com/Azure/azure-extension-foundation/httputil/mockclient.go
сгенерированный
поставляемый
Normal file
26
vendor/github.com/Azure/azure-extension-foundation/httputil/mockclient.go
сгенерированный
поставляемый
Normal file
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package httputil
|
||||
|
||||
type MockHttpClient struct {
|
||||
// overwrite these methods to get the desired output
|
||||
Getfunc func(url string, headers map[string]string) (responseCode int, body []byte, err error)
|
||||
Postfunc func(url string, headers map[string]string, payload []byte) (responseCode int, body []byte, err error)
|
||||
Putfunc func(url string, headers map[string]string, payload []byte) (responseCode int, body []byte, err error)
|
||||
Deletefunc func(url string, headers map[string]string, payload []byte) (responseCode int, body []byte, err error)
|
||||
}
|
||||
|
||||
func (client *MockHttpClient) Get(url string, headers map[string]string) (responseCode int, body []byte, err error) {
|
||||
return client.Getfunc(url, headers)
|
||||
}
|
||||
|
||||
func (client *MockHttpClient) Post(url string, headers map[string]string, payload []byte) (responseCode int, body []byte, err error) {
|
||||
return client.Postfunc(url, headers, payload)
|
||||
}
|
||||
func (client *MockHttpClient) Put(url string, headers map[string]string, payload []byte) (responseCode int, body []byte, err error) {
|
||||
return client.Putfunc(url, headers, payload)
|
||||
}
|
||||
func (client *MockHttpClient) Delete(url string, headers map[string]string, payload []byte) (responseCode int, body []byte, err error) {
|
||||
return client.Deletefunc(url, headers, payload)
|
||||
}
|
129
vendor/github.com/Azure/azure-extension-foundation/msi/msi.go
сгенерированный
поставляемый
Normal file
129
vendor/github.com/Azure/azure-extension-foundation/msi/msi.go
сгенерированный
поставляемый
Normal file
|
@ -0,0 +1,129 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package msi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/Azure/azure-extension-foundation/errorhelper"
|
||||
"github.com/Azure/azure-extension-foundation/httputil"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
metadataIdentityURL = "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01"
|
||||
|
||||
clientIdQueryParam = "client_id"
|
||||
objectIdQueryParam = "object_id"
|
||||
resourceQueryParam = "resource"
|
||||
|
||||
armResourceUri = "https://management.core.windows.net/"
|
||||
)
|
||||
|
||||
type Msi struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
ClientID string `json:"client_id"`
|
||||
ExpiresIn string `json:"expires_in"`
|
||||
ExpiresOn string `json:"expires_on"` // expressed in seconds from epoch
|
||||
ExtExpiresIn string `json:"ext_expires_in"`
|
||||
NotBefore string `json:"not_before"`
|
||||
Resource string `json:"resource"`
|
||||
TokenType string `json:"token_type"`
|
||||
}
|
||||
|
||||
type MsiProvider interface {
|
||||
GetMsi() (Msi, error)
|
||||
GetMsiForResource(targetResource string) (Msi, error)
|
||||
GetMsiUsingClientId(clientId string, targetResource string) (Msi, error)
|
||||
GetMsiUsingObjectId(objectId string, targetResource string) (Msi, error)
|
||||
}
|
||||
|
||||
type provider struct {
|
||||
httpClient httputil.HttpClient
|
||||
}
|
||||
|
||||
func NewMsiProvider(client httputil.HttpClient) provider {
|
||||
return provider{httpClient: client}
|
||||
}
|
||||
|
||||
func (p *provider) getMsiHelper(queryParams map[string]string) (*Msi, error) {
|
||||
var msi = Msi{}
|
||||
requestUrl, err := url.Parse(metadataIdentityURL)
|
||||
if err != nil {
|
||||
return &msi, err
|
||||
}
|
||||
urlQuery := requestUrl.Query()
|
||||
for key, value := range queryParams {
|
||||
urlQuery.Add(key, value)
|
||||
}
|
||||
requestUrl.RawQuery = urlQuery.Encode()
|
||||
|
||||
code, body, err := p.httpClient.Get(requestUrl.String(), map[string]string{"Metadata": "true"})
|
||||
if err != nil {
|
||||
return &msi, err
|
||||
}
|
||||
|
||||
if code != 200 {
|
||||
return &msi, errorhelper.AddStackToError(fmt.Errorf("unable to get msi, metadata service response code %v", code))
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &msi)
|
||||
if err != nil {
|
||||
return &msi, errorhelper.AddStackToError(fmt.Errorf("unable to deserialize metadata service response"))
|
||||
}
|
||||
return &msi, nil
|
||||
}
|
||||
|
||||
func (p *provider) GetMsi() (Msi, error) {
|
||||
msi, err := p.getMsiHelper(map[string]string{resourceQueryParam: armResourceUri})
|
||||
return *msi, err
|
||||
}
|
||||
|
||||
func (p *provider) GetMsiForResource(targetResource string) (Msi, error) {
|
||||
msi, err := p.getMsiHelper(map[string]string{resourceQueryParam: targetResource})
|
||||
return *msi, err
|
||||
}
|
||||
|
||||
func (p *provider) GetMsiUsingClientId(clientId string, targetResource string) (Msi, error) {
|
||||
msi, err := p.getMsiHelper(map[string]string{clientIdQueryParam: clientId, resourceQueryParam: targetResource})
|
||||
return *msi, err
|
||||
}
|
||||
|
||||
func (p *provider) GetMsiUsingObjectId(objectId string, targetResource string) (Msi, error) {
|
||||
msi, err := p.getMsiHelper(map[string]string{objectIdQueryParam: objectId, resourceQueryParam: targetResource})
|
||||
return *msi, err
|
||||
}
|
||||
|
||||
// check expiry of MSI token based on time
|
||||
func (msi *Msi) IsMsiTokenExpired() (bool, error) {
|
||||
expiryTime, err := msi.GetExpiryTime()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Consider token expired 2 minutes before expiry time
|
||||
expiryTime = expiryTime.Add(-2 * time.Minute)
|
||||
|
||||
if time.Now().After(expiryTime) {
|
||||
return true, nil
|
||||
} else {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (msi *Msi) GetExpiryTime() (time.Time, error) {
|
||||
expiryTimeInSeconds, err := strconv.ParseInt(msi.ExpiresOn, 10, 64)
|
||||
if err != nil {
|
||||
return time.Unix(0, 0), err
|
||||
}
|
||||
expiryTime := time.Unix(expiryTimeInSeconds, 0)
|
||||
return expiryTime, nil
|
||||
}
|
||||
|
||||
func (msi *Msi) GetJson() (string, error) {
|
||||
jsonBytes, err := json.Marshal(msi)
|
||||
return string(jsonBytes[:]), err
|
||||
}
|
|
@ -7,11 +7,12 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"net"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
@ -42,7 +43,6 @@ func GetMux() *mux.Router {
|
|||
r.HandleFunc(`/bytes/{n:[\d]+}`, BytesHandler).Methods("GET")
|
||||
r.HandleFunc(`/delay/{n:\d+(\.\d+)?}`, DelayHandler).Methods("GET")
|
||||
r.HandleFunc(`/stream/{n:[\d]+}`, StreamHandler).Methods("GET")
|
||||
r.HandleFunc(`/dos2unix`, Dos2UnixHandler).Methods("GET")
|
||||
r.HandleFunc(`/drip`, DripHandler).Methods("GET").Queries(
|
||||
"numbytes", `{numbytes:\d+}`,
|
||||
"duration", `{duration:\d+(\.\d+)?}`)
|
||||
|
@ -62,10 +62,6 @@ func IPHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
func Dos2UnixHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("#!/bin/sh\r\necho 'Hello, world!'\r\n"))
|
||||
}
|
||||
|
||||
// UserAgentHandler returns user agent.
|
||||
func UserAgentHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if err := writeJSON(w, userAgentResponse{r.UserAgent()}); err != nil {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
language: go
|
||||
go_import_path: github.com/pkg/errors
|
||||
go:
|
||||
- 1.4.3
|
||||
- 1.5.4
|
||||
- 1.6.2
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
- tip
|
||||
|
||||
script:
|
||||
- go test -v ./...
|
||||
- make check
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
PKGS := github.com/pkg/errors
|
||||
SRCDIRS := $(shell go list -f '{{.Dir}}' $(PKGS))
|
||||
GO := go
|
||||
|
||||
check: test vet gofmt misspell unconvert staticcheck ineffassign unparam
|
||||
|
||||
test:
|
||||
$(GO) test $(PKGS)
|
||||
|
||||
vet: | test
|
||||
$(GO) vet $(PKGS)
|
||||
|
||||
staticcheck:
|
||||
$(GO) get honnef.co/go/tools/cmd/staticcheck
|
||||
staticcheck -checks all $(PKGS)
|
||||
|
||||
misspell:
|
||||
$(GO) get github.com/client9/misspell/cmd/misspell
|
||||
misspell \
|
||||
-locale GB \
|
||||
-error \
|
||||
*.md *.go
|
||||
|
||||
unconvert:
|
||||
$(GO) get github.com/mdempsky/unconvert
|
||||
unconvert -v $(PKGS)
|
||||
|
||||
ineffassign:
|
||||
$(GO) get github.com/gordonklaus/ineffassign
|
||||
find $(SRCDIRS) -name '*.go' | xargs ineffassign
|
||||
|
||||
pedantic: check errcheck
|
||||
|
||||
unparam:
|
||||
$(GO) get mvdan.cc/unparam
|
||||
unparam ./...
|
||||
|
||||
errcheck:
|
||||
$(GO) get github.com/kisielk/errcheck
|
||||
errcheck $(PKGS)
|
||||
|
||||
gofmt:
|
||||
@echo Checking code is gofmted
|
||||
@test -z "$(shell gofmt -s -l -d -e $(SRCDIRS) | tee /dev/stderr)"
|
|
@ -1,7 +1,9 @@
|
|||
# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors)
|
||||
# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge)
|
||||
|
||||
Package errors provides simple error handling primitives.
|
||||
|
||||
`go get github.com/pkg/errors`
|
||||
|
||||
The traditional error handling idiom in Go is roughly akin to
|
||||
```go
|
||||
if err != nil {
|
||||
|
@ -39,12 +41,19 @@ default:
|
|||
|
||||
[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors).
|
||||
|
||||
## Roadmap
|
||||
|
||||
With the upcoming [Go2 error proposals](https://go.googlesource.com/proposal/+/master/design/go2draft.md) this package is moving into maintenance mode. The roadmap for a 1.0 release is as follows:
|
||||
|
||||
- 0.9. Remove pre Go 1.9 and Go 1.10 support, address outstanding pull requests (if possible)
|
||||
- 1.0. Final release.
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high.
|
||||
Because of the Go2 errors changes, this package is not accepting proposals for new functionality. With that said, we welcome pull requests, bug fixes and issue reports.
|
||||
|
||||
Before proposing a change, please discuss your change by raising an issue.
|
||||
Before sending a PR, please discuss your change by raising an issue.
|
||||
|
||||
## Licence
|
||||
## License
|
||||
|
||||
BSD-2-Clause
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
// return err
|
||||
// }
|
||||
//
|
||||
// which applied recursively up the call stack results in error reports
|
||||
// which when applied recursively up the call stack results in error reports
|
||||
// without context or debugging information. The errors package allows
|
||||
// programmers to add context to the failure path in their code in a way
|
||||
// that does not destroy the original value of the error.
|
||||
|
@ -14,13 +14,19 @@
|
|||
// Adding context to an error
|
||||
//
|
||||
// The errors.Wrap function returns a new error that adds context to the
|
||||
// original error. For example
|
||||
// original error by recording a stack trace at the point Wrap is called,
|
||||
// together with the supplied message. For example
|
||||
//
|
||||
// _, err := ioutil.ReadAll(r)
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "read failed")
|
||||
// }
|
||||
//
|
||||
// If additional control is required, the errors.WithStack and
|
||||
// errors.WithMessage functions destructure errors.Wrap into its component
|
||||
// operations: annotating an error with a stack trace and with a message,
|
||||
// respectively.
|
||||
//
|
||||
// Retrieving the cause of an error
|
||||
//
|
||||
// Using errors.Wrap constructs a stack of errors, adding context to the
|
||||
|
@ -28,12 +34,12 @@
|
|||
// to reverse the operation of errors.Wrap to retrieve the original error
|
||||
// for inspection. Any error value which implements this interface
|
||||
//
|
||||
// type Causer interface {
|
||||
// type causer interface {
|
||||
// Cause() error
|
||||
// }
|
||||
//
|
||||
// can be inspected by errors.Cause. errors.Cause will recursively retrieve
|
||||
// the topmost error which does not implement causer, which is assumed to be
|
||||
// the topmost error that does not implement causer, which is assumed to be
|
||||
// the original cause. For example:
|
||||
//
|
||||
// switch err := errors.Cause(err).(type) {
|
||||
|
@ -43,13 +49,16 @@
|
|||
// // unknown error
|
||||
// }
|
||||
//
|
||||
// Although the causer interface is not exported by this package, it is
|
||||
// considered a part of its stable public interface.
|
||||
//
|
||||
// Formatted printing of errors
|
||||
//
|
||||
// All error values returned from this package implement fmt.Formatter and can
|
||||
// be formatted by the fmt package. The following verbs are supported
|
||||
// be formatted by the fmt package. The following verbs are supported:
|
||||
//
|
||||
// %s print the error. If the error has a Cause it will be
|
||||
// printed recursively
|
||||
// printed recursively.
|
||||
// %v see %s
|
||||
// %+v extended format. Each Frame of the error's StackTrace will
|
||||
// be printed in detail.
|
||||
|
@ -57,13 +66,13 @@
|
|||
// Retrieving the stack trace of an error or wrapper
|
||||
//
|
||||
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
|
||||
// invoked. This information can be retrieved with the following interface.
|
||||
// invoked. This information can be retrieved with the following interface:
|
||||
//
|
||||
// type stackTracer interface {
|
||||
// StackTrace() errors.StackTrace
|
||||
// }
|
||||
//
|
||||
// Where errors.StackTrace is defined as
|
||||
// The returned errors.StackTrace type is defined as
|
||||
//
|
||||
// type StackTrace []Frame
|
||||
//
|
||||
|
@ -73,10 +82,13 @@
|
|||
//
|
||||
// if err, ok := err.(stackTracer); ok {
|
||||
// for _, f := range err.StackTrace() {
|
||||
// fmt.Printf("%+s:%d", f)
|
||||
// fmt.Printf("%+s:%d\n", f, f)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Although the stackTracer interface is not exported by this package, it is
|
||||
// considered a part of its stable public interface.
|
||||
//
|
||||
// See the documentation for Frame.Format for more details.
|
||||
package errors
|
||||
|
||||
|
@ -85,68 +97,77 @@ import (
|
|||
"io"
|
||||
)
|
||||
|
||||
// _error is an error implementation returned by New and Errorf
|
||||
// that implements its own fmt.Formatter.
|
||||
type _error struct {
|
||||
msg string
|
||||
*stack
|
||||
}
|
||||
|
||||
func (e _error) Error() string { return e.msg }
|
||||
|
||||
func (e _error) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
io.WriteString(s, e.msg)
|
||||
fmt.Fprintf(s, "%+v", e.StackTrace())
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
io.WriteString(s, e.msg)
|
||||
}
|
||||
}
|
||||
|
||||
// New returns an error with the supplied message.
|
||||
// New also records the stack trace at the point it was called.
|
||||
func New(message string) error {
|
||||
return _error{
|
||||
message,
|
||||
callers(),
|
||||
return &fundamental{
|
||||
msg: message,
|
||||
stack: callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Errorf formats according to a format specifier and returns the string
|
||||
// as a value that satisfies error.
|
||||
// Errorf also records the stack trace at the point it was called.
|
||||
func Errorf(format string, args ...interface{}) error {
|
||||
return _error{
|
||||
fmt.Sprintf(format, args...),
|
||||
return &fundamental{
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
stack: callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// fundamental is an error that has a message and a stack, but no caller.
|
||||
type fundamental struct {
|
||||
msg string
|
||||
*stack
|
||||
}
|
||||
|
||||
func (f *fundamental) Error() string { return f.msg }
|
||||
|
||||
func (f *fundamental) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
io.WriteString(s, f.msg)
|
||||
f.stack.Format(s, verb)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
io.WriteString(s, f.msg)
|
||||
case 'q':
|
||||
fmt.Fprintf(s, "%q", f.msg)
|
||||
}
|
||||
}
|
||||
|
||||
// WithStack annotates err with a stack trace at the point WithStack was called.
|
||||
// If err is nil, WithStack returns nil.
|
||||
func WithStack(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
type cause struct {
|
||||
cause error
|
||||
msg string
|
||||
}
|
||||
|
||||
func (c cause) Error() string { return fmt.Sprintf("%s: %v", c.msg, c.Cause()) }
|
||||
func (c cause) Cause() error { return c.cause }
|
||||
|
||||
// wrapper is an error implementation returned by Wrap and Wrapf
|
||||
// that implements its own fmt.Formatter.
|
||||
type wrapper struct {
|
||||
cause
|
||||
type withStack struct {
|
||||
error
|
||||
*stack
|
||||
}
|
||||
|
||||
func (w wrapper) Format(s fmt.State, verb rune) {
|
||||
func (w *withStack) Cause() error { return w.error }
|
||||
|
||||
// Unwrap provides compatibility for Go 1.13 error chains.
|
||||
func (w *withStack) Unwrap() error { return w.error }
|
||||
|
||||
func (w *withStack) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
fmt.Fprintf(s, "%+v\n", w.Cause())
|
||||
io.WriteString(s, w.msg)
|
||||
fmt.Fprintf(s, "%+v", w.StackTrace())
|
||||
fmt.Fprintf(s, "%+v", w.Cause())
|
||||
w.stack.Format(s, verb)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
|
@ -157,33 +178,86 @@ func (w wrapper) Format(s fmt.State, verb rune) {
|
|||
}
|
||||
}
|
||||
|
||||
// Wrap returns an error annotating err with message.
|
||||
// Wrap returns an error annotating err with a stack trace
|
||||
// at the point Wrap is called, and the supplied message.
|
||||
// If err is nil, Wrap returns nil.
|
||||
func Wrap(err error, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return wrapper{
|
||||
cause: cause{
|
||||
cause: err,
|
||||
msg: message,
|
||||
},
|
||||
stack: callers(),
|
||||
err = &withMessage{
|
||||
cause: err,
|
||||
msg: message,
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapf returns an error annotating err with the format specifier.
|
||||
// Wrapf returns an error annotating err with a stack trace
|
||||
// at the point Wrapf is called, and the format specifier.
|
||||
// If err is nil, Wrapf returns nil.
|
||||
func Wrapf(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return wrapper{
|
||||
cause: cause{
|
||||
cause: err,
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
},
|
||||
stack: callers(),
|
||||
err = &withMessage{
|
||||
cause: err,
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// WithMessage annotates err with a new message.
|
||||
// If err is nil, WithMessage returns nil.
|
||||
func WithMessage(err error, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withMessage{
|
||||
cause: err,
|
||||
msg: message,
|
||||
}
|
||||
}
|
||||
|
||||
// WithMessagef annotates err with the format specifier.
|
||||
// If err is nil, WithMessagef returns nil.
|
||||
func WithMessagef(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withMessage{
|
||||
cause: err,
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
type withMessage struct {
|
||||
cause error
|
||||
msg string
|
||||
}
|
||||
|
||||
func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
|
||||
func (w *withMessage) Cause() error { return w.cause }
|
||||
|
||||
// Unwrap provides compatibility for Go 1.13 error chains.
|
||||
func (w *withMessage) Unwrap() error { return w.cause }
|
||||
|
||||
func (w *withMessage) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
fmt.Fprintf(s, "%+v\n", w.Cause())
|
||||
io.WriteString(s, w.msg)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's', 'q':
|
||||
io.WriteString(s, w.Error())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,7 +265,7 @@ func Wrapf(err error, format string, args ...interface{}) error {
|
|||
// An error value has a cause if it implements the following
|
||||
// interface:
|
||||
//
|
||||
// type Causer interface {
|
||||
// type causer interface {
|
||||
// Cause() error
|
||||
// }
|
||||
//
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
// +build go1.13
|
||||
|
||||
package errors
|
||||
|
||||
import (
|
||||
stderrors "errors"
|
||||
)
|
||||
|
||||
// Is reports whether any error in err's chain matches target.
|
||||
//
|
||||
// The chain consists of err itself followed by the sequence of errors obtained by
|
||||
// repeatedly calling Unwrap.
|
||||
//
|
||||
// An error is considered to match a target if it is equal to that target or if
|
||||
// it implements a method Is(error) bool such that Is(target) returns true.
|
||||
func Is(err, target error) bool { return stderrors.Is(err, target) }
|
||||
|
||||
// As finds the first error in err's chain that matches target, and if so, sets
|
||||
// target to that error value and returns true.
|
||||
//
|
||||
// The chain consists of err itself followed by the sequence of errors obtained by
|
||||
// repeatedly calling Unwrap.
|
||||
//
|
||||
// An error matches target if the error's concrete value is assignable to the value
|
||||
// pointed to by target, or if the error has a method As(interface{}) bool such that
|
||||
// As(target) returns true. In the latter case, the As method is responsible for
|
||||
// setting target.
|
||||
//
|
||||
// As will panic if target is not a non-nil pointer to either a type that implements
|
||||
// error, or to any interface type. As returns false if err is nil.
|
||||
func As(err error, target interface{}) bool { return stderrors.As(err, target) }
|
||||
|
||||
// Unwrap returns the result of calling the Unwrap method on err, if err's
|
||||
// type contains an Unwrap method returning error.
|
||||
// Otherwise, Unwrap returns nil.
|
||||
func Unwrap(err error) error {
|
||||
return stderrors.Unwrap(err)
|
||||
}
|
|
@ -5,10 +5,13 @@ import (
|
|||
"io"
|
||||
"path"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Frame represents a program counter inside a stack frame.
|
||||
// For historical reasons if Frame is interpreted as a uintptr
|
||||
// its value represents the program counter + 1.
|
||||
type Frame uintptr
|
||||
|
||||
// pc returns the program counter for this frame;
|
||||
|
@ -37,6 +40,15 @@ func (f Frame) line() int {
|
|||
return line
|
||||
}
|
||||
|
||||
// name returns the name of this function, if known.
|
||||
func (f Frame) name() string {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return "unknown"
|
||||
}
|
||||
return fn.Name()
|
||||
}
|
||||
|
||||
// Format formats the frame according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s source file
|
||||
|
@ -46,29 +58,24 @@ func (f Frame) line() int {
|
|||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+s path of source file relative to the compile time GOPATH
|
||||
// %+s function name and path of source file relative to the compile time
|
||||
// GOPATH separated by \n\t (<funcname>\n\t<path>)
|
||||
// %+v equivalent to %+s:%d
|
||||
func (f Frame) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 's':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
pc := f.pc()
|
||||
fn := runtime.FuncForPC(pc)
|
||||
if fn == nil {
|
||||
io.WriteString(s, "unknown")
|
||||
} else {
|
||||
file, _ := fn.FileLine(pc)
|
||||
fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
|
||||
}
|
||||
io.WriteString(s, f.name())
|
||||
io.WriteString(s, "\n\t")
|
||||
io.WriteString(s, f.file())
|
||||
default:
|
||||
io.WriteString(s, path.Base(f.file()))
|
||||
}
|
||||
case 'd':
|
||||
fmt.Fprintf(s, "%d", f.line())
|
||||
io.WriteString(s, strconv.Itoa(f.line()))
|
||||
case 'n':
|
||||
name := runtime.FuncForPC(f.pc()).Name()
|
||||
io.WriteString(s, funcname(name))
|
||||
io.WriteString(s, funcname(f.name()))
|
||||
case 'v':
|
||||
f.Format(s, 's')
|
||||
io.WriteString(s, ":")
|
||||
|
@ -76,30 +83,75 @@ func (f Frame) Format(s fmt.State, verb rune) {
|
|||
}
|
||||
}
|
||||
|
||||
// MarshalText formats a stacktrace Frame as a text string. The output is the
|
||||
// same as that of fmt.Sprintf("%+v", f), but without newlines or tabs.
|
||||
func (f Frame) MarshalText() ([]byte, error) {
|
||||
name := f.name()
|
||||
if name == "unknown" {
|
||||
return []byte(name), nil
|
||||
}
|
||||
return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil
|
||||
}
|
||||
|
||||
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
|
||||
type StackTrace []Frame
|
||||
|
||||
// Format formats the stack of Frames according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s lists source files for each Frame in the stack
|
||||
// %v lists the source file and line number for each Frame in the stack
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+v Prints filename, function, and line number for each Frame in the stack.
|
||||
func (st StackTrace) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
for _, f := range st {
|
||||
fmt.Fprintf(s, "\n%+v", f)
|
||||
io.WriteString(s, "\n")
|
||||
f.Format(s, verb)
|
||||
}
|
||||
case s.Flag('#'):
|
||||
fmt.Fprintf(s, "%#v", []Frame(st))
|
||||
default:
|
||||
fmt.Fprintf(s, "%v", []Frame(st))
|
||||
st.formatSlice(s, verb)
|
||||
}
|
||||
case 's':
|
||||
fmt.Fprintf(s, "%s", []Frame(st))
|
||||
st.formatSlice(s, verb)
|
||||
}
|
||||
}
|
||||
|
||||
// formatSlice will format this StackTrace into the given buffer as a slice of
|
||||
// Frame, only valid when called with '%s' or '%v'.
|
||||
func (st StackTrace) formatSlice(s fmt.State, verb rune) {
|
||||
io.WriteString(s, "[")
|
||||
for i, f := range st {
|
||||
if i > 0 {
|
||||
io.WriteString(s, " ")
|
||||
}
|
||||
f.Format(s, verb)
|
||||
}
|
||||
io.WriteString(s, "]")
|
||||
}
|
||||
|
||||
// stack represents a stack of program counters.
|
||||
type stack []uintptr
|
||||
|
||||
func (s *stack) Format(st fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case st.Flag('+'):
|
||||
for _, pc := range *s {
|
||||
f := Frame(pc)
|
||||
fmt.Fprintf(st, "\n%+v", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stack) StackTrace() StackTrace {
|
||||
f := make([]Frame, len(*s))
|
||||
for i := 0; i < len(f); i++ {
|
||||
|
@ -123,43 +175,3 @@ func funcname(name string) string {
|
|||
i = strings.Index(name, ".")
|
||||
return name[i+1:]
|
||||
}
|
||||
|
||||
func trimGOPATH(name, file string) string {
|
||||
// Here we want to get the source file path relative to the compile time
|
||||
// GOPATH. As of Go 1.6.x there is no direct way to know the compiled
|
||||
// GOPATH at runtime, but we can infer the number of path segments in the
|
||||
// GOPATH. We note that fn.Name() returns the function name qualified by
|
||||
// the import path, which does not include the GOPATH. Thus we can trim
|
||||
// segments from the beginning of the file path until the number of path
|
||||
// separators remaining is one more than the number of path separators in
|
||||
// the function name. For example, given:
|
||||
//
|
||||
// GOPATH /home/user
|
||||
// file /home/user/src/pkg/sub/file.go
|
||||
// fn.Name() pkg/sub.Type.Method
|
||||
//
|
||||
// We want to produce:
|
||||
//
|
||||
// pkg/sub/file.go
|
||||
//
|
||||
// From this we can easily see that fn.Name() has one less path separator
|
||||
// than our desired output. We count separators from the end of the file
|
||||
// path until it finds two more than in the function name and then move
|
||||
// one character forward to preserve the initial path segment without a
|
||||
// leading separator.
|
||||
const sep = "/"
|
||||
goal := strings.Count(name, sep) + 2
|
||||
i := len(file)
|
||||
for n := 0; n < goal; n++ {
|
||||
i = strings.LastIndex(file[:i], sep)
|
||||
if i == -1 {
|
||||
// not enough separators found, set i so that the slice expression
|
||||
// below leaves file unmodified
|
||||
i = -len(sep)
|
||||
break
|
||||
}
|
||||
}
|
||||
// get back to 0 or trim the leading separator
|
||||
file = file[i+len(sep):]
|
||||
return file
|
||||
}
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell
|
||||
|
||||
Please consider promoting this project if you find it useful.
|
||||
|
||||
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.
|
|
@ -8,7 +8,7 @@
|
|||
// Encoding implementations are provided in other packages, such as
|
||||
// golang.org/x/text/encoding/charmap and
|
||||
// golang.org/x/text/encoding/japanese.
|
||||
package encoding
|
||||
package encoding // import "golang.org/x/text/encoding"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
@ -52,7 +52,7 @@ type Decoder struct {
|
|||
}
|
||||
|
||||
// Bytes converts the given encoded bytes to UTF-8. It returns the converted
|
||||
// bytes or 0, err if any error occurred.
|
||||
// bytes or nil, err if any error occurred.
|
||||
func (d *Decoder) Bytes(b []byte) ([]byte, error) {
|
||||
b, _, err := transform.Bytes(d, b)
|
||||
if err != nil {
|
||||
|
@ -62,7 +62,7 @@ func (d *Decoder) Bytes(b []byte) ([]byte, error) {
|
|||
}
|
||||
|
||||
// String converts the given encoded string to UTF-8. It returns the converted
|
||||
// string or 0, err if any error occurred.
|
||||
// string or "", err if any error occurred.
|
||||
func (d *Decoder) String(s string) (string, error) {
|
||||
s, _, err := transform.String(d, s)
|
||||
if err != nil {
|
||||
|
@ -95,7 +95,7 @@ type Encoder struct {
|
|||
_ struct{}
|
||||
}
|
||||
|
||||
// Bytes converts bytes from UTF-8. It returns the converted bytes or 0, err if
|
||||
// Bytes converts bytes from UTF-8. It returns the converted bytes or nil, err if
|
||||
// any error occurred.
|
||||
func (e *Encoder) Bytes(b []byte) ([]byte, error) {
|
||||
b, _, err := transform.Bytes(e, b)
|
||||
|
@ -106,7 +106,7 @@ func (e *Encoder) Bytes(b []byte) ([]byte, error) {
|
|||
}
|
||||
|
||||
// String converts a string from UTF-8. It returns the converted string or
|
||||
// 0, err if any error occurred.
|
||||
// "", err if any error occurred.
|
||||
func (e *Encoder) String(s string) (string, error) {
|
||||
s, _, err := transform.String(e, s)
|
||||
if err != nil {
|
||||
|
@ -124,7 +124,7 @@ func (e *Encoder) Writer(w io.Writer) io.Writer {
|
|||
}
|
||||
|
||||
// ASCIISub is the ASCII substitute character, as recommended by
|
||||
// http://unicode.org/reports/tr36/#Text_Comparison
|
||||
// https://unicode.org/reports/tr36/#Text_Comparison
|
||||
const ASCIISub = '\x1a'
|
||||
|
||||
// Nop is the nop encoding. Its transformed bytes are the same as the source
|
||||
|
|
|
@ -1,137 +0,0 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/internal/gen"
|
||||
)
|
||||
|
||||
type registry struct {
|
||||
XMLName xml.Name `xml:"registry"`
|
||||
Updated string `xml:"updated"`
|
||||
Registry []struct {
|
||||
ID string `xml:"id,attr"`
|
||||
Record []struct {
|
||||
Name string `xml:"name"`
|
||||
Xref []struct {
|
||||
Type string `xml:"type,attr"`
|
||||
Data string `xml:"data,attr"`
|
||||
} `xml:"xref"`
|
||||
Desc struct {
|
||||
Data string `xml:",innerxml"`
|
||||
// Any []struct {
|
||||
// Data string `xml:",chardata"`
|
||||
// } `xml:",any"`
|
||||
// Data string `xml:",chardata"`
|
||||
} `xml:"description,"`
|
||||
MIB string `xml:"value"`
|
||||
Alias []string `xml:"alias"`
|
||||
MIME string `xml:"preferred_alias"`
|
||||
} `xml:"record"`
|
||||
} `xml:"registry"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := gen.OpenIANAFile("assignments/character-sets/character-sets.xml")
|
||||
reg := ®istry{}
|
||||
if err := xml.NewDecoder(r).Decode(®); err != nil && err != io.EOF {
|
||||
log.Fatalf("Error decoding charset registry: %v", err)
|
||||
}
|
||||
if len(reg.Registry) == 0 || reg.Registry[0].ID != "character-sets-1" {
|
||||
log.Fatalf("Unexpected ID %s", reg.Registry[0].ID)
|
||||
}
|
||||
|
||||
w := &bytes.Buffer{}
|
||||
fmt.Fprintf(w, "const (\n")
|
||||
for _, rec := range reg.Registry[0].Record {
|
||||
constName := ""
|
||||
for _, a := range rec.Alias {
|
||||
if strings.HasPrefix(a, "cs") && strings.IndexByte(a, '-') == -1 {
|
||||
// Some of the constant definitions have comments in them. Strip those.
|
||||
constName = strings.Title(strings.SplitN(a[2:], "\n", 2)[0])
|
||||
}
|
||||
}
|
||||
if constName == "" {
|
||||
switch rec.MIB {
|
||||
case "2085":
|
||||
constName = "HZGB2312" // Not listed as alias for some reason.
|
||||
default:
|
||||
log.Fatalf("No cs alias defined for %s.", rec.MIB)
|
||||
}
|
||||
}
|
||||
if rec.MIME != "" {
|
||||
rec.MIME = fmt.Sprintf(" (MIME: %s)", rec.MIME)
|
||||
}
|
||||
fmt.Fprintf(w, "// %s is the MIB identifier with IANA name %s%s.\n//\n", constName, rec.Name, rec.MIME)
|
||||
if len(rec.Desc.Data) > 0 {
|
||||
fmt.Fprint(w, "// ")
|
||||
d := xml.NewDecoder(strings.NewReader(rec.Desc.Data))
|
||||
inElem := true
|
||||
attr := ""
|
||||
for {
|
||||
t, err := d.Token()
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Fatal(err)
|
||||
}
|
||||
break
|
||||
}
|
||||
switch x := t.(type) {
|
||||
case xml.CharData:
|
||||
attr = "" // Don't need attribute info.
|
||||
a := bytes.Split([]byte(x), []byte("\n"))
|
||||
for i, b := range a {
|
||||
if b = bytes.TrimSpace(b); len(b) != 0 {
|
||||
if !inElem && i > 0 {
|
||||
fmt.Fprint(w, "\n// ")
|
||||
}
|
||||
inElem = false
|
||||
fmt.Fprintf(w, "%s ", string(b))
|
||||
}
|
||||
}
|
||||
case xml.StartElement:
|
||||
if x.Name.Local == "xref" {
|
||||
inElem = true
|
||||
use := false
|
||||
for _, a := range x.Attr {
|
||||
if a.Name.Local == "type" {
|
||||
use = use || a.Value != "person"
|
||||
}
|
||||
if a.Name.Local == "data" && use {
|
||||
attr = a.Value + " "
|
||||
}
|
||||
}
|
||||
}
|
||||
case xml.EndElement:
|
||||
inElem = false
|
||||
fmt.Fprint(w, attr)
|
||||
}
|
||||
}
|
||||
fmt.Fprint(w, "\n")
|
||||
}
|
||||
for _, x := range rec.Xref {
|
||||
switch x.Type {
|
||||
case "rfc":
|
||||
fmt.Fprintf(w, "// Reference: %s\n", strings.ToUpper(x.Data))
|
||||
case "uri":
|
||||
fmt.Fprintf(w, "// Reference: %s\n", x.Data)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(w, "%s MIB = %s\n", constName, rec.MIB)
|
||||
fmt.Fprintln(w)
|
||||
}
|
||||
fmt.Fprintln(w, ")")
|
||||
|
||||
gen.WriteGoFile("mib.go", "identifier", w.Bytes())
|
||||
}
|
|
@ -34,10 +34,10 @@ package identifier
|
|||
// - http://www.iana.org/assignments/character-sets/character-sets.xhtml
|
||||
// - http://www.iana.org/assignments/ianacharset-mib/ianacharset-mib
|
||||
// - http://www.ietf.org/rfc/rfc2978.txt
|
||||
// - http://www.unicode.org/reports/tr22/
|
||||
// - https://www.unicode.org/reports/tr22/
|
||||
// - http://www.w3.org/TR/encoding/
|
||||
// - http://www.w3.org/TR/encoding/indexes/encodings.json
|
||||
// - https://encoding.spec.whatwg.org/
|
||||
// - https://encoding.spec.whatwg.org/encodings.json
|
||||
// - https://tools.ietf.org/html/rfc6657#section-5
|
||||
|
||||
// Interface can be implemented by Encodings to define the CCS or CES for which
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// This file was generated by go generate; DO NOT EDIT
|
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
package identifier
|
||||
|
||||
|
@ -538,8 +538,6 @@ const (
|
|||
// ISO111ECMACyrillic is the MIB identifier with IANA name ECMA-cyrillic.
|
||||
//
|
||||
// ISO registry
|
||||
// (formerly ECMA
|
||||
// registry )
|
||||
ISO111ECMACyrillic MIB = 77
|
||||
|
||||
// ISO121Canadian1 is the MIB identifier with IANA name CSA_Z243.4-1985-1.
|
||||
|
@ -732,18 +730,18 @@ const (
|
|||
|
||||
// ISO885913 is the MIB identifier with IANA name ISO-8859-13.
|
||||
//
|
||||
// ISO See http://www.iana.org/assignments/charset-reg/ISO-8859-13 http://www.iana.org/assignments/charset-reg/ISO-8859-13
|
||||
// ISO See https://www.iana.org/assignments/charset-reg/ISO-8859-13 https://www.iana.org/assignments/charset-reg/ISO-8859-13
|
||||
ISO885913 MIB = 109
|
||||
|
||||
// ISO885914 is the MIB identifier with IANA name ISO-8859-14.
|
||||
//
|
||||
// ISO See http://www.iana.org/assignments/charset-reg/ISO-8859-14
|
||||
// ISO See https://www.iana.org/assignments/charset-reg/ISO-8859-14
|
||||
ISO885914 MIB = 110
|
||||
|
||||
// ISO885915 is the MIB identifier with IANA name ISO-8859-15.
|
||||
//
|
||||
// ISO
|
||||
// Please see: http://www.iana.org/assignments/charset-reg/ISO-8859-15
|
||||
// Please see: https://www.iana.org/assignments/charset-reg/ISO-8859-15
|
||||
ISO885915 MIB = 111
|
||||
|
||||
// ISO885916 is the MIB identifier with IANA name ISO-8859-16.
|
||||
|
@ -754,41 +752,41 @@ const (
|
|||
// GBK is the MIB identifier with IANA name GBK.
|
||||
//
|
||||
// Chinese IT Standardization Technical Committee
|
||||
// Please see: http://www.iana.org/assignments/charset-reg/GBK
|
||||
// Please see: https://www.iana.org/assignments/charset-reg/GBK
|
||||
GBK MIB = 113
|
||||
|
||||
// GB18030 is the MIB identifier with IANA name GB18030.
|
||||
//
|
||||
// Chinese IT Standardization Technical Committee
|
||||
// Please see: http://www.iana.org/assignments/charset-reg/GB18030
|
||||
// Please see: https://www.iana.org/assignments/charset-reg/GB18030
|
||||
GB18030 MIB = 114
|
||||
|
||||
// OSDEBCDICDF0415 is the MIB identifier with IANA name OSD_EBCDIC_DF04_15.
|
||||
//
|
||||
// Fujitsu-Siemens standard mainframe EBCDIC encoding
|
||||
// Please see: http://www.iana.org/assignments/charset-reg/OSD-EBCDIC-DF04-15
|
||||
// Please see: https://www.iana.org/assignments/charset-reg/OSD-EBCDIC-DF04-15
|
||||
OSDEBCDICDF0415 MIB = 115
|
||||
|
||||
// OSDEBCDICDF03IRV is the MIB identifier with IANA name OSD_EBCDIC_DF03_IRV.
|
||||
//
|
||||
// Fujitsu-Siemens standard mainframe EBCDIC encoding
|
||||
// Please see: http://www.iana.org/assignments/charset-reg/OSD-EBCDIC-DF03-IRV
|
||||
// Please see: https://www.iana.org/assignments/charset-reg/OSD-EBCDIC-DF03-IRV
|
||||
OSDEBCDICDF03IRV MIB = 116
|
||||
|
||||
// OSDEBCDICDF041 is the MIB identifier with IANA name OSD_EBCDIC_DF04_1.
|
||||
//
|
||||
// Fujitsu-Siemens standard mainframe EBCDIC encoding
|
||||
// Please see: http://www.iana.org/assignments/charset-reg/OSD-EBCDIC-DF04-1
|
||||
// Please see: https://www.iana.org/assignments/charset-reg/OSD-EBCDIC-DF04-1
|
||||
OSDEBCDICDF041 MIB = 117
|
||||
|
||||
// ISO115481 is the MIB identifier with IANA name ISO-11548-1.
|
||||
//
|
||||
// See http://www.iana.org/assignments/charset-reg/ISO-11548-1
|
||||
// See https://www.iana.org/assignments/charset-reg/ISO-11548-1
|
||||
ISO115481 MIB = 118
|
||||
|
||||
// KZ1048 is the MIB identifier with IANA name KZ-1048.
|
||||
//
|
||||
// See http://www.iana.org/assignments/charset-reg/KZ-1048
|
||||
// See https://www.iana.org/assignments/charset-reg/KZ-1048
|
||||
KZ1048 MIB = 119
|
||||
|
||||
// Unicode is the MIB identifier with IANA name ISO-10646-UCS-2.
|
||||
|
@ -855,7 +853,7 @@ const (
|
|||
|
||||
// SCSU is the MIB identifier with IANA name SCSU.
|
||||
//
|
||||
// SCSU See http://www.iana.org/assignments/charset-reg/SCSU
|
||||
// SCSU See https://www.iana.org/assignments/charset-reg/SCSU
|
||||
SCSU MIB = 1011
|
||||
|
||||
// UTF7 is the MIB identifier with IANA name UTF-7.
|
||||
|
@ -884,27 +882,27 @@ const (
|
|||
|
||||
// CESU8 is the MIB identifier with IANA name CESU-8.
|
||||
//
|
||||
// http://www.unicode.org/unicode/reports/tr26
|
||||
// https://www.unicode.org/reports/tr26
|
||||
CESU8 MIB = 1016
|
||||
|
||||
// UTF32 is the MIB identifier with IANA name UTF-32.
|
||||
//
|
||||
// http://www.unicode.org/unicode/reports/tr19/
|
||||
// https://www.unicode.org/reports/tr19/
|
||||
UTF32 MIB = 1017
|
||||
|
||||
// UTF32BE is the MIB identifier with IANA name UTF-32BE.
|
||||
//
|
||||
// http://www.unicode.org/unicode/reports/tr19/
|
||||
// https://www.unicode.org/reports/tr19/
|
||||
UTF32BE MIB = 1018
|
||||
|
||||
// UTF32LE is the MIB identifier with IANA name UTF-32LE.
|
||||
//
|
||||
// http://www.unicode.org/unicode/reports/tr19/
|
||||
// https://www.unicode.org/reports/tr19/
|
||||
UTF32LE MIB = 1019
|
||||
|
||||
// BOCU1 is the MIB identifier with IANA name BOCU-1.
|
||||
//
|
||||
// http://www.unicode.org/notes/tn6/
|
||||
// https://www.unicode.org/notes/tn6/
|
||||
BOCU1 MIB = 1020
|
||||
|
||||
// Windows30Latin1 is the MIB identifier with IANA name ISO-8859-1-Windows-3.0-Latin-1.
|
||||
|
@ -1461,152 +1459,152 @@ const (
|
|||
|
||||
// IBM00858 is the MIB identifier with IANA name IBM00858.
|
||||
//
|
||||
// IBM See http://www.iana.org/assignments/charset-reg/IBM00858
|
||||
// IBM See https://www.iana.org/assignments/charset-reg/IBM00858
|
||||
IBM00858 MIB = 2089
|
||||
|
||||
// IBM00924 is the MIB identifier with IANA name IBM00924.
|
||||
//
|
||||
// IBM See http://www.iana.org/assignments/charset-reg/IBM00924
|
||||
// IBM See https://www.iana.org/assignments/charset-reg/IBM00924
|
||||
IBM00924 MIB = 2090
|
||||
|
||||
// IBM01140 is the MIB identifier with IANA name IBM01140.
|
||||
//
|
||||
// IBM See http://www.iana.org/assignments/charset-reg/IBM01140
|
||||
// IBM See https://www.iana.org/assignments/charset-reg/IBM01140
|
||||
IBM01140 MIB = 2091
|
||||
|
||||
// IBM01141 is the MIB identifier with IANA name IBM01141.
|
||||
//
|
||||
// IBM See http://www.iana.org/assignments/charset-reg/IBM01141
|
||||
// IBM See https://www.iana.org/assignments/charset-reg/IBM01141
|
||||
IBM01141 MIB = 2092
|
||||
|
||||
// IBM01142 is the MIB identifier with IANA name IBM01142.
|
||||
//
|
||||
// IBM See http://www.iana.org/assignments/charset-reg/IBM01142
|
||||
// IBM See https://www.iana.org/assignments/charset-reg/IBM01142
|
||||
IBM01142 MIB = 2093
|
||||
|
||||
// IBM01143 is the MIB identifier with IANA name IBM01143.
|
||||
//
|
||||
// IBM See http://www.iana.org/assignments/charset-reg/IBM01143
|
||||
// IBM See https://www.iana.org/assignments/charset-reg/IBM01143
|
||||
IBM01143 MIB = 2094
|
||||
|
||||
// IBM01144 is the MIB identifier with IANA name IBM01144.
|
||||
//
|
||||
// IBM See http://www.iana.org/assignments/charset-reg/IBM01144
|
||||
// IBM See https://www.iana.org/assignments/charset-reg/IBM01144
|
||||
IBM01144 MIB = 2095
|
||||
|
||||
// IBM01145 is the MIB identifier with IANA name IBM01145.
|
||||
//
|
||||
// IBM See http://www.iana.org/assignments/charset-reg/IBM01145
|
||||
// IBM See https://www.iana.org/assignments/charset-reg/IBM01145
|
||||
IBM01145 MIB = 2096
|
||||
|
||||
// IBM01146 is the MIB identifier with IANA name IBM01146.
|
||||
//
|
||||
// IBM See http://www.iana.org/assignments/charset-reg/IBM01146
|
||||
// IBM See https://www.iana.org/assignments/charset-reg/IBM01146
|
||||
IBM01146 MIB = 2097
|
||||
|
||||
// IBM01147 is the MIB identifier with IANA name IBM01147.
|
||||
//
|
||||
// IBM See http://www.iana.org/assignments/charset-reg/IBM01147
|
||||
// IBM See https://www.iana.org/assignments/charset-reg/IBM01147
|
||||
IBM01147 MIB = 2098
|
||||
|
||||
// IBM01148 is the MIB identifier with IANA name IBM01148.
|
||||
//
|
||||
// IBM See http://www.iana.org/assignments/charset-reg/IBM01148
|
||||
// IBM See https://www.iana.org/assignments/charset-reg/IBM01148
|
||||
IBM01148 MIB = 2099
|
||||
|
||||
// IBM01149 is the MIB identifier with IANA name IBM01149.
|
||||
//
|
||||
// IBM See http://www.iana.org/assignments/charset-reg/IBM01149
|
||||
// IBM See https://www.iana.org/assignments/charset-reg/IBM01149
|
||||
IBM01149 MIB = 2100
|
||||
|
||||
// Big5HKSCS is the MIB identifier with IANA name Big5-HKSCS.
|
||||
//
|
||||
// See http://www.iana.org/assignments/charset-reg/Big5-HKSCS
|
||||
// See https://www.iana.org/assignments/charset-reg/Big5-HKSCS
|
||||
Big5HKSCS MIB = 2101
|
||||
|
||||
// IBM1047 is the MIB identifier with IANA name IBM1047.
|
||||
//
|
||||
// IBM1047 (EBCDIC Latin 1/Open Systems) http://www-1.ibm.com/servers/eserver/iseries/software/globalization/pdf/cp01047z.pdf
|
||||
// IBM1047 (EBCDIC Latin 1/Open Systems) https://www-1.ibm.com/servers/eserver/iseries/software/globalization/pdf/cp01047z.pdf
|
||||
IBM1047 MIB = 2102
|
||||
|
||||
// PTCP154 is the MIB identifier with IANA name PTCP154.
|
||||
//
|
||||
// See http://www.iana.org/assignments/charset-reg/PTCP154
|
||||
// See https://www.iana.org/assignments/charset-reg/PTCP154
|
||||
PTCP154 MIB = 2103
|
||||
|
||||
// Amiga1251 is the MIB identifier with IANA name Amiga-1251.
|
||||
//
|
||||
// See http://www.amiga.ultranet.ru/Amiga-1251.html
|
||||
// See https://www.amiga.ultranet.ru/Amiga-1251.html
|
||||
Amiga1251 MIB = 2104
|
||||
|
||||
// KOI7switched is the MIB identifier with IANA name KOI7-switched.
|
||||
//
|
||||
// See http://www.iana.org/assignments/charset-reg/KOI7-switched
|
||||
// See https://www.iana.org/assignments/charset-reg/KOI7-switched
|
||||
KOI7switched MIB = 2105
|
||||
|
||||
// BRF is the MIB identifier with IANA name BRF.
|
||||
//
|
||||
// See http://www.iana.org/assignments/charset-reg/BRF
|
||||
// See https://www.iana.org/assignments/charset-reg/BRF
|
||||
BRF MIB = 2106
|
||||
|
||||
// TSCII is the MIB identifier with IANA name TSCII.
|
||||
//
|
||||
// See http://www.iana.org/assignments/charset-reg/TSCII
|
||||
// See https://www.iana.org/assignments/charset-reg/TSCII
|
||||
TSCII MIB = 2107
|
||||
|
||||
// CP51932 is the MIB identifier with IANA name CP51932.
|
||||
//
|
||||
// See http://www.iana.org/assignments/charset-reg/CP51932
|
||||
// See https://www.iana.org/assignments/charset-reg/CP51932
|
||||
CP51932 MIB = 2108
|
||||
|
||||
// Windows874 is the MIB identifier with IANA name windows-874.
|
||||
//
|
||||
// See http://www.iana.org/assignments/charset-reg/windows-874
|
||||
// See https://www.iana.org/assignments/charset-reg/windows-874
|
||||
Windows874 MIB = 2109
|
||||
|
||||
// Windows1250 is the MIB identifier with IANA name windows-1250.
|
||||
//
|
||||
// Microsoft http://www.iana.org/assignments/charset-reg/windows-1250
|
||||
// Microsoft https://www.iana.org/assignments/charset-reg/windows-1250
|
||||
Windows1250 MIB = 2250
|
||||
|
||||
// Windows1251 is the MIB identifier with IANA name windows-1251.
|
||||
//
|
||||
// Microsoft http://www.iana.org/assignments/charset-reg/windows-1251
|
||||
// Microsoft https://www.iana.org/assignments/charset-reg/windows-1251
|
||||
Windows1251 MIB = 2251
|
||||
|
||||
// Windows1252 is the MIB identifier with IANA name windows-1252.
|
||||
//
|
||||
// Microsoft http://www.iana.org/assignments/charset-reg/windows-1252
|
||||
// Microsoft https://www.iana.org/assignments/charset-reg/windows-1252
|
||||
Windows1252 MIB = 2252
|
||||
|
||||
// Windows1253 is the MIB identifier with IANA name windows-1253.
|
||||
//
|
||||
// Microsoft http://www.iana.org/assignments/charset-reg/windows-1253
|
||||
// Microsoft https://www.iana.org/assignments/charset-reg/windows-1253
|
||||
Windows1253 MIB = 2253
|
||||
|
||||
// Windows1254 is the MIB identifier with IANA name windows-1254.
|
||||
//
|
||||
// Microsoft http://www.iana.org/assignments/charset-reg/windows-1254
|
||||
// Microsoft https://www.iana.org/assignments/charset-reg/windows-1254
|
||||
Windows1254 MIB = 2254
|
||||
|
||||
// Windows1255 is the MIB identifier with IANA name windows-1255.
|
||||
//
|
||||
// Microsoft http://www.iana.org/assignments/charset-reg/windows-1255
|
||||
// Microsoft https://www.iana.org/assignments/charset-reg/windows-1255
|
||||
Windows1255 MIB = 2255
|
||||
|
||||
// Windows1256 is the MIB identifier with IANA name windows-1256.
|
||||
//
|
||||
// Microsoft http://www.iana.org/assignments/charset-reg/windows-1256
|
||||
// Microsoft https://www.iana.org/assignments/charset-reg/windows-1256
|
||||
Windows1256 MIB = 2256
|
||||
|
||||
// Windows1257 is the MIB identifier with IANA name windows-1257.
|
||||
//
|
||||
// Microsoft http://www.iana.org/assignments/charset-reg/windows-1257
|
||||
// Microsoft https://www.iana.org/assignments/charset-reg/windows-1257
|
||||
Windows1257 MIB = 2257
|
||||
|
||||
// Windows1258 is the MIB identifier with IANA name windows-1258.
|
||||
//
|
||||
// Microsoft http://www.iana.org/assignments/charset-reg/windows-1258
|
||||
// Microsoft https://www.iana.org/assignments/charset-reg/windows-1258
|
||||
Windows1258 MIB = 2258
|
||||
|
||||
// TIS620 is the MIB identifier with IANA name TIS-620.
|
||||
|
@ -1616,6 +1614,6 @@ const (
|
|||
|
||||
// CP50220 is the MIB identifier with IANA name CP50220.
|
||||
//
|
||||
// See http://www.iana.org/assignments/charset-reg/CP50220
|
||||
// See https://www.iana.org/assignments/charset-reg/CP50220
|
||||
CP50220 MIB = 2260
|
||||
)
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package unicode provides Unicode encodings such as UTF-16.
|
||||
package unicode
|
||||
package unicode // import "golang.org/x/text/encoding/unicode"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
|
@ -25,15 +26,95 @@ import (
|
|||
// the introduction of some kind of error type for conveying the erroneous code
|
||||
// point.
|
||||
|
||||
// UTF8 is the UTF-8 encoding.
|
||||
// UTF8 is the UTF-8 encoding. It neither removes nor adds byte order marks.
|
||||
var UTF8 encoding.Encoding = utf8enc
|
||||
|
||||
// UTF8BOM is an UTF-8 encoding where the decoder strips a leading byte order
|
||||
// mark while the encoder adds one.
|
||||
//
|
||||
// Some editors add a byte order mark as a signature to UTF-8 files. Although
|
||||
// the byte order mark is not useful for detecting byte order in UTF-8, it is
|
||||
// sometimes used as a convention to mark UTF-8-encoded files. This relies on
|
||||
// the observation that the UTF-8 byte order mark is either an illegal or at
|
||||
// least very unlikely sequence in any other character encoding.
|
||||
var UTF8BOM encoding.Encoding = utf8bomEncoding{}
|
||||
|
||||
type utf8bomEncoding struct{}
|
||||
|
||||
func (utf8bomEncoding) String() string {
|
||||
return "UTF-8-BOM"
|
||||
}
|
||||
|
||||
func (utf8bomEncoding) ID() (identifier.MIB, string) {
|
||||
return identifier.Unofficial, "x-utf8bom"
|
||||
}
|
||||
|
||||
func (utf8bomEncoding) NewEncoder() *encoding.Encoder {
|
||||
return &encoding.Encoder{
|
||||
Transformer: &utf8bomEncoder{t: runes.ReplaceIllFormed()},
|
||||
}
|
||||
}
|
||||
|
||||
func (utf8bomEncoding) NewDecoder() *encoding.Decoder {
|
||||
return &encoding.Decoder{Transformer: &utf8bomDecoder{}}
|
||||
}
|
||||
|
||||
var utf8enc = &internal.Encoding{
|
||||
&internal.SimpleEncoding{utf8Decoder{}, runes.ReplaceIllFormed()},
|
||||
"UTF-8",
|
||||
identifier.UTF8,
|
||||
}
|
||||
|
||||
type utf8bomDecoder struct {
|
||||
checked bool
|
||||
}
|
||||
|
||||
func (t *utf8bomDecoder) Reset() {
|
||||
t.checked = false
|
||||
}
|
||||
|
||||
func (t *utf8bomDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
if !t.checked {
|
||||
if !atEOF && len(src) < len(utf8BOM) {
|
||||
if len(src) == 0 {
|
||||
return 0, 0, nil
|
||||
}
|
||||
return 0, 0, transform.ErrShortSrc
|
||||
}
|
||||
if bytes.HasPrefix(src, []byte(utf8BOM)) {
|
||||
nSrc += len(utf8BOM)
|
||||
src = src[len(utf8BOM):]
|
||||
}
|
||||
t.checked = true
|
||||
}
|
||||
nDst, n, err := utf8Decoder.Transform(utf8Decoder{}, dst[nDst:], src, atEOF)
|
||||
nSrc += n
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
type utf8bomEncoder struct {
|
||||
written bool
|
||||
t transform.Transformer
|
||||
}
|
||||
|
||||
func (t *utf8bomEncoder) Reset() {
|
||||
t.written = false
|
||||
t.t.Reset()
|
||||
}
|
||||
|
||||
func (t *utf8bomEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
if !t.written {
|
||||
if len(dst) < len(utf8BOM) {
|
||||
return nDst, 0, transform.ErrShortDst
|
||||
}
|
||||
nDst = copy(dst, utf8BOM)
|
||||
t.written = true
|
||||
}
|
||||
n, nSrc, err := utf8Decoder.Transform(utf8Decoder{}, dst[nDst:], src, atEOF)
|
||||
nDst += n
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
type utf8Decoder struct{ transform.NopResetter }
|
||||
|
||||
func (utf8Decoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
|
@ -145,7 +226,7 @@ func (utf8Decoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err e
|
|||
// and consumed in a greater context that implies a certain endianness, use
|
||||
// IgnoreBOM. Otherwise, use ExpectBOM and always produce and consume a BOM.
|
||||
//
|
||||
// In the language of http://www.unicode.org/faq/utf_bom.html#bom10, IgnoreBOM
|
||||
// In the language of https://www.unicode.org/faq/utf_bom.html#bom10, IgnoreBOM
|
||||
// corresponds to "Where the precise type of the data stream is known... the
|
||||
// BOM should not be used" and ExpectBOM corresponds to "A particular
|
||||
// protocol... may require use of the BOM".
|
||||
|
@ -287,16 +368,13 @@ func (u *utf16Decoder) Reset() {
|
|||
}
|
||||
|
||||
func (u *utf16Decoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
if len(src) < 2 && atEOF && u.current.bomPolicy&requireBOM != 0 {
|
||||
return 0, 0, ErrMissingBOM
|
||||
}
|
||||
if len(src) == 0 {
|
||||
if atEOF && u.current.bomPolicy&requireBOM != 0 {
|
||||
return 0, 0, ErrMissingBOM
|
||||
}
|
||||
return 0, 0, nil
|
||||
}
|
||||
if u.current.bomPolicy&acceptBOM != 0 {
|
||||
if len(src) < 2 {
|
||||
return 0, 0, transform.ErrShortSrc
|
||||
}
|
||||
if len(src) >= 2 && u.current.bomPolicy&acceptBOM != 0 {
|
||||
switch {
|
||||
case src[0] == 0xfe && src[1] == 0xff:
|
||||
u.current.endianness = BigEndian
|
||||
|
|
|
@ -41,20 +41,35 @@ func If(s Set, tIn, tNotIn transform.Transformer) Transformer {
|
|||
if tNotIn == nil {
|
||||
tNotIn = transform.Nop
|
||||
}
|
||||
sIn, ok := tIn.(transform.SpanningTransformer)
|
||||
if !ok {
|
||||
sIn = dummySpan{tIn}
|
||||
}
|
||||
sNotIn, ok := tNotIn.(transform.SpanningTransformer)
|
||||
if !ok {
|
||||
sNotIn = dummySpan{tNotIn}
|
||||
}
|
||||
|
||||
a := &cond{
|
||||
tIn: tIn,
|
||||
tNotIn: tNotIn,
|
||||
tIn: sIn,
|
||||
tNotIn: sNotIn,
|
||||
f: s.Contains,
|
||||
}
|
||||
a.Reset()
|
||||
return Transformer{a}
|
||||
}
|
||||
|
||||
type dummySpan struct{ transform.Transformer }
|
||||
|
||||
func (d dummySpan) Span(src []byte, atEOF bool) (n int, err error) {
|
||||
return 0, transform.ErrEndOfSpan
|
||||
}
|
||||
|
||||
type cond struct {
|
||||
tIn, tNotIn transform.Transformer
|
||||
tIn, tNotIn transform.SpanningTransformer
|
||||
f func(rune) bool
|
||||
check func(rune) bool // current check to perform
|
||||
t transform.Transformer // current transformer to use
|
||||
check func(rune) bool // current check to perform
|
||||
t transform.SpanningTransformer // current transformer to use
|
||||
}
|
||||
|
||||
// Reset implements transform.Transformer.
|
||||
|
@ -84,6 +99,51 @@ func (t *cond) isNot(r rune) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// This implementation of Span doesn't help all too much, but it needs to be
|
||||
// there to satisfy this package's Transformer interface.
|
||||
// TODO: there are certainly room for improvements, though. For example, if
|
||||
// t.t == transform.Nop (which will a common occurrence) it will save a bundle
|
||||
// to special-case that loop.
|
||||
func (t *cond) Span(src []byte, atEOF bool) (n int, err error) {
|
||||
p := 0
|
||||
for n < len(src) && err == nil {
|
||||
// Don't process too much at a time as the Spanner that will be
|
||||
// called on this block may terminate early.
|
||||
const maxChunk = 4096
|
||||
max := len(src)
|
||||
if v := n + maxChunk; v < max {
|
||||
max = v
|
||||
}
|
||||
atEnd := false
|
||||
size := 0
|
||||
current := t.t
|
||||
for ; p < max; p += size {
|
||||
r := rune(src[p])
|
||||
if r < utf8.RuneSelf {
|
||||
size = 1
|
||||
} else if r, size = utf8.DecodeRune(src[p:]); size == 1 {
|
||||
if !atEOF && !utf8.FullRune(src[p:]) {
|
||||
err = transform.ErrShortSrc
|
||||
break
|
||||
}
|
||||
}
|
||||
if !t.check(r) {
|
||||
// The next rune will be the start of a new run.
|
||||
atEnd = true
|
||||
break
|
||||
}
|
||||
}
|
||||
n2, err2 := current.Span(src[n:p], atEnd || (atEOF && p == len(src)))
|
||||
n += n2
|
||||
if err2 != nil {
|
||||
return n, err2
|
||||
}
|
||||
// At this point either err != nil or t.check will pass for the rune at p.
|
||||
p = n + size
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (t *cond) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
p := 0
|
||||
for nSrc < len(src) && err == nil {
|
||||
|
@ -99,9 +159,10 @@ func (t *cond) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error
|
|||
size := 0
|
||||
current := t.t
|
||||
for ; p < max; p += size {
|
||||
var r rune
|
||||
r, size = utf8.DecodeRune(src[p:])
|
||||
if r == utf8.RuneError && size == 1 {
|
||||
r := rune(src[p])
|
||||
if r < utf8.RuneSelf {
|
||||
size = 1
|
||||
} else if r, size = utf8.DecodeRune(src[p:]); size == 1 {
|
||||
if !atEOF && !utf8.FullRune(src[p:]) {
|
||||
err = transform.ErrShortSrc
|
||||
break
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package runes provide transforms for UTF-8 encoded text.
|
||||
package runes
|
||||
package runes // import "golang.org/x/text/runes"
|
||||
|
||||
import (
|
||||
"unicode"
|
||||
|
@ -46,9 +46,19 @@ func Predicate(f func(rune) bool) Set {
|
|||
|
||||
// Transformer implements the transform.Transformer interface.
|
||||
type Transformer struct {
|
||||
transform.Transformer
|
||||
t transform.SpanningTransformer
|
||||
}
|
||||
|
||||
func (t Transformer) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
return t.t.Transform(dst, src, atEOF)
|
||||
}
|
||||
|
||||
func (t Transformer) Span(b []byte, atEOF bool) (n int, err error) {
|
||||
return t.t.Span(b, atEOF)
|
||||
}
|
||||
|
||||
func (t Transformer) Reset() { t.t.Reset() }
|
||||
|
||||
// Bytes returns a new byte slice with the result of converting b using t. It
|
||||
// calls Reset on t. It returns nil if any error was found. This can only happen
|
||||
// if an error-producing Transformer is passed to If.
|
||||
|
@ -96,39 +106,57 @@ type remove func(r rune) bool
|
|||
|
||||
func (remove) Reset() {}
|
||||
|
||||
// Span implements transform.Spanner.
|
||||
func (t remove) Span(src []byte, atEOF bool) (n int, err error) {
|
||||
for r, size := rune(0), 0; n < len(src); {
|
||||
if r = rune(src[n]); r < utf8.RuneSelf {
|
||||
size = 1
|
||||
} else if r, size = utf8.DecodeRune(src[n:]); size == 1 {
|
||||
// Invalid rune.
|
||||
if !atEOF && !utf8.FullRune(src[n:]) {
|
||||
err = transform.ErrShortSrc
|
||||
} else {
|
||||
err = transform.ErrEndOfSpan
|
||||
}
|
||||
break
|
||||
}
|
||||
if t(r) {
|
||||
err = transform.ErrEndOfSpan
|
||||
break
|
||||
}
|
||||
n += size
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Transform implements transform.Transformer.
|
||||
func (t remove) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
for r, size := rune(0), 0; nSrc < len(src); {
|
||||
if r = rune(src[nSrc]); r < utf8.RuneSelf {
|
||||
size = 1
|
||||
} else {
|
||||
r, size = utf8.DecodeRune(src[nSrc:])
|
||||
|
||||
if size == 1 {
|
||||
// Invalid rune.
|
||||
if !atEOF && !utf8.FullRune(src[nSrc:]) {
|
||||
err = transform.ErrShortSrc
|
||||
} else if r, size = utf8.DecodeRune(src[nSrc:]); size == 1 {
|
||||
// Invalid rune.
|
||||
if !atEOF && !utf8.FullRune(src[nSrc:]) {
|
||||
err = transform.ErrShortSrc
|
||||
break
|
||||
}
|
||||
// We replace illegal bytes with RuneError. Not doing so might
|
||||
// otherwise turn a sequence of invalid UTF-8 into valid UTF-8.
|
||||
// The resulting byte sequence may subsequently contain runes
|
||||
// for which t(r) is true that were passed unnoticed.
|
||||
if !t(utf8.RuneError) {
|
||||
if nDst+3 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
// We replace illegal bytes with RuneError. Not doing so might
|
||||
// otherwise turn a sequence of invalid UTF-8 into valid UTF-8.
|
||||
// The resulting byte sequence may subsequently contain runes
|
||||
// for which t(r) is true that were passed unnoticed.
|
||||
if !t(utf8.RuneError) {
|
||||
if nDst+3 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst+0] = runeErrorString[0]
|
||||
dst[nDst+1] = runeErrorString[1]
|
||||
dst[nDst+2] = runeErrorString[2]
|
||||
nDst += 3
|
||||
}
|
||||
nSrc++
|
||||
continue
|
||||
dst[nDst+0] = runeErrorString[0]
|
||||
dst[nDst+1] = runeErrorString[1]
|
||||
dst[nDst+2] = runeErrorString[2]
|
||||
nDst += 3
|
||||
}
|
||||
nSrc++
|
||||
continue
|
||||
}
|
||||
|
||||
if t(r) {
|
||||
nSrc += size
|
||||
continue
|
||||
|
@ -157,6 +185,28 @@ type mapper func(rune) rune
|
|||
|
||||
func (mapper) Reset() {}
|
||||
|
||||
// Span implements transform.Spanner.
|
||||
func (t mapper) Span(src []byte, atEOF bool) (n int, err error) {
|
||||
for r, size := rune(0), 0; n < len(src); n += size {
|
||||
if r = rune(src[n]); r < utf8.RuneSelf {
|
||||
size = 1
|
||||
} else if r, size = utf8.DecodeRune(src[n:]); size == 1 {
|
||||
// Invalid rune.
|
||||
if !atEOF && !utf8.FullRune(src[n:]) {
|
||||
err = transform.ErrShortSrc
|
||||
} else {
|
||||
err = transform.ErrEndOfSpan
|
||||
}
|
||||
break
|
||||
}
|
||||
if t(r) != r {
|
||||
err = transform.ErrEndOfSpan
|
||||
break
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Transform implements transform.Transformer.
|
||||
func (t mapper) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
var replacement rune
|
||||
|
@ -230,24 +280,51 @@ func ReplaceIllFormed() Transformer {
|
|||
|
||||
type replaceIllFormed struct{ transform.NopResetter }
|
||||
|
||||
func (t replaceIllFormed) Span(src []byte, atEOF bool) (n int, err error) {
|
||||
for n < len(src) {
|
||||
// ASCII fast path.
|
||||
if src[n] < utf8.RuneSelf {
|
||||
n++
|
||||
continue
|
||||
}
|
||||
|
||||
r, size := utf8.DecodeRune(src[n:])
|
||||
|
||||
// Look for a valid non-ASCII rune.
|
||||
if r != utf8.RuneError || size != 1 {
|
||||
n += size
|
||||
continue
|
||||
}
|
||||
|
||||
// Look for short source data.
|
||||
if !atEOF && !utf8.FullRune(src[n:]) {
|
||||
err = transform.ErrShortSrc
|
||||
break
|
||||
}
|
||||
|
||||
// We have an invalid rune.
|
||||
err = transform.ErrEndOfSpan
|
||||
break
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (t replaceIllFormed) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
for nSrc < len(src) {
|
||||
r, size := utf8.DecodeRune(src[nSrc:])
|
||||
|
||||
// Look for an ASCII rune.
|
||||
if r < utf8.RuneSelf {
|
||||
// ASCII fast path.
|
||||
if r := src[nSrc]; r < utf8.RuneSelf {
|
||||
if nDst == len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst] = byte(r)
|
||||
dst[nDst] = r
|
||||
nDst++
|
||||
nSrc++
|
||||
continue
|
||||
}
|
||||
|
||||
// Look for a valid non-ASCII rune.
|
||||
if r != utf8.RuneError || size != 1 {
|
||||
if _, size := utf8.DecodeRune(src[nSrc:]); size != 1 {
|
||||
if size != copy(dst[nDst:], src[nSrc:nSrc+size]) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
// bytes passing through as well as various transformations. Example
|
||||
// transformations provided by other packages include normalization and
|
||||
// conversion between character sets.
|
||||
package transform
|
||||
package transform // import "golang.org/x/text/transform"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -24,6 +24,10 @@ var (
|
|||
// complete the transformation.
|
||||
ErrShortSrc = errors.New("transform: short source buffer")
|
||||
|
||||
// ErrEndOfSpan means that the input and output (the transformed input)
|
||||
// are not identical.
|
||||
ErrEndOfSpan = errors.New("transform: input and output are not identical")
|
||||
|
||||
// errInconsistentByteCount means that Transform returned success (nil
|
||||
// error) but also returned nSrc inconsistent with the src argument.
|
||||
errInconsistentByteCount = errors.New("transform: inconsistent byte count returned")
|
||||
|
@ -60,6 +64,41 @@ type Transformer interface {
|
|||
Reset()
|
||||
}
|
||||
|
||||
// SpanningTransformer extends the Transformer interface with a Span method
|
||||
// that determines how much of the input already conforms to the Transformer.
|
||||
type SpanningTransformer interface {
|
||||
Transformer
|
||||
|
||||
// Span returns a position in src such that transforming src[:n] results in
|
||||
// identical output src[:n] for these bytes. It does not necessarily return
|
||||
// the largest such n. The atEOF argument tells whether src represents the
|
||||
// last bytes of the input.
|
||||
//
|
||||
// Callers should always account for the n bytes consumed before
|
||||
// considering the error err.
|
||||
//
|
||||
// A nil error means that all input bytes are known to be identical to the
|
||||
// output produced by the Transformer. A nil error can be returned
|
||||
// regardless of whether atEOF is true. If err is nil, then n must
|
||||
// equal len(src); the converse is not necessarily true.
|
||||
//
|
||||
// ErrEndOfSpan means that the Transformer output may differ from the
|
||||
// input after n bytes. Note that n may be len(src), meaning that the output
|
||||
// would contain additional bytes after otherwise identical output.
|
||||
// ErrShortSrc means that src had insufficient data to determine whether the
|
||||
// remaining bytes would change. Other than the error conditions listed
|
||||
// here, implementations are free to report other errors that arise.
|
||||
//
|
||||
// Calling Span can modify the Transformer state as a side effect. In
|
||||
// effect, it does the transformation just as calling Transform would, only
|
||||
// without copying to a destination buffer and only up to a point it can
|
||||
// determine the input and output bytes are the same. This is obviously more
|
||||
// limited than calling Transform, but can be more efficient in terms of
|
||||
// copying and allocating buffers. Calls to Span and Transform may be
|
||||
// interleaved.
|
||||
Span(src []byte, atEOF bool) (n int, err error)
|
||||
}
|
||||
|
||||
// NopResetter can be embedded by implementations of Transformer to add a nop
|
||||
// Reset method.
|
||||
type NopResetter struct{}
|
||||
|
@ -278,6 +317,10 @@ func (nop) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
|||
return n, n, err
|
||||
}
|
||||
|
||||
func (nop) Span(src []byte, atEOF bool) (n int, err error) {
|
||||
return len(src), nil
|
||||
}
|
||||
|
||||
type discard struct{ NopResetter }
|
||||
|
||||
func (discard) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
|
@ -289,8 +332,8 @@ var (
|
|||
// by consuming all bytes and writing nothing.
|
||||
Discard Transformer = discard{}
|
||||
|
||||
// Nop is a Transformer that copies src to dst.
|
||||
Nop Transformer = nop{}
|
||||
// Nop is a SpanningTransformer that copies src to dst.
|
||||
Nop SpanningTransformer = nop{}
|
||||
)
|
||||
|
||||
// chain is a sequence of links. A chain with N Transformers has N+1 links and
|
||||
|
@ -358,6 +401,8 @@ func (c *chain) Reset() {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: make chain use Span (is going to be fun to implement!)
|
||||
|
||||
// Transform applies the transformers of c in sequence.
|
||||
func (c *chain) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
// Set up src and dst in the chain.
|
||||
|
@ -448,8 +493,7 @@ func (c *chain) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err erro
|
|||
return dstL.n, srcL.p, err
|
||||
}
|
||||
|
||||
// RemoveFunc returns a Transformer that removes from the input all runes r for
|
||||
// which f(r) is true. Illegal bytes in the input are replaced by RuneError.
|
||||
// Deprecated: Use runes.Remove instead.
|
||||
func RemoveFunc(f func(r rune) bool) Transformer {
|
||||
return removeF(f)
|
||||
}
|
||||
|
@ -604,7 +648,8 @@ func String(t Transformer, s string) (result string, n int, err error) {
|
|||
// Transform the remaining input, growing dst and src buffers as necessary.
|
||||
for {
|
||||
n := copy(src, s[pSrc:])
|
||||
nDst, nSrc, err := t.Transform(dst[pDst:], src[:n], pSrc+n == len(s))
|
||||
atEOF := pSrc+n == len(s)
|
||||
nDst, nSrc, err := t.Transform(dst[pDst:], src[:n], atEOF)
|
||||
pDst += nDst
|
||||
pSrc += nSrc
|
||||
|
||||
|
@ -615,6 +660,9 @@ func String(t Transformer, s string) (result string, n int, err error) {
|
|||
dst = grow(dst, pDst)
|
||||
}
|
||||
} else if err == ErrShortSrc {
|
||||
if atEOF {
|
||||
return string(dst[:pDst]), pSrc, err
|
||||
}
|
||||
if nSrc == 0 {
|
||||
src = grow(src, 0)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
# github.com/Azure/azure-extension-foundation v0.0.0-20190726000431-02f4f599e64a
|
||||
## explicit
|
||||
github.com/Azure/azure-extension-foundation/errorhelper
|
||||
github.com/Azure/azure-extension-foundation/httputil
|
||||
github.com/Azure/azure-extension-foundation/msi
|
||||
# github.com/Azure/azure-sdk-for-go v3.1.0-beta.0.20160802173609-87de771fcdf5+incompatible
|
||||
## explicit
|
||||
github.com/Azure/azure-sdk-for-go/storage
|
||||
# github.com/ahmetalpbalkan/go-httpbin v0.0.0-20160706084156-8817b883dae1
|
||||
## explicit
|
||||
github.com/ahmetalpbalkan/go-httpbin
|
||||
# github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2
|
||||
## explicit
|
||||
github.com/davecgh/go-spew/spew
|
||||
# github.com/go-kit/kit v0.1.1-0.20160721083846-b076b44dbec2
|
||||
## explicit
|
||||
github.com/go-kit/kit/log
|
||||
# github.com/go-logfmt/logfmt v0.2.1-0.20160601130801-d4327190ff83
|
||||
## explicit
|
||||
github.com/go-logfmt/logfmt
|
||||
# github.com/go-stack/stack v1.5.2
|
||||
## explicit
|
||||
github.com/go-stack/stack
|
||||
# github.com/gorilla/context v0.0.0-20160525203319-aed02d124ae4
|
||||
## explicit
|
||||
github.com/gorilla/context
|
||||
# github.com/gorilla/mux v0.0.0-20160605233521-9fa818a44c2b
|
||||
## explicit
|
||||
github.com/gorilla/mux
|
||||
# github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515
|
||||
## explicit
|
||||
github.com/kr/logfmt
|
||||
# github.com/pkg/errors v0.9.1
|
||||
## explicit
|
||||
github.com/pkg/errors
|
||||
# github.com/pmezard/go-difflib v1.0.0
|
||||
## explicit
|
||||
github.com/pmezard/go-difflib/difflib
|
||||
# github.com/stretchr/testify v1.1.4-0.20160615092844-d77da356e56a
|
||||
## explicit
|
||||
github.com/stretchr/testify/assert
|
||||
github.com/stretchr/testify/require
|
||||
# github.com/xeipuuv/gojsonpointer v0.0.0-20151027082146-e0fe6f683076
|
||||
## explicit
|
||||
github.com/xeipuuv/gojsonpointer
|
||||
# github.com/xeipuuv/gojsonreference v0.0.0-20150808065054-e02fc20de94c
|
||||
## explicit
|
||||
github.com/xeipuuv/gojsonreference
|
||||
# github.com/xeipuuv/gojsonschema v0.0.0-20160623135812-c539bca196be
|
||||
## explicit
|
||||
github.com/xeipuuv/gojsonschema
|
||||
# golang.org/x/text v0.3.7
|
||||
## explicit
|
||||
golang.org/x/text/encoding
|
||||
golang.org/x/text/encoding/internal
|
||||
golang.org/x/text/encoding/internal/identifier
|
||||
golang.org/x/text/encoding/unicode
|
||||
golang.org/x/text/internal/utf8internal
|
||||
golang.org/x/text/runes
|
||||
golang.org/x/text/transform
|
||||
# gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
|
||||
## explicit
|
Загрузка…
Ссылка в новой задаче